From 0ea020e568507db76cd5693b4cb03bacda8bc702 Mon Sep 17 00:00:00 2001 From: Reuben Hawkins Date: Wed, 4 Jun 2014 12:33:58 -0500 Subject: [PATCH] code cleanup and reorganization --- interface.c | 2 +- netlink.c | 10 +- netlink.h | 4 +- process.c | 257 +++++++++++++------------ radvd.c | 538 ++++++++++++++++++++++++++++------------------------ radvd.h | 14 +- radvdump.c | 322 ++++++++++++++++--------------- 7 files changed, 609 insertions(+), 538 deletions(-) diff --git a/interface.c b/interface.c index dea8af1d..a9b7b8b7 100644 --- a/interface.c +++ b/interface.c @@ -23,7 +23,7 @@ void iface_init_defaults(struct Interface *iface) iface->cease_adv = 0; - iface->HasFailed = 0; + iface->ready = 0; iface->IgnoreIfMissing = DFLT_IgnoreIfMissing; iface->AdvSendAdvert = DFLT_AdvSendAdv; iface->MaxRtrAdvInterval = DFLT_MaxRtrAdvInterval; diff --git a/netlink.c b/netlink.c index 05a716ca..899675d1 100644 --- a/netlink.c +++ b/netlink.c @@ -32,7 +32,7 @@ #define SOL_NETLINK 270 #endif -void process_netlink_msg(int sock) +void process_netlink_msg(int sock, struct Interface * ifaces) { int rc = 0; @@ -77,7 +77,7 @@ void process_netlink_msg(int sock) } /* Reinit the interfaces which needs it. */ - struct Interface *iface = find_iface_by_index(interfaces, ifinfo->ifi_index); + struct Interface *iface = find_iface_by_index(ifaces, ifinfo->ifi_index); if (iface) { iface->racount = 0; reschedule_iface(iface, 0); @@ -104,7 +104,7 @@ void process_netlink_msg(int sock) ++rc; - struct Interface *iface = find_iface_by_index(interfaces, ifaddr->ifa_index); + struct Interface *iface = find_iface_by_index(ifaces, ifaddr->ifa_index); if (iface) { iface->racount = 0; reschedule_iface(iface, 0); @@ -115,8 +115,8 @@ void process_netlink_msg(int sock) int netlink_socket(void) { - int rc, sock; unsigned int val = 1; + int sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock == -1) { flog(LOG_ERR, "Unable to open netlink socket: %s", strerror(errno)); } @@ -130,7 +130,7 @@ int netlink_socket(void) snl.nl_family = AF_NETLINK; snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV6_IFADDR; - rc = bind(sock, (struct sockaddr *)&snl, sizeof(snl)); + int rc = bind(sock, (struct sockaddr *)&snl, sizeof(snl)); if (rc == -1) { flog(LOG_ERR, "Unable to bind netlink socket: %s", strerror(errno)); close(sock); diff --git a/netlink.h b/netlink.h index cf2a9992..e2b706e3 100644 --- a/netlink.h +++ b/netlink.h @@ -15,5 +15,7 @@ #pragma once -void process_netlink_msg(int sock); +#include "radvd.h" + +void process_netlink_msg(int sock, struct Interface * ifaces); int netlink_socket(void); diff --git a/process.c b/process.c index 681850cb..2cb187fd 100644 --- a/process.c +++ b/process.c @@ -24,7 +24,12 @@ static int addr_match(struct in6_addr *a1, struct in6_addr *a2, int prefixlen); void process(int sock, struct Interface *interfaces, unsigned char *msg, int len, struct sockaddr_in6 *addr, struct in6_pktinfo *pkt_info, int hoplimit) { - struct Interface *iface; + char if_namebuf[IF_NAMESIZE] = { "" }; + char *if_name = if_indextoname(pkt_info->ipi6_ifindex, if_namebuf); + if (!if_name) { + if_name = "unknown"; + } + dlog(LOG_DEBUG, 2, "received packet on interface: %d %s", pkt_info->ipi6_ifindex, if_name); char addr_str[INET6_ADDRSTRLEN]; addrtostr(&addr->sin6_addr, addr_str, sizeof(addr_str)); @@ -79,8 +84,6 @@ void process(int sock, struct Interface *interfaces, unsigned char *msg, int len return; } - dlog(LOG_DEBUG, 4, "if_index %u", pkt_info->ipi6_ifindex); - /* get iface by received if_index */ struct Interface *iface = find_iface_by_index(interfaces, pkt_info->ipi6_ifindex); @@ -90,21 +93,20 @@ void process(int sock, struct Interface *interfaces, unsigned char *msg, int len return; } - if (hoplimit != 255) { - flog(LOG_WARNING, "received RS or RA with invalid hoplimit %d from %s", hoplimit, addr_str); + if (!iface->ready && (0 != setup_iface(sock, iface))) { + flog(LOG_WARNING, "received RS or RA on %s but %s is not ready and setup_iface failed", iface->Name, + iface->Name); return; } - if (!iface->AdvSendAdvert) { - dlog(LOG_DEBUG, 2, "AdvSendAdvert is off for %s", iface->Name); + if (hoplimit != 255) { + flog(LOG_WARNING, "received RS or RA with invalid hoplimit %d from %s", hoplimit, addr_str); return; } - dlog(LOG_DEBUG, 4, "found Interface: %s", iface->Name); - if (icmph->icmp6_type == ND_ROUTER_SOLICIT) { dlog(LOG_DEBUG, 4, "received RS from %s on %s", addr_str, if_name); - process_rs(iface, msg, len, addr); + process_rs(sock, iface, msg, len, addr); } else if (icmph->icmp6_type == ND_ROUTER_ADVERT) { dlog(LOG_DEBUG, 4, "received RA from %s on %s", addr_str, if_name); process_ra(iface, msg, len, addr); @@ -150,7 +152,7 @@ static void process_rs(int sock, struct Interface *iface, unsigned char *msg, in double delay = MAX_RA_DELAY_TIME * rand() / (RAND_MAX + 1.0); if (iface->UnicastOnly) { - send_ra_forall(iface, &addr->sin6_addr); + send_ra_forall(sock, iface, &addr->sin6_addr); } else if (timevaldiff(&tv, &iface->last_multicast) / 1000.0 < iface->MinDelayBetweenRAs) { /* last RA was sent only a few moments ago, don't send another immediately. */ double next = @@ -161,7 +163,7 @@ static void process_rs(int sock, struct Interface *iface, unsigned char *msg, in reschedule_iface(iface, next); } else { /* no RA sent in a while, send a multicast reply */ - send_ra_forall(iface, NULL); + send_ra_forall(sock, iface, NULL); double next = rand_between(iface->MinRtrAdvInterval, iface->MaxRtrAdvInterval); reschedule_iface(iface, next); } @@ -210,17 +212,6 @@ static void process_ra(struct Interface *iface, unsigned char *msg, int len, str uint8_t *opt_str = (uint8_t *) (msg + sizeof(struct nd_router_advert)); while (len > 0) { - struct nd_opt_prefix_info *pinfo; - struct nd_opt_rdnss_info_local *rdnssinfo; - struct nd_opt_dnssl_info_local *dnsslinfo; - struct nd_opt_mtu *mtu; - struct AdvPrefix *prefix; - struct AdvRDNSS *rdnss; - char prefix_str[INET6_ADDRSTRLEN]; - char rdnss_str[INET6_ADDRSTRLEN]; - char suffix[256]; - unsigned int offset, label_len; - uint32_t preferred, valid, count; if (len < 2) { flog(LOG_ERR, "trailing garbage in RA on %s from %s", iface->Name, addr_str); @@ -239,39 +230,49 @@ static void process_ra(struct Interface *iface, unsigned char *msg, int len, str } switch (*opt_str) { - case ND_OPT_MTU: - mtu = (struct nd_opt_mtu *)opt_str; - if (len < sizeof(*mtu)) - return; + case ND_OPT_MTU:{ + struct nd_opt_mtu *mtu = (struct nd_opt_mtu *)opt_str; + if (len < sizeof(*mtu)) + return; - if (iface->AdvLinkMTU && (ntohl(mtu->nd_opt_mtu_mtu) != iface->AdvLinkMTU)) { - flog(LOG_WARNING, "our AdvLinkMTU on %s doesn't agree with %s", iface->Name, addr_str); + if (iface->AdvLinkMTU && (ntohl(mtu->nd_opt_mtu_mtu) != iface->AdvLinkMTU)) { + flog(LOG_WARNING, "our AdvLinkMTU on %s doesn't agree with %s", iface->Name, + addr_str); + } + break; } - break; - case ND_OPT_PREFIX_INFORMATION: - pinfo = (struct nd_opt_prefix_info *)opt_str; - if (len < sizeof(*pinfo)) - return; - preferred = ntohl(pinfo->nd_opt_pi_preferred_time); - valid = ntohl(pinfo->nd_opt_pi_valid_time); - - prefix = iface->AdvPrefixList; - while (prefix) { - if (prefix->enabled && - (prefix->PrefixLen == pinfo->nd_opt_pi_prefix_len) && addr_match(&prefix->Prefix, &pinfo->nd_opt_pi_prefix, prefix->PrefixLen)) { - addrtostr(&prefix->Prefix, prefix_str, sizeof(prefix_str)); - - if (!prefix->DecrementLifetimesFlag && valid != prefix->AdvValidLifetime) { - flog(LOG_WARNING, "our AdvValidLifetime on" " %s for %s doesn't agree with %s", iface->Name, prefix_str, addr_str); - } - if (!prefix->DecrementLifetimesFlag && preferred != prefix->AdvPreferredLifetime) { - flog(LOG_WARNING, "our AdvPreferredLifetime on" " %s for %s doesn't agree with %s", iface->Name, prefix_str, addr_str); + case ND_OPT_PREFIX_INFORMATION:{ + struct nd_opt_prefix_info *pinfo = (struct nd_opt_prefix_info *)opt_str; + if (len < sizeof(*pinfo)) + return; + int preferred = ntohl(pinfo->nd_opt_pi_preferred_time); + int valid = ntohl(pinfo->nd_opt_pi_valid_time); + + struct AdvPrefix *prefix = iface->AdvPrefixList; + while (prefix) { + char prefix_str[INET6_ADDRSTRLEN]; + if (prefix->enabled && (prefix->PrefixLen == pinfo->nd_opt_pi_prefix_len) + && addr_match(&prefix->Prefix, &pinfo->nd_opt_pi_prefix, prefix->PrefixLen)) { + addrtostr(&prefix->Prefix, prefix_str, sizeof(prefix_str)); + + if (!prefix->DecrementLifetimesFlag && valid != prefix->AdvValidLifetime) { + flog(LOG_WARNING, + "our AdvValidLifetime on" " %s for %s doesn't agree with %s", + iface->Name, prefix_str, addr_str); + } + if (!prefix->DecrementLifetimesFlag + && preferred != prefix->AdvPreferredLifetime) { + flog(LOG_WARNING, + "our AdvPreferredLifetime on" + " %s for %s doesn't agree with %s", iface->Name, prefix_str, + addr_str); + } } - } - prefix = prefix->next; + prefix = prefix->next; + } + break; } - break; case ND_OPT_ROUTE_INFORMATION: /* not checked: these will very likely vary a lot */ break; @@ -287,87 +288,103 @@ static void process_ra(struct Interface *iface, unsigned char *msg, int len, str case ND_OPT_HOME_AGENT_INFO: /* not checked */ break; - case ND_OPT_RDNSS_INFORMATION: - rdnssinfo = (struct nd_opt_rdnss_info_local *)opt_str; - if (len < sizeof(*rdnssinfo)) - return; - count = rdnssinfo->nd_opt_rdnssi_len; - - /* Check the RNDSS addresses received */ - switch (count) { - case 7: - rdnss = iface->AdvRDNSSList; - if (!check_rdnss_presence(rdnss, &rdnssinfo->nd_opt_rdnssi_addr3)) { - /* no match found in iface->AdvRDNSSList */ - addrtostr(&rdnssinfo->nd_opt_rdnssi_addr3, rdnss_str, sizeof(rdnss_str)); - flog(LOG_WARNING, "RDNSS address %s received on %s from %s is not advertised by us", rdnss_str, iface->Name, addr_str); - } - /* FALLTHROUGH */ - case 5: - rdnss = iface->AdvRDNSSList; - if (!check_rdnss_presence(rdnss, &rdnssinfo->nd_opt_rdnssi_addr2)) { - /* no match found in iface->AdvRDNSSList */ - addrtostr(&rdnssinfo->nd_opt_rdnssi_addr2, rdnss_str, sizeof(rndss_str)); - flog(LOG_WARNING, "RDNSS address %s received on %s from %s is not advertised by us", rdnss_str, iface->Name, addr_str); - } - /* FALLTHROUGH */ - case 3: - rdnss = iface->AdvRDNSSList; - if (!check_rdnss_presence(rdnss, &rdnssinfo->nd_opt_rdnssi_addr1)) { - /* no match found in iface->AdvRDNSSList */ - addrtostr(&rdnssinfo->nd_opt_rdnssi_addr1, rdnss_str, sizeof(rdnss_str)); - flog(LOG_WARNING, "RDNSS address %s received on %s from %s is not advertised by us", rdnss_str, iface->Name, addr_str); + case ND_OPT_RDNSS_INFORMATION:{ + char rdnss_str[INET6_ADDRSTRLEN]; + struct AdvRDNSS *rdnss = 0; + struct nd_opt_rdnss_info_local *rdnssinfo = (struct nd_opt_rdnss_info_local *)opt_str; + if (len < sizeof(*rdnssinfo)) + return; + int count = rdnssinfo->nd_opt_rdnssi_len; + + /* Check the RNDSS addresses received */ + switch (count) { + case 7: + rdnss = iface->AdvRDNSSList; + if (!check_rdnss_presence(rdnss, &rdnssinfo->nd_opt_rdnssi_addr3)) { + /* no match found in iface->AdvRDNSSList */ + addrtostr(&rdnssinfo->nd_opt_rdnssi_addr3, rdnss_str, sizeof(rdnss_str)); + flog(LOG_WARNING, + "RDNSS address %s received on %s from %s is not advertised by us", + rdnss_str, iface->Name, addr_str); + } + /* FALLTHROUGH */ + case 5: + rdnss = iface->AdvRDNSSList; + if (!check_rdnss_presence(rdnss, &rdnssinfo->nd_opt_rdnssi_addr2)) { + /* no match found in iface->AdvRDNSSList */ + addrtostr(&rdnssinfo->nd_opt_rdnssi_addr2, rdnss_str, sizeof(rdnss_str)); + flog(LOG_WARNING, + "RDNSS address %s received on %s from %s is not advertised by us", + rdnss_str, iface->Name, addr_str); + } + /* FALLTHROUGH */ + case 3: + rdnss = iface->AdvRDNSSList; + if (!check_rdnss_presence(rdnss, &rdnssinfo->nd_opt_rdnssi_addr1)) { + /* no match found in iface->AdvRDNSSList */ + addrtostr(&rdnssinfo->nd_opt_rdnssi_addr1, rdnss_str, sizeof(rdnss_str)); + flog(LOG_WARNING, + "RDNSS address %s received on %s from %s is not advertised by us", + rdnss_str, iface->Name, addr_str); + } + + break; + default: + flog(LOG_ERR, "invalid len %i in RDNSS option on %s from %s", count, iface->Name, + addr_str); } break; - default: - flog(LOG_ERR, "invalid len %i in RDNSS option on %s from %s", count, iface->Name, addr_str); } - - break; - case ND_OPT_DNSSL_INFORMATION: - dnsslinfo = (struct nd_opt_dnssl_info_local *)opt_str; - if (len < sizeof(*dnsslinfo)) - return; - - suffix[0] = '\0'; - for (offset = 0; offset < (dnsslinfo->nd_opt_dnssli_len - 1) * 8;) { - if (&dnsslinfo->nd_opt_dnssli_suffixes[offset] - opt_str >= len) + case ND_OPT_DNSSL_INFORMATION:{ + struct nd_opt_dnssl_info_local *dnsslinfo = (struct nd_opt_dnssl_info_local *)opt_str; + if (len < sizeof(*dnsslinfo)) return; - label_len = dnsslinfo->nd_opt_dnssli_suffixes[offset++]; - if (label_len == 0) { - /* - * Ignore empty suffixes. They're - * probably just padding... - */ - if (suffix[0] == '\0') + for (int offset = 0; offset < (dnsslinfo->nd_opt_dnssli_len - 1) * 8;) { + char suffix[256] = { "" }; + if (&dnsslinfo->nd_opt_dnssli_suffixes[offset] - opt_str >= len) + return; + int label_len = dnsslinfo->nd_opt_dnssli_suffixes[offset++]; + + if (label_len == 0) { + /* + * Ignore empty suffixes. They're + * probably just padding... + */ + if (suffix[0] == '\0') + continue; + + if (!check_dnssl_presence(iface->AdvDNSSLList, suffix)) { + flog(LOG_WARNING, + "DNSSL suffix %s received on %s from %s is not advertised by us", + suffix, iface->Name, addr_str); + } + + suffix[0] = '\0'; continue; + } - if (!check_dnssl_presence(iface->AdvDNSSLList, suffix)) { - flog(LOG_WARNING, "DNSSL suffix %s received on %s from %s is not advertised by us", suffix, iface->Name, addr_str); + /* + * 1) must not overflow int: label + 2, offset + label_len + * 2) last byte of dnssli_suffix must not overflow opt_str + len + */ + if ((sizeof(suffix) - strlen(suffix)) < (label_len + 2) || + label_len > label_len + 2 + || &dnsslinfo->nd_opt_dnssli_suffixes[offset + label_len] - opt_str >= len + || offset + label_len < offset) { + flog(LOG_ERR, "oversized suffix in DNSSL option on %s from %s", iface->Name, + addr_str); + break; } - suffix[0] = '\0'; - continue; + if (suffix[0] != '\0') + strcat(suffix, "."); + strncat(suffix, (char *)&dnsslinfo->nd_opt_dnssli_suffixes[offset], label_len); + offset += label_len; } - - /* - * 1) must not overflow int: label + 2, offset + label_len - * 2) last byte of dnssli_suffix must not overflow opt_str + len - */ - if ((sizeof(suffix) - strlen(suffix)) < (label_len + 2) || - label_len > label_len + 2 || &dnsslinfo->nd_opt_dnssli_suffixes[offset + label_len] - opt_str >= len || offset + label_len < offset) { - flog(LOG_ERR, "oversized suffix in DNSSL option on %s from %s", iface->Name, addr_str); - break; - } - - if (suffix[0] != '\0') - strcat(suffix, "."); - strncat(suffix, (char *)&dnsslinfo->nd_opt_dnssli_suffixes[offset], label_len); - offset += label_len; + break; } - break; default: dlog(LOG_DEBUG, 1, "unknown option %d in RA on %s from %s", (int)*opt_str, iface->Name, addr_str); break; diff --git a/radvd.c b/radvd.c index 3e9e9e79..4d13bb16 100644 --- a/radvd.c +++ b/radvd.c @@ -26,8 +26,6 @@ #include #include -struct Interface *IfaceList = NULL; - #ifdef HAVE_GETOPT_LONG /* *INDENT-OFF* */ @@ -69,21 +67,11 @@ static char usage_str[] = { "[-hsvcn] [-d level] [-C config_path] [-m log_method] [-l log_file]\n" "\t[-f facility] [-p pid_file] [-u username] [-t chrootdir]" -char *conf_file = NULL; -char *pidfile = NULL; -char *pname; -int sock = -1; - -void timer_handler(void *data); -void config_interface(void); -void kickoff_adverts(void); -void stop_adverts(void); -void usage(void); -int drop_root_privileges(const char *); -int readin_config(char *); -int check_conffile_perm(const char *, const char *); -const char *get_pidfile(void); -void main_loop(void); +}; + +#endif + +/* *INDENT-ON* */ static volatile int sighup_received = 0; static volatile int sigterm_received = 0; static volatile int sigint_received = 0; @@ -93,7 +81,22 @@ static void sighup_handler(int sig); static void sigterm_handler(int sig); static void sigint_handler(int sig); static void sigusr1_handler(int sig); +static void timer_handler(int sock, struct Interface *iface); +static void config_interface(struct Interface *iface); +static void kickoff_adverts(int sock, struct Interface *iface); +static void stop_advert_foo(struct Interface *iface, void *data); +static void stop_adverts(int sock, struct Interface *ifaces); static void version(void); +static void usage(char const *pname); +static int drop_root_privileges(const char *); +static int check_conffile_perm(const char *, const char *); +static const char *get_pidfile(void); +static void setup_iface_foo(struct Interface *iface, void *data); +static void setup_ifaces(int sock, struct Interface *ifaces); +static void main_loop(int sock, struct Interface *ifaces, char const *conf_path); +static void reset_prefix_lifetimes_foo(struct Interface *iface, void *data); +static void reset_prefix_lifetimes(struct Interface *ifaces); +static struct Interface *reload_config(int sock, struct Interface *ifaces, char const *conf_path); int main(int argc, char *argv[]) { @@ -105,17 +108,17 @@ int main(int argc, char *argv[]) char *chrootdir = NULL; int configtest = 0; int daemonize = 1; + int force_pid_file = 0; #ifdef HAVE_GETOPT_LONG int opt_idx; #endif - pid_t pid; - pname = ((pname = strrchr(argv[0], '/')) != NULL) ? pname + 1 : argv[0]; + char const *pname = ((pname = strrchr(argv[0], '/')) != NULL) ? pname + 1 : argv[0]; srand((unsigned int)time(NULL)); - conf_file = PATH_RADVD_CONF; - pidfile = PATH_RADVD_PID; + char const *conf_path = PATH_RADVD_CONF; + daemon_pid_file_ident = PATH_RADVD_PID; /* libdaemon defines daemon_pid_file_ident */ /* parse args */ #define OPTIONS_STR "d:C:l:m:p:t:u:vhcn" @@ -127,7 +130,7 @@ int main(int argc, char *argv[]) { switch (c) { case 'C': - conf_file = optarg; + conf_path = optarg; break; case 'd': set_debuglevel(atoi(optarg)); @@ -139,7 +142,8 @@ int main(int argc, char *argv[]) logfile = optarg; break; case 'p': - pidfile = optarg; + daemon_pid_file_ident = optarg; + force_pid_file = 1; break; case 'm': if (!strcmp(optarg, "syslog")) { @@ -173,7 +177,7 @@ int main(int argc, char *argv[]) daemonize = 0; break; case 'h': - usage(); + usage(pname); #ifdef HAVE_GETOPT_LONG case ':': fprintf(stderr, "%s: option %s: parameter expected\n", pname, prog_opt[opt_idx].name); @@ -203,6 +207,7 @@ int main(int argc, char *argv[]) } if (configtest) { + set_debuglevel(1); log_method = L_STDERR; } @@ -213,73 +218,70 @@ int main(int argc, char *argv[]) if (!configtest) { flog(LOG_INFO, "version %s started", VERSION); - } - /* get a raw socket for sending and receiving ICMPv6 messages */ - sock = open_icmpv6_socket(); - if (sock < 0) { - perror("open_icmpv6_socket"); - exit(1); + /* Calling privsep here, before opening the socket and reading the config + * file, ensures we're not going to be wasting resources in the privsep + * process. */ + dlog(LOG_DEBUG, 3, "Initializing privsep"); + if (privsep_init() < 0) { + flog(LOG_INFO, "Failed to initialize privsep."); + exit(1); + } } /* check that 'other' cannot write the file * for non-root, also that self/own group can't either */ - if (check_conffile_perm(username, conf_file) < 0) { + if (check_conffile_perm(username, conf_path) != 0) { if (get_debuglevel() == 0) { - flog(LOG_ERR, "Exiting, permissions on conf_file invalid.\n"); + flog(LOG_ERR, "Exiting, permissions on conf_file invalid."); exit(1); } else flog(LOG_WARNING, "Insecure file permissions, but continuing anyway"); } - /* if we know how to do it, check whether forwarding is enabled */ - if (check_ip6_forwarding()) { - flog(LOG_WARNING, "IPv6 forwarding seems to be disabled, but continuing anyway."); - } - /* parse config file */ - if (readin_config(conf_file) < 0) { - flog(LOG_ERR, "Exiting, failed to read config file.\n"); + struct Interface *ifaces = NULL; + if ((ifaces = readin_config(conf_path)) == 0) { + flog(LOG_ERR, "Exiting, failed to read config file."); exit(1); } if (configtest) { - fprintf(stderr, "Syntax OK\n"); + free_ifaces(ifaces); exit(0); } -#ifdef USE_PRIVSEP - dlog(LOG_DEBUG, 3, "Initializing privsep"); - if (privsep_init() < 0) { - perror("Failed to initialize privsep."); + + /* get a raw socket for sending and receiving ICMPv6 messages */ + int sock = open_icmpv6_socket(); + if (sock < 0) { + perror("open_icmpv6_socket"); exit(1); } -#endif - /* drop root privileges if requested. */ - if (username) { - if (drop_root_privileges(username) < 0) { - perror("drop_root_privileges"); - exit(1); - } + /* if we know how to do it, check whether forwarding is enabled */ + if (check_ip6_forwarding()) { + flog(LOG_WARNING, "IPv6 forwarding seems to be disabled, but continuing anyway."); } + daemon_pid_file_proc = get_pidfile; + /* * okay, config file is read in, socket and stuff is setup, so * lets fork now... */ - - if (get_debuglevel() > 0) { - daemonize = 0; - } - + dlog(LOG_DEBUG, 3, "radvd startup PID is %d", getpid()); if (daemonize) { + pid_t pid; + if (daemon_retval_init()) { flog(LOG_ERR, "Could not initialize daemon IPC."); exit(1); } + /* TODO: research daemon_log (in libdaemon) and have it log the same as radvd. */ pid = daemon_fork(); + if (-1 == pid) { flog(LOG_ERR, "Could not fork: %s", strerror(errno)); daemon_retval_done(); @@ -287,47 +289,94 @@ int main(int argc, char *argv[]) } if (0 < pid) { - if (daemon_retval_wait(0)) { - flog(LOG_ERR, "Could not daemonize."); + switch (daemon_retval_wait(1)) { + case 0: + dlog(LOG_DEBUG, 3, "radvd PID is %d", pid); + exit(0); + break; + + case 1: + flog(LOG_ERR, "radvd already running, terminating."); exit(1); + break; + + case 2: + flog(LOG_ERR, "Cannot create radvd PID file, terminating: %s", strerror(errno)); + exit(2); + break; + + default: + flog(LOG_ERR, "Could not daemonize."); + exit(-1); + break; } - exit(0); } - daemon_pid_file_proc = get_pidfile; if (daemon_pid_file_is_running() >= 0) { - flog(LOG_ERR, "radvd already running, terminating."); daemon_retval_send(1); exit(1); } + if (daemon_pid_file_create()) { - flog(LOG_ERR, "Cannot create radvd PID file, terminating: %s", strerror(errno)); daemon_retval_send(2); - exit(1); + exit(2); } + daemon_retval_send(0); + } else { + if (force_pid_file) { + if (daemon_pid_file_is_running() >= 0) { + flog(LOG_ERR, "radvd already running, terminating."); + exit(1); + } + + if (daemon_pid_file_create()) { + flog(LOG_ERR, "Cannot create radvd PID file, terminating: %s", strerror(errno)); + exit(2); + } + } + + dlog(LOG_DEBUG, 3, "radvd PID is %d", getpid()); } + if (username) { + if (drop_root_privileges(username) < 0) { + perror("drop_root_privileges"); + flog(LOG_ERR, "unable to drop root privileges"); + exit(1); + } + } - config_interface(); - kickoff_adverts(); - main_loop(); + setup_ifaces(sock, ifaces); + main_loop(sock, ifaces, conf_path); flog(LOG_INFO, "sending stop adverts"); - stop_adverts(); + stop_adverts(sock, ifaces); + if (daemonize) { - flog(LOG_INFO, "removing %s", pidfile); - unlink(pidfile); + flog(LOG_INFO, "removing %s", daemon_pid_file_ident); + daemon_pid_file_remove(); + } else if (force_pid_file) { + flog(LOG_INFO, "removing %s", get_pidfile()); + daemon_pid_file_remove(); } + flog(LOG_INFO, "returning from radvd main"); + log_close(); return 0; } -const char *get_pidfile(void) +/* This function is copied from dpid.c (in libdaemon) and renamed. */ +static const char *get_pidfile(void) { - return pidfile; + static char *fn = NULL; + if (fn) + free(fn); + fn = strdupf("%s", daemon_pid_file_ident ? daemon_pid_file_ident : "unknown"); + + return fn; } -void main_loop(void) +static void main_loop(int sock, struct Interface *ifaces, char const *conf_path) { struct pollfd fds[2]; sigset_t sigmask; @@ -367,45 +416,31 @@ void main_loop(void) fds[0].fd = sock; fds[0].events = POLLIN; - fds[0].revents = 0; #if HAVE_NETLINK - if (!disablenetlink) { - fds[1].fd = netlink_socket(); - fds[1].events = POLLIN; - } else { - fds[1].fd = -1; - fds[1].events = 0; - } - fds[1].revents = 0; + fds[1].fd = netlink_socket(); + fds[1].events = POLLIN; #else fds[1].fd = -1; - fds[1].events = 0; - fds[1].revents = 0; #endif for (;;) { - struct Interface *next = NULL; - struct Interface *iface; - int timeout = -1; - int rc; - - if (IfaceList) { - timeout = next_time_msec(IfaceList); - next = IfaceList; - for (iface = IfaceList; iface; iface = iface->next) { - int t; - t = next_time_msec(iface); - if (timeout > t) { - timeout = t; - next = iface; - } - } + struct timespec *tsp = 0; + + struct Interface *next_iface_to_expire = find_iface_by_time(ifaces); + if (next_iface_to_expire) { + static struct timespec ts; + int timeout = next_time_msec(next_iface_to_expire); + ts.tv_sec = timeout / 1000; + ts.tv_nsec = (timeout - 1000 * ts.tv_sec) * 1000000; + tsp = &ts; + dlog(LOG_DEBUG, 1, "polling for %g seconds. Next iface is %s.", timeout / 1000.0, + next_iface_to_expire->Name); + } else { + dlog(LOG_DEBUG, 1, "No iface is next. Polling indefinitely."); } - dlog(LOG_DEBUG, 5, "polling for %g seconds.", timeout / 1000.0); - - rc = poll(fds, sizeof(fds) / sizeof(fds[0]), timeout); + int rc = ppoll(fds, sizeof(fds) / sizeof(fds[0]), tsp, &sigempty); if (rc > 0) { #ifdef HAVE_NETLINK @@ -424,195 +459,200 @@ void main_loop(void) struct in6_pktinfo *pkt_info = NULL; unsigned char msg[MSG_SIZE_RECV]; - len = recv_rs_ra(msg, &rcv_addr, &pkt_info, &hoplimit); - if (len > 0) { - process(IfaceList, msg, len, &rcv_addr, pkt_info, hoplimit); + len = recv_rs_ra(sock, msg, &rcv_addr, &pkt_info, &hoplimit); + if (len > 0 && pkt_info) { + process(sock, ifaces, msg, len, &rcv_addr, pkt_info, hoplimit); + } else if (!pkt_info) { + dlog(LOG_INFO, 4, "recv_rs_ra returned null pkt_info."); + } else if (len <= 0) { + dlog(LOG_INFO, 4, "recv_rs_ra returned len <= 0: %d", len); } } } else if (rc == 0) { - if (next) - timer_handler(next); + if (next_iface_to_expire) + timer_handler(sock, next_iface_to_expire); } else if (rc == -1) { dlog(LOG_INFO, 3, "poll returned early: %s", strerror(errno)); } - if (sigterm_received || sigint_received) { - flog(LOG_WARNING, "Exiting, sigterm or sigint received.\n"); + if (sigint_received) { + flog(LOG_WARNING, "Exiting, %d sigint(s) received.", sigint_received); + break; + } + + if (sigterm_received) { + flog(LOG_WARNING, "Exiting, %d sigterm(s) received.", sigterm_received); break; } if (sighup_received) { - dlog(LOG_INFO, 3, "sig hup received.\n"); - reload_config(); + dlog(LOG_INFO, 3, "sig hup received."); + ifaces = reload_config(sock, ifaces, conf_path); sighup_received = 0; } if (sigusr1_received) { - dlog(LOG_INFO, 3, "sig usr1 received.\n"); - reset_prefix_lifetimes(); + dlog(LOG_INFO, 3, "sig usr1 received."); + reset_prefix_lifetimes(ifaces); sigusr1_received = 0; } } } -void timer_handler(void *data) +static void timer_handler(int sock, struct Interface *iface) { - struct Interface *iface = (struct Interface *)data; - double next; + dlog(LOG_DEBUG, 1, "timer_handler called for %s", iface->Name); - dlog(LOG_DEBUG, 4, "timer_handler called for %s", iface->Name); - - if (send_ra_forall(iface, NULL) != 0) { + if (send_ra_forall(sock, iface, NULL) != 0) { dlog(LOG_DEBUG, 4, "send_ra_forall failed on interface %s", iface->Name); } - next = rand_between(iface->MinRtrAdvInterval, iface->MaxRtrAdvInterval); - - if (iface->init_racount < MAX_INITIAL_RTR_ADVERTISEMENTS) { - next = min(MAX_INITIAL_RTR_ADVERT_INTERVAL, next); - } + double next = rand_between(iface->MinRtrAdvInterval, iface->MaxRtrAdvInterval); - iface->next_multicast = next_timeval(next); + reschedule_iface(iface, next); } -void config_interface(void) +static void config_interface(struct Interface *iface) { - struct Interface *iface; - for (iface = IfaceList; iface; iface = iface->next) { - if (iface->AdvLinkMTU) - set_interface_linkmtu(iface->Name, iface->AdvLinkMTU); - if (iface->AdvCurHopLimit) - set_interface_curhlim(iface->Name, iface->AdvCurHopLimit); - if (iface->AdvReachableTime) - set_interface_reachtime(iface->Name, iface->AdvReachableTime); - if (iface->AdvRetransTimer) - set_interface_retranstimer(iface->Name, iface->AdvRetransTimer); - } + if (iface->AdvLinkMTU) + set_interface_linkmtu(iface->Name, iface->AdvLinkMTU); + if (iface->AdvCurHopLimit) + set_interface_curhlim(iface->Name, iface->AdvCurHopLimit); + if (iface->AdvReachableTime) + set_interface_reachtime(iface->Name, iface->AdvReachableTime); + if (iface->AdvRetransTimer) + set_interface_retranstimer(iface->Name, iface->AdvRetransTimer); } -void kickoff_adverts(void) +static void kickoff_adverts(int sock, struct Interface *iface) { - struct Interface *iface; - /* * send initial advertisement and set timers */ - for (iface = IfaceList; iface; iface = iface->next) { - double next; - - gettimeofday(&iface->last_ra_time, NULL); + gettimeofday(&iface->last_ra_time, NULL); - if (iface->UnicastOnly) - continue; + if (iface->UnicastOnly) + return; - gettimeofday(&iface->last_multicast, NULL); + gettimeofday(&iface->last_multicast, NULL); - /* TODO: AdvSendAdvert is being checked in send_ra now so it can be removed here. */ - if (!iface->AdvSendAdvert) - continue; - - /* send an initial advertisement */ - if (send_ra_forall(iface, NULL) == 0) { + /* send an initial advertisement */ + if (send_ra_forall(sock, iface, NULL) == 0) { + dlog(LOG_DEBUG, 4, "send_ra_forall failed on interface %s", iface->Name); + } + double next = min(MAX_INITIAL_RTR_ADVERT_INTERVAL, iface->MaxRtrAdvInterval); + reschedule_iface(iface, next); +} - next = min(MAX_INITIAL_RTR_ADVERT_INTERVAL, iface->MaxRtrAdvInterval); - iface->next_multicast = next_timeval(next); - } +static void stop_advert_foo(struct Interface *iface, void *data) +{ + if (!iface->UnicastOnly) { + /* send a final advertisement with zero Router Lifetime */ + dlog(LOG_DEBUG, 4, "stopping all adverts on %s.", iface->Name); + iface->cease_adv = 1; + int sock = *(int *)data; + send_ra_forall(sock, iface, NULL); } } -void stop_adverts(void) +static void stop_adverts(int sock, struct Interface *ifaces) { - struct Interface *iface; - /* * send final RA (a SHOULD in RFC4861 section 6.2.5) */ - - for (iface = IfaceList; iface; iface = iface->next) { - if (!iface->UnicastOnly) { - /* TODO: AdvSendAdvert is being checked in send_ra now so it can be removed here. */ - if (iface->AdvSendAdvert) { - /* send a final advertisement with zero Router Lifetime */ - iface->cease_adv = 1; - send_ra_forall(iface, NULL); - } - } - } + for_each_iface(ifaces, stop_advert_foo, &sock); } -void reload_config(void) +int setup_iface(int sock, struct Interface *iface) { - struct Interface *iface; - - flog(LOG_INFO, "attempting to reread config file"); + iface->ready = 0; - iface = IfaceList; - while (iface) { - struct Interface *next_iface = iface->next; - struct AdvPrefix *prefix; - struct AdvRoute *route; - struct AdvRDNSS *rdnss; - struct AdvDNSSL *dnssl; + /* Check IFF_UP, IFF_RUNNING and IFF_MULTICAST */ + if (check_device(sock, iface) < 0) { + return -1; + } - dlog(LOG_DEBUG, 4, "freeing interface %s", iface->Name); + if (update_device_index(iface) < 0) { + return -1; + } - prefix = iface->AdvPrefixList; - while (prefix) { - struct AdvPrefix *next_prefix = prefix->next; + /* Set iface->if_index, iface->max_mtu and iface hardware address */ + if (update_device_info(sock, iface) < 0) { + return -1; + } - free(prefix); - prefix = next_prefix; - } + /* Make sure the settings in the config file for this interface are ok (this depends + * on iface->max_mtu already being set). */ + if (check_iface(iface) < 0) { + return -1; + } - route = iface->AdvRouteList; - while (route) { - struct AdvRoute *next_route = route->next; + /* Make sure this is diabled. We don't want this interface to autoconfig using its + * own advert messages. */ + if (disable_ipv6_autoconfig(iface->Name)) { + return -1; + } - free(route); - route = next_route; - } + /* Save the first link local address seen on the specified interface to iface->if_addr */ + if (setup_linklocal_addr(iface) < 0) { + return -1; + } - rdnss = iface->AdvRDNSSList; - while (rdnss) { - struct AdvRDNSS *next_rdnss = rdnss->next; + /* join the allrouters multicast group so we get the solicitations */ + if (setup_allrouters_membership(sock, iface) < 0) { + return -1; + } - free(rdnss); - rdnss = next_rdnss; - } + iface->ready = 1; - dnssl = iface->AdvDNSSLList; - while (dnssl) { - struct AdvDNSSL *next_dnssl = dnssl->next; - int i; + dlog(LOG_DEBUG, 4, "interface definition for %s is ok", iface->Name); - for (i = 0; i < dnssl->AdvDNSSLNumber; i++) - free(dnssl->AdvDNSSLSuffixes[i]); - free(dnssl->AdvDNSSLSuffixes); - free(dnssl); + return 0; +} - dnssl = next_dnssl; +static void setup_iface_foo(struct Interface *iface, void *data) +{ + int sock = *(int *)data; + + if (setup_iface(sock, iface) < 0) { + if (iface->IgnoreIfMissing) { + dlog(LOG_DEBUG, 4, "interface %s does not exist or is not set up properly, ignoring the interface", + iface->Name); + } else { + flog(LOG_ERR, "interface %s does not exist or is not set up properly", iface->Name); + exit(1); } - - free(iface); - iface = next_iface; } - IfaceList = NULL; + config_interface(iface); + kickoff_adverts(sock, iface); +} + +static void setup_ifaces(int sock, struct Interface *ifaces) +{ + for_each_iface(ifaces, setup_iface_foo, &sock); +} + +static struct Interface *reload_config(int sock, struct Interface *ifaces, char const *conf_path) +{ + free_ifaces(ifaces); + + flog(LOG_INFO, "attempting to reread config file"); /* reread config file */ - if (readin_config(conf_file) < 0) { - perror("readin_config failed."); + ifaces = readin_config(conf_path); + if (!ifaces) { + flog(LOG_ERR, "Exiting, failed to read config file."); exit(1); } - - /* XXX: fails due to lack of permissions with non-root user */ - config_interface(); - kickoff_adverts(); + setup_ifaces(sock, ifaces); flog(LOG_INFO, "resuming normal operation"); + + return ifaces; } static void sighup_handler(int sig) @@ -643,35 +683,32 @@ static void sigusr1_handler(int sig) sigusr1_received = 1; } -void reset_prefix_lifetimes(void) +static void reset_prefix_lifetimes_foo(struct Interface *iface, void *data) { - struct Interface *iface; - struct AdvPrefix *prefix; - char pfx_str[INET6_ADDRSTRLEN]; - - flog(LOG_INFO, "Resetting prefix lifetimes"); - - for (iface = IfaceList; iface; iface = iface->next) { - for (prefix = iface->AdvPrefixList; prefix; prefix = prefix->next) { - if (prefix->DecrementLifetimesFlag) { - addrtostr(&prefix->Prefix, pfx_str, sizeof(pfx_str)); - dlog(LOG_DEBUG, 4, "%s/%u%%%s plft reset from %u to %u secs", pfx_str, prefix->PrefixLen, iface->Name, prefix->curr_preferredlft, - prefix->AdvPreferredLifetime); - dlog(LOG_DEBUG, 4, "%s/%u%%%s vlft reset from %u to %u secs", pfx_str, prefix->PrefixLen, iface->Name, prefix->curr_validlft, - prefix->AdvValidLifetime); - prefix->curr_validlft = prefix->AdvValidLifetime; - prefix->curr_preferredlft = prefix->AdvPreferredLifetime; - } + flog(LOG_INFO, "Resetting prefix lifetimes on %s", iface->Name); + + for (struct AdvPrefix * prefix = iface->AdvPrefixList; prefix; prefix = prefix->next) { + if (prefix->DecrementLifetimesFlag) { + char pfx_str[INET6_ADDRSTRLEN]; + addrtostr(&prefix->Prefix, pfx_str, sizeof(pfx_str)); + dlog(LOG_DEBUG, 4, "%s/%u%%%s plft reset from %u to %u secs", pfx_str, prefix->PrefixLen, + iface->Name, prefix->curr_preferredlft, prefix->AdvPreferredLifetime); + dlog(LOG_DEBUG, 4, "%s/%u%%%s vlft reset from %u to %u secs", pfx_str, prefix->PrefixLen, + iface->Name, prefix->curr_validlft, prefix->AdvValidLifetime); + prefix->curr_validlft = prefix->AdvValidLifetime; + prefix->curr_preferredlft = prefix->AdvPreferredLifetime; } - } +} +static void reset_prefix_lifetimes(struct Interface *ifaces) +{ + for_each_iface(ifaces, reset_prefix_lifetimes_foo, 0); } -int drop_root_privileges(const char *username) +static int drop_root_privileges(const char *username) { - struct passwd *pw = NULL; - pw = getpwnam(username); + struct passwd *pw = getpwnam(username); if (pw) { if (initgroups(username, pw->pw_gid) != 0 || setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0) { flog(LOG_ERR, "Couldn't change to '%.32s' uid=%d gid=%d", username, pw->pw_uid, pw->pw_gid); @@ -684,12 +721,9 @@ int drop_root_privileges(const char *username) return 0; } -int check_conffile_perm(const char *username, const char *conf_file) +static int check_conffile_perm(const char *username, const char *conf_file) { - struct stat stbuf; - struct passwd *pw = NULL; FILE *fp = fopen(conf_file, "r"); - if (fp == NULL) { flog(LOG_ERR, "can't open %s: %s", conf_file, strerror(errno)); return -1; @@ -699,10 +733,15 @@ int check_conffile_perm(const char *username, const char *conf_file) if (!username) username = "root"; - pw = getpwnam(username); + struct passwd *pw = getpwnam(username); + if (!pw) { + return -1; + } - if (stat(conf_file, &stbuf) || pw == NULL) + struct stat stbuf; + if (0 != stat(conf_file, &stbuf)) { return -1; + } if (stbuf.st_mode & S_IWOTH) { flog(LOG_ERR, "Insecure file permissions (writable by others): %s", conf_file); @@ -710,7 +749,8 @@ int check_conffile_perm(const char *username, const char *conf_file) } /* for non-root: must not be writable by self/own group */ - if (strncmp(username, "root", 5) != 0 && ((stbuf.st_mode & S_IWGRP && pw->pw_gid == stbuf.st_gid) || (stbuf.st_mode & S_IWUSR && pw->pw_uid == stbuf.st_uid))) { + if (strncmp(username, "root", 5) != 0 && ((stbuf.st_mode & S_IWGRP && pw->pw_gid == stbuf.st_gid) + || (stbuf.st_mode & S_IWUSR && pw->pw_uid == stbuf.st_uid))) { flog(LOG_ERR, "Insecure file permissions (writable by self/group): %s", conf_file); return -1; } @@ -731,7 +771,7 @@ static void version(void) exit(1); } -void usage(void) +static void usage(char const *pname) { fprintf(stderr, "usage: %s %s\n", pname, usage_str); exit(1); diff --git a/radvd.h b/radvd.h index e19333ee..c43169be 100644 --- a/radvd.h +++ b/radvd.h @@ -40,8 +40,9 @@ struct Interface { struct in6_addr if_addr; unsigned int if_index; + unsigned int *flags; - uint8_t init_racount; /* Initial RAs */ + uint8_t racount; /* Initial RAs */ uint8_t if_hwaddr[HWADDR_MAX]; int if_hwaddr_len; @@ -90,8 +91,8 @@ struct Interface { struct timeval last_multicast; struct timeval next_multicast; - /* Info whether this interface has failed in the past (and may need to be reinitialized) */ - int HasFailed; + /* Info whether this interface has been initialized successfully */ + int ready; struct Interface *next; }; @@ -232,8 +233,8 @@ struct nd_opt_6co { struct Interface *readin_config(char const *fname); /* radvd.c */ -void reload_config(void); -void reset_prefix_lifetimes(void); +int disable_ipv6_autoconfig(char const *iface); +int setup_iface(int sock, struct Interface *iface); /* timer.c */ struct timeval next_timeval(double next); @@ -247,14 +248,13 @@ int update_device_info(int sock, struct Interface *); int check_device(int sock, struct Interface *); int setup_linklocal_addr(struct Interface *); int setup_allrouters_membership(int sock, struct Interface *); -int check_allrouters_membership(struct Interface *); int setup_linklocal_addr(struct Interface *iface); int get_v4addr(const char *, unsigned int *); -int set_interface_var(const char *, const char *, const char *, uint32_t); int set_interface_linkmtu(const char *, uint32_t); int set_interface_curhlim(const char *, uint8_t); int set_interface_reachtime(const char *, uint32_t); int set_interface_retranstimer(const char *, uint32_t); +int check_ip6_forwarding(void); /* interface.c */ void iface_init_defaults(struct Interface *); diff --git a/radvdump.c b/radvdump.c index 20fac6be..e65090a1 100644 --- a/radvdump.c +++ b/radvdump.c @@ -32,18 +32,15 @@ struct option prog_opt[] = { int sock = -1; -void version(void); -void usage(void); -void print_ff(unsigned char *, int, struct sockaddr_in6 *, int, unsigned int, int); -void print_preferences(int); +static void version(void); +static void usage(char const * pname); +static void print_ff(unsigned char *, int, struct sockaddr_in6 *, int, unsigned int, int); +static void print_preferences(int); int main(int argc, char *argv[]) { - unsigned char msg[MSG_SIZE_RECV]; - int c, len, hoplimit; + int c; int edefs = 0; - struct sockaddr_in6 rcv_addr; - struct in6_pktinfo *pkt_info = NULL; #ifdef HAVE_GETOPT_LONG int opt_idx; #endif @@ -86,14 +83,18 @@ int main(int argc, char *argv[]) } /* get a raw socket for sending and receiving ICMPv6 messages */ - sock = open_icmpv6_socket(); + int sock = open_icmpv6_socket(); if (sock < 0) { perror("open_icmpv6_socket"); exit(1); } for (;;) { - len = recv_rs_ra(msg, &rcv_addr, &pkt_info, &hoplimit); + unsigned char msg[MSG_SIZE_RECV]; + int hoplimit = 0; + struct in6_pktinfo *pkt_info = NULL; + struct sockaddr_in6 rcv_addr; + int len = recv_rs_ra(sock, msg, &rcv_addr, &pkt_info, &hoplimit); if (len > 0) { struct icmp6_hdr *icmph; @@ -135,25 +136,23 @@ int main(int argc, char *argv[]) exit(0); } -void print_ff(unsigned char *msg, int len, struct sockaddr_in6 *addr, int hoplimit, unsigned int if_index, int edefs) +static void print_ff(unsigned char *msg, int len, struct sockaddr_in6 *addr, int hoplimit, unsigned int if_index, int edefs) { /* XXX: hoplimit not being used for anything here.. */ - struct nd_router_advert *radvert; - char addr_str[INET6_ADDRSTRLEN]; - uint8_t *opt_str; int orig_len = len; - char if_name[IFNAMSIZ] = ""; + char addr_str[INET6_ADDRSTRLEN]; addrtostr(&addr->sin6_addr, addr_str, sizeof(addr_str)); printf("#\n# radvd configuration generated by radvdump %s\n", VERSION); printf("# based on Router Advertisement from %s\n", addr_str); + char if_name[IFNAMSIZ] = { "" }; if_indextoname(if_index, if_name); printf("# received by interface %s\n", if_name); printf("#\n\ninterface %s\n{\n\tAdvSendAdvert on;\n", if_name); printf("\t# Note: {Min,Max}RtrAdvInterval cannot be obtained with radvdump\n"); - radvert = (struct nd_router_advert *)msg; + struct nd_router_advert *radvert = (struct nd_router_advert *)msg; if (!edefs || DFLT_AdvManagedFlag != (ND_RA_FLAG_MANAGED == (radvert->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED))) printf("\tAdvManagedFlag %s;\n", (radvert->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) ? "on" : "off"); @@ -174,7 +173,8 @@ void print_ff(unsigned char *msg, int len, struct sockaddr_in6 *addr, int hoplim printf("\tAdvDefaultLifetime %hu;\n", ntohs(radvert->nd_ra_router_lifetime)); /* Mobile IPv6 ext */ - if (!edefs || DFLT_AdvHomeAgentFlag != (ND_RA_FLAG_HOME_AGENT == (radvert->nd_ra_flags_reserved & ND_RA_FLAG_HOME_AGENT))) + if (!edefs + || DFLT_AdvHomeAgentFlag != (ND_RA_FLAG_HOME_AGENT == (radvert->nd_ra_flags_reserved & ND_RA_FLAG_HOME_AGENT))) printf("\tAdvHomeAgentFlag %s;\n", (radvert->nd_ra_flags_reserved & ND_RA_FLAG_HOME_AGENT) ? "on" : "off"); /* Route Preferences and more specific routes */ @@ -190,35 +190,34 @@ void print_ff(unsigned char *msg, int len, struct sockaddr_in6 *addr, int hoplim if (len == 0) return; - opt_str = (uint8_t *) (msg + sizeof(struct nd_router_advert)); + uint8_t *opt_str = (uint8_t *) (msg + sizeof(struct nd_router_advert)); while (len > 0) { - int optlen; - struct nd_opt_mtu *mtu; - struct HomeAgentInfo *ha_info; if (len < 2) { flog(LOG_ERR, "trailing garbage in RA from %s", addr_str); break; } - optlen = (opt_str[1] << 3); + int optlen = (opt_str[1] << 3); if (optlen == 0) { flog(LOG_ERR, "zero length option in RA"); break; } else if (optlen > len) { - flog(LOG_ERR, "option length greater than total" " length in RA (type %d, optlen %d, len %d)", (int)*opt_str, optlen, len); + flog(LOG_ERR, "option length greater than total" " length in RA (type %d, optlen %d, len %d)", + (int)*opt_str, optlen, len); break; } switch (*opt_str) { - case ND_OPT_MTU: - mtu = (struct nd_opt_mtu *)opt_str; + case ND_OPT_MTU:{ + struct nd_opt_mtu *mtu = (struct nd_opt_mtu *)opt_str; - if (!edefs || DFLT_AdvLinkMTU != ntohl(mtu->nd_opt_mtu_mtu)) - printf("\tAdvLinkMTU %u;\n", ntohl(mtu->nd_opt_mtu_mtu)); - break; + if (!edefs || DFLT_AdvLinkMTU != ntohl(mtu->nd_opt_mtu_mtu)) + printf("\tAdvLinkMTU %u;\n", ntohl(mtu->nd_opt_mtu_mtu)); + break; + } case ND_OPT_SOURCE_LINKADDR: /* XXX: !DFLT depends on current DFLT_ value */ if (!edefs || !DFLT_AdvSourceLLAddress) @@ -231,23 +230,26 @@ void print_ff(unsigned char *msg, int len, struct sockaddr_in6 *addr, int hoplim printf("\tAdvIntervalOpt on;\n"); break; /* Mobile IPv6 ext */ - case ND_OPT_HOME_AGENT_INFO: - ha_info = (struct HomeAgentInfo *)opt_str; - - /* XXX: we check DFLT_HomeAgentInfo by interface, and it's outside - of context here, so we always need to print it out.. */ - printf("\tAdvHomeAgentInfo on;\n"); - - /* NEMO ext */ - if (!edefs || DFLT_AdvMobRtrSupportFlag != (ha_info->flags_reserved & ND_OPT_HAI_FLAG_SUPPORT_MR)) - printf("\tAdvMobRtrSupportFlag %s;\n", (ha_info->flags_reserved & ND_OPT_HAI_FLAG_SUPPORT_MR) ? "on" : "off"); - - if (!edefs || DFLT_HomeAgentPreference != ntohs(ha_info->preference)) - printf("\tHomeAgentPreference %hu;\n", ntohs(ha_info->preference)); - /* Hum.. */ - if (!edefs || (3 * DFLT_MaxRtrAdvInterval) != ntohs(ha_info->lifetime)) - printf("\tHomeAgentLifetime %hu;\n", ntohs(ha_info->lifetime)); - break; + case ND_OPT_HOME_AGENT_INFO:{ + struct HomeAgentInfo *ha_info = (struct HomeAgentInfo *)opt_str; + + /* XXX: we check DFLT_HomeAgentInfo by interface, and it's outside + of context here, so we always need to print it out.. */ + printf("\tAdvHomeAgentInfo on;\n"); + + /* NEMO ext */ + if (!edefs + || DFLT_AdvMobRtrSupportFlag != (ha_info->flags_reserved & ND_OPT_HAI_FLAG_SUPPORT_MR)) + printf("\tAdvMobRtrSupportFlag %s;\n", + (ha_info->flags_reserved & ND_OPT_HAI_FLAG_SUPPORT_MR) ? "on" : "off"); + + if (!edefs || DFLT_HomeAgentPreference != ntohs(ha_info->preference)) + printf("\tHomeAgentPreference %hu;\n", ntohs(ha_info->preference)); + /* Hum.. */ + if (!edefs || (3 * DFLT_MaxRtrAdvInterval) != ntohs(ha_info->lifetime)) + printf("\tHomeAgentLifetime %hu;\n", ntohs(ha_info->lifetime)); + break; + } case ND_OPT_TARGET_LINKADDR: case ND_OPT_REDIRECTED_HEADER: flog(LOG_ERR, "invalid option %d in RA", (int)*opt_str); @@ -277,161 +279,170 @@ void print_ff(unsigned char *msg, int len, struct sockaddr_in6 *addr, int hoplim opt_str = (uint8_t *) (msg + sizeof(struct nd_router_advert)); while (orig_len > 0) { - int optlen; - struct nd_opt_prefix_info *pinfo; - struct nd_opt_route_info_local *rinfo; - struct nd_opt_rdnss_info_local *rdnss_info; - struct nd_opt_dnssl_info_local *dnssl_info; char prefix_str[INET6_ADDRSTRLEN]; - char suffix[256]; - int offset, label_len; if (orig_len < 2) { flog(LOG_ERR, "trailing garbage in RA from %s", addr_str); break; } - optlen = (opt_str[1] << 3); + int optlen = (opt_str[1] << 3); if (optlen == 0) { flog(LOG_ERR, "zero length option in RA"); break; } else if (optlen > orig_len) { - flog(LOG_ERR, "option length greater than total" " length in RA (type %d, optlen %d, len %d)", (int)*opt_str, optlen, orig_len); + flog(LOG_ERR, "option length greater than total" " length in RA (type %d, optlen %d, len %d)", + (int)*opt_str, optlen, orig_len); break; } switch (*opt_str) { - case ND_OPT_PREFIX_INFORMATION: - pinfo = (struct nd_opt_prefix_info *)opt_str; + case ND_OPT_PREFIX_INFORMATION:{ + struct nd_opt_prefix_info *pinfo = (struct nd_opt_prefix_info *)opt_str; addrtostr(&pinfo->nd_opt_pi_prefix, prefix_str, sizeof(prefix_str)); - printf("\n\tprefix %s/%d\n\t{\n", prefix_str, pinfo->nd_opt_pi_prefix_len); - - if (ntohl(pinfo->nd_opt_pi_valid_time) == 0xffffffff) { - if (!edefs || DFLT_AdvValidLifetime != 0xffffffff) - printf("\t\tAdvValidLifetime infinity; # (0xffffffff)\n"); - } else { - if (!edefs || DFLT_AdvValidLifetime != ntohl(pinfo->nd_opt_pi_valid_time)) - printf("\t\tAdvValidLifetime %u;\n", ntohl(pinfo->nd_opt_pi_valid_time)); - } - if (ntohl(pinfo->nd_opt_pi_preferred_time) == 0xffffffff) { - if (!edefs || DFLT_AdvPreferredLifetime != 0xffffffff) - printf("\t\tAdvPreferredLifetime infinity; # (0xffffffff)\n"); - } else { - if (!edefs || DFLT_AdvPreferredLifetime != ntohl(pinfo->nd_opt_pi_preferred_time)) - printf("\t\tAdvPreferredLifetime %u;\n", ntohl(pinfo->nd_opt_pi_preferred_time)); - } + printf("\n\tprefix %s/%d\n\t{\n", prefix_str, pinfo->nd_opt_pi_prefix_len); - if (!edefs || DFLT_AdvOnLinkFlag != (ND_OPT_PI_FLAG_ONLINK == (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK))) - printf("\t\tAdvOnLink %s;\n", (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) ? "on" : "off"); + if (ntohl(pinfo->nd_opt_pi_valid_time) == 0xffffffff) { + if (!edefs || DFLT_AdvValidLifetime != 0xffffffff) + printf("\t\tAdvValidLifetime infinity; # (0xffffffff)\n"); + } else { + if (!edefs || DFLT_AdvValidLifetime != ntohl(pinfo->nd_opt_pi_valid_time)) + printf("\t\tAdvValidLifetime %u;\n", ntohl(pinfo->nd_opt_pi_valid_time)); + } + if (ntohl(pinfo->nd_opt_pi_preferred_time) == 0xffffffff) { + if (!edefs || DFLT_AdvPreferredLifetime != 0xffffffff) + printf("\t\tAdvPreferredLifetime infinity; # (0xffffffff)\n"); + } else { + if (!edefs || DFLT_AdvPreferredLifetime != ntohl(pinfo->nd_opt_pi_preferred_time)) + printf("\t\tAdvPreferredLifetime %u;\n", + ntohl(pinfo->nd_opt_pi_preferred_time)); + } - if (!edefs || DFLT_AdvAutonomousFlag != (ND_OPT_PI_FLAG_AUTO == (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO))) - printf("\t\tAdvAutonomous %s;\n", (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) ? "on" : "off"); + if (!edefs + || DFLT_AdvOnLinkFlag != (ND_OPT_PI_FLAG_ONLINK == + (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK))) + printf("\t\tAdvOnLink %s;\n", + (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) ? "on" : "off"); + + if (!edefs + || DFLT_AdvAutonomousFlag != (ND_OPT_PI_FLAG_AUTO == + (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO))) + printf("\t\tAdvAutonomous %s;\n", + (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) ? "on" : "off"); + + /* Mobile IPv6 ext */ + if (!edefs + || DFLT_AdvRouterAddr != (ND_OPT_PI_FLAG_RADDR == + (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_RADDR))) + printf("\t\tAdvRouterAddr %s;\n", + (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_RADDR) ? "on" : "off"); + + printf("\t}; # End of prefix definition\n\n"); + break; + } + case ND_OPT_ROUTE_INFORMATION:{ + struct nd_opt_route_info_local *rinfo = (struct nd_opt_route_info_local *)opt_str; + + if (optlen == 8) { + printf("\n\troute ::/0\n\t{\n"); + } else { + struct in6_addr addr; + memset(&addr, 0, sizeof(addr)); + memcpy(&addr, &rinfo->nd_opt_ri_prefix, 8); addrtostr(&addr, prefix_str, sizeof(prefix_str)); + printf("\n\troute %s/%d\n\t{\n", prefix_str, rinfo->nd_opt_ri_prefix_len); + } - /* Mobile IPv6 ext */ - if (!edefs || DFLT_AdvRouterAddr != (ND_OPT_PI_FLAG_RADDR == (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_RADDR))) - printf("\t\tAdvRouterAddr %s;\n", (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_RADDR) ? "on" : "off"); + if (!edefs + || (((radvert->nd_ra_flags_reserved & 0x18) >> 3) & 0xff) != DFLT_AdvRoutePreference) { + printf("\t\tAdvRoutePreference "); + print_preferences(((rinfo->nd_opt_ri_flags_reserved & 0x18) >> 3) & 0xff); + printf(";\n"); + } - printf("\t}; # End of prefix definition\n\n"); - break; - case ND_OPT_ROUTE_INFORMATION: - rinfo = (struct nd_opt_route_info_local *)opt_str; - - if (optlen == 8) { - printf("\n\troute ::/0\n\t{\n"); - } else { - struct in6_addr addr; - memset(&addr, 0, sizeof(addr)); - memcpy(&addr, &rinfo->nd_opt_ri_prefix, 8); - printf("\n\troute %s/%d\n\t{\n", prefix_str, rinfo->nd_opt_ri_prefix_len); - } + /* XXX: we check DFLT_AdvRouteLifetime by interface, and it's outside of context here */ + if (ntohl(rinfo->nd_opt_ri_lifetime) == 0xffffffff) + printf("\t\tAdvRouteLifetime infinity; # (0xffffffff)\n"); + else + printf("\t\tAdvRouteLifetime %u;\n", ntohl(rinfo->nd_opt_ri_lifetime)); - if (!edefs || (((radvert->nd_ra_flags_reserved & 0x18) >> 3) & 0xff) != DFLT_AdvRoutePreference) { - printf("\t\tAdvRoutePreference "); - print_preferences(((rinfo->nd_opt_ri_flags_reserved & 0x18) >> 3) & 0xff); - printf(";\n"); + printf("\t}; # End of route definition\n\n"); + break; } + case ND_OPT_RDNSS_INFORMATION:{ + struct nd_opt_rdnss_info_local *rdnss_info = (struct nd_opt_rdnss_info_local *)opt_str; - /* XXX: we check DFLT_AdvRouteLifetime by interface, and it's outside of context here */ - if (ntohl(rinfo->nd_opt_ri_lifetime) == 0xffffffff) - printf("\t\tAdvRouteLifetime infinity; # (0xffffffff)\n"); - else - printf("\t\tAdvRouteLifetime %u;\n", ntohl(rinfo->nd_opt_ri_lifetime)); + printf("\n\tRDNSS"); addrtostr(&rdnss_info->nd_opt_rdnssi_addr1, prefix_str, sizeof(prefix_str)); - printf("\t}; # End of route definition\n\n"); - break; - case ND_OPT_RDNSS_INFORMATION: - rdnss_info = (struct nd_opt_rdnss_info_local *)opt_str; + printf(" %s", prefix_str); - printf("\n\tRDNSS"); + if (rdnss_info->nd_opt_rdnssi_len >= 5) { addrtostr(&rdnss_info->nd_opt_rdnssi_addr2, prefix_str, sizeof(prefix_str)); + printf(" %s", prefix_str); + } + if (rdnss_info->nd_opt_rdnssi_len >= 7) { addrtostr(&rdnss_info->nd_opt_rdnssi_addr3, prefix_str, sizeof(prefix_str)); + printf(" %s", prefix_str); + } - printf(" %s", prefix_str); + printf("\n\t{\n"); + /* as AdvRDNSSLifetime may depend on MaxRtrAdvInterval, it could change */ + if (ntohl(rdnss_info->nd_opt_rdnssi_lifetime) == 0xffffffff) + printf("\t\tAdvRDNSSLifetime infinity; # (0xffffffff)\n"); + else + printf("\t\tAdvRDNSSLifetime %u;\n", ntohl(rdnss_info->nd_opt_rdnssi_lifetime)); - if (rdnss_info->nd_opt_rdnssi_len >= 5) { - printf(" %s", prefix_str); - } - if (rdnss_info->nd_opt_rdnssi_len >= 7) { - printf(" %s", prefix_str); + printf("\t}; # End of RDNSS definition\n\n"); + break; } + case ND_OPT_DNSSL_INFORMATION:{ + struct nd_opt_dnssl_info_local *dnssl_info = (struct nd_opt_dnssl_info_local *)opt_str; - printf("\n\t{\n"); - /* as AdvRDNSSLifetime may depend on MaxRtrAdvInterval, it could change */ - if (ntohl(rdnss_info->nd_opt_rdnssi_lifetime) == 0xffffffff) - printf("\t\tAdvRDNSSLifetime infinity; # (0xffffffff)\n"); - else - printf("\t\tAdvRDNSSLifetime %u;\n", ntohl(rdnss_info->nd_opt_rdnssi_lifetime)); + printf("\n\tDNSSL"); - printf("\t}; # End of RDNSS definition\n\n"); - break; - case ND_OPT_DNSSL_INFORMATION: - dnssl_info = (struct nd_opt_dnssl_info_local *)opt_str; + for (int offset = 0; offset < (dnssl_info->nd_opt_dnssli_len - 1) * 8;) { + char suffix[256] = { "" }; + int label_len = dnssl_info->nd_opt_dnssli_suffixes[offset++]; - printf("\n\tDNSSL"); - suffix[0] = '\0'; + if (label_len == 0) { + /* + * Ignore empty suffixes. They're + * probably just padding... + */ + if (suffix[0] == '\0') + continue; - for (offset = 0; offset < (dnssl_info->nd_opt_dnssli_len - 1) * 8;) { - label_len = dnssl_info->nd_opt_dnssli_suffixes[offset++]; + printf(" %s", suffix); - if (label_len == 0) { - /* - * Ignore empty suffixes. They're - * probably just padding... - */ - if (suffix[0] == '\0') + suffix[0] = '\0'; continue; + } - printf(" %s", suffix); + if ((sizeof(suffix) - strlen(suffix)) < (label_len + 2)) { + flog(LOG_ERR, "oversized suffix in DNSSL option from %s", addr_str); + break; + } - suffix[0] = '\0'; - continue; + if (suffix[0] != '\0') + strcat(suffix, "."); + strncat(suffix, (char *)&dnssl_info->nd_opt_dnssli_suffixes[offset], label_len); + offset += label_len; } - if ((sizeof(suffix) - strlen(suffix)) < (label_len + 2)) { - flog(LOG_ERR, "oversized suffix in DNSSL option from %s", addr_str); - break; - } + printf("\n\t{\n"); + /* as AdvDNSSLLifetime may depend on MaxRtrAdvInterval, it could change */ + if (ntohl(dnssl_info->nd_opt_dnssli_lifetime) == 0xffffffff) + printf("\t\tAdvDNSSLLifetime infinity; # (0xffffffff)\n"); + else + printf("\t\tAdvDNSSLLifetime %u;\n", ntohl(dnssl_info->nd_opt_dnssli_lifetime)); - if (suffix[0] != '\0') - strcat(suffix, "."); - strncat(suffix, (char *)&dnssl_info->nd_opt_dnssli_suffixes[offset], label_len); - offset += label_len; + printf("\t}; # End of DNSSL definition\n\n"); + break; } - - printf("\n\t{\n"); - /* as AdvDNSSLLifetime may depend on MaxRtrAdvInterval, it could change */ - if (ntohl(dnssl_info->nd_opt_dnssli_lifetime) == 0xffffffff) - printf("\t\tAdvDNSSLLifetime infinity; # (0xffffffff)\n"); - else - printf("\t\tAdvDNSSLLifetime %u;\n", ntohl(dnssl_info->nd_opt_dnssli_lifetime)); - - printf("\t}; # End of DNSSL definition\n\n"); - break; default: break; } @@ -444,7 +455,7 @@ void print_ff(unsigned char *msg, int len, struct sockaddr_in6 *addr, int hoplim fflush(stdout); } -void print_preferences(int p) +static void print_preferences(int p) { switch (p) { case 0: @@ -462,7 +473,7 @@ void print_preferences(int p) } } -void version(void) +static void version(void) { fprintf(stderr, "Version: %s\n\n", VERSION); fprintf(stderr, "Please send bug reports and suggestions to %s\n", CONTACT_EMAIL); @@ -474,3 +485,4 @@ static void usage(char const * pname) fprintf(stderr, "usage: %s %s\n", pname, usage_str); exit(-1); } +