VirtualBox

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

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

NAT: Slirp don't use ether address of guest anymore
instead it calculates ethernet address of destination
with lookup operation. Currently it's very simple looks
over send addresses via dhcp or assume destination in outer
network and gets Slirp's ethernet address.

  • Property svn:eol-style set to native
File size: 12.4 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#ifdef VBOX_WITHOUT_SLIRP_CLIENT_ETHER
44 bc->addr.s_addr = paddr->s_addr;
45#endif
46 return bc;
47 }
48 }
49 return NULL;
50}
51
52static int release_addr(PNATState pData, struct in_addr *paddr)
53{
54 unsigned i;
55
56 i = ntohl(paddr->s_addr) - START_ADDR - ntohl(special_addr.s_addr);
57 if (i >= NB_ADDR)
58 return 0;
59
60 memset(bootp_clients[i].macaddr, '\0', 6);
61 bootp_clients[i].allocated = 0;
62 return 1;
63}
64
65static BOOTPClient *find_addr(PNATState pData, struct in_addr *paddr, const uint8_t *macaddr)
66{
67 int i;
68
69 for(i = 0; i < NB_ADDR; i++)
70 {
71 if (!memcmp(macaddr, bootp_clients[i].macaddr, 6))
72 {
73 BOOTPClient *bc;
74
75 bc = &bootp_clients[i];
76 bc->allocated = 1;
77 paddr->s_addr = htonl(ntohl(special_addr.s_addr) | (i + START_ADDR));
78 return bc;
79 }
80 }
81 return NULL;
82}
83
84static void dhcp_decode(const uint8_t *buf, int size,
85 int *pmsg_type, struct in_addr *req_ip)
86{
87 const uint8_t *p, *p_end;
88 int len, tag;
89
90 *pmsg_type = 0;
91
92 p = buf;
93 p_end = buf + size;
94 if (size < 5)
95 return;
96 if (memcmp(p, rfc1533_cookie, 4) != 0)
97 return;
98 p += 4;
99 while (p < p_end)
100 {
101 tag = p[0];
102 if (tag == RFC1533_PAD)
103 p++;
104 else if (tag == RFC1533_END)
105 break;
106 else
107 {
108 p++;
109 if (p >= p_end)
110 break;
111 len = *p++;
112 Log(("dhcp: tag=0x%02x len=%d\n", tag, len));
113
114 switch(tag)
115 {
116 case RFC2132_REQ_ADDR:
117 if (len >= 4)
118 *req_ip = *(struct in_addr*)p;
119 break;
120 case RFC2132_MSG_TYPE:
121 if (len >= 1)
122 *pmsg_type = p[0];
123 break;
124 default:
125 break;
126 }
127 p += len;
128 }
129 }
130}
131
132#ifndef VBOX_WITHOUT_SLIRP_CLIENT_ETHER
133static void bootp_reply(PNATState pData, struct bootp_t *bp)
134#else
135static void bootp_reply(PNATState pData, struct mbuf *m0)
136#endif
137{
138 BOOTPClient *bc;
139 struct mbuf *m; /* XXX: @todo vasily - it'd be better to reuse this mbuf here */
140#ifdef VBOX_WITHOUT_SLIRP_CLIENT_ETHER
141 struct bootp_t *bp = mtod(m0, struct bootp_t *);
142 struct ethhdr *eh;
143#endif
144 struct bootp_t *rbp;
145 struct sockaddr_in saddr, daddr;
146#ifndef VBOX_WITH_MULTI_DNS
147 struct in_addr dns_addr_dhcp;
148#endif
149 int dhcp_msg_type, val;
150 uint8_t *q;
151 struct in_addr requested_ip; /* the requested IP in DHCPREQUEST */
152 int send_nak = 0;
153
154#define FILL_BOOTP_EXT(q, tag, len, pvalue) \
155 do { \
156 struct bootp_ext *be = (struct bootp_ext *)(q); \
157 be->bpe_tag = (tag); \
158 be->bpe_len = (len); \
159 memcpy(&be[1], (pvalue), (len)); \
160 (q) = (uint8_t *)(&be[1]) + (len); \
161 }while(0)
162/* appending another value to tag, calculates len of whole block*/
163#define FILL_BOOTP_APP(head, q, tag, len, pvalue) \
164 do { \
165 struct bootp_ext *be = (struct bootp_ext *)(head); \
166 memcpy(q, (pvalue), (len)); \
167 (q) += (len); \
168 Assert(be->bpe_tag == (tag)); \
169 be->bpe_len += (len); \
170 }while(0)
171
172 /* extract exact DHCP msg type */
173 requested_ip.s_addr = 0xffffffff;
174 dhcp_decode(bp->bp_vend, DHCP_OPT_LEN, &dhcp_msg_type, &requested_ip);
175 Log(("bootp packet op=%d msgtype=%d\n", bp->bp_op, dhcp_msg_type));
176
177 if (dhcp_msg_type == 0)
178 dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */
179
180 if (dhcp_msg_type == DHCPRELEASE)
181 {
182 int rc;
183 rc = release_addr(pData, &bp->bp_ciaddr);
184 LogRel(("NAT: %s %R[IP4]\n",
185 rc ? "DHCP released IP address" : "Ignored DHCP release for IP address",
186 &bp->bp_ciaddr));
187 /* This message is not to be answered in any way. */
188 return;
189 }
190 if ( dhcp_msg_type != DHCPDISCOVER
191 && dhcp_msg_type != DHCPREQUEST)
192 return;
193
194#ifndef VBOX_WITHOUT_SLIRP_CLIENT_ETHER
195 /* XXX: this is a hack to get the client mac address */
196 memcpy(client_ethaddr, bp->bp_hwaddr, 6);
197#endif
198
199 if ((m = m_get(pData)) == NULL)
200 return;
201#ifdef VBOX_WITHOUT_SLIRP_CLIENT_ETHER
202 eh = mtod(m, struct ethhdr *);
203 memcpy(eh->h_source, bp->bp_hwaddr, ETH_ALEN); /* XXX: if_encap just swap source with dest*/
204#endif
205 m->m_data += if_maxlinkhdr; /*reserve ether header */
206 rbp = mtod(m, struct bootp_t *);
207 memset(rbp, 0, sizeof(struct bootp_t));
208
209 if (dhcp_msg_type == DHCPDISCOVER)
210 {
211 /* Do not allocate a new lease for clients that forgot that they had a lease. */
212 bc = find_addr(pData, &daddr.sin_addr, bp->bp_hwaddr);
213 if (!bc)
214 {
215 new_addr:
216 bc = get_new_addr(pData, &daddr.sin_addr);
217 if (!bc)
218 {
219 LogRel(("NAT: DHCP no IP address left\n"));
220 Log(("no address left\n"));
221 return;
222 }
223#ifdef VBOX_WITHOUT_SLIRP_CLIENT_ETHER
224 memcpy(bc->macaddr, bp->bp_hwaddr, 6);
225#else
226 memcpy(bc->macaddr, client_ethaddr, 6);
227#endif
228 }
229 }
230 else
231 {
232 bc = find_addr(pData, &daddr.sin_addr, bp->bp_hwaddr);
233 if (!bc)
234 {
235 /* if never assigned, behaves as if it was already
236 assigned (windows fix because it remembers its address) */
237 goto new_addr;
238 }
239 }
240
241 if ( tftp_prefix
242 && RTDirExists(tftp_prefix)
243 && bootp_filename)
244 RTStrPrintf((char*)rbp->bp_file, sizeof(rbp->bp_file), "%s", bootp_filename);
245
246 /* Address/port of the DHCP server. */
247#ifndef VBOX_WITHOUT_SLIRP_CLIENT_ETHER
248 saddr.sin_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_ALIAS);
249#else
250 saddr.sin_addr.s_addr = special_addr.s_addr;
251#endif
252
253 saddr.sin_port = htons(BOOTP_SERVER);
254
255 daddr.sin_port = htons(BOOTP_CLIENT);
256
257 rbp->bp_op = BOOTP_REPLY;
258 rbp->bp_xid = bp->bp_xid;
259 rbp->bp_htype = 1;
260 rbp->bp_hlen = 6;
261 memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6);
262
263 rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */
264 rbp->bp_siaddr = pData->tftp_server; /* Next Server IP address, i.e. TFTP */
265
266 q = rbp->bp_vend;
267 memcpy(q, rfc1533_cookie, 4);
268 q += 4;
269
270 if (dhcp_msg_type == DHCPDISCOVER)
271 {
272 *q++ = RFC2132_MSG_TYPE;
273 *q++ = 1;
274 *q++ = DHCPOFFER;
275 }
276 else if (dhcp_msg_type == DHCPREQUEST)
277 {
278 *q++ = RFC2132_MSG_TYPE;
279 *q++ = 1;
280 if ( requested_ip.s_addr != 0xffffffff
281 && requested_ip.s_addr != daddr.sin_addr.s_addr)
282 {
283 /* network changed */
284 *q++ = DHCPNAK;
285 send_nak = 1;
286 }
287 else
288 *q++ = DHCPACK;
289 }
290
291 if (send_nak)
292 LogRel(("NAT: Client requested IP address %R[IP4] -- sending NAK\n",
293 &requested_ip));
294 else
295 LogRel(("NAT: DHCP offered IP address %R[IP4]\n",
296 &daddr.sin_addr));
297 if ( dhcp_msg_type == DHCPDISCOVER
298 || dhcp_msg_type == DHCPREQUEST)
299 {
300 FILL_BOOTP_EXT(q, RFC2132_SRV_ID, 4, &saddr.sin_addr);
301 }
302
303 if (!send_nak &&
304 ( dhcp_msg_type == DHCPDISCOVER
305 || dhcp_msg_type == DHCPREQUEST))
306 {
307#ifdef VBOX_WITH_MULTI_DNS
308 struct dns_entry *de = NULL;
309 struct dns_domain_entry *dd = NULL;
310 int added = 0;
311 uint8_t *q_dns_header = NULL;
312#endif
313 uint32_t lease_time = htonl(LEASE_TIME);
314 uint32_t netmask = htonl(pData->netmask);
315
316 FILL_BOOTP_EXT(q, RFC1533_NETMASK, 4, &netmask);
317 FILL_BOOTP_EXT(q, RFC1533_GATEWAY, 4, &saddr.sin_addr);
318
319#ifndef VBOX_WITH_MULTI_DNS
320 dns_addr_dhcp.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_DNS);
321 FILL_BOOTP_EXT(q, RFC1533_DNS, 4, &dns_addr_dhcp.s_addr);
322#else
323# ifdef VBOX_WITH_SLIRP_DNS_PROXY
324 if (pData->use_dns_proxy)
325 {
326 uint32_t addr = htonl(ntohl(special_addr.s_addr) | CTL_DNS);
327 FILL_BOOTP_EXT(q, RFC1533_DNS, 4, &addr);
328 goto skip_dns_servers;
329 }
330# endif
331
332
333 if (!LIST_EMPTY(&pData->dns_list_head))
334 {
335 de = LIST_FIRST(&pData->dns_list_head);
336 q_dns_header = q;
337 FILL_BOOTP_EXT(q, RFC1533_DNS, 4, &de->de_addr.s_addr);
338 }
339
340 LIST_FOREACH(de, &pData->dns_list_head, de_list)
341 {
342 if (LIST_FIRST(&pData->dns_list_head) == de)
343 continue; /* first value with head we've ingected before */
344 FILL_BOOTP_APP(q_dns_header, q, RFC1533_DNS, 4, &de->de_addr.s_addr);
345 }
346
347#ifdef VBOX_WITH_SLIRP_DNS_PROXY
348 skip_dns_servers:
349#endif
350 if (LIST_EMPTY(&pData->dns_domain_list_head))
351 {
352 /* Microsoft dhcp client doen't like domain-less dhcp and trimmed packets*/
353 /* dhcpcd client very sad if no domain name is passed */
354 FILL_BOOTP_EXT(q, RFC1533_DOMAINNAME, 1, " ");
355 }
356 LIST_FOREACH(dd, &pData->dns_domain_list_head, dd_list)
357 {
358
359 if (dd->dd_pszDomain == NULL)
360 continue;
361 if (added != 0)
362 FILL_BOOTP_EXT(q, RFC1533_DOMAINNAME, 1, ","); /* never meet valid separator here in RFC1533*/
363 else
364 added = 1;
365 val = (int)strlen(dd->dd_pszDomain);
366 FILL_BOOTP_EXT(q, RFC1533_DOMAINNAME, val, dd->dd_pszDomain);
367 }
368#endif
369
370 FILL_BOOTP_EXT(q, RFC2132_LEASE_TIME, 4, &lease_time);
371
372 if (*slirp_hostname)
373 {
374 val = (int)strlen(slirp_hostname);
375 FILL_BOOTP_EXT(q, RFC1533_HOSTNAME, val, slirp_hostname);
376 }
377
378#ifndef VBOX_WITH_MULTI_DNS
379 if (pData->pszDomain && pData->fPassDomain)
380 {
381 val = (int)strlen(pData->pszDomain);
382 FILL_BOOTP_EXT(q, RFC1533_DOMAINNAME, val, pData->pszDomain);
383 }
384#endif
385 }
386 *q++ = RFC1533_END;
387
388 m->m_len = sizeof(struct bootp_t)
389 - sizeof(struct ip)
390 - sizeof(struct udphdr);
391 m->m_data += sizeof(struct udphdr)
392 + sizeof(struct ip);
393 /* Reply to the broadcast address, as some clients perform paranoid checks. */
394 daddr.sin_addr.s_addr = INADDR_BROADCAST;
395 udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
396}
397
398void bootp_input(PNATState pData, struct mbuf *m)
399{
400 struct bootp_t *bp = mtod(m, struct bootp_t *);
401
402 if (bp->bp_op == BOOTP_REQUEST)
403#ifndef VBOX_WITHOUT_SLIRP_CLIENT_ETHER
404 bootp_reply(pData, bp);
405#else
406 bootp_reply(pData, m);
407#endif
408}
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