VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/NAT/proxy_dhcp6ds.c@ 48873

Last change on this file since 48873 was 48338, checked in by vboxsync, 11 years ago

Disable stateless DHCPv6 server for now. It only tells guests the
nameserver IPv6 address, but HostDnsService doesn't support IPv6
nameserver addresses yet. Do not set "O" (other configuration) flag
in router advertisements, accordingly. This should avoid confusion
for the beta.

Add "TODO" comments in DHCPv6 server code where the nameserver address
should be picked up - it currently has leftover testing kludge, that
advertised mapped host loopback as IPv6 nameserver (with a
corresponding hack to remap IPv6 to IPv4 in pxudp.c that has been
disabled since).

  • Property svn:eol-style set to native
File size: 8.5 KB
Line 
1/* -*- indent-tabs-mode: nil; -*- */
2/**
3 * Simple stateless DHCPv6 (RFC 3736) server.
4 */
5#include "winutils.h"
6#include "dhcp6.h"
7#include "proxytest.h"
8
9#include <string.h>
10
11#include "lwip/opt.h"
12#include "lwip/mld6.h"
13#include "lwip/udp.h"
14
15
16static void dhcp6ds_recv(void *, struct udp_pcb *, struct pbuf *, ip6_addr_t *, u16_t);
17
18
19/* ff02::1:2 - "All_DHCP_Relay_Agents_and_Servers" link-scoped multicast */
20static /* const */ ip6_addr_t all_dhcp_relays_and_servers = {
21 { PP_HTONL(0xff020000UL), 0, 0, PP_HTONL(0x00010002UL) }
22};
23
24/* ff05::1:3 - "All_DHCP_Servers" site-scoped multicast */
25static /* const */ ip6_addr_t all_dhcp_servers = {
26 { PP_HTONL(0xff050000UL), 0, 0, PP_HTONL(0x00010003UL) }
27};
28
29
30static struct udp_pcb *dhcp6ds_pcb;
31
32/* prebuilt Server ID option */
33#define DUID_LL_LEN (/* duid type */ 2 + /* hw type */ 2 + /* ether addr */ 6)
34static u8_t dhcp6ds_serverid[/* opt */ 2 + /* optlen */ 2 + DUID_LL_LEN];
35
36/* prebuilt DNS Servers option */
37static u8_t dhcp6ds_dns[/* opt */ 2 + /* optlen */ 2 + /* IPv6 addr */ 16];
38
39
40/**
41 * Initialize DHCP6 server.
42 *
43 * Join DHCP6 multicast groups.
44 * Create and bind server pcb.
45 * Prebuild fixed parts of reply.
46 */
47err_t
48dhcp6ds_init(struct netif *proxy_netif)
49{
50 ip6_addr_t *pxaddr, *pxaddr_nonlocal;
51 int i;
52 err_t error;
53
54 LWIP_ASSERT1(proxy_netif != NULL);
55 LWIP_ASSERT1(proxy_netif->hwaddr_len == 6); /* ethernet */
56
57 pxaddr = netif_ip6_addr(proxy_netif, 0); /* link local */
58
59 /*
60 * XXX: TODO: This is a leftover from testing with IPv6 mapped
61 * loopback with a special IPv6->IPv4 mapping hack in pxudp.c
62 */
63 /* advertise ourself as DNS resolver - will be proxied to host */
64 pxaddr_nonlocal = NULL;
65 for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
66 if (ip6_addr_ispreferred(netif_ip6_addr_state(proxy_netif, i))
67 && !ip6_addr_islinklocal(netif_ip6_addr(proxy_netif, i)))
68 {
69 pxaddr_nonlocal = netif_ip6_addr(proxy_netif, i);
70 break;
71 }
72 }
73 LWIP_ASSERT1(pxaddr_nonlocal != NULL); /* must be configured on the netif */
74
75
76 error = mld6_joingroup(pxaddr, &all_dhcp_relays_and_servers);
77 if (error != ERR_OK) {
78 DPRINTF0(("%s: failed to join All_DHCP_Relay_Agents_and_Servers: %s\n",
79 __func__, proxy_lwip_strerr(error)));
80 goto err;
81 }
82
83 error = mld6_joingroup(pxaddr, &all_dhcp_servers);
84 if (error != ERR_OK) {
85 DPRINTF0(("%s: failed to join All_DHCP_Servers: %s\n",
86 __func__, proxy_lwip_strerr(error)));
87 goto err1;
88 }
89
90
91 dhcp6ds_pcb = udp_new_ip6();
92 if (dhcp6ds_pcb == NULL) {
93 DPRINTF0(("%s: failed to allocate PCB\n", __func__));
94 error = ERR_MEM;
95 goto err2;
96 }
97
98 udp_recv_ip6(dhcp6ds_pcb, dhcp6ds_recv, NULL);
99
100 error = udp_bind_ip6(dhcp6ds_pcb, pxaddr, DHCP6_SERVER_PORT);
101 if (error != ERR_OK) {
102 DPRINTF0(("%s: failed to bind PCB\n", __func__));
103 goto err3;
104 }
105
106
107#define OPT_SET(buf, off, c) do { \
108 u16_t _s = PP_HTONS(c); \
109 memcpy(&(buf)[off], &_s, sizeof(u16_t)); \
110 } while (0)
111
112#define SERVERID_SET(off, c) OPT_SET(dhcp6ds_serverid, (off), (c))
113#define DNSSRV_SET(off, c) OPT_SET(dhcp6ds_dns, (off), (c))
114
115 SERVERID_SET(0, DHCP6_OPTION_SERVERID);
116 SERVERID_SET(2, DUID_LL_LEN);
117 SERVERID_SET(4, DHCP6_DUID_LL);
118 SERVERID_SET(6, ARES_HRD_ETHERNET);
119 memcpy(&dhcp6ds_serverid[8], proxy_netif->hwaddr, 6);
120
121 DNSSRV_SET(0, DHCP6_OPTION_DNS_SERVERS);
122 DNSSRV_SET(2, 16); /* one IPv6 address */
123 /*
124 * XXX: TODO: This is a leftover from testing with IPv6 mapped
125 * loopback with a special IPv6->IPv4 mapping hack in pxudp.c
126 */
127 memcpy(&dhcp6ds_dns[4], pxaddr_nonlocal, sizeof(ip6_addr_t));
128
129#undef SERVERID_SET
130#undef DNSSRV_SET
131
132 return ERR_OK;
133
134
135 err3:
136 udp_remove(dhcp6ds_pcb);
137 dhcp6ds_pcb = NULL;
138 err2:
139 mld6_leavegroup(pxaddr, &all_dhcp_servers);
140 err1:
141 mld6_leavegroup(pxaddr, &all_dhcp_relays_and_servers);
142 err:
143 return error;
144}
145
146
147static u8_t dhcp6ds_reply_buf[1024];
148
149static void
150dhcp6ds_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p,
151 ip6_addr_t *addr, u16_t port)
152{
153 u8_t msg_header[4];
154 unsigned int msg_type, msg_tid;
155 int copied;
156 size_t roff;
157 struct pbuf *q;
158 err_t error;
159
160 LWIP_UNUSED_ARG(arg);
161 LWIP_ASSERT1(p != NULL);
162
163 copied = pbuf_copy_partial(p, msg_header, sizeof(msg_header), 0);
164 if (copied != sizeof(msg_header)) {
165 DPRINTF(("%s: message header truncated\n", __func__));
166 pbuf_free(p);
167 return;
168 }
169 pbuf_header(p, -(s16_t)sizeof(msg_header));
170
171 msg_type = msg_header[0];
172 msg_tid = (msg_header[1] << 16) | (msg_header[2] << 8) | msg_header[3];
173 DPRINTF(("%s: type %u, tid 0x%6x\n", __func__, msg_type, msg_tid));
174 if (msg_type != DHCP6_INFORMATION_REQUEST) { /* TODO:? RELAY_FORW */
175 pbuf_free(p);
176 return;
177 }
178
179 roff = 0;
180
181 msg_header[0] = DHCP6_REPLY;
182 memcpy(dhcp6ds_reply_buf + roff, msg_header, sizeof(msg_header));
183 roff += sizeof(msg_header);
184
185
186 /* loop over options */
187 while (p->tot_len > 0) {
188 u16_t opt, optlen;
189
190 /* fetch option code */
191 copied = pbuf_copy_partial(p, &opt, sizeof(opt), 0);
192 if (copied != sizeof(opt)) {
193 DPRINTF(("%s: option header truncated\n", __func__));
194 pbuf_free(p);
195 return;
196 }
197 pbuf_header(p, -(s16_t)sizeof(opt));
198 opt = ntohs(opt);
199
200 /* fetch option length */
201 copied = pbuf_copy_partial(p, &optlen, sizeof(optlen), 0);
202 if (copied != sizeof(optlen)) {
203 DPRINTF(("%s: option %u length truncated\n", __func__, opt));
204 pbuf_free(p);
205 return;
206 }
207 pbuf_header(p, -(s16_t)sizeof(optlen));
208 optlen = ntohs(optlen);
209
210 /* enough data? */
211 if (optlen > p->tot_len) {
212 DPRINTF(("%s: option %u truncated: expect %u, got %u\n",
213 __func__, opt, optlen, p->tot_len));
214 pbuf_free(p);
215 return;
216 }
217
218 DPRINTF2(("%s: option %u length %u\n", __func__, opt, optlen));
219
220 if (opt == DHCP6_OPTION_CLIENTID) {
221 u16_t s;
222
223 /* "A DUID can be no more than 128 octets long (not
224 including the type code)." */
225 if (optlen > 130) {
226 DPRINTF(("%s: client DUID too long: %u\n", __func__, optlen));
227 pbuf_free(p);
228 return;
229 }
230
231 s = PP_HTONS(DHCP6_OPTION_CLIENTID);
232 memcpy(dhcp6ds_reply_buf + roff, &s, sizeof(s));
233 roff += sizeof(s);
234
235 s = ntohs(optlen);
236 memcpy(dhcp6ds_reply_buf + roff, &s, sizeof(s));
237 roff += sizeof(s);
238
239 pbuf_copy_partial(p, dhcp6ds_reply_buf + roff, optlen, 0);
240 roff += optlen;
241 }
242 else if (opt == DHCP6_OPTION_ORO) {
243 u16_t *opts;
244 int i, nopts;
245
246 if (optlen % 2 != 0) {
247 DPRINTF2(("%s: Option Request of odd length\n", __func__));
248 goto bad_oro;
249 }
250 nopts = optlen / 2;
251
252 opts = (u16_t *)malloc(optlen);
253 if (opts == NULL) {
254 DPRINTF2(("%s: failed to allocate space for Option Request\n",
255 __func__));
256 goto bad_oro;
257 }
258
259 pbuf_copy_partial(p, opts, optlen, 0);
260 for (i = 0; i < nopts; ++i) {
261 opt = ntohs(opts[i]);
262 DPRINTF2(("> request option %u\n", opt));
263 };
264 free(opts);
265
266 bad_oro: /* empty */;
267 }
268
269 pbuf_header(p, -optlen); /* go to next option */
270 }
271 pbuf_free(p); /* done */
272
273
274 memcpy(dhcp6ds_reply_buf + roff, dhcp6ds_serverid, sizeof(dhcp6ds_serverid));
275 roff += sizeof(dhcp6ds_serverid);
276
277 memcpy(dhcp6ds_reply_buf + roff, dhcp6ds_dns, sizeof(dhcp6ds_dns));
278 roff += sizeof(dhcp6ds_dns);
279
280 q = pbuf_alloc(PBUF_RAW, roff, PBUF_RAM);
281 if (q == NULL) {
282 DPRINTF(("%s: pbuf_alloc(%d) failed\n", __func__, (int)roff));
283 return;
284 }
285
286 error = pbuf_take(q, dhcp6ds_reply_buf, roff);
287 if (error != ERR_OK) {
288 DPRINTF(("%s: pbuf_take(%d) failed: %s\n",
289 __func__, (int)roff, proxy_lwip_strerr(error)));
290 pbuf_free(q);
291 return;
292 }
293
294 error = udp_sendto_ip6(pcb, q, addr, port);
295 if (error != ERR_OK) {
296 DPRINTF(("%s: udp_sendto failed: %s\n",
297 __func__, proxy_lwip_strerr(error)));
298 }
299
300 pbuf_free(q);
301}
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