VirtualBox

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

Last change on this file since 49983 was 49016, checked in by vboxsync, 11 years ago

Change vestigial names proxytest.* to proxy.*

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
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 "proxy.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