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