VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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