VirtualBox

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

Last change on this file since 94701 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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