1/* 2 * $Id: radvdump.c,v 1.18 2007/06/25 11:42:10 psavola Exp $ 3 * 4 * Authors: 5 * Lars Fenneberg <lf@elemental.net> 6 * Marko Myllynen <myllynen@lut.fi> 7 * 8 * This software is Copyright 1996-2000 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 21char usage_str[] = "[-vhfe] [-d level]"; 22 23#ifdef HAVE_GETOPT_LONG 24struct option prog_opt[] = { 25 {"debug", 1, 0, 'd'}, 26 {"file-format", 0, 0, 'f'}, 27 {"exclude-defaults", 0, 0, 'e'}, 28 {"version", 0, 0, 'v'}, 29 {"help", 0, 0, 'h'}, 30 {NULL, 0, 0, 0} 31}; 32#endif 33 34char *pname; 35int sock = -1; 36 37void version(void); 38void usage(void); 39void print_ff(unsigned char *, int, struct sockaddr_in6 *, int, unsigned int, int); 40void print_preferences(int); 41 42int 43main(int argc, char *argv[]) 44{ 45 unsigned char msg[MSG_SIZE]; 46 int c, len, hoplimit; 47 int edefs = 0; 48 struct sockaddr_in6 rcv_addr; 49 struct in6_pktinfo *pkt_info = NULL; 50#ifdef HAVE_GETOPT_LONG 51 int opt_idx; 52#endif 53 54 pname = ((pname=strrchr(argv[0],'/')) != NULL)?pname+1:argv[0]; 55 56 /* parse args */ 57#ifdef HAVE_GETOPT_LONG 58 while ((c = getopt_long(argc, argv, "d:fehv", prog_opt, &opt_idx)) > 0) 59#else 60 while ((c = getopt(argc, argv, "d:fehv")) > 0) 61#endif 62 { 63 switch (c) { 64 case 'd': 65 set_debuglevel(atoi(optarg)); 66 break; 67 case 'f': 68 break; 69 case 'e': 70 edefs = 1; 71 break; 72 case 'v': 73 version(); 74 break; 75 case 'h': 76 usage(); 77#ifdef HAVE_GETOPT_LONG 78 case ':': 79 fprintf(stderr, "%s: option %s: parameter expected\n", pname, 80 prog_opt[opt_idx].name); 81 exit(1); 82#endif 83 case '?': 84 exit(1); 85 } 86 } 87 88 if (log_open(L_STDERR, pname, NULL, 0) < 0) 89 exit(1); 90 91 /* get a raw socket for sending and receiving ICMPv6 messages */ 92 sock = open_icmpv6_socket(); 93 if (sock < 0) 94 exit(1); 95 96 for(;;) 97 { 98 len = recv_rs_ra(sock, msg, &rcv_addr, &pkt_info, &hoplimit); 99 if (len > 0) 100 { 101 struct icmp6_hdr *icmph; 102 103 /* 104 * can this happen? 105 */ 106 107 if (len < sizeof(struct icmp6_hdr)) 108 { 109 flog(LOG_WARNING, "received icmpv6 packet with invalid length: %d", 110 len); 111 exit(1); 112 } 113 114 icmph = (struct icmp6_hdr *) msg; 115 116 if (icmph->icmp6_type != ND_ROUTER_SOLICIT && 117 icmph->icmp6_type != ND_ROUTER_ADVERT) 118 { 119 /* 120 * We just want to listen to RSs and RAs 121 */ 122 123 flog(LOG_ERR, "icmpv6 filter failed"); 124 exit(1); 125 } 126 127 dlog(LOG_DEBUG, 4, "receiver if_index: %u", pkt_info->ipi6_ifindex); 128 129 if (icmph->icmp6_type == ND_ROUTER_SOLICIT) 130 { 131 /* not yet */ 132 } 133 else if (icmph->icmp6_type == ND_ROUTER_ADVERT) 134 print_ff(msg, len, &rcv_addr, hoplimit, (unsigned int)pkt_info->ipi6_ifindex, edefs); 135 } 136 else if (len == 0) 137 { 138 flog(LOG_ERR, "received zero lenght packet"); 139 exit(1); 140 } 141 else 142 { 143 flog(LOG_ERR, "recv_rs_ra: %s", strerror(errno)); 144 exit(1); 145 } 146 } 147 148 exit(0); 149} 150 151void 152print_ff(unsigned char *msg, int len, struct sockaddr_in6 *addr, int hoplimit, unsigned int if_index, int edefs) 153{ 154 /* XXX: hoplimit not being used for anything here.. */ 155 struct nd_router_advert *radvert; 156 char addr_str[INET6_ADDRSTRLEN]; 157 uint8_t *opt_str; 158 int orig_len = len; 159 char if_name[IFNAMSIZ] = ""; 160 161 print_addr(&addr->sin6_addr, addr_str); 162 printf("#\n# radvd configuration generated by radvdump %s\n", VERSION); 163 printf("# based on Router Advertisement from %s\n", addr_str); 164 if_indextoname(if_index, if_name); 165 printf("# received by interface %s\n", if_name); 166 printf("#\n\ninterface %s\n{\n\tAdvSendAdvert on;\n", if_name); 167 168 printf("\t# Note: {Min,Max}RtrAdvInterval cannot be obtained with radvdump\n"); 169 170 radvert = (struct nd_router_advert *) msg; 171 172 if (!edefs || DFLT_AdvManagedFlag != (ND_RA_FLAG_MANAGED == (radvert->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED))) 173 printf("\tAdvManagedFlag %s;\n", 174 (radvert->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)?"on":"off"); 175 176 if (!edefs || DFLT_AdvOtherConfigFlag != (ND_RA_FLAG_OTHER == (radvert->nd_ra_flags_reserved & ND_RA_FLAG_OTHER))) 177 printf("\tAdvOtherConfigFlag %s;\n", 178 (radvert->nd_ra_flags_reserved & ND_RA_FLAG_OTHER)?"on":"off"); 179 180 if (!edefs || DFLT_AdvReachableTime != ntohl(radvert->nd_ra_reachable)) 181 printf("\tAdvReachableTime %u;\n", ntohl(radvert->nd_ra_reachable)); 182 183 if (!edefs || DFLT_AdvRetransTimer != ntohl(radvert->nd_ra_retransmit)) 184 printf("\tAdvRetransTimer %u;\n", ntohl(radvert->nd_ra_retransmit)); 185 186 if (!edefs || DFLT_AdvCurHopLimit != radvert->nd_ra_curhoplimit) 187 printf("\tAdvCurHopLimit %u;\n", radvert->nd_ra_curhoplimit); 188 189 if (!edefs || (3*DFLT_MaxRtrAdvInterval) != ntohs(radvert->nd_ra_router_lifetime)) 190 printf("\tAdvDefaultLifetime %hu;\n", ntohs(radvert->nd_ra_router_lifetime)); 191 192 /* Mobile IPv6 ext */ 193 if (!edefs || DFLT_AdvHomeAgentFlag != (ND_RA_FLAG_HOME_AGENT == (radvert->nd_ra_flags_reserved & ND_RA_FLAG_HOME_AGENT))) 194 printf("\tAdvHomeAgentFlag %s;\n", 195 (radvert->nd_ra_flags_reserved & ND_RA_FLAG_HOME_AGENT)?"on":"off"); 196 197 /* Route Preferences and more specific routes */ 198 /* XXX two middlemost bits from 8 bit field */ 199 if (!edefs || (((radvert->nd_ra_flags_reserved & 0x18) >> 3) & 0xff) != DFLT_AdvDefaultPreference) { 200 printf("\tAdvDefaultPreference "); 201 print_preferences(((radvert->nd_ra_flags_reserved & 0x18) >> 3) & 0xff); 202 printf(";\n"); 203 } 204 205 len -= sizeof(struct nd_router_advert); 206 207 if (len == 0) 208 return; 209 210 opt_str = (uint8_t *)(msg + sizeof(struct nd_router_advert)); 211 212 while (len > 0) 213 { 214 int optlen; 215 struct nd_opt_mtu *mtu; 216 struct HomeAgentInfo *ha_info; 217 218 if (len < 2) 219 { 220 flog(LOG_ERR, "trailing garbage in RA from %s", 221 addr_str); 222 break; 223 } 224 225 optlen = (opt_str[1] << 3); 226 227 if (optlen == 0) 228 { 229 flog(LOG_ERR, "zero length option in RA"); 230 break; 231 } 232 else if (optlen > len) 233 { 234 flog(LOG_ERR, "option length greater than total" 235 " length in RA (type %d, optlen %d, len %d)", 236 (int)*opt_str, optlen, len); 237 break; 238 } 239 240 switch (*opt_str) 241 { 242 case ND_OPT_MTU: 243 mtu = (struct nd_opt_mtu *)opt_str; 244 245 if (!edefs || DFLT_AdvLinkMTU != ntohl(mtu->nd_opt_mtu_mtu)) 246 printf("\tAdvLinkMTU %u;\n", ntohl(mtu->nd_opt_mtu_mtu)); 247 break; 248 case ND_OPT_SOURCE_LINKADDR: 249 /* XXX: !DFLT depends on current DFLT_ value */ 250 if (!edefs || !DFLT_AdvSourceLLAddress) 251 printf("\tAdvSourceLLAddress on;\n"); 252 break; 253 /* Mobile IPv6 ext */ 254 case ND_OPT_RTR_ADV_INTERVAL: 255 /* XXX: !DFLT depends on current DFLT_ value */ 256 if (!edefs || !DFLT_AdvIntervalOpt) 257 printf("\tAdvIntervalOpt on;\n"); 258 break; 259 /* Mobile IPv6 ext */ 260 case ND_OPT_HOME_AGENT_INFO: 261 ha_info = (struct HomeAgentInfo *)opt_str; 262 263 /* XXX: we check DFLT_HomeAgentInfo by interface, and it's outside 264 of context here, so we always need to print it out.. */ 265 printf("\tAdvHomeAgentInfo on;\n"); 266 267 /* NEMO ext */ 268 if (!edefs || DFLT_AdvMobRtrSupportFlag != (ha_info->flags_reserved & ND_OPT_HAI_FLAG_SUPPORT_MR)) 269 printf("\tAdvMobRtrSupportFlag %s;\n", (ha_info->flags_reserved & ND_OPT_HAI_FLAG_SUPPORT_MR)?"on":"off"); 270 271 if (!edefs || DFLT_HomeAgentPreference != ntohs(ha_info->preference)) 272 printf("\tHomeAgentPreference %hu;\n", ntohs(ha_info->preference)); 273 /* Hum.. */ 274 if (!edefs || (3*DFLT_MaxRtrAdvInterval) != ntohs(ha_info->lifetime)) 275 printf("\tHomeAgentLifetime %hu;\n", ntohs(ha_info->lifetime)); 276 break; 277 case ND_OPT_TARGET_LINKADDR: 278 case ND_OPT_REDIRECTED_HEADER: 279 flog(LOG_ERR, "invalid option %d in RA", (int)*opt_str); 280 break; 281 case ND_OPT_PREFIX_INFORMATION: 282 break; 283 case ND_OPT_ROUTE_INFORMATION: 284 break; 285 case ND_OPT_RDNSS_INFORMATION: 286 break; 287 default: 288 dlog(LOG_DEBUG, 1, "unknown option %d in RA", 289 (int)*opt_str); 290 break; 291 } 292 293 len -= optlen; 294 opt_str += optlen; 295 } 296 297 orig_len -= sizeof(struct nd_router_advert); 298 299 if (orig_len == 0) 300 return; 301 302 opt_str = (uint8_t *)(msg + sizeof(struct nd_router_advert)); 303 304 while (orig_len > 0) 305 { 306 int optlen; 307 struct nd_opt_prefix_info *pinfo; 308 struct nd_opt_route_info_local *rinfo; 309 struct nd_opt_rdnss_info_local *rdnss_info; 310 char prefix_str[INET6_ADDRSTRLEN]; 311 312 if (orig_len < 2) 313 { 314 flog(LOG_ERR, "trailing garbage in RA from %s", 315 addr_str); 316 break; 317 } 318 319 optlen = (opt_str[1] << 3); 320 321 if (optlen == 0) 322 { 323 flog(LOG_ERR, "zero length option in RA"); 324 break; 325 } 326 else if (optlen > orig_len) 327 { 328 flog(LOG_ERR, "option length greater than total" 329 " length in RA (type %d, optlen %d, len %d)", 330 (int)*opt_str, optlen, orig_len); 331 break; 332 } 333 334 switch (*opt_str) 335 { 336 case ND_OPT_PREFIX_INFORMATION: 337 pinfo = (struct nd_opt_prefix_info *) opt_str; 338 339 print_addr(&pinfo->nd_opt_pi_prefix, prefix_str); 340 341 printf("\n\tprefix %s/%d\n\t{\n", prefix_str, pinfo->nd_opt_pi_prefix_len); 342 343 if (ntohl(pinfo->nd_opt_pi_valid_time) == 0xffffffff) 344 { 345 if (!edefs || DFLT_AdvValidLifetime != 0xffffffff) 346 printf("\t\tAdvValidLifetime infinity; # (0xffffffff)\n"); 347 } 348 else 349 { 350 if (!edefs || DFLT_AdvValidLifetime != ntohl(pinfo->nd_opt_pi_valid_time)) 351 printf("\t\tAdvValidLifetime %u;\n", ntohl(pinfo->nd_opt_pi_valid_time)); 352 } 353 if (ntohl(pinfo->nd_opt_pi_preferred_time) == 0xffffffff) 354 { 355 if (!edefs || DFLT_AdvPreferredLifetime != 0xffffffff) 356 printf("\t\tAdvPreferredLifetime infinity; # (0xffffffff)\n"); 357 } 358 else 359 { 360 if (!edefs || DFLT_AdvPreferredLifetime != ntohl(pinfo->nd_opt_pi_preferred_time)) 361 printf("\t\tAdvPreferredLifetime %u;\n", ntohl(pinfo->nd_opt_pi_preferred_time)); 362 } 363 364 if (!edefs || DFLT_AdvOnLinkFlag != (ND_OPT_PI_FLAG_ONLINK == (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK))) 365 printf("\t\tAdvOnLink %s;\n", 366 (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK)?"on":"off"); 367 368 if (!edefs || DFLT_AdvAutonomousFlag != (ND_OPT_PI_FLAG_AUTO == (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO))) 369 printf("\t\tAdvAutonomous %s;\n", 370 (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO)?"on":"off"); 371 372 /* Mobile IPv6 ext */ 373 if (!edefs || DFLT_AdvRouterAddr != (ND_OPT_PI_FLAG_RADDR == (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_RADDR))) 374 printf("\t\tAdvRouterAddr %s;\n", 375 (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_RADDR)?"on":"off"); 376 377 printf("\t}; # End of prefix definition\n\n"); 378 break; 379 case ND_OPT_ROUTE_INFORMATION: 380 rinfo = (struct nd_opt_route_info_local *) opt_str; 381 382 print_addr(&rinfo->nd_opt_ri_prefix, prefix_str); 383 384 printf("\n\troute %s/%d\n\t{\n", prefix_str, rinfo->nd_opt_ri_prefix_len); 385 386 if (!edefs || (((radvert->nd_ra_flags_reserved & 0x18) >> 3) & 0xff) != DFLT_AdvRoutePreference) { 387 printf("\t\tAdvRoutePreference "); 388 print_preferences(((rinfo->nd_opt_ri_flags_reserved & 0x18) >> 3) & 0xff); 389 printf(";\n"); 390 } 391 392 /* XXX: we check DFLT_AdvRouteLifetime by interface, and it's outside of context here */ 393 if (ntohl(rinfo->nd_opt_ri_lifetime) == 0xffffffff) 394 printf("\t\tAdvRouteLifetime infinity; # (0xffffffff)\n"); 395 else 396 printf("\t\tAdvRouteLifetime %u;\n", ntohl(rinfo->nd_opt_ri_lifetime)); 397 398 printf("\t}; # End of route definition\n\n"); 399 break; 400 case ND_OPT_RDNSS_INFORMATION: 401 rdnss_info = (struct nd_opt_rdnss_info_local *) opt_str; 402 403 printf("\n\tRDNSS"); 404 405 print_addr(&rdnss_info->nd_opt_rdnssi_addr1, prefix_str); 406 printf(" %s", prefix_str); 407 408 if (rdnss_info->nd_opt_rdnssi_len >= 5) { 409 print_addr(&rdnss_info->nd_opt_rdnssi_addr2, prefix_str); 410 printf(" %s", prefix_str); 411 } 412 if (rdnss_info->nd_opt_rdnssi_len >= 7) { 413 print_addr(&rdnss_info->nd_opt_rdnssi_addr3, prefix_str); 414 printf(" %s", prefix_str); 415 } 416 417 printf("\n\t{\n"); 418 if (!edefs 419 || ((rdnss_info->nd_opt_rdnssi_pref_flag_reserved & ND_OPT_RDNSSI_PREF_MASK) >> ND_OPT_RDNSSI_PREF_SHIFT) != DFLT_AdvRDNSSPreference) 420 printf("\t\tAdvRDNSSPreference %d;\n", 421 (rdnss_info->nd_opt_rdnssi_pref_flag_reserved & ND_OPT_RDNSSI_PREF_MASK) >> ND_OPT_RDNSSI_PREF_SHIFT); 422 423 if (!edefs 424 || ((rdnss_info->nd_opt_rdnssi_pref_flag_reserved & ND_OPT_RDNSSI_FLAG_S) == 0 ) == DFLT_AdvRDNSSOpenFlag) 425 printf("\t\tAdvRDNSSOpen %s;\n", rdnss_info->nd_opt_rdnssi_pref_flag_reserved & ND_OPT_RDNSSI_FLAG_S ? "on" : "off"); 426 427 /* as AdvRDNSSLifetime may depend on MaxRtrAdvInterval, it could change */ 428 if (ntohl(rdnss_info->nd_opt_rdnssi_lifetime) == 0xffffffff) 429 printf("\t\tAdvRDNSSLifetime infinity; # (0xffffffff)\n"); 430 else 431 printf("\t\tAdvRDNSSLifetime %u;\n", ntohl(rdnss_info->nd_opt_rdnssi_lifetime)); 432 433 printf("\t}; # End of RDNSS definition\n\n"); 434 break; 435 default: 436 break; 437 } 438 orig_len -= optlen; 439 opt_str += optlen; 440 } 441 442 printf("}; # End of interface definition\n"); 443 444 fflush(stdout); 445} 446 447void 448print_preferences(int p) 449{ 450 switch (p) { 451 case 0: 452 printf("medium"); 453 break; 454 case 1: 455 printf("high"); 456 break; 457 case 2: 458 /* reserved, ignore */ 459 break; 460 case 3: 461 printf("low"); 462 break; 463 } 464} 465 466void 467version(void) 468{ 469 fprintf(stderr,"Version: %s\n\n", VERSION); 470 fprintf(stderr,"Please send bug reports and suggestions to %s\n", 471 CONTACT_EMAIL); 472 exit(1); 473} 474 475void 476usage(void) 477{ 478 fprintf(stderr,"usage: %s %s\n", pname, usage_str); 479 exit(1); 480} 481