1/* 2 * $Id: process.c,v 1.17 2009/06/19 07:34:07 psavola Exp $ 3 * 4 * Authors: 5 * Pedro Roque <roque@di.fc.ul.pt> 6 * Lars Fenneberg <lf@elemental.net> 7 * 8 * This software is Copyright 1996,1997 by the above mentioned author(s), 9 * All Rights Reserved. 10 * 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>. 14 * 15 */ 16 17#include <config.h> 18#include <includes.h> 19#include <radvd.h> 20 21static void process_rs(int, struct Interface *, unsigned char *msg, 22 int len, struct sockaddr_in6 *); 23static void process_ra(struct Interface *, unsigned char *msg, int len, 24 struct sockaddr_in6 *); 25static int addr_match(struct in6_addr *a1, struct in6_addr *a2, 26 int prefixlen); 27 28void 29process(int sock, struct Interface *ifacel, unsigned char *msg, int len, 30 struct sockaddr_in6 *addr, struct in6_pktinfo *pkt_info, int hoplimit) 31{ 32 struct Interface *iface; 33 struct icmp6_hdr *icmph; 34 char addr_str[INET6_ADDRSTRLEN]; 35 36 if ( ! pkt_info ) 37 { 38 flog(LOG_WARNING, "received packet with no pkt_info!" ); 39 return; 40 } 41 42 /* 43 * can this happen? 44 */ 45 46 if (len < sizeof(struct icmp6_hdr)) 47 { 48 flog(LOG_WARNING, "received icmpv6 packet with invalid length: %d", 49 len); 50 return; 51 } 52 53 icmph = (struct icmp6_hdr *) msg; 54 55 if (icmph->icmp6_type != ND_ROUTER_SOLICIT && 56 icmph->icmp6_type != ND_ROUTER_ADVERT) 57 { 58 /* 59 * We just want to listen to RSs and RAs 60 */ 61 62 flog(LOG_ERR, "icmpv6 filter failed"); 63 return; 64 } 65 66 if (icmph->icmp6_type == ND_ROUTER_ADVERT) 67 { 68 if (len < sizeof(struct nd_router_advert)) { 69 flog(LOG_WARNING, "received icmpv6 RA packet with invalid length: %d", 70 len); 71 return; 72 } 73 74 if (!IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) { 75 flog(LOG_WARNING, "received icmpv6 RA packet with non-linklocal source address"); 76 return; 77 } 78 } 79 80 if (icmph->icmp6_type == ND_ROUTER_SOLICIT) 81 { 82 if (len < sizeof(struct nd_router_solicit)) { 83 flog(LOG_WARNING, "received icmpv6 RS packet with invalid length: %d", 84 len); 85 return; 86 } 87 } 88 89 if (icmph->icmp6_code != 0) 90 { 91 flog(LOG_WARNING, "received icmpv6 RS/RA packet with invalid code: %d", 92 icmph->icmp6_code); 93 return; 94 } 95 96 dlog(LOG_DEBUG, 4, "if_index %u", pkt_info->ipi6_ifindex); 97 98 /* get iface by received if_index */ 99 100 for (iface = ifacel; iface; iface=iface->next) 101 { 102 if (iface->if_index == pkt_info->ipi6_ifindex) 103 { 104 break; 105 } 106 } 107 108 if (iface == NULL) 109 { 110 dlog(LOG_DEBUG, 2, "received packet from unknown interface: %d", 111 pkt_info->ipi6_ifindex); 112 return; 113 } 114 115 if (hoplimit != 255) 116 { 117 print_addr(&addr->sin6_addr, addr_str); 118 flog(LOG_WARNING, "received RS or RA with invalid hoplimit %d from %s", 119 hoplimit, addr_str); 120 return; 121 } 122 123 if (!iface->AdvSendAdvert) 124 { 125 dlog(LOG_DEBUG, 2, "AdvSendAdvert is off for %s", iface->Name); 126 return; 127 } 128 129 dlog(LOG_DEBUG, 4, "found Interface: %s", iface->Name); 130 131 if (icmph->icmp6_type == ND_ROUTER_SOLICIT) 132 { 133 process_rs(sock, iface, msg, len, addr); 134 } 135 else if (icmph->icmp6_type == ND_ROUTER_ADVERT) 136 { 137 process_ra(iface, msg, len, addr); 138 } 139} 140 141static void 142process_rs(int sock, struct Interface *iface, unsigned char *msg, int len, 143 struct sockaddr_in6 *addr) 144{ 145 double delay; 146 double next; 147 struct timeval tv; 148 uint8_t *opt_str; 149 150 /* validation */ 151 len -= sizeof(struct nd_router_solicit); 152 153 opt_str = (uint8_t *)(msg + sizeof(struct nd_router_solicit)); 154 155 while (len > 0) 156 { 157 int optlen; 158 159 if (len < 2) 160 { 161 flog(LOG_WARNING, "trailing garbage in RS"); 162 return; 163 } 164 165 optlen = (opt_str[1] << 3); 166 167 if (optlen == 0) 168 { 169 flog(LOG_WARNING, "zero length option in RS"); 170 return; 171 } 172 else if (optlen > len) 173 { 174 flog(LOG_WARNING, "option length greater than total length in RS"); 175 return; 176 } 177 178 if (*opt_str == ND_OPT_SOURCE_LINKADDR && 179 IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr)) { 180 flog(LOG_WARNING, "received icmpv6 RS packet with unspecified source address and there is a lladdr option"); 181 return; 182 } 183 184 len -= optlen; 185 opt_str += optlen; 186 } 187 188 gettimeofday(&tv, NULL); 189 190 delay = MAX_RA_DELAY_TIME*rand()/(RAND_MAX+1.0); 191 dlog(LOG_DEBUG, 3, "random mdelay for %s: %.2f", iface->Name, delay); 192 193 if (iface->UnicastOnly) { 194 mdelay(delay); 195 send_ra_forall(sock, iface, &addr->sin6_addr); 196 } 197 else if ((tv.tv_sec + tv.tv_usec / 1000000.0) - (iface->last_multicast_sec + 198 iface->last_multicast_usec / 1000000.0) < iface->MinDelayBetweenRAs) { 199 /* last RA was sent only a few moments ago, don't send another immediately */ 200 clear_timer(&iface->tm); 201 next = iface->MinDelayBetweenRAs - (tv.tv_sec + tv.tv_usec / 1000000.0) + 202 (iface->last_multicast_sec + iface->last_multicast_usec / 1000000.0) + delay/1000.0; 203 set_timer(&iface->tm, next); 204 } 205 else { 206 /* no RA sent in a while, send an immediate multicast reply */ 207 clear_timer(&iface->tm); 208 send_ra_forall(sock, iface, &addr->sin6_addr); 209 210 next = rand_between(iface->MinRtrAdvInterval, iface->MaxRtrAdvInterval); 211 set_timer(&iface->tm, next); 212 } 213} 214 215/* 216 * check router advertisements according to RFC 4861, 6.2.7 217 */ 218static void 219process_ra(struct Interface *iface, unsigned char *msg, int len, 220 struct sockaddr_in6 *addr) 221{ 222 struct nd_router_advert *radvert; 223 char addr_str[INET6_ADDRSTRLEN]; 224 uint8_t *opt_str; 225 226 print_addr(&addr->sin6_addr, addr_str); 227 228 radvert = (struct nd_router_advert *) msg; 229 230 if ((radvert->nd_ra_curhoplimit && iface->AdvCurHopLimit) && 231 (radvert->nd_ra_curhoplimit != iface->AdvCurHopLimit)) 232 { 233 flog(LOG_WARNING, "our AdvCurHopLimit on %s doesn't agree with %s", 234 iface->Name, addr_str); 235 } 236 237 if ((radvert->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) && !iface->AdvManagedFlag) 238 { 239 flog(LOG_WARNING, "our AdvManagedFlag on %s doesn't agree with %s", 240 iface->Name, addr_str); 241 } 242 243 if ((radvert->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) && !iface->AdvOtherConfigFlag) 244 { 245 flog(LOG_WARNING, "our AdvOtherConfigFlag on %s doesn't agree with %s", 246 iface->Name, addr_str); 247 } 248 249 /* note: we don't check the default router preference here, because they're likely different */ 250 251 if ((radvert->nd_ra_reachable && iface->AdvReachableTime) && 252 (ntohl(radvert->nd_ra_reachable) != iface->AdvReachableTime)) 253 { 254 flog(LOG_WARNING, "our AdvReachableTime on %s doesn't agree with %s", 255 iface->Name, addr_str); 256 } 257 258 if ((radvert->nd_ra_retransmit && iface->AdvRetransTimer) && 259 (ntohl(radvert->nd_ra_retransmit) != iface->AdvRetransTimer)) 260 { 261 flog(LOG_WARNING, "our AdvRetransTimer on %s doesn't agree with %s", 262 iface->Name, addr_str); 263 } 264 265 len -= sizeof(struct nd_router_advert); 266 267 if (len == 0) 268 return; 269 270 opt_str = (uint8_t *)(msg + sizeof(struct nd_router_advert)); 271 272 while (len > 0) 273 { 274 int optlen; 275 struct nd_opt_prefix_info *pinfo; 276 struct nd_opt_rdnss_info_local *rdnssinfo; 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 uint32_t preferred, valid, count; 283 284 if (len < 2) 285 { 286 flog(LOG_ERR, "trailing garbage in RA on %s from %s", 287 iface->Name, addr_str); 288 break; 289 } 290 291 optlen = (opt_str[1] << 3); 292 293 if (optlen == 0) 294 { 295 flog(LOG_ERR, "zero length option in RA on %s from %s", 296 iface->Name, addr_str); 297 break; 298 } 299 else if (optlen > len) 300 { 301 flog(LOG_ERR, "option length greater than total" 302 " length in RA on %s from %s", 303 iface->Name, addr_str); 304 break; 305 } 306 307 switch (*opt_str) 308 { 309 case ND_OPT_MTU: 310 mtu = (struct nd_opt_mtu *)opt_str; 311 312 if (iface->AdvLinkMTU && (ntohl(mtu->nd_opt_mtu_mtu) != iface->AdvLinkMTU)) 313 { 314 flog(LOG_WARNING, "our AdvLinkMTU on %s doesn't agree with %s", 315 iface->Name, addr_str); 316 } 317 break; 318 case ND_OPT_PREFIX_INFORMATION: 319 pinfo = (struct nd_opt_prefix_info *) opt_str; 320 preferred = ntohl(pinfo->nd_opt_pi_preferred_time); 321 valid = ntohl(pinfo->nd_opt_pi_valid_time); 322 323 prefix = iface->AdvPrefixList; 324 while (prefix) 325 { 326 if (prefix->enabled && 327 (prefix->PrefixLen == pinfo->nd_opt_pi_prefix_len) && 328 addr_match(&prefix->Prefix, &pinfo->nd_opt_pi_prefix, 329 prefix->PrefixLen)) 330 { 331 print_addr(&prefix->Prefix, prefix_str); 332 333 if (valid != prefix->AdvValidLifetime) 334 { 335 flog(LOG_WARNING, "our AdvValidLifetime on" 336 " %s for %s doesn't agree with %s", 337 iface->Name, 338 prefix_str, 339 addr_str 340 ); 341 } 342 if (preferred != prefix->AdvPreferredLifetime) 343 { 344 flog(LOG_WARNING, "our AdvPreferredLifetime on" 345 " %s for %s doesn't agree with %s", 346 iface->Name, 347 prefix_str, 348 addr_str 349 ); 350 } 351 } 352 353 prefix = prefix->next; 354 } 355 break; 356 case ND_OPT_ROUTE_INFORMATION: 357 /* not checked: these will very likely vary a lot */ 358 break; 359 case ND_OPT_SOURCE_LINKADDR: 360 /* not checked */ 361 break; 362 case ND_OPT_TARGET_LINKADDR: 363 case ND_OPT_REDIRECTED_HEADER: 364 flog(LOG_ERR, "invalid option %d in RA on %s from %s", 365 (int)*opt_str, iface->Name, addr_str); 366 break; 367 /* Mobile IPv6 extensions */ 368 case ND_OPT_RTR_ADV_INTERVAL: 369 case ND_OPT_HOME_AGENT_INFO: 370 /* not checked */ 371 break; 372 case ND_OPT_RDNSS_INFORMATION: 373 rdnssinfo = (struct nd_opt_rdnss_info_local *) opt_str; 374 count = rdnssinfo->nd_opt_rdnssi_len; 375 376 /* Check the RNDSS addresses received */ 377 switch (count) { 378 case 7: 379 rdnss = iface->AdvRDNSSList; 380 if (!check_rdnss_presence(rdnss, &rdnssinfo->nd_opt_rdnssi_addr3 )) { 381 /* no match found in iface->AdvRDNSSList */ 382 print_addr(&rdnssinfo->nd_opt_rdnssi_addr3, rdnss_str); 383 flog(LOG_WARNING, "RDNSS address %s received on %s from %s is not advertised by us", 384 rdnss_str, iface->Name, addr_str); 385 } 386 /* FALLTHROUGH */ 387 case 5: 388 rdnss = iface->AdvRDNSSList; 389 if (!check_rdnss_presence(rdnss, &rdnssinfo->nd_opt_rdnssi_addr2 )) { 390 /* no match found in iface->AdvRDNSSList */ 391 print_addr(&rdnssinfo->nd_opt_rdnssi_addr2, rdnss_str); 392 flog(LOG_WARNING, "RDNSS address %s received on %s from %s is not advertised by us", 393 rdnss_str, iface->Name, addr_str); 394 } 395 /* FALLTHROUGH */ 396 case 3: 397 rdnss = iface->AdvRDNSSList; 398 if (!check_rdnss_presence(rdnss, &rdnssinfo->nd_opt_rdnssi_addr1 )) { 399 /* no match found in iface->AdvRDNSSList */ 400 print_addr(&rdnssinfo->nd_opt_rdnssi_addr1, rdnss_str); 401 flog(LOG_WARNING, "RDNSS address %s received on %s from %s is not advertised by us", 402 rdnss_str, iface->Name, addr_str); 403 } 404 405 break; 406 default: 407 flog(LOG_ERR, "invalid len %i in RDNSS option on %s from %s", 408 count, iface->Name, addr_str); 409 } 410 411 break; 412 default: 413 dlog(LOG_DEBUG, 1, "unknown option %d in RA on %s from %s", 414 (int)*opt_str, iface->Name, addr_str); 415 break; 416 } 417 418 len -= optlen; 419 opt_str += optlen; 420 } 421} 422 423static int 424addr_match(struct in6_addr *a1, struct in6_addr *a2, int prefixlen) 425{ 426 unsigned int pdw; 427 unsigned int pbi; 428 429 pdw = prefixlen >> 0x05; /* num of whole uint32_t in prefix */ 430 pbi = prefixlen & 0x1f; /* num of bits in incomplete uint32_t in prefix */ 431 432 if (pdw) 433 { 434 if (memcmp(a1, a2, pdw << 2)) 435 return 0; 436 } 437 438 if (pbi) 439 { 440 uint32_t w1, w2; 441 uint32_t mask; 442 443 w1 = *((uint32_t *)a1 + pdw); 444 w2 = *((uint32_t *)a2 + pdw); 445 446 mask = htonl(((uint32_t) 0xffffffff) << (0x20 - pbi)); 447 448 if ((w1 ^ w2) & mask) 449 return 0; 450 } 451 452 return 1; 453} 454 455