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 |
|
---|
22 | static void proxy_rtadvd_timer(void *);
|
---|
23 | static void proxy_rtadvd_send_multicast(struct netif *);
|
---|
24 | static void proxy_rtadvd_fill_payload(struct netif *, int);
|
---|
25 |
|
---|
26 | static u8_t rtadvd_recv(void *, struct raw_pcb *, struct pbuf *, ip6_addr_t *);
|
---|
27 |
|
---|
28 |
|
---|
29 | /* ff02::1 - link-local all nodes multicast address */
|
---|
30 | static 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 | */
|
---|
42 | static unsigned int unsolicited_ra_payload_length;
|
---|
43 | static 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 |
|
---|
52 | static int ndefaults = 0;
|
---|
53 |
|
---|
54 | static struct raw_pcb *rtadvd_pcb;
|
---|
55 |
|
---|
56 |
|
---|
57 | void
|
---|
58 | proxy_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 |
|
---|
88 | static int quick_ras = 2;
|
---|
89 |
|
---|
90 |
|
---|
91 | /**
|
---|
92 | * lwIP thread callback invoked when we start/stop advertising default
|
---|
93 | * route.
|
---|
94 | */
|
---|
95 | void
|
---|
96 | proxy_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 |
|
---|
106 | static void
|
---|
107 | proxy_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 | */
|
---|
144 | static u8_t
|
---|
145 | rtadvd_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 |
|
---|
268 | static void
|
---|
269 | proxy_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 | */
|
---|
310 | static void
|
---|
311 | proxy_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 | }
|
---|