VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/NAT/proxy_rtadvd.c@ 67954

Last change on this file since 67954 was 62481, checked in by vboxsync, 8 years ago

(C) 2016

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 11.6 KB
Line 
1/* $Id: proxy_rtadvd.c 62481 2016-07-22 18:30:21Z vboxsync $ */
2/** @file
3 * NAT Network - IPv6 router advertisement daemon.
4 */
5
6/*
7 * Copyright (C) 2013-2016 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
22#include "proxy.h"
23
24#include "lwip/opt.h"
25#include "lwip/sys.h"
26#include "lwip/stats.h"
27#include "lwip/timers.h"
28
29#include "lwip/inet_chksum.h"
30#include "lwip/icmp6.h"
31#include "lwip/nd6.h"
32
33#include "lwip/raw.h"
34
35#include <string.h>
36
37
38static void proxy_rtadvd_timer(void *);
39static void proxy_rtadvd_send_multicast(struct netif *);
40static void proxy_rtadvd_fill_payload(struct netif *, int);
41
42static u8_t rtadvd_recv(void *, struct raw_pcb *, struct pbuf *, ip6_addr_t *);
43
44
45/* ff02::1 - link-local all nodes multicast address */
46static ip6_addr_t allnodes_linklocal = {
47 { PP_HTONL(0xff020000UL), 0, 0, PP_HTONL(0x00000001UL) }
48};
49
50
51/*
52 * Unsolicited Router Advertisement payload.
53 *
54 * NB: Since ICMP checksum covers pseudo-header with destination
55 * address (link-local allnodes multicast in this case) this payload
56 * cannot be used for solicited replies to unicast addresses.
57 */
58static unsigned int unsolicited_ra_payload_length;
59static u8_t unsolicited_ra_payload[
60 sizeof(struct ra_header)
61 /* reserves enough space for NETIF_MAX_HWADDR_LEN */
62 + sizeof(struct lladdr_option)
63 /* we only announce one prefix */
64 + sizeof(struct prefix_option) * 1
65];
66
67
68static int ndefaults = 0;
69
70static struct raw_pcb *rtadvd_pcb;
71
72
73void
74proxy_rtadvd_start(struct netif *proxy_netif)
75{
76#if 0 /* XXX */
77 ndefaults = rtmon_get_defaults();
78#else
79 ndefaults = g_proxy_options->ipv6_defroute;
80#endif
81 if (ndefaults < 0) {
82 DPRINTF0(("rtadvd: failed to read IPv6 routing table, aborting\n"));
83 return;
84 }
85
86 proxy_rtadvd_fill_payload(proxy_netif, ndefaults > 0);
87
88 rtadvd_pcb = raw_new_ip6(IP6_NEXTH_ICMP6);
89 if (rtadvd_pcb == NULL) {
90 DPRINTF0(("rtadvd: failed to allocate pcb, aborting\n"));
91 return;
92 }
93
94 /*
95 * We cannot use raw_bind_ip6() since raw_input() doesn't grok
96 * multicasts. We are going to use ip6_output_if() directly.
97 */
98 raw_recv_ip6(rtadvd_pcb, rtadvd_recv, proxy_netif);
99
100 sys_timeout(3 * 1000, proxy_rtadvd_timer, proxy_netif);
101}
102
103
104static int quick_ras = 2;
105
106
107/**
108 * lwIP thread callback invoked when we start/stop advertising default
109 * route.
110 */
111void
112proxy_rtadvd_do_quick(void *arg)
113{
114 struct netif *proxy_netif = (struct netif *)arg;
115
116 quick_ras = 2;
117 sys_untimeout(proxy_rtadvd_timer, proxy_netif);
118 proxy_rtadvd_timer(proxy_netif); /* sends and re-arms */
119}
120
121
122static void
123proxy_rtadvd_timer(void *arg)
124{
125 struct netif *proxy_netif = (struct netif *)arg;
126 int newdefs;
127 u32_t delay;
128
129#if 0 /* XXX */
130 newdefs = rtmon_get_defaults();
131#else
132 newdefs = g_proxy_options->ipv6_defroute;
133#endif
134 if (newdefs != ndefaults && newdefs != -1) {
135 ndefaults = newdefs;
136 proxy_rtadvd_fill_payload(proxy_netif, ndefaults > 0);
137 }
138
139 proxy_rtadvd_send_multicast(proxy_netif);
140
141 if (quick_ras > 0) {
142 --quick_ras;
143 delay = 16 * 1000;
144 }
145 else {
146 delay = 600 * 1000;
147 }
148
149 sys_timeout(delay, proxy_rtadvd_timer, proxy_netif);
150}
151
152
153/*
154 * This should be folded into icmp6/nd6 input, but I don't want to
155 * solve this in general, making it configurable, etc.
156 *
157 * Cf. RFC 4861:
158 * 6.1.1. Validation of Router Solicitation Messages
159 */
160static u8_t
161rtadvd_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, ip6_addr_t *addr)
162{
163 enum raw_recv_status { RAW_RECV_CONTINUE = 0, RAW_RECV_CONSUMED = 1 };
164
165 struct netif *proxy_netif = (struct netif *)arg;
166 struct ip6_hdr *ip6_hdr;
167 struct icmp6_hdr *icmp6_hdr;
168 struct lladdr_option *lladdr_opt;
169 void *option;
170 u8_t opttype, optlen8;
171
172 LWIP_UNUSED_ARG(pcb);
173 LWIP_UNUSED_ARG(addr);
174
175 /* save a pointer to IP6 header and skip to ICMP6 payload */
176 ip6_hdr = (struct ip6_hdr *)p->payload;
177 pbuf_header(p, -ip_current_header_tot_len());
178
179 if (p->len < sizeof(struct icmp6_hdr)) {
180 ICMP6_STATS_INC(icmp6.lenerr);
181 goto drop;
182 }
183
184 if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len,
185 ip6_current_src_addr(),
186 ip6_current_dest_addr()) != 0)
187 {
188 ICMP6_STATS_INC(icmp6.chkerr);
189 goto drop;
190 }
191
192 icmp6_hdr = (struct icmp6_hdr *)p->payload;
193 if (icmp6_hdr->type != ICMP6_TYPE_RS) {
194 pbuf_header(p, ip_current_header_tot_len()); /* restore payload ptr */
195 return RAW_RECV_CONTINUE; /* not interested */
196 }
197
198 /* only now that we know it's ICMP6_TYPE_RS we can check IP6 hop limit */
199 if (IP6H_HOPLIM(ip6_hdr) != 255) {
200 ICMP6_STATS_INC(icmp6.proterr);
201 goto drop;
202 }
203
204 /* future, backward-incompatible changes may use different Code values. */
205 if (icmp6_hdr->code != 0) {
206 ICMP6_STATS_INC(icmp6.proterr);
207 goto drop;
208 }
209
210 /* skip past rs_header, nothing interesting in it */
211 if (p->len < sizeof(struct rs_header)) {
212 ICMP6_STATS_INC(icmp6.lenerr);
213 goto drop;
214 }
215 pbuf_header(p, -(s16_t)sizeof(struct rs_header));
216
217 lladdr_opt = NULL;
218 while (p->len > 0) {
219 int optlen;
220
221 if (p->len < 8) {
222 ICMP6_STATS_INC(icmp6.lenerr);
223 goto drop;
224 }
225
226 option = p->payload;
227 opttype = ((u8_t *)option)[0];
228 optlen8 = ((u8_t *)option)[1]; /* in units of 8 octets */
229
230 if (optlen8 == 0) {
231 ICMP6_STATS_INC(icmp6.proterr);
232 goto drop;
233 }
234
235 optlen = (unsigned int)optlen8 << 3;
236 if (p->len < optlen) {
237 ICMP6_STATS_INC(icmp6.lenerr);
238 goto drop;
239 }
240
241 if (opttype == ND6_OPTION_TYPE_SOURCE_LLADDR) {
242 if (lladdr_opt != NULL) { /* duplicate */
243 ICMP6_STATS_INC(icmp6.proterr);
244 goto drop;
245 }
246 lladdr_opt = (struct lladdr_option *)option;
247 }
248
249 pbuf_header(p, -optlen);
250 }
251
252 if (ip6_addr_isany(ip6_current_src_addr())) {
253 if (lladdr_opt != NULL) {
254 ICMP6_STATS_INC(icmp6.proterr);
255 goto drop;
256 }
257
258 /* reply with multicast RA */
259 }
260 else {
261 /*
262 * XXX: Router is supposed to update its Neighbor Cache (6.2.6),
263 * but it's hidden inside nd6.c.
264 */
265
266 /* may reply with either unicast or multicast RA */
267 }
268 /* we just always reply with multicast RA */
269
270 pbuf_free(p); /* NB: this invalidates lladdr_option */
271
272 sys_untimeout(proxy_rtadvd_timer, proxy_netif);
273 proxy_rtadvd_timer(proxy_netif); /* sends and re-arms */
274
275 return RAW_RECV_CONSUMED;
276
277 drop:
278 pbuf_free(p);
279 ICMP6_STATS_INC(icmp6.drop);
280 return RAW_RECV_CONSUMED;
281}
282
283
284static void
285proxy_rtadvd_send_multicast(struct netif *proxy_netif)
286{
287 struct pbuf *ph, *pp;
288 err_t error;
289
290 ph = pbuf_alloc(PBUF_IP, 0, PBUF_RAM);
291 if (ph == NULL) {
292 DPRINTF0(("%s: failed to allocate RA header pbuf\n", __func__));
293 return;
294 }
295
296 pp = pbuf_alloc(PBUF_RAW, unsolicited_ra_payload_length, PBUF_ROM);
297 if (pp == NULL) {
298 DPRINTF0(("%s: failed to allocate RA payload pbuf\n", __func__));
299 pbuf_free(ph);
300 return;
301 }
302 pp->payload = unsolicited_ra_payload;
303 pbuf_chain(ph, pp);
304
305 error = ip6_output_if(ph,
306 netif_ip6_addr(proxy_netif, 0), /* src: link-local */
307 &allnodes_linklocal, /* dst */
308 255, /* hop limit */
309 0, /* traffic class */
310 IP6_NEXTH_ICMP6,
311 proxy_netif);
312 if (error != ERR_OK) {
313 DPRINTF0(("%s: failed to send RA (err=%d)\n", __func__, error));
314 }
315
316 pbuf_free(pp);
317 pbuf_free(ph);
318}
319
320
321/*
322 * XXX: TODO: Only ra_header::router_lifetime (and hence
323 * ra_header::chksum) need to be changed, so we can precompute it once
324 * and then only update these two fields.
325 */
326static void
327proxy_rtadvd_fill_payload(struct netif *proxy_netif, int is_default)
328{
329 struct pbuf *p;
330 struct ra_header *ra_hdr;
331 struct lladdr_option *lladdr_opt;
332 struct prefix_option *pfx_opt;
333 unsigned int lladdr_optlen;
334
335 LWIP_ASSERT("netif hwaddr too long",
336 proxy_netif->hwaddr_len <= NETIF_MAX_HWADDR_LEN);
337
338 /* type + length + ll addr + round up to 8 octets */
339 lladdr_optlen = (2 + proxy_netif->hwaddr_len + 7) & ~0x7;
340
341 /* actual payload length */
342 unsolicited_ra_payload_length =
343 sizeof(struct ra_header)
344 + lladdr_optlen
345 + sizeof(struct prefix_option) * 1;
346
347 /* Set fields. */
348 ra_hdr = (struct ra_header *)unsolicited_ra_payload;
349 lladdr_opt = (struct lladdr_option *)((u8_t *)ra_hdr + sizeof(struct ra_header));
350 pfx_opt = (struct prefix_option *)((u8_t *)lladdr_opt + lladdr_optlen);
351
352 memset(unsolicited_ra_payload, 0, sizeof(unsolicited_ra_payload));
353
354 ra_hdr->type = ICMP6_TYPE_RA;
355
356#if 0
357 /*
358 * "M" flag. Tell guests to use stateful DHCP6. Disabled here
359 * since we don't provide stateful server.
360 */
361 ra_hdr->flags |= ND6_RA_FLAG_MANAGED_ADDR_CONFIG;
362#endif
363 /*
364 * XXX: TODO: Disable "O" flag for now to match disabled stateless
365 * server. We don't yet get IPv6 nameserver addresses from
366 * HostDnsService, so we have nothing to say, don't tell guests to
367 * come asking.
368 */
369#if 0
370 /*
371 * "O" flag. Tell guests to use DHCP6 for DNS and the like. This
372 * is served by simple stateless server (RFC 3736).
373 *
374 * XXX: "STATEFUL" in the flag name was probably a bug in RFC2461.
375 * It's present in the text, but not in the router configuration
376 * variable name. It's dropped in the text in RFC4861.
377 */
378 ra_hdr->flags |= ND6_RA_FLAG_OTHER_STATEFUL_CONFIG;
379#endif
380
381 if (is_default) {
382 ra_hdr->router_lifetime = PP_HTONS(1200); /* seconds */
383 }
384 else {
385 ra_hdr->router_lifetime = 0;
386 }
387
388 lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR;
389 lladdr_opt->length = lladdr_optlen >> 3; /* in units of 8 octets */
390 memcpy(lladdr_opt->addr, proxy_netif->hwaddr, proxy_netif->hwaddr_len);
391
392 pfx_opt->type = ND6_OPTION_TYPE_PREFIX_INFO;
393 pfx_opt->length = 4;
394 pfx_opt->prefix_length = 64;
395 pfx_opt->flags = ND6_PREFIX_FLAG_ON_LINK
396 | ND6_PREFIX_FLAG_AUTONOMOUS;
397 pfx_opt->valid_lifetime = ~0U; /* infinite */
398 pfx_opt->preferred_lifetime = ~0U; /* infinite */
399 pfx_opt->prefix.addr[0] = netif_ip6_addr(proxy_netif, 1)->addr[0];
400 pfx_opt->prefix.addr[1] = netif_ip6_addr(proxy_netif, 1)->addr[1];
401
402
403 /* we need a temp pbuf to calculate the checksum */
404 p = pbuf_alloc(PBUF_IP, unsolicited_ra_payload_length, PBUF_ROM);
405 if (p == NULL) {
406 DPRINTF0(("rtadvd: failed to allocate RA pbuf\n"));
407 return;
408 }
409 p->payload = unsolicited_ra_payload;
410
411 ra_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len,
412 /* src addr: netif's link-local */
413 netif_ip6_addr(proxy_netif, 0),
414 /* dst addr */
415 &allnodes_linklocal);
416 pbuf_free(p);
417}
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