2 * $Id: process.c,v 1.26.2.3 2011/08/22 12:30:47 reubenhwk Exp $
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),
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>.
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
,
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
);
40 flog(LOG_WARNING
, "received packet with no pkt_info from %s!", addr_str
);
48 if (len
< sizeof(struct icmp6_hdr
))
50 flog(LOG_WARNING
, "received icmpv6 packet with invalid length (%d) from %s",
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");
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",
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
);
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",
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
);
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
)
112 dlog(LOG_DEBUG
, 2, "received packet from unknown interface: %d",
113 pkt_info
->ipi6_ifindex
);
119 print_addr(&addr
->sin6_addr
, addr_str
);
120 flog(LOG_WARNING
, "received RS or RA with invalid hoplimit %d from %s",
125 if (!iface
->AdvSendAdvert
)
127 dlog(LOG_DEBUG
, 2, "AdvSendAdvert is off for %s", iface
->Name
);
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
);
146 process_rs(struct Interface
*iface
, unsigned char *msg
, int len
,
147 struct sockaddr_in6
*addr
)
155 len
-= sizeof(struct nd_router_solicit
);
157 opt_str
= (uint8_t *)(msg
+ sizeof(struct nd_router_solicit
));
165 flog(LOG_WARNING
, "trailing garbage in RS");
169 optlen
= (opt_str
[1] << 3);
173 flog(LOG_WARNING
, "zero length option in RS");
176 else if (optlen
> len
)
178 flog(LOG_WARNING
, "option length greater than total length in RS");
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");
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);
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
);
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
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
];
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
);
269 opt_str
= (uint8_t *)(msg
+ sizeof(struct nd_router_advert
));
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
];
283 int offset
, label_len
;
284 uint32_t preferred
, valid
, count
;
288 flog(LOG_ERR
, "trailing garbage in RA on %s from %s",
289 iface
->Name
, addr_str
);
293 optlen
= (opt_str
[1] << 3);
297 flog(LOG_ERR
, "zero length option in RA on %s from %s",
298 iface
->Name
, addr_str
);
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
);
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
);
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
;
328 if (prefix
->enabled
&&
329 (prefix
->PrefixLen
== pinfo
->nd_opt_pi_prefix_len
) &&
330 addr_match(&prefix
->Prefix
, &pinfo
->nd_opt_pi_prefix
,
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",
344 if (!prefix
->DecrementLifetimesFlag
&& preferred
!= prefix
->AdvPreferredLifetime
)
346 flog(LOG_WARNING
, "our AdvPreferredLifetime on"
347 " %s for %s doesn't agree with %s",
355 prefix
= prefix
->next
;
358 case ND_OPT_ROUTE_INFORMATION
:
359 /* not checked: these will very likely vary a lot */
361 case ND_OPT_SOURCE_LINKADDR
:
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
);
369 /* Mobile IPv6 extensions */
370 case ND_OPT_RTR_ADV_INTERVAL
:
371 case ND_OPT_HOME_AGENT_INFO
:
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 */
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
);
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
);
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
);
409 flog(LOG_ERR
, "invalid len %i in RDNSS option on %s from %s",
410 count
, iface
->Name
, addr_str
);
414 case ND_OPT_DNSSL_INFORMATION
:
415 dnsslinfo
= (struct nd_opt_dnssl_info_local
*) opt_str
;
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')
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
);
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
);
443 if (suffix
[0] != '\0')
445 strncat(suffix
, &dnsslinfo
->nd_opt_dnssli_suffixes
[offset
], label_len
);
450 dlog(LOG_DEBUG
, 1, "unknown option %d in RA on %s from %s",
451 (int)*opt_str
, iface
->Name
, addr_str
);
461 addr_match(struct in6_addr
*a1
, struct in6_addr
*a2
, int prefixlen
)
466 pdw
= prefixlen
>> 0x05; /* num of whole uint32_t in prefix */
467 pbi
= prefixlen
& 0x1f; /* num of bits in incomplete uint32_t in prefix */
471 if (memcmp(a1
, a2
, pdw
<< 2))
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
)