| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| * |
| * Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| #include "defs.h" |
| #include "tables.h" |
| #include <fcntl.h> |
| #include <sys/un.h> |
| |
| static void initlog(void); |
| static void run_timeouts(void); |
| |
| static void advertise(struct sockaddr_in6 *sin6, struct phyint *pi, |
| boolean_t no_prefixes); |
| static void solicit(struct sockaddr_in6 *sin6, struct phyint *pi); |
| static void initifs(boolean_t first); |
| static void check_if_removed(struct phyint *pi); |
| static void loopback_ra_enqueue(struct phyint *pi, |
| struct nd_router_advert *ra, int len); |
| static void loopback_ra_dequeue(void); |
| static void check_daemonize(void); |
| |
| struct in6_addr all_nodes_mcast = { { 0xff, 0x2, 0x0, 0x0, |
| 0x0, 0x0, 0x0, 0x0, |
| 0x0, 0x0, 0x0, 0x0, |
| 0x0, 0x0, 0x0, 0x1 } }; |
| |
| struct in6_addr all_routers_mcast = { { 0xff, 0x2, 0x0, 0x0, |
| 0x0, 0x0, 0x0, 0x0, |
| 0x0, 0x0, 0x0, 0x0, |
| 0x0, 0x0, 0x0, 0x2 } }; |
| |
| static struct sockaddr_in6 v6allnodes = { AF_INET6, 0, 0, |
| { 0xff, 0x2, 0x0, 0x0, |
| 0x0, 0x0, 0x0, 0x0, |
| 0x0, 0x0, 0x0, 0x0, |
| 0x0, 0x0, 0x0, 0x1 } }; |
| |
| static struct sockaddr_in6 v6allrouters = { AF_INET6, 0, 0, |
| { 0xff, 0x2, 0x0, 0x0, |
| 0x0, 0x0, 0x0, 0x0, |
| 0x0, 0x0, 0x0, 0x0, |
| 0x0, 0x0, 0x0, 0x2 } }; |
| |
| static char **argv0; /* Saved for re-exec on SIGHUP */ |
| |
| static uint64_t packet[(IP_MAXPACKET + 1)/8]; |
| |
| static int show_ifs = 0; |
| static boolean_t already_daemonized = _B_FALSE; |
| int debug = 0; |
| int no_loopback = 0; /* Do not send RA packets to ourselves */ |
| |
| /* |
| * Size of routing socket message used by in.ndpd which includes the header, |
| * space for the RTA_DST, RTA_GATEWAY and RTA_NETMASK (each a sockaddr_in6) |
| * plus space for the RTA_IFP (a sockaddr_dl). |
| */ |
| #define NDP_RTM_MSGLEN sizeof (struct rt_msghdr) + \ |
| sizeof (struct sockaddr_in6) + \ |
| sizeof (struct sockaddr_in6) + \ |
| sizeof (struct sockaddr_in6) + \ |
| sizeof (struct sockaddr_dl) |
| |
| /* |
| * These are referenced externally in tables.c in order to fill in the |
| * dynamic portions of the routing socket message and then to send the message |
| * itself. |
| */ |
| int rtsock = -1; /* Routing socket */ |
| struct rt_msghdr *rt_msg; /* Routing socket message */ |
| struct sockaddr_in6 *rta_gateway; /* RTA_GATEWAY sockaddr */ |
| struct sockaddr_dl *rta_ifp; /* RTA_IFP sockaddr */ |
| int mibsock = -1; /* mib request socket */ |
| |
| /* |
| * Return the current time in milliseconds truncated to |
| * fit in an integer. |
| */ |
| uint_t |
| getcurrenttime(void) |
| { |
| struct timeval tp; |
| |
| if (gettimeofday(&tp, NULL) < 0) { |
| logperror("getcurrenttime: gettimeofday failed"); |
| exit(1); |
| } |
| return (tp.tv_sec * 1000 + tp.tv_usec / 1000); |
| } |
| |
| /* |
| * Output a preformated packet from the packet[] buffer. |
| */ |
| static void |
| sendpacket(struct sockaddr_in6 *sin6, int sock, int size, int flags) |
| { |
| int cc; |
| char abuf[INET6_ADDRSTRLEN]; |
| |
| cc = sendto(sock, (char *)packet, size, flags, |
| (struct sockaddr *)sin6, sizeof (*sin6)); |
| if (cc < 0 || cc != size) { |
| if (cc < 0) { |
| logperror("sendpacket: sendto"); |
| } |
| logmsg(LOG_ERR, "sendpacket: wrote %s %d chars, ret=%d\n", |
| inet_ntop(sin6->sin6_family, |
| (void *)&sin6->sin6_addr, |
| abuf, sizeof (abuf)), |
| size, cc); |
| } |
| } |
| |
| /* |
| * If possible, place an ND_OPT_SOURCE_LINKADDR option at `optp'. |
| * Return the number of bytes placed in the option. |
| */ |
| static uint_t |
| add_opt_lla(struct phyint *pi, struct nd_opt_lla *optp) |
| { |
| uint_t optlen; |
| uint_t hwaddrlen; |
| struct lifreq lifr; |
| |
| /* If this phyint doesn't have a link-layer address, bail */ |
| if (phyint_get_lla(pi, &lifr) == -1) |
| return (0); |
| |
| hwaddrlen = lifr.lifr_nd.lnr_hdw_len; |
| /* roundup to multiple of 8 and make padding zero */ |
| optlen = ((sizeof (struct nd_opt_hdr) + hwaddrlen + 7) / 8) * 8; |
| bzero(optp, optlen); |
| optp->nd_opt_lla_type = ND_OPT_SOURCE_LINKADDR; |
| optp->nd_opt_lla_len = optlen / 8; |
| bcopy(lifr.lifr_nd.lnr_hdw_addr, optp->nd_opt_lla_hdw_addr, hwaddrlen); |
| |
| return (optlen); |
| } |
| |
| /* Send a Router Solicitation */ |
| static void |
| solicit(struct sockaddr_in6 *sin6, struct phyint *pi) |
| { |
| int packetlen = 0; |
| struct nd_router_solicit *rs = (struct nd_router_solicit *)packet; |
| char *pptr = (char *)packet; |
| |
| rs->nd_rs_type = ND_ROUTER_SOLICIT; |
| rs->nd_rs_code = 0; |
| rs->nd_rs_cksum = htons(0); |
| rs->nd_rs_reserved = htonl(0); |
| |
| packetlen += sizeof (*rs); |
| pptr += sizeof (*rs); |
| |
| /* add options */ |
| packetlen += add_opt_lla(pi, (struct nd_opt_lla *)pptr); |
| |
| if (debug & D_PKTOUT) { |
| print_route_sol("Sending solicitation to ", pi, rs, packetlen, |
| sin6); |
| } |
| sendpacket(sin6, pi->pi_sock, packetlen, 0); |
| } |
| |
| /* |
| * Send a (set of) Router Advertisements and feed them back to ourselves |
| * for processing. Unless no_prefixes is set all prefixes are included. |
| * If there are too many prefix options to fit in one packet multiple |
| * packets will be sent - each containing a subset of the prefix options. |
| */ |
| static void |
| advertise(struct sockaddr_in6 *sin6, struct phyint *pi, boolean_t no_prefixes) |
| { |
| struct nd_opt_prefix_info *po; |
| char *pptr = (char *)packet; |
| struct nd_router_advert *ra; |
| struct adv_prefix *adv_pr; |
| int packetlen = 0; |
| |
| ra = (struct nd_router_advert *)pptr; |
| ra->nd_ra_type = ND_ROUTER_ADVERT; |
| ra->nd_ra_code = 0; |
| ra->nd_ra_cksum = htons(0); |
| ra->nd_ra_curhoplimit = pi->pi_AdvCurHopLimit; |
| ra->nd_ra_flags_reserved = 0; |
| if (pi->pi_AdvManagedFlag) |
| ra->nd_ra_flags_reserved |= ND_RA_FLAG_MANAGED; |
| if (pi->pi_AdvOtherConfigFlag) |
| ra->nd_ra_flags_reserved |= ND_RA_FLAG_OTHER; |
| |
| if (pi->pi_adv_state == FINAL_ADV) |
| ra->nd_ra_router_lifetime = htons(0); |
| else |
| ra->nd_ra_router_lifetime = htons(pi->pi_AdvDefaultLifetime); |
| ra->nd_ra_reachable = htonl(pi->pi_AdvReachableTime); |
| ra->nd_ra_retransmit = htonl(pi->pi_AdvRetransTimer); |
| |
| packetlen = sizeof (*ra); |
| pptr += sizeof (*ra); |
| |
| if (pi->pi_adv_state == FINAL_ADV) { |
| if (debug & D_PKTOUT) { |
| print_route_adv("Sending advert (FINAL) to ", pi, |
| ra, packetlen, sin6); |
| } |
| sendpacket(sin6, pi->pi_sock, packetlen, 0); |
| /* Feed packet back in for router operation */ |
| loopback_ra_enqueue(pi, ra, packetlen); |
| return; |
| } |
| |
| /* add options */ |
| packetlen += add_opt_lla(pi, (struct nd_opt_lla *)pptr); |
| pptr = (char *)packet + packetlen; |
| |
| if (pi->pi_AdvLinkMTU != 0) { |
| struct nd_opt_mtu *mo = (struct nd_opt_mtu *)pptr; |
| |
| mo->nd_opt_mtu_type = ND_OPT_MTU; |
| mo->nd_opt_mtu_len = sizeof (struct nd_opt_mtu) / 8; |
| mo->nd_opt_mtu_reserved = 0; |
| mo->nd_opt_mtu_mtu = htonl(pi->pi_AdvLinkMTU); |
| |
| packetlen += sizeof (struct nd_opt_mtu); |
| pptr += sizeof (struct nd_opt_mtu); |
| } |
| |
| if (no_prefixes) { |
| if (debug & D_PKTOUT) { |
| print_route_adv("Sending advert to ", pi, |
| ra, packetlen, sin6); |
| } |
| sendpacket(sin6, pi->pi_sock, packetlen, 0); |
| /* Feed packet back in for router operation */ |
| loopback_ra_enqueue(pi, ra, packetlen); |
| return; |
| } |
| |
| po = (struct nd_opt_prefix_info *)pptr; |
| for (adv_pr = pi->pi_adv_prefix_list; adv_pr != NULL; |
| adv_pr = adv_pr->adv_pr_next) { |
| if (!adv_pr->adv_pr_AdvOnLinkFlag && |
| !adv_pr->adv_pr_AdvAutonomousFlag) { |
| continue; |
| } |
| |
| /* |
| * If the prefix doesn't fit in packet send |
| * what we have so far and start with new packet. |
| */ |
| if (packetlen + sizeof (*po) > |
| pi->pi_LinkMTU - sizeof (struct ip6_hdr)) { |
| if (debug & D_PKTOUT) { |
| print_route_adv("Sending advert " |
| "(FRAG) to ", |
| pi, ra, packetlen, sin6); |
| } |
| sendpacket(sin6, pi->pi_sock, packetlen, 0); |
| /* Feed packet back in for router operation */ |
| loopback_ra_enqueue(pi, ra, packetlen); |
| packetlen = sizeof (*ra); |
| pptr = (char *)packet + sizeof (*ra); |
| po = (struct nd_opt_prefix_info *)pptr; |
| } |
| po->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION; |
| po->nd_opt_pi_len = sizeof (*po)/8; |
| po->nd_opt_pi_flags_reserved = 0; |
| if (adv_pr->adv_pr_AdvOnLinkFlag) { |
| po->nd_opt_pi_flags_reserved |= |
| ND_OPT_PI_FLAG_ONLINK; |
| } |
| if (adv_pr->adv_pr_AdvAutonomousFlag) { |
| po->nd_opt_pi_flags_reserved |= |
| ND_OPT_PI_FLAG_AUTO; |
| } |
| po->nd_opt_pi_prefix_len = adv_pr->adv_pr_prefix_len; |
| /* |
| * If both Adv*Expiration and Adv*Lifetime are |
| * set we prefer the former and make the lifetime |
| * decrement in real time. |
| */ |
| if (adv_pr->adv_pr_AdvValidRealTime) { |
| po->nd_opt_pi_valid_time = |
| htonl(adv_pr->adv_pr_AdvValidExpiration); |
| } else { |
| po->nd_opt_pi_valid_time = |
| htonl(adv_pr->adv_pr_AdvValidLifetime); |
| } |
| if (adv_pr->adv_pr_AdvPreferredRealTime) { |
| po->nd_opt_pi_preferred_time = |
| htonl(adv_pr->adv_pr_AdvPreferredExpiration); |
| } else { |
| po->nd_opt_pi_preferred_time = |
| htonl(adv_pr->adv_pr_AdvPreferredLifetime); |
| } |
| po->nd_opt_pi_reserved2 = htonl(0); |
| po->nd_opt_pi_prefix = adv_pr->adv_pr_prefix; |
| |
| po++; |
| packetlen += sizeof (*po); |
| } |
| if (debug & D_PKTOUT) { |
| print_route_adv("Sending advert to ", pi, |
| ra, packetlen, sin6); |
| } |
| sendpacket(sin6, pi->pi_sock, packetlen, 0); |
| /* Feed packet back in for router operation */ |
| loopback_ra_enqueue(pi, ra, packetlen); |
| } |
| |
| /* Poll support */ |
| static int pollfd_num = 0; /* Allocated and initialized */ |
| static struct pollfd *pollfds = NULL; |
| |
| /* |
| * Add fd to the set being polled. Returns 0 if ok; -1 if failed. |
| */ |
| int |
| poll_add(int fd) |
| { |
| int i; |
| int new_num; |
| struct pollfd *newfds; |
| |
| /* Check if already present */ |
| for (i = 0; i < pollfd_num; i++) { |
| if (pollfds[i].fd == fd) |
| return (0); |
| } |
| /* Check for empty spot already present */ |
| for (i = 0; i < pollfd_num; i++) { |
| if (pollfds[i].fd == -1) { |
| pollfds[i].fd = fd; |
| return (0); |
| } |
| } |
| |
| /* Allocate space for 32 more fds and initialize to -1 */ |
| new_num = pollfd_num + 32; |
| newfds = realloc(pollfds, new_num * sizeof (struct pollfd)); |
| if (newfds == NULL) { |
| logperror("poll_add: realloc"); |
| return (-1); |
| } |
| |
| newfds[pollfd_num].fd = fd; |
| newfds[pollfd_num++].events = POLLIN; |
| |
| for (i = pollfd_num; i < new_num; i++) { |
| newfds[i].fd = -1; |
| newfds[i].events = POLLIN; |
| } |
| pollfd_num = new_num; |
| pollfds = newfds; |
| return (0); |
| } |
| |
| /* |
| * Remove fd from the set being polled. Returns 0 if ok; -1 if failed. |
| */ |
| int |
| poll_remove(int fd) |
| { |
| int i; |
| |
| /* Check if already present */ |
| for (i = 0; i < pollfd_num; i++) { |
| if (pollfds[i].fd == fd) { |
| pollfds[i].fd = -1; |
| return (0); |
| } |
| } |
| return (-1); |
| } |
| |
| /* |
| * Extract information about the ifname (either a physical interface and |
| * the ":0" logical interface or just a logical interface). |
| * If the interface (still) exists in kernel set pr_in_use |
| * for caller to be able to detect interfaces that are removed. |
| * Starts sending advertisements/solicitations when new physical interfaces |
| * are detected. |
| */ |
| static void |
| if_process(int s, char *ifname, boolean_t first) |
| { |
| struct lifreq lifr; |
| struct phyint *pi; |
| struct prefix *pr; |
| char *cp; |
| char phyintname[LIFNAMSIZ + 1]; |
| |
| if (debug & D_IFSCAN) |
| logmsg(LOG_DEBUG, "if_process(%s)\n", ifname); |
| |
| (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); |
| lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0'; |
| if (ioctl(s, SIOCGLIFFLAGS, (char *)&lifr) < 0) { |
| if (errno == ENXIO) { |
| /* |
| * Interface has disappeared |
| */ |
| return; |
| } |
| logperror("if_process: ioctl (get interface flags)"); |
| return; |
| } |
| |
| /* |
| * Ignore loopback and point-to-multipoint interfaces. |
| * Point-to-point interfaces always have IFF_MULTICAST set. |
| */ |
| if (!(lifr.lifr_flags & IFF_MULTICAST) || |
| (lifr.lifr_flags & IFF_LOOPBACK)) { |
| return; |
| } |
| |
| if (!(lifr.lifr_flags & IFF_IPV6)) |
| return; |
| |
| (void) strncpy(phyintname, ifname, sizeof (phyintname)); |
| phyintname[sizeof (phyintname) - 1] = '\0'; |
| if ((cp = strchr(phyintname, IF_SEPARATOR)) != NULL) { |
| *cp = '\0'; |
| } |
| |
| pi = phyint_lookup(phyintname); |
| if (pi == NULL) { |
| /* |
| * Do not add anything for new interfaces until they are UP. |
| * For existing interfaces we track the up flag. |
| */ |
| if (!(lifr.lifr_flags & IFF_UP)) |
| return; |
| |
| pi = phyint_create(phyintname); |
| if (pi == NULL) { |
| logmsg(LOG_ERR, "if_process: out of memory\n"); |
| return; |
| } |
| } |
| (void) phyint_init_from_k(pi); |
| if (pi->pi_sock == -1 && !(pi->pi_kernel_state & PI_PRESENT)) { |
| /* Interface is not yet present */ |
| if (debug & D_PHYINT) { |
| logmsg(LOG_DEBUG, "if_process: interface not yet " |
| "present %s\n", pi->pi_name); |
| } |
| return; |
| } |
| |
| if (pi->pi_sock != -1) { |
| if (poll_add(pi->pi_sock) == -1) { |
| /* |
| * reset state. |
| */ |
| phyint_cleanup(pi); |
| } |
| } |
| |
| /* |
| * Check if IFF_ROUTER has been turned off in kernel in which |
| * case we have to turn off AdvSendAdvertisements. |
| * The kernel will automatically turn off IFF_ROUTER if |
| * ip6_forwarding is turned off. |
| * Note that we do not switch back should IFF_ROUTER be turned on. |
| */ |
| if (!first && |
| pi->pi_AdvSendAdvertisements && !(pi->pi_flags & IFF_ROUTER)) { |
| logmsg(LOG_INFO, "No longer a router on %s\n", pi->pi_name); |
| check_to_advertise(pi, START_FINAL_ADV); |
| |
| pi->pi_AdvSendAdvertisements = 0; |
| pi->pi_sol_state = NO_SOLICIT; |
| } |
| |
| /* |
| * Send advertisments and solicitation only if the interface is |
| * present in the kernel. |
| */ |
| if (pi->pi_kernel_state & PI_PRESENT) { |
| |
| if (pi->pi_AdvSendAdvertisements) { |
| if (pi->pi_adv_state == NO_ADV) |
| check_to_advertise(pi, START_INIT_ADV); |
| } else { |
| if (pi->pi_sol_state == NO_SOLICIT) |
| check_to_solicit(pi, START_INIT_SOLICIT); |
| } |
| } |
| |
| /* |
| * Track static kernel prefixes to prevent in.ndpd from clobbering |
| * them by creating a struct prefix for each prefix detected in the |
| * kernel. |
| */ |
| pr = prefix_lookup_name(pi, ifname); |
| if (pr == NULL) { |
| pr = prefix_create_name(pi, ifname); |
| if (pr == NULL) { |
| logmsg(LOG_ERR, "if_process: out of memory\n"); |
| return; |
| } |
| if (prefix_init_from_k(pr) == -1) { |
| prefix_delete(pr); |
| return; |
| } |
| } |
| /* Detect prefixes which are removed */ |
| if (pr->pr_kernel_state != 0) |
| pr->pr_in_use = _B_TRUE; |
| |
| if ((lifr.lifr_flags & IFF_DUPLICATE) && |
| !(lifr.lifr_flags & IFF_DHCPRUNNING) && |
| (pr->pr_flags & IFF_TEMPORARY)) { |
| in6_addr_t *token; |
| int i; |
| char abuf[INET6_ADDRSTRLEN]; |
| |
| if (++pr->pr_attempts >= MAX_DAD_FAILURES) { |
| logmsg(LOG_ERR, "%s: token %s is duplicate after %d " |
| "attempts; disabling temporary addresses on %s", |
| pr->pr_name, inet_ntop(AF_INET6, |
| (void *)&pi->pi_tmp_token, abuf, sizeof (abuf)), |
| pr->pr_attempts, pi->pi_name); |
| pi->pi_TmpAddrsEnabled = 0; |
| tmptoken_delete(pi); |
| prefix_delete(pr); |
| return; |
| } |
| logmsg(LOG_WARNING, "%s: token %s is duplicate; trying again", |
| pr->pr_name, inet_ntop(AF_INET6, (void *)&pi->pi_tmp_token, |
| abuf, sizeof (abuf))); |
| if (!tmptoken_create(pi)) { |
| prefix_delete(pr); |
| return; |
| } |
| token = &pi->pi_tmp_token; |
| for (i = 0; i < 16; i++) { |
| /* |
| * prefix_create ensures that pr_prefix has all-zero |
| * bits after prefixlen. |
| */ |
| pr->pr_address.s6_addr[i] = pr->pr_prefix.s6_addr[i] | |
| token->s6_addr[i]; |
| } |
| if (prefix_lookup_addr_match(pr) != NULL) { |
| prefix_delete(pr); |
| return; |
| } |
| pr->pr_CreateTime = getcurrenttime() / MILLISEC; |
| /* |
| * We've got a new token. Clearing PR_AUTO causes |
| * prefix_update_k to bring the interface up and set the |
| * address. |
| */ |
| pr->pr_kernel_state &= ~PR_AUTO; |
| prefix_update_k(pr); |
| } |
| } |
| |
| static int ifsock = -1; |
| |
| /* |
| * Scan all interfaces to detect changes as well as new and deleted intefaces |
| * 'first' is set for the initial call only. Do not effect anything. |
| */ |
| static void |
| initifs(boolean_t first) |
| { |
| char *buf; |
| int bufsize; |
| int numifs; |
| int n; |
| struct lifnum lifn; |
| struct lifconf lifc; |
| struct lifreq *lifr; |
| struct phyint *pi; |
| struct phyint *next_pi; |
| struct prefix *pr; |
| |
| if (debug & D_IFSCAN) |
| logmsg(LOG_DEBUG, "Reading interface configuration\n"); |
| if (ifsock < 0) { |
| ifsock = socket(AF_INET6, SOCK_DGRAM, 0); |
| if (ifsock < 0) { |
| logperror("initifs: socket"); |
| return; |
| } |
| } |
| lifn.lifn_family = AF_INET6; |
| lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY; |
| if (ioctl(ifsock, SIOCGLIFNUM, (char *)&lifn) < 0) { |
| logperror("initifs: ioctl (get interface numbers)"); |
| return; |
| } |
| numifs = lifn.lifn_count; |
| bufsize = numifs * sizeof (struct lifreq); |
| |
| buf = (char *)malloc(bufsize); |
| if (buf == NULL) { |
| logmsg(LOG_ERR, "initifs: out of memory\n"); |
| return; |
| } |
| |
| /* |
| * Mark the interfaces so that we can find phyints and prefixes |
| * which have disappeared from the kernel. |
| * if_process will set pr_in_use when it finds the interface |
| * in the kernel. |
| */ |
| for (pi = phyints; pi != NULL; pi = pi->pi_next) { |
| /* |
| * Before re-examining the state of the interfaces, |
| * PI_PRESENT should be cleared from pi_kernel_state. |
| */ |
| pi->pi_kernel_state &= ~PI_PRESENT; |
| for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) { |
| pr->pr_in_use = _B_FALSE; |
| } |
| } |
| |
| lifc.lifc_family = AF_INET6; |
| lifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY; |
| lifc.lifc_len = bufsize; |
| lifc.lifc_buf = buf; |
| |
| if (ioctl(ifsock, SIOCGLIFCONF, (char *)&lifc) < 0) { |
| logperror("initifs: ioctl (get interface configuration)"); |
| free(buf); |
| return; |
| } |
| |
| lifr = (struct lifreq *)lifc.lifc_req; |
| for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifr++) |
| if_process(ifsock, lifr->lifr_name, first); |
| free(buf); |
| |
| /* |
| * Detect phyints that have been removed from the kernel. |
| * Since we can't recreate it here (would require ifconfig plumb |
| * logic) we just terminate use of that phyint. |
| */ |
| for (pi = phyints; pi != NULL; pi = next_pi) { |
| next_pi = pi->pi_next; |
| /* |
| * If interface (still) exists in kernel, set |
| * pi_state to indicate that. |
| */ |
| if (pi->pi_kernel_state & PI_PRESENT) { |
| pi->pi_state |= PI_PRESENT; |
| } |
| |
| check_if_removed(pi); |
| } |
| if (show_ifs) |
| phyint_print_all(); |
| } |
| |
| |
| /* |
| * Router advertisement state machine. Used for everything but timer |
| * events which use advertise_event directly. |
| */ |
| void |
| check_to_advertise(struct phyint *pi, enum adv_events event) |
| { |
| uint_t delay; |
| enum adv_states old_state = pi->pi_adv_state; |
| |
| if (debug & D_STATE) { |
| logmsg(LOG_DEBUG, "check_to_advertise(%s, %d) state %d\n", |
| pi->pi_name, (int)event, (int)old_state); |
| } |
| delay = advertise_event(pi, event, 0); |
| if (delay != TIMER_INFINITY) { |
| /* Make sure the global next event is updated */ |
| timer_schedule(delay); |
| } |
| |
| if (debug & D_STATE) { |
| logmsg(LOG_DEBUG, "check_to_advertise(%s, %d) state %d -> %d\n", |
| pi->pi_name, (int)event, (int)old_state, |
| (int)pi->pi_adv_state); |
| } |
| } |
| |
| /* |
| * Router advertisement state machine. |
| * Return the number of milliseconds until next timeout (TIMER_INFINITY |
| * if never). |
| * For the ADV_TIMER event the caller passes in the number of milliseconds |
| * since the last timer event in the 'elapsed' parameter. |
| */ |
| uint_t |
| advertise_event(struct phyint *pi, enum adv_events event, uint_t elapsed) |
| { |
| uint_t delay; |
| |
| if (debug & D_STATE) { |
| logmsg(LOG_DEBUG, "advertise_event(%s, %d, %d) state %d\n", |
| pi->pi_name, (int)event, elapsed, (int)pi->pi_adv_state); |
| } |
| check_daemonize(); |
| if (!pi->pi_AdvSendAdvertisements) |
| return (TIMER_INFINITY); |
| if (pi->pi_flags & IFF_NORTEXCH) { |
| if (debug & D_PKTOUT) { |
| logmsg(LOG_DEBUG, "Suppress sending RA packet on %s " |
| "(no route exchange on interface)\n", |
| pi->pi_name); |
| } |
| return (TIMER_INFINITY); |
| } |
| |
| switch (event) { |
| case ADV_OFF: |
| pi->pi_adv_state = NO_ADV; |
| return (TIMER_INFINITY); |
| |
| case START_INIT_ADV: |
| if (pi->pi_adv_state == INIT_ADV) |
| return (pi->pi_adv_time_left); |
| pi->pi_adv_count = ND_MAX_INITIAL_RTR_ADVERTISEMENTS; |
| pi->pi_adv_time_left = 0; |
| pi->pi_adv_state = INIT_ADV; |
| break; /* send advertisement */ |
| |
| case START_FINAL_ADV: |
| if (pi->pi_adv_state == NO_ADV) |
| return (TIMER_INFINITY); |
| if (pi->pi_adv_state == FINAL_ADV) |
| return (pi->pi_adv_time_left); |
| pi->pi_adv_count = ND_MAX_FINAL_RTR_ADVERTISEMENTS; |
| pi->pi_adv_time_left = 0; |
| pi->pi_adv_state = FINAL_ADV; |
| break; /* send advertisement */ |
| |
| case RECEIVED_SOLICIT: |
| if (pi->pi_adv_state == NO_ADV) |
| return (TIMER_INFINITY); |
| if (pi->pi_adv_state == SOLICIT_ADV) { |
| if (pi->pi_adv_time_left != 0) |
| return (pi->pi_adv_time_left); |
| break; |
| } |
| delay = GET_RANDOM(0, ND_MAX_RA_DELAY_TIME); |
| if (delay < pi->pi_adv_time_left) |
| pi->pi_adv_time_left = delay; |
| if (pi->pi_adv_time_since_sent < ND_MIN_DELAY_BETWEEN_RAS) { |
| /* |
| * Send an advertisement (ND_MIN_DELAY_BETWEEN_RAS |
| * plus random delay) after the previous |
| * advertisement was sent. |
| */ |
| pi->pi_adv_time_left = delay + |
| ND_MIN_DELAY_BETWEEN_RAS - |
| pi->pi_adv_time_since_sent; |
| } |
| pi->pi_adv_state = SOLICIT_ADV; |
| break; |
| |
| case ADV_TIMER: |
| if (pi->pi_adv_state == NO_ADV) |
| return (TIMER_INFINITY); |
| /* Decrease time left */ |
| if (pi->pi_adv_time_left >= elapsed) |
| pi->pi_adv_time_left -= elapsed; |
| else |
| pi->pi_adv_time_left = 0; |
| |
| /* Increase time since last advertisement was sent */ |
| pi->pi_adv_time_since_sent += elapsed; |
| break; |
| default: |
| logmsg(LOG_ERR, "advertise_event: Unknown event %d\n", |
| (int)event); |
| return (TIMER_INFINITY); |
| } |
| |
| if (pi->pi_adv_time_left != 0) |
| return (pi->pi_adv_time_left); |
| |
| /* Send advertisement and calculate next time to send */ |
| if (pi->pi_adv_state == FINAL_ADV) { |
| /* Omit the prefixes */ |
| advertise(&v6allnodes, pi, _B_TRUE); |
| } else { |
| advertise(&v6allnodes, pi, _B_FALSE); |
| } |
| pi->pi_adv_time_since_sent = 0; |
| |
| switch (pi->pi_adv_state) { |
| case SOLICIT_ADV: |
| /* |
| * The solicited advertisement has been sent. |
| * Revert to periodic advertisements. |
| */ |
| pi->pi_adv_state = REG_ADV; |
| /* FALLTHRU */ |
| case REG_ADV: |
| pi->pi_adv_time_left = |
| GET_RANDOM(1000 * pi->pi_MinRtrAdvInterval, |
| 1000 * pi->pi_MaxRtrAdvInterval); |
| break; |
| |
| case INIT_ADV: |
| if (--pi->pi_adv_count > 0) { |
| delay = GET_RANDOM(1000 * pi->pi_MinRtrAdvInterval, |
| 1000 * pi->pi_MaxRtrAdvInterval); |
| if (delay > ND_MAX_INITIAL_RTR_ADVERT_INTERVAL) |
| delay = ND_MAX_INITIAL_RTR_ADVERT_INTERVAL; |
| pi->pi_adv_time_left = delay; |
| } else { |
| pi->pi_adv_time_left = |
| GET_RANDOM(1000 * pi->pi_MinRtrAdvInterval, |
| 1000 * pi->pi_MaxRtrAdvInterval); |
| pi->pi_adv_state = REG_ADV; |
| } |
| break; |
| |
| case FINAL_ADV: |
| if (--pi->pi_adv_count > 0) { |
| pi->pi_adv_time_left = |
| ND_MAX_INITIAL_RTR_ADVERT_INTERVAL; |
| } else { |
| pi->pi_adv_state = NO_ADV; |
| } |
| break; |
| } |
| if (pi->pi_adv_state != NO_ADV) |
| return (pi->pi_adv_time_left); |
| else |
| return (TIMER_INFINITY); |
| } |
| |
| /* |
| * Router solicitation state machine. Used for everything but timer |
| * events which use solicit_event directly. |
| */ |
| void |
| check_to_solicit(struct phyint *pi, enum solicit_events event) |
| { |
| uint_t delay; |
| enum solicit_states old_state = pi->pi_sol_state; |
| |
| if (debug & D_STATE) { |
| logmsg(LOG_DEBUG, "check_to_solicit(%s, %d) state %d\n", |
| pi->pi_name, (int)event, (int)old_state); |
| } |
| delay = solicit_event(pi, event, 0); |
| if (delay != TIMER_INFINITY) { |
| /* Make sure the global next event is updated */ |
| timer_schedule(delay); |
| } |
| |
| if (debug & D_STATE) { |
| logmsg(LOG_DEBUG, "check_to_solicit(%s, %d) state %d -> %d\n", |
| pi->pi_name, (int)event, (int)old_state, |
| (int)pi->pi_sol_state); |
| } |
| } |
| |
| static void |
| daemonize_ndpd(void) |
| { |
| FILE *pidfp; |
| mode_t pidmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); /* 0644 */ |
| struct itimerval it; |
| boolean_t timerval = _B_TRUE; |
| |
| /* |
| * Need to get current timer settings so they can be restored |
| * after the fork(), as the it_value and it_interval values for |
| * the ITIMER_REAL timer are reset to 0 in the child process. |
| */ |
| if (getitimer(ITIMER_REAL, &it) < 0) { |
| if (debug & D_TIMER) |
| logmsg(LOG_DEBUG, |
| "daemonize_ndpd: failed to get itimerval\n"); |
| timerval = _B_FALSE; |
| } |
| |
| /* Daemonize. */ |
| switch (fork()) { |
| case 0: |
| /* Child */ |
| break; |
| case -1: |
| logperror("fork"); |
| exit(1); |
| default: |
| /* Parent */ |
| _exit(0); |
| } |
| |
| /* Store our process id, blow away any existing file if it exists. */ |
| if ((pidfp = fopen(PATH_PID, "w")) == NULL) { |
| (void) fprintf(stderr, "%s: unable to open " PATH_PID ": %s\n", |
| argv0[0], strerror(errno)); |
| } else { |
| (void) fprintf(pidfp, "%ld\n", getpid()); |
| (void) fclose(pidfp); |
| (void) chmod(PATH_PID, pidmode); |
| } |
| |
| (void) close(0); |
| (void) close(1); |
| (void) close(2); |
| |
| (void) chdir("/"); |
| (void) open("/dev/null", O_RDWR); |
| (void) dup2(0, 1); |
| (void) dup2(0, 2); |
| (void) setsid(); |
| |
| already_daemonized = _B_TRUE; |
| |
| /* |
| * Restore timer values, if we were able to save them; if not, |
| * check and set the right value by calling run_timeouts(). |
| */ |
| if (timerval) { |
| if (setitimer(ITIMER_REAL, &it, NULL) < 0) { |
| logperror("daemonize_ndpd: setitimer"); |
| exit(2); |
| } |
| } else { |
| run_timeouts(); |
| } |
| } |
| |
| /* |
| * Check to see if the time is right to daemonize. The right time is when: |
| * |
| * 1. We haven't already daemonized. |
| * 2. We are not in debug mode. |
| * 3. All interfaces are marked IFF_NOXMIT. |
| * 4. All non-router interfaces have their prefixes set up and we're |
| * done sending router solicitations on those interfaces without |
| * prefixes. |
| */ |
| static void |
| check_daemonize(void) |
| { |
| struct phyint *pi; |
| |
| if (already_daemonized || debug != 0) |
| return; |
| |
| for (pi = phyints; pi != NULL; pi = pi->pi_next) { |
| if (!(pi->pi_flags & IFF_NOXMIT)) |
| break; |
| } |
| |
| /* |
| * If we can't transmit on any of the interfaces there is no reason |
| * to hold up progress. |
| */ |
| if (pi == NULL) { |
| daemonize_ndpd(); |
| return; |
| } |
| |
| /* Check all interfaces. If any are still soliciting, just return. */ |
| for (pi = phyints; pi != NULL; pi = pi->pi_next) { |
| if (pi->pi_AdvSendAdvertisements || |
| !(pi->pi_kernel_state & PI_PRESENT)) |
| continue; |
| |
| if (pi->pi_sol_state == INIT_SOLICIT) |
| return; |
| } |
| |
| daemonize_ndpd(); |
| } |
| |
| /* |
| * Router solicitation state machine. |
| * Return the number of milliseconds until next timeout (TIMER_INFINITY |
| * if never). |
| * For the SOL_TIMER event the caller passes in the number of milliseconds |
| * since the last timer event in the 'elapsed' parameter. |
| */ |
| uint_t |
| solicit_event(struct phyint *pi, enum solicit_events event, uint_t elapsed) |
| { |
| if (debug & D_STATE) { |
| logmsg(LOG_DEBUG, "solicit_event(%s, %d, %d) state %d\n", |
| pi->pi_name, (int)event, elapsed, (int)pi->pi_sol_state); |
| } |
| |
| if (pi->pi_AdvSendAdvertisements) |
| return (TIMER_INFINITY); |
| if (pi->pi_flags & IFF_NORTEXCH) { |
| if (debug & D_PKTOUT) { |
| logmsg(LOG_DEBUG, "Suppress sending RS packet on %s " |
| "(no route exchange on interface)\n", |
| pi->pi_name); |
| } |
| return (TIMER_INFINITY); |
| } |
| |
| switch (event) { |
| case SOLICIT_OFF: |
| pi->pi_sol_state = NO_SOLICIT; |
| check_daemonize(); |
| return (TIMER_INFINITY); |
| |
| case SOLICIT_DONE: |
| pi->pi_sol_state = DONE_SOLICIT; |
| check_daemonize(); |
| return (TIMER_INFINITY); |
| |
| case RESTART_INIT_SOLICIT: |
| /* |
| * This event allows us to start solicitation over again |
| * without losing the RA flags. We start solicitation over |
| * when we are missing an interface prefix for a newly- |
| * encountered DHCP interface. |
| */ |
| if (pi->pi_sol_state == INIT_SOLICIT) |
| return (pi->pi_sol_time_left); |
| pi->pi_sol_count = ND_MAX_RTR_SOLICITATIONS; |
| pi->pi_sol_time_left = |
| GET_RANDOM(0, ND_MAX_RTR_SOLICITATION_DELAY); |
| pi->pi_sol_state = INIT_SOLICIT; |
| break; |
| |
| case START_INIT_SOLICIT: |
| if (pi->pi_sol_state == INIT_SOLICIT) |
| return (pi->pi_sol_time_left); |
| pi->pi_ra_flags = 0; |
| pi->pi_sol_count = ND_MAX_RTR_SOLICITATIONS; |
| pi->pi_sol_time_left = |
| GET_RANDOM(0, ND_MAX_RTR_SOLICITATION_DELAY); |
| pi->pi_sol_state = INIT_SOLICIT; |
| break; |
| |
| case SOL_TIMER: |
| if (pi->pi_sol_state == NO_SOLICIT) |
| return (TIMER_INFINITY); |
| /* Decrease time left */ |
| if (pi->pi_sol_time_left >= elapsed) |
| pi->pi_sol_time_left -= elapsed; |
| else |
| pi->pi_sol_time_left = 0; |
| break; |
| default: |
| logmsg(LOG_ERR, "solicit_event: Unknown event %d\n", |
| (int)event); |
| return (TIMER_INFINITY); |
| } |
| |
| if (pi->pi_sol_time_left != 0) |
| return (pi->pi_sol_time_left); |
| |
| /* Send solicitation and calculate next time */ |
| switch (pi->pi_sol_state) { |
| case INIT_SOLICIT: |
| solicit(&v6allrouters, pi); |
| if (--pi->pi_sol_count == 0) { |
| if (debug & D_STATE) { |
| logmsg(LOG_DEBUG, "solicit_event: no routers " |
| "found on %s; assuming default flags\n", |
| pi->pi_name); |
| } |
| if (pi->pi_StatefulAddrConf) { |
| pi->pi_ra_flags |= ND_RA_FLAG_MANAGED | |
| ND_RA_FLAG_OTHER; |
| start_dhcp(pi); |
| } |
| pi->pi_sol_state = DONE_SOLICIT; |
| check_daemonize(); |
| return (TIMER_INFINITY); |
| } |
| pi->pi_sol_time_left = ND_RTR_SOLICITATION_INTERVAL; |
| return (pi->pi_sol_time_left); |
| case NO_SOLICIT: |
| case DONE_SOLICIT: |
| return (TIMER_INFINITY); |
| default: |
| return (pi->pi_sol_time_left); |
| } |
| } |
| |
| /* |
| * Timer mechanism using relative time (in milliseconds) from the |
| * previous timer event. Timers exceeding TIMER_INFINITY milliseconds |
| * will fire after TIMER_INFINITY milliseconds. |
| */ |
| static uint_t timer_previous; /* When last SIGALRM occurred */ |
| static uint_t timer_next; /* Currently scheduled timeout */ |
| |
| static void |
| timer_init(void) |
| { |
| timer_previous = getcurrenttime(); |
| timer_next = TIMER_INFINITY; |
| run_timeouts(); |
| } |
| |
| /* |
| * Make sure the next SIGALRM occurs delay milliseconds from the current |
| * time if not earlier. |
| * Handles getcurrenttime (32 bit integer holding milliseconds) wraparound |
| * by treating differences greater than 0x80000000 as negative. |
| */ |
| void |
| timer_schedule(uint_t delay) |
| { |
| uint_t now; |
| struct itimerval itimerval; |
| |
| now = getcurrenttime(); |
| if (debug & D_TIMER) { |
| logmsg(LOG_DEBUG, "timer_schedule(%u): now %u next %u\n", |
| delay, now, timer_next); |
| } |
| /* Will this timer occur before the currently scheduled SIGALRM? */ |
| if (delay >= timer_next - now) { |
| if (debug & D_TIMER) { |
| logmsg(LOG_DEBUG, "timer_schedule(%u): no action - " |
| "next in %u ms\n", |
| delay, timer_next - now); |
| } |
| return; |
| } |
| if (delay == 0) { |
| /* Minimum allowed delay */ |
| delay = 1; |
| } |
| timer_next = now + delay; |
| |
| itimerval.it_value.tv_sec = delay / 1000; |
| itimerval.it_value.tv_usec = (delay % 1000) * 1000; |
| itimerval.it_interval.tv_sec = 0; |
| itimerval.it_interval.tv_usec = 0; |
| if (debug & D_TIMER) { |
| logmsg(LOG_DEBUG, "timer_schedule(%u): sec %lu usec %lu\n", |
| delay, |
| itimerval.it_value.tv_sec, itimerval.it_value.tv_usec); |
| } |
| if (setitimer(ITIMER_REAL, &itimerval, NULL) < 0) { |
| logperror("timer_schedule: setitimer"); |
| exit(2); |
| } |
| } |
| |
| /* |
| * Conditional running of timer. If more than 'minimal_time' millseconds |
| * since the timer routines were last run we run them. |
| * Used when packets arrive. |
| */ |
| static void |
| conditional_run_timeouts(uint_t minimal_time) |
| { |
| uint_t now; |
| uint_t elapsed; |
| |
| now = getcurrenttime(); |
| elapsed = now - timer_previous; |
| if (elapsed > minimal_time) { |
| if (debug & D_TIMER) { |
| logmsg(LOG_DEBUG, "conditional_run_timeouts: " |
| "elapsed %d\n", elapsed); |
| } |
| run_timeouts(); |
| } |
| } |
| |
| /* |
| * Timer has fired. |
| * Determine when the next timer event will occur by asking all |
| * the timer routines. |
| * Should not be called from a timer routine but in some cases this is |
| * done because the code doesn't know that e.g. it was called from |
| * ifconfig_timer(). In this case the nested run_timeouts will just return but |
| * the running run_timeouts will ensure to call all the timer functions by |
| * looping once more. |
| */ |
| static void |
| run_timeouts(void) |
| { |
| uint_t now; |
| uint_t elapsed; |
| uint_t next; |
| uint_t nexti; |
| struct phyint *pi; |
| struct phyint *next_pi; |
| struct prefix *pr; |
| struct prefix *next_pr; |
| struct adv_prefix *adv_pr; |
| struct adv_prefix *next_adv_pr; |
| struct router *dr; |
| struct router *next_dr; |
| static boolean_t timeout_running; |
| static boolean_t do_retry; |
| |
| if (timeout_running) { |
| if (debug & D_TIMER) |
| logmsg(LOG_DEBUG, "run_timeouts: nested call\n"); |
| do_retry = _B_TRUE; |
| return; |
| } |
| timeout_running = _B_TRUE; |
| retry: |
| /* How much time since the last time we were called? */ |
| now = getcurrenttime(); |
| elapsed = now - timer_previous; |
| timer_previous = now; |
| |
| if (debug & D_TIMER) |
| logmsg(LOG_DEBUG, "run_timeouts: elapsed %d\n", elapsed); |
| |
| next = TIMER_INFINITY; |
| for (pi = phyints; pi != NULL; pi = next_pi) { |
| next_pi = pi->pi_next; |
| nexti = phyint_timer(pi, elapsed); |
| if (nexti != TIMER_INFINITY && nexti < next) |
| next = nexti; |
| if (debug & D_TIMER) { |
| logmsg(LOG_DEBUG, "run_timeouts (pi %s): %d -> %u ms\n", |
| pi->pi_name, nexti, next); |
| } |
| for (pr = pi->pi_prefix_list; pr != NULL; pr = next_pr) { |
| next_pr = pr->pr_next; |
| nexti = prefix_timer(pr, elapsed); |
| if (nexti != TIMER_INFINITY && nexti < next) |
| next = nexti; |
| if (debug & D_TIMER) { |
| logmsg(LOG_DEBUG, "run_timeouts (pr %s): " |
| "%d -> %u ms\n", pr->pr_name, nexti, next); |
| } |
| } |
| for (adv_pr = pi->pi_adv_prefix_list; adv_pr != NULL; |
| adv_pr = next_adv_pr) { |
| next_adv_pr = adv_pr->adv_pr_next; |
| nexti = adv_prefix_timer(adv_pr, elapsed); |
| if (nexti != TIMER_INFINITY && nexti < next) |
| next = nexti; |
| if (debug & D_TIMER) { |
| logmsg(LOG_DEBUG, "run_timeouts " |
| "(adv pr on %s): %d -> %u ms\n", |
| adv_pr->adv_pr_physical->pi_name, |
| nexti, next); |
| } |
| } |
| for (dr = pi->pi_router_list; dr != NULL; dr = next_dr) { |
| next_dr = dr->dr_next; |
| nexti = router_timer(dr, elapsed); |
| if (nexti != TIMER_INFINITY && nexti < next) |
| next = nexti; |
| if (debug & D_TIMER) { |
| logmsg(LOG_DEBUG, "run_timeouts (dr): " |
| "%d -> %u ms\n", nexti, next); |
| } |
| } |
| if (pi->pi_TmpAddrsEnabled) { |
| nexti = tmptoken_timer(pi, elapsed); |
| if (nexti != TIMER_INFINITY && nexti < next) |
| next = nexti; |
| if (debug & D_TIMER) { |
| logmsg(LOG_DEBUG, "run_timeouts (tmp on %s): " |
| "%d -> %u ms\n", pi->pi_name, nexti, next); |
| } |
| } |
| } |
| /* |
| * Make sure the timer functions are run at least once |
| * an hour. |
| */ |
| if (next == TIMER_INFINITY) |
| next = 3600 * 1000; /* 1 hour */ |
| |
| if (debug & D_TIMER) |
| logmsg(LOG_DEBUG, "run_timeouts: %u ms\n", next); |
| timer_schedule(next); |
| if (do_retry) { |
| if (debug & D_TIMER) |
| logmsg(LOG_DEBUG, "run_timeouts: retry\n"); |
| do_retry = _B_FALSE; |
| goto retry; |
| } |
| timeout_running = _B_FALSE; |
| } |
| |
| static int eventpipe_read = -1; /* Used for synchronous signal delivery */ |
| static int eventpipe_write = -1; |
| |
| /* |
| * Ensure that signals are processed synchronously with the rest of |
| * the code by just writing a one character signal number on the pipe. |
| * The poll loop will pick this up and process the signal event. |
| */ |
| static void |
| sig_handler(int signo) |
| { |
| uchar_t buf = (uchar_t)signo; |
| |
| if (eventpipe_write == -1) { |
| logmsg(LOG_ERR, "sig_handler: no pipe\n"); |
| return; |
| } |
| if (write(eventpipe_write, &buf, sizeof (buf)) < 0) |
| logperror("sig_handler: write"); |
| } |
| |
| /* |
| * Pick up a signal "byte" from the pipe and process it. |
| */ |
| static void |
| in_signal(int fd) |
| { |
| uchar_t buf; |
| struct phyint *pi; |
| struct phyint *next_pi; |
| |
| switch (read(fd, &buf, sizeof (buf))) { |
| case -1: |
| logperror("in_signal: read"); |
| exit(1); |
| /* NOTREACHED */ |
| case 1: |
| break; |
| case 0: |
| logmsg(LOG_ERR, "in_signal: read eof\n"); |
| exit(1); |
| /* NOTREACHED */ |
| default: |
| logmsg(LOG_ERR, "in_signal: read > 1\n"); |
| exit(1); |
| } |
| |
| if (debug & D_TIMER) |
| logmsg(LOG_DEBUG, "in_signal() got %d\n", buf); |
| |
| switch (buf) { |
| case SIGALRM: |
| if (debug & D_TIMER) { |
| uint_t now = getcurrenttime(); |
| |
| logmsg(LOG_DEBUG, "in_signal(SIGALRM) delta %u\n", |
| now - timer_next); |
| } |
| timer_next = TIMER_INFINITY; |
| run_timeouts(); |
| break; |
| case SIGHUP: |
| /* Re-read config file by exec'ing ourselves */ |
| for (pi = phyints; pi != NULL; pi = next_pi) { |
| next_pi = pi->pi_next; |
| if (pi->pi_AdvSendAdvertisements) |
| check_to_advertise(pi, START_FINAL_ADV); |
| |
| phyint_delete(pi); |
| } |
| |
| /* |
| * Prevent fd leaks. Everything gets re-opened at start-up |
| * time. 0, 1, and 2 are closed and re-opened as |
| * /dev/null, so we'll leave those open. |
| */ |
| closefrom(3); |
| |
| logmsg(LOG_ERR, "SIGHUP: restart and reread config file\n"); |
| (void) execv(argv0[0], argv0); |
| (void) unlink(PATH_PID); |
| _exit(0177); |
| /* NOTREACHED */ |
| case SIGUSR1: |
| logmsg(LOG_DEBUG, "Printing configuration:\n"); |
| phyint_print_all(); |
| break; |
| case SIGINT: |
| case SIGTERM: |
| case SIGQUIT: |
| for (pi = phyints; pi != NULL; pi = next_pi) { |
| next_pi = pi->pi_next; |
| if (pi->pi_AdvSendAdvertisements) |
| check_to_advertise(pi, START_FINAL_ADV); |
| |
| phyint_delete(pi); |
| } |
| (void) unlink(NDPD_SNMP_SOCKET); |
| (void) unlink(PATH_PID); |
| exit(0); |
| /* NOTREACHED */ |
| case 255: |
| /* |
| * Special "signal" from looback_ra_enqueue. |
| * Handle any queued loopback router advertisements. |
| */ |
| loopback_ra_dequeue(); |
| break; |
| default: |
| logmsg(LOG_ERR, "in_signal: unknown signal: %d\n", buf); |
| } |
| } |
| |
| /* |
| * Create pipe for signal delivery and set up signal handlers. |
| */ |
| static void |
| setup_eventpipe(void) |
| { |
| int fds[2]; |
| struct sigaction act; |
| |
| if ((pipe(fds)) < 0) { |
| logperror("setup_eventpipe: pipe"); |
| exit(1); |
| } |
| eventpipe_read = fds[0]; |
| eventpipe_write = fds[1]; |
| if (poll_add(eventpipe_read) == -1) { |
| exit(1); |
| } |
| act.sa_handler = sig_handler; |
| act.sa_flags = SA_RESTART; |
| (void) sigaction(SIGALRM, &act, NULL); |
| |
| (void) sigset(SIGHUP, sig_handler); |
| (void) sigset(SIGUSR1, sig_handler); |
| (void) sigset(SIGTERM, sig_handler); |
| (void) sigset(SIGINT, sig_handler); |
| (void) sigset(SIGQUIT, sig_handler); |
| } |
| |
| /* |
| * Create a routing socket for receiving RTM_IFINFO messages and initialize |
| * the routing socket message header and as much of the sockaddrs as possible. |
| */ |
| static int |
| setup_rtsock(void) |
| { |
| int s; |
| int ret; |
| char *cp; |
| struct sockaddr_in6 *sin6; |
| |
| s = socket(PF_ROUTE, SOCK_RAW, AF_INET6); |
| if (s == -1) { |
| logperror("socket(PF_ROUTE)"); |
| exit(1); |
| } |
| ret = fcntl(s, F_SETFL, O_NDELAY|O_NONBLOCK); |
| if (ret < 0) { |
| logperror("fcntl(O_NDELAY)"); |
| exit(1); |
| } |
| if (poll_add(s) == -1) { |
| exit(1); |
| } |
| |
| /* |
| * Allocate storage for the routing socket message. |
| */ |
| rt_msg = (struct rt_msghdr *)malloc(NDP_RTM_MSGLEN); |
| if (rt_msg == NULL) { |
| logperror("malloc"); |
| exit(1); |
| } |
| |
| /* |
| * Initialize the routing socket message by zero-filling it and then |
| * setting the fields where are constant through the lifetime of the |
| * process. |
| */ |
| bzero(rt_msg, NDP_RTM_MSGLEN); |
| rt_msg->rtm_msglen = NDP_RTM_MSGLEN; |
| rt_msg->rtm_version = RTM_VERSION; |
| rt_msg->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_IFP; |
| rt_msg->rtm_pid = getpid(); |
| if (rt_msg->rtm_pid < 0) { |
| logperror("getpid"); |
| exit(1); |
| } |
| |
| /* |
| * The RTA_DST sockaddr does not change during the lifetime of the |
| * process so it can be completely initialized at this time. |
| */ |
| cp = (char *)rt_msg + sizeof (struct rt_msghdr); |
| sin6 = (struct sockaddr_in6 *)cp; |
| sin6->sin6_family = AF_INET6; |
| sin6->sin6_addr = in6addr_any; |
| |
| /* |
| * Initialize the constant portion of the RTA_GATEWAY sockaddr. |
| */ |
| cp += sizeof (struct sockaddr_in6); |
| rta_gateway = (struct sockaddr_in6 *)cp; |
| rta_gateway->sin6_family = AF_INET6; |
| |
| /* |
| * The RTA_NETMASK sockaddr does not change during the lifetime of the |
| * process so it can be completely initialized at this time. |
| */ |
| cp += sizeof (struct sockaddr_in6); |
| sin6 = (struct sockaddr_in6 *)cp; |
| sin6->sin6_family = AF_INET6; |
| sin6->sin6_addr = in6addr_any; |
| |
| /* |
| * Initialize the constant portion of the RTA_IFP sockaddr. |
| */ |
| cp += sizeof (struct sockaddr_in6); |
| rta_ifp = (struct sockaddr_dl *)cp; |
| rta_ifp->sdl_family = AF_LINK; |
| |
| return (s); |
| } |
| |
| static int |
| setup_mibsock(void) |
| { |
| int sock; |
| int ret; |
| int len; |
| struct sockaddr_un laddr; |
| |
| sock = socket(AF_UNIX, SOCK_DGRAM, 0); |
| if (sock == -1) { |
| logperror("setup_mibsock: socket(AF_UNIX)"); |
| exit(1); |
| } |
| |
| bzero(&laddr, sizeof (laddr)); |
| laddr.sun_family = AF_UNIX; |
| |
| (void) strncpy(laddr.sun_path, NDPD_SNMP_SOCKET, |
| sizeof (laddr.sun_path)); |
| len = sizeof (struct sockaddr_un); |
| |
| (void) unlink(NDPD_SNMP_SOCKET); |
| ret = bind(sock, (struct sockaddr *)&laddr, len); |
| if (ret < 0) { |
| logperror("setup_mibsock: bind\n"); |
| exit(1); |
| } |
| |
| ret = fcntl(sock, F_SETFL, O_NONBLOCK); |
| if (ret < 0) { |
| logperror("fcntl(O_NONBLOCK)"); |
| exit(1); |
| } |
| if (poll_add(sock) == -1) { |
| exit(1); |
| } |
| return (sock); |
| } |
| |
| /* |
| * Retrieve one routing socket message. If RTM_IFINFO indicates |
| * new phyint do a full scan of the interfaces. If RTM_IFINFO |
| * indicates an existing phyint, only scan that phyint and associated |
| * prefixes. |
| */ |
| static void |
| process_rtsock(int rtsock) |
| { |
| int n; |
| #define MSG_SIZE 2048/8 |
| int64_t msg[MSG_SIZE]; |
| struct rt_msghdr *rtm; |
| struct if_msghdr *ifm; |
| struct phyint *pi; |
| struct prefix *pr; |
| boolean_t need_initifs = _B_FALSE; |
| boolean_t need_ifscan = _B_FALSE; |
| int64_t ifscan_msg[10][MSG_SIZE]; |
| int ifscan_index = 0; |
| int i; |
| |
| /* Empty the rtsock and coealesce all the work that we have */ |
| while (ifscan_index < 10) { |
| n = read(rtsock, msg, sizeof (msg)); |
| if (n <= 0) { |
| /* No more messages */ |
| break; |
| } |
| rtm = (struct rt_msghdr *)msg; |
| if (rtm->rtm_version != RTM_VERSION) { |
| logmsg(LOG_ERR, |
| "process_rtsock: version %d not understood\n", |
| rtm->rtm_version); |
| return; |
| } |
| switch (rtm->rtm_type) { |
| case RTM_NEWADDR: |
| case RTM_DELADDR: |
| /* |
| * Some logical interface has changed - have to scan |
| * everything to determine what actually changed. |
| */ |
| if (debug & D_IFSCAN) { |
| logmsg(LOG_DEBUG, "process_rtsock: " |
| "message %d\n", rtm->rtm_type); |
| } |
| need_initifs = _B_TRUE; |
| break; |
| case RTM_IFINFO: |
| need_ifscan = _B_TRUE; |
| (void) memcpy(ifscan_msg[ifscan_index], rtm, |
| sizeof (msg)); |
| ifscan_index++; |
| /* Handled below */ |
| break; |
| default: |
| /* Not interesting */ |
| break; |
| } |
| } |
| /* |
| * If we do full scan i.e initifs, we don't need to |
| * scan a particular interface as we should have |
| * done that as part of initifs. |
| */ |
| if (need_initifs) { |
| initifs(_B_FALSE); |
| return; |
| } |
| |
| if (!need_ifscan) |
| return; |
| |
| for (i = 0; i < ifscan_index; i++) { |
| ifm = (struct if_msghdr *)ifscan_msg[i]; |
| if (debug & D_IFSCAN) |
| logmsg(LOG_DEBUG, "process_rtsock: index %d\n", |
| ifm->ifm_index); |
| |
| pi = phyint_lookup_on_index(ifm->ifm_index); |
| if (pi == NULL) { |
| /* |
| * A new physical interface. Do a full scan of the |
| * to catch any new logical interfaces. |
| */ |
| initifs(_B_FALSE); |
| return; |
| } |
| |
| if (ifm->ifm_flags != (uint_t)pi->pi_flags) { |
| if (debug & D_IFSCAN) { |
| logmsg(LOG_DEBUG, "process_rtsock: clr for " |
| "%s old flags 0x%llx new flags 0x%x\n", |
| pi->pi_name, pi->pi_flags, ifm->ifm_flags); |
| } |
| } |
| |
| |
| /* |
| * Mark the interfaces so that we can find phyints and prefixes |
| * which have disappeared from the kernel. |
| * if_process will set pr_in_use when it finds the |
| * interface in the kernel. |
| * Before re-examining the state of the interfaces, |
| * PI_PRESENT should be cleared from pi_kernel_state. |
| */ |
| pi->pi_kernel_state &= ~PI_PRESENT; |
| for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) { |
| pr->pr_in_use = _B_FALSE; |
| } |
| |
| if (ifsock < 0) { |
| ifsock = socket(AF_INET6, SOCK_DGRAM, 0); |
| if (ifsock < 0) { |
| logperror("process_rtsock: socket"); |
| return; |
| } |
| } |
| if_process(ifsock, pi->pi_name, _B_FALSE); |
| for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) { |
| if_process(ifsock, pr->pr_name, _B_FALSE); |
| } |
| /* |
| * If interface (still) exists in kernel, set |
| * pi_state to indicate that. |
| */ |
| if (pi->pi_kernel_state & PI_PRESENT) { |
| pi->pi_state |= PI_PRESENT; |
| } |
| check_if_removed(pi); |
| if (show_ifs) |
| phyint_print_all(); |
| } |
| } |
| |
| static void |
| process_mibsock(int mibsock) |
| { |
| struct phyint *pi; |
| socklen_t fromlen; |
| struct sockaddr_un from; |
| ndpd_info_t ndpd_info; |
| ssize_t len; |
| int command; |
| |
| fromlen = (socklen_t)sizeof (from); |
| len = recvfrom(mibsock, &command, sizeof (int), 0, |
| (struct sockaddr *)&from, &fromlen); |
| |
| if (len < sizeof (int) || command != NDPD_SNMP_INFO_REQ) { |
| logperror("process_mibsock: bad command \n"); |
| return; |
| } |
| |
| ndpd_info.info_type = NDPD_SNMP_INFO_RESPONSE; |
| ndpd_info.info_version = NDPD_SNMP_INFO_VER; |
| ndpd_info.info_num_of_phyints = num_of_phyints; |
| |
| (void) sendto(mibsock, &ndpd_info, sizeof (ndpd_info_t), 0, |
| (struct sockaddr *)&from, fromlen); |
| |
| for (pi = phyints; pi != NULL; pi = pi->pi_next) { |
| int prefixes; |
| int routers; |
| struct prefix *prefix_list; |
| struct router *router_list; |
| ndpd_phyint_info_t phyint; |
| ndpd_prefix_info_t prefix; |
| ndpd_router_info_t router; |
| /* |
| * get number of prefixes |
| */ |
| routers = 0; |
| prefixes = 0; |
| prefix_list = pi->pi_prefix_list; |
| while (prefix_list != NULL) { |
| prefixes++; |
| prefix_list = prefix_list->pr_next; |
| } |
| |
| /* |
| * get number of routers |
| */ |
| router_list = pi->pi_router_list; |
| while (router_list != NULL) { |
| routers++; |
| router_list = router_list->dr_next; |
| } |
| |
| phyint.phyint_info_type = NDPD_PHYINT_INFO; |
| phyint.phyint_info_version = NDPD_PHYINT_INFO_VER; |
| phyint.phyint_index = pi->pi_index; |
| bcopy(pi->pi_config, |
| phyint.phyint_config, I_IFSIZE); |
| phyint.phyint_num_of_prefixes = prefixes; |
| phyint.phyint_num_of_routers = routers; |
| (void) sendto(mibsock, &phyint, sizeof (phyint), 0, |
| (struct sockaddr *)&from, fromlen); |
| |
| /* |
| * Copy prefix information |
| */ |
| |
| prefix_list = pi->pi_prefix_list; |
| while (prefix_list != NULL) { |
| prefix.prefix_info_type = NDPD_PREFIX_INFO; |
| prefix.prefix_info_version = NDPD_PREFIX_INFO_VER; |
| prefix.prefix_prefix = prefix_list->pr_prefix; |
| prefix.prefix_len = prefix_list->pr_prefix_len; |
| prefix.prefix_flags = prefix_list->pr_flags; |
| prefix.prefix_phyint_index = pi->pi_index; |
| prefix.prefix_ValidLifetime = |
| prefix_list->pr_ValidLifetime; |
| prefix.prefix_PreferredLifetime = |
| prefix_list->pr_PreferredLifetime; |
| prefix.prefix_OnLinkLifetime = |
| prefix_list->pr_OnLinkLifetime; |
| prefix.prefix_OnLinkFlag = |
| prefix_list->pr_OnLinkFlag; |
| prefix.prefix_AutonomousFlag = |
| prefix_list->pr_AutonomousFlag; |
| (void) sendto(mibsock, &prefix, sizeof (prefix), 0, |
| (struct sockaddr *)&from, fromlen); |
| prefix_list = prefix_list->pr_next; |
| } |
| /* |
| * Copy router information |
| */ |
| router_list = pi->pi_router_list; |
| while (router_list != NULL) { |
| router.router_info_type = NDPD_ROUTER_INFO; |
| router.router_info_version = NDPD_ROUTER_INFO_VER; |
| router.router_address = router_list->dr_address; |
| router.router_lifetime = router_list->dr_lifetime; |
| router.router_phyint_index = pi->pi_index; |
| (void) sendto(mibsock, &router, sizeof (router), 0, |
| (struct sockaddr *)&from, fromlen); |
| router_list = router_list->dr_next; |
| } |
| } |
| } |
| |
| /* |
| * Look if the phyint or one of its prefixes have been removed from |
| * the kernel and take appropriate action. |
| * Uses pr_in_use and pi{,_kernel}_state. |
| */ |
| static void |
| check_if_removed(struct phyint *pi) |
| { |
| struct prefix *pr, *next_pr; |
| |
| /* |
| * Detect prefixes which are removed. |
| * Static prefixes are just removed from our tables. |
| * Non-static prefixes are recreated i.e. in.ndpd takes precedence |
| * over manually removing prefixes via ifconfig. |
| */ |
| for (pr = pi->pi_prefix_list; pr != NULL; pr = next_pr) { |
| next_pr = pr->pr_next; |
| if (!pr->pr_in_use) { |
| /* Clear everything except PR_STATIC */ |
| pr->pr_kernel_state &= PR_STATIC; |
| pr->pr_name[0] = '\0'; |
| if (pr->pr_state & PR_STATIC) { |
| prefix_delete(pr); |
| } else if (!(pi->pi_kernel_state & PI_PRESENT)) { |
| /* |
| * Ensure that there are no future attempts to |
| * run prefix_update_k since the phyint is gone. |
| */ |
| pr->pr_state = pr->pr_kernel_state; |
| } else if (pr->pr_state != pr->pr_kernel_state) { |
| logmsg(LOG_INFO, "Prefix manually removed " |
| "on %s; recreating\n", pi->pi_name); |
| prefix_update_k(pr); |
| } |
| } |
| } |
| |
| /* |
| * Detect phyints that have been removed from the kernel, and tear |
| * down any prefixes we created that are associated with that phyint. |
| * (NOTE: IPMP depends on in.ndpd tearing down these prefixes so an |
| * administrator can easily place an IP interface with ADDRCONF'd |
| * addresses into an IPMP group.) |
| */ |
| if (!(pi->pi_kernel_state & PI_PRESENT) && |
| (pi->pi_state & PI_PRESENT)) { |
| logmsg(LOG_ERR, "Interface %s has been removed from kernel. " |
| "in.ndpd will no longer use it\n", pi->pi_name); |
| |
| for (pr = pi->pi_prefix_list; pr != NULL; pr = next_pr) { |
| next_pr = pr->pr_next; |
| if (pr->pr_state & PR_AUTO) |
| prefix_delete(pr); |
| } |
| |
| /* |
| * Clear state so that should the phyint reappear we will |
| * start with initial advertisements or solicitations. |
| */ |
| phyint_cleanup(pi); |
| } |
| } |
| |
| |
| /* |
| * Queuing mechanism for router advertisements that are sent by in.ndpd |
| * and that also need to be processed by in.ndpd. |
| * Uses "signal number" 255 to indicate to the main poll loop |
| * that there is something to dequeue and send to incomining_ra(). |
| */ |
| struct raq { |
| struct raq *raq_next; |
| struct phyint *raq_pi; |
| int raq_packetlen; |
| uchar_t *raq_packet; |
| }; |
| static struct raq *raq_head = NULL; |
| |
| /* |
| * Allocate a struct raq and memory for the packet. |
| * Send signal 255 to have poll dequeue. |
| */ |
| static void |
| loopback_ra_enqueue(struct phyint *pi, struct nd_router_advert *ra, int len) |
| { |
| struct raq *raq; |
| struct raq **raqp; |
| |
| if (no_loopback) |
| return; |
| |
| if (debug & D_PKTOUT) |
| logmsg(LOG_DEBUG, "loopback_ra_enqueue for %s\n", pi->pi_name); |
| |
| raq = calloc(sizeof (struct raq), 1); |
| if (raq == NULL) { |
| logmsg(LOG_ERR, "loopback_ra_enqueue: out of memory\n"); |
| return; |
| } |
| raq->raq_packet = malloc(len); |
| if (raq->raq_packet == NULL) { |
| free(raq); |
| logmsg(LOG_ERR, "loopback_ra_enqueue: out of memory\n"); |
| return; |
| } |
| bcopy(ra, raq->raq_packet, len); |
| raq->raq_packetlen = len; |
| raq->raq_pi = pi; |
| |
| /* Tail insert */ |
| raqp = &raq_head; |
| while (*raqp != NULL) |
| raqp = &((*raqp)->raq_next); |
| *raqp = raq; |
| |
| /* Signal for poll loop */ |
| sig_handler(255); |
| } |
| |
| /* |
| * Dequeue and process all queued advertisements. |
| */ |
| static void |
| loopback_ra_dequeue(void) |
| { |
| struct sockaddr_in6 from = IN6ADDR_LOOPBACK_INIT; |
| struct raq *raq; |
| |
| if (debug & D_PKTIN) |
| logmsg(LOG_DEBUG, "loopback_ra_dequeue()\n"); |
| |
| while ((raq = raq_head) != NULL) { |
| raq_head = raq->raq_next; |
| raq->raq_next = NULL; |
| |
| if (debug & D_PKTIN) { |
| logmsg(LOG_DEBUG, "loopback_ra_dequeue for %s\n", |
| raq->raq_pi->pi_name); |
| } |
| |
| incoming_ra(raq->raq_pi, |
| (struct nd_router_advert *)raq->raq_packet, |
| raq->raq_packetlen, &from, _B_TRUE); |
| free(raq->raq_packet); |
| free(raq); |
| } |
| } |
| |
| |
| static void |
| usage(char *cmd) |
| { |
| (void) fprintf(stderr, |
| "usage: %s [ -adt ] [-f <config file>]\n", cmd); |
| } |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| int i; |
| struct phyint *pi; |
| int c; |
| char *config_file = PATH_NDPD_CONF; |
| boolean_t file_required = _B_FALSE; |
| |
| argv0 = argv; |
| srandom(gethostid()); |
| (void) umask(0022); |
| |
| while ((c = getopt(argc, argv, "adD:ntIf:")) != EOF) { |
| switch (c) { |
| case 'a': |
| /* |
| * The StatelessAddrConf variable in ndpd.conf, if |
| * present, will override this setting. |
| */ |
| ifdefaults[I_StatelessAddrConf].cf_value = 0; |
| break; |
| case 'd': |
| debug = D_ALL; |
| break; |
| case 'D': |
| i = strtol((char *)optarg, NULL, 0); |
| if (i == 0) { |
| (void) fprintf(stderr, "Bad debug flags: %s\n", |
| (char *)optarg); |
| exit(1); |
| } |
| debug |= i; |
| break; |
| case 'n': |
| no_loopback = 1; |
| break; |
| case 'I': |
| show_ifs = 1; |
| break; |
| case 't': |
| debug |= D_PKTIN | D_PKTOUT | D_PKTBAD; |
| break; |
| case 'f': |
| config_file = (char *)optarg; |
| file_required = _B_TRUE; |
| break; |
| case '?': |
| usage(argv[0]); |
| exit(1); |
| } |
| } |
| |
| if (parse_config(config_file, file_required) == -1) |
| exit(2); |
| |
| if (show_ifs) |
| phyint_print_all(); |
| |
| if (debug == 0) { |
| initlog(); |
| } |
| |
| setup_eventpipe(); |
| rtsock = setup_rtsock(); |
| mibsock = setup_mibsock(); |
| timer_init(); |
| initifs(_B_TRUE); |
| |
| check_daemonize(); |
| |
| for (;;) { |
| if (poll(pollfds, pollfd_num, -1) < 0) { |
| if (errno == EINTR) |
| continue; |
| logperror("main: poll"); |
| exit(1); |
| } |
| for (i = 0; i < pollfd_num; i++) { |
| if (!(pollfds[i].revents & POLLIN)) |
| continue; |
| if (pollfds[i].fd == eventpipe_read) { |
| in_signal(eventpipe_read); |
| break; |
| } |
| if (pollfds[i].fd == rtsock) { |
| process_rtsock(rtsock); |
| break; |
| } |
| if (pollfds[i].fd == mibsock) { |
| process_mibsock(mibsock); |
| break; |
| } |
| /* |
| * Run timer routine to advance clock if more than |
| * half a second since the clock was advanced. |
| * This limits CPU usage under severe packet |
| * arrival rates but it creates a slight inaccuracy |
| * in the timer mechanism. |
| */ |
| conditional_run_timeouts(500U); |
| for (pi = phyints; pi != NULL; pi = pi->pi_next) { |
| if (pollfds[i].fd == pi->pi_sock) { |
| in_data(pi); |
| break; |
| } |
| } |
| } |
| } |
| /* NOTREACHED */ |
| return (0); |
| } |
| |
| /* |
| * LOGGER |
| */ |
| |
| static boolean_t logging = _B_FALSE; |
| |
| static void |
| initlog(void) |
| { |
| logging = _B_TRUE; |
| openlog("in.ndpd", LOG_PID | LOG_CONS, LOG_DAEMON); |
| } |
| |
| /* Print the date/time without a trailing carridge return */ |
| static void |
| fprintdate(FILE *file) |
| { |
| char buf[BUFSIZ]; |
| struct tm tms; |
| time_t now; |
| |
| now = time(NULL); |
| (void) localtime_r(&now, &tms); |
| (void) strftime(buf, sizeof (buf), "%h %d %X", &tms); |
| (void) fprintf(file, "%s ", buf); |
| } |
| |
| /* PRINTFLIKE2 */ |
| void |
| logmsg(int level, const char *fmt, ...) |
| { |
| va_list ap; |
| va_start(ap, fmt); |
| |
| if (logging) { |
| vsyslog(level, fmt, ap); |
| } else { |
| fprintdate(stderr); |
| (void) vfprintf(stderr, fmt, ap); |
| } |
| va_end(ap); |
| } |
| |
| void |
| logperror(const char *str) |
| { |
| if (logging) { |
| syslog(LOG_ERR, "%s: %m\n", str); |
| } else { |
| fprintdate(stderr); |
| (void) fprintf(stderr, "%s: %s\n", str, strerror(errno)); |
| } |
| } |
| |
| void |
| logperror_pi(const struct phyint *pi, const char *str) |
| { |
| if (logging) { |
| syslog(LOG_ERR, "%s (interface %s): %m\n", |
| str, pi->pi_name); |
| } else { |
| fprintdate(stderr); |
| (void) fprintf(stderr, "%s (interface %s): %s\n", |
| str, pi->pi_name, strerror(errno)); |
| } |
| } |
| |
| void |
| logperror_pr(const struct prefix *pr, const char *str) |
| { |
| if (logging) { |
| syslog(LOG_ERR, "%s (prefix %s if %s): %m\n", |
| str, pr->pr_name, pr->pr_physical->pi_name); |
| } else { |
| fprintdate(stderr); |
| (void) fprintf(stderr, "%s (prefix %s if %s): %s\n", |
| str, pr->pr_name, pr->pr_physical->pi_name, |
| strerror(errno)); |
| } |
| } |