VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/slirp/bootp.c@ 15890

Last change on this file since 15890 was 15890, checked in by vboxsync, 16 years ago

NAT: 1. wo sync enhancement branch is still functional (was corrupted with using ICMP file handler in select(1))

  1. after sending send queue doesn't need to synchronize with NAT thread to free mbuf instead NAT queue used to call freeing slirp routine.
  2. no more copying on slirp to guest sent.


  • Property svn:eol-style set to native
File size: 9.1 KB
Line 
1/*
2 * QEMU BOOTP/DHCP server
3 *
4 * Copyright (c) 2004 Fabrice Bellard
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24#include <slirp.h>
25
26/* XXX: only DHCP is supported */
27
28static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE };
29
30static BOOTPClient *get_new_addr(PNATState pData, struct in_addr *paddr)
31{
32 int i;
33
34 for(i = 0; i < NB_ADDR; i++)
35 {
36 if (!bootp_clients[i].allocated)
37 {
38 BOOTPClient *bc;
39
40 bc = &bootp_clients[i];
41 bc->allocated = 1;
42 paddr->s_addr = htonl(ntohl(special_addr.s_addr) | (i + START_ADDR));
43 return bc;
44 }
45 }
46 return NULL;
47}
48
49static int release_addr(PNATState pData, struct in_addr *paddr)
50{
51 unsigned i;
52
53 i = ntohl(paddr->s_addr) - START_ADDR - ntohl(special_addr.s_addr);
54 if (i >= NB_ADDR)
55 return 0;
56
57 memset(bootp_clients[i].macaddr, '\0', 6);
58 bootp_clients[i].allocated = 0;
59 return 1;
60}
61
62static BOOTPClient *find_addr(PNATState pData, struct in_addr *paddr, const uint8_t *macaddr)
63{
64 int i;
65
66 for(i = 0; i < NB_ADDR; i++)
67 {
68 if (!memcmp(macaddr, bootp_clients[i].macaddr, 6))
69 {
70 BOOTPClient *bc;
71
72 bc = &bootp_clients[i];
73 bc->allocated = 1;
74 paddr->s_addr = htonl(ntohl(special_addr.s_addr) | (i + START_ADDR));
75 return bc;
76 }
77 }
78 return NULL;
79}
80
81static void dhcp_decode(const uint8_t *buf, int size,
82 int *pmsg_type, struct in_addr *req_ip)
83{
84 const uint8_t *p, *p_end;
85 int len, tag;
86
87 *pmsg_type = 0;
88
89 p = buf;
90 p_end = buf + size;
91 if (size < 5)
92 return;
93 if (memcmp(p, rfc1533_cookie, 4) != 0)
94 return;
95 p += 4;
96 while (p < p_end)
97 {
98 tag = p[0];
99 if (tag == RFC1533_PAD)
100 p++;
101 else if (tag == RFC1533_END)
102 break;
103 else
104 {
105 p++;
106 if (p >= p_end)
107 break;
108 len = *p++;
109 Log(("dhcp: tag=0x%02x len=%d\n", tag, len));
110
111 switch(tag)
112 {
113 case RFC2132_REQ_ADDR:
114 if (len >= 4)
115 *req_ip = *(struct in_addr*)p;
116 break;
117 case RFC2132_MSG_TYPE:
118 if (len >= 1)
119 *pmsg_type = p[0];
120 break;
121 default:
122 break;
123 }
124 p += len;
125 }
126 }
127}
128
129static void bootp_reply(PNATState pData, struct bootp_t *bp)
130{
131 BOOTPClient *bc;
132 struct mbuf *m;
133 struct bootp_t *rbp;
134 struct sockaddr_in saddr, daddr;
135 struct in_addr dns_addr_dhcp;
136 int dhcp_msg_type, val;
137 uint8_t *q;
138 struct in_addr requested_ip; /* the requested IP in DHCPREQUEST */
139 int send_nak = 0;
140
141 /* extract exact DHCP msg type */
142 requested_ip.s_addr = 0xffffffff;
143 dhcp_decode(bp->bp_vend, DHCP_OPT_LEN, &dhcp_msg_type, &requested_ip);
144 Log(("bootp packet op=%d msgtype=%d\n", bp->bp_op, dhcp_msg_type));
145
146 if (dhcp_msg_type == 0)
147 dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */
148
149 if (dhcp_msg_type == DHCPRELEASE)
150 {
151 int rc;
152 rc = release_addr(pData, &bp->bp_ciaddr);
153 LogRel(("NAT: %s %R[IP4]\n",
154 rc ? "DHCP released IP address" : "Ignored DHCP release for IP address",
155 &bp->bp_ciaddr));
156 /* This message is not to be answered in any way. */
157 return;
158 }
159 if ( dhcp_msg_type != DHCPDISCOVER
160 && dhcp_msg_type != DHCPREQUEST)
161 return;
162
163 /* XXX: this is a hack to get the client mac address */
164 memcpy(client_ethaddr, bp->bp_hwaddr, 6);
165
166 if ((m = m_get(pData)) == NULL)
167 return;
168 m->m_data += if_maxlinkhdr; /*reserve ether header */
169 rbp = mtod(m, struct bootp_t *);
170 memset(rbp, 0, sizeof(struct bootp_t));
171#ifndef VBOX_WITH_SIMPLIFIED_SLIRP_SYNC
172 m->m_data += sizeof(struct udpiphdr);
173#endif
174
175 if (dhcp_msg_type == DHCPDISCOVER)
176 {
177 /* Do not allocate a new lease for clients that forgot that they had a lease. */
178 bc = find_addr(pData, &daddr.sin_addr, bp->bp_hwaddr);
179 if (!bc)
180 {
181 new_addr:
182 bc = get_new_addr(pData, &daddr.sin_addr);
183 if (!bc)
184 {
185 LogRel(("NAT: DHCP no IP address left\n"));
186 Log(("no address left\n"));
187 return;
188 }
189 memcpy(bc->macaddr, client_ethaddr, 6);
190 }
191 }
192 else
193 {
194 bc = find_addr(pData, &daddr.sin_addr, bp->bp_hwaddr);
195 if (!bc)
196 {
197 /* if never assigned, behaves as if it was already
198 assigned (windows fix because it remembers its address) */
199 goto new_addr;
200 }
201 }
202
203 if ( tftp_prefix
204 && RTDirExists(tftp_prefix)
205 && bootp_filename)
206 RTStrPrintf((char*)rbp->bp_file, sizeof(rbp->bp_file), "%s", bootp_filename);
207
208 saddr.sin_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_ALIAS);
209 saddr.sin_port = htons(BOOTP_SERVER);
210
211 daddr.sin_port = htons(BOOTP_CLIENT);
212
213 rbp->bp_op = BOOTP_REPLY;
214 rbp->bp_xid = bp->bp_xid;
215 rbp->bp_htype = 1;
216 rbp->bp_hlen = 6;
217 memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6);
218
219 rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */
220 rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */
221
222 q = rbp->bp_vend;
223 memcpy(q, rfc1533_cookie, 4);
224 q += 4;
225
226 if (dhcp_msg_type == DHCPDISCOVER)
227 {
228 *q++ = RFC2132_MSG_TYPE;
229 *q++ = 1;
230 *q++ = DHCPOFFER;
231 }
232 else if (dhcp_msg_type == DHCPREQUEST)
233 {
234 *q++ = RFC2132_MSG_TYPE;
235 *q++ = 1;
236 if ( requested_ip.s_addr != 0xffffffff
237 && requested_ip.s_addr != daddr.sin_addr.s_addr)
238 {
239 /* network changed */
240 *q++ = DHCPNAK;
241 send_nak = 1;
242 }
243 else
244 *q++ = DHCPACK;
245 }
246
247 if (send_nak)
248 LogRel(("NAT: Client requested IP address %R[IP4] -- sending NAK\n",
249 &requested_ip));
250 else
251 LogRel(("NAT: DHCP offered IP address %R[IP4]\n",
252 &daddr.sin_addr));
253
254 if ( dhcp_msg_type == DHCPDISCOVER
255 || dhcp_msg_type == DHCPREQUEST)
256 {
257 *q++ = RFC2132_SRV_ID;
258 *q++ = 4;
259 memcpy(q, &saddr.sin_addr, 4);
260 q += 4;
261 }
262
263 if (!send_nak &&
264 ( dhcp_msg_type == DHCPDISCOVER
265 || dhcp_msg_type == DHCPREQUEST))
266 {
267 *q++ = RFC1533_NETMASK;
268 *q++ = 4;
269 *q++ = (pData->netmask & 0xff000000) >> 24;
270 *q++ = (pData->netmask & 0x00ff0000) >> 16;
271 *q++ = (pData->netmask & 0x0000ff00) >> 8;
272 *q++ = (pData->netmask & 0x000000ff);
273
274 *q++ = RFC1533_GATEWAY;
275 *q++ = 4;
276 memcpy(q, &saddr.sin_addr, 4);
277 q += 4;
278
279 *q++ = RFC1533_DNS;
280 *q++ = 4;
281 dns_addr_dhcp.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_DNS);
282 memcpy(q, &dns_addr_dhcp, 4);
283 q += 4;
284
285 *q++ = RFC2132_LEASE_TIME;
286 *q++ = 4;
287 val = htonl(LEASE_TIME);
288 memcpy(q, &val, 4);
289 q += 4;
290
291 if (*slirp_hostname)
292 {
293 val = (int)strlen(slirp_hostname);
294 *q++ = RFC1533_HOSTNAME;
295 *q++ = val;
296 memcpy(q, slirp_hostname, val);
297 q += val;
298 }
299
300 if (pData->pszDomain && pData->fPassDomain)
301 {
302 val = (int)strlen(pData->pszDomain);
303 *q++ = RFC1533_DOMAINNAME;
304 *q++ = val;
305 memcpy(q, pData->pszDomain, val);
306 q += val;
307 }
308 }
309 *q++ = RFC1533_END;
310
311 m->m_len = sizeof(struct bootp_t)
312 - sizeof(struct ip)
313 - sizeof(struct udphdr);
314#ifdef VBOX_WITH_SIMPLIFIED_SLIRP_SYNC
315 m->m_data += sizeof(struct udphdr)
316 + sizeof(struct ip);
317#endif
318 /* Reply to the broadcast address, as some clients perform paranoid checks. */
319 daddr.sin_addr.s_addr = INADDR_BROADCAST;
320 udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
321}
322
323void bootp_input(PNATState pData, struct mbuf *m)
324{
325 struct bootp_t *bp = mtod(m, struct bootp_t *);
326
327 if (bp->bp_op == BOOTP_REQUEST)
328 bootp_reply(pData, bp);
329}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette