VirtualBox

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

Last change on this file since 52934 was 51653, checked in by vboxsync, 11 years ago

NAT/Net: add missing newlines to DPRINTFs.

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