VirtualBox

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

Last change on this file since 52560 was 51574, checked in by vboxsync, 10 years ago

NAT/Net: #define LOG_GROUP LOG_GROUP_NAT_SERVICE

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