VirtualBox

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

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

NAT: log

  • Property svn:eol-style set to native
File size: 12.2 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 int dhcp_msg_type, val;
147 uint8_t *q;
148 struct in_addr requested_ip; /* the requested IP in DHCPREQUEST */
149 int send_nak = 0;
150
151#define FILL_BOOTP_EXT(q, tag, len, pvalue) \
152 do { \
153 struct bootp_ext *be = (struct bootp_ext *)(q); \
154 be->bpe_tag = (tag); \
155 be->bpe_len = (len); \
156 memcpy(&be[1], (pvalue), (len)); \
157 (q) = (uint8_t *)(&be[1]) + (len); \
158 }while(0)
159/* appending another value to tag, calculates len of whole block*/
160#define FILL_BOOTP_APP(head, q, tag, len, pvalue) \
161 do { \
162 struct bootp_ext *be = (struct bootp_ext *)(head); \
163 memcpy(q, (pvalue), (len)); \
164 (q) += (len); \
165 Assert(be->bpe_tag == (tag)); \
166 be->bpe_len += (len); \
167 }while(0)
168
169 /* extract exact DHCP msg type */
170 requested_ip.s_addr = 0xffffffff;
171 dhcp_decode(bp->bp_vend, DHCP_OPT_LEN, &dhcp_msg_type, &requested_ip);
172 Log(("bootp packet op=%d msgtype=%d\n", bp->bp_op, dhcp_msg_type));
173
174 if (dhcp_msg_type == 0)
175 dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */
176
177 if (dhcp_msg_type == DHCPRELEASE)
178 {
179 int rc;
180 rc = release_addr(pData, &bp->bp_ciaddr);
181 LogRel(("NAT: %s %R[IP4]\n",
182 rc ? "DHCP released IP address" : "Ignored DHCP release for IP address",
183 &bp->bp_ciaddr));
184 /* This message is not to be answered in any way. */
185 return;
186 }
187 if ( dhcp_msg_type != DHCPDISCOVER
188 && dhcp_msg_type != DHCPREQUEST)
189 return;
190
191#ifndef VBOX_WITHOUT_SLIRP_CLIENT_ETHER
192 /* XXX: this is a hack to get the client mac address */
193 memcpy(client_ethaddr, bp->bp_hwaddr, 6);
194#endif
195
196 if ((m = m_get(pData)) == NULL)
197 return;
198#ifdef VBOX_WITHOUT_SLIRP_CLIENT_ETHER
199 eh = mtod(m, struct ethhdr *);
200 memcpy(eh->h_source, bp->bp_hwaddr, ETH_ALEN); /* XXX: if_encap just swap source with dest*/
201#endif
202 m->m_data += if_maxlinkhdr; /*reserve ether header */
203 rbp = mtod(m, struct bootp_t *);
204 memset(rbp, 0, sizeof(struct bootp_t));
205
206 if (dhcp_msg_type == DHCPDISCOVER)
207 {
208 /* Do not allocate a new lease for clients that forgot that they had a lease. */
209 bc = find_addr(pData, &daddr.sin_addr, bp->bp_hwaddr);
210 if (!bc)
211 {
212 new_addr:
213 bc = get_new_addr(pData, &daddr.sin_addr);
214 if (!bc)
215 {
216 LogRel(("NAT: DHCP no IP address left\n"));
217 Log(("no address left\n"));
218 return;
219 }
220#ifdef VBOX_WITHOUT_SLIRP_CLIENT_ETHER
221 memcpy(bc->macaddr, bp->bp_hwaddr, 6);
222#else
223 memcpy(bc->macaddr, client_ethaddr, 6);
224#endif
225 }
226 }
227 else
228 {
229 bc = find_addr(pData, &daddr.sin_addr, bp->bp_hwaddr);
230 if (!bc)
231 {
232 /* if never assigned, behaves as if it was already
233 assigned (windows fix because it remembers its address) */
234 goto new_addr;
235 }
236 }
237
238 if ( tftp_prefix
239 && RTDirExists(tftp_prefix)
240 && bootp_filename)
241 RTStrPrintf((char*)rbp->bp_file, sizeof(rbp->bp_file), "%s", bootp_filename);
242
243 Log(("NAT: DHCP: bp_file:%s\n", &rbp->bp_file));
244 /* Address/port of the DHCP server. */
245#ifndef VBOX_WITH_NAT_SERVICE
246 saddr.sin_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_ALIAS);
247#else
248 saddr.sin_addr.s_addr = special_addr.s_addr;
249#endif
250 Log(("NAT: DHCP: s_addr:%R[IP4]\n", &saddr.sin_addr));
251
252 saddr.sin_port = htons(BOOTP_SERVER);
253
254 daddr.sin_port = htons(BOOTP_CLIENT);
255
256 rbp->bp_op = BOOTP_REPLY;
257 rbp->bp_xid = bp->bp_xid;
258 rbp->bp_htype = 1;
259 rbp->bp_hlen = 6;
260 memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6);
261
262 rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */
263 Log(("NAT: DHCP: bp_yiaddr:%R[IP4]\n", &rbp->bp_yiaddr));
264 rbp->bp_siaddr = pData->tftp_server; /* Next Server IP address, i.e. TFTP */
265 Log(("NAT: DHCP: bp_siaddr:%R[IP4]\n", &rbp->bp_siaddr));
266
267 q = rbp->bp_vend;
268 memcpy(q, rfc1533_cookie, 4);
269 q += 4;
270
271 if (dhcp_msg_type == DHCPDISCOVER)
272 {
273 *q++ = RFC2132_MSG_TYPE;
274 *q++ = 1;
275 *q++ = DHCPOFFER;
276 }
277 else if (dhcp_msg_type == DHCPREQUEST)
278 {
279 *q++ = RFC2132_MSG_TYPE;
280 *q++ = 1;
281 if ( requested_ip.s_addr != 0xffffffff
282 && requested_ip.s_addr != daddr.sin_addr.s_addr)
283 {
284 /* network changed */
285 *q++ = DHCPNAK;
286 send_nak = 1;
287 }
288 else
289 *q++ = DHCPACK;
290 }
291
292 if (send_nak)
293 LogRel(("NAT: Client requested IP address %R[IP4] -- sending NAK\n",
294 &requested_ip));
295 else
296 LogRel(("NAT: DHCP offered IP address %R[IP4]\n",
297 &daddr.sin_addr));
298 if ( dhcp_msg_type == DHCPDISCOVER
299 || dhcp_msg_type == DHCPREQUEST)
300 {
301 FILL_BOOTP_EXT(q, RFC2132_SRV_ID, 4, &saddr.sin_addr);
302 }
303
304 if (!send_nak &&
305 ( dhcp_msg_type == DHCPDISCOVER
306 || dhcp_msg_type == DHCPREQUEST))
307 {
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 uint32_t lease_time = htonl(LEASE_TIME);
313 uint32_t netmask = htonl(pData->netmask);
314
315 FILL_BOOTP_EXT(q, RFC1533_NETMASK, 4, &netmask);
316 FILL_BOOTP_EXT(q, RFC1533_GATEWAY, 4, &saddr.sin_addr);
317
318 if (pData->use_dns_proxy)
319 {
320 uint32_t addr = htonl(ntohl(special_addr.s_addr) | CTL_DNS);
321 FILL_BOOTP_EXT(q, RFC1533_DNS, 4, &addr);
322 goto skip_dns_servers;
323 }
324
325 if (!TAILQ_EMPTY(&pData->dns_list_head))
326 {
327 de = TAILQ_LAST(&pData->dns_list_head, dns_list_head);
328 q_dns_header = q;
329 FILL_BOOTP_EXT(q, RFC1533_DNS, 4, &de->de_addr.s_addr);
330 }
331
332 TAILQ_FOREACH_REVERSE(de, &pData->dns_list_head, dns_list_head, de_list)
333 {
334 if (TAILQ_LAST(&pData->dns_list_head, dns_list_head) == de)
335 continue; /* first value with head we've ingected before */
336 FILL_BOOTP_APP(q_dns_header, q, RFC1533_DNS, 4, &de->de_addr.s_addr);
337 }
338
339skip_dns_servers:
340 if (LIST_EMPTY(&pData->dns_domain_list_head))
341 {
342 /* Microsoft dhcp client doen't like domain-less dhcp and trimmed packets*/
343 /* dhcpcd client very sad if no domain name is passed */
344 FILL_BOOTP_EXT(q, RFC1533_DOMAINNAME, 1, " ");
345 }
346 if (pData->fPassDomain)
347 {
348 LIST_FOREACH(dd, &pData->dns_domain_list_head, dd_list)
349 {
350
351 if (dd->dd_pszDomain == NULL)
352 continue;
353 /* never meet valid separator here in RFC1533*/
354 if (added != 0)
355 FILL_BOOTP_EXT(q, RFC1533_DOMAINNAME, 1, ",");
356 else
357 added = 1;
358 val = (int)strlen(dd->dd_pszDomain);
359 FILL_BOOTP_EXT(q, RFC1533_DOMAINNAME, val, dd->dd_pszDomain);
360 }
361 }
362
363 FILL_BOOTP_EXT(q, RFC2132_LEASE_TIME, 4, &lease_time);
364
365 if (*slirp_hostname)
366 {
367 val = (int)strlen(slirp_hostname);
368 FILL_BOOTP_EXT(q, RFC1533_HOSTNAME, val, slirp_hostname);
369 }
370 }
371 *q++ = RFC1533_END;
372
373 m->m_len = sizeof(struct bootp_t)
374 - sizeof(struct ip)
375 - sizeof(struct udphdr);
376 m->m_data += sizeof(struct udphdr)
377 + sizeof(struct ip);
378 /* Reply to the broadcast address, as some clients perform paranoid checks. */
379 daddr.sin_addr.s_addr = INADDR_BROADCAST;
380 udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
381}
382
383void bootp_input(PNATState pData, struct mbuf *m)
384{
385 struct bootp_t *bp = mtod(m, struct bootp_t *);
386
387 if (bp->bp_op == BOOTP_REQUEST)
388#ifndef VBOX_WITHOUT_SLIRP_CLIENT_ETHER
389 bootp_reply(pData, bp);
390#else
391 bootp_reply(pData, m);
392#endif
393}
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