diff options
Diffstat (limited to 'networking/rptra6.c')
-rw-r--r-- | networking/rptra6.c | 614 |
1 files changed, 396 insertions, 218 deletions
diff --git a/networking/rptra6.c b/networking/rptra6.c index 9499125..c015134 100644 --- a/networking/rptra6.c +++ b/networking/rptra6.c @@ -8,10 +8,16 @@ #include <netinet/ip_icmp.h> #include <netinet/icmp6.h> -#define OPT_STRING "P:" +#define OPT_STRING "lP:r:" + +enum { + OPT_l = (1 << 0), +}; #define DBQ(str) "\"" #str "\"" +#define N_DNS 3 /* Number of DNS resolvers to keep track of */ + #define IN6ADDR_ALL_NODES_INIT { { { 0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } struct in6_addr in6addr_all_nodes = IN6ADDR_ALL_NODES_INIT; /* ff02::1 */ @@ -35,40 +41,394 @@ static void usage(void) exit(1); } -int rptra6_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; -int rptra6_main(int argc, char *argv[]) +static void do_resolv(char *str_resolv, char *str_resolv_new, + char *packet, ssize_t nrecv, + char dnscurr[N_DNS][INET6_ADDRSTRLEN], + time_t *dnsexpires) +{ + int i, olen, n_dns; + size_t o; + uint32_t lifetime; + struct nd_router_advert *ra; + struct nd_opt_hdr *oh; + struct opt_rdnss *rdnss; + FILE *f; + char namebuf[NI_MAXHOST]; + char dnsnext[N_DNS][INET6_ADDRSTRLEN]; + + ra= (struct nd_router_advert *)packet; + + /* Clear resolver list */ + for (n_dns= 0; n_dns < N_DNS; n_dns++) + strcpy(dnsnext[n_dns], ""); + + for (o= sizeof(*ra); o<nrecv;) + { + if (o+sizeof(*oh) > nrecv) + { + printf("partial option\n"); + break; + } + + oh= (struct nd_opt_hdr *)&packet[o]; + if (oh->nd_opt_len == 0) + { + printf("bad option length (0) at %ld\n", + (long)o); + break; + } + olen= oh->nd_opt_len * 8; + + switch(oh->nd_opt_type) + { + case OPT_RDNSS: /* 25 */ + + rdnss= (struct opt_rdnss *)oh; + lifetime= ntohl(rdnss->nd_opt_rdnss_lifetime); + /* Assume one year is infinite enough */ + if (lifetime == (uint32_t)-1) + lifetime= 365*24*3600; + + n_dns= 0; + + for (i= 8; i+16 <= olen; i+= 16) + { + if (lifetime == 0) + { + /* zero lifetime implies empty list */ + break; + } + inet_ntop(AF_INET6, ((char *)oh)+i, + namebuf, sizeof(namebuf)); + if (n_dns < N_DNS) + { + strcpy(dnsnext[n_dns], namebuf); + n_dns++; + } + } + + /* Check if the list of resolvers changed */ + for (n_dns= 0; n_dns < N_DNS; n_dns++) + { + if (strcmp(dnscurr[n_dns], + dnsnext[n_dns]) != 0) + { + break; + } + } + if (str_resolv && n_dns < N_DNS) + { + memcpy(dnscurr, dnsnext, + sizeof(dnsnext)); + + /* Ignore errors */ + f= fopen(str_resolv_new, "w"); + for (n_dns= 0; n_dns<N_DNS; n_dns++) + { + if (strlen(dnscurr[n_dns]) == 0) + break; + fprintf(f, "nameserver %s\n", + dnscurr[n_dns]); + } + fclose(f); + rename(str_resolv_new, str_resolv); + } + if (lifetime) + *dnsexpires= time(NULL) + lifetime; + else + *dnsexpires= 0; + + break; + } + + + o += olen; + } + + /* Check if we have to expire DNS entries */ + if (*dnsexpires && *dnsexpires < time(NULL)) + { + *dnsexpires= 0; + for (n_dns= 0; n_dns<N_DNS; n_dns++) + strcpy(dnscurr[n_dns], "!"); + if (str_resolv) + { + /* Ignore errors */ + f= fopen(str_resolv_new, "w"); + fclose(f); + rename(str_resolv_new, str_resolv); + } + } +} + +static void log_ra(char *out_name, char *new_name, + struct sockaddr_in6 *remotep, + struct msghdr *msgp, char *packet, ssize_t nrecv) { - int i, r, first, sock, on, nrecv, rcvd_ttl, olen; + int i, r, first, rcvd_ttl, olen; uint8_t flags_reserved; size_t o; - char *new_name, *out_name; + FILE *of; + struct cmsghdr *cmsgptr; + struct sockaddr_in6 *sin6p; + struct sockaddr_in6 loc_sin6; struct nd_router_advert *ra; struct nd_opt_hdr *oh; struct nd_opt_prefix_info *pi; struct nd_opt_mtu *mtup; struct opt_rdnss *rdnssp; + struct stat sb; + char namebuf[NI_MAXHOST]; + + of= fopen(new_name, "a"); + if (of == NULL) + { + fprintf(stderr, "unable to open '%s': %s\n", new_name, strerror(errno)); + exit(1); + } + + fprintf(of, "RESULT { " DBQ(id) ": " DBQ(9019) ", " DBQ(time) ": %ld", + (long)time(NULL)); + getnameinfo((struct sockaddr *)remotep, msgp->msg_namelen, + namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST); + fprintf(of, ", " DBQ(src) ": " DBQ(%s), namebuf); + + /* Set destination address of packet as local address */ + memset(&loc_sin6, '\0', sizeof(loc_sin6)); + for (cmsgptr= CMSG_FIRSTHDR(msgp); cmsgptr; + cmsgptr= CMSG_NXTHDR(msgp, cmsgptr)) + { + if (cmsgptr->cmsg_len == 0) + break; /* Can this happen? */ + if (cmsgptr->cmsg_level == IPPROTO_IPV6 && + cmsgptr->cmsg_type == IPV6_PKTINFO) + { + sin6p= &loc_sin6; + sin6p->sin6_family= AF_INET6; + sin6p->sin6_addr= ((struct in6_pktinfo *) + CMSG_DATA(cmsgptr))->ipi6_addr; + } + if (cmsgptr->cmsg_level == IPPROTO_IPV6 && + cmsgptr->cmsg_type == IPV6_HOPLIMIT) + { + rcvd_ttl= *(int *)CMSG_DATA(cmsgptr); + } + } + + if (memcmp(&loc_sin6.sin6_addr, &in6addr_all_nodes, + sizeof(loc_sin6.sin6_addr)) != 0) + { + getnameinfo((struct sockaddr *)&loc_sin6, sizeof(loc_sin6), + namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST); + fprintf(of, ", " DBQ(dst) ": " DBQ(%s), namebuf); + } + if (rcvd_ttl != 255) + fprintf(of, ", " DBQ(ttl) ": %d", rcvd_ttl); + + ra= (struct nd_router_advert *)packet; + fprintf(of, ", " DBQ(hop_limit) ": %d", ra->nd_ra_curhoplimit); + flags_reserved= ra->nd_ra_flags_reserved; + if (flags_reserved & ND_RA_FLAG_OTHER) + { + fprintf(of, ", " DBQ(other_conf) ": true"); + flags_reserved &= ~ND_RA_FLAG_OTHER; + } + switch(flags_reserved & RA_PREF_MASK) + { + case RA_PREF_HIGH: + fprintf(of, ", " DBQ(preference) ": " DBQ(high)); + flags_reserved &= ~RA_PREF_MASK; + break; + case RA_PREF_LOW: + fprintf(of, ", " DBQ(preference) ": " DBQ(low)); + flags_reserved &= ~RA_PREF_MASK; + break; + } + if (flags_reserved) + fprintf(of, ", " DBQ(reserved) ": 0x%x", flags_reserved); + fprintf(of, ", " DBQ(lifetime) ": %d", ntohs(ra->nd_ra_router_lifetime)); + if (ra->nd_ra_reachable) + fprintf(of, ", " DBQ(reachable_time) ": %d", ntohl(ra->nd_ra_reachable)); + if (ra->nd_ra_retransmit) + fprintf(of, ", " DBQ(retransmit_time) ": %d", ntohl(ra->nd_ra_retransmit)); + + fprintf(of, ", " DBQ(options) ": [ "); + first= 1; + for (o= sizeof(*ra); o<nrecv;) + { + if (!first) + fprintf(of, ", "); + else + first= 0; + + if (o+sizeof(*oh) > nrecv) + { + printf("partial option\n"); + break; + } + + oh= (struct nd_opt_hdr *)&packet[o]; + if (oh->nd_opt_len == 0) + { + printf("bad option length (0) at %ld\n", + (long)o); + break; + } + olen= oh->nd_opt_len * 8; + + switch(oh->nd_opt_type) + { + case ND_OPT_SOURCE_LINKADDR: /* 1 */ + fprintf(of, "{ " DBQ(type) ": " DBQ(link layer address) ", " + DBQ(addr) ": \""); + for (i= 2; i<olen; i++) + { + fprintf(of, "%s%02x", i == 2 ? "" : ":", + ((uint8_t *)oh)[i]); + } + fprintf(of, "\" }"); + break; + case ND_OPT_PREFIX_INFORMATION: /* 3 */ + if (olen < sizeof(*pi)) + { + printf( + "bad option length (%d) for prefix info\n", + oh->nd_opt_len); + break; + } + pi= (struct nd_opt_prefix_info *)oh; + fprintf(of, "{ " DBQ(prefix_len) ": %d", + pi->nd_opt_pi_prefix_len); + flags_reserved= pi->nd_opt_pi_flags_reserved; + if (flags_reserved & ND_OPT_PI_FLAG_ONLINK) + { + fprintf(of, ", " DBQ(onlink) ": true"); + flags_reserved &= ~ND_OPT_PI_FLAG_ONLINK; + } + if (flags_reserved & ND_OPT_PI_FLAG_AUTO) + { + fprintf(of, ", " DBQ(auto) ": true"); + flags_reserved &= ~ND_OPT_PI_FLAG_AUTO; + } + + if (flags_reserved) + { + fprintf(of, ", " DBQ(reserved1) ": 0x%x", flags_reserved); + } + fprintf(of, ", " DBQ(valid_time) ": %d", + ntohl(pi-> nd_opt_pi_valid_time)); + fprintf(of, ", " DBQ(preferred_time) ": %d", + ntohl(pi-> nd_opt_pi_preferred_time)); + if (pi-> nd_opt_pi_reserved2) + { + fprintf(of, ", " DBQ(reserved2) ": %d", + ntohl(pi-> nd_opt_pi_reserved2)); + } + + fprintf(of, ", " DBQ(prefix) ": " DBQ(%s) " }", + inet_ntop(AF_INET6, &pi->nd_opt_pi_prefix, + namebuf, sizeof(namebuf))); + break; + + case ND_OPT_MTU: /* 5 */ + fprintf(of, "{ " DBQ(type) ": " DBQ(mtu)); + mtup= (struct nd_opt_mtu *)oh; + if (mtup->nd_opt_mtu_reserved) + { + fprintf(of, ", " DBQ(reserved) ": 0x%x", + ntohs(mtup->nd_opt_mtu_reserved)); + } + fprintf(of, ", " DBQ(mtu) ": %d }", + ntohl(mtup->nd_opt_mtu_mtu)); + break; + + case OPT_RDNSS: /* 25 */ + fprintf(of, "{ " DBQ(type) ": " DBQ(rdnss)); + rdnssp= (struct opt_rdnss *)oh; + if (rdnssp->nd_opt_rdnss_reserved) + { + fprintf(of, ", " DBQ(reserved) ": %d", + ntohs(rdnssp->nd_opt_rdnss_reserved)); + } + fprintf(of, ", " DBQ(lifetime) ": %d", + ntohl(rdnssp->nd_opt_rdnss_lifetime)); + + fprintf(of, ", " DBQ(addrs) ": [ "); + for (i= 8; i+16 <= olen; i+= 16) + { + inet_ntop(AF_INET6, ((char *)oh)+i, + namebuf, sizeof(namebuf)); + fprintf(of, "%s" DBQ(%s), + i == 8 ? "" : ", ", + namebuf); + } + fprintf(of, " ] }"); + + break; + + default: + fprintf(of, "{ " DBQ(type_no) ": %d }", oh->nd_opt_type); + break; + } + + + o += olen; + } + fprintf(of, " ] }\n"); + + fclose(of); + + r= stat(out_name, &sb); + if (r == 0) + return; + if (errno == ENOENT) + { + rename(new_name, out_name); + return; + } + fprintf(stderr, "stat '%s' failed: %s\n", out_name, strerror(errno)); + exit(1); +} + +int rptra6_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; +int rptra6_main(int argc, char *argv[]) +{ + int i, sock, on, nrecv, do_log; + unsigned opts; + size_t len; + time_t dnsexpires; + char *new_name, *out_name, *str_resolv, *str_resolv_new; struct icmp6_hdr * icmp; - struct cmsghdr *cmsgptr; - struct sockaddr_in6 *sin6p; FILE *of; char *str_pidfile; - struct stat sb; struct sockaddr_in6 remote; /* responding internet address */ - struct sockaddr_in6 loc_sin6; struct msghdr msg; struct iovec iov[1]; - char namebuf[NI_MAXHOST]; + char dnscurr[N_DNS][INET6_ADDRSTRLEN]; char cmsgbuf[256]; char packet[4096]; str_pidfile= NULL; - (void) getopt32(argv, OPT_STRING, &str_pidfile); + str_resolv= NULL; + opts= getopt32(argv, OPT_STRING, &str_pidfile, &str_resolv); + + do_log= !!(opts & OPT_l); - if (argc != optind+2) - usage(); + if (do_log) + { + if (argc != optind+2) + usage(); - new_name= argv[optind]; - out_name= argv[optind+1]; + new_name= argv[optind]; + out_name= argv[optind+1]; + } + else + { + if (argc != optind) + usage(); + new_name= NULL; + out_name= NULL; + } if (str_pidfile) { @@ -79,6 +439,14 @@ int rptra6_main(int argc, char *argv[]) fclose(of); } } + + str_resolv_new= NULL; + if (str_resolv) + { + len= strlen(str_resolv) + 4 + 1; + str_resolv_new= malloc(len); + snprintf(str_resolv_new, len, "%s.new", str_resolv); + } of= NULL; @@ -98,6 +466,13 @@ int rptra6_main(int argc, char *argv[]) icmp = (struct icmp6_hdr *) packet; + /* Put something weird in the current list of DNS resolvers to + * trigger an update. + */ + for (i= 0; i<N_DNS; i++) + strcpy(dnscurr[i], "!"); + dnsexpires= 0; /* Currently, there is no DNS info */ + for(;;) { iov[0].iov_base= packet; @@ -139,213 +514,16 @@ int rptra6_main(int argc, char *argv[]) continue; } - of= fopen(new_name, "a"); - if (of == NULL) - { - fprintf(stderr, "unable to open '%s': %s\n", new_name, strerror(errno)); - exit(1); - } - - fprintf(of, "RESULT { " DBQ(id) ": " DBQ(9019) ", " DBQ(time) ": %ld", - (long)time(NULL)); - getnameinfo((struct sockaddr *)&remote, msg.msg_namelen, - namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST); - fprintf(of, ", " DBQ(src) ": " DBQ(%s), namebuf); - - /* Set destination address of packet as local address */ - memset(&loc_sin6, '\0', sizeof(loc_sin6)); - for (cmsgptr= CMSG_FIRSTHDR(&msg); cmsgptr; - cmsgptr= CMSG_NXTHDR(&msg, cmsgptr)) - { - if (cmsgptr->cmsg_len == 0) - break; /* Can this happen? */ - if (cmsgptr->cmsg_level == IPPROTO_IPV6 && - cmsgptr->cmsg_type == IPV6_PKTINFO) - { - sin6p= &loc_sin6; - sin6p->sin6_family= AF_INET6; - sin6p->sin6_addr= ((struct in6_pktinfo *) - CMSG_DATA(cmsgptr))->ipi6_addr; - } - if (cmsgptr->cmsg_level == IPPROTO_IPV6 && - cmsgptr->cmsg_type == IPV6_HOPLIMIT) - { - rcvd_ttl= *(int *)CMSG_DATA(cmsgptr); - } - } - - if (memcmp(&loc_sin6.sin6_addr, &in6addr_all_nodes, - sizeof(loc_sin6.sin6_addr)) != 0) - { - getnameinfo((struct sockaddr *)&loc_sin6, sizeof(loc_sin6), - namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST); - fprintf(of, ", " DBQ(dst) ": " DBQ(%s), namebuf); - } - if (rcvd_ttl != 255) - fprintf(of, ", " DBQ(ttl) ": %d", rcvd_ttl); - - ra= (struct nd_router_advert *)packet; - fprintf(of, ", " DBQ(hop_limit) ": %d", ra->nd_ra_curhoplimit); - flags_reserved= ra->nd_ra_flags_reserved; - if (flags_reserved & ND_RA_FLAG_OTHER) + if (do_log) { - fprintf(of, ", " DBQ(other_conf) ": true"); - flags_reserved &= ~ND_RA_FLAG_OTHER; + log_ra(out_name, new_name, &remote, &msg, + packet, nrecv); } - switch(flags_reserved & RA_PREF_MASK) + if (str_resolv) { - case RA_PREF_HIGH: - fprintf(of, ", " DBQ(preference) ": " DBQ(high)); - flags_reserved &= ~RA_PREF_MASK; - break; - case RA_PREF_LOW: - fprintf(of, ", " DBQ(preference) ": " DBQ(low)); - flags_reserved &= ~RA_PREF_MASK; - break; - } - if (flags_reserved) - fprintf(of, ", " DBQ(reserved) ": 0x%x", flags_reserved); - fprintf(of, ", " DBQ(lifetime) ": %d", ntohs(ra->nd_ra_router_lifetime)); - if (ra->nd_ra_reachable) - fprintf(of, ", " DBQ(reachable_time) ": %d", ntohl(ra->nd_ra_reachable)); - if (ra->nd_ra_retransmit) - fprintf(of, ", " DBQ(retransmit_time) ": %d", ntohl(ra->nd_ra_retransmit)); - - fprintf(of, ", " DBQ(options) ": [ "); - first= 1; - for (o= sizeof(*ra); o<nrecv;) - { - if (!first) - fprintf(of, ", "); - else - first= 0; - - if (o+sizeof(*oh) > nrecv) - { - printf("partial option\n"); - break; - } - - oh= (struct nd_opt_hdr *)&packet[o]; - if (oh->nd_opt_len == 0) - { - printf("bad option length (0) at %ld\n", - (long)o); - break; - } - olen= oh->nd_opt_len * 8; - - switch(oh->nd_opt_type) - { - case ND_OPT_SOURCE_LINKADDR: /* 1 */ - fprintf(of, "{ " DBQ(type) ": " DBQ(link layer address) ", " - DBQ(addr) ": \""); - for (i= 2; i<olen; i++) - { - fprintf(of, "%s%02x", i == 2 ? "" : ":", - ((uint8_t *)oh)[i]); - } - fprintf(of, "\" }"); - break; - case ND_OPT_PREFIX_INFORMATION: /* 3 */ - if (olen < sizeof(*pi)) - { - printf( - "bad option length (%d) for prefix info\n", - oh->nd_opt_len); - break; - } - pi= (struct nd_opt_prefix_info *)oh; - fprintf(of, "{ " DBQ(prefix_len) ": %d", - pi->nd_opt_pi_prefix_len); - flags_reserved= pi->nd_opt_pi_flags_reserved; - if (flags_reserved & ND_OPT_PI_FLAG_ONLINK) - { - fprintf(of, ", " DBQ(onlink) ": true"); - flags_reserved &= ~ND_OPT_PI_FLAG_ONLINK; - } - if (flags_reserved & ND_OPT_PI_FLAG_AUTO) - { - fprintf(of, ", " DBQ(auto) ": true"); - flags_reserved &= ~ND_OPT_PI_FLAG_AUTO; - } - - if (flags_reserved) - { - fprintf(of, ", " DBQ(reserved1) ": 0x%x", flags_reserved); - } - fprintf(of, ", " DBQ(valid_time) ": %d", - ntohl(pi-> nd_opt_pi_valid_time)); - fprintf(of, ", " DBQ(preferred_time) ": %d", - ntohl(pi-> nd_opt_pi_preferred_time)); - if (pi-> nd_opt_pi_reserved2) - { - fprintf(of, ", " DBQ(reserved2) ": %d", - ntohl(pi-> nd_opt_pi_reserved2)); - } - - fprintf(of, ", " DBQ(prefix) ": " DBQ(%s) " }", - inet_ntop(AF_INET6, &pi->nd_opt_pi_prefix, - namebuf, sizeof(namebuf))); - break; - - case ND_OPT_MTU: /* 5 */ - fprintf(of, "{ " DBQ(type) ": " DBQ(mtu)); - mtup= (struct nd_opt_mtu *)oh; - if (mtup->nd_opt_mtu_reserved) - { - fprintf(of, ", " DBQ(reserved) ": 0x%x", - ntohs(mtup->nd_opt_mtu_reserved)); - } - fprintf(of, ", " DBQ(mtu) ": %d }", - ntohl(mtup->nd_opt_mtu_mtu)); - break; - - case OPT_RDNSS: /* 25 */ - fprintf(of, "{ " DBQ(type) ": " DBQ(rdnss)); - rdnssp= (struct opt_rdnss *)oh; - if (rdnssp->nd_opt_rdnss_reserved) - { - fprintf(of, ", " DBQ(reserved) ": %d", - ntohs(rdnssp->nd_opt_rdnss_reserved)); - } - fprintf(of, ", " DBQ(lifetime) ": %d", - ntohl(rdnssp->nd_opt_rdnss_lifetime)); - - fprintf(of, ", " DBQ(addrs) ": [ "); - for (i= 8; i+16 <= olen; i+= 16) - { - inet_ntop(AF_INET6, ((char *)oh)+i, - namebuf, sizeof(namebuf)); - fprintf(of, "%s" DBQ(%s), - i == 8 ? "" : ", ", - namebuf); - } - fprintf(of, " ] }"); - break; - - default: - fprintf(of, "{ " DBQ(type_no) ": %d }", oh->nd_opt_type); - break; - } - - - o += olen; + do_resolv(str_resolv, str_resolv_new, packet, nrecv, + dnscurr, &dnsexpires); } - fprintf(of, " ] }\n"); - - fclose(of); - - r= stat(out_name, &sb); - if (r == 0) - continue; - if (errno == ENOENT) - { - rename(new_name, out_name); - continue; - } - fprintf(stderr, "stat '%s' failed: %s\n", out_name, strerror(errno)); - exit(1); } fprintf(stderr, "end of main\n"); |