17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
58121d73fSseb  * Common Development and Distribution License (the "License").
68121d73fSseb  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
20e11c3f44Smeem  *
2164639aafSDarren Reed  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
22*2514b110SRyan Goodfellow  * Copyright 2022 Oxide Computer Company
237c478bd9Sstevel@tonic-gate  */
247c478bd9Sstevel@tonic-gate 
257c478bd9Sstevel@tonic-gate #include "defs.h"
267c478bd9Sstevel@tonic-gate #include "tables.h"
277c478bd9Sstevel@tonic-gate #include <fcntl.h>
283173664eSapersson #include <sys/un.h>
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate static void	initlog(void);
317c478bd9Sstevel@tonic-gate static void	run_timeouts(void);
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate static void	advertise(struct sockaddr_in6 *sin6, struct phyint *pi,
347c478bd9Sstevel@tonic-gate 		    boolean_t no_prefixes);
357c478bd9Sstevel@tonic-gate static void	solicit(struct sockaddr_in6 *sin6, struct phyint *pi);
367c478bd9Sstevel@tonic-gate static void	initifs(boolean_t first);
377c478bd9Sstevel@tonic-gate static void	check_if_removed(struct phyint *pi);
387c478bd9Sstevel@tonic-gate static void	loopback_ra_enqueue(struct phyint *pi,
397c478bd9Sstevel@tonic-gate 		    struct nd_router_advert *ra, int len);
407c478bd9Sstevel@tonic-gate static void	loopback_ra_dequeue(void);
417c478bd9Sstevel@tonic-gate static void	check_daemonize(void);
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate struct in6_addr all_nodes_mcast = { { 0xff, 0x2, 0x0, 0x0,
447c478bd9Sstevel@tonic-gate 				    0x0, 0x0, 0x0, 0x0,
457c478bd9Sstevel@tonic-gate 				    0x0, 0x0, 0x0, 0x0,
467c478bd9Sstevel@tonic-gate 				    0x0, 0x0, 0x0, 0x1 } };
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate struct in6_addr all_routers_mcast = { { 0xff, 0x2, 0x0, 0x0,
497c478bd9Sstevel@tonic-gate 				    0x0, 0x0, 0x0, 0x0,
507c478bd9Sstevel@tonic-gate 				    0x0, 0x0, 0x0, 0x0,
517c478bd9Sstevel@tonic-gate 				    0x0, 0x0, 0x0, 0x2 } };
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate static struct sockaddr_in6 v6allnodes = { AF_INET6, 0, 0,
547c478bd9Sstevel@tonic-gate 				    { 0xff, 0x2, 0x0, 0x0,
557c478bd9Sstevel@tonic-gate 				    0x0, 0x0, 0x0, 0x0,
567c478bd9Sstevel@tonic-gate 				    0x0, 0x0, 0x0, 0x0,
577c478bd9Sstevel@tonic-gate 				    0x0, 0x0, 0x0, 0x1 } };
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate static struct sockaddr_in6 v6allrouters = { AF_INET6, 0, 0,
607c478bd9Sstevel@tonic-gate 				    { 0xff, 0x2, 0x0, 0x0,
617c478bd9Sstevel@tonic-gate 				    0x0, 0x0, 0x0, 0x0,
627c478bd9Sstevel@tonic-gate 				    0x0, 0x0, 0x0, 0x0,
637c478bd9Sstevel@tonic-gate 				    0x0, 0x0, 0x0, 0x2 } };
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate static char **argv0;		/* Saved for re-exec on SIGHUP */
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate static uint64_t packet[(IP_MAXPACKET + 1)/8];
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate static int	show_ifs = 0;
707c478bd9Sstevel@tonic-gate static boolean_t	already_daemonized = _B_FALSE;
717c478bd9Sstevel@tonic-gate int		debug = 0;
727c478bd9Sstevel@tonic-gate int		no_loopback = 0; /* Do not send RA packets to ourselves */
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate /*
757c478bd9Sstevel@tonic-gate  * Size of routing socket message used by in.ndpd which includes the header,
767c478bd9Sstevel@tonic-gate  * space for the RTA_DST, RTA_GATEWAY and RTA_NETMASK (each a sockaddr_in6)
777c478bd9Sstevel@tonic-gate  * plus space for the RTA_IFP (a sockaddr_dl).
787c478bd9Sstevel@tonic-gate  */
797c478bd9Sstevel@tonic-gate #define	NDP_RTM_MSGLEN	sizeof (struct rt_msghdr) +	\
807c478bd9Sstevel@tonic-gate 			sizeof (struct sockaddr_in6) +	\
817c478bd9Sstevel@tonic-gate 			sizeof (struct sockaddr_in6) +	\
827c478bd9Sstevel@tonic-gate 			sizeof (struct sockaddr_in6) +	\
837c478bd9Sstevel@tonic-gate 			sizeof (struct sockaddr_dl)
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate /*
867c478bd9Sstevel@tonic-gate  * These are referenced externally in tables.c in order to fill in the
877c478bd9Sstevel@tonic-gate  * dynamic portions of the routing socket message and then to send the message
887c478bd9Sstevel@tonic-gate  * itself.
897c478bd9Sstevel@tonic-gate  */
907c478bd9Sstevel@tonic-gate int	rtsock = -1;			/* Routing socket */
917c478bd9Sstevel@tonic-gate struct	rt_msghdr	*rt_msg;	/* Routing socket message */
927c478bd9Sstevel@tonic-gate struct	sockaddr_in6	*rta_gateway;	/* RTA_GATEWAY sockaddr */
937c478bd9Sstevel@tonic-gate struct	sockaddr_dl	*rta_ifp;	/* RTA_IFP sockaddr */
946e91bba0SGirish Moodalbail 
956e91bba0SGirish Moodalbail /*
966e91bba0SGirish Moodalbail  * These sockets are used internally in this file.
976e91bba0SGirish Moodalbail  */
986e91bba0SGirish Moodalbail static int	mibsock = -1;			/* mib request socket */
996e91bba0SGirish Moodalbail static int	cmdsock = -1;			/* command socket */
1006e91bba0SGirish Moodalbail 
1016e91bba0SGirish Moodalbail static	int	ndpd_setup_cmd_listener(void);
1026e91bba0SGirish Moodalbail static	void	ndpd_cmd_handler(int);
1036e91bba0SGirish Moodalbail static	int	ndpd_process_cmd(int, ipadm_ndpd_msg_t *);
1046e91bba0SGirish Moodalbail static	int	ndpd_send_error(int, int);
1056e91bba0SGirish Moodalbail static	int	ndpd_set_autoconf(const char *, boolean_t);
1066e91bba0SGirish Moodalbail static	int	ndpd_create_addrs(const char *, struct sockaddr_in6, int,
1076e91bba0SGirish Moodalbail     boolean_t, boolean_t, char *);
1086e91bba0SGirish Moodalbail static	int	ndpd_delete_addrs(const char *);
1096e91bba0SGirish Moodalbail static	int	phyint_check_ipadm_intfid(struct phyint *);
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate /*
1127c478bd9Sstevel@tonic-gate  * Return the current time in milliseconds truncated to
1137c478bd9Sstevel@tonic-gate  * fit in an integer.
1147c478bd9Sstevel@tonic-gate  */
1157c478bd9Sstevel@tonic-gate uint_t
getcurrenttime(void)1167c478bd9Sstevel@tonic-gate getcurrenttime(void)
1177c478bd9Sstevel@tonic-gate {
1187c478bd9Sstevel@tonic-gate 	struct timeval tp;
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate 	if (gettimeofday(&tp, NULL) < 0) {
1217c478bd9Sstevel@tonic-gate 		logperror("getcurrenttime: gettimeofday failed");
1227c478bd9Sstevel@tonic-gate 		exit(1);
1237c478bd9Sstevel@tonic-gate 	}
1247c478bd9Sstevel@tonic-gate 	return (tp.tv_sec * 1000 + tp.tv_usec / 1000);
1257c478bd9Sstevel@tonic-gate }
1267c478bd9Sstevel@tonic-gate 
1277c478bd9Sstevel@tonic-gate /*
1287c478bd9Sstevel@tonic-gate  * Output a preformated packet from the packet[] buffer.
1297c478bd9Sstevel@tonic-gate  */
1307c478bd9Sstevel@tonic-gate static void
sendpacket(struct sockaddr_in6 * sin6,int sock,int size,int flags)1317c478bd9Sstevel@tonic-gate sendpacket(struct sockaddr_in6 *sin6, int sock, int size, int flags)
1327c478bd9Sstevel@tonic-gate {
1337c478bd9Sstevel@tonic-gate 	int cc;
1347c478bd9Sstevel@tonic-gate 	char abuf[INET6_ADDRSTRLEN];
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate 	cc = sendto(sock, (char *)packet, size, flags,
137e11c3f44Smeem 	    (struct sockaddr *)sin6, sizeof (*sin6));
1387c478bd9Sstevel@tonic-gate 	if (cc < 0 || cc != size) {
1397c478bd9Sstevel@tonic-gate 		if (cc < 0) {
1407c478bd9Sstevel@tonic-gate 			logperror("sendpacket: sendto");
1417c478bd9Sstevel@tonic-gate 		}
1427c478bd9Sstevel@tonic-gate 		logmsg(LOG_ERR, "sendpacket: wrote %s %d chars, ret=%d\n",
1437c478bd9Sstevel@tonic-gate 		    inet_ntop(sin6->sin6_family,
1447c478bd9Sstevel@tonic-gate 		    (void *)&sin6->sin6_addr,
1457c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf)),
1467c478bd9Sstevel@tonic-gate 		    size, cc);
1477c478bd9Sstevel@tonic-gate 	}
1487c478bd9Sstevel@tonic-gate }
1497c478bd9Sstevel@tonic-gate 
150e11c3f44Smeem /*
151e11c3f44Smeem  * If possible, place an ND_OPT_SOURCE_LINKADDR option at `optp'.
152e11c3f44Smeem  * Return the number of bytes placed in the option.
153e11c3f44Smeem  */
154e11c3f44Smeem static uint_t
add_opt_lla(struct phyint * pi,struct nd_opt_lla * optp)155e11c3f44Smeem add_opt_lla(struct phyint *pi, struct nd_opt_lla *optp)
156e11c3f44Smeem {
157e11c3f44Smeem 	uint_t optlen;
158e11c3f44Smeem 	uint_t hwaddrlen;
159e11c3f44Smeem 	struct lifreq lifr;
160e11c3f44Smeem 
161e11c3f44Smeem 	/* If this phyint doesn't have a link-layer address, bail */
162e11c3f44Smeem 	if (phyint_get_lla(pi, &lifr) == -1)
163e11c3f44Smeem 		return (0);
164e11c3f44Smeem 
165e11c3f44Smeem 	hwaddrlen = lifr.lifr_nd.lnr_hdw_len;
166e11c3f44Smeem 	/* roundup to multiple of 8 and make padding zero */
167e11c3f44Smeem 	optlen = ((sizeof (struct nd_opt_hdr) + hwaddrlen + 7) / 8) * 8;
168e11c3f44Smeem 	bzero(optp, optlen);
169e11c3f44Smeem 	optp->nd_opt_lla_type = ND_OPT_SOURCE_LINKADDR;
170e11c3f44Smeem 	optp->nd_opt_lla_len = optlen / 8;
171e11c3f44Smeem 	bcopy(lifr.lifr_nd.lnr_hdw_addr, optp->nd_opt_lla_hdw_addr, hwaddrlen);
172e11c3f44Smeem 
173e11c3f44Smeem 	return (optlen);
174e11c3f44Smeem }
175e11c3f44Smeem 
1767c478bd9Sstevel@tonic-gate /* Send a Router Solicitation */
1777c478bd9Sstevel@tonic-gate static void
solicit(struct sockaddr_in6 * sin6,struct phyint * pi)1787c478bd9Sstevel@tonic-gate solicit(struct sockaddr_in6 *sin6, struct phyint *pi)
1797c478bd9Sstevel@tonic-gate {
1807c478bd9Sstevel@tonic-gate 	int packetlen = 0;
1817c478bd9Sstevel@tonic-gate 	struct	nd_router_solicit *rs = (struct nd_router_solicit *)packet;
1827c478bd9Sstevel@tonic-gate 	char *pptr = (char *)packet;
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate 	rs->nd_rs_type = ND_ROUTER_SOLICIT;
1857c478bd9Sstevel@tonic-gate 	rs->nd_rs_code = 0;
1867c478bd9Sstevel@tonic-gate 	rs->nd_rs_cksum = htons(0);
1877c478bd9Sstevel@tonic-gate 	rs->nd_rs_reserved = htonl(0);
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 	packetlen += sizeof (*rs);
1907c478bd9Sstevel@tonic-gate 	pptr += sizeof (*rs);
1917c478bd9Sstevel@tonic-gate 
192e11c3f44Smeem 	/* add options */
193e11c3f44Smeem 	packetlen += add_opt_lla(pi, (struct nd_opt_lla *)pptr);
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate 	if (debug & D_PKTOUT) {
1967c478bd9Sstevel@tonic-gate 		print_route_sol("Sending solicitation to ", pi, rs, packetlen,
1977c478bd9Sstevel@tonic-gate 		    sin6);
1987c478bd9Sstevel@tonic-gate 	}
1997c478bd9Sstevel@tonic-gate 	sendpacket(sin6, pi->pi_sock, packetlen, 0);
2007c478bd9Sstevel@tonic-gate }
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate /*
2037c478bd9Sstevel@tonic-gate  * Send a (set of) Router Advertisements and feed them back to ourselves
2047c478bd9Sstevel@tonic-gate  * for processing. Unless no_prefixes is set all prefixes are included.
2057c478bd9Sstevel@tonic-gate  * If there are too many prefix options to fit in one packet multiple
2067c478bd9Sstevel@tonic-gate  * packets will be sent - each containing a subset of the prefix options.
2077c478bd9Sstevel@tonic-gate  */
2087c478bd9Sstevel@tonic-gate static void
advertise(struct sockaddr_in6 * sin6,struct phyint * pi,boolean_t no_prefixes)2097c478bd9Sstevel@tonic-gate advertise(struct sockaddr_in6 *sin6, struct phyint *pi, boolean_t no_prefixes)
2107c478bd9Sstevel@tonic-gate {
2117c478bd9Sstevel@tonic-gate 	struct	nd_opt_prefix_info *po;
2127c478bd9Sstevel@tonic-gate 	char *pptr = (char *)packet;
2137c478bd9Sstevel@tonic-gate 	struct nd_router_advert *ra;
2147c478bd9Sstevel@tonic-gate 	struct adv_prefix *adv_pr;
2157c478bd9Sstevel@tonic-gate 	int packetlen = 0;
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate 	ra = (struct nd_router_advert *)pptr;
2187c478bd9Sstevel@tonic-gate 	ra->nd_ra_type = ND_ROUTER_ADVERT;
2197c478bd9Sstevel@tonic-gate 	ra->nd_ra_code = 0;
2207c478bd9Sstevel@tonic-gate 	ra->nd_ra_cksum = htons(0);
2217c478bd9Sstevel@tonic-gate 	ra->nd_ra_curhoplimit = pi->pi_AdvCurHopLimit;
2227c478bd9Sstevel@tonic-gate 	ra->nd_ra_flags_reserved = 0;
2237c478bd9Sstevel@tonic-gate 	if (pi->pi_AdvManagedFlag)
2247c478bd9Sstevel@tonic-gate 		ra->nd_ra_flags_reserved |= ND_RA_FLAG_MANAGED;
2257c478bd9Sstevel@tonic-gate 	if (pi->pi_AdvOtherConfigFlag)
2267c478bd9Sstevel@tonic-gate 		ra->nd_ra_flags_reserved |= ND_RA_FLAG_OTHER;
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate 	if (pi->pi_adv_state == FINAL_ADV)
2297c478bd9Sstevel@tonic-gate 		ra->nd_ra_router_lifetime = htons(0);
2307c478bd9Sstevel@tonic-gate 	else
2317c478bd9Sstevel@tonic-gate 		ra->nd_ra_router_lifetime = htons(pi->pi_AdvDefaultLifetime);
2327c478bd9Sstevel@tonic-gate 	ra->nd_ra_reachable = htonl(pi->pi_AdvReachableTime);
2337c478bd9Sstevel@tonic-gate 	ra->nd_ra_retransmit = htonl(pi->pi_AdvRetransTimer);
2347c478bd9Sstevel@tonic-gate 
2357c478bd9Sstevel@tonic-gate 	packetlen = sizeof (*ra);
2367c478bd9Sstevel@tonic-gate 	pptr += sizeof (*ra);
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate 	if (pi->pi_adv_state == FINAL_ADV) {
2397c478bd9Sstevel@tonic-gate 		if (debug & D_PKTOUT) {
2407c478bd9Sstevel@tonic-gate 			print_route_adv("Sending advert (FINAL) to ", pi,
2417c478bd9Sstevel@tonic-gate 			    ra, packetlen, sin6);
2427c478bd9Sstevel@tonic-gate 		}
2437c478bd9Sstevel@tonic-gate 		sendpacket(sin6, pi->pi_sock, packetlen, 0);
2447c478bd9Sstevel@tonic-gate 		/* Feed packet back in for router operation */
2457c478bd9Sstevel@tonic-gate 		loopback_ra_enqueue(pi, ra, packetlen);
2467c478bd9Sstevel@tonic-gate 		return;
2477c478bd9Sstevel@tonic-gate 	}
2487c478bd9Sstevel@tonic-gate 
249e11c3f44Smeem 	/* add options */
250e11c3f44Smeem 	packetlen += add_opt_lla(pi, (struct nd_opt_lla *)pptr);
251e11c3f44Smeem 	pptr = (char *)packet + packetlen;
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate 	if (pi->pi_AdvLinkMTU != 0) {
2547c478bd9Sstevel@tonic-gate 		struct nd_opt_mtu *mo = (struct nd_opt_mtu *)pptr;
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate 		mo->nd_opt_mtu_type = ND_OPT_MTU;
2577c478bd9Sstevel@tonic-gate 		mo->nd_opt_mtu_len = sizeof (struct nd_opt_mtu) / 8;
2587c478bd9Sstevel@tonic-gate 		mo->nd_opt_mtu_reserved = 0;
2597c478bd9Sstevel@tonic-gate 		mo->nd_opt_mtu_mtu = htonl(pi->pi_AdvLinkMTU);
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 		packetlen += sizeof (struct nd_opt_mtu);
2627c478bd9Sstevel@tonic-gate 		pptr += sizeof (struct nd_opt_mtu);
2637c478bd9Sstevel@tonic-gate 	}
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate 	if (no_prefixes) {
2667c478bd9Sstevel@tonic-gate 		if (debug & D_PKTOUT) {
2677c478bd9Sstevel@tonic-gate 			print_route_adv("Sending advert to ", pi,
2687c478bd9Sstevel@tonic-gate 			    ra, packetlen, sin6);
2697c478bd9Sstevel@tonic-gate 		}
2707c478bd9Sstevel@tonic-gate 		sendpacket(sin6, pi->pi_sock, packetlen, 0);
2717c478bd9Sstevel@tonic-gate 		/* Feed packet back in for router operation */
2727c478bd9Sstevel@tonic-gate 		loopback_ra_enqueue(pi, ra, packetlen);
2737c478bd9Sstevel@tonic-gate 		return;
2747c478bd9Sstevel@tonic-gate 	}
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 	po = (struct nd_opt_prefix_info *)pptr;
2777c478bd9Sstevel@tonic-gate 	for (adv_pr = pi->pi_adv_prefix_list; adv_pr != NULL;
2787c478bd9Sstevel@tonic-gate 	    adv_pr = adv_pr->adv_pr_next) {
2797c478bd9Sstevel@tonic-gate 		if (!adv_pr->adv_pr_AdvOnLinkFlag &&
2807c478bd9Sstevel@tonic-gate 		    !adv_pr->adv_pr_AdvAutonomousFlag) {
2817c478bd9Sstevel@tonic-gate 			continue;
2827c478bd9Sstevel@tonic-gate 		}
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 		/*
2857c478bd9Sstevel@tonic-gate 		 * If the prefix doesn't fit in packet send
2867c478bd9Sstevel@tonic-gate 		 * what we have so far and start with new packet.
2877c478bd9Sstevel@tonic-gate 		 */
2887c478bd9Sstevel@tonic-gate 		if (packetlen + sizeof (*po) >
2897c478bd9Sstevel@tonic-gate 		    pi->pi_LinkMTU - sizeof (struct ip6_hdr)) {
2907c478bd9Sstevel@tonic-gate 			if (debug & D_PKTOUT) {
2917c478bd9Sstevel@tonic-gate 				print_route_adv("Sending advert "
2927c478bd9Sstevel@tonic-gate 				    "(FRAG) to ",
2937c478bd9Sstevel@tonic-gate 				    pi, ra, packetlen, sin6);
2947c478bd9Sstevel@tonic-gate 			}
2957c478bd9Sstevel@tonic-gate 			sendpacket(sin6, pi->pi_sock, packetlen, 0);
2967c478bd9Sstevel@tonic-gate 			/* Feed packet back in for router operation */
2977c478bd9Sstevel@tonic-gate 			loopback_ra_enqueue(pi, ra, packetlen);
2987c478bd9Sstevel@tonic-gate 			packetlen = sizeof (*ra);
2997c478bd9Sstevel@tonic-gate 			pptr = (char *)packet + sizeof (*ra);
3007c478bd9Sstevel@tonic-gate 			po = (struct nd_opt_prefix_info *)pptr;
3017c478bd9Sstevel@tonic-gate 		}
3027c478bd9Sstevel@tonic-gate 		po->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
3037c478bd9Sstevel@tonic-gate 		po->nd_opt_pi_len = sizeof (*po)/8;
3047c478bd9Sstevel@tonic-gate 		po->nd_opt_pi_flags_reserved = 0;
3057c478bd9Sstevel@tonic-gate 		if (adv_pr->adv_pr_AdvOnLinkFlag) {
3067c478bd9Sstevel@tonic-gate 			po->nd_opt_pi_flags_reserved |=
3077c478bd9Sstevel@tonic-gate 			    ND_OPT_PI_FLAG_ONLINK;
3087c478bd9Sstevel@tonic-gate 		}
3097c478bd9Sstevel@tonic-gate 		if (adv_pr->adv_pr_AdvAutonomousFlag) {
3107c478bd9Sstevel@tonic-gate 			po->nd_opt_pi_flags_reserved |=
3117c478bd9Sstevel@tonic-gate 			    ND_OPT_PI_FLAG_AUTO;
3127c478bd9Sstevel@tonic-gate 		}
3137c478bd9Sstevel@tonic-gate 		po->nd_opt_pi_prefix_len = adv_pr->adv_pr_prefix_len;
3147c478bd9Sstevel@tonic-gate 		/*
3157c478bd9Sstevel@tonic-gate 		 * If both Adv*Expiration and Adv*Lifetime are
3167c478bd9Sstevel@tonic-gate 		 * set we prefer the former and make the lifetime
3177c478bd9Sstevel@tonic-gate 		 * decrement in real time.
3187c478bd9Sstevel@tonic-gate 		 */
3197c478bd9Sstevel@tonic-gate 		if (adv_pr->adv_pr_AdvValidRealTime) {
3207c478bd9Sstevel@tonic-gate 			po->nd_opt_pi_valid_time =
3217c478bd9Sstevel@tonic-gate 			    htonl(adv_pr->adv_pr_AdvValidExpiration);
3227c478bd9Sstevel@tonic-gate 		} else {
3237c478bd9Sstevel@tonic-gate 			po->nd_opt_pi_valid_time =
3247c478bd9Sstevel@tonic-gate 			    htonl(adv_pr->adv_pr_AdvValidLifetime);
3257c478bd9Sstevel@tonic-gate 		}
3267c478bd9Sstevel@tonic-gate 		if (adv_pr->adv_pr_AdvPreferredRealTime) {
3277c478bd9Sstevel@tonic-gate 			po->nd_opt_pi_preferred_time =
3287c478bd9Sstevel@tonic-gate 			    htonl(adv_pr->adv_pr_AdvPreferredExpiration);
3297c478bd9Sstevel@tonic-gate 		} else {
3307c478bd9Sstevel@tonic-gate 			po->nd_opt_pi_preferred_time =
3317c478bd9Sstevel@tonic-gate 			    htonl(adv_pr->adv_pr_AdvPreferredLifetime);
3327c478bd9Sstevel@tonic-gate 		}
3337c478bd9Sstevel@tonic-gate 		po->nd_opt_pi_reserved2 = htonl(0);
3347c478bd9Sstevel@tonic-gate 		po->nd_opt_pi_prefix = adv_pr->adv_pr_prefix;
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate 		po++;
3377c478bd9Sstevel@tonic-gate 		packetlen += sizeof (*po);
3387c478bd9Sstevel@tonic-gate 	}
3397c478bd9Sstevel@tonic-gate 	if (debug & D_PKTOUT) {
3407c478bd9Sstevel@tonic-gate 		print_route_adv("Sending advert to ", pi,
3417c478bd9Sstevel@tonic-gate 		    ra, packetlen, sin6);
3427c478bd9Sstevel@tonic-gate 	}
3437c478bd9Sstevel@tonic-gate 	sendpacket(sin6, pi->pi_sock, packetlen, 0);
3447c478bd9Sstevel@tonic-gate 	/* Feed packet back in for router operation */
3457c478bd9Sstevel@tonic-gate 	loopback_ra_enqueue(pi, ra, packetlen);
3467c478bd9Sstevel@tonic-gate }
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate /* Poll support */
3497c478bd9Sstevel@tonic-gate static int		pollfd_num = 0;	/* Allocated and initialized */
3507c478bd9Sstevel@tonic-gate static struct pollfd	*pollfds = NULL;
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate /*
3537c478bd9Sstevel@tonic-gate  * Add fd to the set being polled. Returns 0 if ok; -1 if failed.
3547c478bd9Sstevel@tonic-gate  */
3557c478bd9Sstevel@tonic-gate int
poll_add(int fd)3567c478bd9Sstevel@tonic-gate poll_add(int fd)
3577c478bd9Sstevel@tonic-gate {
3587c478bd9Sstevel@tonic-gate 	int i;
3597c478bd9Sstevel@tonic-gate 	int new_num;
3607c478bd9Sstevel@tonic-gate 	struct pollfd *newfds;
3613173664eSapersson 
3627c478bd9Sstevel@tonic-gate 	/* Check if already present */
3637c478bd9Sstevel@tonic-gate 	for (i = 0; i < pollfd_num; i++) {
3647c478bd9Sstevel@tonic-gate 		if (pollfds[i].fd == fd)
3657c478bd9Sstevel@tonic-gate 			return (0);
3667c478bd9Sstevel@tonic-gate 	}
3677c478bd9Sstevel@tonic-gate 	/* Check for empty spot already present */
3687c478bd9Sstevel@tonic-gate 	for (i = 0; i < pollfd_num; i++) {
3697c478bd9Sstevel@tonic-gate 		if (pollfds[i].fd == -1) {
3707c478bd9Sstevel@tonic-gate 			pollfds[i].fd = fd;
3717c478bd9Sstevel@tonic-gate 			return (0);
3727c478bd9Sstevel@tonic-gate 		}
3737c478bd9Sstevel@tonic-gate 	}
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate 	/* Allocate space for 32 more fds and initialize to -1 */
3767c478bd9Sstevel@tonic-gate 	new_num = pollfd_num + 32;
3777c478bd9Sstevel@tonic-gate 	newfds = realloc(pollfds, new_num * sizeof (struct pollfd));
3787c478bd9Sstevel@tonic-gate 	if (newfds == NULL) {
3796e91bba0SGirish Moodalbail 		logperror("realloc");
3807c478bd9Sstevel@tonic-gate 		return (-1);
3817c478bd9Sstevel@tonic-gate 	}
3823173664eSapersson 
3833173664eSapersson 	newfds[pollfd_num].fd = fd;
3843173664eSapersson 	newfds[pollfd_num++].events = POLLIN;
3853173664eSapersson 
3867c478bd9Sstevel@tonic-gate 	for (i = pollfd_num; i < new_num; i++) {
3877c478bd9Sstevel@tonic-gate 		newfds[i].fd = -1;
3887c478bd9Sstevel@tonic-gate 		newfds[i].events = POLLIN;
3897c478bd9Sstevel@tonic-gate 	}
3907c478bd9Sstevel@tonic-gate 	pollfd_num = new_num;
3917c478bd9Sstevel@tonic-gate 	pollfds = newfds;
3923173664eSapersson 	return (0);
3937c478bd9Sstevel@tonic-gate }
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate /*
3967c478bd9Sstevel@tonic-gate  * Remove fd from the set being polled. Returns 0 if ok; -1 if failed.
3977c478bd9Sstevel@tonic-gate  */
3987c478bd9Sstevel@tonic-gate int
poll_remove(int fd)3997c478bd9Sstevel@tonic-gate poll_remove(int fd)
4007c478bd9Sstevel@tonic-gate {
4017c478bd9Sstevel@tonic-gate 	int i;
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate 	/* Check if already present */
4047c478bd9Sstevel@tonic-gate 	for (i = 0; i < pollfd_num; i++) {
4057c478bd9Sstevel@tonic-gate 		if (pollfds[i].fd == fd) {
4067c478bd9Sstevel@tonic-gate 			pollfds[i].fd = -1;
4077c478bd9Sstevel@tonic-gate 			return (0);
4087c478bd9Sstevel@tonic-gate 		}
4097c478bd9Sstevel@tonic-gate 	}
4107c478bd9Sstevel@tonic-gate 	return (-1);
4117c478bd9Sstevel@tonic-gate }
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate /*
4147c478bd9Sstevel@tonic-gate  * Extract information about the ifname (either a physical interface and
4157c478bd9Sstevel@tonic-gate  * the ":0" logical interface or just a logical interface).
4167c478bd9Sstevel@tonic-gate  * If the interface (still) exists in kernel set pr_in_use
4177c478bd9Sstevel@tonic-gate  * for caller to be able to detect interfaces that are removed.
4187c478bd9Sstevel@tonic-gate  * Starts sending advertisements/solicitations when new physical interfaces
4197c478bd9Sstevel@tonic-gate  * are detected.
4207c478bd9Sstevel@tonic-gate  */
4217c478bd9Sstevel@tonic-gate static void
if_process(int s,char * ifname,boolean_t first)4227c478bd9Sstevel@tonic-gate if_process(int s, char *ifname, boolean_t first)
4237c478bd9Sstevel@tonic-gate {
4247c478bd9Sstevel@tonic-gate 	struct lifreq lifr;
4257c478bd9Sstevel@tonic-gate 	struct phyint *pi;
4267c478bd9Sstevel@tonic-gate 	struct prefix *pr;
4277c478bd9Sstevel@tonic-gate 	char *cp;
4287c478bd9Sstevel@tonic-gate 	char phyintname[LIFNAMSIZ + 1];
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 	if (debug & D_IFSCAN)
4317c478bd9Sstevel@tonic-gate 		logmsg(LOG_DEBUG, "if_process(%s)\n", ifname);
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate 	(void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
4347c478bd9Sstevel@tonic-gate 	lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
4357c478bd9Sstevel@tonic-gate 	if (ioctl(s, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
4367c478bd9Sstevel@tonic-gate 		if (errno == ENXIO) {
4377c478bd9Sstevel@tonic-gate 			/*
4387c478bd9Sstevel@tonic-gate 			 * Interface has disappeared
4397c478bd9Sstevel@tonic-gate 			 */
4407c478bd9Sstevel@tonic-gate 			return;
4417c478bd9Sstevel@tonic-gate 		}
4427c478bd9Sstevel@tonic-gate 		logperror("if_process: ioctl (get interface flags)");
4437c478bd9Sstevel@tonic-gate 		return;
4447c478bd9Sstevel@tonic-gate 	}
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate 	/*
4471cb875aeSCathy Zhou 	 * Ignore loopback, point-to-multipoint and VRRP interfaces.
4481cb875aeSCathy Zhou 	 * The IP addresses over VRRP interfaces cannot be auto-configured.
4497c478bd9Sstevel@tonic-gate 	 * Point-to-point interfaces always have IFF_MULTICAST set.
4507c478bd9Sstevel@tonic-gate 	 */
4517c478bd9Sstevel@tonic-gate 	if (!(lifr.lifr_flags & IFF_MULTICAST) ||
4521cb875aeSCathy Zhou 	    (lifr.lifr_flags & (IFF_LOOPBACK|IFF_VRRP))) {
4537c478bd9Sstevel@tonic-gate 		return;
4547c478bd9Sstevel@tonic-gate 	}
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate 	if (!(lifr.lifr_flags & IFF_IPV6))
4577c478bd9Sstevel@tonic-gate 		return;
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	(void) strncpy(phyintname, ifname, sizeof (phyintname));
4607c478bd9Sstevel@tonic-gate 	phyintname[sizeof (phyintname) - 1] = '\0';
4617c478bd9Sstevel@tonic-gate 	if ((cp = strchr(phyintname, IF_SEPARATOR)) != NULL) {
4627c478bd9Sstevel@tonic-gate 		*cp = '\0';
4637c478bd9Sstevel@tonic-gate 	}
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 	pi = phyint_lookup(phyintname);
4667c478bd9Sstevel@tonic-gate 	if (pi == NULL) {
4677c478bd9Sstevel@tonic-gate 		pi = phyint_create(phyintname);
4687c478bd9Sstevel@tonic-gate 		if (pi == NULL) {
4697c478bd9Sstevel@tonic-gate 			logmsg(LOG_ERR, "if_process: out of memory\n");
4707c478bd9Sstevel@tonic-gate 			return;
4717c478bd9Sstevel@tonic-gate 		}
4726e91bba0SGirish Moodalbail 		/*
4736e91bba0SGirish Moodalbail 		 * if in.ndpd is restarted, check with ipmgmtd if there is any
4746e91bba0SGirish Moodalbail 		 * interface id to be configured for this interface.
4756e91bba0SGirish Moodalbail 		 */
4766e91bba0SGirish Moodalbail 		if (first) {
4776e91bba0SGirish Moodalbail 			if (phyint_check_ipadm_intfid(pi) == -1)
4786e91bba0SGirish Moodalbail 				logmsg(LOG_ERR, "Could not get ipadm info\n");
4796e91bba0SGirish Moodalbail 		}
4806e91bba0SGirish Moodalbail 	} else {
4816e91bba0SGirish Moodalbail 		/*
4826e91bba0SGirish Moodalbail 		 * if the phyint already exists, synchronize it with
4836e91bba0SGirish Moodalbail 		 * the kernel state. For a newly created phyint, phyint_create
4846e91bba0SGirish Moodalbail 		 * calls phyint_init_from_k().
4856e91bba0SGirish Moodalbail 		 */
4866e91bba0SGirish Moodalbail 		(void) phyint_init_from_k(pi);
4877c478bd9Sstevel@tonic-gate 	}
4887c478bd9Sstevel@tonic-gate 	if (pi->pi_sock == -1 && !(pi->pi_kernel_state & PI_PRESENT)) {
4897c478bd9Sstevel@tonic-gate 		/* Interface is not yet present */
4907c478bd9Sstevel@tonic-gate 		if (debug & D_PHYINT) {
4917c478bd9Sstevel@tonic-gate 			logmsg(LOG_DEBUG, "if_process: interface not yet "
4927c478bd9Sstevel@tonic-gate 			    "present %s\n", pi->pi_name);
4937c478bd9Sstevel@tonic-gate 		}
4947c478bd9Sstevel@tonic-gate 		return;
4957c478bd9Sstevel@tonic-gate 	}
4967c478bd9Sstevel@tonic-gate 
4977c478bd9Sstevel@tonic-gate 	if (pi->pi_sock != -1) {
4987c478bd9Sstevel@tonic-gate 		if (poll_add(pi->pi_sock) == -1) {
4997c478bd9Sstevel@tonic-gate 			/*
5007c478bd9Sstevel@tonic-gate 			 * reset state.
5017c478bd9Sstevel@tonic-gate 			 */
5027c478bd9Sstevel@tonic-gate 			phyint_cleanup(pi);
5037c478bd9Sstevel@tonic-gate 		}
5047c478bd9Sstevel@tonic-gate 	}
5057c478bd9Sstevel@tonic-gate 
5067c478bd9Sstevel@tonic-gate 	/*
5077c478bd9Sstevel@tonic-gate 	 * Check if IFF_ROUTER has been turned off in kernel in which
5087c478bd9Sstevel@tonic-gate 	 * case we have to turn off AdvSendAdvertisements.
5097c478bd9Sstevel@tonic-gate 	 * The kernel will automatically turn off IFF_ROUTER if
5107c478bd9Sstevel@tonic-gate 	 * ip6_forwarding is turned off.
5117c478bd9Sstevel@tonic-gate 	 * Note that we do not switch back should IFF_ROUTER be turned on.
5127c478bd9Sstevel@tonic-gate 	 */
5137c478bd9Sstevel@tonic-gate 	if (!first &&
5147c478bd9Sstevel@tonic-gate 	    pi->pi_AdvSendAdvertisements && !(pi->pi_flags & IFF_ROUTER)) {
5157c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "No longer a router on %s\n", pi->pi_name);
5167c478bd9Sstevel@tonic-gate 		check_to_advertise(pi, START_FINAL_ADV);
5177c478bd9Sstevel@tonic-gate 
5187c478bd9Sstevel@tonic-gate 		pi->pi_AdvSendAdvertisements = 0;
5197c478bd9Sstevel@tonic-gate 		pi->pi_sol_state = NO_SOLICIT;
5207c478bd9Sstevel@tonic-gate 	}
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate 	/*
5237c478bd9Sstevel@tonic-gate 	 * Send advertisments and solicitation only if the interface is
5247c478bd9Sstevel@tonic-gate 	 * present in the kernel.
5257c478bd9Sstevel@tonic-gate 	 */
5267c478bd9Sstevel@tonic-gate 	if (pi->pi_kernel_state & PI_PRESENT) {
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate 		if (pi->pi_AdvSendAdvertisements) {
5297c478bd9Sstevel@tonic-gate 			if (pi->pi_adv_state == NO_ADV)
5307c478bd9Sstevel@tonic-gate 				check_to_advertise(pi, START_INIT_ADV);
5317c478bd9Sstevel@tonic-gate 		} else {
5327c478bd9Sstevel@tonic-gate 			if (pi->pi_sol_state == NO_SOLICIT)
5337c478bd9Sstevel@tonic-gate 				check_to_solicit(pi, START_INIT_SOLICIT);
5347c478bd9Sstevel@tonic-gate 		}
5357c478bd9Sstevel@tonic-gate 	}
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 	/*
5387c478bd9Sstevel@tonic-gate 	 * Track static kernel prefixes to prevent in.ndpd from clobbering
5397c478bd9Sstevel@tonic-gate 	 * them by creating a struct prefix for each prefix detected in the
5407c478bd9Sstevel@tonic-gate 	 * kernel.
5417c478bd9Sstevel@tonic-gate 	 */
5427c478bd9Sstevel@tonic-gate 	pr = prefix_lookup_name(pi, ifname);
5437c478bd9Sstevel@tonic-gate 	if (pr == NULL) {
5447c478bd9Sstevel@tonic-gate 		pr = prefix_create_name(pi, ifname);
5457c478bd9Sstevel@tonic-gate 		if (pr == NULL) {
5467c478bd9Sstevel@tonic-gate 			logmsg(LOG_ERR, "if_process: out of memory\n");
5477c478bd9Sstevel@tonic-gate 			return;
5487c478bd9Sstevel@tonic-gate 		}
5497c478bd9Sstevel@tonic-gate 		if (prefix_init_from_k(pr) == -1) {
5507c478bd9Sstevel@tonic-gate 			prefix_delete(pr);
5517c478bd9Sstevel@tonic-gate 			return;
5527c478bd9Sstevel@tonic-gate 		}
5537c478bd9Sstevel@tonic-gate 	}
5547c478bd9Sstevel@tonic-gate 	/* Detect prefixes which are removed */
5557c478bd9Sstevel@tonic-gate 	if (pr->pr_kernel_state != 0)
5567c478bd9Sstevel@tonic-gate 		pr->pr_in_use = _B_TRUE;
55769bb4bb4Scarlsonj 
55869bb4bb4Scarlsonj 	if ((lifr.lifr_flags & IFF_DUPLICATE) &&
559d04ccbb3Scarlsonj 	    !(lifr.lifr_flags & IFF_DHCPRUNNING) &&
56069bb4bb4Scarlsonj 	    (pr->pr_flags & IFF_TEMPORARY)) {
56169bb4bb4Scarlsonj 		in6_addr_t *token;
56269bb4bb4Scarlsonj 		int i;
56369bb4bb4Scarlsonj 		char abuf[INET6_ADDRSTRLEN];
56469bb4bb4Scarlsonj 
56569bb4bb4Scarlsonj 		if (++pr->pr_attempts >= MAX_DAD_FAILURES) {
56669bb4bb4Scarlsonj 			logmsg(LOG_ERR, "%s: token %s is duplicate after %d "
56769bb4bb4Scarlsonj 			    "attempts; disabling temporary addresses on %s",
56869bb4bb4Scarlsonj 			    pr->pr_name, inet_ntop(AF_INET6,
56969bb4bb4Scarlsonj 			    (void *)&pi->pi_tmp_token, abuf, sizeof (abuf)),
57069bb4bb4Scarlsonj 			    pr->pr_attempts, pi->pi_name);
57169bb4bb4Scarlsonj 			pi->pi_TmpAddrsEnabled = 0;
57269bb4bb4Scarlsonj 			tmptoken_delete(pi);
57369bb4bb4Scarlsonj 			prefix_delete(pr);
57469bb4bb4Scarlsonj 			return;
57569bb4bb4Scarlsonj 		}
57669bb4bb4Scarlsonj 		logmsg(LOG_WARNING, "%s: token %s is duplicate; trying again",
57769bb4bb4Scarlsonj 		    pr->pr_name, inet_ntop(AF_INET6, (void *)&pi->pi_tmp_token,
57869bb4bb4Scarlsonj 		    abuf, sizeof (abuf)));
57969bb4bb4Scarlsonj 		if (!tmptoken_create(pi)) {
58069bb4bb4Scarlsonj 			prefix_delete(pr);
58169bb4bb4Scarlsonj 			return;
58269bb4bb4Scarlsonj 		}
58369bb4bb4Scarlsonj 		token = &pi->pi_tmp_token;
58469bb4bb4Scarlsonj 		for (i = 0; i < 16; i++) {
58569bb4bb4Scarlsonj 			/*
58669bb4bb4Scarlsonj 			 * prefix_create ensures that pr_prefix has all-zero
58769bb4bb4Scarlsonj 			 * bits after prefixlen.
58869bb4bb4Scarlsonj 			 */
58969bb4bb4Scarlsonj 			pr->pr_address.s6_addr[i] = pr->pr_prefix.s6_addr[i] |
59069bb4bb4Scarlsonj 			    token->s6_addr[i];
59169bb4bb4Scarlsonj 		}
59269bb4bb4Scarlsonj 		if (prefix_lookup_addr_match(pr) != NULL) {
59369bb4bb4Scarlsonj 			prefix_delete(pr);
59469bb4bb4Scarlsonj 			return;
59569bb4bb4Scarlsonj 		}
59669bb4bb4Scarlsonj 		pr->pr_CreateTime = getcurrenttime() / MILLISEC;
59769bb4bb4Scarlsonj 		/*
59869bb4bb4Scarlsonj 		 * We've got a new token.  Clearing PR_AUTO causes
59969bb4bb4Scarlsonj 		 * prefix_update_k to bring the interface up and set the
60069bb4bb4Scarlsonj 		 * address.
60169bb4bb4Scarlsonj 		 */
60269bb4bb4Scarlsonj 		pr->pr_kernel_state &= ~PR_AUTO;
60369bb4bb4Scarlsonj 		prefix_update_k(pr);
604