Merge branch 'tomato-ND-USBmod' into tomato-RT
[tomato.git] / release / src / router / radvd / process.c
blob75e6aae587f44ec033eedbcff5ff37eed062359e
1 /*
2 * $Id: process.c,v 1.26.2.3 2011/08/22 12:30:47 reubenhwk Exp $
4 * Authors:
5 * Pedro Roque <roque@di.fc.ul.pt>
6 * Lars Fenneberg <lf@elemental.net>
8 * This software is Copyright 1996,1997 by the above mentioned author(s),
9 * All Rights Reserved.
11 * The license which is distributed with this software in the file COPYRIGHT
12 * applies to this software. If your distribution is missing this file, you
13 * may request it from <pekkas@netcore.fi>.
17 #include "config.h"
18 #include "includes.h"
19 #include "radvd.h"
21 static void process_rs(struct Interface *, unsigned char *msg,
22 int len, struct sockaddr_in6 *);
23 static void process_ra(struct Interface *, unsigned char *msg, int len,
24 struct sockaddr_in6 *);
25 static int addr_match(struct in6_addr *a1, struct in6_addr *a2,
26 int prefixlen);
28 void
29 process(struct Interface *ifacel, unsigned char *msg, int len,
30 struct sockaddr_in6 *addr, struct in6_pktinfo *pkt_info, int hoplimit)
32 struct Interface *iface;
33 struct icmp6_hdr *icmph;
34 char addr_str[INET6_ADDRSTRLEN];
36 print_addr(&addr->sin6_addr, addr_str);
38 if ( ! pkt_info )
40 flog(LOG_WARNING, "received packet with no pkt_info from %s!", addr_str );
41 return;
45 * can this happen?
48 if (len < sizeof(struct icmp6_hdr))
50 flog(LOG_WARNING, "received icmpv6 packet with invalid length (%d) from %s",
51 len, addr_str);
52 return;
55 icmph = (struct icmp6_hdr *) msg;
57 if (icmph->icmp6_type != ND_ROUTER_SOLICIT &&
58 icmph->icmp6_type != ND_ROUTER_ADVERT)
61 * We just want to listen to RSs and RAs
64 flog(LOG_ERR, "icmpv6 filter failed");
65 return;
68 if (icmph->icmp6_type == ND_ROUTER_ADVERT)
70 if (len < sizeof(struct nd_router_advert)) {
71 flog(LOG_WARNING, "received icmpv6 RA packet with invalid length (%d) from %s",
72 len, addr_str);
73 return;
76 if (!IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) {
77 flog(LOG_WARNING, "received icmpv6 RA packet with non-linklocal source address from %s", addr_str);
78 return;
82 if (icmph->icmp6_type == ND_ROUTER_SOLICIT)
84 if (len < sizeof(struct nd_router_solicit)) {
85 flog(LOG_WARNING, "received icmpv6 RS packet with invalid length (%d) from %s",
86 len, addr_str);
87 return;
91 if (icmph->icmp6_code != 0)
93 flog(LOG_WARNING, "received icmpv6 RS/RA packet with invalid code (%d) from %s",
94 icmph->icmp6_code, addr_str);
95 return;
98 dlog(LOG_DEBUG, 4, "if_index %u", pkt_info->ipi6_ifindex);
100 /* get iface by received if_index */
102 for (iface = ifacel; iface; iface=iface->next)
104 if (iface->if_index == pkt_info->ipi6_ifindex)
106 break;
110 if (iface == NULL)
112 dlog(LOG_DEBUG, 2, "received packet from unknown interface: %d",
113 pkt_info->ipi6_ifindex);
114 return;
117 if (hoplimit != 255)
119 print_addr(&addr->sin6_addr, addr_str);
120 flog(LOG_WARNING, "received RS or RA with invalid hoplimit %d from %s",
121 hoplimit, addr_str);
122 return;
125 if (!iface->AdvSendAdvert)
127 dlog(LOG_DEBUG, 2, "AdvSendAdvert is off for %s", iface->Name);
128 return;
131 dlog(LOG_DEBUG, 4, "found Interface: %s", iface->Name);
133 if (icmph->icmp6_type == ND_ROUTER_SOLICIT)
135 dlog(LOG_DEBUG, 2, "received RS from %s", addr_str);
136 process_rs(iface, msg, len, addr);
138 else if (icmph->icmp6_type == ND_ROUTER_ADVERT)
140 dlog(LOG_DEBUG, 2, "received RA from %s", addr_str);
141 process_ra(iface, msg, len, addr);
145 static void
146 process_rs(struct Interface *iface, unsigned char *msg, int len,
147 struct sockaddr_in6 *addr)
149 double delay;
150 double next;
151 struct timeval tv;
152 uint8_t *opt_str;
154 /* validation */
155 len -= sizeof(struct nd_router_solicit);
157 opt_str = (uint8_t *)(msg + sizeof(struct nd_router_solicit));
159 while (len > 0)
161 int optlen;
163 if (len < 2)
165 flog(LOG_WARNING, "trailing garbage in RS");
166 return;
169 optlen = (opt_str[1] << 3);
171 if (optlen == 0)
173 flog(LOG_WARNING, "zero length option in RS");
174 return;
176 else if (optlen > len)
178 flog(LOG_WARNING, "option length greater than total length in RS");
179 return;
182 if (*opt_str == ND_OPT_SOURCE_LINKADDR &&
183 IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr)) {
184 flog(LOG_WARNING, "received icmpv6 RS packet with unspecified source address and there is a lladdr option");
185 return;
188 len -= optlen;
189 opt_str += optlen;
192 gettimeofday(&tv, NULL);
194 delay = MAX_RA_DELAY_TIME * rand() / (RAND_MAX +1.0);
196 if (iface->UnicastOnly) {
197 dlog(LOG_DEBUG, 5, "random mdelay for %s: %g seconds.", iface->Name, delay/1000.0);
198 mdelay(delay);
199 send_ra_forall(iface, &addr->sin6_addr);
201 else if ( timevaldiff(&tv, &iface->last_multicast) / 1000.0 < iface->MinDelayBetweenRAs ) {
202 /* last RA was sent only a few moments ago, don't send another immediately. */
203 next = iface->MinDelayBetweenRAs - (tv.tv_sec + tv.tv_usec / 1000000.0) + (iface->last_multicast.tv_sec + iface->last_multicast.tv_usec / 1000000.0) + delay/1000.0;
204 iface->next_multicast = next_timeval(next);
206 else {
207 /* no RA sent in a while, send a multicast reply */
208 send_ra_forall(iface, NULL);
209 next = rand_between(iface->MinRtrAdvInterval, iface->MaxRtrAdvInterval);
210 iface->next_multicast = next_timeval(next);
215 * check router advertisements according to RFC 4861, 6.2.7
217 static void
218 process_ra(struct Interface *iface, unsigned char *msg, int len,
219 struct sockaddr_in6 *addr)
221 struct nd_router_advert *radvert;
222 char addr_str[INET6_ADDRSTRLEN];
223 uint8_t *opt_str;
225 print_addr(&addr->sin6_addr, addr_str);
227 radvert = (struct nd_router_advert *) msg;
229 if ((radvert->nd_ra_curhoplimit && iface->AdvCurHopLimit) &&
230 (radvert->nd_ra_curhoplimit != iface->AdvCurHopLimit))
232 flog(LOG_WARNING, "our AdvCurHopLimit on %s doesn't agree with %s",
233 iface->Name, addr_str);
236 if ((radvert->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) && !iface->AdvManagedFlag)
238 flog(LOG_WARNING, "our AdvManagedFlag on %s doesn't agree with %s",
239 iface->Name, addr_str);
242 if ((radvert->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) && !iface->AdvOtherConfigFlag)
244 flog(LOG_WARNING, "our AdvOtherConfigFlag on %s doesn't agree with %s",
245 iface->Name, addr_str);
248 /* note: we don't check the default router preference here, because they're likely different */
250 if ((radvert->nd_ra_reachable && iface->AdvReachableTime) &&
251 (ntohl(radvert->nd_ra_reachable) != iface->AdvReachableTime))
253 flog(LOG_WARNING, "our AdvReachableTime on %s doesn't agree with %s",
254 iface->Name, addr_str);
257 if ((radvert->nd_ra_retransmit && iface->AdvRetransTimer) &&
258 (ntohl(radvert->nd_ra_retransmit) != iface->AdvRetransTimer))
260 flog(LOG_WARNING, "our AdvRetransTimer on %s doesn't agree with %s",
261 iface->Name, addr_str);
264 len -= sizeof(struct nd_router_advert);
266 if (len == 0)
267 return;
269 opt_str = (uint8_t *)(msg + sizeof(struct nd_router_advert));
271 while (len > 0)
273 int optlen;
274 struct nd_opt_prefix_info *pinfo;
275 struct nd_opt_rdnss_info_local *rdnssinfo;
276 struct nd_opt_dnssl_info_local *dnsslinfo;
277 struct nd_opt_mtu *mtu;
278 struct AdvPrefix *prefix;
279 struct AdvRDNSS *rdnss;
280 char prefix_str[INET6_ADDRSTRLEN];
281 char rdnss_str[INET6_ADDRSTRLEN];
282 char suffix[256];
283 int offset, label_len;
284 uint32_t preferred, valid, count;
286 if (len < 2)
288 flog(LOG_ERR, "trailing garbage in RA on %s from %s",
289 iface->Name, addr_str);
290 break;
293 optlen = (opt_str[1] << 3);
295 if (optlen == 0)
297 flog(LOG_ERR, "zero length option in RA on %s from %s",
298 iface->Name, addr_str);
299 break;
301 else if (optlen > len)
303 flog(LOG_ERR, "option length greater than total"
304 " length in RA on %s from %s",
305 iface->Name, addr_str);
306 break;
309 switch (*opt_str)
311 case ND_OPT_MTU:
312 mtu = (struct nd_opt_mtu *)opt_str;
314 if (iface->AdvLinkMTU && (ntohl(mtu->nd_opt_mtu_mtu) != iface->AdvLinkMTU))
316 flog(LOG_WARNING, "our AdvLinkMTU on %s doesn't agree with %s",
317 iface->Name, addr_str);
319 break;
320 case ND_OPT_PREFIX_INFORMATION:
321 pinfo = (struct nd_opt_prefix_info *) opt_str;
322 preferred = ntohl(pinfo->nd_opt_pi_preferred_time);
323 valid = ntohl(pinfo->nd_opt_pi_valid_time);
325 prefix = iface->AdvPrefixList;
326 while (prefix)
328 if (prefix->enabled &&
329 (prefix->PrefixLen == pinfo->nd_opt_pi_prefix_len) &&
330 addr_match(&prefix->Prefix, &pinfo->nd_opt_pi_prefix,
331 prefix->PrefixLen))
333 print_addr(&prefix->Prefix, prefix_str);
335 if (!prefix->DecrementLifetimesFlag && valid != prefix->AdvValidLifetime)
337 flog(LOG_WARNING, "our AdvValidLifetime on"
338 " %s for %s doesn't agree with %s",
339 iface->Name,
340 prefix_str,
341 addr_str
344 if (!prefix->DecrementLifetimesFlag && preferred != prefix->AdvPreferredLifetime)
346 flog(LOG_WARNING, "our AdvPreferredLifetime on"
347 " %s for %s doesn't agree with %s",
348 iface->Name,
349 prefix_str,
350 addr_str
355 prefix = prefix->next;
357 break;
358 case ND_OPT_ROUTE_INFORMATION:
359 /* not checked: these will very likely vary a lot */
360 break;
361 case ND_OPT_SOURCE_LINKADDR:
362 /* not checked */
363 break;
364 case ND_OPT_TARGET_LINKADDR:
365 case ND_OPT_REDIRECTED_HEADER:
366 flog(LOG_ERR, "invalid option %d in RA on %s from %s",
367 (int)*opt_str, iface->Name, addr_str);
368 break;
369 /* Mobile IPv6 extensions */
370 case ND_OPT_RTR_ADV_INTERVAL:
371 case ND_OPT_HOME_AGENT_INFO:
372 /* not checked */
373 break;
374 case ND_OPT_RDNSS_INFORMATION:
375 rdnssinfo = (struct nd_opt_rdnss_info_local *) opt_str;
376 count = rdnssinfo->nd_opt_rdnssi_len;
378 /* Check the RNDSS addresses received */
379 switch (count) {
380 case 7:
381 rdnss = iface->AdvRDNSSList;
382 if (!check_rdnss_presence(rdnss, &rdnssinfo->nd_opt_rdnssi_addr3 )) {
383 /* no match found in iface->AdvRDNSSList */
384 print_addr(&rdnssinfo->nd_opt_rdnssi_addr3, rdnss_str);
385 flog(LOG_WARNING, "RDNSS address %s received on %s from %s is not advertised by us",
386 rdnss_str, iface->Name, addr_str);
388 /* FALLTHROUGH */
389 case 5:
390 rdnss = iface->AdvRDNSSList;
391 if (!check_rdnss_presence(rdnss, &rdnssinfo->nd_opt_rdnssi_addr2 )) {
392 /* no match found in iface->AdvRDNSSList */
393 print_addr(&rdnssinfo->nd_opt_rdnssi_addr2, rdnss_str);
394 flog(LOG_WARNING, "RDNSS address %s received on %s from %s is not advertised by us",
395 rdnss_str, iface->Name, addr_str);
397 /* FALLTHROUGH */
398 case 3:
399 rdnss = iface->AdvRDNSSList;
400 if (!check_rdnss_presence(rdnss, &rdnssinfo->nd_opt_rdnssi_addr1 )) {
401 /* no match found in iface->AdvRDNSSList */
402 print_addr(&rdnssinfo->nd_opt_rdnssi_addr1, rdnss_str);
403 flog(LOG_WARNING, "RDNSS address %s received on %s from %s is not advertised by us",
404 rdnss_str, iface->Name, addr_str);
407 break;
408 default:
409 flog(LOG_ERR, "invalid len %i in RDNSS option on %s from %s",
410 count, iface->Name, addr_str);
413 break;
414 case ND_OPT_DNSSL_INFORMATION:
415 dnsslinfo = (struct nd_opt_dnssl_info_local *) opt_str;
416 suffix[0] = '\0';
417 for (offset = 0; offset < (dnsslinfo->nd_opt_dnssli_len-1)*8;) {
418 label_len = dnsslinfo->nd_opt_dnssli_suffixes[offset++];
420 if (label_len == 0) {
422 * Ignore empty suffixes. They're
423 * probably just padding...
425 if (suffix[0] == '\0')
426 continue;
428 if (!check_dnssl_presence(iface->AdvDNSSLList, suffix)) {
429 flog(LOG_WARNING, "DNSSL suffix %s received on %s from %s is not advertised by us",
430 suffix, iface->Name, addr_str);
433 suffix[0] = '\0';
434 continue;
437 if ((sizeof(suffix) - strlen(suffix)) < (label_len + 2)) {
438 flog(LOG_ERR, "oversized suffix in DNSSL option on %s from %s",
439 iface->Name, addr_str);
440 break;
443 if (suffix[0] != '\0')
444 strcat(suffix, ".");
445 strncat(suffix, &dnsslinfo->nd_opt_dnssli_suffixes[offset], label_len);
446 offset += label_len;
448 break;
449 default:
450 dlog(LOG_DEBUG, 1, "unknown option %d in RA on %s from %s",
451 (int)*opt_str, iface->Name, addr_str);
452 break;
455 len -= optlen;
456 opt_str += optlen;
460 static int
461 addr_match(struct in6_addr *a1, struct in6_addr *a2, int prefixlen)
463 unsigned int pdw;
464 unsigned int pbi;
466 pdw = prefixlen >> 0x05; /* num of whole uint32_t in prefix */
467 pbi = prefixlen & 0x1f; /* num of bits in incomplete uint32_t in prefix */
469 if (pdw)
471 if (memcmp(a1, a2, pdw << 2))
472 return 0;
475 if (pbi)
477 uint32_t w1, w2;
478 uint32_t mask;
480 w1 = *((uint32_t *)a1 + pdw);
481 w2 = *((uint32_t *)a2 + pdw);
483 mask = htonl(((uint32_t) 0xffffffff) << (0x20 - pbi));
485 if ((w1 ^ w2) & mask)
486 return 0;
489 return 1;