diff --git a/sys/net/if_spppsubr.c b/sys/net/if_spppsubr.c
index 5a18147f14a7..fbf7b0ea8f4c 100644
--- a/sys/net/if_spppsubr.c
+++ b/sys/net/if_spppsubr.c
@@ -1,5413 +1,5417 @@
 /*
  * Synchronous PPP/Cisco/Frame Relay link level subroutines.
  * Keepalive protocol implemented in both Cisco and PPP modes.
  */
 /*-
  * Copyright (C) 1994-2000 Cronyx Engineering.
  * Author: Serge Vakulenko, <vak@cronyx.ru>
  *
  * Heavily revamped to conform to RFC 1661.
  * Copyright (C) 1997, 2001 Joerg Wunsch.
  *
  * This software is distributed with NO WARRANTIES, not even the implied
  * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  *
  * Authors grant any other persons or organisations permission to use
  * or modify this software as long as this message is kept with the software,
  * all derivative works or modified versions.
  *
  * From: Version 2.4, Thu Apr 30 17:17:21 MSD 1997
  *
  * $FreeBSD$
  */
 
 #include <sys/param.h>
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
 
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/module.h>
 #include <sys/rmlock.h>
 #include <sys/sockio.h>
 #include <sys/socket.h>
 #include <sys/syslog.h>
 #include <sys/random.h>
 #include <sys/malloc.h>
 #include <sys/mbuf.h>
 
 #include <sys/md5.h>
 
 #include <net/if.h>
 #include <net/if_var.h>
 #include <net/netisr.h>
 #include <net/if_types.h>
 #include <net/route.h>
 #include <net/vnet.h>
 #include <netinet/in.h>
+#include <netinet/in_var.h>
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
 #include <net/slcompress.h>
 
 #include <machine/stdarg.h>
 
 #include <netinet/in_var.h>
 
 #ifdef INET
 #include <netinet/ip.h>
 #include <netinet/tcp.h>
 #endif
 
 #ifdef INET6
 #include <netinet6/scope6_var.h>
 #endif
 
 #include <netinet/if_ether.h>
 
 #include <net/if_sppp.h>
 
 #define IOCTL_CMD_T	u_long
 #define MAXALIVECNT     3               /* max. alive packets */
 
 /*
  * Interface flags that can be set in an ifconfig command.
  *
  * Setting link0 will make the link passive, i.e. it will be marked
  * as being administrative openable, but won't be opened to begin
  * with.  Incoming calls will be answered, or subsequent calls with
  * -link1 will cause the administrative open of the LCP layer.
  *
  * Setting link1 will cause the link to auto-dial only as packets
  * arrive to be sent.
  *
  * Setting IFF_DEBUG will syslog the option negotiation and state
  * transitions at level kern.debug.  Note: all logs consistently look
  * like
  *
  *   <if-name><unit>: <proto-name> <additional info...>
  *
  * with <if-name><unit> being something like "bppp0", and <proto-name>
  * being one of "lcp", "ipcp", "cisco", "chap", "pap", etc.
  */
 
 #define IFF_PASSIVE	IFF_LINK0	/* wait passively for connection */
 #define IFF_AUTO	IFF_LINK1	/* auto-dial on output */
 #define IFF_CISCO	IFF_LINK2	/* auto-dial on output */
 
 #define PPP_ALLSTATIONS 0xff		/* All-Stations broadcast address */
 #define PPP_UI		0x03		/* Unnumbered Information */
 #define PPP_IP		0x0021		/* Internet Protocol */
 #define PPP_ISO		0x0023		/* ISO OSI Protocol */
 #define PPP_XNS		0x0025		/* Xerox NS Protocol */
 #define PPP_IPX		0x002b		/* Novell IPX Protocol */
 #define PPP_VJ_COMP	0x002d		/* VJ compressed TCP/IP */
 #define PPP_VJ_UCOMP	0x002f		/* VJ uncompressed TCP/IP */
 #define PPP_IPV6	0x0057		/* Internet Protocol Version 6 */
 #define PPP_LCP		0xc021		/* Link Control Protocol */
 #define PPP_PAP		0xc023		/* Password Authentication Protocol */
 #define PPP_CHAP	0xc223		/* Challenge-Handshake Auth Protocol */
 #define PPP_IPCP	0x8021		/* Internet Protocol Control Protocol */
 #define PPP_IPV6CP	0x8057		/* IPv6 Control Protocol */
 
 #define CONF_REQ	1		/* PPP configure request */
 #define CONF_ACK	2		/* PPP configure acknowledge */
 #define CONF_NAK	3		/* PPP configure negative ack */
 #define CONF_REJ	4		/* PPP configure reject */
 #define TERM_REQ	5		/* PPP terminate request */
 #define TERM_ACK	6		/* PPP terminate acknowledge */
 #define CODE_REJ	7		/* PPP code reject */
 #define PROTO_REJ	8		/* PPP protocol reject */
 #define ECHO_REQ	9		/* PPP echo request */
 #define ECHO_REPLY	10		/* PPP echo reply */
 #define DISC_REQ	11		/* PPP discard request */
 
 #define LCP_OPT_MRU		1	/* maximum receive unit */
 #define LCP_OPT_ASYNC_MAP	2	/* async control character map */
 #define LCP_OPT_AUTH_PROTO	3	/* authentication protocol */
 #define LCP_OPT_QUAL_PROTO	4	/* quality protocol */
 #define LCP_OPT_MAGIC		5	/* magic number */
 #define LCP_OPT_RESERVED	6	/* reserved */
 #define LCP_OPT_PROTO_COMP	7	/* protocol field compression */
 #define LCP_OPT_ADDR_COMP	8	/* address/control field compression */
 
 #define IPCP_OPT_ADDRESSES	1	/* both IP addresses; deprecated */
 #define IPCP_OPT_COMPRESSION	2	/* IP compression protocol (VJ) */
 #define IPCP_OPT_ADDRESS	3	/* local IP address */
 
 #define IPV6CP_OPT_IFID	1	/* interface identifier */
 #define IPV6CP_OPT_COMPRESSION	2	/* IPv6 compression protocol */
 
 #define IPCP_COMP_VJ		0x2d	/* Code for VJ compression */
 
 #define PAP_REQ			1	/* PAP name/password request */
 #define PAP_ACK			2	/* PAP acknowledge */
 #define PAP_NAK			3	/* PAP fail */
 
 #define CHAP_CHALLENGE		1	/* CHAP challenge request */
 #define CHAP_RESPONSE		2	/* CHAP challenge response */
 #define CHAP_SUCCESS		3	/* CHAP response ok */
 #define CHAP_FAILURE		4	/* CHAP response failed */
 
 #define CHAP_MD5		5	/* hash algorithm - MD5 */
 
 #define CISCO_MULTICAST		0x8f	/* Cisco multicast address */
 #define CISCO_UNICAST		0x0f	/* Cisco unicast address */
 #define CISCO_KEEPALIVE		0x8035	/* Cisco keepalive protocol */
 #define CISCO_ADDR_REQ		0	/* Cisco address request */
 #define CISCO_ADDR_REPLY	1	/* Cisco address reply */
 #define CISCO_KEEPALIVE_REQ	2	/* Cisco keepalive request */
 
 /* states are named and numbered according to RFC 1661 */
 #define STATE_INITIAL	0
 #define STATE_STARTING	1
 #define STATE_CLOSED	2
 #define STATE_STOPPED	3
 #define STATE_CLOSING	4
 #define STATE_STOPPING	5
 #define STATE_REQ_SENT	6
 #define STATE_ACK_RCVD	7
 #define STATE_ACK_SENT	8
 #define STATE_OPENED	9
 
 static MALLOC_DEFINE(M_SPPP, "sppp", "synchronous PPP interface internals");
 
 struct ppp_header {
 	u_char address;
 	u_char control;
 	u_short protocol;
 } __packed;
 #define PPP_HEADER_LEN          sizeof (struct ppp_header)
 
 struct lcp_header {
 	u_char type;
 	u_char ident;
 	u_short len;
 } __packed;
 #define LCP_HEADER_LEN          sizeof (struct lcp_header)
 
 struct cisco_packet {
 	u_long type;
 	u_long par1;
 	u_long par2;
 	u_short rel;
 	u_short time0;
 	u_short time1;
 } __packed;
 #define CISCO_PACKET_LEN	sizeof (struct cisco_packet)
 
 /*
  * We follow the spelling and capitalization of RFC 1661 here, to make
  * it easier comparing with the standard.  Please refer to this RFC in
  * case you can't make sense out of these abbreviation; it will also
  * explain the semantics related to the various events and actions.
  */
 struct cp {
 	u_short	proto;		/* PPP control protocol number */
 	u_char protoidx;	/* index into state table in struct sppp */
 	u_char flags;
 #define CP_LCP		0x01	/* this is the LCP */
 #define CP_AUTH		0x02	/* this is an authentication protocol */
 #define CP_NCP		0x04	/* this is a NCP */
 #define CP_QUAL		0x08	/* this is a quality reporting protocol */
 	const char *name;	/* name of this control protocol */
 	/* event handlers */
 	void	(*Up)(struct sppp *sp);
 	void	(*Down)(struct sppp *sp);
 	void	(*Open)(struct sppp *sp);
 	void	(*Close)(struct sppp *sp);
 	void	(*TO)(void *sp);
 	int	(*RCR)(struct sppp *sp, struct lcp_header *h, int len);
 	void	(*RCN_rej)(struct sppp *sp, struct lcp_header *h, int len);
 	void	(*RCN_nak)(struct sppp *sp, struct lcp_header *h, int len);
 	/* actions */
 	void	(*tlu)(struct sppp *sp);
 	void	(*tld)(struct sppp *sp);
 	void	(*tls)(struct sppp *sp);
 	void	(*tlf)(struct sppp *sp);
 	void	(*scr)(struct sppp *sp);
 };
 
 #define	SPP_FMT		"%s: "
 #define	SPP_ARGS(ifp)	(ifp)->if_xname
 
 #define SPPP_LOCK(sp)	mtx_lock (&(sp)->mtx)
 #define SPPP_UNLOCK(sp)	mtx_unlock (&(sp)->mtx)
 #define SPPP_LOCK_ASSERT(sp)	mtx_assert (&(sp)->mtx, MA_OWNED)
 #define SPPP_LOCK_OWNED(sp)	mtx_owned (&(sp)->mtx)
 
 #ifdef INET
 /*
  * The following disgusting hack gets around the problem that IP TOS
  * can't be set yet.  We want to put "interactive" traffic on a high
  * priority queue.  To decide if traffic is interactive, we check that
  * a) it is TCP and b) one of its ports is telnet, rlogin or ftp control.
  *
  * XXX is this really still necessary?  - joerg -
  */
 static const u_short interactive_ports[8] = {
 	0,	513,	0,	0,
 	0,	21,	0,	23,
 };
 #define INTERACTIVE(p) (interactive_ports[(p) & 7] == (p))
 #endif
 
 /* almost every function needs these */
 #define STDDCL							\
 	struct ifnet *ifp = SP2IFP(sp);				\
 	int debug = ifp->if_flags & IFF_DEBUG
 
 static int sppp_output(struct ifnet *ifp, struct mbuf *m,
 	const struct sockaddr *dst, struct route *ro);
 
 static void sppp_cisco_send(struct sppp *sp, int type, long par1, long par2);
 static void sppp_cisco_input(struct sppp *sp, struct mbuf *m);
 
 static void sppp_cp_input(const struct cp *cp, struct sppp *sp,
 			  struct mbuf *m);
 static void sppp_cp_send(struct sppp *sp, u_short proto, u_char type,
 			 u_char ident, u_short len, void *data);
 /* static void sppp_cp_timeout(void *arg); */
 static void sppp_cp_change_state(const struct cp *cp, struct sppp *sp,
 				 int newstate);
 static void sppp_auth_send(const struct cp *cp,
 			   struct sppp *sp, unsigned int type, unsigned int id,
 			   ...);
 
 static void sppp_up_event(const struct cp *cp, struct sppp *sp);
 static void sppp_down_event(const struct cp *cp, struct sppp *sp);
 static void sppp_open_event(const struct cp *cp, struct sppp *sp);
 static void sppp_close_event(const struct cp *cp, struct sppp *sp);
 static void sppp_to_event(const struct cp *cp, struct sppp *sp);
 
 static void sppp_null(struct sppp *sp);
 
 static void sppp_pp_up(struct sppp *sp);
 static void sppp_pp_down(struct sppp *sp);
 
 static void sppp_lcp_init(struct sppp *sp);
 static void sppp_lcp_up(struct sppp *sp);
 static void sppp_lcp_down(struct sppp *sp);
 static void sppp_lcp_open(struct sppp *sp);
 static void sppp_lcp_close(struct sppp *sp);
 static void sppp_lcp_TO(void *sp);
 static int sppp_lcp_RCR(struct sppp *sp, struct lcp_header *h, int len);
 static void sppp_lcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len);
 static void sppp_lcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len);
 static void sppp_lcp_tlu(struct sppp *sp);
 static void sppp_lcp_tld(struct sppp *sp);
 static void sppp_lcp_tls(struct sppp *sp);
 static void sppp_lcp_tlf(struct sppp *sp);
 static void sppp_lcp_scr(struct sppp *sp);
 static void sppp_lcp_check_and_close(struct sppp *sp);
 static int sppp_ncp_check(struct sppp *sp);
 
 static void sppp_ipcp_init(struct sppp *sp);
 static void sppp_ipcp_up(struct sppp *sp);
 static void sppp_ipcp_down(struct sppp *sp);
 static void sppp_ipcp_open(struct sppp *sp);
 static void sppp_ipcp_close(struct sppp *sp);
 static void sppp_ipcp_TO(void *sp);
 static int sppp_ipcp_RCR(struct sppp *sp, struct lcp_header *h, int len);
 static void sppp_ipcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len);
 static void sppp_ipcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len);
 static void sppp_ipcp_tlu(struct sppp *sp);
 static void sppp_ipcp_tld(struct sppp *sp);
 static void sppp_ipcp_tls(struct sppp *sp);
 static void sppp_ipcp_tlf(struct sppp *sp);
 static void sppp_ipcp_scr(struct sppp *sp);
 
 static void sppp_ipv6cp_init(struct sppp *sp);
 static void sppp_ipv6cp_up(struct sppp *sp);
 static void sppp_ipv6cp_down(struct sppp *sp);
 static void sppp_ipv6cp_open(struct sppp *sp);
 static void sppp_ipv6cp_close(struct sppp *sp);
 static void sppp_ipv6cp_TO(void *sp);
 static int sppp_ipv6cp_RCR(struct sppp *sp, struct lcp_header *h, int len);
 static void sppp_ipv6cp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len);
 static void sppp_ipv6cp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len);
 static void sppp_ipv6cp_tlu(struct sppp *sp);
 static void sppp_ipv6cp_tld(struct sppp *sp);
 static void sppp_ipv6cp_tls(struct sppp *sp);
 static void sppp_ipv6cp_tlf(struct sppp *sp);
 static void sppp_ipv6cp_scr(struct sppp *sp);
 
 static void sppp_pap_input(struct sppp *sp, struct mbuf *m);
 static void sppp_pap_init(struct sppp *sp);
 static void sppp_pap_open(struct sppp *sp);
 static void sppp_pap_close(struct sppp *sp);
 static void sppp_pap_TO(void *sp);
 static void sppp_pap_my_TO(void *sp);
 static void sppp_pap_tlu(struct sppp *sp);
 static void sppp_pap_tld(struct sppp *sp);
 static void sppp_pap_scr(struct sppp *sp);
 
 static void sppp_chap_input(struct sppp *sp, struct mbuf *m);
 static void sppp_chap_init(struct sppp *sp);
 static void sppp_chap_open(struct sppp *sp);
 static void sppp_chap_close(struct sppp *sp);
 static void sppp_chap_TO(void *sp);
 static void sppp_chap_tlu(struct sppp *sp);
 static void sppp_chap_tld(struct sppp *sp);
 static void sppp_chap_scr(struct sppp *sp);
 
 static const char *sppp_auth_type_name(u_short proto, u_char type);
 static const char *sppp_cp_type_name(u_char type);
 #ifdef INET
 static const char *sppp_dotted_quad(u_long addr);
 static const char *sppp_ipcp_opt_name(u_char opt);
 #endif
 #ifdef INET6
 static const char *sppp_ipv6cp_opt_name(u_char opt);
 #endif
 static const char *sppp_lcp_opt_name(u_char opt);
 static const char *sppp_phase_name(enum ppp_phase phase);
 static const char *sppp_proto_name(u_short proto);
 static const char *sppp_state_name(int state);
 static int sppp_params(struct sppp *sp, u_long cmd, void *data);
 static int sppp_strnlen(u_char *p, int max);
 static void sppp_keepalive(void *dummy);
 static void sppp_phase_network(struct sppp *sp);
 static void sppp_print_bytes(const u_char *p, u_short len);
 static void sppp_print_string(const char *p, u_short len);
 static void sppp_qflush(struct ifqueue *ifq);
 #ifdef INET
 static void sppp_set_ip_addr(struct sppp *sp, u_long src);
 #endif
 #ifdef INET6
 static void sppp_get_ip6_addrs(struct sppp *sp, struct in6_addr *src,
 			       struct in6_addr *dst, struct in6_addr *srcmask);
 #ifdef IPV6CP_MYIFID_DYN
 static void sppp_set_ip6_addr(struct sppp *sp, const struct in6_addr *src);
 static void sppp_gen_ip6_addr(struct sppp *sp, const struct in6_addr *src);
 #endif
 static void sppp_suggest_ip6_addr(struct sppp *sp, struct in6_addr *src);
 #endif
 
 /* if_start () wrapper */
 static void sppp_ifstart (struct ifnet *ifp);
 
 /* our control protocol descriptors */
 static const struct cp lcp = {
 	PPP_LCP, IDX_LCP, CP_LCP, "lcp",
 	sppp_lcp_up, sppp_lcp_down, sppp_lcp_open, sppp_lcp_close,
 	sppp_lcp_TO, sppp_lcp_RCR, sppp_lcp_RCN_rej, sppp_lcp_RCN_nak,
 	sppp_lcp_tlu, sppp_lcp_tld, sppp_lcp_tls, sppp_lcp_tlf,
 	sppp_lcp_scr
 };
 
 static const struct cp ipcp = {
 	PPP_IPCP, IDX_IPCP,
 #ifdef INET	/* don't run IPCP if there's no IPv4 support */
 	CP_NCP,
 #else
 	0,
 #endif
 	"ipcp",
 	sppp_ipcp_up, sppp_ipcp_down, sppp_ipcp_open, sppp_ipcp_close,
 	sppp_ipcp_TO, sppp_ipcp_RCR, sppp_ipcp_RCN_rej, sppp_ipcp_RCN_nak,
 	sppp_ipcp_tlu, sppp_ipcp_tld, sppp_ipcp_tls, sppp_ipcp_tlf,
 	sppp_ipcp_scr
 };
 
 static const struct cp ipv6cp = {
 	PPP_IPV6CP, IDX_IPV6CP,
 #ifdef INET6	/*don't run IPv6CP if there's no IPv6 support*/
 	CP_NCP,
 #else
 	0,
 #endif
 	"ipv6cp",
 	sppp_ipv6cp_up, sppp_ipv6cp_down, sppp_ipv6cp_open, sppp_ipv6cp_close,
 	sppp_ipv6cp_TO, sppp_ipv6cp_RCR, sppp_ipv6cp_RCN_rej, sppp_ipv6cp_RCN_nak,
 	sppp_ipv6cp_tlu, sppp_ipv6cp_tld, sppp_ipv6cp_tls, sppp_ipv6cp_tlf,
 	sppp_ipv6cp_scr
 };
 
 static const struct cp pap = {
 	PPP_PAP, IDX_PAP, CP_AUTH, "pap",
 	sppp_null, sppp_null, sppp_pap_open, sppp_pap_close,
 	sppp_pap_TO, 0, 0, 0,
 	sppp_pap_tlu, sppp_pap_tld, sppp_null, sppp_null,
 	sppp_pap_scr
 };
 
 static const struct cp chap = {
 	PPP_CHAP, IDX_CHAP, CP_AUTH, "chap",
 	sppp_null, sppp_null, sppp_chap_open, sppp_chap_close,
 	sppp_chap_TO, 0, 0, 0,
 	sppp_chap_tlu, sppp_chap_tld, sppp_null, sppp_null,
 	sppp_chap_scr
 };
 
 static const struct cp *cps[IDX_COUNT] = {
 	&lcp,			/* IDX_LCP */
 	&ipcp,			/* IDX_IPCP */
 	&ipv6cp,		/* IDX_IPV6CP */
 	&pap,			/* IDX_PAP */
 	&chap,			/* IDX_CHAP */
 };
 
 static void*
 sppp_alloc(u_char type, struct ifnet *ifp)
 {
 	struct sppp	*sp;
 
         sp = malloc(sizeof(struct sppp), M_SPPP, M_WAITOK | M_ZERO);
 	sp->pp_ifp = ifp;
 
 	return (sp);
 }
 
 static void
 sppp_free(void *com, u_char type)
 {
 
 	free(com, M_SPPP);
 }
 
 static int
 sppp_modevent(module_t mod, int type, void *unused)
 {
 	switch (type) {
 	case MOD_LOAD:
 		/*
 		 * XXX: should probably be IFT_SPPP, but it's fairly
 		 * harmless to allocate struct sppp's for non-sppp
 		 * interfaces.
 		 */
 
 		if_register_com_alloc(IFT_PPP, sppp_alloc, sppp_free);
 		break;
 	case MOD_UNLOAD:
 		/* if_deregister_com_alloc(IFT_PPP); */
 		return EACCES;
 	default:
 		return EOPNOTSUPP;
 	}
 	return 0;
 }
 static moduledata_t spppmod = {
 	"sppp",
 	sppp_modevent,
 	0
 };
 MODULE_VERSION(sppp, 1);
 DECLARE_MODULE(sppp, spppmod, SI_SUB_DRIVERS, SI_ORDER_ANY);
 
 /*
  * Exported functions, comprising our interface to the lower layer.
  */
 
 /*
  * Process the received packet.
  */
 void
 sppp_input(struct ifnet *ifp, struct mbuf *m)
 {
 	struct ppp_header *h;
 	int isr = -1;
 	struct sppp *sp = IFP2SP(ifp);
 	int debug, do_account = 0;
 #ifdef INET
 	int hlen, vjlen;
 	u_char *iphdr;
 #endif
 
 	SPPP_LOCK(sp);
 	debug = ifp->if_flags & IFF_DEBUG;
 
 	if (ifp->if_flags & IFF_UP)
 		/* Count received bytes, add FCS and one flag */
 		if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len + 3);
 
 	if (m->m_pkthdr.len <= PPP_HEADER_LEN) {
 		/* Too small packet, drop it. */
 		if (debug)
 			log(LOG_DEBUG,
 			    SPP_FMT "input packet is too small, %d bytes\n",
 			    SPP_ARGS(ifp), m->m_pkthdr.len);
 	  drop:
 		m_freem (m);
 		SPPP_UNLOCK(sp);
 	  drop2:
 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
 		if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
 		return;
 	}
 
 	if (sp->pp_mode == PP_FR) {
 		sppp_fr_input (sp, m);
 		SPPP_UNLOCK(sp);
 		return;
 	}
 
 	/* Get PPP header. */
 	h = mtod (m, struct ppp_header*);
 	m_adj (m, PPP_HEADER_LEN);
 
 	switch (h->address) {
 	case PPP_ALLSTATIONS:
 		if (h->control != PPP_UI)
 			goto invalid;
 		if (sp->pp_mode == IFF_CISCO) {
 			if (debug)
 				log(LOG_DEBUG,
 				    SPP_FMT "PPP packet in Cisco mode "
 				    "<addr=0x%x ctrl=0x%x proto=0x%x>\n",
 				    SPP_ARGS(ifp),
 				    h->address, h->control, ntohs(h->protocol));
 			goto drop;
 		}
 		switch (ntohs (h->protocol)) {
 		default:
 			if (debug)
 				log(LOG_DEBUG,
 				    SPP_FMT "rejecting protocol "
 				    "<addr=0x%x ctrl=0x%x proto=0x%x>\n",
 				    SPP_ARGS(ifp),
 				    h->address, h->control, ntohs(h->protocol));
 			if (sp->state[IDX_LCP] == STATE_OPENED)
 				sppp_cp_send (sp, PPP_LCP, PROTO_REJ,
 					++sp->pp_seq[IDX_LCP], m->m_pkthdr.len + 2,
 					&h->protocol);
 			if_inc_counter(ifp, IFCOUNTER_NOPROTO, 1);
 			goto drop;
 		case PPP_LCP:
 			sppp_cp_input(&lcp, sp, m);
 			m_freem (m);
 			SPPP_UNLOCK(sp);
 			return;
 		case PPP_PAP:
 			if (sp->pp_phase >= PHASE_AUTHENTICATE)
 				sppp_pap_input(sp, m);
 			m_freem (m);
 			SPPP_UNLOCK(sp);
 			return;
 		case PPP_CHAP:
 			if (sp->pp_phase >= PHASE_AUTHENTICATE)
 				sppp_chap_input(sp, m);
 			m_freem (m);
 			SPPP_UNLOCK(sp);
 			return;
 #ifdef INET
 		case PPP_IPCP:
 			if (sp->pp_phase == PHASE_NETWORK)
 				sppp_cp_input(&ipcp, sp, m);
 			m_freem (m);
 			SPPP_UNLOCK(sp);
 			return;
 		case PPP_IP:
 			if (sp->state[IDX_IPCP] == STATE_OPENED) {
 				isr = NETISR_IP;
 			}
 			do_account++;
 			break;
 		case PPP_VJ_COMP:
 			if (sp->state[IDX_IPCP] == STATE_OPENED) {
 				if ((vjlen =
 				     sl_uncompress_tcp_core(mtod(m, u_char *),
 							    m->m_len, m->m_len,
 							    TYPE_COMPRESSED_TCP,
 							    sp->pp_comp,
 							    &iphdr, &hlen)) <= 0) {
 					if (debug)
 						log(LOG_INFO,
 			    SPP_FMT "VJ uncompress failed on compressed packet\n",
 						    SPP_ARGS(ifp));
 					goto drop;
 				}
 
 				/*
 				 * Trim the VJ header off the packet, and prepend
 				 * the uncompressed IP header (which will usually
 				 * end up in two chained mbufs since there's not
 				 * enough leading space in the existing mbuf).
 				 */
 				m_adj(m, vjlen);
 				M_PREPEND(m, hlen, M_NOWAIT);
 				if (m == NULL) {
 					SPPP_UNLOCK(sp);
 					goto drop2;
 				}
 				bcopy(iphdr, mtod(m, u_char *), hlen);
 				isr = NETISR_IP;
 			}
 			do_account++;
 			break;
 		case PPP_VJ_UCOMP:
 			if (sp->state[IDX_IPCP] == STATE_OPENED) {
 				if (sl_uncompress_tcp_core(mtod(m, u_char *),
 							   m->m_len, m->m_len,
 							   TYPE_UNCOMPRESSED_TCP,
 							   sp->pp_comp,
 							   &iphdr, &hlen) != 0) {
 					if (debug)
 						log(LOG_INFO,
 			    SPP_FMT "VJ uncompress failed on uncompressed packet\n",
 						    SPP_ARGS(ifp));
 					goto drop;
 				}
 				isr = NETISR_IP;
 			}
 			do_account++;
 			break;
 #endif
 #ifdef INET6
 		case PPP_IPV6CP:
 			if (sp->pp_phase == PHASE_NETWORK)
 			    sppp_cp_input(&ipv6cp, sp, m);
 			m_freem (m);
 			SPPP_UNLOCK(sp);
 			return;
 
 		case PPP_IPV6:
 			if (sp->state[IDX_IPV6CP] == STATE_OPENED)
 				isr = NETISR_IPV6;
 			do_account++;
 			break;
 #endif
 		}
 		break;
 	case CISCO_MULTICAST:
 	case CISCO_UNICAST:
 		/* Don't check the control field here (RFC 1547). */
 		if (sp->pp_mode != IFF_CISCO) {
 			if (debug)
 				log(LOG_DEBUG,
 				    SPP_FMT "Cisco packet in PPP mode "
 				    "<addr=0x%x ctrl=0x%x proto=0x%x>\n",
 				    SPP_ARGS(ifp),
 				    h->address, h->control, ntohs(h->protocol));
 			goto drop;
 		}
 		switch (ntohs (h->protocol)) {
 		default:
 			if_inc_counter(ifp, IFCOUNTER_NOPROTO, 1);
 			goto invalid;
 		case CISCO_KEEPALIVE:
 			sppp_cisco_input (sp, m);
 			m_freem (m);
 			SPPP_UNLOCK(sp);
 			return;
 #ifdef INET
 		case ETHERTYPE_IP:
 			isr = NETISR_IP;
 			do_account++;
 			break;
 #endif
 #ifdef INET6
 		case ETHERTYPE_IPV6:
 			isr = NETISR_IPV6;
 			do_account++;
 			break;
 #endif
 		}
 		break;
 	default:        /* Invalid PPP packet. */
 	  invalid:
 		if (debug)
 			log(LOG_DEBUG,
 			    SPP_FMT "invalid input packet "
 			    "<addr=0x%x ctrl=0x%x proto=0x%x>\n",
 			    SPP_ARGS(ifp),
 			    h->address, h->control, ntohs(h->protocol));
 		goto drop;
 	}
 
 	if (! (ifp->if_flags & IFF_UP) || isr == -1)
 		goto drop;
 
 	SPPP_UNLOCK(sp);
 	M_SETFIB(m, ifp->if_fib);
 	/* Check queue. */
 	if (netisr_queue(isr, m)) {	/* (0) on success. */
 		if (debug)
 			log(LOG_DEBUG, SPP_FMT "protocol queue overflow\n",
 				SPP_ARGS(ifp));
 		goto drop2;
 	}
 
 	if (do_account)
 		/*
 		 * Do only account for network packets, not for control
 		 * packets.  This is used by some subsystems to detect
 		 * idle lines.
 		 */
 		sp->pp_last_recv = time_uptime;
 }
 
 static void
 sppp_ifstart_sched(void *dummy)
 {
 	struct sppp *sp = dummy;
 
 	sp->if_start(SP2IFP(sp));
 }
 
 /* if_start () wrapper function. We use it to schedule real if_start () for
  * execution. We can't call it directly
  */
 static void
 sppp_ifstart(struct ifnet *ifp)
 {
 	struct sppp *sp = IFP2SP(ifp);
 
 	if (SPPP_LOCK_OWNED(sp)) {
 		if (callout_pending(&sp->ifstart_callout))
 			return;
 		callout_reset(&sp->ifstart_callout, 1, sppp_ifstart_sched,
 		    (void *)sp); 
 	} else {
 		sp->if_start(ifp);
 	}
 }
 
 /*
  * Enqueue transmit packet.
  */
 static int
 sppp_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
 	struct route *ro)
 {
 	struct sppp *sp = IFP2SP(ifp);
 	struct ppp_header *h;
 	struct ifqueue *ifq = NULL;
 	int error, rv = 0;
 #ifdef INET
 	int ipproto = PPP_IP;
 #endif
 	int debug = ifp->if_flags & IFF_DEBUG;
 
 	SPPP_LOCK(sp);
 
 	if (!(ifp->if_flags & IFF_UP) ||
 	    (!(ifp->if_flags & IFF_AUTO) &&
 	    !(ifp->if_drv_flags & IFF_DRV_RUNNING))) {
 #ifdef INET6
 	  drop:
 #endif
 		m_freem (m);
 		SPPP_UNLOCK(sp);
 		return (ENETDOWN);
 	}
 
 	if ((ifp->if_flags & IFF_AUTO) &&
 	    !(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
 #ifdef INET6
 		/*
 		 * XXX
 		 *
 		 * Hack to prevent the initialization-time generated
 		 * IPv6 multicast packet to erroneously cause a
 		 * dialout event in case IPv6 has been
 		 * administratively disabled on that interface.
 		 */
 		if (dst->sa_family == AF_INET6 &&
 		    !(sp->confflags & CONF_ENABLE_IPV6))
 			goto drop;
 #endif
 		/*
 		 * Interface is not yet running, but auto-dial.  Need
 		 * to start LCP for it.
 		 */
 		ifp->if_drv_flags |= IFF_DRV_RUNNING;
 		lcp.Open(sp);
 	}
 
 #ifdef INET
 	if (dst->sa_family == AF_INET) {
 		/* XXX Check mbuf length here? */
 		struct ip *ip = mtod (m, struct ip*);
 		struct tcphdr *tcp = (struct tcphdr*) ((long*)ip + ip->ip_hl);
 
 		/*
 		 * When using dynamic local IP address assignment by using
 		 * 0.0.0.0 as a local address, the first TCP session will
 		 * not connect because the local TCP checksum is computed
 		 * using 0.0.0.0 which will later become our real IP address
 		 * so the TCP checksum computed at the remote end will
 		 * become invalid. So we
 		 * - don't let packets with src ip addr 0 thru
 		 * - we flag TCP packets with src ip 0 as an error
 		 */
 
 		if(ip->ip_src.s_addr == INADDR_ANY)	/* -hm */
 		{
 			m_freem(m);
 			SPPP_UNLOCK(sp);
 			if(ip->ip_p == IPPROTO_TCP)
 				return(EADDRNOTAVAIL);
 			else
 				return(0);
 		}
 
 		/*
 		 * Put low delay, telnet, rlogin and ftp control packets
 		 * in front of the queue or let ALTQ take care.
 		 */
 		if (ALTQ_IS_ENABLED(&ifp->if_snd))
 			;
 		else if (_IF_QFULL(&sp->pp_fastq))
 			;
 		else if (ip->ip_tos & IPTOS_LOWDELAY)
 			ifq = &sp->pp_fastq;
 		else if (m->m_len < sizeof *ip + sizeof *tcp)
 			;
 		else if (ip->ip_p != IPPROTO_TCP)
 			;
 		else if (INTERACTIVE (ntohs (tcp->th_sport)))
 			ifq = &sp->pp_fastq;
 		else if (INTERACTIVE (ntohs (tcp->th_dport)))
 			ifq = &sp->pp_fastq;
 
 		/*
 		 * Do IP Header compression
 		 */
 		if (sp->pp_mode != IFF_CISCO && sp->pp_mode != PP_FR &&
 		    (sp->ipcp.flags & IPCP_VJ) && ip->ip_p == IPPROTO_TCP)
 			switch (sl_compress_tcp(m, ip, sp->pp_comp,
 						sp->ipcp.compress_cid)) {
 			case TYPE_COMPRESSED_TCP:
 				ipproto = PPP_VJ_COMP;
 				break;
 			case TYPE_UNCOMPRESSED_TCP:
 				ipproto = PPP_VJ_UCOMP;
 				break;
 			case TYPE_IP:
 				ipproto = PPP_IP;
 				break;
 			default:
 				m_freem(m);
 				SPPP_UNLOCK(sp);
 				return (EINVAL);
 			}
 	}
 #endif
 
 #ifdef INET6
 	if (dst->sa_family == AF_INET6) {
 		/* XXX do something tricky here? */
 	}
 #endif
 
 	if (sp->pp_mode == PP_FR) {
 		/* Add frame relay header. */
 		m = sppp_fr_header (sp, m, dst->sa_family);
 		if (! m)
 			goto nobufs;
 		goto out;
 	}
 
 	/*
 	 * Prepend general data packet PPP header. For now, IP only.
 	 */
 	M_PREPEND (m, PPP_HEADER_LEN, M_NOWAIT);
 	if (! m) {
 nobufs:		if (debug)
 			log(LOG_DEBUG, SPP_FMT "no memory for transmit header\n",
 				SPP_ARGS(ifp));
 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
 		SPPP_UNLOCK(sp);
 		return (ENOBUFS);
 	}
 	/*
 	 * May want to check size of packet
 	 * (albeit due to the implementation it's always enough)
 	 */
 	h = mtod (m, struct ppp_header*);
 	if (sp->pp_mode == IFF_CISCO) {
 		h->address = CISCO_UNICAST;        /* unicast address */
 		h->control = 0;
 	} else {
 		h->address = PPP_ALLSTATIONS;        /* broadcast address */
 		h->control = PPP_UI;                 /* Unnumbered Info */
 	}
 
 	switch (dst->sa_family) {
 #ifdef INET
 	case AF_INET:   /* Internet Protocol */
 		if (sp->pp_mode == IFF_CISCO)
 			h->protocol = htons (ETHERTYPE_IP);
 		else {
 			/*
 			 * Don't choke with an ENETDOWN early.  It's
 			 * possible that we just started dialing out,
 			 * so don't drop the packet immediately.  If
 			 * we notice that we run out of buffer space
 			 * below, we will however remember that we are
 			 * not ready to carry IP packets, and return
 			 * ENETDOWN, as opposed to ENOBUFS.
 			 */
 			h->protocol = htons(ipproto);
 			if (sp->state[IDX_IPCP] != STATE_OPENED)
 				rv = ENETDOWN;
 		}
 		break;
 #endif
 #ifdef INET6
 	case AF_INET6:   /* Internet Protocol */
 		if (sp->pp_mode == IFF_CISCO)
 			h->protocol = htons (ETHERTYPE_IPV6);
 		else {
 			/*
 			 * Don't choke with an ENETDOWN early.  It's
 			 * possible that we just started dialing out,
 			 * so don't drop the packet immediately.  If
 			 * we notice that we run out of buffer space
 			 * below, we will however remember that we are
 			 * not ready to carry IP packets, and return
 			 * ENETDOWN, as opposed to ENOBUFS.
 			 */
 			h->protocol = htons(PPP_IPV6);
 			if (sp->state[IDX_IPV6CP] != STATE_OPENED)
 				rv = ENETDOWN;
 		}
 		break;
 #endif
 	default:
 		m_freem (m);
 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
 		SPPP_UNLOCK(sp);
 		return (EAFNOSUPPORT);
 	}
 
 	/*
 	 * Queue message on interface, and start output if interface
 	 * not yet active.
 	 */
 out:
 	if (ifq != NULL)
 		error = !(IF_HANDOFF_ADJ(ifq, m, ifp, 3));
 	else
 		IFQ_HANDOFF_ADJ(ifp, m, 3, error);
 	if (error) {
 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
 		SPPP_UNLOCK(sp);
 		return (rv? rv: ENOBUFS);
 	}
 	SPPP_UNLOCK(sp);
 	/*
 	 * Unlike in sppp_input(), we can always bump the timestamp
 	 * here since sppp_output() is only called on behalf of
 	 * network-layer traffic; control-layer traffic is handled
 	 * by sppp_cp_send().
 	 */
 	sp->pp_last_sent = time_uptime;
 	return (0);
 }
 
 void
 sppp_attach(struct ifnet *ifp)
 {
 	struct sppp *sp = IFP2SP(ifp);
 
 	/* Initialize mtx lock */
 	mtx_init(&sp->mtx, "sppp", MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE);
 
 	/* Initialize keepalive handler. */
  	callout_init(&sp->keepalive_callout, 1);
 	callout_reset(&sp->keepalive_callout, hz * 10, sppp_keepalive,
  		    (void *)sp); 
 
 	ifp->if_mtu = PP_MTU;
 	ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST;
 	ifp->if_output = sppp_output;
 #if 0
 	sp->pp_flags = PP_KEEPALIVE;
 #endif
  	ifp->if_snd.ifq_maxlen = 32;
  	sp->pp_fastq.ifq_maxlen = 32;
  	sp->pp_cpq.ifq_maxlen = 20;
 	sp->pp_loopcnt = 0;
 	sp->pp_alivecnt = 0;
 	bzero(&sp->pp_seq[0], sizeof(sp->pp_seq));
 	bzero(&sp->pp_rseq[0], sizeof(sp->pp_rseq));
 	sp->pp_phase = PHASE_DEAD;
 	sp->pp_up = sppp_pp_up;
 	sp->pp_down = sppp_pp_down;
 	if(!mtx_initialized(&sp->pp_cpq.ifq_mtx))
 		mtx_init(&sp->pp_cpq.ifq_mtx, "sppp_cpq", NULL, MTX_DEF);
 	if(!mtx_initialized(&sp->pp_fastq.ifq_mtx))
 		mtx_init(&sp->pp_fastq.ifq_mtx, "sppp_fastq", NULL, MTX_DEF);
 	sp->pp_last_recv = sp->pp_last_sent = time_uptime;
 	sp->confflags = 0;
 #ifdef INET
 	sp->confflags |= CONF_ENABLE_VJ;
 #endif
 #ifdef INET6
 	sp->confflags |= CONF_ENABLE_IPV6;
 #endif
  	callout_init(&sp->ifstart_callout, 1);
 	sp->if_start = ifp->if_start;
 	ifp->if_start = sppp_ifstart;
 	sp->pp_comp = malloc(sizeof(struct slcompress), M_TEMP, M_WAITOK);
 	sl_compress_init(sp->pp_comp, -1);
 	sppp_lcp_init(sp);
 	sppp_ipcp_init(sp);
 	sppp_ipv6cp_init(sp);
 	sppp_pap_init(sp);
 	sppp_chap_init(sp);
 }
 
 void
 sppp_detach(struct ifnet *ifp)
 {
 	struct sppp *sp = IFP2SP(ifp);
 	int i;
 
 	KASSERT(mtx_initialized(&sp->mtx), ("sppp mutex is not initialized"));
 
 	/* Stop keepalive handler. */
  	callout_drain(&sp->keepalive_callout);
 
 	for (i = 0; i < IDX_COUNT; i++) {
 		callout_drain(&sp->ch[i]);
 	}
 	callout_drain(&sp->pap_my_to_ch);
 
 	mtx_destroy(&sp->pp_cpq.ifq_mtx);
 	mtx_destroy(&sp->pp_fastq.ifq_mtx);
 	mtx_destroy(&sp->mtx);
 }
 
 /*
  * Flush the interface output queue.
  */
 static void
 sppp_flush_unlocked(struct ifnet *ifp)
 {
 	struct sppp *sp = IFP2SP(ifp);
 
 	sppp_qflush ((struct ifqueue *)&SP2IFP(sp)->if_snd);
 	sppp_qflush (&sp->pp_fastq);
 	sppp_qflush (&sp->pp_cpq);
 }
 
 void
 sppp_flush(struct ifnet *ifp)
 {
 	struct sppp *sp = IFP2SP(ifp);
 
 	SPPP_LOCK(sp);
 	sppp_flush_unlocked (ifp);
 	SPPP_UNLOCK(sp);
 }
 
 /*
  * Check if the output queue is empty.
  */
 int
 sppp_isempty(struct ifnet *ifp)
 {
 	struct sppp *sp = IFP2SP(ifp);
 	int empty;
 
 	SPPP_LOCK(sp);
 	empty = !sp->pp_fastq.ifq_head && !sp->pp_cpq.ifq_head &&
 		!SP2IFP(sp)->if_snd.ifq_head;
 	SPPP_UNLOCK(sp);
 	return (empty);
 }
 
 /*
  * Get next packet to send.
  */
 struct mbuf *
 sppp_dequeue(struct ifnet *ifp)
 {
 	struct sppp *sp = IFP2SP(ifp);
 	struct mbuf *m;
 
 	SPPP_LOCK(sp);
 	/*
 	 * Process only the control protocol queue until we have at
 	 * least one NCP open.
 	 *
 	 * Do always serve all three queues in Cisco mode.
 	 */
 	IF_DEQUEUE(&sp->pp_cpq, m);
 	if (m == NULL &&
 	    (sppp_ncp_check(sp) || sp->pp_mode == IFF_CISCO ||
 	     sp->pp_mode == PP_FR)) {
 		IF_DEQUEUE(&sp->pp_fastq, m);
 		if (m == NULL)
 			IF_DEQUEUE (&SP2IFP(sp)->if_snd, m);
 	}
 	SPPP_UNLOCK(sp);
 	return m;
 }
 
 /*
  * Pick the next packet, do not remove it from the queue.
  */
 struct mbuf *
 sppp_pick(struct ifnet *ifp)
 {
 	struct sppp *sp = IFP2SP(ifp);
 	struct mbuf *m;
 
 	SPPP_LOCK(sp);
 
 	m = sp->pp_cpq.ifq_head;
 	if (m == NULL &&
 	    (sp->pp_phase == PHASE_NETWORK ||
 	     sp->pp_mode == IFF_CISCO ||
 	     sp->pp_mode == PP_FR))
 		if ((m = sp->pp_fastq.ifq_head) == NULL)
 			m = SP2IFP(sp)->if_snd.ifq_head;
 	SPPP_UNLOCK(sp);
 	return (m);
 }
 
 /*
  * Process an ioctl request.  Called on low priority level.
  */
 int
 sppp_ioctl(struct ifnet *ifp, IOCTL_CMD_T cmd, void *data)
 {
 	struct ifreq *ifr = (struct ifreq*) data;
 	struct sppp *sp = IFP2SP(ifp);
 	int rv, going_up, going_down, newmode;
 
 	SPPP_LOCK(sp);
 	rv = 0;
 	switch (cmd) {
 	case SIOCAIFADDR:
 		break;
 
 	case SIOCSIFADDR:
 		/* set the interface "up" when assigning an IP address */
 		ifp->if_flags |= IFF_UP;
 		/* FALLTHROUGH */
 
 	case SIOCSIFFLAGS:
 		going_up = ifp->if_flags & IFF_UP &&
 			(ifp->if_drv_flags & IFF_DRV_RUNNING) == 0;
 		going_down = (ifp->if_flags & IFF_UP) == 0 &&
 			ifp->if_drv_flags & IFF_DRV_RUNNING;
 
 		newmode = ifp->if_flags & IFF_PASSIVE;
 		if (!newmode)
 			newmode = ifp->if_flags & IFF_AUTO;
 		if (!newmode)
 			newmode = ifp->if_flags & IFF_CISCO;
 		ifp->if_flags &= ~(IFF_PASSIVE | IFF_AUTO | IFF_CISCO);
 		ifp->if_flags |= newmode;
 
 		if (!newmode)
 			newmode = sp->pp_flags & PP_FR;
 
 		if (newmode != sp->pp_mode) {
 			going_down = 1;
 			if (!going_up)
 				going_up = ifp->if_drv_flags & IFF_DRV_RUNNING;
 		}
 
 		if (going_down) {
 			if (sp->pp_mode != IFF_CISCO &&
 			    sp->pp_mode != PP_FR)
 				lcp.Close(sp);
 			else if (sp->pp_tlf)
 				(sp->pp_tlf)(sp);
 			sppp_flush_unlocked(ifp);
 			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
 			sp->pp_mode = newmode;
 		}
 
 		if (going_up) {
 			if (sp->pp_mode != IFF_CISCO &&
 			    sp->pp_mode != PP_FR)
 				lcp.Close(sp);
 			sp->pp_mode = newmode;
 			if (sp->pp_mode == 0) {
 				ifp->if_drv_flags |= IFF_DRV_RUNNING;
 				lcp.Open(sp);
 			}
 			if ((sp->pp_mode == IFF_CISCO) ||
 			    (sp->pp_mode == PP_FR)) {
 				if (sp->pp_tls)
 					(sp->pp_tls)(sp);
 				ifp->if_drv_flags |= IFF_DRV_RUNNING;
 			}
 		}
 
 		break;
 
 #ifdef SIOCSIFMTU
 #ifndef ifr_mtu
 #define ifr_mtu ifr_metric
 #endif
 	case SIOCSIFMTU:
 		if (ifr->ifr_mtu < 128 || ifr->ifr_mtu > sp->lcp.their_mru)
 			return (EINVAL);
 		ifp->if_mtu = ifr->ifr_mtu;
 		break;
 #endif
 #ifdef SLIOCSETMTU
 	case SLIOCSETMTU:
 		if (*(short*)data < 128 || *(short*)data > sp->lcp.their_mru)
 			return (EINVAL);
 		ifp->if_mtu = *(short*)data;
 		break;
 #endif
 #ifdef SIOCGIFMTU
 	case SIOCGIFMTU:
 		ifr->ifr_mtu = ifp->if_mtu;
 		break;
 #endif
 #ifdef SLIOCGETMTU
 	case SLIOCGETMTU:
 		*(short*)data = ifp->if_mtu;
 		break;
 #endif
 	case SIOCADDMULTI:
 	case SIOCDELMULTI:
 		break;
 
 	case SIOCGIFGENERIC:
 	case SIOCSIFGENERIC:
 		rv = sppp_params(sp, cmd, data);
 		break;
 
 	default:
 		rv = ENOTTY;
 	}
 	SPPP_UNLOCK(sp);
 	return rv;
 }
 
 /*
  * Cisco framing implementation.
  */
 
 /*
  * Handle incoming Cisco keepalive protocol packets.
  */
 static void
 sppp_cisco_input(struct sppp *sp, struct mbuf *m)
 {
 	STDDCL;
 	struct cisco_packet *h;
 	u_long me, mymask;
 
 	if (m->m_pkthdr.len < CISCO_PACKET_LEN) {
 		if (debug)
 			log(LOG_DEBUG,
 			    SPP_FMT "cisco invalid packet length: %d bytes\n",
 			    SPP_ARGS(ifp), m->m_pkthdr.len);
 		return;
 	}
 	h = mtod (m, struct cisco_packet*);
 	if (debug)
 		log(LOG_DEBUG,
 		    SPP_FMT "cisco input: %d bytes "
 		    "<0x%lx 0x%lx 0x%lx 0x%x 0x%x-0x%x>\n",
 		    SPP_ARGS(ifp), m->m_pkthdr.len,
 		    (u_long)ntohl (h->type), (u_long)h->par1, (u_long)h->par2, (u_int)h->rel,
 		    (u_int)h->time0, (u_int)h->time1);
 	switch (ntohl (h->type)) {
 	default:
 		if (debug)
 			log(-1, SPP_FMT "cisco unknown packet type: 0x%lx\n",
 			       SPP_ARGS(ifp), (u_long)ntohl (h->type));
 		break;
 	case CISCO_ADDR_REPLY:
 		/* Reply on address request, ignore */
 		break;
 	case CISCO_KEEPALIVE_REQ:
 		sp->pp_alivecnt = 0;
 		sp->pp_rseq[IDX_LCP] = ntohl (h->par1);
 		if (sp->pp_seq[IDX_LCP] == sp->pp_rseq[IDX_LCP]) {
 			/* Local and remote sequence numbers are equal.
 			 * Probably, the line is in loopback mode. */
 			if (sp->pp_loopcnt >= MAXALIVECNT) {
 				printf (SPP_FMT "loopback\n",
 					SPP_ARGS(ifp));
 				sp->pp_loopcnt = 0;
 				if (ifp->if_flags & IFF_UP) {
 					if_down (ifp);
 					sppp_qflush (&sp->pp_cpq);
 				}
 			}
 			++sp->pp_loopcnt;
 
 			/* Generate new local sequence number */
 			sp->pp_seq[IDX_LCP] = random();
 			break;
 		}
 		sp->pp_loopcnt = 0;
 		if (! (ifp->if_flags & IFF_UP) &&
 		    (ifp->if_drv_flags & IFF_DRV_RUNNING)) {
 			if_up(ifp);
 			printf (SPP_FMT "up\n", SPP_ARGS(ifp));
 		}
 		break;
 	case CISCO_ADDR_REQ:
 		sppp_get_ip_addrs(sp, &me, 0, &mymask);
 		if (me != 0L)
 			sppp_cisco_send(sp, CISCO_ADDR_REPLY, me, mymask);
 		break;
 	}
 }
 
 /*
  * Send Cisco keepalive packet.
  */
 static void
 sppp_cisco_send(struct sppp *sp, int type, long par1, long par2)
 {
 	STDDCL;
 	struct ppp_header *h;
 	struct cisco_packet *ch;
 	struct mbuf *m;
 	struct timeval tv;
 
 	getmicrouptime(&tv);
 
 	MGETHDR (m, M_NOWAIT, MT_DATA);
 	if (! m)
 		return;
 	m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + CISCO_PACKET_LEN;
 	m->m_pkthdr.rcvif = 0;
 
 	h = mtod (m, struct ppp_header*);
 	h->address = CISCO_MULTICAST;
 	h->control = 0;
 	h->protocol = htons (CISCO_KEEPALIVE);
 
 	ch = (struct cisco_packet*) (h + 1);
 	ch->type = htonl (type);
 	ch->par1 = htonl (par1);
 	ch->par2 = htonl (par2);
 	ch->rel = -1;
 
 	ch->time0 = htons ((u_short) (tv.tv_sec >> 16));
 	ch->time1 = htons ((u_short) tv.tv_sec);
 
 	if (debug)
 		log(LOG_DEBUG,
 		    SPP_FMT "cisco output: <0x%lx 0x%lx 0x%lx 0x%x 0x%x-0x%x>\n",
 			SPP_ARGS(ifp), (u_long)ntohl (ch->type), (u_long)ch->par1,
 			(u_long)ch->par2, (u_int)ch->rel, (u_int)ch->time0, (u_int)ch->time1);
 
 	if (! IF_HANDOFF_ADJ(&sp->pp_cpq, m, ifp, 3))
 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
 }
 
 /*
  * PPP protocol implementation.
  */
 
 /*
  * Send PPP control protocol packet.
  */
 static void
 sppp_cp_send(struct sppp *sp, u_short proto, u_char type,
 	     u_char ident, u_short len, void *data)
 {
 	STDDCL;
 	struct ppp_header *h;
 	struct lcp_header *lh;
 	struct mbuf *m;
 
 	if (len > MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN)
 		len = MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN;
 	MGETHDR (m, M_NOWAIT, MT_DATA);
 	if (! m)
 		return;
 	m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + LCP_HEADER_LEN + len;
 	m->m_pkthdr.rcvif = 0;
 
 	h = mtod (m, struct ppp_header*);
 	h->address = PPP_ALLSTATIONS;        /* broadcast address */
 	h->control = PPP_UI;                 /* Unnumbered Info */
 	h->protocol = htons (proto);         /* Link Control Protocol */
 
 	lh = (struct lcp_header*) (h + 1);
 	lh->type = type;
 	lh->ident = ident;
 	lh->len = htons (LCP_HEADER_LEN + len);
 	if (len)
 		bcopy (data, lh+1, len);
 
 	if (debug) {
 		log(LOG_DEBUG, SPP_FMT "%s output <%s id=0x%x len=%d",
 		    SPP_ARGS(ifp),
 		    sppp_proto_name(proto),
 		    sppp_cp_type_name (lh->type), lh->ident,
 		    ntohs (lh->len));
 		sppp_print_bytes ((u_char*) (lh+1), len);
 		log(-1, ">\n");
 	}
 	if (! IF_HANDOFF_ADJ(&sp->pp_cpq, m, ifp, 3))
 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
 }
 
 /*
  * Handle incoming PPP control protocol packets.
  */
 static void
 sppp_cp_input(const struct cp *cp, struct sppp *sp, struct mbuf *m)
 {
 	STDDCL;
 	struct lcp_header *h;
 	int len = m->m_pkthdr.len;
 	int rv;
 	u_char *p;
 
 	if (len < 4) {
 		if (debug)
 			log(LOG_DEBUG,
 			    SPP_FMT "%s invalid packet length: %d bytes\n",
 			    SPP_ARGS(ifp), cp->name, len);
 		return;
 	}
 	h = mtod (m, struct lcp_header*);
 	if (debug) {
 		log(LOG_DEBUG,
 		    SPP_FMT "%s input(%s): <%s id=0x%x len=%d",
 		    SPP_ARGS(ifp), cp->name,
 		    sppp_state_name(sp->state[cp->protoidx]),
 		    sppp_cp_type_name (h->type), h->ident, ntohs (h->len));
 		sppp_print_bytes ((u_char*) (h+1), len-4);
 		log(-1, ">\n");
 	}
 	if (len > ntohs (h->len))
 		len = ntohs (h->len);
 	p = (u_char *)(h + 1);
 	switch (h->type) {
 	case CONF_REQ:
 		if (len < 4) {
 			if (debug)
 				log(-1, SPP_FMT "%s invalid conf-req length %d\n",
 				       SPP_ARGS(ifp), cp->name,
 				       len);
 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
 			break;
 		}
 		/* handle states where RCR doesn't get a SCA/SCN */
 		switch (sp->state[cp->protoidx]) {
 		case STATE_CLOSING:
 		case STATE_STOPPING:
 			return;
 		case STATE_CLOSED:
 			sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident,
 				     0, 0);
 			return;
 		}
 		rv = (cp->RCR)(sp, h, len);
 		switch (sp->state[cp->protoidx]) {
 		case STATE_OPENED:
 			(cp->tld)(sp);
 			(cp->scr)(sp);
 			/* FALLTHROUGH */
 		case STATE_ACK_SENT:
 		case STATE_REQ_SENT:
 			/*
 			 * sppp_cp_change_state() have the side effect of
 			 * restarting the timeouts. We want to avoid that
 			 * if the state don't change, otherwise we won't
 			 * ever timeout and resend a configuration request
 			 * that got lost.
 			 */
 			if (sp->state[cp->protoidx] == (rv ? STATE_ACK_SENT:
 			    STATE_REQ_SENT))
 				break;
 			sppp_cp_change_state(cp, sp, rv?
 					     STATE_ACK_SENT: STATE_REQ_SENT);
 			break;
 		case STATE_STOPPED:
 			sp->rst_counter[cp->protoidx] = sp->lcp.max_configure;
 			(cp->scr)(sp);
 			sppp_cp_change_state(cp, sp, rv?
 					     STATE_ACK_SENT: STATE_REQ_SENT);
 			break;
 		case STATE_ACK_RCVD:
 			if (rv) {
 				sppp_cp_change_state(cp, sp, STATE_OPENED);
 				if (debug)
 					log(LOG_DEBUG, SPP_FMT "%s tlu\n",
 					    SPP_ARGS(ifp),
 					    cp->name);
 				(cp->tlu)(sp);
 			} else
 				sppp_cp_change_state(cp, sp, STATE_ACK_RCVD);
 			break;
 		default:
 			printf(SPP_FMT "%s illegal %s in state %s\n",
 			       SPP_ARGS(ifp), cp->name,
 			       sppp_cp_type_name(h->type),
 			       sppp_state_name(sp->state[cp->protoidx]));
 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
 		}
 		break;
 	case CONF_ACK:
 		if (h->ident != sp->confid[cp->protoidx]) {
 			if (debug)
 				log(-1, SPP_FMT "%s id mismatch 0x%x != 0x%x\n",
 				       SPP_ARGS(ifp), cp->name,
 				       h->ident, sp->confid[cp->protoidx]);
 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
 			break;
 		}
 		switch (sp->state[cp->protoidx]) {
 		case STATE_CLOSED:
 		case STATE_STOPPED:
 			sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0);
 			break;
 		case STATE_CLOSING:
 		case STATE_STOPPING:
 			break;
 		case STATE_REQ_SENT:
 			sp->rst_counter[cp->protoidx] = sp->lcp.max_configure;
 			sppp_cp_change_state(cp, sp, STATE_ACK_RCVD);
 			break;
 		case STATE_OPENED:
 			(cp->tld)(sp);
 			/* FALLTHROUGH */
 		case STATE_ACK_RCVD:
 			(cp->scr)(sp);
 			sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
 			break;
 		case STATE_ACK_SENT:
 			sp->rst_counter[cp->protoidx] = sp->lcp.max_configure;
 			sppp_cp_change_state(cp, sp, STATE_OPENED);
 			if (debug)
 				log(LOG_DEBUG, SPP_FMT "%s tlu\n",
 				       SPP_ARGS(ifp), cp->name);
 			(cp->tlu)(sp);
 			break;
 		default:
 			printf(SPP_FMT "%s illegal %s in state %s\n",
 			       SPP_ARGS(ifp), cp->name,
 			       sppp_cp_type_name(h->type),
 			       sppp_state_name(sp->state[cp->protoidx]));
 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
 		}
 		break;
 	case CONF_NAK:
 	case CONF_REJ:
 		if (h->ident != sp->confid[cp->protoidx]) {
 			if (debug)
 				log(-1, SPP_FMT "%s id mismatch 0x%x != 0x%x\n",
 				       SPP_ARGS(ifp), cp->name,
 				       h->ident, sp->confid[cp->protoidx]);
 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
 			break;
 		}
 		if (h->type == CONF_NAK)
 			(cp->RCN_nak)(sp, h, len);
 		else /* CONF_REJ */
 			(cp->RCN_rej)(sp, h, len);
 
 		switch (sp->state[cp->protoidx]) {
 		case STATE_CLOSED:
 		case STATE_STOPPED:
 			sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0);
 			break;
 		case STATE_REQ_SENT:
 		case STATE_ACK_SENT:
 			sp->rst_counter[cp->protoidx] = sp->lcp.max_configure;
 			/*
 			 * Slow things down a bit if we think we might be
 			 * in loopback. Depend on the timeout to send the
 			 * next configuration request.
 			 */
 			if (sp->pp_loopcnt)
 				break;
 			(cp->scr)(sp);
 			break;
 		case STATE_OPENED:
 			(cp->tld)(sp);
 			/* FALLTHROUGH */
 		case STATE_ACK_RCVD:
 			sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
 			(cp->scr)(sp);
 			break;
 		case STATE_CLOSING:
 		case STATE_STOPPING:
 			break;
 		default:
 			printf(SPP_FMT "%s illegal %s in state %s\n",
 			       SPP_ARGS(ifp), cp->name,
 			       sppp_cp_type_name(h->type),
 			       sppp_state_name(sp->state[cp->protoidx]));
 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
 		}
 		break;
 
 	case TERM_REQ:
 		switch (sp->state[cp->protoidx]) {
 		case STATE_ACK_RCVD:
 		case STATE_ACK_SENT:
 			sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
 			/* FALLTHROUGH */
 		case STATE_CLOSED:
 		case STATE_STOPPED:
 		case STATE_CLOSING:
 		case STATE_STOPPING:
 		case STATE_REQ_SENT:
 		  sta:
 			/* Send Terminate-Ack packet. */
 			if (debug)
 				log(LOG_DEBUG, SPP_FMT "%s send terminate-ack\n",
 				    SPP_ARGS(ifp), cp->name);
 			sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0);
 			break;
 		case STATE_OPENED:
 			(cp->tld)(sp);
 			sp->rst_counter[cp->protoidx] = 0;
 			sppp_cp_change_state(cp, sp, STATE_STOPPING);
 			goto sta;
 			break;
 		default:
 			printf(SPP_FMT "%s illegal %s in state %s\n",
 			       SPP_ARGS(ifp), cp->name,
 			       sppp_cp_type_name(h->type),
 			       sppp_state_name(sp->state[cp->protoidx]));
 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
 		}
 		break;
 	case TERM_ACK:
 		switch (sp->state[cp->protoidx]) {
 		case STATE_CLOSED:
 		case STATE_STOPPED:
 		case STATE_REQ_SENT:
 		case STATE_ACK_SENT:
 			break;
 		case STATE_CLOSING:
 			sppp_cp_change_state(cp, sp, STATE_CLOSED);
 			(cp->tlf)(sp);
 			break;
 		case STATE_STOPPING:
 			sppp_cp_change_state(cp, sp, STATE_STOPPED);
 			(cp->tlf)(sp);
 			break;
 		case STATE_ACK_RCVD:
 			sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
 			break;
 		case STATE_OPENED:
 			(cp->tld)(sp);
 			(cp->scr)(sp);
 			sppp_cp_change_state(cp, sp, STATE_ACK_RCVD);
 			break;
 		default:
 			printf(SPP_FMT "%s illegal %s in state %s\n",
 			       SPP_ARGS(ifp), cp->name,
 			       sppp_cp_type_name(h->type),
 			       sppp_state_name(sp->state[cp->protoidx]));
 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
 		}
 		break;
 	case CODE_REJ:
 		/* XXX catastrophic rejects (RXJ-) aren't handled yet. */
 		log(LOG_INFO,
 		    SPP_FMT "%s: ignoring RXJ (%s) for proto 0x%x, "
 		    "danger will robinson\n",
 		    SPP_ARGS(ifp), cp->name,
 		    sppp_cp_type_name(h->type), ntohs(*((u_short *)p)));
 		switch (sp->state[cp->protoidx]) {
 		case STATE_CLOSED:
 		case STATE_STOPPED:
 		case STATE_REQ_SENT:
 		case STATE_ACK_SENT:
 		case STATE_CLOSING:
 		case STATE_STOPPING:
 		case STATE_OPENED:
 			break;
 		case STATE_ACK_RCVD:
 			sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
 			break;
 		default:
 			printf(SPP_FMT "%s illegal %s in state %s\n",
 			       SPP_ARGS(ifp), cp->name,
 			       sppp_cp_type_name(h->type),
 			       sppp_state_name(sp->state[cp->protoidx]));
 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
 		}
 		break;
 	case PROTO_REJ:
 	    {
 		int catastrophic;
 		const struct cp *upper;
 		int i;
 		u_int16_t proto;
 
 		catastrophic = 0;
 		upper = NULL;
 		proto = ntohs(*((u_int16_t *)p));
 		for (i = 0; i < IDX_COUNT; i++) {
 			if (cps[i]->proto == proto) {
 				upper = cps[i];
 				break;
 			}
 		}
 		if (upper == NULL)
 			catastrophic++;
 
 		if (catastrophic || debug)
 			log(catastrophic? LOG_INFO: LOG_DEBUG,
 			    SPP_FMT "%s: RXJ%c (%s) for proto 0x%x (%s/%s)\n",
 			    SPP_ARGS(ifp), cp->name, catastrophic ? '-' : '+',
 			    sppp_cp_type_name(h->type), proto,
 			    upper ? upper->name : "unknown",
 			    upper ? sppp_state_name(sp->state[upper->protoidx]) : "?");
 
 		/*
 		 * if we got RXJ+ against conf-req, the peer does not implement
 		 * this particular protocol type.  terminate the protocol.
 		 */
 		if (upper && !catastrophic) {
 			if (sp->state[upper->protoidx] == STATE_REQ_SENT) {
 				upper->Close(sp);
 				break;
 			}
 		}
 
 		/* XXX catastrophic rejects (RXJ-) aren't handled yet. */
 		switch (sp->state[cp->protoidx]) {
 		case STATE_CLOSED:
 		case STATE_STOPPED:
 		case STATE_REQ_SENT:
 		case STATE_ACK_SENT:
 		case STATE_CLOSING:
 		case STATE_STOPPING:
 		case STATE_OPENED:
 			break;
 		case STATE_ACK_RCVD:
 			sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
 			break;
 		default:
 			printf(SPP_FMT "%s illegal %s in state %s\n",
 			       SPP_ARGS(ifp), cp->name,
 			       sppp_cp_type_name(h->type),
 			       sppp_state_name(sp->state[cp->protoidx]));
 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
 		}
 		break;
 	    }
 	case DISC_REQ:
 		if (cp->proto != PPP_LCP)
 			goto illegal;
 		/* Discard the packet. */
 		break;
 	case ECHO_REQ:
 		if (cp->proto != PPP_LCP)
 			goto illegal;
 		if (sp->state[cp->protoidx] != STATE_OPENED) {
 			if (debug)
 				log(-1, SPP_FMT "lcp echo req but lcp closed\n",
 				       SPP_ARGS(ifp));
 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
 			break;
 		}
 		if (len < 8) {
 			if (debug)
 				log(-1, SPP_FMT "invalid lcp echo request "
 				       "packet length: %d bytes\n",
 				       SPP_ARGS(ifp), len);
 			break;
 		}
 		if ((sp->lcp.opts & (1 << LCP_OPT_MAGIC)) &&
 		    ntohl (*(long*)(h+1)) == sp->lcp.magic) {
 			/* Line loopback mode detected. */
 			printf(SPP_FMT "loopback\n", SPP_ARGS(ifp));
 			sp->pp_loopcnt = MAXALIVECNT * 5;
 			if_down (ifp);
 			sppp_qflush (&sp->pp_cpq);
 
 			/* Shut down the PPP link. */
 			/* XXX */
 			lcp.Down(sp);
 			lcp.Up(sp);
 			break;
 		}
 		*(long*)(h+1) = htonl (sp->lcp.magic);
 		if (debug)
 			log(-1, SPP_FMT "got lcp echo req, sending echo rep\n",
 			       SPP_ARGS(ifp));
 		sppp_cp_send (sp, PPP_LCP, ECHO_REPLY, h->ident, len-4, h+1);
 		break;
 	case ECHO_REPLY:
 		if (cp->proto != PPP_LCP)
 			goto illegal;
 		if (h->ident != sp->lcp.echoid) {
 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
 			break;
 		}
 		if (len < 8) {
 			if (debug)
 				log(-1, SPP_FMT "lcp invalid echo reply "
 				       "packet length: %d bytes\n",
 				       SPP_ARGS(ifp), len);
 			break;
 		}
 		if (debug)
 			log(-1, SPP_FMT "lcp got echo rep\n",
 			       SPP_ARGS(ifp));
 		if (!(sp->lcp.opts & (1 << LCP_OPT_MAGIC)) ||
 		    ntohl (*(long*)(h+1)) != sp->lcp.magic)
 			sp->pp_alivecnt = 0;
 		break;
 	default:
 		/* Unknown packet type -- send Code-Reject packet. */
 	  illegal:
 		if (debug)
 			log(-1, SPP_FMT "%s send code-rej for 0x%x\n",
 			       SPP_ARGS(ifp), cp->name, h->type);
 		sppp_cp_send(sp, cp->proto, CODE_REJ,
 			     ++sp->pp_seq[cp->protoidx], m->m_pkthdr.len, h);
 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
 	}
 }
 
 /*
  * The generic part of all Up/Down/Open/Close/TO event handlers.
  * Basically, the state transition handling in the automaton.
  */
 static void
 sppp_up_event(const struct cp *cp, struct sppp *sp)
 {
 	STDDCL;
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "%s up(%s)\n",
 		    SPP_ARGS(ifp), cp->name,
 		    sppp_state_name(sp->state[cp->protoidx]));
 
 	switch (sp->state[cp->protoidx]) {
 	case STATE_INITIAL:
 		sppp_cp_change_state(cp, sp, STATE_CLOSED);
 		break;
 	case STATE_STARTING:
 		sp->rst_counter[cp->protoidx] = sp->lcp.max_configure;
 		(cp->scr)(sp);
 		sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
 		break;
 	default:
 		printf(SPP_FMT "%s illegal up in state %s\n",
 		       SPP_ARGS(ifp), cp->name,
 		       sppp_state_name(sp->state[cp->protoidx]));
 	}
 }
 
 static void
 sppp_down_event(const struct cp *cp, struct sppp *sp)
 {
 	STDDCL;
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "%s down(%s)\n",
 		    SPP_ARGS(ifp), cp->name,
 		    sppp_state_name(sp->state[cp->protoidx]));
 
 	switch (sp->state[cp->protoidx]) {
 	case STATE_CLOSED:
 	case STATE_CLOSING:
 		sppp_cp_change_state(cp, sp, STATE_INITIAL);
 		break;
 	case STATE_STOPPED:
 		sppp_cp_change_state(cp, sp, STATE_STARTING);
 		(cp->tls)(sp);
 		break;
 	case STATE_STOPPING:
 	case STATE_REQ_SENT:
 	case STATE_ACK_RCVD:
 	case STATE_ACK_SENT:
 		sppp_cp_change_state(cp, sp, STATE_STARTING);
 		break;
 	case STATE_OPENED:
 		(cp->tld)(sp);
 		sppp_cp_change_state(cp, sp, STATE_STARTING);
 		break;
 	default:
 		printf(SPP_FMT "%s illegal down in state %s\n",
 		       SPP_ARGS(ifp), cp->name,
 		       sppp_state_name(sp->state[cp->protoidx]));
 	}
 }
 
 static void
 sppp_open_event(const struct cp *cp, struct sppp *sp)
 {
 	STDDCL;
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "%s open(%s)\n",
 		    SPP_ARGS(ifp), cp->name,
 		    sppp_state_name(sp->state[cp->protoidx]));
 
 	switch (sp->state[cp->protoidx]) {
 	case STATE_INITIAL:
 		sppp_cp_change_state(cp, sp, STATE_STARTING);
 		(cp->tls)(sp);
 		break;
 	case STATE_STARTING:
 		break;
 	case STATE_CLOSED:
 		sp->rst_counter[cp->protoidx] = sp->lcp.max_configure;
 		(cp->scr)(sp);
 		sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
 		break;
 	case STATE_STOPPED:
 		/*
 		 * Try escaping stopped state.  This seems to bite
 		 * people occasionally, in particular for IPCP,
 		 * presumably following previous IPCP negotiation
 		 * aborts.  Somehow, we must have missed a Down event
 		 * which would have caused a transition into starting
 		 * state, so as a bandaid we force the Down event now.
 		 * This effectively implements (something like the)
 		 * `restart' option mentioned in the state transition
 		 * table of RFC 1661.
 		 */
 		sppp_cp_change_state(cp, sp, STATE_STARTING);
 		(cp->tls)(sp);
 		break;
 	case STATE_STOPPING:
 	case STATE_REQ_SENT:
 	case STATE_ACK_RCVD:
 	case STATE_ACK_SENT:
 	case STATE_OPENED:
 		break;
 	case STATE_CLOSING:
 		sppp_cp_change_state(cp, sp, STATE_STOPPING);
 		break;
 	}
 }
 
 static void
 sppp_close_event(const struct cp *cp, struct sppp *sp)
 {
 	STDDCL;
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "%s close(%s)\n",
 		    SPP_ARGS(ifp), cp->name,
 		    sppp_state_name(sp->state[cp->protoidx]));
 
 	switch (sp->state[cp->protoidx]) {
 	case STATE_INITIAL:
 	case STATE_CLOSED:
 	case STATE_CLOSING:
 		break;
 	case STATE_STARTING:
 		sppp_cp_change_state(cp, sp, STATE_INITIAL);
 		(cp->tlf)(sp);
 		break;
 	case STATE_STOPPED:
 		sppp_cp_change_state(cp, sp, STATE_CLOSED);
 		break;
 	case STATE_STOPPING:
 		sppp_cp_change_state(cp, sp, STATE_CLOSING);
 		break;
 	case STATE_OPENED:
 		(cp->tld)(sp);
 		/* FALLTHROUGH */
 	case STATE_REQ_SENT:
 	case STATE_ACK_RCVD:
 	case STATE_ACK_SENT:
 		sp->rst_counter[cp->protoidx] = sp->lcp.max_terminate;
 		sppp_cp_send(sp, cp->proto, TERM_REQ,
 			     ++sp->pp_seq[cp->protoidx], 0, 0);
 		sppp_cp_change_state(cp, sp, STATE_CLOSING);
 		break;
 	}
 }
 
 static void
 sppp_to_event(const struct cp *cp, struct sppp *sp)
 {
 	STDDCL;
 
 	SPPP_LOCK(sp);
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "%s TO(%s) rst_counter = %d\n",
 		    SPP_ARGS(ifp), cp->name,
 		    sppp_state_name(sp->state[cp->protoidx]),
 		    sp->rst_counter[cp->protoidx]);
 
 	if (--sp->rst_counter[cp->protoidx] < 0)
 		/* TO- event */
 		switch (sp->state[cp->protoidx]) {
 		case STATE_CLOSING:
 			sppp_cp_change_state(cp, sp, STATE_CLOSED);
 			(cp->tlf)(sp);
 			break;
 		case STATE_STOPPING:
 			sppp_cp_change_state(cp, sp, STATE_STOPPED);
 			(cp->tlf)(sp);
 			break;
 		case STATE_REQ_SENT:
 		case STATE_ACK_RCVD:
 		case STATE_ACK_SENT:
 			sppp_cp_change_state(cp, sp, STATE_STOPPED);
 			(cp->tlf)(sp);
 			break;
 		}
 	else
 		/* TO+ event */
 		switch (sp->state[cp->protoidx]) {
 		case STATE_CLOSING:
 		case STATE_STOPPING:
 			sppp_cp_send(sp, cp->proto, TERM_REQ,
 				     ++sp->pp_seq[cp->protoidx], 0, 0);
 			callout_reset(&sp->ch[cp->protoidx], sp->lcp.timeout,
 				      cp->TO, (void *)sp);
 			break;
 		case STATE_REQ_SENT:
 		case STATE_ACK_RCVD:
 			(cp->scr)(sp);
 			/* sppp_cp_change_state() will restart the timer */
 			sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
 			break;
 		case STATE_ACK_SENT:
 			(cp->scr)(sp);
 			callout_reset(&sp->ch[cp->protoidx], sp->lcp.timeout,
 				      cp->TO, (void *)sp);
 			break;
 		}
 
 	SPPP_UNLOCK(sp);
 }
 
 /*
  * Change the state of a control protocol in the state automaton.
  * Takes care of starting/stopping the restart timer.
  */
 static void
 sppp_cp_change_state(const struct cp *cp, struct sppp *sp, int newstate)
 {
 	sp->state[cp->protoidx] = newstate;
 
 	callout_stop (&sp->ch[cp->protoidx]);
 
 	switch (newstate) {
 	case STATE_INITIAL:
 	case STATE_STARTING:
 	case STATE_CLOSED:
 	case STATE_STOPPED:
 	case STATE_OPENED:
 		break;
 	case STATE_CLOSING:
 	case STATE_STOPPING:
 	case STATE_REQ_SENT:
 	case STATE_ACK_RCVD:
 	case STATE_ACK_SENT:
 		callout_reset(&sp->ch[cp->protoidx], sp->lcp.timeout,
 			      cp->TO, (void *)sp);
 		break;
 	}
 }
 
 /*
  *--------------------------------------------------------------------------*
  *                                                                          *
  *                         The LCP implementation.                          *
  *                                                                          *
  *--------------------------------------------------------------------------*
  */
 static void
 sppp_pp_up(struct sppp *sp)
 {
 	SPPP_LOCK(sp);
 	lcp.Up(sp);
 	SPPP_UNLOCK(sp);
 }
 
 static void
 sppp_pp_down(struct sppp *sp)
 {
 	SPPP_LOCK(sp);
 	lcp.Down(sp);
 	SPPP_UNLOCK(sp);
 }
 
 static void
 sppp_lcp_init(struct sppp *sp)
 {
 	sp->lcp.opts = (1 << LCP_OPT_MAGIC);
 	sp->lcp.magic = 0;
 	sp->state[IDX_LCP] = STATE_INITIAL;
 	sp->fail_counter[IDX_LCP] = 0;
 	sp->pp_seq[IDX_LCP] = 0;
 	sp->pp_rseq[IDX_LCP] = 0;
 	sp->lcp.protos = 0;
 	sp->lcp.mru = sp->lcp.their_mru = PP_MTU;
 
 	/* Note that these values are  relevant for all control protocols */
 	sp->lcp.timeout = 3 * hz;
 	sp->lcp.max_terminate = 2;
 	sp->lcp.max_configure = 10;
 	sp->lcp.max_failure = 10;
  	callout_init(&sp->ch[IDX_LCP], 1);
 }
 
 static void
 sppp_lcp_up(struct sppp *sp)
 {
 	STDDCL;
 
 	sp->pp_alivecnt = 0;
 	sp->lcp.opts = (1 << LCP_OPT_MAGIC);
 	sp->lcp.magic = 0;
 	sp->lcp.protos = 0;
 	sp->lcp.mru = sp->lcp.their_mru = PP_MTU;
 	/*
 	 * If we are authenticator, negotiate LCP_AUTH
 	 */
 	if (sp->hisauth.proto != 0)
 		sp->lcp.opts |= (1 << LCP_OPT_AUTH_PROTO);
 	else
 		sp->lcp.opts &= ~(1 << LCP_OPT_AUTH_PROTO);
 	sp->pp_flags &= ~PP_NEEDAUTH;
 	/*
 	 * If this interface is passive or dial-on-demand, and we are
 	 * still in Initial state, it means we've got an incoming
 	 * call.  Activate the interface.
 	 */
 	if ((ifp->if_flags & (IFF_AUTO | IFF_PASSIVE)) != 0) {
 		if (debug)
 			log(LOG_DEBUG,
 			    SPP_FMT "Up event", SPP_ARGS(ifp));
 		ifp->if_drv_flags |= IFF_DRV_RUNNING;
 		if (sp->state[IDX_LCP] == STATE_INITIAL) {
 			if (debug)
 				log(-1, "(incoming call)\n");
 			sp->pp_flags |= PP_CALLIN;
 			lcp.Open(sp);
 		} else if (debug)
 			log(-1, "\n");
 	} else if ((ifp->if_flags & (IFF_AUTO | IFF_PASSIVE)) == 0 &&
 		   (sp->state[IDX_LCP] == STATE_INITIAL)) {
 		ifp->if_drv_flags |= IFF_DRV_RUNNING;
 		lcp.Open(sp);
 	}
 
 	sppp_up_event(&lcp, sp);
 }
 
 static void
 sppp_lcp_down(struct sppp *sp)
 {
 	STDDCL;
 
 	sppp_down_event(&lcp, sp);
 
 	/*
 	 * If this is neither a dial-on-demand nor a passive
 	 * interface, simulate an ``ifconfig down'' action, so the
 	 * administrator can force a redial by another ``ifconfig
 	 * up''.  XXX For leased line operation, should we immediately
 	 * try to reopen the connection here?
 	 */
 	if ((ifp->if_flags & (IFF_AUTO | IFF_PASSIVE)) == 0) {
 		log(LOG_INFO,
 		    SPP_FMT "Down event, taking interface down.\n",
 		    SPP_ARGS(ifp));
 		if_down(ifp);
 	} else {
 		if (debug)
 			log(LOG_DEBUG,
 			    SPP_FMT "Down event (carrier loss)\n",
 			    SPP_ARGS(ifp));
 		sp->pp_flags &= ~PP_CALLIN;
 		if (sp->state[IDX_LCP] != STATE_INITIAL)
 			lcp.Close(sp);
 		ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
 	}
 }
 
 static void
 sppp_lcp_open(struct sppp *sp)
 {
 	sppp_open_event(&lcp, sp);
 }
 
 static void
 sppp_lcp_close(struct sppp *sp)
 {
 	sppp_close_event(&lcp, sp);
 }
 
 static void
 sppp_lcp_TO(void *cookie)
 {
 	sppp_to_event(&lcp, (struct sppp *)cookie);
 }
 
 /*
  * Analyze a configure request.  Return true if it was agreeable, and
  * caused action sca, false if it has been rejected or nak'ed, and
  * caused action scn.  (The return value is used to make the state
  * transition decision in the state automaton.)
  */
 static int
 sppp_lcp_RCR(struct sppp *sp, struct lcp_header *h, int len)
 {
 	STDDCL;
 	u_char *buf, *r, *p;
 	int origlen, rlen;
 	u_long nmagic;
 	u_short authproto;
 
 	len -= 4;
 	origlen = len;
 	buf = r = malloc (len, M_TEMP, M_NOWAIT);
 	if (! buf)
 		return (0);
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "lcp parse opts: ",
 		    SPP_ARGS(ifp));
 
 	/* pass 1: check for things that need to be rejected */
 	p = (void*) (h+1);
 	for (rlen=0; len >= 2 && p[1] >= 2 && len >= p[1];
 	    len-=p[1], p+=p[1]) {
 		if (debug)
 			log(-1, " %s ", sppp_lcp_opt_name(*p));
 		switch (*p) {
 		case LCP_OPT_MAGIC:
 			/* Magic number. */
 			if (len >= 6 && p[1] == 6)
 				continue;
 			if (debug)
 				log(-1, "[invalid] ");
 			break;
 		case LCP_OPT_ASYNC_MAP:
 			/* Async control character map. */
 			if (len >= 6 && p[1] == 6)
 				continue;
 			if (debug)
 				log(-1, "[invalid] ");
 			break;
 		case LCP_OPT_MRU:
 			/* Maximum receive unit. */
 			if (len >= 4 && p[1] == 4)
 				continue;
 			if (debug)
 				log(-1, "[invalid] ");
 			break;
 		case LCP_OPT_AUTH_PROTO:
 			if (len < 4) {
 				if (debug)
 					log(-1, "[invalid] ");
 				break;
 			}
 			authproto = (p[2] << 8) + p[3];
 			if (authproto == PPP_CHAP && p[1] != 5) {
 				if (debug)
 					log(-1, "[invalid chap len] ");
 				break;
 			}
 			if (sp->myauth.proto == 0) {
 				/* we are not configured to do auth */
 				if (debug)
 					log(-1, "[not configured] ");
 				break;
 			}
 			/*
 			 * Remote want us to authenticate, remember this,
 			 * so we stay in PHASE_AUTHENTICATE after LCP got
 			 * up.
 			 */
 			sp->pp_flags |= PP_NEEDAUTH;
 			continue;
 		default:
 			/* Others not supported. */
 			if (debug)
 				log(-1, "[rej] ");
 			break;
 		}
 		/* Add the option to rejected list. */
 		bcopy (p, r, p[1]);
 		r += p[1];
 		rlen += p[1];
 	}
 	if (rlen) {
 		if (debug)
 			log(-1, " send conf-rej\n");
 		sppp_cp_send (sp, PPP_LCP, CONF_REJ, h->ident, rlen, buf);
 		return 0;
 	} else if (debug)
 		log(-1, "\n");
 
 	/*
 	 * pass 2: check for option values that are unacceptable and
 	 * thus require to be nak'ed.
 	 */
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "lcp parse opt values: ",
 		    SPP_ARGS(ifp));
 
 	p = (void*) (h+1);
 	len = origlen;
 	for (rlen=0; len >= 2 && p[1] >= 2 && len >= p[1];
 	    len-=p[1], p+=p[1]) {
 		if (debug)
 			log(-1, " %s ", sppp_lcp_opt_name(*p));
 		switch (*p) {
 		case LCP_OPT_MAGIC:
 			/* Magic number -- extract. */
 			nmagic = (u_long)p[2] << 24 |
 				(u_long)p[3] << 16 | p[4] << 8 | p[5];
 			if (nmagic != sp->lcp.magic) {
 				sp->pp_loopcnt = 0;
 				if (debug)
 					log(-1, "0x%lx ", nmagic);
 				continue;
 			}
 			if (debug && sp->pp_loopcnt < MAXALIVECNT*5)
 				log(-1, "[glitch] ");
 			++sp->pp_loopcnt;
 			/*
 			 * We negate our magic here, and NAK it.  If
 			 * we see it later in an NAK packet, we
 			 * suggest a new one.
 			 */
 			nmagic = ~sp->lcp.magic;
 			/* Gonna NAK it. */
 			p[2] = nmagic >> 24;
 			p[3] = nmagic >> 16;
 			p[4] = nmagic >> 8;
 			p[5] = nmagic;
 			break;
 
 		case LCP_OPT_ASYNC_MAP:
 			/*
 			 * Async control character map -- just ignore it.
 			 *
 			 * Quote from RFC 1662, chapter 6:
 			 * To enable this functionality, synchronous PPP
 			 * implementations MUST always respond to the
 			 * Async-Control-Character-Map Configuration
 			 * Option with the LCP Configure-Ack.  However,
 			 * acceptance of the Configuration Option does
 			 * not imply that the synchronous implementation
 			 * will do any ACCM mapping.  Instead, all such
 			 * octet mapping will be performed by the
 			 * asynchronous-to-synchronous converter.
 			 */
 			continue;
 
 		case LCP_OPT_MRU:
 			/*
 			 * Maximum receive unit.  Always agreeable,
 			 * but ignored by now.
 			 */
 			sp->lcp.their_mru = p[2] * 256 + p[3];
 			if (debug)
 				log(-1, "%lu ", sp->lcp.their_mru);
 			continue;
 
 		case LCP_OPT_AUTH_PROTO:
 			authproto = (p[2] << 8) + p[3];
 			if (sp->myauth.proto != authproto) {
 				/* not agreed, nak */
 				if (debug)
 					log(-1, "[mine %s != his %s] ",
 					       sppp_proto_name(sp->hisauth.proto),
 					       sppp_proto_name(authproto));
 				p[2] = sp->myauth.proto >> 8;
 				p[3] = sp->myauth.proto;
 				break;
 			}
 			if (authproto == PPP_CHAP && p[4] != CHAP_MD5) {
 				if (debug)
 					log(-1, "[chap not MD5] ");
 				p[4] = CHAP_MD5;
 				break;
 			}
 			continue;
 		}
 		/* Add the option to nak'ed list. */
 		bcopy (p, r, p[1]);
 		r += p[1];
 		rlen += p[1];
 	}
 	if (rlen) {
 		/*
 		 * Local and remote magics equal -- loopback?
 		 */
 		if (sp->pp_loopcnt >= MAXALIVECNT*5) {
 			if (sp->pp_loopcnt == MAXALIVECNT*5)
 				printf (SPP_FMT "loopback\n",
 					SPP_ARGS(ifp));
 			if (ifp->if_flags & IFF_UP) {
 				if_down(ifp);
 				sppp_qflush(&sp->pp_cpq);
 				/* XXX ? */
 				lcp.Down(sp);
 				lcp.Up(sp);
 			}
 		} else if (!sp->pp_loopcnt &&
 			   ++sp->fail_counter[IDX_LCP] >= sp->lcp.max_failure) {
 			if (debug)
 				log(-1, " max_failure (%d) exceeded, "
 				       "send conf-rej\n",
 				       sp->lcp.max_failure);
 			sppp_cp_send(sp, PPP_LCP, CONF_REJ, h->ident, rlen, buf);
 		} else {
 			if (debug)
 				log(-1, " send conf-nak\n");
 			sppp_cp_send (sp, PPP_LCP, CONF_NAK, h->ident, rlen, buf);
 		}
 	} else {
 		if (debug)
 			log(-1, " send conf-ack\n");
 		sp->fail_counter[IDX_LCP] = 0;
 		sp->pp_loopcnt = 0;
 		sppp_cp_send (sp, PPP_LCP, CONF_ACK,
 			      h->ident, origlen, h+1);
 	}
 
 	free (buf, M_TEMP);
 	return (rlen == 0);
 }
 
 /*
  * Analyze the LCP Configure-Reject option list, and adjust our
  * negotiation.
  */
 static void
 sppp_lcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len)
 {
 	STDDCL;
 	u_char *buf, *p;
 
 	len -= 4;
 	buf = malloc (len, M_TEMP, M_NOWAIT);
 	if (!buf)
 		return;
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "lcp rej opts: ",
 		    SPP_ARGS(ifp));
 
 	p = (void*) (h+1);
 	for (; len >= 2 && p[1] >= 2 && len >= p[1];
 	    len -= p[1], p += p[1]) {
 		if (debug)
 			log(-1, " %s ", sppp_lcp_opt_name(*p));
 		switch (*p) {
 		case LCP_OPT_MAGIC:
 			/* Magic number -- can't use it, use 0 */
 			sp->lcp.opts &= ~(1 << LCP_OPT_MAGIC);
 			sp->lcp.magic = 0;
 			break;
 		case LCP_OPT_MRU:
 			/*
 			 * Should not be rejected anyway, since we only
 			 * negotiate a MRU if explicitly requested by
 			 * peer.
 			 */
 			sp->lcp.opts &= ~(1 << LCP_OPT_MRU);
 			break;
 		case LCP_OPT_AUTH_PROTO:
 			/*
 			 * Peer doesn't want to authenticate himself,
 			 * deny unless this is a dialout call, and
 			 * AUTHFLAG_NOCALLOUT is set.
 			 */
 			if ((sp->pp_flags & PP_CALLIN) == 0 &&
 			    (sp->hisauth.flags & AUTHFLAG_NOCALLOUT) != 0) {
 				if (debug)
 					log(-1, "[don't insist on auth "
 					       "for callout]");
 				sp->lcp.opts &= ~(1 << LCP_OPT_AUTH_PROTO);
 				break;
 			}
 			if (debug)
 				log(-1, "[access denied]\n");
 			lcp.Close(sp);
 			break;
 		}
 	}
 	if (debug)
 		log(-1, "\n");
 	free (buf, M_TEMP);
 	return;
 }
 
 /*
  * Analyze the LCP Configure-NAK option list, and adjust our
  * negotiation.
  */
 static void
 sppp_lcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len)
 {
 	STDDCL;
 	u_char *buf, *p;
 	u_long magic;
 
 	len -= 4;
 	buf = malloc (len, M_TEMP, M_NOWAIT);
 	if (!buf)
 		return;
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "lcp nak opts: ",
 		    SPP_ARGS(ifp));
 
 	p = (void*) (h+1);
 	for (; len >= 2 && p[1] >= 2 && len >= p[1];
 	    len -= p[1], p += p[1]) {
 		if (debug)
 			log(-1, " %s ", sppp_lcp_opt_name(*p));
 		switch (*p) {
 		case LCP_OPT_MAGIC:
 			/* Magic number -- renegotiate */
 			if ((sp->lcp.opts & (1 << LCP_OPT_MAGIC)) &&
 			    len >= 6 && p[1] == 6) {
 				magic = (u_long)p[2] << 24 |
 					(u_long)p[3] << 16 | p[4] << 8 | p[5];
 				/*
 				 * If the remote magic is our negated one,
 				 * this looks like a loopback problem.
 				 * Suggest a new magic to make sure.
 				 */
 				if (magic == ~sp->lcp.magic) {
 					if (debug)
 						log(-1, "magic glitch ");
 					sp->lcp.magic = random();
 				} else {
 					sp->lcp.magic = magic;
 					if (debug)
 						log(-1, "%lu ", magic);
 				}
 			}
 			break;
 		case LCP_OPT_MRU:
 			/*
 			 * Peer wants to advise us to negotiate an MRU.
 			 * Agree on it if it's reasonable, or use
 			 * default otherwise.
 			 */
 			if (len >= 4 && p[1] == 4) {
 				u_int mru = p[2] * 256 + p[3];
 				if (debug)
 					log(-1, "%d ", mru);
 				if (mru < PP_MTU || mru > PP_MAX_MRU)
 					mru = PP_MTU;
 				sp->lcp.mru = mru;
 				sp->lcp.opts |= (1 << LCP_OPT_MRU);
 			}
 			break;
 		case LCP_OPT_AUTH_PROTO:
 			/*
 			 * Peer doesn't like our authentication method,
 			 * deny.
 			 */
 			if (debug)
 				log(-1, "[access denied]\n");
 			lcp.Close(sp);
 			break;
 		}
 	}
 	if (debug)
 		log(-1, "\n");
 	free (buf, M_TEMP);
 	return;
 }
 
 static void
 sppp_lcp_tlu(struct sppp *sp)
 {
 	STDDCL;
 	int i;
 	u_long mask;
 
 	/* XXX ? */
 	if (! (ifp->if_flags & IFF_UP) &&
 	    (ifp->if_drv_flags & IFF_DRV_RUNNING)) {
 		/* Coming out of loopback mode. */
 		if_up(ifp);
 		printf (SPP_FMT "up\n", SPP_ARGS(ifp));
 	}
 
 	for (i = 0; i < IDX_COUNT; i++)
 		if ((cps[i])->flags & CP_QUAL)
 			(cps[i])->Open(sp);
 
 	if ((sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0 ||
 	    (sp->pp_flags & PP_NEEDAUTH) != 0)
 		sp->pp_phase = PHASE_AUTHENTICATE;
 	else
 		sp->pp_phase = PHASE_NETWORK;
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp),
 		    sppp_phase_name(sp->pp_phase));
 
 	/*
 	 * Open all authentication protocols.  This is even required
 	 * if we already proceeded to network phase, since it might be
 	 * that remote wants us to authenticate, so we might have to
 	 * send a PAP request.  Undesired authentication protocols
 	 * don't do anything when they get an Open event.
 	 */
 	for (i = 0; i < IDX_COUNT; i++)
 		if ((cps[i])->flags & CP_AUTH)
 			(cps[i])->Open(sp);
 
 	if (sp->pp_phase == PHASE_NETWORK) {
 		/* Notify all NCPs. */
 		for (i = 0; i < IDX_COUNT; i++)
 			if (((cps[i])->flags & CP_NCP) &&
 			    /*
 			     * XXX
 			     * Hack to administratively disable IPv6 if
 			     * not desired.  Perhaps we should have another
 			     * flag for this, but right now, we can make
 			     * all struct cp's read/only.
 			     */
 			    (cps[i] != &ipv6cp ||
 			     (sp->confflags & CONF_ENABLE_IPV6)))
 				(cps[i])->Open(sp);
 	}
 
 	/* Send Up events to all started protos. */
 	for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1)
 		if ((sp->lcp.protos & mask) && ((cps[i])->flags & CP_LCP) == 0)
 			(cps[i])->Up(sp);
 
 	/* notify low-level driver of state change */
 	if (sp->pp_chg)
 		sp->pp_chg(sp, (int)sp->pp_phase);
 
 	if (sp->pp_phase == PHASE_NETWORK)
 		/* if no NCP is starting, close down */
 		sppp_lcp_check_and_close(sp);
 }
 
 static void
 sppp_lcp_tld(struct sppp *sp)
 {
 	STDDCL;
 	int i;
 	u_long mask;
 
 	sp->pp_phase = PHASE_TERMINATE;
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp),
 		    sppp_phase_name(sp->pp_phase));
 
 	/*
 	 * Take upper layers down.  We send the Down event first and
 	 * the Close second to prevent the upper layers from sending
 	 * ``a flurry of terminate-request packets'', as the RFC
 	 * describes it.
 	 */
 	for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1)
 		if ((sp->lcp.protos & mask) && ((cps[i])->flags & CP_LCP) == 0) {
 			(cps[i])->Down(sp);
 			(cps[i])->Close(sp);
 		}
 }
 
 static void
 sppp_lcp_tls(struct sppp *sp)
 {
 	STDDCL;
 
 	sp->pp_phase = PHASE_ESTABLISH;
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp),
 		    sppp_phase_name(sp->pp_phase));
 
 	/* Notify lower layer if desired. */
 	if (sp->pp_tls)
 		(sp->pp_tls)(sp);
 	else
 		(sp->pp_up)(sp);
 }
 
 static void
 sppp_lcp_tlf(struct sppp *sp)
 {
 	STDDCL;
 
 	sp->pp_phase = PHASE_DEAD;
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp),
 		    sppp_phase_name(sp->pp_phase));
 
 	/* Notify lower layer if desired. */
 	if (sp->pp_tlf)
 		(sp->pp_tlf)(sp);
 	else
 		(sp->pp_down)(sp);
 }
 
 static void
 sppp_lcp_scr(struct sppp *sp)
 {
 	char opt[6 /* magicnum */ + 4 /* mru */ + 5 /* chap */];
 	int i = 0;
 	u_short authproto;
 
 	if (sp->lcp.opts & (1 << LCP_OPT_MAGIC)) {
 		if (! sp->lcp.magic)
 			sp->lcp.magic = random();
 		opt[i++] = LCP_OPT_MAGIC;
 		opt[i++] = 6;
 		opt[i++] = sp->lcp.magic >> 24;
 		opt[i++] = sp->lcp.magic >> 16;
 		opt[i++] = sp->lcp.magic >> 8;
 		opt[i++] = sp->lcp.magic;
 	}
 
 	if (sp->lcp.opts & (1 << LCP_OPT_MRU)) {
 		opt[i++] = LCP_OPT_MRU;
 		opt[i++] = 4;
 		opt[i++] = sp->lcp.mru >> 8;
 		opt[i++] = sp->lcp.mru;
 	}
 
 	if (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) {
 		authproto = sp->hisauth.proto;
 		opt[i++] = LCP_OPT_AUTH_PROTO;
 		opt[i++] = authproto == PPP_CHAP? 5: 4;
 		opt[i++] = authproto >> 8;
 		opt[i++] = authproto;
 		if (authproto == PPP_CHAP)
 			opt[i++] = CHAP_MD5;
 	}
 
 	sp->confid[IDX_LCP] = ++sp->pp_seq[IDX_LCP];
 	sppp_cp_send (sp, PPP_LCP, CONF_REQ, sp->confid[IDX_LCP], i, &opt);
 }
 
 /*
  * Check the open NCPs, return true if at least one NCP is open.
  */
 static int
 sppp_ncp_check(struct sppp *sp)
 {
 	int i, mask;
 
 	for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1)
 		if ((sp->lcp.protos & mask) && (cps[i])->flags & CP_NCP)
 			return 1;
 	return 0;
 }
 
 /*
  * Re-check the open NCPs and see if we should terminate the link.
  * Called by the NCPs during their tlf action handling.
  */
 static void
 sppp_lcp_check_and_close(struct sppp *sp)
 {
 
 	if (sp->pp_phase < PHASE_NETWORK)
 		/* don't bother, we are already going down */
 		return;
 
 	if (sppp_ncp_check(sp))
 		return;
 
 	lcp.Close(sp);
 }
 
 /*
  *--------------------------------------------------------------------------*
  *                                                                          *
  *                        The IPCP implementation.                          *
  *                                                                          *
  *--------------------------------------------------------------------------*
  */
 
 #ifdef INET
 static void
 sppp_ipcp_init(struct sppp *sp)
 {
 	sp->ipcp.opts = 0;
 	sp->ipcp.flags = 0;
 	sp->state[IDX_IPCP] = STATE_INITIAL;
 	sp->fail_counter[IDX_IPCP] = 0;
 	sp->pp_seq[IDX_IPCP] = 0;
 	sp->pp_rseq[IDX_IPCP] = 0;
  	callout_init(&sp->ch[IDX_IPCP], 1);
 }
 
 static void
 sppp_ipcp_up(struct sppp *sp)
 {
 	sppp_up_event(&ipcp, sp);
 }
 
 static void
 sppp_ipcp_down(struct sppp *sp)
 {
 	sppp_down_event(&ipcp, sp);
 }
 
 static void
 sppp_ipcp_open(struct sppp *sp)
 {
 	STDDCL;
 	u_long myaddr, hisaddr;
 
 	sp->ipcp.flags &= ~(IPCP_HISADDR_SEEN | IPCP_MYADDR_SEEN |
 			    IPCP_MYADDR_DYN | IPCP_VJ);
 	sp->ipcp.opts = 0;
 
 	sppp_get_ip_addrs(sp, &myaddr, &hisaddr, 0);
 	/*
 	 * If we don't have his address, this probably means our
 	 * interface doesn't want to talk IP at all.  (This could
 	 * be the case if somebody wants to speak only IPX, for
 	 * example.)  Don't open IPCP in this case.
 	 */
 	if (hisaddr == 0L) {
 		/* XXX this message should go away */
 		if (debug)
 			log(LOG_DEBUG, SPP_FMT "ipcp_open(): no IP interface\n",
 			    SPP_ARGS(ifp));
 		return;
 	}
 	if (myaddr == 0L) {
 		/*
 		 * I don't have an assigned address, so i need to
 		 * negotiate my address.
 		 */
 		sp->ipcp.flags |= IPCP_MYADDR_DYN;
 		sp->ipcp.opts |= (1 << IPCP_OPT_ADDRESS);
 	} else
 		sp->ipcp.flags |= IPCP_MYADDR_SEEN;
 	if (sp->confflags & CONF_ENABLE_VJ) {
 		sp->ipcp.opts |= (1 << IPCP_OPT_COMPRESSION);
 		sp->ipcp.max_state = MAX_STATES - 1;
 		sp->ipcp.compress_cid = 1;
 	}
 	sppp_open_event(&ipcp, sp);
 }
 
 static void
 sppp_ipcp_close(struct sppp *sp)
 {
 	sppp_close_event(&ipcp, sp);
 	if (sp->ipcp.flags & IPCP_MYADDR_DYN)
 		/*
 		 * My address was dynamic, clear it again.
 		 */
 		sppp_set_ip_addr(sp, 0L);
 }
 
 static void
 sppp_ipcp_TO(void *cookie)
 {
 	sppp_to_event(&ipcp, (struct sppp *)cookie);
 }
 
 /*
  * Analyze a configure request.  Return true if it was agreeable, and
  * caused action sca, false if it has been rejected or nak'ed, and
  * caused action scn.  (The return value is used to make the state
  * transition decision in the state automaton.)
  */
 static int
 sppp_ipcp_RCR(struct sppp *sp, struct lcp_header *h, int len)
 {
 	u_char *buf, *r, *p;
 	struct ifnet *ifp = SP2IFP(sp);
 	int rlen, origlen, debug = ifp->if_flags & IFF_DEBUG;
 	u_long hisaddr, desiredaddr;
 	int gotmyaddr = 0;
 	int desiredcomp;
 
 	len -= 4;
 	origlen = len;
 	/*
 	 * Make sure to allocate a buf that can at least hold a
 	 * conf-nak with an `address' option.  We might need it below.
 	 */
 	buf = r = malloc ((len < 6? 6: len), M_TEMP, M_NOWAIT);
 	if (! buf)
 		return (0);
 
 	/* pass 1: see if we can recognize them */
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "ipcp parse opts: ",
 		    SPP_ARGS(ifp));
 	p = (void*) (h+1);
 	for (rlen=0; len >= 2 && p[1] >= 2 && len >= p[1];
 	    len-=p[1], p+=p[1]) {
 		if (debug)
 			log(-1, " %s ", sppp_ipcp_opt_name(*p));
 		switch (*p) {
 		case IPCP_OPT_COMPRESSION:
 			if (!(sp->confflags & CONF_ENABLE_VJ)) {
 				/* VJ compression administratively disabled */
 				if (debug)
 					log(-1, "[locally disabled] ");
 				break;
 			}
 			/*
 			 * In theory, we should only conf-rej an
 			 * option that is shorter than RFC 1618
 			 * requires (i.e. < 4), and should conf-nak
 			 * anything else that is not VJ.  However,
 			 * since our algorithm always uses the
 			 * original option to NAK it with new values,
 			 * things would become more complicated.  In
 			 * practice, the only commonly implemented IP
 			 * compression option is VJ anyway, so the
 			 * difference is negligible.
 			 */
 			if (len >= 6 && p[1] == 6) {
 				/*
 				 * correctly formed compression option
 				 * that could be VJ compression
 				 */
 				continue;
 			}
 			if (debug)
 				log(-1,
 				    "optlen %d [invalid/unsupported] ",
 				    p[1]);
 			break;
 		case IPCP_OPT_ADDRESS:
 			if (len >= 6 && p[1] == 6) {
 				/* correctly formed address option */
 				continue;
 			}
 			if (debug)
 				log(-1, "[invalid] ");
 			break;
 		default:
 			/* Others not supported. */
 			if (debug)
 				log(-1, "[rej] ");
 			break;
 		}
 		/* Add the option to rejected list. */
 		bcopy (p, r, p[1]);
 		r += p[1];
 		rlen += p[1];
 	}
 	if (rlen) {
 		if (debug)
 			log(-1, " send conf-rej\n");
 		sppp_cp_send (sp, PPP_IPCP, CONF_REJ, h->ident, rlen, buf);
 		return 0;
 	} else if (debug)
 		log(-1, "\n");
 
 	/* pass 2: parse option values */
 	sppp_get_ip_addrs(sp, 0, &hisaddr, 0);
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "ipcp parse opt values: ",
 		       SPP_ARGS(ifp));
 	p = (void*) (h+1);
 	len = origlen;
 	for (rlen=0; len >= 2 && p[1] >= 2 && len >= p[1];
 	    len-=p[1], p+=p[1]) {
 		if (debug)
 			log(-1, " %s ", sppp_ipcp_opt_name(*p));
 		switch (*p) {
 		case IPCP_OPT_COMPRESSION:
 			desiredcomp = p[2] << 8 | p[3];
 			/* We only support VJ */
 			if (desiredcomp == IPCP_COMP_VJ) {
 				if (debug)
 					log(-1, "VJ [ack] ");
 				sp->ipcp.flags |= IPCP_VJ;
 				sl_compress_init(sp->pp_comp, p[4]);
 				sp->ipcp.max_state = p[4];
 				sp->ipcp.compress_cid = p[5];
 				continue;
 			}
 			if (debug)
 				log(-1,
 				    "compproto %#04x [not supported] ",
 				    desiredcomp);
 			p[2] = IPCP_COMP_VJ >> 8;
 			p[3] = IPCP_COMP_VJ;
 			p[4] = sp->ipcp.max_state;
 			p[5] = sp->ipcp.compress_cid;
 			break;
 		case IPCP_OPT_ADDRESS:
 			/* This is the address he wants in his end */
 			desiredaddr = p[2] << 24 | p[3] << 16 |
 				p[4] << 8 | p[5];
 			if (desiredaddr == hisaddr ||
 			    (hisaddr >= 1 && hisaddr <= 254 && desiredaddr != 0)) {
 				/*
 				 * Peer's address is same as our value,
 				 * or we have set it to 0.0.0.* to
 				 * indicate that we do not really care,
 				 * this is agreeable.  Gonna conf-ack
 				 * it.
 				 */
 				if (debug)
 					log(-1, "%s [ack] ",
 						sppp_dotted_quad(hisaddr));
 				/* record that we've seen it already */
 				sp->ipcp.flags |= IPCP_HISADDR_SEEN;
 				continue;
 			}
 			/*
 			 * The address wasn't agreeable.  This is either
 			 * he sent us 0.0.0.0, asking to assign him an
 			 * address, or he send us another address not
 			 * matching our value.  Either case, we gonna
 			 * conf-nak it with our value.
 			 * XXX: we should "rej" if hisaddr == 0
 			 */
 			if (debug) {
 				if (desiredaddr == 0)
 					log(-1, "[addr requested] ");
 				else
 					log(-1, "%s [not agreed] ",
 						sppp_dotted_quad(desiredaddr));
 			}
 			p[2] = hisaddr >> 24;
 			p[3] = hisaddr >> 16;
 			p[4] = hisaddr >> 8;
 			p[5] = hisaddr;
 			break;
 		}
 		/* Add the option to nak'ed list. */
 		bcopy (p, r, p[1]);
 		r += p[1];
 		rlen += p[1];
 	}
 
 	/*
 	 * If we are about to conf-ack the request, but haven't seen
 	 * his address so far, gonna conf-nak it instead, with the
 	 * `address' option present and our idea of his address being
 	 * filled in there, to request negotiation of both addresses.
 	 *
 	 * XXX This can result in an endless req - nak loop if peer
 	 * doesn't want to send us his address.  Q: What should we do
 	 * about it?  XXX  A: implement the max-failure counter.
 	 */
 	if (rlen == 0 && !(sp->ipcp.flags & IPCP_HISADDR_SEEN) && !gotmyaddr) {
 		buf[0] = IPCP_OPT_ADDRESS;
 		buf[1] = 6;
 		buf[2] = hisaddr >> 24;
 		buf[3] = hisaddr >> 16;
 		buf[4] = hisaddr >> 8;
 		buf[5] = hisaddr;
 		rlen = 6;
 		if (debug)
 			log(-1, "still need hisaddr ");
 	}
 
 	if (rlen) {
 		if (debug)
 			log(-1, " send conf-nak\n");
 		sppp_cp_send (sp, PPP_IPCP, CONF_NAK, h->ident, rlen, buf);
 	} else {
 		if (debug)
 			log(-1, " send conf-ack\n");
 		sppp_cp_send (sp, PPP_IPCP, CONF_ACK,
 			      h->ident, origlen, h+1);
 	}
 
 	free (buf, M_TEMP);
 	return (rlen == 0);
 }
 
 /*
  * Analyze the IPCP Configure-Reject option list, and adjust our
  * negotiation.
  */
 static void
 sppp_ipcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len)
 {
 	u_char *buf, *p;
 	struct ifnet *ifp = SP2IFP(sp);
 	int debug = ifp->if_flags & IFF_DEBUG;
 
 	len -= 4;
 	buf = malloc (len, M_TEMP, M_NOWAIT);
 	if (!buf)
 		return;
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "ipcp rej opts: ",
 		    SPP_ARGS(ifp));
 
 	p = (void*) (h+1);
 	for (; len >= 2 && p[1] >= 2 && len >= p[1];
 	    len -= p[1], p += p[1]) {
 		if (debug)
 			log(-1, " %s ", sppp_ipcp_opt_name(*p));
 		switch (*p) {
 		case IPCP_OPT_COMPRESSION:
 			sp->ipcp.opts &= ~(1 << IPCP_OPT_COMPRESSION);
 			break;
 		case IPCP_OPT_ADDRESS:
 			/*
 			 * Peer doesn't grok address option.  This is
 			 * bad.  XXX  Should we better give up here?
 			 * XXX We could try old "addresses" option...
 			 */
 			sp->ipcp.opts &= ~(1 << IPCP_OPT_ADDRESS);
 			break;
 		}
 	}
 	if (debug)
 		log(-1, "\n");
 	free (buf, M_TEMP);
 	return;
 }
 
 /*
  * Analyze the IPCP Configure-NAK option list, and adjust our
  * negotiation.
  */
 static void
 sppp_ipcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len)
 {
 	u_char *buf, *p;
 	struct ifnet *ifp = SP2IFP(sp);
 	int debug = ifp->if_flags & IFF_DEBUG;
 	int desiredcomp;
 	u_long wantaddr;
 
 	len -= 4;
 	buf = malloc (len, M_TEMP, M_NOWAIT);
 	if (!buf)
 		return;
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "ipcp nak opts: ",
 		    SPP_ARGS(ifp));
 
 	p = (void*) (h+1);
 	for (; len >= 2 && p[1] >= 2 && len >= p[1];
 	    len -= p[1], p += p[1]) {
 		if (debug)
 			log(-1, " %s ", sppp_ipcp_opt_name(*p));
 		switch (*p) {
 		case IPCP_OPT_COMPRESSION:
 			if (len >= 6 && p[1] == 6) {
 				desiredcomp = p[2] << 8 | p[3];
 				if (debug)
 					log(-1, "[wantcomp %#04x] ",
 						desiredcomp);
 				if (desiredcomp == IPCP_COMP_VJ) {
 					sl_compress_init(sp->pp_comp, p[4]);
 					sp->ipcp.max_state = p[4];
 					sp->ipcp.compress_cid = p[5];
 					if (debug)
 						log(-1, "[agree] ");
 				} else
 					sp->ipcp.opts &=
 						~(1 << IPCP_OPT_COMPRESSION);
 			}
 			break;
 		case IPCP_OPT_ADDRESS:
 			/*
 			 * Peer doesn't like our local IP address.  See
 			 * if we can do something for him.  We'll drop
 			 * him our address then.
 			 */
 			if (len >= 6 && p[1] == 6) {
 				wantaddr = p[2] << 24 | p[3] << 16 |
 					p[4] << 8 | p[5];
 				sp->ipcp.opts |= (1 << IPCP_OPT_ADDRESS);
 				if (debug)
 					log(-1, "[wantaddr %s] ",
 					       sppp_dotted_quad(wantaddr));
 				/*
 				 * When doing dynamic address assignment,
 				 * we accept his offer.  Otherwise, we
 				 * ignore it and thus continue to negotiate
 				 * our already existing value.
 			 	 * XXX: Bogus, if he said no once, he'll
 				 * just say no again, might as well die.
 				 */
 				if (sp->ipcp.flags & IPCP_MYADDR_DYN) {
 					sppp_set_ip_addr(sp, wantaddr);
 					if (debug)
 						log(-1, "[agree] ");
 					sp->ipcp.flags |= IPCP_MYADDR_SEEN;
 				}
 			}
 			break;
 		}
 	}
 	if (debug)
 		log(-1, "\n");
 	free (buf, M_TEMP);
 	return;
 }
 
 static void
 sppp_ipcp_tlu(struct sppp *sp)
 {
 	/* we are up - notify isdn daemon */
 	if (sp->pp_con)
 		sp->pp_con(sp);
 }
 
 static void
 sppp_ipcp_tld(struct sppp *sp)
 {
 }
 
 static void
 sppp_ipcp_tls(struct sppp *sp)
 {
 	/* indicate to LCP that it must stay alive */
 	sp->lcp.protos |= (1 << IDX_IPCP);
 }
 
 static void
 sppp_ipcp_tlf(struct sppp *sp)
 {
 	/* we no longer need LCP */
 	sp->lcp.protos &= ~(1 << IDX_IPCP);
 	sppp_lcp_check_and_close(sp);
 }
 
 static void
 sppp_ipcp_scr(struct sppp *sp)
 {
 	char opt[6 /* compression */ + 6 /* address */];
 	u_long ouraddr;
 	int i = 0;
 
 	if (sp->ipcp.opts & (1 << IPCP_OPT_COMPRESSION)) {
 		opt[i++] = IPCP_OPT_COMPRESSION;
 		opt[i++] = 6;
 		opt[i++] = IPCP_COMP_VJ >> 8;
 		opt[i++] = IPCP_COMP_VJ;
 		opt[i++] = sp->ipcp.max_state;
 		opt[i++] = sp->ipcp.compress_cid;
 	}
 	if (sp->ipcp.opts & (1 << IPCP_OPT_ADDRESS)) {
 		sppp_get_ip_addrs(sp, &ouraddr, 0, 0);
 		opt[i++] = IPCP_OPT_ADDRESS;
 		opt[i++] = 6;
 		opt[i++] = ouraddr >> 24;
 		opt[i++] = ouraddr >> 16;
 		opt[i++] = ouraddr >> 8;
 		opt[i++] = ouraddr;
 	}
 
 	sp->confid[IDX_IPCP] = ++sp->pp_seq[IDX_IPCP];
 	sppp_cp_send(sp, PPP_IPCP, CONF_REQ, sp->confid[IDX_IPCP], i, &opt);
 }
 #else /* !INET */
 static void
 sppp_ipcp_init(struct sppp *sp)
 {
 }
 
 static void
 sppp_ipcp_up(struct sppp *sp)
 {
 }
 
 static void
 sppp_ipcp_down(struct sppp *sp)
 {
 }
 
 static void
 sppp_ipcp_open(struct sppp *sp)
 {
 }
 
 static void
 sppp_ipcp_close(struct sppp *sp)
 {
 }
 
 static void
 sppp_ipcp_TO(void *cookie)
 {
 }
 
 static int
 sppp_ipcp_RCR(struct sppp *sp, struct lcp_header *h, int len)
 {
 	return (0);
 }
 
 static void
 sppp_ipcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len)
 {
 }
 
 static void
 sppp_ipcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len)
 {
 }
 
 static void
 sppp_ipcp_tlu(struct sppp *sp)
 {
 }
 
 static void
 sppp_ipcp_tld(struct sppp *sp)
 {
 }
 
 static void
 sppp_ipcp_tls(struct sppp *sp)
 {
 }
 
 static void
 sppp_ipcp_tlf(struct sppp *sp)
 {
 }
 
 static void
 sppp_ipcp_scr(struct sppp *sp)
 {
 }
 #endif
 
 /*
  *--------------------------------------------------------------------------*
  *                                                                          *
  *                      The IPv6CP implementation.                          *
  *                                                                          *
  *--------------------------------------------------------------------------*
  */
 
 #ifdef INET6
 static void
 sppp_ipv6cp_init(struct sppp *sp)
 {
 	sp->ipv6cp.opts = 0;
 	sp->ipv6cp.flags = 0;
 	sp->state[IDX_IPV6CP] = STATE_INITIAL;
 	sp->fail_counter[IDX_IPV6CP] = 0;
 	sp->pp_seq[IDX_IPV6CP] = 0;
 	sp->pp_rseq[IDX_IPV6CP] = 0;
  	callout_init(&sp->ch[IDX_IPV6CP], 1);
 }
 
 static void
 sppp_ipv6cp_up(struct sppp *sp)
 {
 	sppp_up_event(&ipv6cp, sp);
 }
 
 static void
 sppp_ipv6cp_down(struct sppp *sp)
 {
 	sppp_down_event(&ipv6cp, sp);
 }
 
 static void
 sppp_ipv6cp_open(struct sppp *sp)
 {
 	STDDCL;
 	struct in6_addr myaddr, hisaddr;
 
 #ifdef IPV6CP_MYIFID_DYN
 	sp->ipv6cp.flags &= ~(IPV6CP_MYIFID_SEEN|IPV6CP_MYIFID_DYN);
 #else
 	sp->ipv6cp.flags &= ~IPV6CP_MYIFID_SEEN;
 #endif
 
 	sppp_get_ip6_addrs(sp, &myaddr, &hisaddr, 0);
 	/*
 	 * If we don't have our address, this probably means our
 	 * interface doesn't want to talk IPv6 at all.  (This could
 	 * be the case if somebody wants to speak only IPX, for
 	 * example.)  Don't open IPv6CP in this case.
 	 */
 	if (IN6_IS_ADDR_UNSPECIFIED(&myaddr)) {
 		/* XXX this message should go away */
 		if (debug)
 			log(LOG_DEBUG, SPP_FMT "ipv6cp_open(): no IPv6 interface\n",
 			    SPP_ARGS(ifp));
 		return;
 	}
 
 	sp->ipv6cp.flags |= IPV6CP_MYIFID_SEEN;
 	sp->ipv6cp.opts |= (1 << IPV6CP_OPT_IFID);
 	sppp_open_event(&ipv6cp, sp);
 }
 
 static void
 sppp_ipv6cp_close(struct sppp *sp)
 {
 	sppp_close_event(&ipv6cp, sp);
 }
 
 static void
 sppp_ipv6cp_TO(void *cookie)
 {
 	sppp_to_event(&ipv6cp, (struct sppp *)cookie);
 }
 
 /*
  * Analyze a configure request.  Return true if it was agreeable, and
  * caused action sca, false if it has been rejected or nak'ed, and
  * caused action scn.  (The return value is used to make the state
  * transition decision in the state automaton.)
  */
 static int
 sppp_ipv6cp_RCR(struct sppp *sp, struct lcp_header *h, int len)
 {
 	u_char *buf, *r, *p;
 	struct ifnet *ifp = SP2IFP(sp);
 	int rlen, origlen, debug = ifp->if_flags & IFF_DEBUG;
 	struct in6_addr myaddr, desiredaddr, suggestaddr;
 	int ifidcount;
 	int type;
 	int collision, nohisaddr;
 	char ip6buf[INET6_ADDRSTRLEN];
 
 	len -= 4;
 	origlen = len;
 	/*
 	 * Make sure to allocate a buf that can at least hold a
 	 * conf-nak with an `address' option.  We might need it below.
 	 */
 	buf = r = malloc ((len < 6? 6: len), M_TEMP, M_NOWAIT);
 	if (! buf)
 		return (0);
 
 	/* pass 1: see if we can recognize them */
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "ipv6cp parse opts:",
 		    SPP_ARGS(ifp));
 	p = (void*) (h+1);
 	ifidcount = 0;
 	for (rlen=0; len >= 2 && p[1] >= 2 && len >= p[1];
 	    len-=p[1], p+=p[1]) {
 		if (debug)
 			log(-1, " %s", sppp_ipv6cp_opt_name(*p));
 		switch (*p) {
 		case IPV6CP_OPT_IFID:
 			if (len >= 10 && p[1] == 10 && ifidcount == 0) {
 				/* correctly formed address option */
 				ifidcount++;
 				continue;
 			}
 			if (debug)
 				log(-1, " [invalid]");
 			break;
 #ifdef notyet
 		case IPV6CP_OPT_COMPRESSION:
 			if (len >= 4 && p[1] >= 4) {
 				/* correctly formed compress option */
 				continue;
 			}
 			if (debug)
 				log(-1, " [invalid]");
 			break;
 #endif
 		default:
 			/* Others not supported. */
 			if (debug)
 				log(-1, " [rej]");
 			break;
 		}
 		/* Add the option to rejected list. */
 		bcopy (p, r, p[1]);
 		r += p[1];
 		rlen += p[1];
 	}
 	if (rlen) {
 		if (debug)
 			log(-1, " send conf-rej\n");
 		sppp_cp_send (sp, PPP_IPV6CP, CONF_REJ, h->ident, rlen, buf);
 		goto end;
 	} else if (debug)
 		log(-1, "\n");
 
 	/* pass 2: parse option values */
 	sppp_get_ip6_addrs(sp, &myaddr, 0, 0);
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "ipv6cp parse opt values: ",
 		    SPP_ARGS(ifp));
 	p = (void*) (h+1);
 	len = origlen;
 	type = CONF_ACK;
 	for (rlen=0; len >= 2 && p[1] >= 2 && len >= p[1];
 	    len-=p[1], p+=p[1]) {
 		if (debug)
 			log(-1, " %s", sppp_ipv6cp_opt_name(*p));
 		switch (*p) {
 #ifdef notyet
 		case IPV6CP_OPT_COMPRESSION:
 			continue;
 #endif
 		case IPV6CP_OPT_IFID:
 			bzero(&desiredaddr, sizeof(desiredaddr));
 			bcopy(&p[2], &desiredaddr.s6_addr[8], 8);
 			collision = (bcmp(&desiredaddr.s6_addr[8],
 					  &myaddr.s6_addr[8], 8) == 0);
 			nohisaddr = IN6_IS_ADDR_UNSPECIFIED(&desiredaddr);
 
 			desiredaddr.s6_addr16[0] = htons(0xfe80);
 			(void)in6_setscope(&desiredaddr, SP2IFP(sp), NULL);
 
 			if (!collision && !nohisaddr) {
 				/* no collision, hisaddr known - Conf-Ack */
 				type = CONF_ACK;
 
 				if (debug) {
 					log(-1, " %s [%s]",
 					    ip6_sprintf(ip6buf, &desiredaddr),
 					    sppp_cp_type_name(type));
 				}
 				continue;
 			}
 
 			bzero(&suggestaddr, sizeof(suggestaddr));
 			if (collision && nohisaddr) {
 				/* collision, hisaddr unknown - Conf-Rej */
 				type = CONF_REJ;
 				bzero(&p[2], 8);
 			} else {
 				/*
 				 * - no collision, hisaddr unknown, or
 				 * - collision, hisaddr known
 				 * Conf-Nak, suggest hisaddr
 				 */
 				type = CONF_NAK;
 				sppp_suggest_ip6_addr(sp, &suggestaddr);
 				bcopy(&suggestaddr.s6_addr[8], &p[2], 8);
 			}
 			if (debug)
 				log(-1, " %s [%s]",
 				    ip6_sprintf(ip6buf, &desiredaddr),
 				    sppp_cp_type_name(type));
 			break;
 		}
 		/* Add the option to nak'ed list. */
 		bcopy (p, r, p[1]);
 		r += p[1];
 		rlen += p[1];
 	}
 
 	if (rlen == 0 && type == CONF_ACK) {
 		if (debug)
 			log(-1, " send %s\n", sppp_cp_type_name(type));
 		sppp_cp_send (sp, PPP_IPV6CP, type, h->ident, origlen, h+1);
 	} else {
 #ifdef DIAGNOSTIC
 		if (type == CONF_ACK)
 			panic("IPv6CP RCR: CONF_ACK with non-zero rlen");
 #endif
 
 		if (debug) {
 			log(-1, " send %s suggest %s\n",
 			    sppp_cp_type_name(type),
 			    ip6_sprintf(ip6buf, &suggestaddr));
 		}
 		sppp_cp_send (sp, PPP_IPV6CP, type, h->ident, rlen, buf);
 	}
 
  end:
 	free (buf, M_TEMP);
 	return (rlen == 0);
 }
 
 /*
  * Analyze the IPv6CP Configure-Reject option list, and adjust our
  * negotiation.
  */
 static void
 sppp_ipv6cp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len)
 {
 	u_char *buf, *p;
 	struct ifnet *ifp = SP2IFP(sp);
 	int debug = ifp->if_flags & IFF_DEBUG;
 
 	len -= 4;
 	buf = malloc (len, M_TEMP, M_NOWAIT);
 	if (!buf)
 		return;
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "ipv6cp rej opts:",
 		    SPP_ARGS(ifp));
 
 	p = (void*) (h+1);
 	for (; len >= 2 && p[1] >= 2 && len >= p[1];
 	    len -= p[1], p += p[1]) {
 		if (debug)
 			log(-1, " %s", sppp_ipv6cp_opt_name(*p));
 		switch (*p) {
 		case IPV6CP_OPT_IFID:
 			/*
 			 * Peer doesn't grok address option.  This is
 			 * bad.  XXX  Should we better give up here?
 			 */
 			sp->ipv6cp.opts &= ~(1 << IPV6CP_OPT_IFID);
 			break;
 #ifdef notyet
 		case IPV6CP_OPT_COMPRESS:
 			sp->ipv6cp.opts &= ~(1 << IPV6CP_OPT_COMPRESS);
 			break;
 #endif
 		}
 	}
 	if (debug)
 		log(-1, "\n");
 	free (buf, M_TEMP);
 	return;
 }
 
 /*
  * Analyze the IPv6CP Configure-NAK option list, and adjust our
  * negotiation.
  */
 static void
 sppp_ipv6cp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len)
 {
 	u_char *buf, *p;
 	struct ifnet *ifp = SP2IFP(sp);
 	int debug = ifp->if_flags & IFF_DEBUG;
 	struct in6_addr suggestaddr;
 	char ip6buf[INET6_ADDRSTRLEN];
 
 	len -= 4;
 	buf = malloc (len, M_TEMP, M_NOWAIT);
 	if (!buf)
 		return;
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "ipv6cp nak opts:",
 		    SPP_ARGS(ifp));
 
 	p = (void*) (h+1);
 	for (; len >= 2 && p[1] >= 2 && len >= p[1];
 	    len -= p[1], p += p[1]) {
 		if (debug)
 			log(-1, " %s", sppp_ipv6cp_opt_name(*p));
 		switch (*p) {
 		case IPV6CP_OPT_IFID:
 			/*
 			 * Peer doesn't like our local ifid.  See
 			 * if we can do something for him.  We'll drop
 			 * him our address then.
 			 */
 			if (len < 10 || p[1] != 10)
 				break;
 			bzero(&suggestaddr, sizeof(suggestaddr));
 			suggestaddr.s6_addr16[0] = htons(0xfe80);
 			(void)in6_setscope(&suggestaddr, SP2IFP(sp), NULL);
 			bcopy(&p[2], &suggestaddr.s6_addr[8], 8);
 
 			sp->ipv6cp.opts |= (1 << IPV6CP_OPT_IFID);
 			if (debug)
 				log(-1, " [suggestaddr %s]",
 				       ip6_sprintf(ip6buf, &suggestaddr));
 #ifdef IPV6CP_MYIFID_DYN
 			/*
 			 * When doing dynamic address assignment,
 			 * we accept his offer.
 			 */
 			if (sp->ipv6cp.flags & IPV6CP_MYIFID_DYN) {
 				struct in6_addr lastsuggest;
 				/*
 				 * If <suggested myaddr from peer> equals to
 				 * <hisaddr we have suggested last time>,
 				 * we have a collision.  generate new random
 				 * ifid.
 				 */
 				sppp_suggest_ip6_addr(&lastsuggest);
 				if (IN6_ARE_ADDR_EQUAL(&suggestaddr,
 						       lastsuggest)) {
 					if (debug)
 						log(-1, " [random]");
 					sppp_gen_ip6_addr(sp, &suggestaddr);
 				}
 				sppp_set_ip6_addr(sp, &suggestaddr, 0);
 				if (debug)
 					log(-1, " [agree]");
 				sp->ipv6cp.flags |= IPV6CP_MYIFID_SEEN;
 			}
 #else
 			/*
 			 * Since we do not do dynamic address assignment,
 			 * we ignore it and thus continue to negotiate
 			 * our already existing value.  This can possibly
 			 * go into infinite request-reject loop.
 			 *
 			 * This is not likely because we normally use
 			 * ifid based on MAC-address.
 			 * If you have no ethernet card on the node, too bad.
 			 * XXX should we use fail_counter?
 			 */
 #endif
 			break;
 #ifdef notyet
 		case IPV6CP_OPT_COMPRESS:
 			/*
 			 * Peer wants different compression parameters.
 			 */
 			break;
 #endif
 		}
 	}
 	if (debug)
 		log(-1, "\n");
 	free (buf, M_TEMP);
 	return;
 }
 static void
 sppp_ipv6cp_tlu(struct sppp *sp)
 {
 	/* we are up - notify isdn daemon */
 	if (sp->pp_con)
 		sp->pp_con(sp);
 }
 
 static void
 sppp_ipv6cp_tld(struct sppp *sp)
 {
 }
 
 static void
 sppp_ipv6cp_tls(struct sppp *sp)
 {
 	/* indicate to LCP that it must stay alive */
 	sp->lcp.protos |= (1 << IDX_IPV6CP);
 }
 
 static void
 sppp_ipv6cp_tlf(struct sppp *sp)
 {
 
 #if 0	/* need #if 0 to close IPv6CP properly */
 	/* we no longer need LCP */
 	sp->lcp.protos &= ~(1 << IDX_IPV6CP);
 	sppp_lcp_check_and_close(sp);
 #endif
 }
 
 static void
 sppp_ipv6cp_scr(struct sppp *sp)
 {
 	char opt[10 /* ifid */ + 4 /* compression, minimum */];
 	struct in6_addr ouraddr;
 	int i = 0;
 
 	if (sp->ipv6cp.opts & (1 << IPV6CP_OPT_IFID)) {
 		sppp_get_ip6_addrs(sp, &ouraddr, 0, 0);
 		opt[i++] = IPV6CP_OPT_IFID;
 		opt[i++] = 10;
 		bcopy(&ouraddr.s6_addr[8], &opt[i], 8);
 		i += 8;
 	}
 
 #ifdef notyet
 	if (sp->ipv6cp.opts & (1 << IPV6CP_OPT_COMPRESSION)) {
 		opt[i++] = IPV6CP_OPT_COMPRESSION;
 		opt[i++] = 4;
 		opt[i++] = 0;   /* TBD */
 		opt[i++] = 0;   /* TBD */
 		/* variable length data may follow */
 	}
 #endif
 
 	sp->confid[IDX_IPV6CP] = ++sp->pp_seq[IDX_IPV6CP];
 	sppp_cp_send(sp, PPP_IPV6CP, CONF_REQ, sp->confid[IDX_IPV6CP], i, &opt);
 }
 #else /*INET6*/
 static void sppp_ipv6cp_init(struct sppp *sp)
 {
 }
 
 static void sppp_ipv6cp_up(struct sppp *sp)
 {
 }
 
 static void sppp_ipv6cp_down(struct sppp *sp)
 {
 }
 
 static void sppp_ipv6cp_open(struct sppp *sp)
 {
 }
 
 static void sppp_ipv6cp_close(struct sppp *sp)
 {
 }
 
 static void sppp_ipv6cp_TO(void *sp)
 {
 }
 
 static int sppp_ipv6cp_RCR(struct sppp *sp, struct lcp_header *h, int len)
 {
 	return 0;
 }
 
 static void sppp_ipv6cp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len)
 {
 }
 
 static void sppp_ipv6cp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len)
 {
 }
 
 static void sppp_ipv6cp_tlu(struct sppp *sp)
 {
 }
 
 static void sppp_ipv6cp_tld(struct sppp *sp)
 {
 }
 
 static void sppp_ipv6cp_tls(struct sppp *sp)
 {
 }
 
 static void sppp_ipv6cp_tlf(struct sppp *sp)
 {
 }
 
 static void sppp_ipv6cp_scr(struct sppp *sp)
 {
 }
 #endif /*INET6*/
 
 /*
  *--------------------------------------------------------------------------*
  *                                                                          *
  *                        The CHAP implementation.                          *
  *                                                                          *
  *--------------------------------------------------------------------------*
  */
 
 /*
  * The authentication protocols don't employ a full-fledged state machine as
  * the control protocols do, since they do have Open and Close events, but
  * not Up and Down, nor are they explicitly terminated.  Also, use of the
  * authentication protocols may be different in both directions (this makes
  * sense, think of a machine that never accepts incoming calls but only
  * calls out, it doesn't require the called party to authenticate itself).
  *
  * Our state machine for the local authentication protocol (we are requesting
  * the peer to authenticate) looks like:
  *
  *						    RCA-
  *	      +--------------------------------------------+
  *	      V					    scn,tld|
  *	  +--------+			       Close   +---------+ RCA+
  *	  |	   |<----------------------------------|	 |------+
  *   +--->| Closed |				TO*    | Opened	 | sca	|
  *   |	  |	   |-----+		       +-------|	 |<-----+
  *   |	  +--------+ irc |		       |       +---------+
  *   |	    ^		 |		       |	   ^
  *   |	    |		 |		       |	   |
  *   |	    |		 |		       |	   |
  *   |	 TO-|		 |		       |	   |
  *   |	    |tld  TO+	 V		       |	   |
  *   |	    |	+------->+		       |	   |
  *   |	    |	|	 |		       |	   |
  *   |	  +--------+	 V		       |	   |
  *   |	  |	   |<----+<--------------------+	   |
  *   |	  | Req-   | scr				   |
  *   |	  | Sent   |					   |
  *   |	  |	   |					   |
  *   |	  +--------+					   |
  *   | RCA- |	| RCA+					   |
  *   +------+	+------------------------------------------+
  *   scn,tld	  sca,irc,ict,tlu
  *
  *
  *   with:
  *
  *	Open:	LCP reached authentication phase
  *	Close:	LCP reached terminate phase
  *
  *	RCA+:	received reply (pap-req, chap-response), acceptable
  *	RCN:	received reply (pap-req, chap-response), not acceptable
  *	TO+:	timeout with restart counter >= 0
  *	TO-:	timeout with restart counter < 0
  *	TO*:	reschedule timeout for CHAP
  *
  *	scr:	send request packet (none for PAP, chap-challenge)
  *	sca:	send ack packet (pap-ack, chap-success)
  *	scn:	send nak packet (pap-nak, chap-failure)
  *	ict:	initialize re-challenge timer (CHAP only)
  *
  *	tlu:	this-layer-up, LCP reaches network phase
  *	tld:	this-layer-down, LCP enters terminate phase
  *
  * Note that in CHAP mode, after sending a new challenge, while the state
  * automaton falls back into Req-Sent state, it doesn't signal a tld
  * event to LCP, so LCP remains in network phase.  Only after not getting
  * any response (or after getting an unacceptable response), CHAP closes,
  * causing LCP to enter terminate phase.
  *
  * With PAP, there is no initial request that can be sent.  The peer is
  * expected to send one based on the successful negotiation of PAP as
  * the authentication protocol during the LCP option negotiation.
  *
  * Incoming authentication protocol requests (remote requests
  * authentication, we are peer) don't employ a state machine at all,
  * they are simply answered.  Some peers [Ascend P50 firmware rev
  * 4.50] react allergically when sending IPCP requests while they are
  * still in authentication phase (thereby violating the standard that
  * demands that these NCP packets are to be discarded), so we keep
  * track of the peer demanding us to authenticate, and only proceed to
  * phase network once we've seen a positive acknowledge for the
  * authentication.
  */
 
 /*
  * Handle incoming CHAP packets.
  */
 static void
 sppp_chap_input(struct sppp *sp, struct mbuf *m)
 {
 	STDDCL;
 	struct lcp_header *h;
 	int len;
 	u_char *value, *name, digest[AUTHKEYLEN], dsize;
 	int value_len, name_len;
 	MD5_CTX ctx;
 
 	len = m->m_pkthdr.len;
 	if (len < 4) {
 		if (debug)
 			log(LOG_DEBUG,
 			    SPP_FMT "chap invalid packet length: %d bytes\n",
 			    SPP_ARGS(ifp), len);
 		return;
 	}
 	h = mtod (m, struct lcp_header*);
 	if (len > ntohs (h->len))
 		len = ntohs (h->len);
 
 	switch (h->type) {
 	/* challenge, failure and success are his authproto */
 	case CHAP_CHALLENGE:
 		value = 1 + (u_char*)(h+1);
 		value_len = value[-1];
 		name = value + value_len;
 		name_len = len - value_len - 5;
 		if (name_len < 0) {
 			if (debug) {
 				log(LOG_DEBUG,
 				    SPP_FMT "chap corrupted challenge "
 				    "<%s id=0x%x len=%d",
 				    SPP_ARGS(ifp),
 				    sppp_auth_type_name(PPP_CHAP, h->type),
 				    h->ident, ntohs(h->len));
 				sppp_print_bytes((u_char*) (h+1), len-4);
 				log(-1, ">\n");
 			}
 			break;
 		}
 
 		if (debug) {
 			log(LOG_DEBUG,
 			    SPP_FMT "chap input <%s id=0x%x len=%d name=",
 			    SPP_ARGS(ifp),
 			    sppp_auth_type_name(PPP_CHAP, h->type), h->ident,
 			    ntohs(h->len));
 			sppp_print_string((char*) name, name_len);
 			log(-1, " value-size=%d value=", value_len);
 			sppp_print_bytes(value, value_len);
 			log(-1, ">\n");
 		}
 
 		/* Compute reply value. */
 		MD5Init(&ctx);
 		MD5Update(&ctx, &h->ident, 1);
 		MD5Update(&ctx, sp->myauth.secret,
 			  sppp_strnlen(sp->myauth.secret, AUTHKEYLEN));
 		MD5Update(&ctx, value, value_len);
 		MD5Final(digest, &ctx);
 		dsize = sizeof digest;
 
 		sppp_auth_send(&chap, sp, CHAP_RESPONSE, h->ident,
 			       sizeof dsize, (const char *)&dsize,
 			       sizeof digest, digest,
 			       (size_t)sppp_strnlen(sp->myauth.name, AUTHNAMELEN),
 			       sp->myauth.name,
 			       0);
 		break;
 
 	case CHAP_SUCCESS:
 		if (debug) {
 			log(LOG_DEBUG, SPP_FMT "chap success",
 			    SPP_ARGS(ifp));
 			if (len > 4) {
 				log(-1, ": ");
 				sppp_print_string((char*)(h + 1), len - 4);
 			}
 			log(-1, "\n");
 		}
 		SPPP_LOCK(sp);
 		sp->pp_flags &= ~PP_NEEDAUTH;
 		if (sp->myauth.proto == PPP_CHAP &&
 		    (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) &&
 		    (sp->lcp.protos & (1 << IDX_CHAP)) == 0) {
 			/*
 			 * We are authenticator for CHAP but didn't
 			 * complete yet.  Leave it to tlu to proceed
 			 * to network phase.
 			 */
 			SPPP_UNLOCK(sp);
 			break;
 		}
 		SPPP_UNLOCK(sp);
 		sppp_phase_network(sp);
 		break;
 
 	case CHAP_FAILURE:
 		if (debug) {
 			log(LOG_INFO, SPP_FMT "chap failure",
 			    SPP_ARGS(ifp));
 			if (len > 4) {
 				log(-1, ": ");
 				sppp_print_string((char*)(h + 1), len - 4);
 			}
 			log(-1, "\n");
 		} else
 			log(LOG_INFO, SPP_FMT "chap failure\n",
 			    SPP_ARGS(ifp));
 		/* await LCP shutdown by authenticator */
 		break;
 
 	/* response is my authproto */
 	case CHAP_RESPONSE:
 		value = 1 + (u_char*)(h+1);
 		value_len = value[-1];
 		name = value + value_len;
 		name_len = len - value_len - 5;
 		if (name_len < 0) {
 			if (debug) {
 				log(LOG_DEBUG,
 				    SPP_FMT "chap corrupted response "
 				    "<%s id=0x%x len=%d",
 				    SPP_ARGS(ifp),
 				    sppp_auth_type_name(PPP_CHAP, h->type),
 				    h->ident, ntohs(h->len));
 				sppp_print_bytes((u_char*)(h+1), len-4);
 				log(-1, ">\n");
 			}
 			break;
 		}
 		if (h->ident != sp->confid[IDX_CHAP]) {
 			if (debug)
 				log(LOG_DEBUG,
 				    SPP_FMT "chap dropping response for old ID "
 				    "(got %d, expected %d)\n",
 				    SPP_ARGS(ifp),
 				    h->ident, sp->confid[IDX_CHAP]);
 			break;
 		}
 		if (name_len != sppp_strnlen(sp->hisauth.name, AUTHNAMELEN)
 		    || bcmp(name, sp->hisauth.name, name_len) != 0) {
 			log(LOG_INFO, SPP_FMT "chap response, his name ",
 			    SPP_ARGS(ifp));
 			sppp_print_string(name, name_len);
 			log(-1, " != expected ");
 			sppp_print_string(sp->hisauth.name,
 					  sppp_strnlen(sp->hisauth.name, AUTHNAMELEN));
 			log(-1, "\n");
 		}
 		if (debug) {
 			log(LOG_DEBUG, SPP_FMT "chap input(%s) "
 			    "<%s id=0x%x len=%d name=",
 			    SPP_ARGS(ifp),
 			    sppp_state_name(sp->state[IDX_CHAP]),
 			    sppp_auth_type_name(PPP_CHAP, h->type),
 			    h->ident, ntohs (h->len));
 			sppp_print_string((char*)name, name_len);
 			log(-1, " value-size=%d value=", value_len);
 			sppp_print_bytes(value, value_len);
 			log(-1, ">\n");
 		}
 		if (value_len != AUTHKEYLEN) {
 			if (debug)
 				log(LOG_DEBUG,
 				    SPP_FMT "chap bad hash value length: "
 				    "%d bytes, should be %d\n",
 				    SPP_ARGS(ifp), value_len,
 				    AUTHKEYLEN);
 			break;
 		}
 
 		MD5Init(&ctx);
 		MD5Update(&ctx, &h->ident, 1);
 		MD5Update(&ctx, sp->hisauth.secret,
 			  sppp_strnlen(sp->hisauth.secret, AUTHKEYLEN));
 		MD5Update(&ctx, sp->myauth.challenge, AUTHKEYLEN);
 		MD5Final(digest, &ctx);
 
 #define FAILMSG "Failed..."
 #define SUCCMSG "Welcome!"
 
 		if (value_len != sizeof digest ||
 		    bcmp(digest, value, value_len) != 0) {
 			/* action scn, tld */
 			sppp_auth_send(&chap, sp, CHAP_FAILURE, h->ident,
 				       sizeof(FAILMSG) - 1, (u_char *)FAILMSG,
 				       0);
 			chap.tld(sp);
 			break;
 		}
 		/* action sca, perhaps tlu */
 		if (sp->state[IDX_CHAP] == STATE_REQ_SENT ||
 		    sp->state[IDX_CHAP] == STATE_OPENED)
 			sppp_auth_send(&chap, sp, CHAP_SUCCESS, h->ident,
 				       sizeof(SUCCMSG) - 1, (u_char *)SUCCMSG,
 				       0);
 		if (sp->state[IDX_CHAP] == STATE_REQ_SENT) {
 			sppp_cp_change_state(&chap, sp, STATE_OPENED);
 			chap.tlu(sp);
 		}
 		break;
 
 	default:
 		/* Unknown CHAP packet type -- ignore. */
 		if (debug) {
 			log(LOG_DEBUG, SPP_FMT "chap unknown input(%s) "
 			    "<0x%x id=0x%xh len=%d",
 			    SPP_ARGS(ifp),
 			    sppp_state_name(sp->state[IDX_CHAP]),
 			    h->type, h->ident, ntohs(h->len));
 			sppp_print_bytes((u_char*)(h+1), len-4);
 			log(-1, ">\n");
 		}
 		break;
 	}
 }
 
 static void
 sppp_chap_init(struct sppp *sp)
 {
 	/* Chap doesn't have STATE_INITIAL at all. */
 	sp->state[IDX_CHAP] = STATE_CLOSED;
 	sp->fail_counter[IDX_CHAP] = 0;
 	sp->pp_seq[IDX_CHAP] = 0;
 	sp->pp_rseq[IDX_CHAP] = 0;
  	callout_init(&sp->ch[IDX_CHAP], 1);
 }
 
 static void
 sppp_chap_open(struct sppp *sp)
 {
 	if (sp->myauth.proto == PPP_CHAP &&
 	    (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0) {
 		/* we are authenticator for CHAP, start it */
 		chap.scr(sp);
 		sp->rst_counter[IDX_CHAP] = sp->lcp.max_configure;
 		sppp_cp_change_state(&chap, sp, STATE_REQ_SENT);
 	}
 	/* nothing to be done if we are peer, await a challenge */
 }
 
 static void
 sppp_chap_close(struct sppp *sp)
 {
 	if (sp->state[IDX_CHAP] != STATE_CLOSED)
 		sppp_cp_change_state(&chap, sp, STATE_CLOSED);
 }
 
 static void
 sppp_chap_TO(void *cookie)
 {
 	struct sppp *sp = (struct sppp *)cookie;
 	STDDCL;
 
 	SPPP_LOCK(sp);
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "chap TO(%s) rst_counter = %d\n",
 		    SPP_ARGS(ifp),
 		    sppp_state_name(sp->state[IDX_CHAP]),
 		    sp->rst_counter[IDX_CHAP]);
 
 	if (--sp->rst_counter[IDX_CHAP] < 0)
 		/* TO- event */
 		switch (sp->state[IDX_CHAP]) {
 		case STATE_REQ_SENT:
 			chap.tld(sp);
 			sppp_cp_change_state(&chap, sp, STATE_CLOSED);
 			break;
 		}
 	else
 		/* TO+ (or TO*) event */
 		switch (sp->state[IDX_CHAP]) {
 		case STATE_OPENED:
 			/* TO* event */
 			sp->rst_counter[IDX_CHAP] = sp->lcp.max_configure;
 			/* FALLTHROUGH */
 		case STATE_REQ_SENT:
 			chap.scr(sp);
 			/* sppp_cp_change_state() will restart the timer */
 			sppp_cp_change_state(&chap, sp, STATE_REQ_SENT);
 			break;
 		}
 
 	SPPP_UNLOCK(sp);
 }
 
 static void
 sppp_chap_tlu(struct sppp *sp)
 {
 	STDDCL;
 	int i;
 
 	i = 0;
 	sp->rst_counter[IDX_CHAP] = sp->lcp.max_configure;
 
 	/*
 	 * Some broken CHAP implementations (Conware CoNet, firmware
 	 * 4.0.?) don't want to re-authenticate their CHAP once the
 	 * initial challenge-response exchange has taken place.
 	 * Provide for an option to avoid rechallenges.
 	 */
 	if ((sp->hisauth.flags & AUTHFLAG_NORECHALLENGE) == 0) {
 		/*
 		 * Compute the re-challenge timeout.  This will yield
 		 * a number between 300 and 810 seconds.
 		 */
 		i = 300 + ((unsigned)(random() & 0xff00) >> 7);
 		callout_reset(&sp->ch[IDX_CHAP], i * hz, chap.TO, (void *)sp);
 	}
 
 	if (debug) {
 		log(LOG_DEBUG,
 		    SPP_FMT "chap %s, ",
 		    SPP_ARGS(ifp),
 		    sp->pp_phase == PHASE_NETWORK? "reconfirmed": "tlu");
 		if ((sp->hisauth.flags & AUTHFLAG_NORECHALLENGE) == 0)
 			log(-1, "next re-challenge in %d seconds\n", i);
 		else
 			log(-1, "re-challenging suppressed\n");
 	}
 
 	SPPP_LOCK(sp);
 	/* indicate to LCP that we need to be closed down */
 	sp->lcp.protos |= (1 << IDX_CHAP);
 
 	if (sp->pp_flags & PP_NEEDAUTH) {
 		/*
 		 * Remote is authenticator, but his auth proto didn't
 		 * complete yet.  Defer the transition to network
 		 * phase.
 		 */
 		SPPP_UNLOCK(sp);
 		return;
 	}
 	SPPP_UNLOCK(sp);
 
 	/*
 	 * If we are already in phase network, we are done here.  This
 	 * is the case if this is a dummy tlu event after a re-challenge.
 	 */
 	if (sp->pp_phase != PHASE_NETWORK)
 		sppp_phase_network(sp);
 }
 
 static void
 sppp_chap_tld(struct sppp *sp)
 {
 	STDDCL;
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "chap tld\n", SPP_ARGS(ifp));
 	callout_stop(&sp->ch[IDX_CHAP]);
 	sp->lcp.protos &= ~(1 << IDX_CHAP);
 
 	lcp.Close(sp);
 }
 
 static void
 sppp_chap_scr(struct sppp *sp)
 {
 	u_long *ch;
 	u_char clen;
 
 	/* Compute random challenge. */
 	ch = (u_long *)sp->myauth.challenge;
 	arc4random_buf(ch, 4 * sizeof(*ch));
 	clen = AUTHKEYLEN;
 
 	sp->confid[IDX_CHAP] = ++sp->pp_seq[IDX_CHAP];
 
 	sppp_auth_send(&chap, sp, CHAP_CHALLENGE, sp->confid[IDX_CHAP],
 		       sizeof clen, (const char *)&clen,
 		       (size_t)AUTHKEYLEN, sp->myauth.challenge,
 		       (size_t)sppp_strnlen(sp->myauth.name, AUTHNAMELEN),
 		       sp->myauth.name,
 		       0);
 }
 
 /*
  *--------------------------------------------------------------------------*
  *                                                                          *
  *                        The PAP implementation.                           *
  *                                                                          *
  *--------------------------------------------------------------------------*
  */
 /*
  * For PAP, we need to keep a little state also if we are the peer, not the
  * authenticator.  This is since we don't get a request to authenticate, but
  * have to repeatedly authenticate ourself until we got a response (or the
  * retry counter is expired).
  */
 
 /*
  * Handle incoming PAP packets.  */
 static void
 sppp_pap_input(struct sppp *sp, struct mbuf *m)
 {
 	STDDCL;
 	struct lcp_header *h;
 	int len;
 	u_char *name, *passwd, mlen;
 	int name_len, passwd_len;
 
 	len = m->m_pkthdr.len;
 	if (len < 5) {
 		if (debug)
 			log(LOG_DEBUG,
 			    SPP_FMT "pap invalid packet length: %d bytes\n",
 			    SPP_ARGS(ifp), len);
 		return;
 	}
 	h = mtod (m, struct lcp_header*);
 	if (len > ntohs (h->len))
 		len = ntohs (h->len);
 	switch (h->type) {
 	/* PAP request is my authproto */
 	case PAP_REQ:
 		name = 1 + (u_char*)(h+1);
 		name_len = name[-1];
 		passwd = name + name_len + 1;
 		if (name_len > len - 6 ||
 		    (passwd_len = passwd[-1]) > len - 6 - name_len) {
 			if (debug) {
 				log(LOG_DEBUG, SPP_FMT "pap corrupted input "
 				    "<%s id=0x%x len=%d",
 				    SPP_ARGS(ifp),
 				    sppp_auth_type_name(PPP_PAP, h->type),
 				    h->ident, ntohs(h->len));
 				sppp_print_bytes((u_char*)(h+1), len-4);
 				log(-1, ">\n");
 			}
 			break;
 		}
 		if (debug) {
 			log(LOG_DEBUG, SPP_FMT "pap input(%s) "
 			    "<%s id=0x%x len=%d name=",
 			    SPP_ARGS(ifp),
 			    sppp_state_name(sp->state[IDX_PAP]),
 			    sppp_auth_type_name(PPP_PAP, h->type),
 			    h->ident, ntohs(h->len));
 			sppp_print_string((char*)name, name_len);
 			log(-1, " passwd=");
 			sppp_print_string((char*)passwd, passwd_len);
 			log(-1, ">\n");
 		}
 		if (name_len != sppp_strnlen(sp->hisauth.name, AUTHNAMELEN) ||
 		    passwd_len != sppp_strnlen(sp->hisauth.secret, AUTHKEYLEN) ||
 		    bcmp(name, sp->hisauth.name, name_len) != 0 ||
 		    bcmp(passwd, sp->hisauth.secret, passwd_len) != 0) {
 			/* action scn, tld */
 			mlen = sizeof(FAILMSG) - 1;
 			sppp_auth_send(&pap, sp, PAP_NAK, h->ident,
 				       sizeof mlen, (const char *)&mlen,
 				       sizeof(FAILMSG) - 1, (u_char *)FAILMSG,
 				       0);
 			pap.tld(sp);
 			break;
 		}
 		/* action sca, perhaps tlu */
 		if (sp->state[IDX_PAP] == STATE_REQ_SENT ||
 		    sp->state[IDX_PAP] == STATE_OPENED) {
 			mlen = sizeof(SUCCMSG) - 1;
 			sppp_auth_send(&pap, sp, PAP_ACK, h->ident,
 				       sizeof mlen, (const char *)&mlen,
 				       sizeof(SUCCMSG) - 1, (u_char *)SUCCMSG,
 				       0);
 		}
 		if (sp->state[IDX_PAP] == STATE_REQ_SENT) {
 			sppp_cp_change_state(&pap, sp, STATE_OPENED);
 			pap.tlu(sp);
 		}
 		break;
 
 	/* ack and nak are his authproto */
 	case PAP_ACK:
 		callout_stop(&sp->pap_my_to_ch);
 		if (debug) {
 			log(LOG_DEBUG, SPP_FMT "pap success",
 			    SPP_ARGS(ifp));
 			name_len = *((char *)h);
 			if (len > 5 && name_len) {
 				log(-1, ": ");
 				sppp_print_string((char*)(h+1), name_len);
 			}
 			log(-1, "\n");
 		}
 		SPPP_LOCK(sp);
 		sp->pp_flags &= ~PP_NEEDAUTH;
 		if (sp->myauth.proto == PPP_PAP &&
 		    (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) &&
 		    (sp->lcp.protos & (1 << IDX_PAP)) == 0) {
 			/*
 			 * We are authenticator for PAP but didn't
 			 * complete yet.  Leave it to tlu to proceed
 			 * to network phase.
 			 */
 			SPPP_UNLOCK(sp);
 			break;
 		}
 		SPPP_UNLOCK(sp);
 		sppp_phase_network(sp);
 		break;
 
 	case PAP_NAK:
 		callout_stop (&sp->pap_my_to_ch);
 		if (debug) {
 			log(LOG_INFO, SPP_FMT "pap failure",
 			    SPP_ARGS(ifp));
 			name_len = *((char *)h);
 			if (len > 5 && name_len) {
 				log(-1, ": ");
 				sppp_print_string((char*)(h+1), name_len);
 			}
 			log(-1, "\n");
 		} else
 			log(LOG_INFO, SPP_FMT "pap failure\n",
 			    SPP_ARGS(ifp));
 		/* await LCP shutdown by authenticator */
 		break;
 
 	default:
 		/* Unknown PAP packet type -- ignore. */
 		if (debug) {
 			log(LOG_DEBUG, SPP_FMT "pap corrupted input "
 			    "<0x%x id=0x%x len=%d",
 			    SPP_ARGS(ifp),
 			    h->type, h->ident, ntohs(h->len));
 			sppp_print_bytes((u_char*)(h+1), len-4);
 			log(-1, ">\n");
 		}
 		break;
 	}
 }
 
 static void
 sppp_pap_init(struct sppp *sp)
 {
 	/* PAP doesn't have STATE_INITIAL at all. */
 	sp->state[IDX_PAP] = STATE_CLOSED;
 	sp->fail_counter[IDX_PAP] = 0;
 	sp->pp_seq[IDX_PAP] = 0;
 	sp->pp_rseq[IDX_PAP] = 0;
  	callout_init(&sp->ch[IDX_PAP], 1);
  	callout_init(&sp->pap_my_to_ch, 1);
 }
 
 static void
 sppp_pap_open(struct sppp *sp)
 {
 	if (sp->hisauth.proto == PPP_PAP &&
 	    (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0) {
 		/* we are authenticator for PAP, start our timer */
 		sp->rst_counter[IDX_PAP] = sp->lcp.max_configure;
 		sppp_cp_change_state(&pap, sp, STATE_REQ_SENT);
 	}
 	if (sp->myauth.proto == PPP_PAP) {
 		/* we are peer, send a request, and start a timer */
 		pap.scr(sp);
 		callout_reset(&sp->pap_my_to_ch, sp->lcp.timeout,
 			      sppp_pap_my_TO, (void *)sp);
 	}
 }
 
 static void
 sppp_pap_close(struct sppp *sp)
 {
 	if (sp->state[IDX_PAP] != STATE_CLOSED)
 		sppp_cp_change_state(&pap, sp, STATE_CLOSED);
 }
 
 /*
  * That's the timeout routine if we are authenticator.  Since the
  * authenticator is basically passive in PAP, we can't do much here.
  */
 static void
 sppp_pap_TO(void *cookie)
 {
 	struct sppp *sp = (struct sppp *)cookie;
 	STDDCL;
 
 	SPPP_LOCK(sp);
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "pap TO(%s) rst_counter = %d\n",
 		    SPP_ARGS(ifp),
 		    sppp_state_name(sp->state[IDX_PAP]),
 		    sp->rst_counter[IDX_PAP]);
 
 	if (--sp->rst_counter[IDX_PAP] < 0)
 		/* TO- event */
 		switch (sp->state[IDX_PAP]) {
 		case STATE_REQ_SENT:
 			pap.tld(sp);
 			sppp_cp_change_state(&pap, sp, STATE_CLOSED);
 			break;
 		}
 	else
 		/* TO+ event, not very much we could do */
 		switch (sp->state[IDX_PAP]) {
 		case STATE_REQ_SENT:
 			/* sppp_cp_change_state() will restart the timer */
 			sppp_cp_change_state(&pap, sp, STATE_REQ_SENT);
 			break;
 		}
 
 	SPPP_UNLOCK(sp);
 }
 
 /*
  * That's the timeout handler if we are peer.  Since the peer is active,
  * we need to retransmit our PAP request since it is apparently lost.
  * XXX We should impose a max counter.
  */
 static void
 sppp_pap_my_TO(void *cookie)
 {
 	struct sppp *sp = (struct sppp *)cookie;
 	STDDCL;
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "pap peer TO\n",
 		    SPP_ARGS(ifp));
 
 	SPPP_LOCK(sp);
 	pap.scr(sp);
 	SPPP_UNLOCK(sp);
 }
 
 static void
 sppp_pap_tlu(struct sppp *sp)
 {
 	STDDCL;
 
 	sp->rst_counter[IDX_PAP] = sp->lcp.max_configure;
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "%s tlu\n",
 		    SPP_ARGS(ifp), pap.name);
 
 	SPPP_LOCK(sp);
 	/* indicate to LCP that we need to be closed down */
 	sp->lcp.protos |= (1 << IDX_PAP);
 
 	if (sp->pp_flags & PP_NEEDAUTH) {
 		/*
 		 * Remote is authenticator, but his auth proto didn't
 		 * complete yet.  Defer the transition to network
 		 * phase.
 		 */
 		SPPP_UNLOCK(sp);
 		return;
 	}
 	SPPP_UNLOCK(sp);
 	sppp_phase_network(sp);
 }
 
 static void
 sppp_pap_tld(struct sppp *sp)
 {
 	STDDCL;
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "pap tld\n", SPP_ARGS(ifp));
 	callout_stop (&sp->ch[IDX_PAP]);
 	callout_stop (&sp->pap_my_to_ch);
 	sp->lcp.protos &= ~(1 << IDX_PAP);
 
 	lcp.Close(sp);
 }
 
 static void
 sppp_pap_scr(struct sppp *sp)
 {
 	u_char idlen, pwdlen;
 
 	sp->confid[IDX_PAP] = ++sp->pp_seq[IDX_PAP];
 	pwdlen = sppp_strnlen(sp->myauth.secret, AUTHKEYLEN);
 	idlen = sppp_strnlen(sp->myauth.name, AUTHNAMELEN);
 
 	sppp_auth_send(&pap, sp, PAP_REQ, sp->confid[IDX_PAP],
 		       sizeof idlen, (const char *)&idlen,
 		       (size_t)idlen, sp->myauth.name,
 		       sizeof pwdlen, (const char *)&pwdlen,
 		       (size_t)pwdlen, sp->myauth.secret,
 		       0);
 }
 
 /*
  * Random miscellaneous functions.
  */
 
 /*
  * Send a PAP or CHAP proto packet.
  *
  * Varadic function, each of the elements for the ellipsis is of type
  * ``size_t mlen, const u_char *msg''.  Processing will stop iff
  * mlen == 0.
  * NOTE: never declare variadic functions with types subject to type
  * promotion (i.e. u_char). This is asking for big trouble depending
  * on the architecture you are on...
  */
 
 static void
 sppp_auth_send(const struct cp *cp, struct sppp *sp,
                unsigned int type, unsigned int id,
 	       ...)
 {
 	STDDCL;
 	struct ppp_header *h;
 	struct lcp_header *lh;
 	struct mbuf *m;
 	u_char *p;
 	int len;
 	unsigned int mlen;
 	const char *msg;
 	va_list ap;
 
 	MGETHDR (m, M_NOWAIT, MT_DATA);
 	if (! m)
 		return;
 	m->m_pkthdr.rcvif = 0;
 
 	h = mtod (m, struct ppp_header*);
 	h->address = PPP_ALLSTATIONS;		/* broadcast address */
 	h->control = PPP_UI;			/* Unnumbered Info */
 	h->protocol = htons(cp->proto);
 
 	lh = (struct lcp_header*)(h + 1);
 	lh->type = type;
 	lh->ident = id;
 	p = (u_char*) (lh+1);
 
 	va_start(ap, id);
 	len = 0;
 
 	while ((mlen = (unsigned int)va_arg(ap, size_t)) != 0) {
 		msg = va_arg(ap, const char *);
 		len += mlen;
 		if (len > MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN) {
 			va_end(ap);
 			m_freem(m);
 			return;
 		}
 
 		bcopy(msg, p, mlen);
 		p += mlen;
 	}
 	va_end(ap);
 
 	m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + LCP_HEADER_LEN + len;
 	lh->len = htons (LCP_HEADER_LEN + len);
 
 	if (debug) {
 		log(LOG_DEBUG, SPP_FMT "%s output <%s id=0x%x len=%d",
 		    SPP_ARGS(ifp), cp->name,
 		    sppp_auth_type_name(cp->proto, lh->type),
 		    lh->ident, ntohs(lh->len));
 		sppp_print_bytes((u_char*) (lh+1), len);
 		log(-1, ">\n");
 	}
 	if (! IF_HANDOFF_ADJ(&sp->pp_cpq, m, ifp, 3))
 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
 }
 
 /*
  * Flush interface queue.
  */
 static void
 sppp_qflush(struct ifqueue *ifq)
 {
 	struct mbuf *m, *n;
 
 	n = ifq->ifq_head;
 	while ((m = n)) {
 		n = m->m_nextpkt;
 		m_freem (m);
 	}
 	ifq->ifq_head = 0;
 	ifq->ifq_tail = 0;
 	ifq->ifq_len = 0;
 }
 
 /*
  * Send keepalive packets, every 10 seconds.
  */
 static void
 sppp_keepalive(void *dummy)
 {
 	struct sppp *sp = (struct sppp*)dummy;
 	struct ifnet *ifp = SP2IFP(sp);
 
 	SPPP_LOCK(sp);
 	/* Keepalive mode disabled or channel down? */
 	if (! (sp->pp_flags & PP_KEEPALIVE) ||
 	    ! (ifp->if_drv_flags & IFF_DRV_RUNNING))
 		goto out;
 
 	if (sp->pp_mode == PP_FR) {
 		sppp_fr_keepalive (sp);
 		goto out;
 	}
 
 	/* No keepalive in PPP mode if LCP not opened yet. */
 	if (sp->pp_mode != IFF_CISCO &&
 	    sp->pp_phase < PHASE_AUTHENTICATE)
 		goto out;
 
 	if (sp->pp_alivecnt == MAXALIVECNT) {
 		/* No keepalive packets got.  Stop the interface. */
 		printf (SPP_FMT "down\n", SPP_ARGS(ifp));
 		if_down (ifp);
 		sppp_qflush (&sp->pp_cpq);
 		if (sp->pp_mode != IFF_CISCO) {
 			/* XXX */
 			/* Shut down the PPP link. */
 			lcp.Down(sp);
 			/* Initiate negotiation. XXX */
 			lcp.Up(sp);
 		}
 	}
 	if (sp->pp_alivecnt <= MAXALIVECNT)
 		++sp->pp_alivecnt;
 	if (sp->pp_mode == IFF_CISCO)
 		sppp_cisco_send (sp, CISCO_KEEPALIVE_REQ,
 			 ++sp->pp_seq[IDX_LCP],	sp->pp_rseq[IDX_LCP]);
 	else if (sp->pp_phase >= PHASE_AUTHENTICATE) {
 		uint32_t nmagic = htonl(sp->lcp.magic);
 		sp->lcp.echoid = ++sp->pp_seq[IDX_LCP];
 		sppp_cp_send (sp, PPP_LCP, ECHO_REQ,
 			sp->lcp.echoid, 4, &nmagic);
 	}
 out:
 	SPPP_UNLOCK(sp);
  	callout_reset(&sp->keepalive_callout, hz * 10, sppp_keepalive,
 		      (void *)sp);
 }
 
 /*
  * Get both IP addresses.
  */
 void
 sppp_get_ip_addrs(struct sppp *sp, u_long *src, u_long *dst, u_long *srcmask)
 {
 	struct epoch_tracker et;
 	struct ifnet *ifp = SP2IFP(sp);
 	struct ifaddr *ifa;
 	struct sockaddr_in *si, *sm;
 	u_long ssrc, ddst;
 
 	sm = NULL;
 	ssrc = ddst = 0L;
 	/*
 	 * Pick the first AF_INET address from the list,
 	 * aliases don't make any sense on a p2p link anyway.
 	 */
 	si = NULL;
 	NET_EPOCH_ENTER(et);
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
 		if (ifa->ifa_addr->sa_family == AF_INET) {
 			si = (struct sockaddr_in *)ifa->ifa_addr;
 			sm = (struct sockaddr_in *)ifa->ifa_netmask;
 			if (si)
 				break;
 		}
 	if (ifa) {
 		if (si && si->sin_addr.s_addr) {
 			ssrc = si->sin_addr.s_addr;
 			if (srcmask)
 				*srcmask = ntohl(sm->sin_addr.s_addr);
 		}
 
 		si = (struct sockaddr_in *)ifa->ifa_dstaddr;
 		if (si && si->sin_addr.s_addr)
 			ddst = si->sin_addr.s_addr;
 	}
 	NET_EPOCH_EXIT(et);
 
 	if (dst) *dst = ntohl(ddst);
 	if (src) *src = ntohl(ssrc);
 }
 
 #ifdef INET
 /*
  * Set my IP address.
  */
 static void
 sppp_set_ip_addr(struct sppp *sp, u_long src)
 {
 	STDDCL;
 	struct epoch_tracker et;
 	struct ifaddr *ifa;
 	struct sockaddr_in *si;
 	struct in_ifaddr *ia;
 
 	/*
 	 * Pick the first AF_INET address from the list,
 	 * aliases don't make any sense on a p2p link anyway.
 	 */
 	si = NULL;
 	NET_EPOCH_ENTER(et);
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 		if (ifa->ifa_addr->sa_family == AF_INET) {
 			si = (struct sockaddr_in *)ifa->ifa_addr;
 			if (si != NULL) {
 				ifa_ref(ifa);
 				break;
 			}
 		}
 	}
 	NET_EPOCH_EXIT(et);
 
 	if (ifa != NULL) {
 		int error;
+		int fibnum = ifp->if_fib;
 
+		rt_addrmsg(RTM_DELETE, ifa, fibnum);
 		/* delete old route */
-		error = rtinit(ifa, (int)RTM_DELETE, RTF_HOST);
+		ia = ifatoia(ifa);
+		error = in_handle_ifaddr_route(RTM_DELETE, ia);
 		if (debug && error) {
 			log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addr: rtinit DEL failed, error=%d\n",
 		    		SPP_ARGS(ifp), error);
 		}
 
 		/* set new address */
 		si->sin_addr.s_addr = htonl(src);
-		ia = ifatoia(ifa);
 		IN_IFADDR_WLOCK();
 		LIST_REMOVE(ia, ia_hash);
 		LIST_INSERT_HEAD(INADDR_HASH(si->sin_addr.s_addr), ia, ia_hash);
 		IN_IFADDR_WUNLOCK();
 
+		rt_addrmsg(RTM_ADD, ifa, fibnum);
 		/* add new route */
-		error = rtinit(ifa, (int)RTM_ADD, RTF_HOST);
+		error = in_handle_ifaddr_route(RTM_ADD, ia);
 		if (debug && error) {
 			log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addr: rtinit ADD failed, error=%d",
 		    		SPP_ARGS(ifp), error);
 		}
 		ifa_free(ifa);
 	}
 }
 #endif
 
 #ifdef INET6
 /*
  * Get both IPv6 addresses.
  */
 static void
 sppp_get_ip6_addrs(struct sppp *sp, struct in6_addr *src, struct in6_addr *dst,
 		   struct in6_addr *srcmask)
 {
 	struct epoch_tracker et;
 	struct ifnet *ifp = SP2IFP(sp);
 	struct ifaddr *ifa;
 	struct sockaddr_in6 *si, *sm;
 	struct in6_addr ssrc, ddst;
 
 	sm = NULL;
 	bzero(&ssrc, sizeof(ssrc));
 	bzero(&ddst, sizeof(ddst));
 	/*
 	 * Pick the first link-local AF_INET6 address from the list,
 	 * aliases don't make any sense on a p2p link anyway.
 	 */
 	si = NULL;
 	NET_EPOCH_ENTER(et);
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
 		if (ifa->ifa_addr->sa_family == AF_INET6) {
 			si = (struct sockaddr_in6 *)ifa->ifa_addr;
 			sm = (struct sockaddr_in6 *)ifa->ifa_netmask;
 			if (si && IN6_IS_ADDR_LINKLOCAL(&si->sin6_addr))
 				break;
 		}
 	if (ifa) {
 		if (si && !IN6_IS_ADDR_UNSPECIFIED(&si->sin6_addr)) {
 			bcopy(&si->sin6_addr, &ssrc, sizeof(ssrc));
 			if (srcmask) {
 				bcopy(&sm->sin6_addr, srcmask,
 				      sizeof(*srcmask));
 			}
 		}
 
 		si = (struct sockaddr_in6 *)ifa->ifa_dstaddr;
 		if (si && !IN6_IS_ADDR_UNSPECIFIED(&si->sin6_addr))
 			bcopy(&si->sin6_addr, &ddst, sizeof(ddst));
 	}
 
 	if (dst)
 		bcopy(&ddst, dst, sizeof(*dst));
 	if (src)
 		bcopy(&ssrc, src, sizeof(*src));
 	NET_EPOCH_EXIT(et);
 }
 
 #ifdef IPV6CP_MYIFID_DYN
 /*
  * Generate random ifid.
  */
 static void
 sppp_gen_ip6_addr(struct sppp *sp, struct in6_addr *addr)
 {
 	/* TBD */
 }
 
 /*
  * Set my IPv6 address.
  */
 static void
 sppp_set_ip6_addr(struct sppp *sp, const struct in6_addr *src)
 {
 	STDDCL;
 	struct epoch_tracker et;
 	struct ifaddr *ifa;
 	struct sockaddr_in6 *sin6;
 
 	/*
 	 * Pick the first link-local AF_INET6 address from the list,
 	 * aliases don't make any sense on a p2p link anyway.
 	 */
 
 	sin6 = NULL;
 	NET_EPOCH_ENTER(et);
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 		if (ifa->ifa_addr->sa_family == AF_INET6) {
 			sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
 			if (sin6 && IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
 				ifa_ref(ifa);
 				break;
 			}
 		}
 	}
 	NET_EPOCH_EXIT(et);
 
 	if (ifa != NULL) {
 		int error;
 		struct sockaddr_in6 new_sin6 = *sin6;
 
 		bcopy(src, &new_sin6.sin6_addr, sizeof(new_sin6.sin6_addr));
 		error = in6_ifinit(ifp, ifatoia6(ifa), &new_sin6, 1);
 		if (debug && error) {
 			log(LOG_DEBUG, SPP_FMT "sppp_set_ip6_addr: in6_ifinit "
 			    " failed, error=%d\n", SPP_ARGS(ifp), error);
 		}
 		ifa_free(ifa);
 	}
 }
 #endif
 
 /*
  * Suggest a candidate address to be used by peer.
  */
 static void
 sppp_suggest_ip6_addr(struct sppp *sp, struct in6_addr *suggest)
 {
 	struct in6_addr myaddr;
 	struct timeval tv;
 
 	sppp_get_ip6_addrs(sp, &myaddr, 0, 0);
 
 	myaddr.s6_addr[8] &= ~0x02;	/* u bit to "local" */
 	microtime(&tv);
 	if ((tv.tv_usec & 0xff) == 0 && (tv.tv_sec & 0xff) == 0) {
 		myaddr.s6_addr[14] ^= 0xff;
 		myaddr.s6_addr[15] ^= 0xff;
 	} else {
 		myaddr.s6_addr[14] ^= (tv.tv_usec & 0xff);
 		myaddr.s6_addr[15] ^= (tv.tv_sec & 0xff);
 	}
 	if (suggest)
 		bcopy(&myaddr, suggest, sizeof(myaddr));
 }
 #endif /*INET6*/
 
 static int
 sppp_params(struct sppp *sp, u_long cmd, void *data)
 {
 	u_long subcmd;
 	struct ifreq *ifr = (struct ifreq *)data;
 	struct spppreq *spr;
 	int rv = 0;
 
 	if ((spr = malloc(sizeof(struct spppreq), M_TEMP, M_NOWAIT)) == NULL)
 		return (EAGAIN);
 	/*
 	 * ifr_data_get_ptr(ifr) is supposed to point to a struct spppreq.
 	 * Check the cmd word first before attempting to fetch all the
 	 * data.
 	 */
 	rv = fueword(ifr_data_get_ptr(ifr), &subcmd);
 	if (rv == -1) {
 		rv = EFAULT;
 		goto quit;
 	}
 
 	if (copyin(ifr_data_get_ptr(ifr), spr, sizeof(struct spppreq)) != 0) {
 		rv = EFAULT;
 		goto quit;
 	}
 
 	switch (subcmd) {
 	case (u_long)SPPPIOGDEFS:
 		if (cmd != SIOCGIFGENERIC) {
 			rv = EINVAL;
 			break;
 		}
 		/*
 		 * We copy over the entire current state, but clean
 		 * out some of the stuff we don't wanna pass up.
 		 * Remember, SIOCGIFGENERIC is unprotected, and can be
 		 * called by any user.  No need to ever get PAP or
 		 * CHAP secrets back to userland anyway.
 		 */
 		spr->defs.pp_phase = sp->pp_phase;
 		spr->defs.enable_vj = (sp->confflags & CONF_ENABLE_VJ) != 0;
 		spr->defs.enable_ipv6 = (sp->confflags & CONF_ENABLE_IPV6) != 0;
 		spr->defs.lcp = sp->lcp;
 		spr->defs.ipcp = sp->ipcp;
 		spr->defs.ipv6cp = sp->ipv6cp;
 		spr->defs.myauth = sp->myauth;
 		spr->defs.hisauth = sp->hisauth;
 		bzero(spr->defs.myauth.secret, AUTHKEYLEN);
 		bzero(spr->defs.myauth.challenge, AUTHKEYLEN);
 		bzero(spr->defs.hisauth.secret, AUTHKEYLEN);
 		bzero(spr->defs.hisauth.challenge, AUTHKEYLEN);
 		/*
 		 * Fixup the LCP timeout value to milliseconds so
 		 * spppcontrol doesn't need to bother about the value
 		 * of "hz".  We do the reverse calculation below when
 		 * setting it.
 		 */
 		spr->defs.lcp.timeout = sp->lcp.timeout * 1000 / hz;
 		rv = copyout(spr, ifr_data_get_ptr(ifr),
 		    sizeof(struct spppreq));
 		break;
 
 	case (u_long)SPPPIOSDEFS:
 		if (cmd != SIOCSIFGENERIC) {
 			rv = EINVAL;
 			break;
 		}
 		/*
 		 * We have a very specific idea of which fields we
 		 * allow being passed back from userland, so to not
 		 * clobber our current state.  For one, we only allow
 		 * setting anything if LCP is in dead or establish
 		 * phase.  Once the authentication negotiations
 		 * started, the authentication settings must not be
 		 * changed again.  (The administrator can force an
 		 * ifconfig down in order to get LCP back into dead
 		 * phase.)
 		 *
 		 * Also, we only allow for authentication parameters to be
 		 * specified.
 		 *
 		 * XXX Should allow to set or clear pp_flags.
 		 *
 		 * Finally, if the respective authentication protocol to
 		 * be used is set differently than 0, but the secret is
 		 * passed as all zeros, we don't trash the existing secret.
 		 * This allows an administrator to change the system name
 		 * only without clobbering the secret (which he didn't get
 		 * back in a previous SPPPIOGDEFS call).  However, the
 		 * secrets are cleared if the authentication protocol is
 		 * reset to 0.  */
 		if (sp->pp_phase != PHASE_DEAD &&
 		    sp->pp_phase != PHASE_ESTABLISH) {
 			rv = EBUSY;
 			break;
 		}
 
 		if ((spr->defs.myauth.proto != 0 && spr->defs.myauth.proto != PPP_PAP &&
 		     spr->defs.myauth.proto != PPP_CHAP) ||
 		    (spr->defs.hisauth.proto != 0 && spr->defs.hisauth.proto != PPP_PAP &&
 		     spr->defs.hisauth.proto != PPP_CHAP)) {
 			rv = EINVAL;
 			break;
 		}
 
 		if (spr->defs.myauth.proto == 0)
 			/* resetting myauth */
 			bzero(&sp->myauth, sizeof sp->myauth);
 		else {
 			/* setting/changing myauth */
 			sp->myauth.proto = spr->defs.myauth.proto;
 			bcopy(spr->defs.myauth.name, sp->myauth.name, AUTHNAMELEN);
 			if (spr->defs.myauth.secret[0] != '\0')
 				bcopy(spr->defs.myauth.secret, sp->myauth.secret,
 				      AUTHKEYLEN);
 		}
 		if (spr->defs.hisauth.proto == 0)
 			/* resetting hisauth */
 			bzero(&sp->hisauth, sizeof sp->hisauth);
 		else {
 			/* setting/changing hisauth */
 			sp->hisauth.proto = spr->defs.hisauth.proto;
 			sp->hisauth.flags = spr->defs.hisauth.flags;
 			bcopy(spr->defs.hisauth.name, sp->hisauth.name, AUTHNAMELEN);
 			if (spr->defs.hisauth.secret[0] != '\0')
 				bcopy(spr->defs.hisauth.secret, sp->hisauth.secret,
 				      AUTHKEYLEN);
 		}
 		/* set LCP restart timer timeout */
 		if (spr->defs.lcp.timeout != 0)
 			sp->lcp.timeout = spr->defs.lcp.timeout * hz / 1000;
 		/* set VJ enable and IPv6 disable flags */
 #ifdef INET
 		if (spr->defs.enable_vj)
 			sp->confflags |= CONF_ENABLE_VJ;
 		else
 			sp->confflags &= ~CONF_ENABLE_VJ;
 #endif
 #ifdef INET6
 		if (spr->defs.enable_ipv6)
 			sp->confflags |= CONF_ENABLE_IPV6;
 		else
 			sp->confflags &= ~CONF_ENABLE_IPV6;
 #endif
 		break;
 
 	default:
 		rv = EINVAL;
 	}
 
  quit:
 	free(spr, M_TEMP);
 
 	return (rv);
 }
 
 static void
 sppp_phase_network(struct sppp *sp)
 {
 	STDDCL;
 	int i;
 	u_long mask;
 
 	sp->pp_phase = PHASE_NETWORK;
 
 	if (debug)
 		log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp),
 		    sppp_phase_name(sp->pp_phase));
 
 	/* Notify NCPs now. */
 	for (i = 0; i < IDX_COUNT; i++)
 		if ((cps[i])->flags & CP_NCP)
 			(cps[i])->Open(sp);
 
 	/* Send Up events to all NCPs. */
 	for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1)
 		if ((sp->lcp.protos & mask) && ((cps[i])->flags & CP_NCP))
 			(cps[i])->Up(sp);
 
 	/* if no NCP is starting, all this was in vain, close down */
 	sppp_lcp_check_and_close(sp);
 }
 
 static const char *
 sppp_cp_type_name(u_char type)
 {
 	static char buf[12];
 	switch (type) {
 	case CONF_REQ:   return "conf-req";
 	case CONF_ACK:   return "conf-ack";
 	case CONF_NAK:   return "conf-nak";
 	case CONF_REJ:   return "conf-rej";
 	case TERM_REQ:   return "term-req";
 	case TERM_ACK:   return "term-ack";
 	case CODE_REJ:   return "code-rej";
 	case PROTO_REJ:  return "proto-rej";
 	case ECHO_REQ:   return "echo-req";
 	case ECHO_REPLY: return "echo-reply";
 	case DISC_REQ:   return "discard-req";
 	}
 	snprintf (buf, sizeof(buf), "cp/0x%x", type);
 	return buf;
 }
 
 static const char *
 sppp_auth_type_name(u_short proto, u_char type)
 {
 	static char buf[12];
 	switch (proto) {
 	case PPP_CHAP:
 		switch (type) {
 		case CHAP_CHALLENGE:	return "challenge";
 		case CHAP_RESPONSE:	return "response";
 		case CHAP_SUCCESS:	return "success";
 		case CHAP_FAILURE:	return "failure";
 		}
 	case PPP_PAP:
 		switch (type) {
 		case PAP_REQ:		return "req";
 		case PAP_ACK:		return "ack";
 		case PAP_NAK:		return "nak";
 		}
 	}
 	snprintf (buf, sizeof(buf), "auth/0x%x", type);
 	return buf;
 }
 
 static const char *
 sppp_lcp_opt_name(u_char opt)
 {
 	static char buf[12];
 	switch (opt) {
 	case LCP_OPT_MRU:		return "mru";
 	case LCP_OPT_ASYNC_MAP:		return "async-map";
 	case LCP_OPT_AUTH_PROTO:	return "auth-proto";
 	case LCP_OPT_QUAL_PROTO:	return "qual-proto";
 	case LCP_OPT_MAGIC:		return "magic";
 	case LCP_OPT_PROTO_COMP:	return "proto-comp";
 	case LCP_OPT_ADDR_COMP:		return "addr-comp";
 	}
 	snprintf (buf, sizeof(buf), "lcp/0x%x", opt);
 	return buf;
 }
 
 #ifdef INET
 static const char *
 sppp_ipcp_opt_name(u_char opt)
 {
 	static char buf[12];
 	switch (opt) {
 	case IPCP_OPT_ADDRESSES:	return "addresses";
 	case IPCP_OPT_COMPRESSION:	return "compression";
 	case IPCP_OPT_ADDRESS:		return "address";
 	}
 	snprintf (buf, sizeof(buf), "ipcp/0x%x", opt);
 	return buf;
 }
 #endif
 
 #ifdef INET6
 static const char *
 sppp_ipv6cp_opt_name(u_char opt)
 {
 	static char buf[12];
 	switch (opt) {
 	case IPV6CP_OPT_IFID:		return "ifid";
 	case IPV6CP_OPT_COMPRESSION:	return "compression";
 	}
 	sprintf (buf, "0x%x", opt);
 	return buf;
 }
 #endif
 
 static const char *
 sppp_state_name(int state)
 {
 	switch (state) {
 	case STATE_INITIAL:	return "initial";
 	case STATE_STARTING:	return "starting";
 	case STATE_CLOSED:	return "closed";
 	case STATE_STOPPED:	return "stopped";
 	case STATE_CLOSING:	return "closing";
 	case STATE_STOPPING:	return "stopping";
 	case STATE_REQ_SENT:	return "req-sent";
 	case STATE_ACK_RCVD:	return "ack-rcvd";
 	case STATE_ACK_SENT:	return "ack-sent";
 	case STATE_OPENED:	return "opened";
 	}
 	return "illegal";
 }
 
 static const char *
 sppp_phase_name(enum ppp_phase phase)
 {
 	switch (phase) {
 	case PHASE_DEAD:	return "dead";
 	case PHASE_ESTABLISH:	return "establish";
 	case PHASE_TERMINATE:	return "terminate";
 	case PHASE_AUTHENTICATE: return "authenticate";
 	case PHASE_NETWORK:	return "network";
 	}
 	return "illegal";
 }
 
 static const char *
 sppp_proto_name(u_short proto)
 {
 	static char buf[12];
 	switch (proto) {
 	case PPP_LCP:	return "lcp";
 	case PPP_IPCP:	return "ipcp";
 	case PPP_PAP:	return "pap";
 	case PPP_CHAP:	return "chap";
 	case PPP_IPV6CP: return "ipv6cp";
 	}
 	snprintf(buf, sizeof(buf), "proto/0x%x", (unsigned)proto);
 	return buf;
 }
 
 static void
 sppp_print_bytes(const u_char *p, u_short len)
 {
 	if (len)
 		log(-1, " %*D", len, p, "-");
 }
 
 static void
 sppp_print_string(const char *p, u_short len)
 {
 	u_char c;
 
 	while (len-- > 0) {
 		c = *p++;
 		/*
 		 * Print only ASCII chars directly.  RFC 1994 recommends
 		 * using only them, but we don't rely on it.  */
 		if (c < ' ' || c > '~')
 			log(-1, "\\x%x", c);
 		else
 			log(-1, "%c", c);
 	}
 }
 
 #ifdef INET
 static const char *
 sppp_dotted_quad(u_long addr)
 {
 	static char s[16];
 	sprintf(s, "%d.%d.%d.%d",
 		(int)((addr >> 24) & 0xff),
 		(int)((addr >> 16) & 0xff),
 		(int)((addr >> 8) & 0xff),
 		(int)(addr & 0xff));
 	return s;
 }
 #endif
 
 static int
 sppp_strnlen(u_char *p, int max)
 {
 	int len;
 
 	for (len = 0; len < max && *p; ++p)
 		++len;
 	return len;
 }
 
 /* a dummy, used to drop uninteresting events */
 static void
 sppp_null(struct sppp *unused)
 {
 	/* do just nothing */
 }
diff --git a/sys/net/route.c b/sys/net/route.c
index b3383f90789b..7e087569d45f 100644
--- a/sys/net/route.c
+++ b/sys/net/route.c
@@ -1,793 +1,769 @@
 /*-
  * SPDX-License-Identifier: BSD-3-Clause
  *
  * Copyright (c) 1980, 1986, 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  *	@(#)route.c	8.3.1.1 (Berkeley) 2/23/95
  * $FreeBSD$
  */
 /************************************************************************
  * Note: In this file a 'fib' is a "forwarding information base"	*
  * Which is the new name for an in kernel routing (next hop) table.	*
  ***********************************************************************/
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
 #include "opt_mrouting.h"
 #include "opt_route.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/malloc.h>
 #include <sys/mbuf.h>
 #include <sys/socket.h>
 #include <sys/sysctl.h>
 #include <sys/syslog.h>
 #include <sys/sysproto.h>
 #include <sys/proc.h>
 #include <sys/domain.h>
 #include <sys/eventhandler.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/rmlock.h>
 
 #include <net/if.h>
 #include <net/if_var.h>
 #include <net/if_dl.h>
 #include <net/route.h>
 #include <net/route/route_ctl.h>
 #include <net/route/route_var.h>
 #include <net/route/nhop.h>
 #include <net/vnet.h>
 
 #include <netinet/in.h>
 #include <netinet/ip_mroute.h>
 
 VNET_PCPUSTAT_DEFINE(struct rtstat, rtstat);
 
 VNET_PCPUSTAT_SYSINIT(rtstat);
 #ifdef VIMAGE
 VNET_PCPUSTAT_SYSUNINIT(rtstat);
 #endif
 
 EVENTHANDLER_LIST_DEFINE(rt_addrmsg);
 
 static int rt_ifdelroute(const struct rtentry *rt, const struct nhop_object *,
     void *arg);
 static int rt_exportinfo(struct rtentry *rt, struct rt_addrinfo *info,
     int flags);
 
 /*
  * route initialization must occur before ip6_init2(), which happenas at
  * SI_ORDER_MIDDLE.
  */
 static void
 route_init(void)
 {
 
 	nhops_init();
 }
 SYSINIT(route_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, route_init, NULL);
 
 struct rib_head *
 rt_table_init(int offset, int family, u_int fibnum)
 {
 	struct rib_head *rh;
 
 	rh = malloc(sizeof(struct rib_head), M_RTABLE, M_WAITOK | M_ZERO);
 
 	/* TODO: These details should be hidded inside radix.c */
 	/* Init masks tree */
 	rn_inithead_internal(&rh->head, rh->rnh_nodes, offset);
 	rn_inithead_internal(&rh->rmhead.head, rh->rmhead.mask_nodes, 0);
 	rh->head.rnh_masks = &rh->rmhead;
 
 	/* Save metadata associated with this routing table. */
 	rh->rib_family = family;
 	rh->rib_fibnum = fibnum;
 #ifdef VIMAGE
 	rh->rib_vnet = curvnet;
 #endif
 
 	tmproutes_init(rh);
 
 	/* Init locks */
 	RIB_LOCK_INIT(rh);
 
 	nhops_init_rib(rh);
 
 	/* Init subscription system */
 	rib_init_subscriptions(rh);
 
 	/* Finally, set base callbacks */
 	rh->rnh_addaddr = rn_addroute;
 	rh->rnh_deladdr = rn_delete;
 	rh->rnh_matchaddr = rn_match;
 	rh->rnh_lookup = rn_lookup;
 	rh->rnh_walktree = rn_walktree;
 	rh->rnh_walktree_from = rn_walktree_from;
 
 	return (rh);
 }
 
 static int
 rt_freeentry(struct radix_node *rn, void *arg)
 {
 	struct radix_head * const rnh = arg;
 	struct radix_node *x;
 
 	x = (struct radix_node *)rn_delete(rn + 2, NULL, rnh);
 	if (x != NULL)
 		R_Free(x);
 	return (0);
 }
 
 void
 rt_table_destroy(struct rib_head *rh)
 {
 
 	RIB_WLOCK(rh);
 	rh->rib_dying = true;
 	RIB_WUNLOCK(rh);
 
 #ifdef FIB_ALGO
 	fib_destroy_rib(rh);
 #endif
 
 	tmproutes_destroy(rh);
 
 	rn_walktree(&rh->rmhead.head, rt_freeentry, &rh->rmhead.head);
 
 	nhops_destroy_rib(rh);
 
 	rib_destroy_subscriptions(rh);
 
 	/* Assume table is already empty */
 	RIB_LOCK_DESTROY(rh);
 	free(rh, M_RTABLE);
 }
 
 /*
  * Adds a temporal redirect entry to the routing table.
  * @fibnum: fib number
  * @dst: destination to install redirect to
  * @gateway: gateway to go via
  * @author: sockaddr of originating router, can be NULL
  * @ifp: interface to use for the redirected route
  * @flags: set of flags to add. Allowed: RTF_GATEWAY
  * @lifetime_sec: time in seconds to expire this redirect.
  *
  * Retuns 0 on success, errno otherwise.
  */
 int
 rib_add_redirect(u_int fibnum, struct sockaddr *dst, struct sockaddr *gateway,
     struct sockaddr *author, struct ifnet *ifp, int flags, int lifetime_sec)
 {
 	struct rib_cmd_info rc;
 	int error;
 	struct rt_addrinfo info;
 	struct rt_metrics rti_rmx;
 	struct ifaddr *ifa;
 
 	NET_EPOCH_ASSERT();
 
 	if (rt_tables_get_rnh(fibnum, dst->sa_family) == NULL)
 		return (EAFNOSUPPORT);
 
 	/* Verify the allowed flag mask. */
 	KASSERT(((flags & ~(RTF_GATEWAY)) == 0),
 	    ("invalid redirect flags: %x", flags));
 	flags |= RTF_HOST | RTF_DYNAMIC;
 
 	/* Get the best ifa for the given interface and gateway. */
 	if ((ifa = ifaof_ifpforaddr(gateway, ifp)) == NULL)
 		return (ENETUNREACH);
 	ifa_ref(ifa);
 
 	bzero(&info, sizeof(info));
 	info.rti_info[RTAX_DST] = dst;
 	info.rti_info[RTAX_GATEWAY] = gateway;
 	info.rti_ifa = ifa;
 	info.rti_ifp = ifp;
 	info.rti_flags = flags;
 
 	/* Setup route metrics to define expire time. */
 	bzero(&rti_rmx, sizeof(rti_rmx));
 	/* Set expire time as absolute. */
 	rti_rmx.rmx_expire = lifetime_sec + time_second;
 	info.rti_mflags |= RTV_EXPIRE;
 	info.rti_rmx = &rti_rmx;
 
 	error = rib_action(fibnum, RTM_ADD, &info, &rc);
 	ifa_free(ifa);
 
 	if (error != 0) {
 		/* TODO: add per-fib redirect stats. */
 		return (error);
 	}
 
 	RTSTAT_INC(rts_dynamic);
 
 	/* Send notification of a route addition to userland. */
 	bzero(&info, sizeof(info));
 	info.rti_info[RTAX_DST] = dst;
 	info.rti_info[RTAX_GATEWAY] = gateway;
 	info.rti_info[RTAX_AUTHOR] = author;
 	rt_missmsg_fib(RTM_REDIRECT, &info, flags | RTF_UP, error, fibnum);
 
 	return (0);
 }
 
 /*
  * Routing table ioctl interface.
  */
 int
 rtioctl_fib(u_long req, caddr_t data, u_int fibnum)
 {
 
 	/*
 	 * If more ioctl commands are added here, make sure the proper
 	 * super-user checks are being performed because it is possible for
 	 * prison-root to make it this far if raw sockets have been enabled
 	 * in jails.
 	 */
 #ifdef INET
 	/* Multicast goop, grrr... */
 	return mrt_ioctl ? mrt_ioctl(req, data, fibnum) : EOPNOTSUPP;
 #else /* INET */
 	return ENXIO;
 #endif /* INET */
 }
 
 struct ifaddr *
 ifa_ifwithroute(int flags, const struct sockaddr *dst,
     const struct sockaddr *gateway, u_int fibnum)
 {
 	struct ifaddr *ifa;
 
 	NET_EPOCH_ASSERT();
 	if ((flags & RTF_GATEWAY) == 0) {
 		/*
 		 * If we are adding a route to an interface,
 		 * and the interface is a pt to pt link
 		 * we should search for the destination
 		 * as our clue to the interface.  Otherwise
 		 * we can use the local address.
 		 */
 		ifa = NULL;
 		if (flags & RTF_HOST)
 			ifa = ifa_ifwithdstaddr(dst, fibnum);
 		if (ifa == NULL)
 			ifa = ifa_ifwithaddr(gateway);
 	} else {
 		/*
 		 * If we are adding a route to a remote net
 		 * or host, the gateway may still be on the
 		 * other end of a pt to pt link.
 		 */
 		ifa = ifa_ifwithdstaddr(gateway, fibnum);
 	}
 	if (ifa == NULL)
 		ifa = ifa_ifwithnet(gateway, 0, fibnum);
 	if (ifa == NULL) {
 		struct nhop_object *nh;
 
 		nh = rib_lookup(fibnum, gateway, NHR_NONE, 0);
 
 		/*
 		 * dismiss a gateway that is reachable only
 		 * through the default router
 		 */
 		if ((nh == NULL) || (nh->nh_flags & NHF_DEFAULT))
 			return (NULL);
 		ifa = nh->nh_ifa;
 	}
 	if (ifa->ifa_addr->sa_family != dst->sa_family) {
 		struct ifaddr *oifa = ifa;
 		ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp);
 		if (ifa == NULL)
 			ifa = oifa;
 	}
 
 	return (ifa);
 }
 
 /*
  * Copy most of @rt data into @info.
  *
  * If @flags contains NHR_COPY, copies dst,netmask and gw to the
  * pointers specified by @info structure. Assume such pointers
  * are zeroed sockaddr-like structures with sa_len field initialized
  * to reflect size of the provided buffer. if no NHR_COPY is specified,
  * point dst,netmask and gw @info fields to appropriate @rt values.
  *
  * if @flags contains NHR_REF, do refcouting on rt_ifp and rt_ifa.
  *
  * Returns 0 on success.
  */
 int
 rt_exportinfo(struct rtentry *rt, struct rt_addrinfo *info, int flags)
 {
 	struct rt_metrics *rmx;
 	struct sockaddr *src, *dst;
 	struct nhop_object *nh;
 	int sa_len;
 
 	nh = rt->rt_nhop;
 	if (flags & NHR_COPY) {
 		/* Copy destination if dst is non-zero */
 		src = rt_key(rt);
 		dst = info->rti_info[RTAX_DST];
 		sa_len = src->sa_len;
 		if (dst != NULL) {
 			if (src->sa_len > dst->sa_len)
 				return (ENOMEM);
 			memcpy(dst, src, src->sa_len);
 			info->rti_addrs |= RTA_DST;
 		}
 
 		/* Copy mask if set && dst is non-zero */
 		src = rt_mask(rt);
 		dst = info->rti_info[RTAX_NETMASK];
 		if (src != NULL && dst != NULL) {
 			/*
 			 * Radix stores different value in sa_len,
 			 * assume rt_mask() to have the same length
 			 * as rt_key()
 			 */
 			if (sa_len > dst->sa_len)
 				return (ENOMEM);
 			memcpy(dst, src, src->sa_len);
 			info->rti_addrs |= RTA_NETMASK;
 		}
 
 		/* Copy gateway is set && dst is non-zero */
 		src = &nh->gw_sa;
 		dst = info->rti_info[RTAX_GATEWAY];
 		if ((nhop_get_rtflags(nh) & RTF_GATEWAY) &&
 		    src != NULL && dst != NULL) {
 			if (src->sa_len > dst->sa_len)
 				return (ENOMEM);
 			memcpy(dst, src, src->sa_len);
 			info->rti_addrs |= RTA_GATEWAY;
 		}
 	} else {
 		info->rti_info[RTAX_DST] = rt_key(rt);
 		info->rti_addrs |= RTA_DST;
 		if (rt_mask(rt) != NULL) {
 			info->rti_info[RTAX_NETMASK] = rt_mask(rt);
 			info->rti_addrs |= RTA_NETMASK;
 		}
 		if (nhop_get_rtflags(nh) & RTF_GATEWAY) {
 			info->rti_info[RTAX_GATEWAY] = &nh->gw_sa;
 			info->rti_addrs |= RTA_GATEWAY;
 		}
 	}
 
 	rmx = info->rti_rmx;
 	if (rmx != NULL) {
 		info->rti_mflags |= RTV_MTU;
 		rmx->rmx_mtu = nh->nh_mtu;
 	}
 
 	info->rti_flags = rt->rte_flags | nhop_get_rtflags(nh);
 	info->rti_ifp = nh->nh_ifp;
 	info->rti_ifa = nh->nh_ifa;
 	if (flags & NHR_REF) {
 		if_ref(info->rti_ifp);
 		ifa_ref(info->rti_ifa);
 	}
 
 	return (0);
 }
 
 /*
  * Lookups up route entry for @dst in RIB database for fib @fibnum.
  * Exports entry data to @info using rt_exportinfo().
  *
  * If @flags contains NHR_REF, refcouting is performed on rt_ifp and rt_ifa.
  * All references can be released later by calling rib_free_info().
  *
  * Returns 0 on success.
  * Returns ENOENT for lookup failure, ENOMEM for export failure.
  */
 int
 rib_lookup_info(uint32_t fibnum, const struct sockaddr *dst, uint32_t flags,
     uint32_t flowid, struct rt_addrinfo *info)
 {
 	RIB_RLOCK_TRACKER;
 	struct rib_head *rh;
 	struct radix_node *rn;
 	struct rtentry *rt;
 	int error;
 
 	KASSERT((fibnum < rt_numfibs), ("rib_lookup_rte: bad fibnum"));
 	rh = rt_tables_get_rnh(fibnum, dst->sa_family);
 	if (rh == NULL)
 		return (ENOENT);
 
 	RIB_RLOCK(rh);
 	rn = rh->rnh_matchaddr(__DECONST(void *, dst), &rh->head);
 	if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
 		rt = RNTORT(rn);
 		/* Ensure route & ifp is UP */
 		if (RT_LINK_IS_UP(rt->rt_nhop->nh_ifp)) {
 			flags = (flags & NHR_REF) | NHR_COPY;
 			error = rt_exportinfo(rt, info, flags);
 			RIB_RUNLOCK(rh);
 
 			return (error);
 		}
 	}
 	RIB_RUNLOCK(rh);
 
 	return (ENOENT);
 }
 
 /*
  * Releases all references acquired by rib_lookup_info() when
  * called with NHR_REF flags.
  */
 void
 rib_free_info(struct rt_addrinfo *info)
 {
 
 	ifa_free(info->rti_ifa);
 	if_rele(info->rti_ifp);
 }
 
 /*
  * Delete Routes for a Network Interface
  *
  * Called for each routing entry via the rnh->rnh_walktree() call above
  * to delete all route entries referencing a detaching network interface.
  *
  * Arguments:
  *	rt	pointer to rtentry
  *	nh	pointer to nhop
  *	arg	argument passed to rnh->rnh_walktree() - detaching interface
  *
  * Returns:
  *	0	successful
  *	errno	failed - reason indicated
  */
 static int
 rt_ifdelroute(const struct rtentry *rt, const struct nhop_object *nh, void *arg)
 {
 	struct ifnet	*ifp = arg;
 
 	if (nh->nh_ifp != ifp)
 		return (0);
 
 	/*
 	 * Protect (sorta) against walktree recursion problems
 	 * with cloned routes
 	 */
 	if ((rt->rte_flags & RTF_UP) == 0)
 		return (0);
 
 	return (1);
 }
 
 /*
  * Delete all remaining routes using this interface
  * Unfortuneatly the only way to do this is to slog through
  * the entire routing table looking for routes which point
  * to this interface...oh well...
  */
 void
 rt_flushifroutes_af(struct ifnet *ifp, int af)
 {
 	KASSERT((af >= 1 && af <= AF_MAX), ("%s: af %d not >= 1 and <= %d",
 	    __func__, af, AF_MAX));
 
 	rib_foreach_table_walk_del(af, rt_ifdelroute, ifp);
 }
 
 void
 rt_flushifroutes(struct ifnet *ifp)
 {
 
 	rib_foreach_table_walk_del(AF_UNSPEC, rt_ifdelroute, ifp);
 }
 
 /*
  * Look up rt_addrinfo for a specific fib.  Note that if rti_ifa is defined,
  * it will be referenced so the caller must free it.
  *
  * Assume basic consistency checks are executed by callers:
  * RTAX_DST exists, if RTF_GATEWAY is set, RTAX_GATEWAY exists as well.
  */
 int
 rt_getifa_fib(struct rt_addrinfo *info, u_int fibnum)
 {
 	const struct sockaddr *dst, *gateway, *ifpaddr, *ifaaddr;
 	struct epoch_tracker et;
 	int needref, error, flags;
 
 	dst = info->rti_info[RTAX_DST];
 	gateway = info->rti_info[RTAX_GATEWAY];
 	ifpaddr = info->rti_info[RTAX_IFP];
 	ifaaddr = info->rti_info[RTAX_IFA];
 	flags = info->rti_flags;
 
 	/*
 	 * ifp may be specified by sockaddr_dl
 	 * when protocol address is ambiguous.
 	 */
 	error = 0;
 	needref = (info->rti_ifa == NULL);
 	NET_EPOCH_ENTER(et);
 
 	/* If we have interface specified by the ifindex in the address, use it */
 	if (info->rti_ifp == NULL && ifpaddr != NULL &&
 	    ifpaddr->sa_family == AF_LINK) {
 	    const struct sockaddr_dl *sdl = (const struct sockaddr_dl *)ifpaddr;
 	    if (sdl->sdl_index != 0)
 		    info->rti_ifp = ifnet_byindex(sdl->sdl_index);
 	}
 	/*
 	 * If we have source address specified, try to find it
 	 * TODO: avoid enumerating all ifas on all interfaces.
 	 */
 	if (info->rti_ifa == NULL && ifaaddr != NULL)
 		info->rti_ifa = ifa_ifwithaddr(ifaaddr);
 	if (info->rti_ifa == NULL) {
 		const struct sockaddr *sa;
 
 		/*
 		 * Most common use case for the userland-supplied routes.
 		 *
 		 * Choose sockaddr to select ifa.
 		 * -- if ifp is set --
 		 * Order of preference:
 		 * 1) IFA address
 		 * 2) gateway address
 		 *   Note: for interface routes link-level gateway address 
 		 *     is specified to indicate the interface index without
 		 *     specifying RTF_GATEWAY. In this case, ignore gateway
 		 *   Note: gateway AF may be different from dst AF. In this case,
 		 *   ignore gateway
 		 * 3) final destination.
 		 * 4) if all of these fails, try to get at least link-level ifa.
 		 * -- else --
 		 * try to lookup gateway or dst in the routing table to get ifa
 		 */
 		if (info->rti_info[RTAX_IFA] != NULL)
 			sa = info->rti_info[RTAX_IFA];
 		else if ((info->rti_flags & RTF_GATEWAY) != 0 &&
 		    gateway->sa_family == dst->sa_family)
 			sa = gateway;
 		else
 			sa = dst;
 		if (info->rti_ifp != NULL) {
 			info->rti_ifa = ifaof_ifpforaddr(sa, info->rti_ifp);
 			/* Case 4 */
 			if (info->rti_ifa == NULL && gateway != NULL)
 				info->rti_ifa = ifaof_ifpforaddr(gateway, info->rti_ifp);
 		} else if (dst != NULL && gateway != NULL)
 			info->rti_ifa = ifa_ifwithroute(flags, dst, gateway,
 							fibnum);
 		else if (sa != NULL)
 			info->rti_ifa = ifa_ifwithroute(flags, sa, sa,
 							fibnum);
 	}
 	if (needref && info->rti_ifa != NULL) {
 		if (info->rti_ifp == NULL)
 			info->rti_ifp = info->rti_ifa->ifa_ifp;
 		ifa_ref(info->rti_ifa);
 	} else
 		error = ENETUNREACH;
 	NET_EPOCH_EXIT(et);
 	return (error);
 }
 
 void
 rt_updatemtu(struct ifnet *ifp)
 {
 	struct rib_head *rnh;
 	int mtu;
 	int i, j;
 
 	/*
 	 * Try to update rt_mtu for all routes using this interface
 	 * Unfortunately the only way to do this is to traverse all
 	 * routing tables in all fibs/domains.
 	 */
 	for (i = 1; i <= AF_MAX; i++) {
 		mtu = if_getmtu_family(ifp, i);
 		for (j = 0; j < rt_numfibs; j++) {
 			rnh = rt_tables_get_rnh(j, i);
 			if (rnh == NULL)
 				continue;
 			nhops_update_ifmtu(rnh, ifp, mtu);
 		}
 	}
 }
 
 #if 0
 int p_sockaddr(char *buf, int buflen, struct sockaddr *s);
 int rt_print(char *buf, int buflen, struct rtentry *rt);
 
 int
 p_sockaddr(char *buf, int buflen, struct sockaddr *s)
 {
 	void *paddr = NULL;
 
 	switch (s->sa_family) {
 	case AF_INET:
 		paddr = &((struct sockaddr_in *)s)->sin_addr;
 		break;
 	case AF_INET6:
 		paddr = &((struct sockaddr_in6 *)s)->sin6_addr;
 		break;
 	}
 
 	if (paddr == NULL)
 		return (0);
 
 	if (inet_ntop(s->sa_family, paddr, buf, buflen) == NULL)
 		return (0);
 
 	return (strlen(buf));
 }
 
 int
 rt_print(char *buf, int buflen, struct rtentry *rt)
 {
 	struct sockaddr *addr, *mask;
 	int i = 0;
 
 	addr = rt_key(rt);
 	mask = rt_mask(rt);
 
 	i = p_sockaddr(buf, buflen, addr);
 	if (!(rt->rt_flags & RTF_HOST)) {
 		buf[i++] = '/';
 		i += p_sockaddr(buf + i, buflen - i, mask);
 	}
 
 	if (rt->rt_flags & RTF_GATEWAY) {
 		buf[i++] = '>';
 		i += p_sockaddr(buf + i, buflen - i, &rt->rt_nhop->gw_sa);
 	}
 
 	return (i);
 }
 #endif
 
 void
 rt_maskedcopy(struct sockaddr *src, struct sockaddr *dst, struct sockaddr *netmask)
 {
 	u_char *cp1 = (u_char *)src;
 	u_char *cp2 = (u_char *)dst;
 	u_char *cp3 = (u_char *)netmask;
 	u_char *cplim = cp2 + *cp3;
 	u_char *cplim2 = cp2 + *cp1;
 
 	*cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */
 	cp3 += 2;
 	if (cplim > cplim2)
 		cplim = cplim2;
 	while (cp2 < cplim)
 		*cp2++ = *cp1++ & *cp3++;
 	if (cp2 < cplim2)
 		bzero((caddr_t)cp2, (unsigned)(cplim2 - cp2));
 }
 
 /*
  * Announce interface address arrival/withdraw
  * Returns 0 on success.
  */
 int
 rt_addrmsg(int cmd, struct ifaddr *ifa, int fibnum)
 {
 
 	KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE,
 	    ("unexpected cmd %d", cmd));
 	KASSERT((fibnum >= 0 && fibnum < rt_numfibs),
 	    ("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs));
 
 	EVENTHANDLER_DIRECT_INVOKE(rt_addrmsg, ifa, cmd);
 
 	if (V_rt_add_addr_allfibs)
 		fibnum = RT_ALL_FIBS;
 	return (rtsock_addrmsg(cmd, ifa, fibnum));
 }
 
 /*
  * Announce kernel-originated route addition/removal to rtsock based on @rt data.
  * cmd: RTM_ cmd
  * @rt: valid rtentry
  * @nh: nhop object to announce
  * @fibnum: fib id or RT_ALL_FIBS
  *
  * Returns 0 on success.
  */
 int
 rt_routemsg(int cmd, struct rtentry *rt, struct nhop_object *nh,
     int fibnum)
 {
 
 	KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE,
 	    ("unexpected cmd %d", cmd));
 
 	KASSERT(fibnum == RT_ALL_FIBS || (fibnum >= 0 && fibnum < rt_numfibs),
 	    ("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs));
 
 	KASSERT(rt_key(rt) != NULL, (":%s: rt_key must be supplied", __func__));
 
 	return (rtsock_routemsg(cmd, rt, nh, fibnum));
 }
 
 /*
  * Announce kernel-originated route addition/removal to rtsock based on @rt data.
  * cmd: RTM_ cmd
  * @info: addrinfo structure with valid data.
  * @fibnum: fib id or RT_ALL_FIBS
  *
  * Returns 0 on success.
  */
 int
 rt_routemsg_info(int cmd, struct rt_addrinfo *info, int fibnum)
 {
 
 	KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE || cmd == RTM_CHANGE,
 	    ("unexpected cmd %d", cmd));
 
 	KASSERT(fibnum == RT_ALL_FIBS || (fibnum >= 0 && fibnum < rt_numfibs),
 	    ("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs));
 
 	KASSERT(info->rti_info[RTAX_DST] != NULL, (":%s: RTAX_DST must be supplied", __func__));
 
 	return (rtsock_routemsg_info(cmd, info, fibnum));
 }
-
-/*
- * This is called to generate messages from the routing socket
- * indicating a network interface has had addresses associated with it.
- */
-void
-rt_newaddrmsg_fib(int cmd, struct ifaddr *ifa, struct rtentry *rt, int fibnum)
-{
-
-	KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE,
-		("unexpected cmd %u", cmd));
-	KASSERT((fibnum >= 0 && fibnum < rt_numfibs),
-	    ("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs));
-
-	if (cmd == RTM_ADD) {
-		rt_addrmsg(cmd, ifa, fibnum);
-		if (rt != NULL)
-			rt_routemsg(cmd, rt, nhop_select(rt->rt_nhop, 0), fibnum);
-	} else {
-		if (rt != NULL)
-			rt_routemsg(cmd, rt, nhop_select(rt->rt_nhop, 0), fibnum);
-		rt_addrmsg(cmd, ifa, fibnum);
-	}
-}
diff --git a/sys/net/route.h b/sys/net/route.h
index 96a8e78ecb3a..f9928ab6a776 100644
--- a/sys/net/route.h
+++ b/sys/net/route.h
@@ -1,454 +1,449 @@
 /*-
  * SPDX-License-Identifier: BSD-3-Clause
  *
  * Copyright (c) 1980, 1986, 1993
  *	The Regents of the University of California.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  *	@(#)route.h	8.4 (Berkeley) 1/9/95
  * $FreeBSD$
  */
 
 #ifndef _NET_ROUTE_H_
 #define _NET_ROUTE_H_
 
 #include <net/vnet.h>
 
 /*
  * Kernel resident routing tables.
  *
  * The routing tables are initialized when interface addresses
  * are set by making entries for all directly connected interfaces.
  */
 
 /*
  * Struct route consiste of a destination address,
  * a route entry pointer, link-layer prepend data pointer along
  * with its length.
  */
 struct route {
 	struct	nhop_object *ro_nh;
 	struct	llentry *ro_lle;
 	/*
 	 * ro_prepend and ro_plen are only used for bpf to pass in a
 	 * preformed header.  They are not cacheable.
 	 */
 	char		*ro_prepend;
 	uint16_t	ro_plen;
 	uint16_t	ro_flags;
 	uint16_t	ro_mtu;	/* saved ro_rt mtu */
 	uint16_t	spare;
 	struct	sockaddr ro_dst;
 };
 
 #define	RT_L2_ME_BIT		2	/* dst L2 addr is our address */
 #define	RT_MAY_LOOP_BIT		3	/* dst may require loop copy */
 #define	RT_HAS_HEADER_BIT	4	/* mbuf already have its header prepended */
 
 #define	RT_L2_ME		(1 << RT_L2_ME_BIT)		/* 0x0004 */
 #define	RT_MAY_LOOP		(1 << RT_MAY_LOOP_BIT)		/* 0x0008 */
 #define	RT_HAS_HEADER		(1 << RT_HAS_HEADER_BIT)	/* 0x0010 */
 
 #define	RT_REJECT		0x0020		/* Destination is reject */
 #define	RT_BLACKHOLE		0x0040		/* Destination is blackhole */
 #define	RT_HAS_GW		0x0080		/* Destination has GW  */
 #define	RT_LLE_CACHE		0x0100		/* Cache link layer  */
 
 struct rt_metrics {
 	u_long	rmx_locks;	/* Kernel must leave these values alone */
 	u_long	rmx_mtu;	/* MTU for this path */
 	u_long	rmx_hopcount;	/* max hops expected */
 	u_long	rmx_expire;	/* lifetime for route, e.g. redirect */
 	u_long	rmx_recvpipe;	/* inbound delay-bandwidth product */
 	u_long	rmx_sendpipe;	/* outbound delay-bandwidth product */
 	u_long	rmx_ssthresh;	/* outbound gateway buffer limit */
 	u_long	rmx_rtt;	/* estimated round trip time */
 	u_long	rmx_rttvar;	/* estimated rtt variance */
 	u_long	rmx_pksent;	/* packets sent using this route */
 	u_long	rmx_weight;	/* route weight */
 	u_long	rmx_nhidx;	/* route nexhop index */
 	u_long	rmx_filler[2];	/* will be used for T/TCP later */
 };
 
 /*
  * rmx_rtt and rmx_rttvar are stored as microseconds;
  * RTTTOPRHZ(rtt) converts to a value suitable for use
  * by a protocol slowtimo counter.
  */
 #define	RTM_RTTUNIT	1000000	/* units for rtt, rttvar, as units per sec */
 #define	RTTTOPRHZ(r)	((r) / (RTM_RTTUNIT / PR_SLOWHZ))
 
 /* lle state is exported in rmx_state rt_metrics field */
 #define	rmx_state	rmx_weight
 
 /* default route weight */
 #define	RT_DEFAULT_WEIGHT	1
 #define	RT_MAX_WEIGHT		16777215	/* 3 bytes */
 
 /*
  * Keep a generation count of routing table, incremented on route addition,
  * so we can invalidate caches.  This is accessed without a lock, as precision
  * is not required.
  */
 typedef volatile u_int rt_gen_t;	/* tree generation (for adds) */
 #define RT_GEN(fibnum, af)	rt_tables_get_gen(fibnum, af)
 
 #define	RT_DEFAULT_FIB	0	/* Explicitly mark fib=0 restricted cases */
 #define	RT_ALL_FIBS	-1	/* Announce event for every fib */
 #ifdef _KERNEL
 VNET_DECLARE(uint32_t, _rt_numfibs);	/* number of existing route tables */
 #define	V_rt_numfibs		VNET(_rt_numfibs)
 /* temporary compat arg */
 #define	rt_numfibs		V_rt_numfibs
 VNET_DECLARE(u_int, rt_add_addr_allfibs); /* Announce interfaces to all fibs */
 #define	V_rt_add_addr_allfibs	VNET(rt_add_addr_allfibs)
 
 /* Calculate flowid for locally-originated packets */
 #define	V_fib_hash_outbound	VNET(fib_hash_outbound)
 VNET_DECLARE(u_int, fib_hash_outbound);
 
 /* Outbound flowid generation rules */
 #ifdef RSS
 
 #define fib4_calc_packet_hash		xps_proto_software_hash_v4
 #define fib6_calc_packet_hash		xps_proto_software_hash_v6
 #define	CALC_FLOWID_OUTBOUND_SENDTO	true
 
 #ifdef ROUTE_MPATH
 #define	CALC_FLOWID_OUTBOUND		V_fib_hash_outbound
 #else
 #define	CALC_FLOWID_OUTBOUND		false
 #endif
 
 #else /* !RSS */
 
 #define fib4_calc_packet_hash		fib4_calc_software_hash
 #define fib6_calc_packet_hash		fib6_calc_software_hash
 
 #ifdef ROUTE_MPATH
 #define	CALC_FLOWID_OUTBOUND_SENDTO	V_fib_hash_outbound
 #define	CALC_FLOWID_OUTBOUND		V_fib_hash_outbound
 #else
 #define	CALC_FLOWID_OUTBOUND_SENDTO	false
 #define	CALC_FLOWID_OUTBOUND		false
 #endif
 
 #endif /* RSS */
 
 
 #endif /* _KERNEL */
 
 /*
  * We distinguish between routes to hosts and routes to networks,
  * preferring the former if available.  For each route we infer
  * the interface to use from the gateway address supplied when
  * the route was entered.  Routes that forward packets through
  * gateways are marked so that the output routines know to address the
  * gateway rather than the ultimate destination.
  */
 #define	RTF_UP		0x1		/* route usable */
 #define	RTF_GATEWAY	0x2		/* destination is a gateway */
 #define	RTF_HOST	0x4		/* host entry (net otherwise) */
 #define	RTF_REJECT	0x8		/* host or net unreachable */
 #define	RTF_DYNAMIC	0x10		/* created dynamically (by redirect) */
 #define	RTF_MODIFIED	0x20		/* modified dynamically (by redirect) */
 #define RTF_DONE	0x40		/* message confirmed */
 /*			0x80		   unused, was RTF_DELCLONE */
 /*			0x100		   unused, was RTF_CLONING */
 #define RTF_XRESOLVE	0x200		/* external daemon resolves name */
 #define RTF_LLINFO	0x400		/* DEPRECATED - exists ONLY for backward 
 					   compatibility */
 #define RTF_LLDATA	0x400		/* used by apps to add/del L2 entries */
 #define RTF_STATIC	0x800		/* manually added */
 #define RTF_BLACKHOLE	0x1000		/* just discard pkts (during updates) */
 #define RTF_PROTO2	0x4000		/* protocol specific routing flag */
 #define RTF_PROTO1	0x8000		/* protocol specific routing flag */
 /*			0x10000		   unused, was RTF_PRCLONING */
 /*			0x20000		   unused, was RTF_WASCLONED */
 #define RTF_PROTO3	0x40000		/* protocol specific routing flag */
 #define	RTF_FIXEDMTU	0x80000		/* MTU was explicitly specified */
 #define RTF_PINNED	0x100000	/* route is immutable */
 #define	RTF_LOCAL	0x200000 	/* route represents a local address */
 #define	RTF_BROADCAST	0x400000	/* route represents a bcast address */
 #define	RTF_MULTICAST	0x800000	/* route represents a mcast address */
 					/* 0x8000000 and up unassigned */
 #define	RTF_STICKY	 0x10000000	/* always route dst->src */
 
 #define	RTF_RNH_LOCKED	 0x40000000	/* radix node head is locked */
 
 #define	RTF_GWFLAG_COMPAT 0x80000000	/* a compatibility bit for interacting
 					   with existing routing apps */
 
 /* Mask of RTF flags that are allowed to be modified by RTM_CHANGE. */
 #define RTF_FMASK	\
 	(RTF_PROTO1 | RTF_PROTO2 | RTF_PROTO3 | RTF_BLACKHOLE | \
 	 RTF_REJECT | RTF_STATIC | RTF_STICKY)
 
 /*
  * fib_ nexthop API flags.
  */
 
 /* Consumer-visible nexthop info flags */
 #define	NHF_MULTIPATH		0x0008	/* Nexhop is a nexthop group */
 #define	NHF_REJECT		0x0010	/* RTF_REJECT */
 #define	NHF_BLACKHOLE		0x0020	/* RTF_BLACKHOLE */
 #define	NHF_REDIRECT		0x0040	/* RTF_DYNAMIC|RTF_MODIFIED */
 #define	NHF_DEFAULT		0x0080	/* Default route */
 #define	NHF_BROADCAST		0x0100	/* RTF_BROADCAST */
 #define	NHF_GATEWAY		0x0200	/* RTF_GATEWAY */
 #define	NHF_HOST		0x0400	/* RTF_HOST */
 
 /* Nexthop request flags */
 #define	NHR_NONE		0x00	/* empty flags field */
 #define	NHR_REF			0x01	/* reference nexhop */
 #define	NHR_NODEFAULT		0x02	/* uRPF: do not consider default route */
 
 /* Control plane route request flags */
 #define	NHR_COPY		0x100	/* Copy rte data */
 #define	NHR_UNLOCKED		0x200	/* Do not lock table */
 
 /*
  * Routing statistics.
  */
 struct rtstat {
 	uint64_t rts_badredirect;	/* bogus redirect calls */
 	uint64_t rts_dynamic;		/* routes created by redirects */
 	uint64_t rts_newgateway;	/* routes modified by redirects */
 	uint64_t rts_unreach;		/* lookups which failed */
 	uint64_t rts_wildcard;		/* lookups satisfied by a wildcard */
 	uint64_t rts_nh_idx_alloc_failure;	/* nexthop index alloc failure*/
 	uint64_t rts_nh_alloc_failure;	/* nexthop allocation failure*/
 	uint64_t rts_add_failure;	/* # of route addition failures */
 	uint64_t rts_add_retry;		/* # of route addition retries */
 	uint64_t rts_del_failure;	/* # of route deletion failure */
 	uint64_t rts_del_retry;		/* # of route deletion retries */
 };
 
 /*
  * Structures for routing messages.
  */
 struct rt_msghdr {
 	u_short	rtm_msglen;	/* to skip over non-understood messages */
 	u_char	rtm_version;	/* future binary compatibility */
 	u_char	rtm_type;	/* message type */
 	u_short	rtm_index;	/* index for associated ifp */
 	u_short _rtm_spare1;
 	int	rtm_flags;	/* flags, incl. kern & message, e.g. DONE */
 	int	rtm_addrs;	/* bitmask identifying sockaddrs in msg */
 	pid_t	rtm_pid;	/* identify sender */
 	int	rtm_seq;	/* for sender to identify action */
 	int	rtm_errno;	/* why failed */
 	int	rtm_fmask;	/* bitmask used in RTM_CHANGE message */
 	u_long	rtm_inits;	/* which metrics we are initializing */
 	struct	rt_metrics rtm_rmx; /* metrics themselves */
 };
 
 #define RTM_VERSION	5	/* Up the ante and ignore older versions */
 
 /*
  * Message types.
  *
  * The format for each message is annotated below using the following
  * identifiers:
  *
  * (1) struct rt_msghdr
  * (2) struct ifa_msghdr
  * (3) struct if_msghdr
  * (4) struct ifma_msghdr
  * (5) struct if_announcemsghdr
  *
  */
 #define	RTM_ADD		0x1	/* (1) Add Route */
 #define	RTM_DELETE	0x2	/* (1) Delete Route */
 #define	RTM_CHANGE	0x3	/* (1) Change Metrics or flags */
 #define	RTM_GET		0x4	/* (1) Report Metrics */
 #define	RTM_LOSING	0x5	/* (1) Kernel Suspects Partitioning */
 #define	RTM_REDIRECT	0x6	/* (1) Told to use different route */
 #define	RTM_MISS	0x7	/* (1) Lookup failed on this address */
 #define	RTM_LOCK	0x8	/* (1) fix specified metrics */
 		    /*	0x9  */
 		    /*	0xa  */
 #define	RTM_RESOLVE	0xb	/* (1) req to resolve dst to LL addr */
 #define	RTM_NEWADDR	0xc	/* (2) address being added to iface */
 #define	RTM_DELADDR	0xd	/* (2) address being removed from iface */
 #define	RTM_IFINFO	0xe	/* (3) iface going up/down etc. */
 #define	RTM_NEWMADDR	0xf	/* (4) mcast group membership being added to if */
 #define	RTM_DELMADDR	0x10	/* (4) mcast group membership being deleted */
 #define	RTM_IFANNOUNCE	0x11	/* (5) iface arrival/departure */
 #define	RTM_IEEE80211	0x12	/* (5) IEEE80211 wireless event */
 
 /*
  * Bitmask values for rtm_inits and rmx_locks.
  */
 #define RTV_MTU		0x1	/* init or lock _mtu */
 #define RTV_HOPCOUNT	0x2	/* init or lock _hopcount */
 #define RTV_EXPIRE	0x4	/* init or lock _expire */
 #define RTV_RPIPE	0x8	/* init or lock _recvpipe */
 #define RTV_SPIPE	0x10	/* init or lock _sendpipe */
 #define RTV_SSTHRESH	0x20	/* init or lock _ssthresh */
 #define RTV_RTT		0x40	/* init or lock _rtt */
 #define RTV_RTTVAR	0x80	/* init or lock _rttvar */
 #define RTV_WEIGHT	0x100	/* init or lock _weight */
 
 /*
  * Bitmask values for rtm_addrs.
  */
 #define RTA_DST		0x1	/* destination sockaddr present */
 #define RTA_GATEWAY	0x2	/* gateway sockaddr present */
 #define RTA_NETMASK	0x4	/* netmask sockaddr present */
 #define RTA_GENMASK	0x8	/* cloning mask sockaddr present */
 #define RTA_IFP		0x10	/* interface name sockaddr present */
 #define RTA_IFA		0x20	/* interface addr sockaddr present */
 #define RTA_AUTHOR	0x40	/* sockaddr for author of redirect */
 #define RTA_BRD		0x80	/* for NEWADDR, broadcast or p-p dest addr */
 
 /*
  * Index offsets for sockaddr array for alternate internal encoding.
  */
 #define RTAX_DST	0	/* destination sockaddr present */
 #define RTAX_GATEWAY	1	/* gateway sockaddr present */
 #define RTAX_NETMASK	2	/* netmask sockaddr present */
 #define RTAX_GENMASK	3	/* cloning mask sockaddr present */
 #define RTAX_IFP	4	/* interface name sockaddr present */
 #define RTAX_IFA	5	/* interface addr sockaddr present */
 #define RTAX_AUTHOR	6	/* sockaddr for author of redirect */
 #define RTAX_BRD	7	/* for NEWADDR, broadcast or p-p dest addr */
 #define RTAX_MAX	8	/* size of array to allocate */
 
 struct rtentry;
 struct nhop_object;
 typedef int rib_filter_f_t(const struct rtentry *, const struct nhop_object *,
     void *);
 
 struct rt_addrinfo {
 	int	rti_addrs;			/* Route RTF_ flags */
 	int	rti_flags;			/* Route RTF_ flags */
 	struct	sockaddr *rti_info[RTAX_MAX];	/* Sockaddr data */
 	struct	ifaddr *rti_ifa;		/* value of rt_ifa addr */
 	struct	ifnet *rti_ifp;			/* route interface */
 	rib_filter_f_t	*rti_filter;		/* filter function */
 	void	*rti_filterdata;		/* filter paramenters */
 	u_long	rti_mflags;			/* metrics RTV_ flags */
 	u_long	rti_spare;			/* Will be used for fib */
 	struct	rt_metrics *rti_rmx;		/* Pointer to route metrics */
 };
 
 /*
  * This macro returns the size of a struct sockaddr when passed
  * through a routing socket. Basically we round up sa_len to
  * a multiple of sizeof(long), with a minimum of sizeof(long).
  * The case sa_len == 0 should only apply to empty structures.
  */
 #define SA_SIZE(sa)						\
     (  (((struct sockaddr *)(sa))->sa_len == 0) ?		\
 	sizeof(long)		:				\
 	1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
 
 #define	sa_equal(a, b) (	\
     (((const struct sockaddr *)(a))->sa_len == ((const struct sockaddr *)(b))->sa_len) && \
     (bcmp((a), (b), ((const struct sockaddr *)(b))->sa_len) == 0))
 
 #ifdef _KERNEL
 
 #define RT_LINK_IS_UP(ifp)	(!((ifp)->if_capabilities & IFCAP_LINKSTATE) \
 				 || (ifp)->if_link_state == LINK_STATE_UP)
 
 #define	RO_NHFREE(_ro) do {					\
 	if ((_ro)->ro_nh) {					\
 		NH_FREE((_ro)->ro_nh);				\
 		(_ro)->ro_nh = NULL;				\
 	}							\
 } while (0)
 
 #define	RO_INVALIDATE_CACHE(ro) do {					\
 		if ((ro)->ro_lle != NULL) {				\
 			LLE_FREE((ro)->ro_lle);				\
 			(ro)->ro_lle = NULL;				\
 		}							\
 		if ((ro)->ro_nh != NULL) {				\
 			NH_FREE((ro)->ro_nh);				\
 			(ro)->ro_nh = NULL;				\
 		}							\
 	} while (0)
 
 /*
  * Validate a cached route based on a supplied cookie.  If there is an
  * out-of-date cache, simply free it.  Update the generation number
  * for the new allocation
  */
 #define NH_VALIDATE(ro, cookiep, fibnum) do {				\
 	rt_gen_t cookie = RT_GEN(fibnum, (ro)->ro_dst.sa_family);	\
 	if (*(cookiep) != cookie) {					\
 		RO_INVALIDATE_CACHE(ro);				\
 		*(cookiep) = cookie;					\
 	}								\
 } while (0)
 
 struct ifmultiaddr;
 struct rib_head;
 
 void	 rt_ieee80211msg(struct ifnet *, int, void *, size_t);
 void	 rt_ifannouncemsg(struct ifnet *, int);
 void	 rt_ifmsg(struct ifnet *);
 void	 rt_missmsg(int, struct rt_addrinfo *, int, int);
 void	 rt_missmsg_fib(int, struct rt_addrinfo *, int, int, int);
-void	 rt_newaddrmsg_fib(int, struct ifaddr *, struct rtentry *, int);
 int	 rt_addrmsg(int, struct ifaddr *, int);
 int	 rt_routemsg(int, struct rtentry *, struct nhop_object *, int);
 int	 rt_routemsg_info(int, struct rt_addrinfo *, int);
 void	 rt_newmaddrmsg(int, struct ifmultiaddr *);
 void 	 rt_maskedcopy(struct sockaddr *, struct sockaddr *, struct sockaddr *);
 struct rib_head *rt_table_init(int, int, u_int);
 void	rt_table_destroy(struct rib_head *);
 u_int	rt_tables_get_gen(uint32_t table, sa_family_t family);
 
 struct sockaddr *rtsock_fix_netmask(const struct sockaddr *dst,
 	    const struct sockaddr *smask, struct sockaddr_storage *dmask);
 
 void	rt_updatemtu(struct ifnet *);
 
 void	rt_flushifroutes_af(struct ifnet *, int);
 void	rt_flushifroutes(struct ifnet *ifp);
 
-/* XXX MRT COMPAT VERSIONS THAT SET UNIVERSE to 0 */
-/* Thes are used by old code not yet converted to use multiple FIBS */
-int	 rtinit(struct ifaddr *, int, int);
-
 /* XXX MRT NEW VERSIONS THAT USE FIBs
  * For now the protocol indepedent versions are the same as the AF_INET ones
  * but this will change.. 
  */
 int	 rtioctl_fib(u_long, caddr_t, u_int);
 int	rib_lookup_info(uint32_t, const struct sockaddr *, uint32_t, uint32_t,
 	    struct rt_addrinfo *);
 void	rib_free_info(struct rt_addrinfo *info);
 
 /* New API */
 struct nhop_object *rib_lookup(uint32_t fibnum, const struct sockaddr *dst,
 	    uint32_t flags, uint32_t flowid);
 #endif
 
 #endif
diff --git a/sys/net/route/route_ctl.h b/sys/net/route/route_ctl.h
index c52c6b96e126..ecbc9ee91dc0 100644
--- a/sys/net/route/route_ctl.h
+++ b/sys/net/route/route_ctl.h
@@ -1,148 +1,149 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  *
  * Copyright (c) 2020 Alexander V. Chernikov
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  * $FreeBSD$
  */
 
 /*
  * This header file contains public functions and structures used for
  * routing table manipulations.
  */
 
 #ifndef	_NET_ROUTE_ROUTE_CTL_H_
 #define	_NET_ROUTE_ROUTE_CTL_H_
 
 struct rib_cmd_info {
 	uint8_t			rc_cmd;		/* RTM_ADD|RTM_DEL|RTM_CHANGE */
 	uint8_t			spare[3];
 	uint32_t		rc_nh_weight;	/* new nhop weight */
 	struct rtentry		*rc_rt;		/* Target entry */
 	struct nhop_object	*rc_nh_old;	/* Target nhop OR mpath */
 	struct nhop_object	*rc_nh_new;	/* Target nhop OR mpath */
 };
 
 int rib_add_route(uint32_t fibnum, struct rt_addrinfo *info,
   struct rib_cmd_info *rc);
 int rib_del_route(uint32_t fibnum, struct rt_addrinfo *info,
   struct rib_cmd_info *rc);
 int rib_change_route(uint32_t fibnum, struct rt_addrinfo *info,
   struct rib_cmd_info *rc);
 int rib_action(uint32_t fibnum, int action, struct rt_addrinfo *info,
   struct rib_cmd_info *rc);
+int rib_handle_ifaddr_info(uint32_t fibnum, int cmd, struct rt_addrinfo *info);
 
 typedef void route_notification_t(struct rib_cmd_info *rc, void *);
 void rib_decompose_notification(struct rib_cmd_info *rc,
     route_notification_t *cb, void *cbdata);
 
 int rib_add_redirect(u_int fibnum, struct sockaddr *dst,
   struct sockaddr *gateway, struct sockaddr *author, struct ifnet *ifp,
   int flags, int expire_sec);
 
 enum rib_walk_hook {
 	RIB_WALK_HOOK_PRE,	/* Hook is called before iteration */
 	RIB_WALK_HOOK_POST,	/* Hook is called after iteration */
 };
 typedef int rib_walktree_f_t(struct rtentry *, void *);
 typedef void rib_walk_hook_f_t(struct rib_head *rnh, enum rib_walk_hook stage,
     void *arg);
 void rib_walk(uint32_t fibnum, int af, bool wlock, rib_walktree_f_t *wa_f,
     void *arg);
 void rib_walk_ext(uint32_t fibnum, int af, bool wlock, rib_walktree_f_t *wa_f,
     rib_walk_hook_f_t *hook_f, void *arg);
 void rib_walk_ext_internal(struct rib_head *rnh, bool wlock,
     rib_walktree_f_t *wa_f, rib_walk_hook_f_t *hook_f, void *arg);
 
 void rib_walk_del(u_int fibnum, int family, rib_filter_f_t *filter_f,
     void *arg, bool report);
 
 void rib_foreach_table_walk(int family, bool wlock, rib_walktree_f_t *wa_f,
     rib_walk_hook_f_t *hook_f, void *arg);
 void rib_foreach_table_walk_del(int family, rib_filter_f_t *filter_f, void *arg);
 
 struct nhop_object;
 struct nhgrp_object;
 struct route_nhop_data {
 	union {
 		struct nhop_object *rnd_nhop;
 		struct nhgrp_object *rnd_nhgrp;
 	};
 	uint32_t rnd_weight;
 };
 
 const struct rtentry *rib_lookup_prefix(uint32_t fibnum, int family,
     const struct sockaddr *dst, const struct sockaddr *netmask,
     struct route_nhop_data *rnd);
 const struct rtentry *rib_lookup_lpm(uint32_t fibnum, int family,
     const struct sockaddr *dst, struct route_nhop_data *rnd);
 
 /* rtentry accessors */
 bool rt_is_host(const struct rtentry *rt);
 sa_family_t rt_get_family(const struct rtentry *);
 struct nhop_object *rt_get_raw_nhop(const struct rtentry *rt);
 #ifdef INET
 struct in_addr;
 void rt_get_inet_prefix_plen(const struct rtentry *rt, struct in_addr *paddr,
     int *plen, uint32_t *pscopeid);
 void rt_get_inet_prefix_pmask(const struct rtentry *rt, struct in_addr *paddr,
     struct in_addr *pmask, uint32_t *pscopeid);
 #endif
 #ifdef INET6
 struct in6_addr;
 void rt_get_inet6_prefix_plen(const struct rtentry *rt, struct in6_addr *paddr,
     int *plen, uint32_t *pscopeid);
 void rt_get_inet6_prefix_pmask(const struct rtentry *rt, struct in6_addr *paddr,
     struct in6_addr *pmask, uint32_t *pscopeid);
 #endif
 
 /* Nexthops */
 uint32_t nhops_get_count(struct rib_head *rh);
 
 /* Multipath */
 struct weightened_nhop;
 
 struct weightened_nhop *nhgrp_get_nhops(struct nhgrp_object *nhg,
     uint32_t *pnum_nhops);
 uint32_t nhgrp_get_count(struct rib_head *rh);
 
 /* Route subscriptions */
 enum rib_subscription_type {
 	RIB_NOTIFY_IMMEDIATE,
 	RIB_NOTIFY_DELAYED
 };
 
 struct rib_subscription;
 typedef void rib_subscription_cb_t(struct rib_head *rnh, struct rib_cmd_info *rc,
     void *arg);
 
 struct rib_subscription *rib_subscribe(uint32_t fibnum, int family,
     rib_subscription_cb_t *f, void *arg, enum rib_subscription_type type,
     bool waitok);
 struct rib_subscription *rib_subscribe_internal(struct rib_head *rnh,
     rib_subscription_cb_t *f, void *arg, enum rib_subscription_type type,
     bool waitok);
 void rib_unsibscribe(struct rib_subscription *rs);
 
 #endif
diff --git a/sys/net/route/route_ifaddrs.c b/sys/net/route/route_ifaddrs.c
index 967aa5d75e68..6e264327d66d 100644
--- a/sys/net/route/route_ifaddrs.c
+++ b/sys/net/route/route_ifaddrs.c
@@ -1,308 +1,206 @@
 /*-
  * SPDX-License-Identifier: BSD-3-Clause
  *
  * Copyright (c) 1980, 1986, 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  *	@(#)route.c	8.3.1.1 (Berkeley) 2/23/95
  * $FreeBSD$
  */
 
 #include "opt_route.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/malloc.h>
 #include <sys/socket.h>
 #include <sys/sysctl.h>
 #include <sys/syslog.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/rmlock.h>
 
 #include <net/if.h>
 #include <net/if_var.h>
 #include <net/if_dl.h>
 #include <net/route.h>
 #include <net/route/route_ctl.h>
 #include <net/route/route_var.h>
 #include <net/route/nhop.h>
 #include <net/vnet.h>
 
 #include <netinet/in.h>
 
 /*
  * Control interface address fib propagation.
  * By default, interface address routes are added to the fib of the interface.
  * Once set to non-zero, adds interface address route to all fibs.
  */
 VNET_DEFINE(u_int, rt_add_addr_allfibs) = 0;
 SYSCTL_UINT(_net, OID_AUTO, add_addr_allfibs, CTLFLAG_RWTUN | CTLFLAG_VNET,
     &VNET_NAME(rt_add_addr_allfibs), 0, "");
 
 /*
- * Set up a routing table entry, normally
- * for an interface.
+ * Executes routing tables change specified by @cmd and @info for the fib
+ * @fibnum. Generates routing message on success.
+ * Note: it assumes there is only single route (interface route) for the
+ * provided prefix.
+ * Returns 0 on success or errno.
  */
-static inline  int
-rtinit1(struct ifaddr *ifa, int cmd, int flags, int fibnum)
+static int
+rib_handle_ifaddr_one(uint32_t fibnum, int cmd, struct rt_addrinfo *info)
 {
-	RIB_RLOCK_TRACKER;
-	struct epoch_tracker et;
-	struct sockaddr *dst;
-	struct sockaddr *netmask;
 	struct rib_cmd_info rc;
-	struct rt_addrinfo info;
-	int error = 0;
-	int startfib, endfib;
-	struct sockaddr_storage ss;
-	int didwork = 0;
-	int a_failure = 0;
-	struct sockaddr_dl_short sdl;
-	struct rib_head *rnh;
+	struct nhop_object *nh;
+	int error;
 
-	if (flags & RTF_HOST) {
-		dst = ifa->ifa_dstaddr;
-		netmask = NULL;
-	} else {
-		dst = ifa->ifa_addr;
-		netmask = ifa->ifa_netmask;
-	}
-	if (dst->sa_len == 0)
-		return(EINVAL);
-	switch (dst->sa_family) {
-	case AF_INET6:
-	case AF_INET:
-		/* We support multiple FIBs. */
-		break;
-	default:
-		fibnum = RT_DEFAULT_FIB;
-		break;
-	}
-	if (fibnum == RT_ALL_FIBS) {
-		if (V_rt_add_addr_allfibs == 0 && cmd == (int)RTM_ADD)
-			startfib = endfib = ifa->ifa_ifp->if_fib;
-		else {
-			startfib = 0;
-			endfib = rt_numfibs - 1;
-		}
-	} else {
-		KASSERT((fibnum < rt_numfibs), ("rtinit1: bad fibnum"));
-		startfib = fibnum;
-		endfib = fibnum;
+	error = rib_action(fibnum, cmd, info, &rc);
+	if (error == 0) {
+		if (cmd == RTM_ADD)
+			nh = nhop_select(rc.rc_nh_new, 0);
+		else
+			nh = nhop_select(rc.rc_nh_old, 0);
+		rt_routemsg(cmd, rc.rc_rt, nh, fibnum);
 	}
 
-	/*
-	 * If it's a delete, check that if it exists,
-	 * it's on the correct interface or we might scrub
-	 * a route to another ifa which would
-	 * be confusing at best and possibly worse.
-	 */
-	if (cmd == RTM_DELETE) {
-		/*
-		 * It's a delete, so it should already exist..
-		 * If it's a net, mask off the host bits
-		 * (Assuming we have a mask)
-		 * XXX this is kinda inet specific..
-		 */
-		if (netmask != NULL) {
-			rt_maskedcopy(dst, (struct sockaddr *)&ss, netmask);
-			dst = (struct sockaddr *)&ss;
-		}
-	}
-	bzero(&sdl, sizeof(struct sockaddr_dl_short));
-	sdl.sdl_family = AF_LINK;
-	sdl.sdl_len = sizeof(struct sockaddr_dl_short);
-	sdl.sdl_type = ifa->ifa_ifp->if_type;
-	sdl.sdl_index = ifa->ifa_ifp->if_index;
-	/*
-	 * Now go through all the requested tables (fibs) and do the
-	 * requested action. Realistically, this will either be fib 0
-	 * for protocols that don't do multiple tables or all the
-	 * tables for those that do.
-	 */
-	for ( fibnum = startfib; fibnum <= endfib; fibnum++) {
-		if (cmd == RTM_DELETE) {
-			struct radix_node *rn;
-			/*
-			 * Look up an rtentry that is in the routing tree and
-			 * contains the correct info.
-			 */
-			rnh = rt_tables_get_rnh(fibnum, dst->sa_family);
-			if (rnh == NULL)
-				/* this table doesn't exist but others might */
-				continue;
-			RIB_RLOCK(rnh);
-			rn = rnh->rnh_lookup(dst, netmask, &rnh->head);
-			error = (rn == NULL ||
-			    (rn->rn_flags & RNF_ROOT) ||
-			    RNTORT(rn)->rt_nhop->nh_ifa != ifa);
-			RIB_RUNLOCK(rnh);
-			if (error) {
-				/* this is only an error if bad on ALL tables */
-				continue;
-			}
-		}
-		/*
-		 * Do the actual request
-		 */
-		bzero((caddr_t)&info, sizeof(info));
-		info.rti_ifa = ifa;
-		info.rti_flags = flags |
-		    (ifa->ifa_flags & ~IFA_RTSELF) | RTF_PINNED;
-		info.rti_info[RTAX_DST] = dst;
-		info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&sdl;
-		info.rti_info[RTAX_NETMASK] = netmask;
-		NET_EPOCH_ENTER(et);
-		error = rib_action(fibnum, cmd, &info, &rc);
-		if (error == 0 && rc.rc_rt != NULL) {
-			/*
-			 * notify any listening routing agents of the change
-			 */
+	return (error);
+}
 
-			/* TODO: interface routes/aliases */
-			rt_newaddrmsg_fib(cmd, ifa, rc.rc_rt, fibnum);
-			didwork = 1;
+/*
+ * Adds/deletes interface prefix specified by @info to the routing table.
+ * If V_rt_add_addr_allfibs is set, iterates over all existing routing
+ * tables, otherwise uses fib in @fibnum. Generates routing message for
+ *  each table.
+ * Returns 0 on success or errno.
+ */
+int
+rib_handle_ifaddr_info(uint32_t fibnum, int cmd, struct rt_addrinfo *info)
+{
+	int error, last_error = 0;
+	bool didwork = false;
+
+	if (V_rt_add_addr_allfibs == 0) {
+		error = rib_handle_ifaddr_one(fibnum, cmd, info);
+		didwork = (error == 0);
+	} else {
+		for (fibnum = 0; fibnum < V_rt_numfibs; fibnum++) {
+			error = rib_handle_ifaddr_one(fibnum, cmd, info);
+			if (error == 0)
+				didwork = true;
+			else
+				last_error = error;
 		}
-		NET_EPOCH_EXIT(et);
-		if (error)
-			a_failure = error;
 	}
+
 	if (cmd == RTM_DELETE) {
 		if (didwork) {
 			error = 0;
 		} else {
 			/* we only give an error if it wasn't in any table */
-			error = ((flags & RTF_HOST) ?
+			error = ((info->rti_flags & RTF_HOST) ?
 			    EHOSTUNREACH : ENETUNREACH);
 		}
 	} else {
-		if (a_failure) {
+		if (last_error != 0) {
 			/* return an error if any of them failed */
-			error = a_failure;
+			error = last_error;
 		}
 	}
 	return (error);
 }
 
-/*
- * Set up a routing table entry, normally
- * for an interface.
- */
-int
-rtinit(struct ifaddr *ifa, int cmd, int flags)
-{
-	struct sockaddr *dst;
-	int fib = RT_DEFAULT_FIB;
-
-	if (flags & RTF_HOST) {
-		dst = ifa->ifa_dstaddr;
-	} else {
-		dst = ifa->ifa_addr;
-	}
-
-	switch (dst->sa_family) {
-	case AF_INET6:
-	case AF_INET:
-		/* We do support multiple FIBs. */
-		fib = RT_ALL_FIBS;
-		break;
-	}
-	return (rtinit1(ifa, cmd, flags, fib));
-}
-
 static int
 ifa_maintain_loopback_route(int cmd, const char *otype, struct ifaddr *ifa,
     struct sockaddr *ia)
 {
 	struct rib_cmd_info rc;
 	struct epoch_tracker et;
 	int error;
 	struct rt_addrinfo info;
 	struct sockaddr_dl null_sdl;
 	struct ifnet *ifp;
 	struct ifaddr *rti_ifa = NULL;
 
 	ifp = ifa->ifa_ifp;
 
 	NET_EPOCH_ENTER(et);
 	bzero(&info, sizeof(info));
 	if (cmd != RTM_DELETE)
 		info.rti_ifp = V_loif;
 	if (cmd == RTM_ADD) {
 		/* explicitly specify (loopback) ifa */
 		if (info.rti_ifp != NULL) {
 			rti_ifa = ifaof_ifpforaddr(ifa->ifa_addr, info.rti_ifp);
 			if (rti_ifa != NULL)
 				ifa_ref(rti_ifa);
 			info.rti_ifa = rti_ifa;
 		}
 	}
 	info.rti_flags = ifa->ifa_flags | RTF_HOST | RTF_STATIC | RTF_PINNED;
 	info.rti_info[RTAX_DST] = ia;
 	info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&null_sdl;
 	link_init_sdl(ifp, (struct sockaddr *)&null_sdl, ifp->if_type);
 
 	error = rib_action(ifp->if_fib, cmd, &info, &rc);
 	NET_EPOCH_EXIT(et);
 
 	if (rti_ifa != NULL)
 		ifa_free(rti_ifa);
 
 	if (error == 0 ||
 	    (cmd == RTM_ADD && error == EEXIST) ||
 	    (cmd == RTM_DELETE && (error == ENOENT || error == ESRCH)))
 		return (error);
 
 	log(LOG_DEBUG, "%s: %s failed for interface %s: %u\n",
 		__func__, otype, if_name(ifp), error);
 
 	return (error);
 }
 
 int
 ifa_add_loopback_route(struct ifaddr *ifa, struct sockaddr *ia)
 {
 
 	return (ifa_maintain_loopback_route(RTM_ADD, "insertion", ifa, ia));
 }
 
 int
 ifa_del_loopback_route(struct ifaddr *ifa, struct sockaddr *ia)
 {
 
 	return (ifa_maintain_loopback_route(RTM_DELETE, "deletion", ifa, ia));
 }
 
 int
 ifa_switch_loopback_route(struct ifaddr *ifa, struct sockaddr *ia)
 {
 
 	return (ifa_maintain_loopback_route(RTM_CHANGE, "switch", ifa, ia));
 }
 
 
diff --git a/sys/netinet/in.c b/sys/netinet/in.c
index cf6541dca879..d6c0c350dec5 100644
--- a/sys/netinet/in.c
+++ b/sys/netinet/in.c
@@ -1,1573 +1,1694 @@
 /*-
  * SPDX-License-Identifier: BSD-3-Clause
  *
  * Copyright (c) 1982, 1986, 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (C) 2001 WIDE Project.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  *	@(#)in.c	8.4 (Berkeley) 1/9/95
  */
 
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/eventhandler.h>
 #include <sys/systm.h>
 #include <sys/sockio.h>
 #include <sys/malloc.h>
 #include <sys/priv.h>
 #include <sys/socket.h>
 #include <sys/jail.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/proc.h>
 #include <sys/rmlock.h>
 #include <sys/sysctl.h>
 #include <sys/syslog.h>
 #include <sys/sx.h>
 
 #include <net/if.h>
 #include <net/if_var.h>
 #include <net/if_arp.h>
 #include <net/if_dl.h>
 #include <net/if_llatbl.h>
 #include <net/if_types.h>
 #include <net/route.h>
+#include <net/route/nhop.h>
+#include <net/route/route_ctl.h>
 #include <net/vnet.h>
 
 #include <netinet/if_ether.h>
 #include <netinet/in.h>
 #include <netinet/in_var.h>
 #include <netinet/in_pcb.h>
 #include <netinet/ip_var.h>
 #include <netinet/ip_carp.h>
 #include <netinet/igmp_var.h>
 #include <netinet/udp.h>
 #include <netinet/udp_var.h>
 
 static int in_aifaddr_ioctl(u_long, caddr_t, struct ifnet *, struct thread *);
 static int in_difaddr_ioctl(u_long, caddr_t, struct ifnet *, struct thread *);
 static int in_gifaddr_ioctl(u_long, caddr_t, struct ifnet *, struct thread *);
 
 static void	in_socktrim(struct sockaddr_in *);
 static void	in_purgemaddrs(struct ifnet *);
 
 VNET_DEFINE_STATIC(int, nosameprefix);
 #define	V_nosameprefix			VNET(nosameprefix)
 SYSCTL_INT(_net_inet_ip, OID_AUTO, no_same_prefix, CTLFLAG_VNET | CTLFLAG_RW,
 	&VNET_NAME(nosameprefix), 0,
 	"Refuse to create same prefixes on different interfaces");
 
 VNET_DECLARE(struct inpcbinfo, ripcbinfo);
 #define	V_ripcbinfo			VNET(ripcbinfo)
 
 static struct sx in_control_sx;
 SX_SYSINIT(in_control_sx, &in_control_sx, "in_control");
 
 /*
  * Return 1 if an internet address is for a ``local'' host
  * (one to which we have a connection).
  */
 int
 in_localaddr(struct in_addr in)
 {
 	struct rm_priotracker in_ifa_tracker;
 	u_long i = ntohl(in.s_addr);
 	struct in_ifaddr *ia;
 
 	IN_IFADDR_RLOCK(&in_ifa_tracker);
 	CK_STAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
 		if ((i & ia->ia_subnetmask) == ia->ia_subnet) {
 			IN_IFADDR_RUNLOCK(&in_ifa_tracker);
 			return (1);
 		}
 	}
 	IN_IFADDR_RUNLOCK(&in_ifa_tracker);
 	return (0);
 }
 
 /*
  * Return 1 if an internet address is for the local host and configured
  * on one of its interfaces.
  */
 int
 in_localip(struct in_addr in)
 {
 	struct rm_priotracker in_ifa_tracker;
 	struct in_ifaddr *ia;
 
 	IN_IFADDR_RLOCK(&in_ifa_tracker);
 	LIST_FOREACH(ia, INADDR_HASH(in.s_addr), ia_hash) {
 		if (IA_SIN(ia)->sin_addr.s_addr == in.s_addr) {
 			IN_IFADDR_RUNLOCK(&in_ifa_tracker);
 			return (1);
 		}
 	}
 	IN_IFADDR_RUNLOCK(&in_ifa_tracker);
 	return (0);
 }
 
 /*
  * Return 1 if an internet address is configured on an interface.
  */
 int
 in_ifhasaddr(struct ifnet *ifp, struct in_addr in)
 {
 	struct ifaddr *ifa;
 	struct in_ifaddr *ia;
 
 	NET_EPOCH_ASSERT();
 
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 		if (ifa->ifa_addr->sa_family != AF_INET)
 			continue;
 		ia = (struct in_ifaddr *)ifa;
 		if (ia->ia_addr.sin_addr.s_addr == in.s_addr)
 			return (1);
 	}
 
 	return (0);
 }
 
 /*
  * Return a reference to the interface address which is different to
  * the supplied one but with same IP address value.
  */
 static struct in_ifaddr *
 in_localip_more(struct in_ifaddr *ia)
 {
 	struct rm_priotracker in_ifa_tracker;
 	in_addr_t in = IA_SIN(ia)->sin_addr.s_addr;
 	struct in_ifaddr *it;
 
 	IN_IFADDR_RLOCK(&in_ifa_tracker);
 	LIST_FOREACH(it, INADDR_HASH(in), ia_hash) {
 		if (it != ia && IA_SIN(it)->sin_addr.s_addr == in) {
 			ifa_ref(&it->ia_ifa);
 			IN_IFADDR_RUNLOCK(&in_ifa_tracker);
 			return (it);
 		}
 	}
 	IN_IFADDR_RUNLOCK(&in_ifa_tracker);
 
 	return (NULL);
 }
 
 /*
  * Determine whether an IP address is in a reserved set of addresses
  * that may not be forwarded, or whether datagrams to that destination
  * may be forwarded.
  */
 int
 in_canforward(struct in_addr in)
 {
 	u_long i = ntohl(in.s_addr);
 
 	if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i) || IN_LINKLOCAL(i) ||
 	    IN_ZERONET(i) || IN_LOOPBACK(i))
 		return (0);
 	return (1);
 }
 
 /*
  * Trim a mask in a sockaddr
  */
 static void
 in_socktrim(struct sockaddr_in *ap)
 {
     char *cplim = (char *) &ap->sin_addr;
     char *cp = (char *) (&ap->sin_addr + 1);
 
     ap->sin_len = 0;
     while (--cp >= cplim)
 	if (*cp) {
 	    (ap)->sin_len = cp - (char *) (ap) + 1;
 	    break;
 	}
 }
 
 /*
  * Generic internet control operations (ioctl's).
  */
 int
 in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
     struct thread *td)
 {
 	struct ifreq *ifr = (struct ifreq *)data;
 	struct sockaddr_in *addr = (struct sockaddr_in *)&ifr->ifr_addr;
 	struct epoch_tracker et;
 	struct ifaddr *ifa;
 	struct in_ifaddr *ia;
 	int error;
 
 	if (ifp == NULL)
 		return (EADDRNOTAVAIL);
 
 	/*
 	 * Filter out 4 ioctls we implement directly.  Forward the rest
 	 * to specific functions and ifp->if_ioctl().
 	 */
 	switch (cmd) {
 	case SIOCGIFADDR:
 	case SIOCGIFBRDADDR:
 	case SIOCGIFDSTADDR:
 	case SIOCGIFNETMASK:
 		break;
 	case SIOCGIFALIAS:
 		sx_xlock(&in_control_sx);
 		error = in_gifaddr_ioctl(cmd, data, ifp, td);
 		sx_xunlock(&in_control_sx);
 		return (error);
 	case SIOCDIFADDR:
 		sx_xlock(&in_control_sx);
 		error = in_difaddr_ioctl(cmd, data, ifp, td);
 		sx_xunlock(&in_control_sx);
 		return (error);
 	case OSIOCAIFADDR:	/* 9.x compat */
 	case SIOCAIFADDR:
 		sx_xlock(&in_control_sx);
 		error = in_aifaddr_ioctl(cmd, data, ifp, td);
 		sx_xunlock(&in_control_sx);
 		return (error);
 	case SIOCSIFADDR:
 	case SIOCSIFBRDADDR:
 	case SIOCSIFDSTADDR:
 	case SIOCSIFNETMASK:
 		/* We no longer support that old commands. */
 		return (EINVAL);
 	default:
 		if (ifp->if_ioctl == NULL)
 			return (EOPNOTSUPP);
 		return ((*ifp->if_ioctl)(ifp, cmd, data));
 	}
 
 	if (addr->sin_addr.s_addr != INADDR_ANY &&
 	    prison_check_ip4(td->td_ucred, &addr->sin_addr) != 0)
 		return (EADDRNOTAVAIL);
 
 	/*
 	 * Find address for this interface, if it exists.  If an
 	 * address was specified, find that one instead of the
 	 * first one on the interface, if possible.
 	 */
 	NET_EPOCH_ENTER(et);
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 		if (ifa->ifa_addr->sa_family != AF_INET)
 			continue;
 		ia = (struct in_ifaddr *)ifa;
 		if (ia->ia_addr.sin_addr.s_addr == addr->sin_addr.s_addr)
 			break;
 	}
 	if (ifa == NULL)
 		CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
 			if (ifa->ifa_addr->sa_family == AF_INET) {
 				ia = (struct in_ifaddr *)ifa;
 				if (prison_check_ip4(td->td_ucred,
 				    &ia->ia_addr.sin_addr) == 0)
 					break;
 			}
 
 	if (ifa == NULL) {
 		NET_EPOCH_EXIT(et);
 		return (EADDRNOTAVAIL);
 	}
 
 	error = 0;
 	switch (cmd) {
 	case SIOCGIFADDR:
 		*addr = ia->ia_addr;
 		break;
 
 	case SIOCGIFBRDADDR:
 		if ((ifp->if_flags & IFF_BROADCAST) == 0) {
 			error = EINVAL;
 			break;
 		}
 		*addr = ia->ia_broadaddr;
 		break;
 
 	case SIOCGIFDSTADDR:
 		if ((ifp->if_flags & IFF_POINTOPOINT) == 0) {
 			error = EINVAL;
 			break;
 		}
 		*addr = ia->ia_dstaddr;
 		break;
 
 	case SIOCGIFNETMASK:
 		*addr = ia->ia_sockmask;
 		break;
 	}
 
 	NET_EPOCH_EXIT(et);
 
 	return (error);
 }
 
 static int
 in_aifaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td)
 {
 	const struct in_aliasreq *ifra = (struct in_aliasreq *)data;
 	const struct sockaddr_in *addr = &ifra->ifra_addr;
 	const struct sockaddr_in *broadaddr = &ifra->ifra_broadaddr;
 	const struct sockaddr_in *mask = &ifra->ifra_mask;
 	const struct sockaddr_in *dstaddr = &ifra->ifra_dstaddr;
 	const int vhid = (cmd == SIOCAIFADDR) ? ifra->ifra_vhid : 0;
 	struct epoch_tracker et;
 	struct ifaddr *ifa;
 	struct in_ifaddr *ia;
 	bool iaIsFirst;
 	int error = 0;
 
 	error = priv_check(td, PRIV_NET_ADDIFADDR);
 	if (error)
 		return (error);
 
 	/*
 	 * ifra_addr must be present and be of INET family.
 	 * ifra_broadaddr/ifra_dstaddr and ifra_mask are optional.
 	 */
 	if (addr->sin_len != sizeof(struct sockaddr_in) ||
 	    addr->sin_family != AF_INET)
 		return (EINVAL);
 	if (broadaddr->sin_len != 0 &&
 	    (broadaddr->sin_len != sizeof(struct sockaddr_in) ||
 	    broadaddr->sin_family != AF_INET))
 		return (EINVAL);
 	if (mask->sin_len != 0 &&
 	    (mask->sin_len != sizeof(struct sockaddr_in) ||
 	    mask->sin_family != AF_INET))
 		return (EINVAL);
 	if ((ifp->if_flags & IFF_POINTOPOINT) &&
 	    (dstaddr->sin_len != sizeof(struct sockaddr_in) ||
 	     dstaddr->sin_addr.s_addr == INADDR_ANY))
 		return (EDESTADDRREQ);
 	if (vhid > 0 && carp_attach_p == NULL)
 		return (EPROTONOSUPPORT);
 
 	/*
 	 * See whether address already exist.
 	 */
 	iaIsFirst = true;
 	ia = NULL;
 	NET_EPOCH_ENTER(et);
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 		struct in_ifaddr *it;
 
 		if (ifa->ifa_addr->sa_family != AF_INET)
 			continue;
 
 		it = (struct in_ifaddr *)ifa;
 		if (it->ia_addr.sin_addr.s_addr == addr->sin_addr.s_addr &&
 		    prison_check_ip4(td->td_ucred, &addr->sin_addr) == 0)
 			ia = it;
 		else
 			iaIsFirst = false;
 	}
 	NET_EPOCH_EXIT(et);
 
 	if (ia != NULL)
 		(void )in_difaddr_ioctl(cmd, data, ifp, td);
 
 	ifa = ifa_alloc(sizeof(struct in_ifaddr), M_WAITOK);
 	ia = (struct in_ifaddr *)ifa;
 	ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr;
 	ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
 	ifa->ifa_netmask = (struct sockaddr *)&ia->ia_sockmask;
 	callout_init_rw(&ia->ia_garp_timer, &ifp->if_addr_lock,
 	    CALLOUT_RETURNUNLOCKED);
 
 	ia->ia_ifp = ifp;
 	ia->ia_addr = *addr;
 	if (mask->sin_len != 0) {
 		ia->ia_sockmask = *mask;
 		ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr);
 	} else {
 		in_addr_t i = ntohl(addr->sin_addr.s_addr);
 
 		/*
 	 	 * Be compatible with network classes, if netmask isn't
 		 * supplied, guess it based on classes.
 	 	 */
 		if (IN_CLASSA(i))
 			ia->ia_subnetmask = IN_CLASSA_NET;
 		else if (IN_CLASSB(i))
 			ia->ia_subnetmask = IN_CLASSB_NET;
 		else
 			ia->ia_subnetmask = IN_CLASSC_NET;
 		ia->ia_sockmask.sin_addr.s_addr = htonl(ia->ia_subnetmask);
 	}
 	ia->ia_subnet = ntohl(addr->sin_addr.s_addr) & ia->ia_subnetmask;
 	in_socktrim(&ia->ia_sockmask);
 
 	if (ifp->if_flags & IFF_BROADCAST) {
 		if (broadaddr->sin_len != 0) {
 			ia->ia_broadaddr = *broadaddr;
 		} else if (ia->ia_subnetmask == IN_RFC3021_MASK) {
 			ia->ia_broadaddr.sin_addr.s_addr = INADDR_BROADCAST;
 			ia->ia_broadaddr.sin_len = sizeof(struct sockaddr_in);
 			ia->ia_broadaddr.sin_family = AF_INET;
 		} else {
 			ia->ia_broadaddr.sin_addr.s_addr =
 			    htonl(ia->ia_subnet | ~ia->ia_subnetmask);
 			ia->ia_broadaddr.sin_len = sizeof(struct sockaddr_in);
 			ia->ia_broadaddr.sin_family = AF_INET;
 		}
 	}
 
 	if (ifp->if_flags & IFF_POINTOPOINT)
 		ia->ia_dstaddr = *dstaddr;
 
 	/* XXXGL: rtinit() needs this strange assignment. */
 	if (ifp->if_flags & IFF_LOOPBACK)
                 ia->ia_dstaddr = ia->ia_addr;
 
 	if (vhid != 0) {
 		error = (*carp_attach_p)(&ia->ia_ifa, vhid);
 		if (error)
 			return (error);
 	}
 
 	/* if_addrhead is already referenced by ifa_alloc() */
 	IF_ADDR_WLOCK(ifp);
 	CK_STAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link);
 	IF_ADDR_WUNLOCK(ifp);
 
 	ifa_ref(ifa);			/* in_ifaddrhead */
 	IN_IFADDR_WLOCK();
 	CK_STAILQ_INSERT_TAIL(&V_in_ifaddrhead, ia, ia_link);
 	LIST_INSERT_HEAD(INADDR_HASH(ia->ia_addr.sin_addr.s_addr), ia, ia_hash);
 	IN_IFADDR_WUNLOCK();
 
 	/*
 	 * Give the interface a chance to initialize
 	 * if this is its first address,
 	 * and to validate the address if necessary.
 	 */
 	if (ifp->if_ioctl != NULL) {
 		error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia);
 		if (error)
 			goto fail1;
 	}
 
 	/*
 	 * Add route for the network.
 	 */
 	if (vhid == 0) {
 		int flags = RTF_UP;
 
 		if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT))
 			flags |= RTF_HOST;
 
 		error = in_addprefix(ia, flags);
 		if (error)
 			goto fail1;
 	}
 
 	/*
 	 * Add a loopback route to self.
 	 */
 	if (vhid == 0 && (ifp->if_flags & IFF_LOOPBACK) == 0 &&
 	    ia->ia_addr.sin_addr.s_addr != INADDR_ANY &&
 	    !((ifp->if_flags & IFF_POINTOPOINT) &&
 	     ia->ia_dstaddr.sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr)) {
 		struct in_ifaddr *eia;
 
 		eia = in_localip_more(ia);
 
 		if (eia == NULL) {
 			error = ifa_add_loopback_route((struct ifaddr *)ia,
 			    (struct sockaddr *)&ia->ia_addr);
 			if (error)
 				goto fail2;
 		} else
 			ifa_free(&eia->ia_ifa);
 	}
 
 	if (iaIsFirst && (ifp->if_flags & IFF_MULTICAST)) {
 		struct in_addr allhosts_addr;
 		struct in_ifinfo *ii;
 
 		ii = ((struct in_ifinfo *)ifp->if_afdata[AF_INET]);
 		allhosts_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
 
 		error = in_joingroup(ifp, &allhosts_addr, NULL,
 			&ii->ii_allhosts);
 	}
 
 	/*
 	 * Note: we don't need extra reference for ifa, since we called
 	 * with sx lock held, and ifaddr can not be deleted in concurrent
 	 * thread.
 	 */
 	EVENTHANDLER_INVOKE(ifaddr_event_ext, ifp, ifa, IFADDR_EVENT_ADD);
 
 	return (error);
 
 fail2:
 	if (vhid == 0)
 		(void )in_scrubprefix(ia, LLE_STATIC);
 
 fail1:
 	if (ia->ia_ifa.ifa_carp)
 		(*carp_detach_p)(&ia->ia_ifa, false);
 
 	IF_ADDR_WLOCK(ifp);
 	CK_STAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifaddr, ifa_link);
 	IF_ADDR_WUNLOCK(ifp);
 	ifa_free(&ia->ia_ifa);		/* if_addrhead */
 
 	IN_IFADDR_WLOCK();
 	CK_STAILQ_REMOVE(&V_in_ifaddrhead, ia, in_ifaddr, ia_link);
 	LIST_REMOVE(ia, ia_hash);
 	IN_IFADDR_WUNLOCK();
 	ifa_free(&ia->ia_ifa);		/* in_ifaddrhead */
 
 	return (error);
 }
 
 static int
 in_difaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td)
 {
 	const struct ifreq *ifr = (struct ifreq *)data;
 	const struct sockaddr_in *addr = (const struct sockaddr_in *)
 	    &ifr->ifr_addr;
 	struct ifaddr *ifa;
 	struct in_ifaddr *ia;
 	bool deleteAny, iaIsLast;
 	int error;
 
 	if (td != NULL) {
 		error = priv_check(td, PRIV_NET_DELIFADDR);
 		if (error)
 			return (error);
 	}
 
 	if (addr->sin_len != sizeof(struct sockaddr_in) ||
 	    addr->sin_family != AF_INET)
 		deleteAny = true;
 	else
 		deleteAny = false;
 
 	iaIsLast = true;
 	ia = NULL;
 	IF_ADDR_WLOCK(ifp);
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 		struct in_ifaddr *it;
 
 		if (ifa->ifa_addr->sa_family != AF_INET)
 			continue;
 
 		it = (struct in_ifaddr *)ifa;
 		if (deleteAny && ia == NULL && (td == NULL ||
 		    prison_check_ip4(td->td_ucred, &it->ia_addr.sin_addr) == 0))
 			ia = it;
 
 		if (it->ia_addr.sin_addr.s_addr == addr->sin_addr.s_addr &&
 		    (td == NULL || prison_check_ip4(td->td_ucred,
 		    &addr->sin_addr) == 0))
 			ia = it;
 
 		if (it != ia)
 			iaIsLast = false;
 	}
 
 	if (ia == NULL) {
 		IF_ADDR_WUNLOCK(ifp);
 		return (EADDRNOTAVAIL);
 	}
 
 	CK_STAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifaddr, ifa_link);
 	IF_ADDR_WUNLOCK(ifp);
 	ifa_free(&ia->ia_ifa);		/* if_addrhead */
 
 	IN_IFADDR_WLOCK();
 	CK_STAILQ_REMOVE(&V_in_ifaddrhead, ia, in_ifaddr, ia_link);
 	LIST_REMOVE(ia, ia_hash);
 	IN_IFADDR_WUNLOCK();
 
 	/*
 	 * in_scrubprefix() kills the interface route.
 	 */
 	in_scrubprefix(ia, LLE_STATIC);
 
 	/*
 	 * in_ifadown gets rid of all the rest of
 	 * the routes.  This is not quite the right
 	 * thing to do, but at least if we are running
 	 * a routing process they will come back.
 	 */
 	in_ifadown(&ia->ia_ifa, 1);
 
 	if (ia->ia_ifa.ifa_carp)
 		(*carp_detach_p)(&ia->ia_ifa, cmd == SIOCAIFADDR);
 
 	/*
 	 * If this is the last IPv4 address configured on this
 	 * interface, leave the all-hosts group.
 	 * No state-change report need be transmitted.
 	 */
 	if (iaIsLast && (ifp->if_flags & IFF_MULTICAST)) {
 		struct in_ifinfo *ii;
 
 		ii = ((struct in_ifinfo *)ifp->if_afdata[AF_INET]);
 		if (ii->ii_allhosts) {
 			(void)in_leavegroup(ii->ii_allhosts, NULL);
 			ii->ii_allhosts = NULL;
 		}
 	}
 
 	IF_ADDR_WLOCK(ifp);
 	if (callout_stop(&ia->ia_garp_timer) == 1) {
 		ifa_free(&ia->ia_ifa);
 	}
 	IF_ADDR_WUNLOCK(ifp);
 
 	EVENTHANDLER_INVOKE(ifaddr_event_ext, ifp, &ia->ia_ifa,
 	    IFADDR_EVENT_DEL);
 	ifa_free(&ia->ia_ifa);		/* in_ifaddrhead */
 
 	return (0);
 }
 
 static int
 in_gifaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td)
 {
 	struct in_aliasreq *ifra = (struct in_aliasreq *)data;
 	const struct sockaddr_in *addr = &ifra->ifra_addr;
 	struct epoch_tracker et;
 	struct ifaddr *ifa;
 	struct in_ifaddr *ia;
 
 	/*
 	 * ifra_addr must be present and be of INET family.
 	 */
 	if (addr->sin_len != sizeof(struct sockaddr_in) ||
 	    addr->sin_family != AF_INET)
 		return (EINVAL);
 
 	/*
 	 * See whether address exist.
 	 */
 	ia = NULL;
 	NET_EPOCH_ENTER(et);
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 		struct in_ifaddr *it;
 
 		if (ifa->ifa_addr->sa_family != AF_INET)
 			continue;
 
 		it = (struct in_ifaddr *)ifa;
 		if (it->ia_addr.sin_addr.s_addr == addr->sin_addr.s_addr &&
 		    prison_check_ip4(td->td_ucred, &addr->sin_addr) == 0) {
 			ia = it;
 			break;
 		}
 	}
 	if (ia == NULL) {
 		NET_EPOCH_EXIT(et);
 		return (EADDRNOTAVAIL);
 	}
 
 	ifra->ifra_mask = ia->ia_sockmask;
 	if ((ifp->if_flags & IFF_POINTOPOINT) &&
 	    ia->ia_dstaddr.sin_family == AF_INET)
 		ifra->ifra_dstaddr = ia->ia_dstaddr;
 	else if ((ifp->if_flags & IFF_BROADCAST) &&
 	    ia->ia_broadaddr.sin_family == AF_INET)
 		ifra->ifra_broadaddr = ia->ia_broadaddr;
 	else
 		memset(&ifra->ifra_broadaddr, 0,
 		    sizeof(ifra->ifra_broadaddr));
 
 	NET_EPOCH_EXIT(et);
 	return (0);
 }
 
+static int
+in_match_ifaddr(const struct rtentry *rt, const struct nhop_object *nh, void *arg)
+{
+
+	if (nh->nh_ifa == (struct ifaddr *)arg)
+		return (1);
+
+	return (0);
+}
+
+static int
+in_handle_prefix_route(uint32_t fibnum, int cmd,
+    struct sockaddr_in *dst, struct sockaddr_in *netmask, struct ifaddr *ifa)
+{
+
+	NET_EPOCH_ASSERT();
+
+	/* Prepare gateway */
+	struct sockaddr_dl_short sdl = {
+		.sdl_family = AF_LINK,
+		.sdl_len = sizeof(struct sockaddr_dl_short),
+		.sdl_type = ifa->ifa_ifp->if_type,
+		.sdl_index = ifa->ifa_ifp->if_index,
+	};
+
+	struct rt_addrinfo info = {
+		.rti_ifa = ifa,
+		.rti_flags = RTF_PINNED | ((netmask != NULL) ? 0 : RTF_HOST),
+		.rti_info = {
+			[RTAX_DST] = (struct sockaddr *)dst,
+			[RTAX_NETMASK] = (struct sockaddr *)netmask,
+			[RTAX_GATEWAY] = (struct sockaddr *)&sdl,
+		},
+		/* Ensure we delete the prefix IFF prefix ifa matches */
+		.rti_filter = in_match_ifaddr,
+		.rti_filterdata = ifa,
+	};
+
+	return (rib_handle_ifaddr_info(fibnum, cmd, &info));
+}
+
+/*
+ * Adds or delete interface route corresponding to @ifa.
+ * There can be multiple options:
+ * 1) Adding addr with prefix on non-p2p/non-lo interface.
+ *  Example: 192.0.2.1/24. Action: add route towards
+ *  192.0.2.0/24 via this interface, using ifa as an address source.
+ *  Note: route to 192.0.2.1 will be installed separately via
+ *  ifa_maintain_loopback_route().
+ * 2) Adding addr with "host" mask.
+ *  Example: 192.0.2.2/32. In this case no action is performed,
+ *  as the route should be installed by ifa_maintain_loopback_route().
+ *  Returns 0 to indicate success.
+ * 3) Adding address with or without prefix to p2p interface.
+ *  Example: 10.0.0.1/24->10.0.0.2. In this case, all other addresses
+ *  covered by prefix, does not make sense in the context of p2p link.
+ *  Action: add route towards 10.0.0.2 via this interface, using ifa as an
+ *  address source.
+ *  Similar to (1), route to 10.0.0.1 will be installed by
+ *  ifa_maintain_loopback_route().
+ * 4) Adding address with or without prefix to loopback interface.
+ *  Example: 192.0.2.1/24. In this case, trafic to non-host addresses cannot
+ *  be forwarded, as it would introduce an infinite cycle.
+ *  Similar to (2), perform no action and return 0. Loopback route
+ *  will be installed by ifa_maintain_loopback_route().
+ */
+int
+in_handle_ifaddr_route(int cmd, struct in_ifaddr *ia)
+{
+	struct ifaddr *ifa = &ia->ia_ifa;
+	struct in_addr daddr, maddr;
+	struct sockaddr_in *pmask;
+	struct epoch_tracker et;
+	int error;
+
+	/* Case 4: ignore loopbacks */
+	if (ifa->ifa_ifp->if_flags & IFF_LOOPBACK)
+		return (0);
+
+	if (ifa->ifa_ifp->if_flags & IFF_POINTOPOINT) {
+		/* Case 3: install route towards dst addr */
+		daddr = ia->ia_dstaddr.sin_addr;
+		pmask = NULL;
+		maddr.s_addr = INADDR_BROADCAST;
+	} else {
+		daddr = ia->ia_addr.sin_addr;
+		pmask = &ia->ia_sockmask;
+		maddr = pmask->sin_addr;
+
+		if (maddr.s_addr == INADDR_BROADCAST) {
+			/* Case 2: ignore /32 routes */
+			return (0);
+		}
+	}
+
+	struct sockaddr_in mask = {
+		.sin_family = AF_INET,
+		.sin_len = sizeof(struct sockaddr_in),
+		.sin_addr = maddr,
+	};
+
+	if (pmask != NULL)
+		pmask = &mask;
+
+	struct sockaddr_in dst = {
+		.sin_family = AF_INET,
+		.sin_len = sizeof(struct sockaddr_in),
+		.sin_addr.s_addr = daddr.s_addr & maddr.s_addr,
+	};
+
+	uint32_t fibnum = ifa->ifa_ifp->if_fib;
+	NET_EPOCH_ENTER(et);
+	error = in_handle_prefix_route(fibnum, cmd, &dst, pmask, ifa);
+	NET_EPOCH_EXIT(et);
+
+	return (error);
+}
+
+
 #define rtinitflags(x) \
 	((((x)->ia_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) != 0) \
 	    ? RTF_HOST : 0)
 
 /*
  * Check if we have a route for the given prefix already.
  */
 static bool
 in_hasrtprefix(struct in_ifaddr *target, int flags)
 {
 	struct rm_priotracker in_ifa_tracker;
 	struct in_ifaddr *ia;
 	struct in_addr prefix, mask, p, m;
 	bool result = false;
 
 	if ((flags & RTF_HOST) != 0) {
 		prefix = target->ia_dstaddr.sin_addr;
 		mask.s_addr = 0;
 	} else {
 		prefix = target->ia_addr.sin_addr;
 		mask = target->ia_sockmask.sin_addr;
 		prefix.s_addr &= mask.s_addr;
 	}
 
 	IN_IFADDR_RLOCK(&in_ifa_tracker);
 	/* Look for an existing address with the same prefix, mask, and fib */
 	CK_STAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
 		if (rtinitflags(ia)) {
 			p = ia->ia_dstaddr.sin_addr;
 
 			if (prefix.s_addr != p.s_addr)
 				continue;
 		} else {
 			p = ia->ia_addr.sin_addr;
 			m = ia->ia_sockmask.sin_addr;
 			p.s_addr &= m.s_addr;
 
 			if (prefix.s_addr != p.s_addr ||
 			    mask.s_addr != m.s_addr)
 				continue;
 		}
 		if (target->ia_ifp->if_fib != ia->ia_ifp->if_fib)
 			continue;
 
 		/*
 		 * If we got a matching prefix route inserted by other
 		 * interface address, we are done here.
 		 */
 		if (ia->ia_flags & IFA_ROUTE) {
 			result = true;
 			break;
 		}
 	}
 	IN_IFADDR_RUNLOCK(&in_ifa_tracker);
 
 	return (result);
 }
 
 int
 in_addprefix(struct in_ifaddr *target, int flags)
 {
 	int error;
 
 	if (in_hasrtprefix(target, flags)) {
 		if (V_nosameprefix)
 			return (EEXIST);
 		else {
 			rt_addrmsg(RTM_ADD, &target->ia_ifa,
 			    target->ia_ifp->if_fib);
 			return (0);
 		}
 	}
 
 	/*
 	 * No-one seem to have this prefix route, so we try to insert it.
 	 */
-	error = rtinit(&target->ia_ifa, (int)RTM_ADD, flags);
+	rt_addrmsg(RTM_ADD, &target->ia_ifa, target->ia_ifp->if_fib);
+	error = in_handle_ifaddr_route(RTM_ADD, target);
 	if (!error)
 		target->ia_flags |= IFA_ROUTE;
 	return (error);
 }
 
 /*
  * Removes either all lle entries for given @ia, or lle
  * corresponding to @ia address.
  */
 static void
 in_scrubprefixlle(struct in_ifaddr *ia, int all, u_int flags)
 {
 	struct sockaddr_in addr, mask;
 	struct sockaddr *saddr, *smask;
 	struct ifnet *ifp;
 
 	saddr = (struct sockaddr *)&addr;
 	bzero(&addr, sizeof(addr));
 	addr.sin_len = sizeof(addr);
 	addr.sin_family = AF_INET;
 	smask = (struct sockaddr *)&mask;
 	bzero(&mask, sizeof(mask));
 	mask.sin_len = sizeof(mask);
 	mask.sin_family = AF_INET;
 	mask.sin_addr.s_addr = ia->ia_subnetmask;
 	ifp = ia->ia_ifp;
 
 	if (all) {
 		/*
 		 * Remove all L2 entries matching given prefix.
 		 * Convert address to host representation to avoid
 		 * doing this on every callback. ia_subnetmask is already
 		 * stored in host representation.
 		 */
 		addr.sin_addr.s_addr = ntohl(ia->ia_addr.sin_addr.s_addr);
 		lltable_prefix_free(AF_INET, saddr, smask, flags);
 	} else {
 		/* Remove interface address only */
 		addr.sin_addr.s_addr = ia->ia_addr.sin_addr.s_addr;
 		lltable_delete_addr(LLTABLE(ifp), LLE_IFADDR, saddr);
 	}
 }
 
 /*
  * If there is no other address in the system that can serve a route to the
  * same prefix, remove the route.  Hand over the route to the new address
  * otherwise.
  */
 int
 in_scrubprefix(struct in_ifaddr *target, u_int flags)
 {
 	struct rm_priotracker in_ifa_tracker;
 	struct in_ifaddr *ia;
 	struct in_addr prefix, mask, p, m;
 	int error = 0;
 
 	/*
 	 * Remove the loopback route to the interface address.
 	 */
 	if ((target->ia_addr.sin_addr.s_addr != INADDR_ANY) &&
 	    !(target->ia_ifp->if_flags & IFF_LOOPBACK) &&
 	    (flags & LLE_STATIC)) {
 		struct in_ifaddr *eia;
 
 		/*
 		 * XXXME: add fib-aware in_localip.
 		 * We definitely don't want to switch between
 		 * prefixes in different fibs.
 		 */
 		eia = in_localip_more(target);
 
 		if (eia != NULL) {
 			error = ifa_switch_loopback_route((struct ifaddr *)eia,
 			    (struct sockaddr *)&target->ia_addr);
 			ifa_free(&eia->ia_ifa);
 		} else {
 			error = ifa_del_loopback_route((struct ifaddr *)target,
 			    (struct sockaddr *)&target->ia_addr);
 		}
 	}
 
 	if (rtinitflags(target)) {
 		prefix = target->ia_dstaddr.sin_addr;
 		mask.s_addr = 0;
 	} else {
 		prefix = target->ia_addr.sin_addr;
 		mask = target->ia_sockmask.sin_addr;
 		prefix.s_addr &= mask.s_addr;
 	}
 
 	if ((target->ia_flags & IFA_ROUTE) == 0) {
 		rt_addrmsg(RTM_DELETE, &target->ia_ifa, target->ia_ifp->if_fib);
 
 		/*
 		 * Removing address from !IFF_UP interface or
 		 * prefix which exists on other interface (along with route).
 		 * No entries should exist here except target addr.
 		 * Given that, delete this entry only.
 		 */
 		in_scrubprefixlle(target, 0, flags);
 		return (0);
 	}
 
 	IN_IFADDR_RLOCK(&in_ifa_tracker);
 	CK_STAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
 		if (rtinitflags(ia)) {
 			p = ia->ia_dstaddr.sin_addr;
 
 			if (prefix.s_addr != p.s_addr)
 				continue;
 		} else {
 			p = ia->ia_addr.sin_addr;
 			m = ia->ia_sockmask.sin_addr;
 			p.s_addr &= m.s_addr;
 
 			if (prefix.s_addr != p.s_addr ||
 			    mask.s_addr != m.s_addr)
 				continue;
 		}
 
 		if ((ia->ia_ifp->if_flags & IFF_UP) == 0)
 			continue;
 
 		/*
 		 * If we got a matching prefix address, move IFA_ROUTE and
 		 * the route itself to it.  Make sure that routing daemons
 		 * get a heads-up.
 		 */
 		if ((ia->ia_flags & IFA_ROUTE) == 0) {
 			ifa_ref(&ia->ia_ifa);
 			IN_IFADDR_RUNLOCK(&in_ifa_tracker);
-			error = rtinit(&(target->ia_ifa), (int)RTM_DELETE,
-			    rtinitflags(target));
+			error = in_handle_ifaddr_route(RTM_DELETE, target);
 			if (error == 0)
 				target->ia_flags &= ~IFA_ROUTE;
 			else
 				log(LOG_INFO, "in_scrubprefix: err=%d, old prefix delete failed\n",
 					error);
 			/* Scrub all entries IFF interface is different */
 			in_scrubprefixlle(target, target->ia_ifp != ia->ia_ifp,
 			    flags);
-			error = rtinit(&ia->ia_ifa, (int)RTM_ADD,
-			    rtinitflags(ia) | RTF_UP);
+			error = in_handle_ifaddr_route(RTM_ADD, ia);
 			if (error == 0)
 				ia->ia_flags |= IFA_ROUTE;
 			else
 				log(LOG_INFO, "in_scrubprefix: err=%d, new prefix add failed\n",
 					error);
 			ifa_free(&ia->ia_ifa);
 			return (error);
 		}
 	}
 	IN_IFADDR_RUNLOCK(&in_ifa_tracker);
 
 	/*
 	 * remove all L2 entries on the given prefix
 	 */
 	in_scrubprefixlle(target, 1, flags);
 
 	/*
 	 * As no-one seem to have this prefix, we can remove the route.
 	 */
-	error = rtinit(&(target->ia_ifa), (int)RTM_DELETE, rtinitflags(target));
+	rt_addrmsg(RTM_DELETE, &target->ia_ifa, target->ia_ifp->if_fib);
+	error = in_handle_ifaddr_route(RTM_DELETE, target);
 	if (error == 0)
 		target->ia_flags &= ~IFA_ROUTE;
 	else
 		log(LOG_INFO, "in_scrubprefix: err=%d, prefix delete failed\n", error);
 	return (error);
 }
 
 #undef rtinitflags
 
 void
 in_ifscrub_all(void)
 {
 	struct ifnet *ifp;
 	struct ifaddr *ifa, *nifa;
 	struct ifaliasreq ifr;
 
 	IFNET_RLOCK();
 	CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
 		/* Cannot lock here - lock recursion. */
 		/* NET_EPOCH_ENTER(et); */
 		CK_STAILQ_FOREACH_SAFE(ifa, &ifp->if_addrhead, ifa_link, nifa) {
 			if (ifa->ifa_addr->sa_family != AF_INET)
 				continue;
 
 			/*
 			 * This is ugly but the only way for legacy IP to
 			 * cleanly remove addresses and everything attached.
 			 */
 			bzero(&ifr, sizeof(ifr));
 			ifr.ifra_addr = *ifa->ifa_addr;
 			if (ifa->ifa_dstaddr)
 			ifr.ifra_broadaddr = *ifa->ifa_dstaddr;
 			(void)in_control(NULL, SIOCDIFADDR, (caddr_t)&ifr,
 			    ifp, NULL);
 		}
 		/* NET_EPOCH_EXIT(et); */
 		in_purgemaddrs(ifp);
 		igmp_domifdetach(ifp);
 	}
 	IFNET_RUNLOCK();
 }
 
 int
 in_ifaddr_broadcast(struct in_addr in, struct in_ifaddr *ia)
 {
 
 	return ((in.s_addr == ia->ia_broadaddr.sin_addr.s_addr ||
 	     /*
 	      * Check for old-style (host 0) broadcast, but
 	      * taking into account that RFC 3021 obsoletes it.
 	      */
 	    (ia->ia_subnetmask != IN_RFC3021_MASK &&
 	    ntohl(in.s_addr) == ia->ia_subnet)) &&
 	     /*
 	      * Check for an all one subnetmask. These
 	      * only exist when an interface gets a secondary
 	      * address.
 	      */
 	    ia->ia_subnetmask != (u_long)0xffffffff);
 }
 
 /*
  * Return 1 if the address might be a local broadcast address.
  */
 int
 in_broadcast(struct in_addr in, struct ifnet *ifp)
 {
 	struct ifaddr *ifa;
 	int found;
 
 	NET_EPOCH_ASSERT();
 
 	if (in.s_addr == INADDR_BROADCAST ||
 	    in.s_addr == INADDR_ANY)
 		return (1);
 	if ((ifp->if_flags & IFF_BROADCAST) == 0)
 		return (0);
 	found = 0;
 	/*
 	 * Look through the list of addresses for a match
 	 * with a broadcast address.
 	 */
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
 		if (ifa->ifa_addr->sa_family == AF_INET &&
 		    in_ifaddr_broadcast(in, (struct in_ifaddr *)ifa)) {
 			found = 1;
 			break;
 		}
 	return (found);
 }
 
 /*
  * On interface removal, clean up IPv4 data structures hung off of the ifnet.
  */
 void
 in_ifdetach(struct ifnet *ifp)
 {
 	IN_MULTI_LOCK();
 	in_pcbpurgeif0(&V_ripcbinfo, ifp);
 	in_pcbpurgeif0(&V_udbinfo, ifp);
 	in_pcbpurgeif0(&V_ulitecbinfo, ifp);
 	in_purgemaddrs(ifp);
 	IN_MULTI_UNLOCK();
 
 	/*
 	 * Make sure all multicast deletions invoking if_ioctl() are
 	 * completed before returning. Else we risk accessing a freed
 	 * ifnet structure pointer.
 	 */
 	inm_release_wait(NULL);
 }
 
 /*
  * Delete all IPv4 multicast address records, and associated link-layer
  * multicast address records, associated with ifp.
  * XXX It looks like domifdetach runs AFTER the link layer cleanup.
  * XXX This should not race with ifma_protospec being set during
  * a new allocation, if it does, we have bigger problems.
  */
 static void
 in_purgemaddrs(struct ifnet *ifp)
 {
 	struct in_multi_head purgeinms;
 	struct in_multi		*inm;
 	struct ifmultiaddr	*ifma, *next;
 
 	SLIST_INIT(&purgeinms);
 	IN_MULTI_LIST_LOCK();
 
 	/*
 	 * Extract list of in_multi associated with the detaching ifp
 	 * which the PF_INET layer is about to release.
 	 * We need to do this as IF_ADDR_LOCK() may be re-acquired
 	 * by code further down.
 	 */
 	IF_ADDR_WLOCK(ifp);
  restart:
 	CK_STAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) {
 		if (ifma->ifma_addr->sa_family != AF_INET ||
 		    ifma->ifma_protospec == NULL)
 			continue;
 		inm = (struct in_multi *)ifma->ifma_protospec;
 		inm_rele_locked(&purgeinms, inm);
 		if (__predict_false(ifma_restart)) {
 			ifma_restart = true;
 			goto restart;
 		}
 	}
 	IF_ADDR_WUNLOCK(ifp);
 
 	inm_release_list_deferred(&purgeinms);
 	igmp_ifdetach(ifp);
 	IN_MULTI_LIST_UNLOCK();
 }
 
 struct in_llentry {
 	struct llentry		base;
 };
 
 #define	IN_LLTBL_DEFAULT_HSIZE	32
 #define	IN_LLTBL_HASH(k, h) \
 	(((((((k >> 8) ^ k) >> 8) ^ k) >> 8) ^ k) & ((h) - 1))
 
 /*
  * Do actual deallocation of @lle.
  */
 static void
 in_lltable_destroy_lle_unlocked(epoch_context_t ctx)
 {
 	struct llentry *lle;
 
 	lle = __containerof(ctx, struct llentry, lle_epoch_ctx);
 	LLE_LOCK_DESTROY(lle);
 	LLE_REQ_DESTROY(lle);
 	free(lle, M_LLTABLE);
 }
 
 /*
  * Called by the datapath to indicate that
  * the entry was used.
  */
 static void
 in_lltable_mark_used(struct llentry *lle)
 {
 
 	LLE_REQ_LOCK(lle);
 	lle->r_skip_req = 0;
 	LLE_REQ_UNLOCK(lle);
 }
 
 /*
  * Called by LLE_FREE_LOCKED when number of references
  * drops to zero.
  */
 static void
 in_lltable_destroy_lle(struct llentry *lle)
 {
 
 	LLE_WUNLOCK(lle);
 	NET_EPOCH_CALL(in_lltable_destroy_lle_unlocked, &lle->lle_epoch_ctx);
 }
 
 static struct llentry *
 in_lltable_new(struct in_addr addr4, u_int flags)
 {
 	struct in_llentry *lle;
 
 	lle = malloc(sizeof(struct in_llentry), M_LLTABLE, M_NOWAIT | M_ZERO);
 	if (lle == NULL)		/* NB: caller generates msg */
 		return NULL;
 
 	/*
 	 * For IPv4 this will trigger "arpresolve" to generate
 	 * an ARP request.
 	 */
 	lle->base.la_expire = time_uptime; /* mark expired */
 	lle->base.r_l3addr.addr4 = addr4;
 	lle->base.lle_refcnt = 1;
 	lle->base.lle_free = in_lltable_destroy_lle;
 	LLE_LOCK_INIT(&lle->base);
 	LLE_REQ_INIT(&lle->base);
 	callout_init(&lle->base.lle_timer, 1);
 
 	return (&lle->base);
 }
 
 #define IN_ARE_MASKED_ADDR_EQUAL(d, a, m)	(		\
 	((((d).s_addr ^ (a).s_addr) & (m).s_addr)) == 0 )
 
 static int
 in_lltable_match_prefix(const struct sockaddr *saddr,
     const struct sockaddr *smask, u_int flags, struct llentry *lle)
 {
 	struct in_addr addr, mask, lle_addr;
 
 	addr = ((const struct sockaddr_in *)saddr)->sin_addr;
 	mask = ((const struct sockaddr_in *)smask)->sin_addr;
 	lle_addr.s_addr = ntohl(lle->r_l3addr.addr4.s_addr);
 
 	if (IN_ARE_MASKED_ADDR_EQUAL(lle_addr, addr, mask) == 0)
 		return (0);
 
 	if (lle->la_flags & LLE_IFADDR) {
 		/*
 		 * Delete LLE_IFADDR records IFF address & flag matches.
 		 * Note that addr is the interface address within prefix
 		 * being matched.
 		 * Note also we should handle 'ifdown' cases without removing
 		 * ifaddr macs.
 		 */
 		if (addr.s_addr == lle_addr.s_addr && (flags & LLE_STATIC) != 0)
 			return (1);
 		return (0);
 	}
 
 	/* flags & LLE_STATIC means deleting both dynamic and static entries */
 	if ((flags & LLE_STATIC) || !(lle->la_flags & LLE_STATIC))
 		return (1);
 
 	return (0);
 }
 
 static void
 in_lltable_free_entry(struct lltable *llt, struct llentry *lle)
 {
 	size_t pkts_dropped;
 
 	LLE_WLOCK_ASSERT(lle);
 	KASSERT(llt != NULL, ("lltable is NULL"));
 
 	/* Unlink entry from table if not already */
 	if ((lle->la_flags & LLE_LINKED) != 0) {
 		IF_AFDATA_WLOCK_ASSERT(llt->llt_ifp);
 		lltable_unlink_entry(llt, lle);
 	}
 
 	/* Drop hold queue */
 	pkts_dropped = llentry_free(lle);
 	ARPSTAT_ADD(dropped, pkts_dropped);
 }
 
 static int
 in_lltable_rtcheck(struct ifnet *ifp, u_int flags, const struct sockaddr *l3addr)
 {
 	struct rt_addrinfo info;
 	struct sockaddr_in rt_key, rt_mask;
 	struct sockaddr rt_gateway;
 	int rt_flags;
 
 	KASSERT(l3addr->sa_family == AF_INET,
 	    ("sin_family %d", l3addr->sa_family));
 
 	bzero(&rt_key, sizeof(rt_key));
 	rt_key.sin_len = sizeof(rt_key);
 	bzero(&rt_mask, sizeof(rt_mask));
 	rt_mask.sin_len = sizeof(rt_mask);
 	bzero(&rt_gateway, sizeof(rt_gateway));
 	rt_gateway.sa_len = sizeof(rt_gateway);
 
 	bzero(&info, sizeof(info));
 	info.rti_info[RTAX_DST] = (struct sockaddr *)&rt_key;
 	info.rti_info[RTAX_NETMASK] = (struct sockaddr *)&rt_mask;
 	info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&rt_gateway;
 
 	if (rib_lookup_info(ifp->if_fib, l3addr, NHR_REF, 0, &info) != 0)
 		return (EINVAL);
 
 	rt_flags = info.rti_flags;
 
 	/*
 	 * If the gateway for an existing host route matches the target L3
 	 * address, which is a special route inserted by some implementation
 	 * such as MANET, and the interface is of the correct type, then
 	 * allow for ARP to proceed.
 	 */
 	if (rt_flags & RTF_GATEWAY) {
 		if (!(rt_flags & RTF_HOST) || !info.rti_ifp ||
 		    info.rti_ifp->if_type != IFT_ETHER ||
 		    (info.rti_ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) != 0 ||
 		    memcmp(rt_gateway.sa_data, l3addr->sa_data,
 		    sizeof(in_addr_t)) != 0) {
 			rib_free_info(&info);
 			return (EINVAL);
 		}
 	}
 	rib_free_info(&info);
 
 	/*
 	 * Make sure that at least the destination address is covered
 	 * by the route. This is for handling the case where 2 or more
 	 * interfaces have the same prefix. An incoming packet arrives
 	 * on one interface and the corresponding outgoing packet leaves
 	 * another interface.
 	 */
 	if (!(rt_flags & RTF_HOST) && info.rti_ifp != ifp) {
 		const char *sa, *mask, *addr, *lim;
 		const struct sockaddr_in *l3sin;
 
 		mask = (const char *)&rt_mask;
 		/*
 		 * Just being extra cautious to avoid some custom
 		 * code getting into trouble.
 		 */
 		if ((info.rti_addrs & RTA_NETMASK) == 0)
 			return (EINVAL);
 
 		sa = (const char *)&rt_key;
 		addr = (const char *)l3addr;
 		l3sin = (const struct sockaddr_in *)l3addr;
 		lim = addr + l3sin->sin_len;
 
 		for ( ; addr < lim; sa++, mask++, addr++) {
 			if ((*sa ^ *addr) & *mask) {
 #ifdef DIAGNOSTIC
 				char addrbuf[INET_ADDRSTRLEN];
 
 				log(LOG_INFO, "IPv4 address: \"%s\" "
 				    "is not on the network\n",
 				    inet_ntoa_r(l3sin->sin_addr, addrbuf));
 #endif
 				return (EINVAL);
 			}
 		}
 	}
 
 	return (0);
 }
 
 static inline uint32_t
 in_lltable_hash_dst(const struct in_addr dst, uint32_t hsize)
 {
 
 	return (IN_LLTBL_HASH(dst.s_addr, hsize));
 }
 
 static uint32_t
 in_lltable_hash(const struct llentry *lle, uint32_t hsize)
 {
 
 	return (in_lltable_hash_dst(lle->r_l3addr.addr4, hsize));
 }
 
 static void
 in_lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa)
 {
 	struct sockaddr_in *sin;
 
 	sin = (struct sockaddr_in *)sa;
 	bzero(sin, sizeof(*sin));
 	sin->sin_family = AF_INET;
 	sin->sin_len = sizeof(*sin);
 	sin->sin_addr = lle->r_l3addr.addr4;
 }
 
 static inline struct llentry *
 in_lltable_find_dst(struct lltable *llt, struct in_addr dst)
 {
 	struct llentry *lle;
 	struct llentries *lleh;
 	u_int hashidx;
 
 	hashidx = in_lltable_hash_dst(dst, llt->llt_hsize);
 	lleh = &llt->lle_head[hashidx];
 	CK_LIST_FOREACH(lle, lleh, lle_next) {
 		if (lle->la_flags & LLE_DELETED)
 			continue;
 		if (lle->r_l3addr.addr4.s_addr == dst.s_addr)
 			break;
 	}
 
 	return (lle);
 }
 
 static void
 in_lltable_delete_entry(struct lltable *llt, struct llentry *lle)
 {
 
 	lle->la_flags |= LLE_DELETED;
 	EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_DELETED);
 #ifdef DIAGNOSTIC
 	log(LOG_INFO, "ifaddr cache = %p is deleted\n", lle);
 #endif
 	llentry_free(lle);
 }
 
 static struct llentry *
 in_lltable_alloc(struct lltable *llt, u_int flags, const struct sockaddr *l3addr)
 {
 	const struct sockaddr_in *sin = (const struct sockaddr_in *)l3addr;
 	struct ifnet *ifp = llt->llt_ifp;
 	struct llentry *lle;
 	char linkhdr[LLE_MAX_LINKHDR];
 	size_t linkhdrsize;
 	int lladdr_off;
 
 	KASSERT(l3addr->sa_family == AF_INET,
 	    ("sin_family %d", l3addr->sa_family));
 
 	/*
 	 * A route that covers the given address must have
 	 * been installed 1st because we are doing a resolution,
 	 * verify this.
 	 */
 	if (!(flags & LLE_IFADDR) &&
 	    in_lltable_rtcheck(ifp, flags, l3addr) != 0)
 		return (NULL);
 
 	lle = in_lltable_new(sin->sin_addr, flags);
 	if (lle == NULL) {
 		log(LOG_INFO, "lla_lookup: new lle malloc failed\n");
 		return (NULL);
 	}
 	lle->la_flags = flags;
 	if (flags & LLE_STATIC)
 		lle->r_flags |= RLLE_VALID;
 	if ((flags & LLE_IFADDR) == LLE_IFADDR) {
 		linkhdrsize = LLE_MAX_LINKHDR;
 		if (lltable_calc_llheader(ifp, AF_INET, IF_LLADDR(ifp),
 		    linkhdr, &linkhdrsize, &lladdr_off) != 0) {
 			NET_EPOCH_CALL(in_lltable_destroy_lle_unlocked, &lle->lle_epoch_ctx);
 			return (NULL);
 		}
 		lltable_set_entry_addr(ifp, lle, linkhdr, linkhdrsize,
 		    lladdr_off);
 		lle->la_flags |= LLE_STATIC;
 		lle->r_flags |= (RLLE_VALID | RLLE_IFADDR);
 	}
 
 	return (lle);
 }
 
 /*
  * Return NULL if not found or marked for deletion.
  * If found return lle read locked.
  */
 static struct llentry *
 in_lltable_lookup(struct lltable *llt, u_int flags, const struct sockaddr *l3addr)
 {
 	const struct sockaddr_in *sin = (const struct sockaddr_in *)l3addr;
 	struct llentry *lle;
 
 	IF_AFDATA_LOCK_ASSERT(llt->llt_ifp);
 	KASSERT(l3addr->sa_family == AF_INET,
 	    ("sin_family %d", l3addr->sa_family));
 	KASSERT((flags & (LLE_UNLOCKED | LLE_EXCLUSIVE)) !=
 	    (LLE_UNLOCKED | LLE_EXCLUSIVE),
 	    ("wrong lle request flags: %#x", flags));
 
 	lle = in_lltable_find_dst(llt, sin->sin_addr);
 	if (lle == NULL)
 		return (NULL);
 	if (flags & LLE_UNLOCKED)
 		return (lle);
 
 	if (flags & LLE_EXCLUSIVE)
 		LLE_WLOCK(lle);
 	else
 		LLE_RLOCK(lle);
 
 	/*
 	 * If the afdata lock is not held, the LLE may have been unlinked while
 	 * we were blocked on the LLE lock.  Check for this case.
 	 */
 	if (__predict_false((lle->la_flags & LLE_LINKED) == 0)) {
 		if (flags & LLE_EXCLUSIVE)
 			LLE_WUNLOCK(lle);
 		else
 			LLE_RUNLOCK(lle);
 		return (NULL);
 	}
 	return (lle);
 }
 
 static int
 in_lltable_dump_entry(struct lltable *llt, struct llentry *lle,
     struct sysctl_req *wr)
 {
 	struct ifnet *ifp = llt->llt_ifp;
 	/* XXX stack use */
 	struct {
 		struct rt_msghdr	rtm;
 		struct sockaddr_in	sin;
 		struct sockaddr_dl	sdl;
 	} arpc;
 	struct sockaddr_dl *sdl;
 	int error;
 
 	bzero(&arpc, sizeof(arpc));
 	/* skip deleted entries */
 	if ((lle->la_flags & LLE_DELETED) == LLE_DELETED)
 		return (0);
 	/* Skip if jailed and not a valid IP of the prison. */
 	lltable_fill_sa_entry(lle,(struct sockaddr *)&arpc.sin);
 	if (prison_if(wr->td->td_ucred, (struct sockaddr *)&arpc.sin) != 0)
 		return (0);
 	/*
 	 * produce a msg made of:
 	 *  struct rt_msghdr;
 	 *  struct sockaddr_in; (IPv4)
 	 *  struct sockaddr_dl;
 	 */
 	arpc.rtm.rtm_msglen = sizeof(arpc);
 	arpc.rtm.rtm_version = RTM_VERSION;
 	arpc.rtm.rtm_type = RTM_GET;
 	arpc.rtm.rtm_flags = RTF_UP;
 	arpc.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY;
 
 	/* publish */
 	if (lle->la_flags & LLE_PUB)
 		arpc.rtm.rtm_flags |= RTF_ANNOUNCE;
 
 	sdl = &arpc.sdl;
 	sdl->sdl_family = AF_LINK;
 	sdl->sdl_len = sizeof(*sdl);
 	sdl->sdl_index = ifp->if_index;
 	sdl->sdl_type = ifp->if_type;
 	if ((lle->la_flags & LLE_VALID) == LLE_VALID) {
 		sdl->sdl_alen = ifp->if_addrlen;
 		bcopy(lle->ll_addr, LLADDR(sdl), ifp->if_addrlen);
 	} else {
 		sdl->sdl_alen = 0;
 		bzero(LLADDR(sdl), ifp->if_addrlen);
 	}
 
 	arpc.rtm.rtm_rmx.rmx_expire =
 	    lle->la_flags & LLE_STATIC ? 0 : lle->la_expire;
 	arpc.rtm.rtm_flags |= (RTF_HOST | RTF_LLDATA);
 	if (lle->la_flags & LLE_STATIC)
 		arpc.rtm.rtm_flags |= RTF_STATIC;
 	if (lle->la_flags & LLE_IFADDR)
 		arpc.rtm.rtm_flags |= RTF_PINNED;
 	arpc.rtm.rtm_index = ifp->if_index;
 	error = SYSCTL_OUT(wr, &arpc, sizeof(arpc));
 
 	return (error);
 }
 
 static struct lltable *
 in_lltattach(struct ifnet *ifp)
 {
 	struct lltable *llt;
 
 	llt = lltable_allocate_htbl(IN_LLTBL_DEFAULT_HSIZE);
  	llt->llt_af = AF_INET;
  	llt->llt_ifp = ifp;
 
 	llt->llt_lookup = in_lltable_lookup;
 	llt->llt_alloc_entry = in_lltable_alloc;
 	llt->llt_delete_entry = in_lltable_delete_entry;
 	llt->llt_dump_entry = in_lltable_dump_entry;
 	llt->llt_hash = in_lltable_hash;
 	llt->llt_fill_sa_entry = in_lltable_fill_sa_entry;
 	llt->llt_free_entry = in_lltable_free_entry;
 	llt->llt_match_prefix = in_lltable_match_prefix;
 	llt->llt_mark_used = in_lltable_mark_used;
  	lltable_link(llt);
 
 	return (llt);
 }
 
 void *
 in_domifattach(struct ifnet *ifp)
 {
 	struct in_ifinfo *ii;
 
 	ii = malloc(sizeof(struct in_ifinfo), M_IFADDR, M_WAITOK|M_ZERO);
 
 	ii->ii_llt = in_lltattach(ifp);
 	ii->ii_igmp = igmp_domifattach(ifp);
 
 	return (ii);
 }
 
 void
 in_domifdetach(struct ifnet *ifp, void *aux)
 {
 	struct in_ifinfo *ii = (struct in_ifinfo *)aux;
 
 	igmp_domifdetach(ifp);
 	lltable_free(ii->ii_llt);
 	free(ii, M_IFADDR);
 }
diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h
index fabd8e1ab50c..3a83c5e832ab 100644
--- a/sys/netinet/in_var.h
+++ b/sys/netinet/in_var.h
@@ -1,483 +1,484 @@
 /*-
  * SPDX-License-Identifier: BSD-3-Clause
  *
  * Copyright (c) 1985, 1986, 1993
  *	The Regents of the University of California.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  *	@(#)in_var.h	8.2 (Berkeley) 1/9/95
  * $FreeBSD$
  */
 
 #ifndef _NETINET_IN_VAR_H_
 #define _NETINET_IN_VAR_H_
 
 /*
  * Argument structure for SIOCAIFADDR.
  */
 struct	in_aliasreq {
 	char	ifra_name[IFNAMSIZ];		/* if name, e.g. "en0" */
 	struct	sockaddr_in ifra_addr;
 	struct	sockaddr_in ifra_broadaddr;
 #define ifra_dstaddr ifra_broadaddr
 	struct	sockaddr_in ifra_mask;
 	int	ifra_vhid;
 };
 
 #ifdef _KERNEL
 #include <sys/queue.h>
 #include <sys/fnv_hash.h>
 #include <sys/tree.h>
 
 struct igmp_ifsoftc;
 struct in_multi;
 struct lltable;
 SLIST_HEAD(in_multi_head, in_multi);
 
 /*
  * IPv4 per-interface state.
  */
 struct in_ifinfo {
 	struct lltable		*ii_llt;	/* ARP state */
 	struct igmp_ifsoftc	*ii_igmp;	/* IGMP state */
 	struct in_multi		*ii_allhosts;	/* 224.0.0.1 membership */
 };
 
 /*
  * Interface address, Internet version.  One of these structures
  * is allocated for each Internet address on an interface.
  * The ifaddr structure contains the protocol-independent part
  * of the structure and is assumed to be first.
  */
 struct in_ifaddr {
 	struct	ifaddr ia_ifa;		/* protocol-independent info */
 #define	ia_ifp		ia_ifa.ifa_ifp
 #define ia_flags	ia_ifa.ifa_flags
 					/* ia_subnet{,mask} in host order */
 	u_long	ia_subnet;		/* subnet address */
 	u_long	ia_subnetmask;		/* mask of subnet */
 	LIST_ENTRY(in_ifaddr) ia_hash;	/* entry in bucket of inet addresses */
 	CK_STAILQ_ENTRY(in_ifaddr) ia_link;	/* list of internet addresses */
 	struct	sockaddr_in ia_addr;	/* reserve space for interface name */
 	struct	sockaddr_in ia_dstaddr; /* reserve space for broadcast addr */
 #define	ia_broadaddr	ia_dstaddr
 	struct	sockaddr_in ia_sockmask; /* reserve space for general netmask */
 	struct	callout ia_garp_timer;	/* timer for retransmitting GARPs */
 	int	ia_garp_count;		/* count of retransmitted GARPs */
 };
 
 /*
  * Given a pointer to an in_ifaddr (ifaddr),
  * return a pointer to the addr as a sockaddr_in.
  */
 #define IA_SIN(ia)    (&(((struct in_ifaddr *)(ia))->ia_addr))
 #define IA_DSTSIN(ia) (&(((struct in_ifaddr *)(ia))->ia_dstaddr))
 #define IA_MASKSIN(ia) (&(((struct in_ifaddr *)(ia))->ia_sockmask))
 
 #define IN_LNAOF(in, ifa) \
 	((ntohl((in).s_addr) & ~((struct in_ifaddr *)(ifa)->ia_subnetmask))
 
 extern	u_char	inetctlerrmap[];
 
 #define LLTABLE(ifp)	\
 	((struct in_ifinfo *)(ifp)->if_afdata[AF_INET])->ii_llt
 /*
  * Hash table for IP addresses.
  */
 CK_STAILQ_HEAD(in_ifaddrhead, in_ifaddr);
 LIST_HEAD(in_ifaddrhashhead, in_ifaddr);
 
 VNET_DECLARE(struct in_ifaddrhashhead *, in_ifaddrhashtbl);
 VNET_DECLARE(struct in_ifaddrhead, in_ifaddrhead);
 VNET_DECLARE(u_long, in_ifaddrhmask);		/* mask for hash table */
 
 #define	V_in_ifaddrhashtbl	VNET(in_ifaddrhashtbl)
 #define	V_in_ifaddrhead		VNET(in_ifaddrhead)
 #define	V_in_ifaddrhmask	VNET(in_ifaddrhmask)
 
 #define INADDR_NHASH_LOG2       9
 #define INADDR_NHASH		(1 << INADDR_NHASH_LOG2)
 #define INADDR_HASHVAL(x)	fnv_32_buf((&(x)), sizeof(x), FNV1_32_INIT)
 #define INADDR_HASH(x) \
 	(&V_in_ifaddrhashtbl[INADDR_HASHVAL(x) & V_in_ifaddrhmask])
 
 extern	struct rmlock in_ifaddr_lock;
 
 #define	IN_IFADDR_LOCK_ASSERT()	rm_assert(&in_ifaddr_lock, RA_LOCKED)
 #define	IN_IFADDR_RLOCK(t)	rm_rlock(&in_ifaddr_lock, (t))
 #define	IN_IFADDR_RLOCK_ASSERT()	rm_assert(&in_ifaddr_lock, RA_RLOCKED)
 #define	IN_IFADDR_RUNLOCK(t)	rm_runlock(&in_ifaddr_lock, (t))
 #define	IN_IFADDR_WLOCK()	rm_wlock(&in_ifaddr_lock)
 #define	IN_IFADDR_WLOCK_ASSERT()	rm_assert(&in_ifaddr_lock, RA_WLOCKED)
 #define	IN_IFADDR_WUNLOCK()	rm_wunlock(&in_ifaddr_lock)
 
 /*
  * Macro for finding the internet address structure (in_ifaddr)
  * corresponding to one of our IP addresses (in_addr).
  */
 #define INADDR_TO_IFADDR(addr, ia) \
 	/* struct in_addr addr; */ \
 	/* struct in_ifaddr *ia; */ \
 do { \
 \
 	LIST_FOREACH(ia, INADDR_HASH((addr).s_addr), ia_hash) \
 		if (IA_SIN(ia)->sin_addr.s_addr == (addr).s_addr) \
 			break; \
 } while (0)
 
 /*
  * Macro for finding the interface (ifnet structure) corresponding to one
  * of our IP addresses.
  */
 #define INADDR_TO_IFP(addr, ifp) \
 	/* struct in_addr addr; */ \
 	/* struct ifnet *ifp; */ \
 { \
 	struct in_ifaddr *ia; \
 \
 	INADDR_TO_IFADDR(addr, ia); \
 	(ifp) = (ia == NULL) ? NULL : ia->ia_ifp; \
 }
 
 /*
  * Macro for finding the internet address structure (in_ifaddr) corresponding
  * to a given interface (ifnet structure).
  */
 #define IFP_TO_IA(ifp, ia, t)						\
 	/* struct ifnet *ifp; */					\
 	/* struct in_ifaddr *ia; */					\
 	/* struct rm_priotracker *t; */					\
 do {									\
 	NET_EPOCH_ASSERT();						\
 	IN_IFADDR_RLOCK((t));						\
 	for ((ia) = CK_STAILQ_FIRST(&V_in_ifaddrhead);			\
 	    (ia) != NULL && (ia)->ia_ifp != (ifp);			\
 	    (ia) = CK_STAILQ_NEXT((ia), ia_link))				\
 		continue;						\
 	IN_IFADDR_RUNLOCK((t));						\
 } while (0)
 
 /*
  * Legacy IPv4 IGMP per-link structure.
  */
 struct router_info {
 	struct ifnet *rti_ifp;
 	int    rti_type; /* type of router which is querier on this interface */
 	int    rti_time; /* # of slow timeouts since last old query */
 	SLIST_ENTRY(router_info) rti_list;
 };
 
 /*
  * IPv4 multicast IGMP-layer source entry.
  */
 struct ip_msource {
 	RB_ENTRY(ip_msource)	ims_link;	/* RB tree links */
 	in_addr_t		ims_haddr;	/* host byte order */
 	struct ims_st {
 		uint16_t	ex;		/* # of exclusive members */
 		uint16_t	in;		/* # of inclusive members */
 	}			ims_st[2];	/* state at t0, t1 */
 	uint8_t			ims_stp;	/* pending query */
 };
 
 /*
  * IPv4 multicast PCB-layer source entry.
  */
 struct in_msource {
 	RB_ENTRY(ip_msource)	ims_link;	/* RB tree links */
 	in_addr_t		ims_haddr;	/* host byte order */
 	uint8_t			imsl_st[2];	/* state before/at commit */
 };
 
 RB_HEAD(ip_msource_tree, ip_msource);	/* define struct ip_msource_tree */
 
 static __inline int
 ip_msource_cmp(const struct ip_msource *a, const struct ip_msource *b)
 {
 
 	if (a->ims_haddr < b->ims_haddr)
 		return (-1);
 	if (a->ims_haddr == b->ims_haddr)
 		return (0);
 	return (1);
 }
 RB_PROTOTYPE(ip_msource_tree, ip_msource, ims_link, ip_msource_cmp);
 
 /*
  * IPv4 multicast PCB-layer group filter descriptor.
  */
 struct in_mfilter {
 	struct ip_msource_tree	imf_sources; /* source list for (S,G) */
 	u_long			imf_nsrc;    /* # of source entries */
 	uint8_t			imf_st[2];   /* state before/at commit */
 	struct in_multi	       *imf_inm;     /* associated multicast address */
 	STAILQ_ENTRY(in_mfilter) imf_entry;  /* list entry */
 };
 
 /*
  * Helper types and functions for IPv4 multicast filters.
  */
 STAILQ_HEAD(ip_mfilter_head, in_mfilter);
 
 struct in_mfilter *ip_mfilter_alloc(int mflags, int st0, int st1);
 void ip_mfilter_free(struct in_mfilter *);
 
 static inline void
 ip_mfilter_init(struct ip_mfilter_head *head)
 {
 
 	STAILQ_INIT(head);
 }
 
 static inline struct in_mfilter *
 ip_mfilter_first(const struct ip_mfilter_head *head)
 {
 
 	return (STAILQ_FIRST(head));
 }
 
 static inline void
 ip_mfilter_insert(struct ip_mfilter_head *head, struct in_mfilter *imf)
 {
 
 	STAILQ_INSERT_TAIL(head, imf, imf_entry);
 }
 
 static inline void
 ip_mfilter_remove(struct ip_mfilter_head *head, struct in_mfilter *imf)
 {
 
 	STAILQ_REMOVE(head, imf, in_mfilter, imf_entry);
 }
 
 #define	IP_MFILTER_FOREACH(imf, head) \
 	STAILQ_FOREACH(imf, head, imf_entry)
 
 static inline size_t
 ip_mfilter_count(struct ip_mfilter_head *head)
 {
 	struct in_mfilter *imf;
 	size_t num = 0;
 
 	STAILQ_FOREACH(imf, head, imf_entry)
 		num++;
 	return (num);
 }
 
 /*
  * IPv4 group descriptor.
  *
  * For every entry on an ifnet's if_multiaddrs list which represents
  * an IP multicast group, there is one of these structures.
  *
  * If any source filters are present, then a node will exist in the RB-tree
  * to permit fast lookup by source whenever an operation takes place.
  * This permits pre-order traversal when we issue reports.
  * Source filter trees are kept separately from the socket layer to
  * greatly simplify locking.
  *
  * When IGMPv3 is active, inm_timer is the response to group query timer.
  * The state-change timer inm_sctimer is separate; whenever state changes
  * for the group the state change record is generated and transmitted,
  * and kept if retransmissions are necessary.
  *
  * FUTURE: inm_link is now only used when groups are being purged
  * on a detaching ifnet. It could be demoted to a SLIST_ENTRY, but
  * because it is at the very start of the struct, we can't do this
  * w/o breaking the ABI for ifmcstat.
  */
 struct in_multi {
 	LIST_ENTRY(in_multi) inm_link;	/* to-be-released by in_ifdetach */
 	struct	in_addr inm_addr;	/* IP multicast address, convenience */
 	struct	ifnet *inm_ifp;		/* back pointer to ifnet */
 	struct	ifmultiaddr *inm_ifma;	/* back pointer to ifmultiaddr */
 	u_int	inm_timer;		/* IGMPv1/v2 group / v3 query timer */
 	u_int	inm_state;		/* state of the membership */
 	void	*inm_rti;		/* unused, legacy field */
 	u_int	inm_refcount;		/* reference count */
 
 	/* New fields for IGMPv3 follow. */
 	struct igmp_ifsoftc	*inm_igi;	/* IGMP info */
 	SLIST_ENTRY(in_multi)	 inm_nrele;	/* to-be-released by IGMP */
 	struct ip_msource_tree	 inm_srcs;	/* tree of sources */
 	u_long			 inm_nsrc;	/* # of tree entries */
 
 	struct mbufq		 inm_scq;	/* queue of pending
 						 * state-change packets */
 	struct timeval		 inm_lastgsrtv;	/* Time of last G-S-R query */
 	uint16_t		 inm_sctimer;	/* state-change timer */
 	uint16_t		 inm_scrv;	/* state-change rexmit count */
 
 	/*
 	 * SSM state counters which track state at T0 (the time the last
 	 * state-change report's RV timer went to zero) and T1
 	 * (time of pending report, i.e. now).
 	 * Used for computing IGMPv3 state-change reports. Several refcounts
 	 * are maintained here to optimize for common use-cases.
 	 */
 	struct inm_st {
 		uint16_t	iss_fmode;	/* IGMP filter mode */
 		uint16_t	iss_asm;	/* # of ASM listeners */
 		uint16_t	iss_ex;		/* # of exclusive members */
 		uint16_t	iss_in;		/* # of inclusive members */
 		uint16_t	iss_rec;	/* # of recorded sources */
 	}			inm_st[2];	/* state at t0, t1 */
 };
 
 /*
  * Helper function to derive the filter mode on a source entry
  * from its internal counters. Predicates are:
  *  A source is only excluded if all listeners exclude it.
  *  A source is only included if no listeners exclude it,
  *  and at least one listener includes it.
  * May be used by ifmcstat(8).
  */
 static __inline uint8_t
 ims_get_mode(const struct in_multi *inm, const struct ip_msource *ims,
     uint8_t t)
 {
 
 	t = !!t;
 	if (inm->inm_st[t].iss_ex > 0 &&
 	    inm->inm_st[t].iss_ex == ims->ims_st[t].ex)
 		return (MCAST_EXCLUDE);
 	else if (ims->ims_st[t].in > 0 && ims->ims_st[t].ex == 0)
 		return (MCAST_INCLUDE);
 	return (MCAST_UNDEFINED);
 }
 
 #ifdef SYSCTL_DECL
 SYSCTL_DECL(_net_inet);
 SYSCTL_DECL(_net_inet_ip);
 SYSCTL_DECL(_net_inet_raw);
 #endif
 
 /*
  * Lock macros for IPv4 layer multicast address lists.  IPv4 lock goes
  * before link layer multicast locks in the lock order.  In most cases,
  * consumers of IN_*_MULTI() macros should acquire the locks before
  * calling them; users of the in_{add,del}multi() functions should not.
  */
 extern struct mtx in_multi_list_mtx;
 extern struct sx in_multi_sx;
 
 #define	IN_MULTI_LIST_LOCK()		mtx_lock(&in_multi_list_mtx)
 #define	IN_MULTI_LIST_UNLOCK()	mtx_unlock(&in_multi_list_mtx)
 #define	IN_MULTI_LIST_LOCK_ASSERT()	mtx_assert(&in_multi_list_mtx, MA_OWNED)
 #define	IN_MULTI_LIST_UNLOCK_ASSERT() mtx_assert(&in_multi_list_mtx, MA_NOTOWNED)
 
 #define	IN_MULTI_LOCK()		sx_xlock(&in_multi_sx)
 #define	IN_MULTI_UNLOCK()	sx_xunlock(&in_multi_sx)
 #define	IN_MULTI_LOCK_ASSERT()	sx_assert(&in_multi_sx, SA_XLOCKED)
 #define	IN_MULTI_UNLOCK_ASSERT() sx_assert(&in_multi_sx, SA_XUNLOCKED)
 
 void inm_disconnect(struct in_multi *inm);
 extern int ifma_restart;
 
 /* Acquire an in_multi record. */
 static __inline void
 inm_acquire_locked(struct in_multi *inm)
 {
 
 	IN_MULTI_LIST_LOCK_ASSERT();
 	++inm->inm_refcount;
 }
 
 static __inline void
 inm_acquire(struct in_multi *inm)
 {
 	IN_MULTI_LIST_LOCK();
 	inm_acquire_locked(inm);
 	IN_MULTI_LIST_UNLOCK();
 }
 
 static __inline void
 inm_rele_locked(struct in_multi_head *inmh, struct in_multi *inm)
 {
 	MPASS(inm->inm_refcount > 0);
 	IN_MULTI_LIST_LOCK_ASSERT();
 
 	if (--inm->inm_refcount == 0) {
 		MPASS(inmh != NULL);
 		inm_disconnect(inm);
 		inm->inm_ifma->ifma_protospec = NULL;
 		SLIST_INSERT_HEAD(inmh, inm, inm_nrele);
 	}
 }
 
 /*
  * Return values for imo_multi_filter().
  */
 #define MCAST_PASS		0	/* Pass */
 #define MCAST_NOTGMEMBER	1	/* This host not a member of group */
 #define MCAST_NOTSMEMBER	2	/* This host excluded source */
 #define MCAST_MUTED		3	/* [deprecated] */
 
 struct rib_head;
 struct	ip_moptions;
 
 struct in_multi *inm_lookup_locked(struct ifnet *, const struct in_addr);
 struct in_multi *inm_lookup(struct ifnet *, const struct in_addr);
 int	imo_multi_filter(const struct ip_moptions *, const struct ifnet *,
 	    const struct sockaddr *, const struct sockaddr *);
 void	inm_commit(struct in_multi *);
 void	inm_clear_recorded(struct in_multi *);
 void	inm_print(const struct in_multi *);
 int	inm_record_source(struct in_multi *inm, const in_addr_t);
 void	inm_release_deferred(struct in_multi *);
 void	inm_release_list_deferred(struct in_multi_head *);
 void	inm_release_wait(void *);
 struct	in_multi *
 in_addmulti(struct in_addr *, struct ifnet *);
 int	in_joingroup(struct ifnet *, const struct in_addr *,
 	    /*const*/ struct in_mfilter *, struct in_multi **);
 int	in_joingroup_locked(struct ifnet *, const struct in_addr *,
 	    /*const*/ struct in_mfilter *, struct in_multi **);
 int	in_leavegroup(struct in_multi *, /*const*/ struct in_mfilter *);
 int	in_leavegroup_locked(struct in_multi *,
 	    /*const*/ struct in_mfilter *);
 int	in_control(struct socket *, u_long, caddr_t, struct ifnet *,
 	    struct thread *);
 int	in_addprefix(struct in_ifaddr *, int);
 int	in_scrubprefix(struct in_ifaddr *, u_int);
 void	in_ifscrub_all(void);
+int	in_handle_ifaddr_route(int, struct in_ifaddr *);
 void	ip_input(struct mbuf *);
 void	ip_direct_input(struct mbuf *);
 void	in_ifadown(struct ifaddr *ifa, int);
 struct	mbuf	*ip_tryforward(struct mbuf *);
 void	*in_domifattach(struct ifnet *);
 void	in_domifdetach(struct ifnet *, void *);
 struct rib_head *in_inithead(uint32_t fibnum);
 #ifdef VIMAGE
 void	in_detachhead(struct rib_head *rh);
 #endif
 
 #endif /* _KERNEL */
 
 /* INET6 stuff */
 #include <netinet6/in6_var.h>
 
 #endif /* _NETINET_IN_VAR_H_ */
diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c
index a63fc19587f9..c9def015343c 100644
--- a/sys/netinet/raw_ip.c
+++ b/sys/netinet/raw_ip.c
@@ -1,1183 +1,1185 @@
 /*-
  * SPDX-License-Identifier: BSD-3-Clause
  *
  * Copyright (c) 1982, 1986, 1988, 1993
  *	The Regents of the University of California.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  *	@(#)raw_ip.c	8.7 (Berkeley) 5/15/95
  */
 
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
 #include "opt_ipsec.h"
 #include "opt_route.h"
 
 #include <sys/param.h>
 #include <sys/jail.h>
 #include <sys/kernel.h>
 #include <sys/eventhandler.h>
 #include <sys/lock.h>
 #include <sys/malloc.h>
 #include <sys/mbuf.h>
 #include <sys/priv.h>
 #include <sys/proc.h>
 #include <sys/protosw.h>
 #include <sys/rmlock.h>
 #include <sys/rwlock.h>
 #include <sys/signalvar.h>
 #include <sys/socket.h>
 #include <sys/socketvar.h>
 #include <sys/sx.h>
 #include <sys/sysctl.h>
 #include <sys/systm.h>
 
 #include <vm/uma.h>
 
 #include <net/if.h>
 #include <net/if_var.h>
 #include <net/route.h>
+#include <net/route/route_ctl.h>
 #include <net/vnet.h>
 
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
 #include <netinet/in_fib.h>
 #include <netinet/in_pcb.h>
 #include <netinet/in_var.h>
 #include <netinet/if_ether.h>
 #include <netinet/ip.h>
 #include <netinet/ip_var.h>
 #include <netinet/ip_mroute.h>
 #include <netinet/ip_icmp.h>
 
 #include <netipsec/ipsec_support.h>
 
 #include <machine/stdarg.h>
 #include <security/mac/mac_framework.h>
 
 VNET_DEFINE(int, ip_defttl) = IPDEFTTL;
 SYSCTL_INT(_net_inet_ip, IPCTL_DEFTTL, ttl, CTLFLAG_VNET | CTLFLAG_RW,
     &VNET_NAME(ip_defttl), 0,
     "Maximum TTL on IP packets");
 
 VNET_DEFINE(struct inpcbhead, ripcb);
 VNET_DEFINE(struct inpcbinfo, ripcbinfo);
 
 #define	V_ripcb			VNET(ripcb)
 #define	V_ripcbinfo		VNET(ripcbinfo)
 
 /*
  * Control and data hooks for ipfw, dummynet, divert and so on.
  * The data hooks are not used here but it is convenient
  * to keep them all in one place.
  */
 VNET_DEFINE(ip_fw_chk_ptr_t, ip_fw_chk_ptr) = NULL;
 VNET_DEFINE(ip_fw_ctl_ptr_t, ip_fw_ctl_ptr) = NULL;
 
 int	(*ip_dn_ctl_ptr)(struct sockopt *);
 int	(*ip_dn_io_ptr)(struct mbuf **, struct ip_fw_args *);
 void	(*ip_divert_ptr)(struct mbuf *, bool);
 int	(*ng_ipfw_input_p)(struct mbuf **, struct ip_fw_args *, bool);
 
 #ifdef INET
 /*
  * Hooks for multicast routing. They all default to NULL, so leave them not
  * initialized and rely on BSS being set to 0.
  */
 
 /*
  * The socket used to communicate with the multicast routing daemon.
  */
 VNET_DEFINE(struct socket *, ip_mrouter);
 
 /*
  * The various mrouter and rsvp functions.
  */
 int (*ip_mrouter_set)(struct socket *, struct sockopt *);
 int (*ip_mrouter_get)(struct socket *, struct sockopt *);
 int (*ip_mrouter_done)(void);
 int (*ip_mforward)(struct ip *, struct ifnet *, struct mbuf *,
 		   struct ip_moptions *);
 int (*mrt_ioctl)(u_long, caddr_t, int);
 int (*legal_vif_num)(int);
 u_long (*ip_mcast_src)(int);
 
 int (*rsvp_input_p)(struct mbuf **, int *, int);
 int (*ip_rsvp_vif)(struct socket *, struct sockopt *);
 void (*ip_rsvp_force_done)(struct socket *);
 #endif /* INET */
 
 extern	struct protosw inetsw[];
 
 u_long	rip_sendspace = 9216;
 SYSCTL_ULONG(_net_inet_raw, OID_AUTO, maxdgram, CTLFLAG_RW,
     &rip_sendspace, 0, "Maximum outgoing raw IP datagram size");
 
 u_long	rip_recvspace = 9216;
 SYSCTL_ULONG(_net_inet_raw, OID_AUTO, recvspace, CTLFLAG_RW,
     &rip_recvspace, 0, "Maximum space for incoming raw IP datagrams");
 
 /*
  * Hash functions
  */
 
 #define INP_PCBHASH_RAW_SIZE	256
 #define INP_PCBHASH_RAW(proto, laddr, faddr, mask) \
         (((proto) + (laddr) + (faddr)) % (mask) + 1)
 
 #ifdef INET
 static void
 rip_inshash(struct inpcb *inp)
 {
 	struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
 	struct inpcbhead *pcbhash;
 	int hash;
 
 	INP_INFO_WLOCK_ASSERT(pcbinfo);
 	INP_WLOCK_ASSERT(inp);
 
 	if (inp->inp_ip_p != 0 &&
 	    inp->inp_laddr.s_addr != INADDR_ANY &&
 	    inp->inp_faddr.s_addr != INADDR_ANY) {
 		hash = INP_PCBHASH_RAW(inp->inp_ip_p, inp->inp_laddr.s_addr,
 		    inp->inp_faddr.s_addr, pcbinfo->ipi_hashmask);
 	} else
 		hash = 0;
 	pcbhash = &pcbinfo->ipi_hashbase[hash];
 	CK_LIST_INSERT_HEAD(pcbhash, inp, inp_hash);
 }
 
 static void
 rip_delhash(struct inpcb *inp)
 {
 
 	INP_INFO_WLOCK_ASSERT(inp->inp_pcbinfo);
 	INP_WLOCK_ASSERT(inp);
 
 	CK_LIST_REMOVE(inp, inp_hash);
 }
 #endif /* INET */
 
 /*
  * Raw interface to IP protocol.
  */
 
 /*
  * Initialize raw connection block q.
  */
 static void
 rip_zone_change(void *tag)
 {
 
 	uma_zone_set_max(V_ripcbinfo.ipi_zone, maxsockets);
 }
 
 static int
 rip_inpcb_init(void *mem, int size, int flags)
 {
 	struct inpcb *inp = mem;
 
 	INP_LOCK_INIT(inp, "inp", "rawinp");
 	return (0);
 }
 
 void
 rip_init(void)
 {
 
 	in_pcbinfo_init(&V_ripcbinfo, "rip", &V_ripcb, INP_PCBHASH_RAW_SIZE,
 	    1, "ripcb", rip_inpcb_init, IPI_HASHFIELDS_NONE);
 	EVENTHANDLER_REGISTER(maxsockets_change, rip_zone_change, NULL,
 	    EVENTHANDLER_PRI_ANY);
 }
 
 #ifdef VIMAGE
 static void
 rip_destroy(void *unused __unused)
 {
 
 	in_pcbinfo_destroy(&V_ripcbinfo);
 }
 VNET_SYSUNINIT(raw_ip, SI_SUB_PROTO_DOMAIN, SI_ORDER_FOURTH, rip_destroy, NULL);
 #endif
 
 #ifdef INET
 static int
 rip_append(struct inpcb *last, struct ip *ip, struct mbuf *n,
     struct sockaddr_in *ripsrc)
 {
 	int policyfail = 0;
 
 	INP_LOCK_ASSERT(last);
 
 #if defined(IPSEC) || defined(IPSEC_SUPPORT)
 	/* check AH/ESP integrity. */
 	if (IPSEC_ENABLED(ipv4)) {
 		if (IPSEC_CHECK_POLICY(ipv4, n, last) != 0)
 			policyfail = 1;
 	}
 #endif /* IPSEC */
 #ifdef MAC
 	if (!policyfail && mac_inpcb_check_deliver(last, n) != 0)
 		policyfail = 1;
 #endif
 	/* Check the minimum TTL for socket. */
 	if (last->inp_ip_minttl && last->inp_ip_minttl > ip->ip_ttl)
 		policyfail = 1;
 	if (!policyfail) {
 		struct mbuf *opts = NULL;
 		struct socket *so;
 
 		so = last->inp_socket;
 		if ((last->inp_flags & INP_CONTROLOPTS) ||
 		    (so->so_options & (SO_TIMESTAMP | SO_BINTIME)))
 			ip_savecontrol(last, &opts, ip, n);
 		SOCKBUF_LOCK(&so->so_rcv);
 		if (sbappendaddr_locked(&so->so_rcv,
 		    (struct sockaddr *)ripsrc, n, opts) == 0) {
 			/* should notify about lost packet */
 			m_freem(n);
 			if (opts)
 				m_freem(opts);
 			SOCKBUF_UNLOCK(&so->so_rcv);
 		} else
 			sorwakeup_locked(so);
 	} else
 		m_freem(n);
 	return (policyfail);
 }
 
 /*
  * Setup generic address and protocol structures for raw_input routine, then
  * pass them along with mbuf chain.
  */
 int
 rip_input(struct mbuf **mp, int *offp, int proto)
 {
 	struct ifnet *ifp;
 	struct mbuf *m = *mp;
 	struct ip *ip = mtod(m, struct ip *);
 	struct inpcb *inp, *last;
 	struct sockaddr_in ripsrc;
 	int hash;
 
 	NET_EPOCH_ASSERT();
 
 	*mp = NULL;
 
 	bzero(&ripsrc, sizeof(ripsrc));
 	ripsrc.sin_len = sizeof(ripsrc);
 	ripsrc.sin_family = AF_INET;
 	ripsrc.sin_addr = ip->ip_src;
 	last = NULL;
 
 	ifp = m->m_pkthdr.rcvif;
 
 	hash = INP_PCBHASH_RAW(proto, ip->ip_src.s_addr,
 	    ip->ip_dst.s_addr, V_ripcbinfo.ipi_hashmask);
 	CK_LIST_FOREACH(inp, &V_ripcbinfo.ipi_hashbase[hash], inp_hash) {
 		if (inp->inp_ip_p != proto)
 			continue;
 #ifdef INET6
 		/* XXX inp locking */
 		if ((inp->inp_vflag & INP_IPV4) == 0)
 			continue;
 #endif
 		if (inp->inp_laddr.s_addr != ip->ip_dst.s_addr)
 			continue;
 		if (inp->inp_faddr.s_addr != ip->ip_src.s_addr)
 			continue;
 		if (last != NULL) {
 			struct mbuf *n;
 
 			n = m_copym(m, 0, M_COPYALL, M_NOWAIT);
 			if (n != NULL)
 			    (void) rip_append(last, ip, n, &ripsrc);
 			/* XXX count dropped packet */
 			INP_RUNLOCK(last);
 			last = NULL;
 		}
 		INP_RLOCK(inp);
 		if (__predict_false(inp->inp_flags2 & INP_FREED))
 			goto skip_1;
 		if (jailed_without_vnet(inp->inp_cred)) {
 			/*
 			 * XXX: If faddr was bound to multicast group,
 			 * jailed raw socket will drop datagram.
 			 */
 			if (prison_check_ip4(inp->inp_cred, &ip->ip_dst) != 0)
 				goto skip_1;
 		}
 		last = inp;
 		continue;
 	skip_1:
 		INP_RUNLOCK(inp);
 	}
 	CK_LIST_FOREACH(inp, &V_ripcbinfo.ipi_hashbase[0], inp_hash) {
 		if (inp->inp_ip_p && inp->inp_ip_p != proto)
 			continue;
 #ifdef INET6
 		/* XXX inp locking */
 		if ((inp->inp_vflag & INP_IPV4) == 0)
 			continue;
 #endif
 		if (!in_nullhost(inp->inp_laddr) &&
 		    !in_hosteq(inp->inp_laddr, ip->ip_dst))
 			continue;
 		if (!in_nullhost(inp->inp_faddr) &&
 		    !in_hosteq(inp->inp_faddr, ip->ip_src))
 			continue;
 		if (last != NULL) {
 			struct mbuf *n;
 
 			n = m_copym(m, 0, M_COPYALL, M_NOWAIT);
 			if (n != NULL)
 				(void) rip_append(last, ip, n, &ripsrc);
 			/* XXX count dropped packet */
 			INP_RUNLOCK(last);
 			last = NULL;
 		}
 		INP_RLOCK(inp);
 		if (__predict_false(inp->inp_flags2 & INP_FREED))
 			goto skip_2;
 		if (jailed_without_vnet(inp->inp_cred)) {
 			/*
 			 * Allow raw socket in jail to receive multicast;
 			 * assume process had PRIV_NETINET_RAW at attach,
 			 * and fall through into normal filter path if so.
 			 */
 			if (!IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) &&
 			    prison_check_ip4(inp->inp_cred, &ip->ip_dst) != 0)
 				goto skip_2;
 		}
 		/*
 		 * If this raw socket has multicast state, and we
 		 * have received a multicast, check if this socket
 		 * should receive it, as multicast filtering is now
 		 * the responsibility of the transport layer.
 		 */
 		if (inp->inp_moptions != NULL &&
 		    IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
 			/*
 			 * If the incoming datagram is for IGMP, allow it
 			 * through unconditionally to the raw socket.
 			 *
 			 * In the case of IGMPv2, we may not have explicitly
 			 * joined the group, and may have set IFF_ALLMULTI
 			 * on the interface. imo_multi_filter() may discard
 			 * control traffic we actually need to see.
 			 *
 			 * Userland multicast routing daemons should continue
 			 * filter the control traffic appropriately.
 			 */
 			int blocked;
 
 			blocked = MCAST_PASS;
 			if (proto != IPPROTO_IGMP) {
 				struct sockaddr_in group;
 
 				bzero(&group, sizeof(struct sockaddr_in));
 				group.sin_len = sizeof(struct sockaddr_in);
 				group.sin_family = AF_INET;
 				group.sin_addr = ip->ip_dst;
 
 				blocked = imo_multi_filter(inp->inp_moptions,
 				    ifp,
 				    (struct sockaddr *)&group,
 				    (struct sockaddr *)&ripsrc);
 			}
 
 			if (blocked != MCAST_PASS) {
 				IPSTAT_INC(ips_notmember);
 				goto skip_2;
 			}
 		}
 		last = inp;
 		continue;
 	skip_2:
 		INP_RUNLOCK(inp);
 	}
 	if (last != NULL) {
 		if (rip_append(last, ip, m, &ripsrc) != 0)
 			IPSTAT_INC(ips_delivered);
 		INP_RUNLOCK(last);
 	} else {
 		if (inetsw[ip_protox[ip->ip_p]].pr_input == rip_input) {
 			IPSTAT_INC(ips_noproto);
 			IPSTAT_DEC(ips_delivered);
 			icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PROTOCOL, 0, 0);
 		} else {
 			m_freem(m);
 		}
 	}
 	return (IPPROTO_DONE);
 }
 
 /*
  * Generate IP header and pass packet to ip_output.  Tack on options user may
  * have setup with control call.
  */
 int
 rip_output(struct mbuf *m, struct socket *so, ...)
 {
 	struct epoch_tracker et;
 	struct ip *ip;
 	int error;
 	struct inpcb *inp = sotoinpcb(so);
 	va_list ap;
 	u_long dst;
 	int flags = ((so->so_options & SO_DONTROUTE) ? IP_ROUTETOIF : 0) |
 	    IP_ALLOWBROADCAST;
 	int cnt, hlen;
 	u_char opttype, optlen, *cp;
 
 	va_start(ap, so);
 	dst = va_arg(ap, u_long);
 	va_end(ap);
 
 	/*
 	 * If the user handed us a complete IP packet, use it.  Otherwise,
 	 * allocate an mbuf for a header and fill it in.
 	 */
 	if ((inp->inp_flags & INP_HDRINCL) == 0) {
 		if (m->m_pkthdr.len + sizeof(struct ip) > IP_MAXPACKET) {
 			m_freem(m);
 			return(EMSGSIZE);
 		}
 		M_PREPEND(m, sizeof(struct ip), M_NOWAIT);
 		if (m == NULL)
 			return(ENOBUFS);
 
 		INP_RLOCK(inp);
 		ip = mtod(m, struct ip *);
 		ip->ip_tos = inp->inp_ip_tos;
 		if (inp->inp_flags & INP_DONTFRAG)
 			ip->ip_off = htons(IP_DF);
 		else
 			ip->ip_off = htons(0);
 		ip->ip_p = inp->inp_ip_p;
 		ip->ip_len = htons(m->m_pkthdr.len);
 		ip->ip_src = inp->inp_laddr;
 		ip->ip_dst.s_addr = dst;
 #ifdef ROUTE_MPATH
 		if (CALC_FLOWID_OUTBOUND) {
 			uint32_t hash_type, hash_val;
 
 			hash_val = fib4_calc_software_hash(ip->ip_src,
 			    ip->ip_dst, 0, 0, ip->ip_p, &hash_type);
 			m->m_pkthdr.flowid = hash_val;
 			M_HASHTYPE_SET(m, hash_type);
 			flags |= IP_NODEFAULTFLOWID;
 		}
 #endif
 		if (jailed(inp->inp_cred)) {
 			/*
 			 * prison_local_ip4() would be good enough but would
 			 * let a source of INADDR_ANY pass, which we do not
 			 * want to see from jails.
 			 */
 			if (ip->ip_src.s_addr == INADDR_ANY) {
 				NET_EPOCH_ENTER(et);
 				error = in_pcbladdr(inp, &ip->ip_dst,
 				    &ip->ip_src, inp->inp_cred);
 				NET_EPOCH_EXIT(et);
 			} else {
 				error = prison_local_ip4(inp->inp_cred,
 				    &ip->ip_src);
 			}
 			if (error != 0) {
 				INP_RUNLOCK(inp);
 				m_freem(m);
 				return (error);
 			}
 		}
 		ip->ip_ttl = inp->inp_ip_ttl;
 	} else {
 		if (m->m_pkthdr.len > IP_MAXPACKET) {
 			m_freem(m);
 			return(EMSGSIZE);
 		}
 		ip = mtod(m, struct ip *);
 		hlen = ip->ip_hl << 2;
 		if (m->m_len < hlen) {
 			m = m_pullup(m, hlen);
 			if (m == NULL)
 				return (EINVAL);
 			ip = mtod(m, struct ip *);
 		}
 #ifdef ROUTE_MPATH
 		if (CALC_FLOWID_OUTBOUND) {
 			uint32_t hash_type, hash_val;
 
 			hash_val = fib4_calc_software_hash(ip->ip_dst,
 			    ip->ip_src, 0, 0, ip->ip_p, &hash_type);
 			m->m_pkthdr.flowid = hash_val;
 			M_HASHTYPE_SET(m, hash_type);
 			flags |= IP_NODEFAULTFLOWID;
 		}
 #endif
 		INP_RLOCK(inp);
 		/*
 		 * Don't allow both user specified and setsockopt options,
 		 * and don't allow packet length sizes that will crash.
 		 */
 		if ((hlen < sizeof (*ip))
 		    || ((hlen > sizeof (*ip)) && inp->inp_options)
 		    || (ntohs(ip->ip_len) != m->m_pkthdr.len)) {
 			INP_RUNLOCK(inp);
 			m_freem(m);
 			return (EINVAL);
 		}
 		error = prison_check_ip4(inp->inp_cred, &ip->ip_src);
 		if (error != 0) {
 			INP_RUNLOCK(inp);
 			m_freem(m);
 			return (error);
 		}
 		/*
 		 * Don't allow IP options which do not have the required
 		 * structure as specified in section 3.1 of RFC 791 on
 		 * pages 15-23.
 		 */
 		cp = (u_char *)(ip + 1);
 		cnt = hlen - sizeof (struct ip);
 		for (; cnt > 0; cnt -= optlen, cp += optlen) {
 			opttype = cp[IPOPT_OPTVAL];
 			if (opttype == IPOPT_EOL)
 				break;
 			if (opttype == IPOPT_NOP) {
 				optlen = 1;
 				continue;
 			}
 			if (cnt < IPOPT_OLEN + sizeof(u_char)) {
 				INP_RUNLOCK(inp);
 				m_freem(m);
 				return (EINVAL);
 			}
 			optlen = cp[IPOPT_OLEN];
 			if (optlen < IPOPT_OLEN + sizeof(u_char) ||
 			    optlen > cnt) {
 				INP_RUNLOCK(inp);
 				m_freem(m);
 				return (EINVAL);
 			}
 		}
 		/*
 		 * This doesn't allow application to specify ID of zero,
 		 * but we got this limitation from the beginning of history.
 		 */
 		if (ip->ip_id == 0)
 			ip_fillid(ip);
 
 		/*
 		 * XXX prevent ip_output from overwriting header fields.
 		 */
 		flags |= IP_RAWOUTPUT;
 		IPSTAT_INC(ips_rawout);
 	}
 
 	if (inp->inp_flags & INP_ONESBCAST)
 		flags |= IP_SENDONES;
 
 #ifdef MAC
 	mac_inpcb_create_mbuf(inp, m);
 #endif
 
 	NET_EPOCH_ENTER(et);
 	error = ip_output(m, inp->inp_options, NULL, flags,
 	    inp->inp_moptions, inp);
 	NET_EPOCH_EXIT(et);
 	INP_RUNLOCK(inp);
 	return (error);
 }
 
 /*
  * Raw IP socket option processing.
  *
  * IMPORTANT NOTE regarding access control: Traditionally, raw sockets could
  * only be created by a privileged process, and as such, socket option
  * operations to manage system properties on any raw socket were allowed to
  * take place without explicit additional access control checks.  However,
  * raw sockets can now also be created in jail(), and therefore explicit
  * checks are now required.  Likewise, raw sockets can be used by a process
  * after it gives up privilege, so some caution is required.  For options
  * passed down to the IP layer via ip_ctloutput(), checks are assumed to be
  * performed in ip_ctloutput() and therefore no check occurs here.
  * Unilaterally checking priv_check() here breaks normal IP socket option
  * operations on raw sockets.
  *
  * When adding new socket options here, make sure to add access control
  * checks here as necessary.
  *
  * XXX-BZ inp locking?
  */
 int
 rip_ctloutput(struct socket *so, struct sockopt *sopt)
 {
 	struct	inpcb *inp = sotoinpcb(so);
 	int	error, optval;
 
 	if (sopt->sopt_level != IPPROTO_IP) {
 		if ((sopt->sopt_level == SOL_SOCKET) &&
 		    (sopt->sopt_name == SO_SETFIB)) {
 			inp->inp_inc.inc_fibnum = so->so_fibnum;
 			return (0);
 		}
 		return (EINVAL);
 	}
 
 	error = 0;
 	switch (sopt->sopt_dir) {
 	case SOPT_GET:
 		switch (sopt->sopt_name) {
 		case IP_HDRINCL:
 			optval = inp->inp_flags & INP_HDRINCL;
 			error = sooptcopyout(sopt, &optval, sizeof optval);
 			break;
 
 		case IP_FW3:	/* generic ipfw v.3 functions */
 		case IP_FW_ADD:	/* ADD actually returns the body... */
 		case IP_FW_GET:
 		case IP_FW_TABLE_GETSIZE:
 		case IP_FW_TABLE_LIST:
 		case IP_FW_NAT_GET_CONFIG:
 		case IP_FW_NAT_GET_LOG:
 			if (V_ip_fw_ctl_ptr != NULL)
 				error = V_ip_fw_ctl_ptr(sopt);
 			else
 				error = ENOPROTOOPT;
 			break;
 
 		case IP_DUMMYNET3:	/* generic dummynet v.3 functions */
 		case IP_DUMMYNET_GET:
 			if (ip_dn_ctl_ptr != NULL)
 				error = ip_dn_ctl_ptr(sopt);
 			else
 				error = ENOPROTOOPT;
 			break ;
 
 		case MRT_INIT:
 		case MRT_DONE:
 		case MRT_ADD_VIF:
 		case MRT_DEL_VIF:
 		case MRT_ADD_MFC:
 		case MRT_DEL_MFC:
 		case MRT_VERSION:
 		case MRT_ASSERT:
 		case MRT_API_SUPPORT:
 		case MRT_API_CONFIG:
 		case MRT_ADD_BW_UPCALL:
 		case MRT_DEL_BW_UPCALL:
 			error = priv_check(curthread, PRIV_NETINET_MROUTE);
 			if (error != 0)
 				return (error);
 			error = ip_mrouter_get ? ip_mrouter_get(so, sopt) :
 				EOPNOTSUPP;
 			break;
 
 		default:
 			error = ip_ctloutput(so, sopt);
 			break;
 		}
 		break;
 
 	case SOPT_SET:
 		switch (sopt->sopt_name) {
 		case IP_HDRINCL:
 			error = sooptcopyin(sopt, &optval, sizeof optval,
 					    sizeof optval);
 			if (error)
 				break;
 			if (optval)
 				inp->inp_flags |= INP_HDRINCL;
 			else
 				inp->inp_flags &= ~INP_HDRINCL;
 			break;
 
 		case IP_FW3:	/* generic ipfw v.3 functions */
 		case IP_FW_ADD:
 		case IP_FW_DEL:
 		case IP_FW_FLUSH:
 		case IP_FW_ZERO:
 		case IP_FW_RESETLOG:
 		case IP_FW_TABLE_ADD:
 		case IP_FW_TABLE_DEL:
 		case IP_FW_TABLE_FLUSH:
 		case IP_FW_NAT_CFG:
 		case IP_FW_NAT_DEL:
 			if (V_ip_fw_ctl_ptr != NULL)
 				error = V_ip_fw_ctl_ptr(sopt);
 			else
 				error = ENOPROTOOPT;
 			break;
 
 		case IP_DUMMYNET3:	/* generic dummynet v.3 functions */
 		case IP_DUMMYNET_CONFIGURE:
 		case IP_DUMMYNET_DEL:
 		case IP_DUMMYNET_FLUSH:
 			if (ip_dn_ctl_ptr != NULL)
 				error = ip_dn_ctl_ptr(sopt);
 			else
 				error = ENOPROTOOPT ;
 			break ;
 
 		case IP_RSVP_ON:
 			error = priv_check(curthread, PRIV_NETINET_MROUTE);
 			if (error != 0)
 				return (error);
 			error = ip_rsvp_init(so);
 			break;
 
 		case IP_RSVP_OFF:
 			error = priv_check(curthread, PRIV_NETINET_MROUTE);
 			if (error != 0)
 				return (error);
 			error = ip_rsvp_done();
 			break;
 
 		case IP_RSVP_VIF_ON:
 		case IP_RSVP_VIF_OFF:
 			error = priv_check(curthread, PRIV_NETINET_MROUTE);
 			if (error != 0)
 				return (error);
 			error = ip_rsvp_vif ?
 				ip_rsvp_vif(so, sopt) : EINVAL;
 			break;
 
 		case MRT_INIT:
 		case MRT_DONE:
 		case MRT_ADD_VIF:
 		case MRT_DEL_VIF:
 		case MRT_ADD_MFC:
 		case MRT_DEL_MFC:
 		case MRT_VERSION:
 		case MRT_ASSERT:
 		case MRT_API_SUPPORT:
 		case MRT_API_CONFIG:
 		case MRT_ADD_BW_UPCALL:
 		case MRT_DEL_BW_UPCALL:
 			error = priv_check(curthread, PRIV_NETINET_MROUTE);
 			if (error != 0)
 				return (error);
 			error = ip_mrouter_set ? ip_mrouter_set(so, sopt) :
 					EOPNOTSUPP;
 			break;
 
 		default:
 			error = ip_ctloutput(so, sopt);
 			break;
 		}
 		break;
 	}
 
 	return (error);
 }
 
 /*
  * This function exists solely to receive the PRC_IFDOWN messages which are
  * sent by if_down().  It looks for an ifaddr whose ifa_addr is sa, and calls
  * in_ifadown() to remove all routes corresponding to that address.  It also
  * receives the PRC_IFUP messages from if_up() and reinstalls the interface
  * routes.
  */
 void
 rip_ctlinput(int cmd, struct sockaddr *sa, void *vip)
 {
 	struct rm_priotracker in_ifa_tracker;
 	struct in_ifaddr *ia;
 	struct ifnet *ifp;
 	int err;
 	int flags;
 
 	switch (cmd) {
 	case PRC_IFDOWN:
 		IN_IFADDR_RLOCK(&in_ifa_tracker);
 		CK_STAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
 			if (ia->ia_ifa.ifa_addr == sa
 			    && (ia->ia_flags & IFA_ROUTE)) {
 				ifa_ref(&ia->ia_ifa);
 				IN_IFADDR_RUNLOCK(&in_ifa_tracker);
 				/*
 				 * in_scrubprefix() kills the interface route.
 				 */
 				in_scrubprefix(ia, 0);
 				/*
 				 * in_ifadown gets rid of all the rest of the
 				 * routes.  This is not quite the right thing
 				 * to do, but at least if we are running a
 				 * routing process they will come back.
 				 */
 				in_ifadown(&ia->ia_ifa, 0);
 				ifa_free(&ia->ia_ifa);
 				break;
 			}
 		}
 		if (ia == NULL)		/* If ia matched, already unlocked. */
 			IN_IFADDR_RUNLOCK(&in_ifa_tracker);
 		break;
 
 	case PRC_IFUP:
 		IN_IFADDR_RLOCK(&in_ifa_tracker);
 		CK_STAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
 			if (ia->ia_ifa.ifa_addr == sa)
 				break;
 		}
 		if (ia == NULL || (ia->ia_flags & IFA_ROUTE)) {
 			IN_IFADDR_RUNLOCK(&in_ifa_tracker);
 			return;
 		}
 		ifa_ref(&ia->ia_ifa);
 		IN_IFADDR_RUNLOCK(&in_ifa_tracker);
 		flags = RTF_UP;
 		ifp = ia->ia_ifa.ifa_ifp;
 
 		if ((ifp->if_flags & IFF_LOOPBACK)
 		    || (ifp->if_flags & IFF_POINTOPOINT))
 			flags |= RTF_HOST;
 
 		err = ifa_del_loopback_route((struct ifaddr *)ia, sa);
 
-		err = rtinit(&ia->ia_ifa, RTM_ADD, flags);
+		rt_addrmsg(RTM_ADD, &ia->ia_ifa, ia->ia_ifp->if_fib);
+		err = in_handle_ifaddr_route(RTM_ADD, ia);
 		if (err == 0)
 			ia->ia_flags |= IFA_ROUTE;
 
 		err = ifa_add_loopback_route((struct ifaddr *)ia, sa);
 
 		ifa_free(&ia->ia_ifa);
 		break;
 	}
 }
 
 static int
 rip_attach(struct socket *so, int proto, struct thread *td)
 {
 	struct inpcb *inp;
 	int error;
 
 	inp = sotoinpcb(so);
 	KASSERT(inp == NULL, ("rip_attach: inp != NULL"));
 
 	error = priv_check(td, PRIV_NETINET_RAW);
 	if (error)
 		return (error);
 	if (proto >= IPPROTO_MAX || proto < 0)
 		return EPROTONOSUPPORT;
 	error = soreserve(so, rip_sendspace, rip_recvspace);
 	if (error)
 		return (error);
 	INP_INFO_WLOCK(&V_ripcbinfo);
 	error = in_pcballoc(so, &V_ripcbinfo);
 	if (error) {
 		INP_INFO_WUNLOCK(&V_ripcbinfo);
 		return (error);
 	}
 	inp = (struct inpcb *)so->so_pcb;
 	inp->inp_vflag |= INP_IPV4;
 	inp->inp_ip_p = proto;
 	inp->inp_ip_ttl = V_ip_defttl;
 	rip_inshash(inp);
 	INP_INFO_WUNLOCK(&V_ripcbinfo);
 	INP_WUNLOCK(inp);
 	return (0);
 }
 
 static void
 rip_detach(struct socket *so)
 {
 	struct inpcb *inp;
 
 	inp = sotoinpcb(so);
 	KASSERT(inp != NULL, ("rip_detach: inp == NULL"));
 	KASSERT(inp->inp_faddr.s_addr == INADDR_ANY,
 	    ("rip_detach: not closed"));
 
 	INP_INFO_WLOCK(&V_ripcbinfo);
 	INP_WLOCK(inp);
 	rip_delhash(inp);
 	if (so == V_ip_mrouter && ip_mrouter_done)
 		ip_mrouter_done();
 	if (ip_rsvp_force_done)
 		ip_rsvp_force_done(so);
 	if (so == V_ip_rsvpd)
 		ip_rsvp_done();
 	in_pcbdetach(inp);
 	in_pcbfree(inp);
 	INP_INFO_WUNLOCK(&V_ripcbinfo);
 }
 
 static void
 rip_dodisconnect(struct socket *so, struct inpcb *inp)
 {
 	struct inpcbinfo *pcbinfo;
 
 	pcbinfo = inp->inp_pcbinfo;
 	INP_INFO_WLOCK(pcbinfo);
 	INP_WLOCK(inp);
 	rip_delhash(inp);
 	inp->inp_faddr.s_addr = INADDR_ANY;
 	rip_inshash(inp);
 	SOCK_LOCK(so);
 	so->so_state &= ~SS_ISCONNECTED;
 	SOCK_UNLOCK(so);
 	INP_WUNLOCK(inp);
 	INP_INFO_WUNLOCK(pcbinfo);
 }
 
 static void
 rip_abort(struct socket *so)
 {
 	struct inpcb *inp;
 
 	inp = sotoinpcb(so);
 	KASSERT(inp != NULL, ("rip_abort: inp == NULL"));
 
 	rip_dodisconnect(so, inp);
 }
 
 static void
 rip_close(struct socket *so)
 {
 	struct inpcb *inp;
 
 	inp = sotoinpcb(so);
 	KASSERT(inp != NULL, ("rip_close: inp == NULL"));
 
 	rip_dodisconnect(so, inp);
 }
 
 static int
 rip_disconnect(struct socket *so)
 {
 	struct inpcb *inp;
 
 	if ((so->so_state & SS_ISCONNECTED) == 0)
 		return (ENOTCONN);
 
 	inp = sotoinpcb(so);
 	KASSERT(inp != NULL, ("rip_disconnect: inp == NULL"));
 
 	rip_dodisconnect(so, inp);
 	return (0);
 }
 
 static int
 rip_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
 {
 	struct sockaddr_in *addr = (struct sockaddr_in *)nam;
 	struct inpcb *inp;
 	int error;
 
 	if (nam->sa_len != sizeof(*addr))
 		return (EINVAL);
 
 	error = prison_check_ip4(td->td_ucred, &addr->sin_addr);
 	if (error != 0)
 		return (error);
 
 	inp = sotoinpcb(so);
 	KASSERT(inp != NULL, ("rip_bind: inp == NULL"));
 
 	if (CK_STAILQ_EMPTY(&V_ifnet) ||
 	    (addr->sin_family != AF_INET && addr->sin_family != AF_IMPLINK) ||
 	    (addr->sin_addr.s_addr &&
 	     (inp->inp_flags & INP_BINDANY) == 0 &&
 	     ifa_ifwithaddr_check((struct sockaddr *)addr) == 0))
 		return (EADDRNOTAVAIL);
 
 	INP_INFO_WLOCK(&V_ripcbinfo);
 	INP_WLOCK(inp);
 	rip_delhash(inp);
 	inp->inp_laddr = addr->sin_addr;
 	rip_inshash(inp);
 	INP_WUNLOCK(inp);
 	INP_INFO_WUNLOCK(&V_ripcbinfo);
 	return (0);
 }
 
 static int
 rip_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
 {
 	struct sockaddr_in *addr = (struct sockaddr_in *)nam;
 	struct inpcb *inp;
 
 	if (nam->sa_len != sizeof(*addr))
 		return (EINVAL);
 	if (CK_STAILQ_EMPTY(&V_ifnet))
 		return (EADDRNOTAVAIL);
 	if (addr->sin_family != AF_INET && addr->sin_family != AF_IMPLINK)
 		return (EAFNOSUPPORT);
 
 	inp = sotoinpcb(so);
 	KASSERT(inp != NULL, ("rip_connect: inp == NULL"));
 
 	INP_INFO_WLOCK(&V_ripcbinfo);
 	INP_WLOCK(inp);
 	rip_delhash(inp);
 	inp->inp_faddr = addr->sin_addr;
 	rip_inshash(inp);
 	soisconnected(so);
 	INP_WUNLOCK(inp);
 	INP_INFO_WUNLOCK(&V_ripcbinfo);
 	return (0);
 }
 
 static int
 rip_shutdown(struct socket *so)
 {
 	struct inpcb *inp;
 
 	inp = sotoinpcb(so);
 	KASSERT(inp != NULL, ("rip_shutdown: inp == NULL"));
 
 	INP_WLOCK(inp);
 	socantsendmore(so);
 	INP_WUNLOCK(inp);
 	return (0);
 }
 
 static int
 rip_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
     struct mbuf *control, struct thread *td)
 {
 	struct inpcb *inp;
 	u_long dst;
 
 	inp = sotoinpcb(so);
 	KASSERT(inp != NULL, ("rip_send: inp == NULL"));
 
 	/*
 	 * Note: 'dst' reads below are unlocked.
 	 */
 	if (so->so_state & SS_ISCONNECTED) {
 		if (nam) {
 			m_freem(m);
 			return (EISCONN);
 		}
 		dst = inp->inp_faddr.s_addr;	/* Unlocked read. */
 	} else {
 		if (nam == NULL) {
 			m_freem(m);
 			return (ENOTCONN);
 		}
 		dst = ((struct sockaddr_in *)nam)->sin_addr.s_addr;
 	}
 	return (rip_output(m, so, dst));
 }
 #endif /* INET */
 
 static int
 rip_pcblist(SYSCTL_HANDLER_ARGS)
 {
 	struct xinpgen xig;
 	struct epoch_tracker et;
 	struct inpcb *inp;
 	int error;
 
 	if (req->newptr != 0)
 		return (EPERM);
 
 	if (req->oldptr == 0) {
 		int n;
 
 		n = V_ripcbinfo.ipi_count;
 		n += imax(n / 8, 10);
 		req->oldidx = 2 * (sizeof xig) + n * sizeof(struct xinpcb);
 		return (0);
 	}
 
 	if ((error = sysctl_wire_old_buffer(req, 0)) != 0)
 		return (error);
 
 	bzero(&xig, sizeof(xig));
 	xig.xig_len = sizeof xig;
 	xig.xig_count = V_ripcbinfo.ipi_count;
 	xig.xig_gen = V_ripcbinfo.ipi_gencnt;
 	xig.xig_sogen = so_gencnt;
 	error = SYSCTL_OUT(req, &xig, sizeof xig);
 	if (error)
 		return (error);
 
 	NET_EPOCH_ENTER(et);
 	for (inp = CK_LIST_FIRST(V_ripcbinfo.ipi_listhead);
 	    inp != NULL;
 	    inp = CK_LIST_NEXT(inp, inp_list)) {
 		INP_RLOCK(inp);
 		if (inp->inp_gencnt <= xig.xig_gen &&
 		    cr_canseeinpcb(req->td->td_ucred, inp) == 0) {
 			struct xinpcb xi;
 
 			in_pcbtoxinpcb(inp, &xi);
 			INP_RUNLOCK(inp);
 			error = SYSCTL_OUT(req, &xi, sizeof xi);
 			if (error)
 				break;
 		} else
 			INP_RUNLOCK(inp);
 	}
 	NET_EPOCH_EXIT(et);
 
 	if (!error) {
 		/*
 		 * Give the user an updated idea of our state.  If the
 		 * generation differs from what we told her before, she knows
 		 * that something happened while we were processing this
 		 * request, and it might be necessary to retry.
 		 */
 		xig.xig_gen = V_ripcbinfo.ipi_gencnt;
 		xig.xig_sogen = so_gencnt;
 		xig.xig_count = V_ripcbinfo.ipi_count;
 		error = SYSCTL_OUT(req, &xig, sizeof xig);
 	}
 
 	return (error);
 }
 
 SYSCTL_PROC(_net_inet_raw, OID_AUTO/*XXX*/, pcblist,
     CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
     rip_pcblist, "S,xinpcb",
     "List of active raw IP sockets");
 
 #ifdef INET
 struct pr_usrreqs rip_usrreqs = {
 	.pru_abort =		rip_abort,
 	.pru_attach =		rip_attach,
 	.pru_bind =		rip_bind,
 	.pru_connect =		rip_connect,
 	.pru_control =		in_control,
 	.pru_detach =		rip_detach,
 	.pru_disconnect =	rip_disconnect,
 	.pru_peeraddr =		in_getpeeraddr,
 	.pru_send =		rip_send,
 	.pru_shutdown =		rip_shutdown,
 	.pru_sockaddr =		in_getsockaddr,
 	.pru_sosetlabel =	in_pcbsosetlabel,
 	.pru_close =		rip_close,
 };
 #endif /* INET */
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
index 7c572e7b833b..b42cc16cdb6f 100644
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -1,2547 +1,2591 @@
 /*-
  * SPDX-License-Identifier: BSD-3-Clause
  *
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. Neither the name of the project nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  *	$KAME: in6.c,v 1.259 2002/01/21 11:37:50 keiichi Exp $
  */
 
 /*-
  * Copyright (c) 1982, 1986, 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  *	@(#)in.c	8.2 (Berkeley) 11/15/93
  */
 
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
 
 #include <sys/param.h>
 #include <sys/eventhandler.h>
 #include <sys/errno.h>
 #include <sys/jail.h>
 #include <sys/malloc.h>
 #include <sys/socket.h>
 #include <sys/socketvar.h>
 #include <sys/sockio.h>
 #include <sys/systm.h>
 #include <sys/priv.h>
 #include <sys/proc.h>
 #include <sys/protosw.h>
 #include <sys/time.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/rmlock.h>
 #include <sys/sysctl.h>
 #include <sys/syslog.h>
 
 #include <net/if.h>
 #include <net/if_var.h>
 #include <net/if_types.h>
 #include <net/route.h>
+#include <net/route/route_ctl.h>
 #include <net/route/nhop.h>
 #include <net/if_dl.h>
 #include <net/vnet.h>
 
 #include <netinet/in.h>
 #include <netinet/in_var.h>
 #include <net/if_llatbl.h>
 #include <netinet/if_ether.h>
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
 #include <netinet/in_pcb.h>
 #include <netinet/ip_carp.h>
 
 #include <netinet/ip6.h>
 #include <netinet6/ip6_var.h>
 #include <netinet6/nd6.h>
 #include <netinet6/mld6_var.h>
 #include <netinet6/ip6_mroute.h>
 #include <netinet6/in6_ifattach.h>
 #include <netinet6/scope6_var.h>
 #include <netinet6/in6_fib.h>
 #include <netinet6/in6_pcb.h>
 
 /*
  * struct in6_ifreq and struct ifreq must be type punnable for common members
  * of ifr_ifru to allow accessors to be shared.
  */
 _Static_assert(offsetof(struct in6_ifreq, ifr_ifru) ==
     offsetof(struct ifreq, ifr_ifru),
     "struct in6_ifreq and struct ifreq are not type punnable");
 
 VNET_DECLARE(int, icmp6_nodeinfo_oldmcprefix);
 #define V_icmp6_nodeinfo_oldmcprefix	VNET(icmp6_nodeinfo_oldmcprefix)
 
 /*
  * Definitions of some costant IP6 addresses.
  */
 const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
 const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
 const struct in6_addr in6addr_nodelocal_allnodes =
 	IN6ADDR_NODELOCAL_ALLNODES_INIT;
 const struct in6_addr in6addr_linklocal_allnodes =
 	IN6ADDR_LINKLOCAL_ALLNODES_INIT;
 const struct in6_addr in6addr_linklocal_allrouters =
 	IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
 const struct in6_addr in6addr_linklocal_allv2routers =
 	IN6ADDR_LINKLOCAL_ALLV2ROUTERS_INIT;
 
 const struct in6_addr in6mask0 = IN6MASK0;
 const struct in6_addr in6mask32 = IN6MASK32;
 const struct in6_addr in6mask64 = IN6MASK64;
 const struct in6_addr in6mask96 = IN6MASK96;
 const struct in6_addr in6mask128 = IN6MASK128;
 
 const struct sockaddr_in6 sa6_any =
 	{ sizeof(sa6_any), AF_INET6, 0, 0, IN6ADDR_ANY_INIT, 0 };
 
 static int in6_notify_ifa(struct ifnet *, struct in6_ifaddr *,
 	struct in6_aliasreq *, int);
 static void in6_unlink_ifa(struct in6_ifaddr *, struct ifnet *);
 
 static int in6_validate_ifra(struct ifnet *, struct in6_aliasreq *,
     struct in6_ifaddr *, int);
 static struct in6_ifaddr *in6_alloc_ifa(struct ifnet *,
     struct in6_aliasreq *, int flags);
 static int in6_update_ifa_internal(struct ifnet *, struct in6_aliasreq *,
     struct in6_ifaddr *, int, int);
 static int in6_broadcast_ifa(struct ifnet *, struct in6_aliasreq *,
     struct in6_ifaddr *, int);
 
 #define ifa2ia6(ifa)	((struct in6_ifaddr *)(ifa))
 #define ia62ifa(ia6)	(&((ia6)->ia_ifa))
 
 void
 in6_newaddrmsg(struct in6_ifaddr *ia, int cmd)
 {
 	struct rt_addrinfo info;
 	struct ifaddr *ifa;
 	struct sockaddr_dl gateway;
 	int fibnum;
 
 	ifa = &ia->ia_ifa;
 
 	/*
 	 * Prepare info data for the host route.
 	 * This code mimics one from ifa_maintain_loopback_route().
 	 */
 	bzero(&info, sizeof(struct rt_addrinfo));
 	info.rti_flags = ifa->ifa_flags | RTF_HOST | RTF_STATIC | RTF_PINNED;
 	info.rti_info[RTAX_DST] = ifa->ifa_addr;
 	info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&gateway;
 	link_init_sdl(ifa->ifa_ifp, (struct sockaddr *)&gateway, ifa->ifa_ifp->if_type);
 	if (cmd != RTM_DELETE)
 		info.rti_ifp = V_loif;
 
 	fibnum = ia62ifa(ia)->ifa_ifp->if_fib;
 
 	if (cmd == RTM_ADD) {
 		rt_addrmsg(cmd, &ia->ia_ifa, fibnum);
 		rt_routemsg_info(cmd, &info, fibnum);
 	} else if (cmd == RTM_DELETE) {
 		rt_routemsg_info(cmd, &info, fibnum);
 		rt_addrmsg(cmd, &ia->ia_ifa, fibnum);
 	}
 }
 
 int
 in6_mask2len(struct in6_addr *mask, u_char *lim0)
 {
 	int x = 0, y;
 	u_char *lim = lim0, *p;
 
 	/* ignore the scope_id part */
 	if (lim0 == NULL || lim0 - (u_char *)mask > sizeof(*mask))
 		lim = (u_char *)mask + sizeof(*mask);
 	for (p = (u_char *)mask; p < lim; x++, p++) {
 		if (*p != 0xff)
 			break;
 	}
 	y = 0;
 	if (p < lim) {
 		for (y = 0; y < 8; y++) {
 			if ((*p & (0x80 >> y)) == 0)
 				break;
 		}
 	}
 
 	/*
 	 * when the limit pointer is given, do a stricter check on the
 	 * remaining bits.
 	 */
 	if (p < lim) {
 		if (y != 0 && (*p & (0x00ff >> y)) != 0)
 			return (-1);
 		for (p = p + 1; p < lim; p++)
 			if (*p != 0)
 				return (-1);
 	}
 
 	return x * 8 + y;
 }
 
 #ifdef COMPAT_FREEBSD32
 struct in6_ndifreq32 {
 	char ifname[IFNAMSIZ];
 	uint32_t ifindex;
 };
 #define	SIOCGDEFIFACE32_IN6	_IOWR('i', 86, struct in6_ndifreq32)
 #endif
 
 int
 in6_control(struct socket *so, u_long cmd, caddr_t data,
     struct ifnet *ifp, struct thread *td)
 {
 	struct	in6_ifreq *ifr = (struct in6_ifreq *)data;
 	struct	in6_ifaddr *ia = NULL;
 	struct	in6_aliasreq *ifra = (struct in6_aliasreq *)data;
 	struct sockaddr_in6 *sa6;
 	int carp_attached = 0;
 	int error;
 	u_long ocmd = cmd;
 
 	/*
 	 * Compat to make pre-10.x ifconfig(8) operable.
 	 */
 	if (cmd == OSIOCAIFADDR_IN6)
 		cmd = SIOCAIFADDR_IN6;
 
 	switch (cmd) {
 	case SIOCGETSGCNT_IN6:
 	case SIOCGETMIFCNT_IN6:
 		/*
 		 * XXX mrt_ioctl has a 3rd, unused, FIB argument in route.c.
 		 * We cannot see how that would be needed, so do not adjust the
 		 * KPI blindly; more likely should clean up the IPv4 variant.
 		 */
 		return (mrt6_ioctl ? mrt6_ioctl(cmd, data) : EOPNOTSUPP);
 	}
 
 	switch (cmd) {
 	case SIOCAADDRCTL_POLICY:
 	case SIOCDADDRCTL_POLICY:
 		if (td != NULL) {
 			error = priv_check(td, PRIV_NETINET_ADDRCTRL6);
 			if (error)
 				return (error);
 		}
 		return (in6_src_ioctl(cmd, data));
 	}
 
 	if (ifp == NULL)
 		return (EOPNOTSUPP);
 
 	switch (cmd) {
 	case SIOCSNDFLUSH_IN6:
 	case SIOCSPFXFLUSH_IN6:
 	case SIOCSRTRFLUSH_IN6:
 	case SIOCSDEFIFACE_IN6:
 	case SIOCSIFINFO_FLAGS:
 	case SIOCSIFINFO_IN6:
 		if (td != NULL) {
 			error = priv_check(td, PRIV_NETINET_ND6);
 			if (error)
 				return (error);
 		}
 		/* FALLTHROUGH */
 	case OSIOCGIFINFO_IN6:
 	case SIOCGIFINFO_IN6:
 	case SIOCGNBRINFO_IN6:
 	case SIOCGDEFIFACE_IN6:
 		return (nd6_ioctl(cmd, data, ifp));
 
 #ifdef COMPAT_FREEBSD32
 	case SIOCGDEFIFACE32_IN6:
 		{
 			struct in6_ndifreq ndif;
 			struct in6_ndifreq32 *ndif32;
 
 			error = nd6_ioctl(SIOCGDEFIFACE_IN6, (caddr_t)&ndif,
 			    ifp);
 			if (error)
 				return (error);
 			ndif32 = (struct in6_ndifreq32 *)data;
 			ndif32->ifindex = ndif.ifindex;
 			return (0);
 		}
 #endif
 	}
 
 	switch (cmd) {
 	case SIOCSIFPREFIX_IN6:
 	case SIOCDIFPREFIX_IN6:
 	case SIOCAIFPREFIX_IN6:
 	case SIOCCIFPREFIX_IN6:
 	case SIOCSGIFPREFIX_IN6:
 	case SIOCGIFPREFIX_IN6:
 		log(LOG_NOTICE,
 		    "prefix ioctls are now invalidated. "
 		    "please use ifconfig.\n");
 		return (EOPNOTSUPP);
 	}
 
 	switch (cmd) {
 	case SIOCSSCOPE6:
 		if (td != NULL) {
 			error = priv_check(td, PRIV_NETINET_SCOPE6);
 			if (error)
 				return (error);
 		}
 		/* FALLTHROUGH */
 	case SIOCGSCOPE6:
 	case SIOCGSCOPE6DEF:
 		return (scope6_ioctl(cmd, data, ifp));
 	}
 
 	/*
 	 * Find address for this interface, if it exists.
 	 *
 	 * In netinet code, we have checked ifra_addr in SIOCSIF*ADDR operation
 	 * only, and used the first interface address as the target of other
 	 * operations (without checking ifra_addr).  This was because netinet
 	 * code/API assumed at most 1 interface address per interface.
 	 * Since IPv6 allows a node to assign multiple addresses
 	 * on a single interface, we almost always look and check the
 	 * presence of ifra_addr, and reject invalid ones here.
 	 * It also decreases duplicated code among SIOC*_IN6 operations.
 	 */
 	switch (cmd) {
 	case SIOCAIFADDR_IN6:
 	case SIOCSIFPHYADDR_IN6:
 		sa6 = &ifra->ifra_addr;
 		break;
 	case SIOCSIFADDR_IN6:
 	case SIOCGIFADDR_IN6:
 	case SIOCSIFDSTADDR_IN6:
 	case SIOCSIFNETMASK_IN6:
 	case SIOCGIFDSTADDR_IN6:
 	case SIOCGIFNETMASK_IN6:
 	case SIOCDIFADDR_IN6:
 	case SIOCGIFPSRCADDR_IN6:
 	case SIOCGIFPDSTADDR_IN6:
 	case SIOCGIFAFLAG_IN6:
 	case SIOCSNDFLUSH_IN6:
 	case SIOCSPFXFLUSH_IN6:
 	case SIOCSRTRFLUSH_IN6:
 	case SIOCGIFALIFETIME_IN6:
 	case SIOCGIFSTAT_IN6:
 	case SIOCGIFSTAT_ICMP6:
 		sa6 = &ifr->ifr_addr;
 		break;
 	case SIOCSIFADDR:
 	case SIOCSIFBRDADDR:
 	case SIOCSIFDSTADDR:
 	case SIOCSIFNETMASK:
 		/*
 		 * Although we should pass any non-INET6 ioctl requests
 		 * down to driver, we filter some legacy INET requests.
 		 * Drivers trust SIOCSIFADDR et al to come from an already
 		 * privileged layer, and do not perform any credentials
 		 * checks or input validation.
 		 */
 		return (EINVAL);
 	default:
 		sa6 = NULL;
 		break;
 	}
 	if (sa6 && sa6->sin6_family == AF_INET6) {
 		if (sa6->sin6_scope_id != 0)
 			error = sa6_embedscope(sa6, 0);
 		else
 			error = in6_setscope(&sa6->sin6_addr, ifp, NULL);
 		if (error != 0)
 			return (error);
 		if (td != NULL && (error = prison_check_ip6(td->td_ucred,
 		    &sa6->sin6_addr)) != 0)
 			return (error);
 		ia = in6ifa_ifpwithaddr(ifp, &sa6->sin6_addr);
 	} else
 		ia = NULL;
 
 	switch (cmd) {
 	case SIOCSIFADDR_IN6:
 	case SIOCSIFDSTADDR_IN6:
 	case SIOCSIFNETMASK_IN6:
 		/*
 		 * Since IPv6 allows a node to assign multiple addresses
 		 * on a single interface, SIOCSIFxxx ioctls are deprecated.
 		 */
 		/* we decided to obsolete this command (20000704) */
 		error = EINVAL;
 		goto out;
 
 	case SIOCDIFADDR_IN6:
 		/*
 		 * for IPv4, we look for existing in_ifaddr here to allow
 		 * "ifconfig if0 delete" to remove the first IPv4 address on
 		 * the interface.  For IPv6, as the spec allows multiple
 		 * interface address from the day one, we consider "remove the
 		 * first one" semantics to be not preferable.
 		 */
 		if (ia == NULL) {
 			error = EADDRNOTAVAIL;
 			goto out;
 		}
 		/* FALLTHROUGH */
 	case SIOCAIFADDR_IN6:
 		/*
 		 * We always require users to specify a valid IPv6 address for
 		 * the corresponding operation.
 		 */
 		if (ifra->ifra_addr.sin6_family != AF_INET6 ||
 		    ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6)) {
 			error = EAFNOSUPPORT;
 			goto out;
 		}
 
 		if (td != NULL) {
 			error = priv_check(td, (cmd == SIOCDIFADDR_IN6) ?
 			    PRIV_NET_DELIFADDR : PRIV_NET_ADDIFADDR);
 			if (error)
 				goto out;
 		}
 		/* FALLTHROUGH */
 	case SIOCGIFSTAT_IN6:
 	case SIOCGIFSTAT_ICMP6:
 		if (ifp->if_afdata[AF_INET6] == NULL) {
 			error = EPFNOSUPPORT;
 			goto out;
 		}
 		break;
 
 	case SIOCGIFADDR_IN6:
 		/* This interface is basically deprecated. use SIOCGIFCONF. */
 		/* FALLTHROUGH */
 	case SIOCGIFAFLAG_IN6:
 	case SIOCGIFNETMASK_IN6:
 	case SIOCGIFDSTADDR_IN6:
 	case SIOCGIFALIFETIME_IN6:
 		/* must think again about its semantics */
 		if (ia == NULL) {
 			error = EADDRNOTAVAIL;
 			goto out;
 		}
 		break;
 	}
 
 	switch (cmd) {
 	case SIOCGIFADDR_IN6:
 		ifr->ifr_addr = ia->ia_addr;
 		if ((error = sa6_recoverscope(&ifr->ifr_addr)) != 0)
 			goto out;
 		break;
 
 	case SIOCGIFDSTADDR_IN6:
 		if ((ifp->if_flags & IFF_POINTOPOINT) == 0) {
 			error = EINVAL;
 			goto out;
 		}
 		ifr->ifr_dstaddr = ia->ia_dstaddr;
 		if ((error = sa6_recoverscope(&ifr->ifr_dstaddr)) != 0)
 			goto out;
 		break;
 
 	case SIOCGIFNETMASK_IN6:
 		ifr->ifr_addr = ia->ia_prefixmask;
 		break;
 
 	case SIOCGIFAFLAG_IN6:
 		ifr->ifr_ifru.ifru_flags6 = ia->ia6_flags;
 		break;
 
 	case SIOCGIFSTAT_IN6:
 		COUNTER_ARRAY_COPY(((struct in6_ifextra *)
 		    ifp->if_afdata[AF_INET6])->in6_ifstat,
 		    &ifr->ifr_ifru.ifru_stat,
 		    sizeof(struct in6_ifstat) / sizeof(uint64_t));
 		break;
 
 	case SIOCGIFSTAT_ICMP6:
 		COUNTER_ARRAY_COPY(((struct in6_ifextra *)
 		    ifp->if_afdata[AF_INET6])->icmp6_ifstat,
 		    &ifr->ifr_ifru.ifru_icmp6stat,
 		    sizeof(struct icmp6_ifstat) / sizeof(uint64_t));
 		break;
 
 	case SIOCGIFALIFETIME_IN6:
 		ifr->ifr_ifru.ifru_lifetime = ia->ia6_lifetime;
 		if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
 			time_t maxexpire;
 			struct in6_addrlifetime *retlt =
 			    &ifr->ifr_ifru.ifru_lifetime;
 
 			/*
 			 * XXX: adjust expiration time assuming time_t is
 			 * signed.
 			 */
 			maxexpire = (-1) &
 			    ~((time_t)1 << ((sizeof(maxexpire) * 8) - 1));
 			if (ia->ia6_lifetime.ia6t_vltime <
 			    maxexpire - ia->ia6_updatetime) {
 				retlt->ia6t_expire = ia->ia6_updatetime +
 				    ia->ia6_lifetime.ia6t_vltime;
 			} else
 				retlt->ia6t_expire = maxexpire;
 		}
 		if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
 			time_t maxexpire;
 			struct in6_addrlifetime *retlt =
 			    &ifr->ifr_ifru.ifru_lifetime;
 
 			/*
 			 * XXX: adjust expiration time assuming time_t is
 			 * signed.
 			 */
 			maxexpire = (-1) &
 			    ~((time_t)1 << ((sizeof(maxexpire) * 8) - 1));
 			if (ia->ia6_lifetime.ia6t_pltime <
 			    maxexpire - ia->ia6_updatetime) {
 				retlt->ia6t_preferred = ia->ia6_updatetime +
 				    ia->ia6_lifetime.ia6t_pltime;
 			} else
 				retlt->ia6t_preferred = maxexpire;
 		}
 		break;
 
 	case SIOCAIFADDR_IN6:
 	{
 		struct nd_prefixctl pr0;
 		struct nd_prefix *pr;
 
 		/*
 		 * first, make or update the interface address structure,
 		 * and link it to the list.
 		 */
 		if ((error = in6_update_ifa(ifp, ifra, ia, 0)) != 0)
 			goto out;
 		if (ia != NULL) {
 			if (ia->ia_ifa.ifa_carp)
 				(*carp_detach_p)(&ia->ia_ifa, true);
 			ifa_free(&ia->ia_ifa);
 		}
 		if ((ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr))
 		    == NULL) {
 			/*
 			 * this can happen when the user specify the 0 valid
 			 * lifetime.
 			 */
 			break;
 		}
 
 		if (cmd == ocmd && ifra->ifra_vhid > 0) {
 			if (carp_attach_p != NULL)
 				error = (*carp_attach_p)(&ia->ia_ifa,
 				    ifra->ifra_vhid);
 			else
 				error = EPROTONOSUPPORT;
 			if (error)
 				goto out;
 			else
 				carp_attached = 1;
 		}
 
 		/*
 		 * then, make the prefix on-link on the interface.
 		 * XXX: we'd rather create the prefix before the address, but
 		 * we need at least one address to install the corresponding
 		 * interface route, so we configure the address first.
 		 */
 
 		/*
 		 * convert mask to prefix length (prefixmask has already
 		 * been validated in in6_update_ifa().
 		 */
 		bzero(&pr0, sizeof(pr0));
 		pr0.ndpr_ifp = ifp;
 		pr0.ndpr_plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr,
 		    NULL);
 		if (pr0.ndpr_plen == 128) {
 			/* we don't need to install a host route. */
 			goto aifaddr_out;
 		}
 		pr0.ndpr_prefix = ifra->ifra_addr;
 		/* apply the mask for safety. */
 		IN6_MASK_ADDR(&pr0.ndpr_prefix.sin6_addr,
 		    &ifra->ifra_prefixmask.sin6_addr);
 
 		/*
 		 * XXX: since we don't have an API to set prefix (not address)
 		 * lifetimes, we just use the same lifetimes as addresses.
 		 * The (temporarily) installed lifetimes can be overridden by
 		 * later advertised RAs (when accept_rtadv is non 0), which is
 		 * an intended behavior.
 		 */
 		pr0.ndpr_raf_onlink = 1; /* should be configurable? */
 		pr0.ndpr_raf_auto =
 		    ((ifra->ifra_flags & IN6_IFF_AUTOCONF) != 0);
 		pr0.ndpr_vltime = ifra->ifra_lifetime.ia6t_vltime;
 		pr0.ndpr_pltime = ifra->ifra_lifetime.ia6t_pltime;
 
 		/* add the prefix if not yet. */
 		if ((pr = nd6_prefix_lookup(&pr0)) == NULL) {
 			/*
 			 * nd6_prelist_add will install the corresponding
 			 * interface route.
 			 */
 			if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0) {
 				if (carp_attached)
 					(*carp_detach_p)(&ia->ia_ifa, false);
 				goto out;
 			}
 		}
 
 		/* relate the address to the prefix */
 		if (ia->ia6_ndpr == NULL) {
 			ia->ia6_ndpr = pr;
 			pr->ndpr_addrcnt++;
 
 			/*
 			 * If this is the first autoconf address from the
 			 * prefix, create a temporary address as well
 			 * (when required).
 			 */
 			if ((ia->ia6_flags & IN6_IFF_AUTOCONF) &&
 			    V_ip6_use_tempaddr && pr->ndpr_addrcnt == 1) {
 				int e;
 				if ((e = in6_tmpifadd(ia, 1, 0)) != 0) {
 					log(LOG_NOTICE, "in6_control: failed "
 					    "to create a temporary address, "
 					    "errno=%d\n", e);
 				}
 			}
 		}
 		nd6_prefix_rele(pr);
 
 		/*
 		 * this might affect the status of autoconfigured addresses,
 		 * that is, this address might make other addresses detached.
 		 */
 		pfxlist_onlink_check();
 
 aifaddr_out:
 		/*
 		 * Try to clear the flag when a new IPv6 address is added
 		 * onto an IFDISABLED interface and it succeeds.
 		 */
 		if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) {
 			struct in6_ndireq nd;
 
 			memset(&nd, 0, sizeof(nd));
 			nd.ndi.flags = ND_IFINFO(ifp)->flags;
 			nd.ndi.flags &= ~ND6_IFF_IFDISABLED;
 			if (nd6_ioctl(SIOCSIFINFO_FLAGS, (caddr_t)&nd, ifp) < 0)
 				log(LOG_NOTICE, "SIOCAIFADDR_IN6: "
 				    "SIOCSIFINFO_FLAGS for -ifdisabled "
 				    "failed.");
 			/*
 			 * Ignore failure of clearing the flag intentionally.
 			 * The failure means address duplication was detected.
 			 */
 		}
 		break;
 	}
 
 	case SIOCDIFADDR_IN6:
 	{
 		struct nd_prefix *pr;
 
 		/*
 		 * If the address being deleted is the only one that owns
 		 * the corresponding prefix, expire the prefix as well.
 		 * XXX: theoretically, we don't have to worry about such
 		 * relationship, since we separate the address management
 		 * and the prefix management.  We do this, however, to provide
 		 * as much backward compatibility as possible in terms of
 		 * the ioctl operation.
 		 * Note that in6_purgeaddr() will decrement ndpr_addrcnt.
 		 */
 		pr = ia->ia6_ndpr;
 		in6_purgeaddr(&ia->ia_ifa);
 		if (pr != NULL && pr->ndpr_addrcnt == 0) {
 			ND6_WLOCK();
 			nd6_prefix_unlink(pr, NULL);
 			ND6_WUNLOCK();
 			nd6_prefix_del(pr);
 		}
 		EVENTHANDLER_INVOKE(ifaddr_event_ext, ifp, &ia->ia_ifa,
 		    IFADDR_EVENT_DEL);
 		break;
 	}
 
 	default:
 		if (ifp->if_ioctl == NULL) {
 			error = EOPNOTSUPP;
 			goto out;
 		}
 		error = (*ifp->if_ioctl)(ifp, cmd, data);
 		goto out;
 	}
 
 	error = 0;
 out:
 	if (ia != NULL)
 		ifa_free(&ia->ia_ifa);
 	return (error);
 }
 
 static struct in6_multi_mship *
 in6_joingroup_legacy(struct ifnet *ifp, const struct in6_addr *mcaddr,
     int *errorp, int delay)
 {
 	struct in6_multi_mship *imm;
 	int error;
 
 	imm = malloc(sizeof(*imm), M_IP6MADDR, M_NOWAIT);
 	if (imm == NULL) {
 		*errorp = ENOBUFS;
 		return (NULL);
 	}
 
 	delay = (delay * PR_FASTHZ) / hz;
 
 	error = in6_joingroup(ifp, mcaddr, NULL, &imm->i6mm_maddr, delay);
 	if (error) {
 		*errorp = error;
 		free(imm, M_IP6MADDR);
 		return (NULL);
 	}
 
 	return (imm);
 }
 /*
  * Join necessary multicast groups.  Factored out from in6_update_ifa().
  * This entire work should only be done once, for the default FIB.
  */
 static int
 in6_update_ifa_join_mc(struct ifnet *ifp, struct in6_aliasreq *ifra,
     struct in6_ifaddr *ia, int flags, struct in6_multi **in6m_sol)
 {
 	char ip6buf[INET6_ADDRSTRLEN];
 	struct in6_addr mltaddr;
 	struct in6_multi_mship *imm;
 	int delay, error;
 
 	KASSERT(in6m_sol != NULL, ("%s: in6m_sol is NULL", __func__));
 
 	/* Join solicited multicast addr for new host id. */
 	bzero(&mltaddr, sizeof(struct in6_addr));
 	mltaddr.s6_addr32[0] = IPV6_ADDR_INT32_MLL;
 	mltaddr.s6_addr32[2] = htonl(1);
 	mltaddr.s6_addr32[3] = ifra->ifra_addr.sin6_addr.s6_addr32[3];
 	mltaddr.s6_addr8[12] = 0xff;
 	if ((error = in6_setscope(&mltaddr, ifp, NULL)) != 0) {
 		/* XXX: should not happen */
 		log(LOG_ERR, "%s: in6_setscope failed\n", __func__);
 		goto cleanup;
 	}
 	delay = error = 0;
 	if ((flags & IN6_IFAUPDATE_DADDELAY)) {
 		/*
 		 * We need a random delay for DAD on the address being
 		 * configured.  It also means delaying transmission of the
 		 * corresponding MLD report to avoid report collision.
 		 * [RFC 4861, Section 6.3.7]
 		 */
 		delay = arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz);
 	}
 	imm = in6_joingroup_legacy(ifp, &mltaddr, &error, delay);
 	if (imm == NULL) {
 		nd6log((LOG_WARNING, "%s: in6_joingroup failed for %s on %s "
 		    "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, &mltaddr),
 		    if_name(ifp), error));
 		goto cleanup;
 	}
 	LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain);
 	*in6m_sol = imm->i6mm_maddr;
 
 	/*
 	 * Join link-local all-nodes address.
 	 */
 	mltaddr = in6addr_linklocal_allnodes;
 	if ((error = in6_setscope(&mltaddr, ifp, NULL)) != 0)
 		goto cleanup; /* XXX: should not fail */
 
 	imm = in6_joingroup_legacy(ifp, &mltaddr, &error, 0);
 	if (imm == NULL) {
 		nd6log((LOG_WARNING, "%s: in6_joingroup failed for %s on %s "
 		    "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, &mltaddr),
 		    if_name(ifp), error));
 		goto cleanup;
 	}
 	LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain);
 
 	/*
 	 * Join node information group address.
 	 */
 	delay = 0;
 	if ((flags & IN6_IFAUPDATE_DADDELAY)) {
 		/*
 		 * The spec does not say anything about delay for this group,
 		 * but the same logic should apply.
 		 */
 		delay = arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz);
 	}
 	if (in6_nigroup(ifp, NULL, -1, &mltaddr) == 0) {
 		/* XXX jinmei */
 		imm = in6_joingroup_legacy(ifp, &mltaddr, &error, delay);
 		if (imm == NULL)
 			nd6log((LOG_WARNING,
 			    "%s: in6_joingroup failed for %s on %s "
 			    "(errno=%d)\n", __func__, ip6_sprintf(ip6buf,
 			    &mltaddr), if_name(ifp), error));
 			/* XXX not very fatal, go on... */
 		else
 			LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain);
 	}
 	if (V_icmp6_nodeinfo_oldmcprefix &&
 	    in6_nigroup_oldmcprefix(ifp, NULL, -1, &mltaddr) == 0) {
 		imm = in6_joingroup_legacy(ifp, &mltaddr, &error, delay);
 		if (imm == NULL)
 			nd6log((LOG_WARNING,
 			    "%s: in6_joingroup failed for %s on %s "
 			    "(errno=%d)\n", __func__, ip6_sprintf(ip6buf,
 			    &mltaddr), if_name(ifp), error));
 			/* XXX not very fatal, go on... */
 		else
 			LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain);
 	}
 
 	/*
 	 * Join interface-local all-nodes address.
 	 * (ff01::1%ifN, and ff01::%ifN/32)
 	 */
 	mltaddr = in6addr_nodelocal_allnodes;
 	if ((error = in6_setscope(&mltaddr, ifp, NULL)) != 0)
 		goto cleanup; /* XXX: should not fail */
 
 	imm = in6_joingroup_legacy(ifp, &mltaddr, &error, 0);
 	if (imm == NULL) {
 		nd6log((LOG_WARNING, "%s: in6_joingroup failed for %s on %s "
 		    "(errno=%d)\n", __func__, ip6_sprintf(ip6buf,
 		    &mltaddr), if_name(ifp), error));
 		goto cleanup;
 	}
 	LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain);
 
 cleanup:
 	return (error);
 }
 
 /*
  * Update parameters of an IPv6 interface address.
  * If necessary, a new entry is created and linked into address chains.
  * This function is separated from in6_control().
  */
 int
 in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
     struct in6_ifaddr *ia, int flags)
 {
 	int error, hostIsNew = 0;
 
 	if ((error = in6_validate_ifra(ifp, ifra, ia, flags)) != 0)
 		return (error);
 
 	if (ia == NULL) {
 		hostIsNew = 1;
 		if ((ia = in6_alloc_ifa(ifp, ifra, flags)) == NULL)
 			return (ENOBUFS);
 	}
 
 	error = in6_update_ifa_internal(ifp, ifra, ia, hostIsNew, flags);
 	if (error != 0) {
 		if (hostIsNew != 0) {
 			in6_unlink_ifa(ia, ifp);
 			ifa_free(&ia->ia_ifa);
 		}
 		return (error);
 	}
 
 	if (hostIsNew)
 		error = in6_broadcast_ifa(ifp, ifra, ia, flags);
 
 	return (error);
 }
 
 /*
  * Fill in basic IPv6 address request info.
  */
 void
 in6_prepare_ifra(struct in6_aliasreq *ifra, const struct in6_addr *addr,
     const struct in6_addr *mask)
 {
 
 	memset(ifra, 0, sizeof(struct in6_aliasreq));
 
 	ifra->ifra_addr.sin6_family = AF_INET6;
 	ifra->ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
 	if (addr != NULL)
 		ifra->ifra_addr.sin6_addr = *addr;
 
 	ifra->ifra_prefixmask.sin6_family = AF_INET6;
 	ifra->ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
 	if (mask != NULL)
 		ifra->ifra_prefixmask.sin6_addr = *mask;
 }
 
 static int
 in6_validate_ifra(struct ifnet *ifp, struct in6_aliasreq *ifra,
     struct in6_ifaddr *ia, int flags)
 {
 	int plen = -1;
 	struct sockaddr_in6 dst6;
 	struct in6_addrlifetime *lt;
 	char ip6buf[INET6_ADDRSTRLEN];
 
 	/* Validate parameters */
 	if (ifp == NULL || ifra == NULL) /* this maybe redundant */
 		return (EINVAL);
 
 	/*
 	 * The destination address for a p2p link must have a family
 	 * of AF_UNSPEC or AF_INET6.
 	 */
 	if ((ifp->if_flags & IFF_POINTOPOINT) != 0 &&
 	    ifra->ifra_dstaddr.sin6_family != AF_INET6 &&
 	    ifra->ifra_dstaddr.sin6_family != AF_UNSPEC)
 		return (EAFNOSUPPORT);
 
 	/*
 	 * Validate address
 	 */
 	if (ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6) ||
 	    ifra->ifra_addr.sin6_family != AF_INET6)
 		return (EINVAL);
 
 	/*
 	 * validate ifra_prefixmask.  don't check sin6_family, netmask
 	 * does not carry fields other than sin6_len.
 	 */
 	if (ifra->ifra_prefixmask.sin6_len > sizeof(struct sockaddr_in6))
 		return (EINVAL);
 	/*
 	 * Because the IPv6 address architecture is classless, we require
 	 * users to specify a (non 0) prefix length (mask) for a new address.
 	 * We also require the prefix (when specified) mask is valid, and thus
 	 * reject a non-consecutive mask.
 	 */
 	if (ia == NULL && ifra->ifra_prefixmask.sin6_len == 0)
 		return (EINVAL);
 	if (ifra->ifra_prefixmask.sin6_len != 0) {
 		plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr,
 		    (u_char *)&ifra->ifra_prefixmask +
 		    ifra->ifra_prefixmask.sin6_len);
 		if (plen <= 0)
 			return (EINVAL);
 	} else {
 		/*
 		 * In this case, ia must not be NULL.  We just use its prefix
 		 * length.
 		 */
 		plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL);
 	}
 	/*
 	 * If the destination address on a p2p interface is specified,
 	 * and the address is a scoped one, validate/set the scope
 	 * zone identifier.
 	 */
 	dst6 = ifra->ifra_dstaddr;
 	if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) != 0 &&
 	    (dst6.sin6_family == AF_INET6)) {
 		struct in6_addr in6_tmp;
 		u_int32_t zoneid;
 
 		in6_tmp = dst6.sin6_addr;
 		if (in6_setscope(&in6_tmp, ifp, &zoneid))
 			return (EINVAL); /* XXX: should be impossible */
 
 		if (dst6.sin6_scope_id != 0) {
 			if (dst6.sin6_scope_id != zoneid)
 				return (EINVAL);
 		} else		/* user omit to specify the ID. */
 			dst6.sin6_scope_id = zoneid;
 
 		/* convert into the internal form */
 		if (sa6_embedscope(&dst6, 0))
 			return (EINVAL); /* XXX: should be impossible */
 	}
 	/* Modify original ifra_dstaddr to reflect changes */
 	ifra->ifra_dstaddr = dst6;
 
 	/*
 	 * The destination address can be specified only for a p2p or a
 	 * loopback interface.  If specified, the corresponding prefix length
 	 * must be 128.
 	 */
 	if (ifra->ifra_dstaddr.sin6_family == AF_INET6) {
 		if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) == 0) {
 			/* XXX: noisy message */
 			nd6log((LOG_INFO, "in6_update_ifa: a destination can "
 			    "be specified for a p2p or a loopback IF only\n"));
 			return (EINVAL);
 		}
 		if (plen != 128) {
 			nd6log((LOG_INFO, "in6_update_ifa: prefixlen should "
 			    "be 128 when dstaddr is specified\n"));
 			return (EINVAL);
 		}
 	}
 	/* lifetime consistency check */
 	lt = &ifra->ifra_lifetime;
 	if (lt->ia6t_pltime > lt->ia6t_vltime)
 		return (EINVAL);
 	if (lt->ia6t_vltime == 0) {
 		/*
 		 * the following log might be noisy, but this is a typical
 		 * configuration mistake or a tool's bug.
 		 */
 		nd6log((LOG_INFO,
 		    "in6_update_ifa: valid lifetime is 0 for %s\n",
 		    ip6_sprintf(ip6buf, &ifra->ifra_addr.sin6_addr)));
 
 		if (ia == NULL)
 			return (0); /* there's nothing to do */
 	}
 
 	/* Check prefix mask */
 	if (ia != NULL && ifra->ifra_prefixmask.sin6_len != 0) {
 		/*
 		 * We prohibit changing the prefix length of an existing
 		 * address, because
 		 * + such an operation should be rare in IPv6, and
 		 * + the operation would confuse prefix management.
 		 */
 		if (ia->ia_prefixmask.sin6_len != 0 &&
 		    in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL) != plen) {
 			nd6log((LOG_INFO, "in6_validate_ifa: the prefix length "
 			    "of an existing %s address should not be changed\n",
 			    ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr)));
 
 			return (EINVAL);
 		}
 	}
 
 	return (0);
 }
 
 /*
  * Allocate a new ifaddr and link it into chains.
  */
 static struct in6_ifaddr *
 in6_alloc_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, int flags)
 {
 	struct in6_ifaddr *ia;
 
 	/*
 	 * When in6_alloc_ifa() is called in a process of a received
 	 * RA, it is called under an interrupt context.  So, we should
 	 * call malloc with M_NOWAIT.
 	 */
 	ia = (struct in6_ifaddr *)ifa_alloc(sizeof(*ia), M_NOWAIT);
 	if (ia == NULL)
 		return (NULL);
 	LIST_INIT(&ia->ia6_memberships);
 	/* Initialize the address and masks, and put time stamp */
 	ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
 	ia->ia_addr.sin6_family = AF_INET6;
 	ia->ia_addr.sin6_len = sizeof(ia->ia_addr);
 	/* XXX: Can we assign ,sin6_addr and skip the rest? */
 	ia->ia_addr = ifra->ifra_addr;
 	ia->ia6_createtime = time_uptime;
 	if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) != 0) {
 		/*
 		 * Some functions expect that ifa_dstaddr is not
 		 * NULL for p2p interfaces.
 		 */
 		ia->ia_ifa.ifa_dstaddr =
 		    (struct sockaddr *)&ia->ia_dstaddr;
 	} else {
 		ia->ia_ifa.ifa_dstaddr = NULL;
 	}
 
 	/* set prefix mask if any */
 	ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask;
 	if (ifra->ifra_prefixmask.sin6_len != 0) {
 		ia->ia_prefixmask.sin6_family = AF_INET6;
 		ia->ia_prefixmask.sin6_len = ifra->ifra_prefixmask.sin6_len;
 		ia->ia_prefixmask.sin6_addr = ifra->ifra_prefixmask.sin6_addr;
 	}
 
 	ia->ia_ifp = ifp;
 	ifa_ref(&ia->ia_ifa);			/* if_addrhead */
 	IF_ADDR_WLOCK(ifp);
 	CK_STAILQ_INSERT_TAIL(&ifp->if_addrhead, &ia->ia_ifa, ifa_link);
 	IF_ADDR_WUNLOCK(ifp);
 
 	ifa_ref(&ia->ia_ifa);			/* in6_ifaddrhead */
 	IN6_IFADDR_WLOCK();
 	CK_STAILQ_INSERT_TAIL(&V_in6_ifaddrhead, ia, ia_link);
 	CK_LIST_INSERT_HEAD(IN6ADDR_HASH(&ia->ia_addr.sin6_addr), ia, ia6_hash);
 	IN6_IFADDR_WUNLOCK();
 
 	return (ia);
 }
 
 /*
  * Update/configure interface address parameters:
  *
  * 1) Update lifetime
  * 2) Update interface metric ad flags
  * 3) Notify other subsystems
  */
 static int
 in6_update_ifa_internal(struct ifnet *ifp, struct in6_aliasreq *ifra,
     struct in6_ifaddr *ia, int hostIsNew, int flags)
 {
 	int error;
 
 	/* update timestamp */
 	ia->ia6_updatetime = time_uptime;
 
 	/*
 	 * Set lifetimes.  We do not refer to ia6t_expire and ia6t_preferred
 	 * to see if the address is deprecated or invalidated, but initialize
 	 * these members for applications.
 	 */
 	ia->ia6_lifetime = ifra->ifra_lifetime;
 	if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
 		ia->ia6_lifetime.ia6t_expire =
 		    time_uptime + ia->ia6_lifetime.ia6t_vltime;
 	} else
 		ia->ia6_lifetime.ia6t_expire = 0;
 	if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
 		ia->ia6_lifetime.ia6t_preferred =
 		    time_uptime + ia->ia6_lifetime.ia6t_pltime;
 	} else
 		ia->ia6_lifetime.ia6t_preferred = 0;
 
 	/*
 	 * backward compatibility - if IN6_IFF_DEPRECATED is set from the
 	 * userland, make it deprecated.
 	 */
 	if ((ifra->ifra_flags & IN6_IFF_DEPRECATED) != 0) {
 		ia->ia6_lifetime.ia6t_pltime = 0;
 		ia->ia6_lifetime.ia6t_preferred = time_uptime;
 	}
 
 	/*
 	 * configure address flags.
 	 */
 	ia->ia6_flags = ifra->ifra_flags;
 
 	/*
 	 * Make the address tentative before joining multicast addresses,
 	 * so that corresponding MLD responses would not have a tentative
 	 * source address.
 	 */
 	ia->ia6_flags &= ~IN6_IFF_DUPLICATED;	/* safety */
 
 	/*
 	 * DAD should be performed for an new address or addresses on
 	 * an interface with ND6_IFF_IFDISABLED.
 	 */
 	if (in6if_do_dad(ifp) &&
 	    (hostIsNew || (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED)))
 		ia->ia6_flags |= IN6_IFF_TENTATIVE;
 
 	/* notify other subsystems */
 	error = in6_notify_ifa(ifp, ia, ifra, hostIsNew);
 
 	return (error);
 }
 
 /*
  * Do link-level ifa job:
  * 1) Add lle entry for added address
  * 2) Notifies routing socket users about new address
  * 3) join appropriate multicast group
  * 4) start DAD if enabled
  */
 static int
 in6_broadcast_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
     struct in6_ifaddr *ia, int flags)
 {
 	struct in6_multi *in6m_sol;
 	int error = 0;
 
 	/* Add local address to lltable, if necessary (ex. on p2p link). */
 	if ((error = nd6_add_ifa_lle(ia)) != 0) {
 		in6_purgeaddr(&ia->ia_ifa);
 		ifa_free(&ia->ia_ifa);
 		return (error);
 	}
 
 	/* Join necessary multicast groups. */
 	in6m_sol = NULL;
 	if ((ifp->if_flags & IFF_MULTICAST) != 0) {
 		error = in6_update_ifa_join_mc(ifp, ifra, ia, flags, &in6m_sol);
 		if (error != 0) {
 			in6_purgeaddr(&ia->ia_ifa);
 			ifa_free(&ia->ia_ifa);
 			return (error);
 		}
 	}
 
 	/* Perform DAD, if the address is TENTATIVE. */
 	if ((ia->ia6_flags & IN6_IFF_TENTATIVE)) {
 		int delay, mindelay, maxdelay;
 
 		delay = 0;
 		if ((flags & IN6_IFAUPDATE_DADDELAY)) {
 			/*
 			 * We need to impose a delay before sending an NS
 			 * for DAD.  Check if we also needed a delay for the
 			 * corresponding MLD message.  If we did, the delay
 			 * should be larger than the MLD delay (this could be
 			 * relaxed a bit, but this simple logic is at least
 			 * safe).
 			 * XXX: Break data hiding guidelines and look at
 			 * state for the solicited multicast group.
 			 */
 			mindelay = 0;
 			if (in6m_sol != NULL &&
 			    in6m_sol->in6m_state == MLD_REPORTING_MEMBER) {
 				mindelay = in6m_sol->in6m_timer;
 			}
 			maxdelay = MAX_RTR_SOLICITATION_DELAY * hz;
 			if (maxdelay - mindelay == 0)
 				delay = 0;
 			else {
 				delay =
 				    (arc4random() % (maxdelay - mindelay)) +
 				    mindelay;
 			}
 		}
 		nd6_dad_start((struct ifaddr *)ia, delay);
 	}
 
 	in6_newaddrmsg(ia, RTM_ADD);
 	ifa_free(&ia->ia_ifa);
 	return (error);
 }
 
+/*
+ * Adds or deletes interface route for p2p ifa.
+ * Returns 0 on success or errno.
+ */
+static int
+in6_handle_dstaddr_rtrequest(int cmd, struct in6_ifaddr *ia)
+{
+	struct epoch_tracker et;
+	struct ifaddr *ifa = &ia->ia_ifa;
+	int error;
+
+	/* Prepare gateway */
+	struct sockaddr_dl_short sdl = {
+		.sdl_family = AF_LINK,
+		.sdl_len = sizeof(struct sockaddr_dl_short),
+		.sdl_type = ifa->ifa_ifp->if_type,
+		.sdl_index = ifa->ifa_ifp->if_index,
+	};
+
+	struct sockaddr_in6 dst = {
+		.sin6_family = AF_INET6,
+		.sin6_len = sizeof(struct sockaddr_in6),
+		.sin6_addr = ia->ia_dstaddr.sin6_addr,
+	};
+
+	struct rt_addrinfo info = {
+		.rti_ifa = ifa,
+		.rti_flags = RTF_PINNED | RTF_HOST,
+		.rti_info = {
+			[RTAX_DST] = (struct sockaddr *)&dst,
+			[RTAX_GATEWAY] = (struct sockaddr *)&sdl,
+		},
+	};
+	/* Don't set additional per-gw filters on removal */
+
+	NET_EPOCH_ENTER(et);
+	error = rib_handle_ifaddr_info(ifa->ifa_ifp->if_fib, cmd, &info);
+	NET_EPOCH_EXIT(et);
+
+	return (error);
+}
+
 void
 in6_purgeaddr(struct ifaddr *ifa)
 {
 	struct ifnet *ifp = ifa->ifa_ifp;
 	struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa;
 	struct in6_multi_mship *imm;
 	int plen, error;
 
 	if (ifa->ifa_carp)
 		(*carp_detach_p)(ifa, false);
 
 	/*
 	 * Remove the loopback route to the interface address.
 	 * The check for the current setting of "nd6_useloopback"
 	 * is not needed.
 	 */
 	if (ia->ia_flags & IFA_RTSELF) {
 		error = ifa_del_loopback_route((struct ifaddr *)ia,
 		    (struct sockaddr *)&ia->ia_addr);
 		if (error == 0)
 			ia->ia_flags &= ~IFA_RTSELF;
 	}
 
 	/* stop DAD processing */
 	nd6_dad_stop(ifa);
 
 	/* Leave multicast groups. */
 	while ((imm = LIST_FIRST(&ia->ia6_memberships)) != NULL) {
 		LIST_REMOVE(imm, i6mm_chain);
 		if (imm->i6mm_maddr != NULL)
 			in6_leavegroup(imm->i6mm_maddr, NULL);
 		free(imm, M_IP6MADDR);
 	}
+	/* Check if we need to remove p2p route */
 	plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */
+	if (ia->ia_dstaddr.sin6_family != AF_INET6)
+		plen = 0;
 	if ((ia->ia_flags & IFA_ROUTE) && plen == 128) {
-		error = rtinit(&(ia->ia_ifa), RTM_DELETE, ia->ia_flags |
-		    (ia->ia_dstaddr.sin6_family == AF_INET6 ? RTF_HOST : 0));
+		error = in6_handle_dstaddr_rtrequest(RTM_DELETE, ia);
 		if (error != 0)
 			log(LOG_INFO, "%s: err=%d, destination address delete "
 			    "failed\n", __func__, error);
 		ia->ia_flags &= ~IFA_ROUTE;
 	}
 
 	in6_newaddrmsg(ia, RTM_DELETE);
 	in6_unlink_ifa(ia, ifp);
 }
 
 static void
 in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp)
 {
 	char ip6buf[INET6_ADDRSTRLEN];
 	int remove_lle;
 
 	IF_ADDR_WLOCK(ifp);
 	CK_STAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifaddr, ifa_link);
 	IF_ADDR_WUNLOCK(ifp);
 	ifa_free(&ia->ia_ifa);			/* if_addrhead */
 
 	/*
 	 * Defer the release of what might be the last reference to the
 	 * in6_ifaddr so that it can't be freed before the remainder of the
 	 * cleanup.
 	 */
 	IN6_IFADDR_WLOCK();
 	CK_STAILQ_REMOVE(&V_in6_ifaddrhead, ia, in6_ifaddr, ia_link);
 	CK_LIST_REMOVE(ia, ia6_hash);
 	IN6_IFADDR_WUNLOCK();
 
 	/*
 	 * Release the reference to the base prefix.  There should be a
 	 * positive reference.
 	 */
 	remove_lle = 0;
 	if (ia->ia6_ndpr == NULL) {
 		nd6log((LOG_NOTICE,
 		    "in6_unlink_ifa: autoconf'ed address "
 		    "%s has no prefix\n", ip6_sprintf(ip6buf, IA6_IN6(ia))));
 	} else {
 		ia->ia6_ndpr->ndpr_addrcnt--;
 		/* Do not delete lles within prefix if refcont != 0 */
 		if (ia->ia6_ndpr->ndpr_addrcnt == 0)
 			remove_lle = 1;
 		ia->ia6_ndpr = NULL;
 	}
 
 	nd6_rem_ifa_lle(ia, remove_lle);
 
 	/*
 	 * Also, if the address being removed is autoconf'ed, call
 	 * pfxlist_onlink_check() since the release might affect the status of
 	 * other (detached) addresses.
 	 */
 	if ((ia->ia6_flags & IN6_IFF_AUTOCONF)) {
 		pfxlist_onlink_check();
 	}
 	ifa_free(&ia->ia_ifa);			/* in6_ifaddrhead */
 }
 
 /*
  * Notifies other subsystems about address change/arrival:
  * 1) Notifies device handler on the first IPv6 address assignment
  * 2) Handle routing table changes for P2P links and route
  * 3) Handle routing table changes for address host route
  */
 static int
 in6_notify_ifa(struct ifnet *ifp, struct in6_ifaddr *ia,
     struct in6_aliasreq *ifra, int hostIsNew)
 {
 	int	error = 0, plen, ifacount = 0;
 	struct ifaddr *ifa;
 	struct sockaddr_in6 *pdst;
 	char ip6buf[INET6_ADDRSTRLEN];
 
 	/*
 	 * Give the interface a chance to initialize
 	 * if this is its first address,
 	 */
 	if (hostIsNew != 0) {
 		struct epoch_tracker et;
 
 		NET_EPOCH_ENTER(et);
 		CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 			if (ifa->ifa_addr->sa_family != AF_INET6)
 				continue;
 			ifacount++;
 		}
 		NET_EPOCH_EXIT(et);
 	}
 
 	if (ifacount <= 1 && ifp->if_ioctl) {
 		error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia);
 		if (error)
 			goto done;
 	}
 
 	/*
 	 * If a new destination address is specified, scrub the old one and
 	 * install the new destination.  Note that the interface must be
 	 * p2p or loopback.
 	 */
 	pdst = &ifra->ifra_dstaddr;
 	if (pdst->sin6_family == AF_INET6 &&
 	    !IN6_ARE_ADDR_EQUAL(&pdst->sin6_addr, &ia->ia_dstaddr.sin6_addr)) {
 		if ((ia->ia_flags & IFA_ROUTE) != 0 &&
-		    (rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST) != 0)) {
+		    (in6_handle_dstaddr_rtrequest(RTM_DELETE, ia) != 0)) {
 			nd6log((LOG_ERR, "in6_update_ifa_internal: failed to "
 			    "remove a route to the old destination: %s\n",
 			    ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr)));
 			/* proceed anyway... */
 		} else
 			ia->ia_flags &= ~IFA_ROUTE;
 		ia->ia_dstaddr = *pdst;
 	}
 
 	/*
 	 * If a new destination address is specified for a point-to-point
 	 * interface, install a route to the destination as an interface
 	 * direct route.
 	 * XXX: the logic below rejects assigning multiple addresses on a p2p
 	 * interface that share the same destination.
 	 */
 	plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */
 	if (!(ia->ia_flags & IFA_ROUTE) && plen == 128 &&
 	    ia->ia_dstaddr.sin6_family == AF_INET6) {
-		int rtflags = RTF_UP | RTF_HOST;
 		/*
 		 * Handle the case for ::1 .
 		 */
 		if (ifp->if_flags & IFF_LOOPBACK)
 			ia->ia_flags |= IFA_RTSELF;
-		error = rtinit(&ia->ia_ifa, RTM_ADD, ia->ia_flags | rtflags);
+		error = in6_handle_dstaddr_rtrequest(RTM_ADD, ia);
 		if (error)
 			goto done;
 		ia->ia_flags |= IFA_ROUTE;
 	}
 
 	/*
 	 * add a loopback route to self if not exists
 	 */
 	if (!(ia->ia_flags & IFA_RTSELF) && V_nd6_useloopback) {
 		error = ifa_add_loopback_route((struct ifaddr *)ia,
 		    (struct sockaddr *)&ia->ia_addr);
 		if (error == 0)
 			ia->ia_flags |= IFA_RTSELF;
 	}
 done:
 	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
 	    "Invoking IPv6 network device address event may sleep");
 
 	ifa_ref(&ia->ia_ifa);
 	EVENTHANDLER_INVOKE(ifaddr_event_ext, ifp, &ia->ia_ifa,
 	    IFADDR_EVENT_ADD);
 	ifa_free(&ia->ia_ifa);
 
 	return (error);
 }
 
 /*
  * Find an IPv6 interface link-local address specific to an interface.
  * ifaddr is returned referenced.
  */
 struct in6_ifaddr *
 in6ifa_ifpforlinklocal(struct ifnet *ifp, int ignoreflags)
 {
 	struct ifaddr *ifa;
 
 	NET_EPOCH_ASSERT();
 
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 		if (ifa->ifa_addr->sa_family != AF_INET6)
 			continue;
 		if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa))) {
 			if ((((struct in6_ifaddr *)ifa)->ia6_flags &
 			    ignoreflags) != 0)
 				continue;
 			ifa_ref(ifa);
 			break;
 		}
 	}
 
 	return ((struct in6_ifaddr *)ifa);
 }
 
 /*
  * find the interface address corresponding to a given IPv6 address.
  * ifaddr is returned referenced.
  */
 struct in6_ifaddr *
 in6ifa_ifwithaddr(const struct in6_addr *addr, uint32_t zoneid)
 {
 	struct rm_priotracker in6_ifa_tracker;
 	struct in6_ifaddr *ia;
 
 	IN6_IFADDR_RLOCK(&in6_ifa_tracker);
 	CK_LIST_FOREACH(ia, IN6ADDR_HASH(addr), ia6_hash) {
 		if (IN6_ARE_ADDR_EQUAL(IA6_IN6(ia), addr)) {
 			if (zoneid != 0 &&
 			    zoneid != ia->ia_addr.sin6_scope_id)
 				continue;
 			ifa_ref(&ia->ia_ifa);
 			break;
 		}
 	}
 	IN6_IFADDR_RUNLOCK(&in6_ifa_tracker);
 	return (ia);
 }
 
 /*
  * find the internet address corresponding to a given interface and address.
  * ifaddr is returned referenced.
  */
 struct in6_ifaddr *
 in6ifa_ifpwithaddr(struct ifnet *ifp, const struct in6_addr *addr)
 {
 	struct epoch_tracker et;
 	struct ifaddr *ifa;
 
 	NET_EPOCH_ENTER(et);
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 		if (ifa->ifa_addr->sa_family != AF_INET6)
 			continue;
 		if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa))) {
 			ifa_ref(ifa);
 			break;
 		}
 	}
 	NET_EPOCH_EXIT(et);
 
 	return ((struct in6_ifaddr *)ifa);
 }
 
 /*
  * Find a link-local scoped address on ifp and return it if any.
  */
 struct in6_ifaddr *
 in6ifa_llaonifp(struct ifnet *ifp)
 {
 	struct epoch_tracker et;
 	struct sockaddr_in6 *sin6;
 	struct ifaddr *ifa;
 
 	if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED)
 		return (NULL);
 	NET_EPOCH_ENTER(et);
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 		if (ifa->ifa_addr->sa_family != AF_INET6)
 			continue;
 		sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
 		if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
 		    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr) ||
 		    IN6_IS_ADDR_MC_NODELOCAL(&sin6->sin6_addr))
 			break;
 	}
 	NET_EPOCH_EXIT(et);
 
 	return ((struct in6_ifaddr *)ifa);
 }
 
 /*
  * Convert IP6 address to printable (loggable) representation. Caller
  * has to make sure that ip6buf is at least INET6_ADDRSTRLEN long.
  */
 static char digits[] = "0123456789abcdef";
 char *
 ip6_sprintf(char *ip6buf, const struct in6_addr *addr)
 {
 	int i, cnt = 0, maxcnt = 0, idx = 0, index = 0;
 	char *cp;
 	const u_int16_t *a = (const u_int16_t *)addr;
 	const u_int8_t *d;
 	int dcolon = 0, zero = 0;
 
 	cp = ip6buf;
 
 	for (i = 0; i < 8; i++) {
 		if (*(a + i) == 0) {
 			cnt++;
 			if (cnt == 1)
 				idx = i;
 		}
 		else if (maxcnt < cnt) {
 			maxcnt = cnt;
 			index = idx;
 			cnt = 0;
 		}
 	}
 	if (maxcnt < cnt) {
 		maxcnt = cnt;
 		index = idx;
 	}
 
 	for (i = 0; i < 8; i++) {
 		if (dcolon == 1) {
 			if (*a == 0) {
 				if (i == 7)
 					*cp++ = ':';
 				a++;
 				continue;
 			} else
 				dcolon = 2;
 		}
 		if (*a == 0) {
 			if (dcolon == 0 && *(a + 1) == 0 && i == index) {
 				if (i == 0)
 					*cp++ = ':';
 				*cp++ = ':';
 				dcolon = 1;
 			} else {
 				*cp++ = '0';
 				*cp++ = ':';
 			}
 			a++;
 			continue;
 		}
 		d = (const u_char *)a;
 		/* Try to eliminate leading zeros in printout like in :0001. */
 		zero = 1;
 		*cp = digits[*d >> 4];
 		if (*cp != '0') {
 			zero = 0;
 			cp++;
 		}
 		*cp = digits[*d++ & 0xf];
 		if (zero == 0 || (*cp != '0')) {
 			zero = 0;
 			cp++;
 		}
 		*cp = digits[*d >> 4];
 		if (zero == 0 || (*cp != '0')) {
 			zero = 0;
 			cp++;
 		}
 		*cp++ = digits[*d & 0xf];
 		*cp++ = ':';
 		a++;
 	}
 	*--cp = '\0';
 	return (ip6buf);
 }
 
 int
 in6_localaddr(struct in6_addr *in6)
 {
 	struct rm_priotracker in6_ifa_tracker;
 	struct in6_ifaddr *ia;
 
 	if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6))
 		return 1;
 
 	IN6_IFADDR_RLOCK(&in6_ifa_tracker);
 	CK_STAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) {
 		if (IN6_ARE_MASKED_ADDR_EQUAL(in6, &ia->ia_addr.sin6_addr,
 		    &ia->ia_prefixmask.sin6_addr)) {
 			IN6_IFADDR_RUNLOCK(&in6_ifa_tracker);
 			return 1;
 		}
 	}
 	IN6_IFADDR_RUNLOCK(&in6_ifa_tracker);
 
 	return (0);
 }
 
 /*
  * Return 1 if an internet address is for the local host and configured
  * on one of its interfaces.
  */
 int
 in6_localip(struct in6_addr *in6)
 {
 	struct rm_priotracker in6_ifa_tracker;
 	struct in6_ifaddr *ia;
 
 	IN6_IFADDR_RLOCK(&in6_ifa_tracker);
 	CK_LIST_FOREACH(ia, IN6ADDR_HASH(in6), ia6_hash) {
 		if (IN6_ARE_ADDR_EQUAL(in6, &ia->ia_addr.sin6_addr)) {
 			IN6_IFADDR_RUNLOCK(&in6_ifa_tracker);
 			return (1);
 		}
 	}
 	IN6_IFADDR_RUNLOCK(&in6_ifa_tracker);
 	return (0);
 }
 
 /*
  * Return 1 if an internet address is configured on an interface.
  */
 int
 in6_ifhasaddr(struct ifnet *ifp, struct in6_addr *addr)
 {
 	struct in6_addr in6;
 	struct ifaddr *ifa;
 	struct in6_ifaddr *ia6;
 
 	NET_EPOCH_ASSERT();
 
 	in6 = *addr;
 	if (in6_clearscope(&in6))
 		return (0);
 	in6_setscope(&in6, ifp, NULL);
 
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 		if (ifa->ifa_addr->sa_family != AF_INET6)
 			continue;
 		ia6 = (struct in6_ifaddr *)ifa;
 		if (IN6_ARE_ADDR_EQUAL(&ia6->ia_addr.sin6_addr, &in6))
 			return (1);
 	}
 
 	return (0);
 }
 
 int
 in6_is_addr_deprecated(struct sockaddr_in6 *sa6)
 {
 	struct rm_priotracker in6_ifa_tracker;
 	struct in6_ifaddr *ia;
 
 	IN6_IFADDR_RLOCK(&in6_ifa_tracker);
 	CK_LIST_FOREACH(ia, IN6ADDR_HASH(&sa6->sin6_addr), ia6_hash) {
 		if (IN6_ARE_ADDR_EQUAL(IA6_IN6(ia), &sa6->sin6_addr)) {
 			if (ia->ia6_flags & IN6_IFF_DEPRECATED) {
 				IN6_IFADDR_RUNLOCK(&in6_ifa_tracker);
 				return (1); /* true */
 			}
 			break;
 		}
 	}
 	IN6_IFADDR_RUNLOCK(&in6_ifa_tracker);
 
 	return (0);		/* false */
 }
 
 /*
  * return length of part which dst and src are equal
  * hard coding...
  */
 int
 in6_matchlen(struct in6_addr *src, struct in6_addr *dst)
 {
 	int match = 0;
 	u_char *s = (u_char *)src, *d = (u_char *)dst;
 	u_char *lim = s + 16, r;
 
 	while (s < lim)
 		if ((r = (*d++ ^ *s++)) != 0) {
 			while (r < 128) {
 				match++;
 				r <<= 1;
 			}
 			break;
 		} else
 			match += 8;
 	return match;
 }
 
 /* XXX: to be scope conscious */
 int
 in6_are_prefix_equal(struct in6_addr *p1, struct in6_addr *p2, int len)
 {
 	int bytelen, bitlen;
 
 	/* sanity check */
 	if (0 > len || len > 128) {
 		log(LOG_ERR, "in6_are_prefix_equal: invalid prefix length(%d)\n",
 		    len);
 		return (0);
 	}
 
 	bytelen = len / 8;
 	bitlen = len % 8;
 
 	if (bcmp(&p1->s6_addr, &p2->s6_addr, bytelen))
 		return (0);
 	if (bitlen != 0 &&
 	    p1->s6_addr[bytelen] >> (8 - bitlen) !=
 	    p2->s6_addr[bytelen] >> (8 - bitlen))
 		return (0);
 
 	return (1);
 }
 
 void
 in6_prefixlen2mask(struct in6_addr *maskp, int len)
 {
 	u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};
 	int bytelen, bitlen, i;
 
 	/* sanity check */
 	if (0 > len || len > 128) {
 		log(LOG_ERR, "in6_prefixlen2mask: invalid prefix length(%d)\n",
 		    len);
 		return;
 	}
 
 	bzero(maskp, sizeof(*maskp));
 	bytelen = len / 8;
 	bitlen = len % 8;
 	for (i = 0; i < bytelen; i++)
 		maskp->s6_addr[i] = 0xff;
 	if (bitlen)
 		maskp->s6_addr[bytelen] = maskarray[bitlen - 1];
 }
 
 /*
  * return the best address out of the same scope. if no address was
  * found, return the first valid address from designated IF.
  */
 struct in6_ifaddr *
 in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst)
 {
 	int dst_scope =	in6_addrscope(dst), blen = -1, tlen;
 	struct ifaddr *ifa;
 	struct in6_ifaddr *besta = NULL;
 	struct in6_ifaddr *dep[2];	/* last-resort: deprecated */
 
 	NET_EPOCH_ASSERT();
 
 	dep[0] = dep[1] = NULL;
 
 	/*
 	 * We first look for addresses in the same scope.
 	 * If there is one, return it.
 	 * If two or more, return one which matches the dst longest.
 	 * If none, return one of global addresses assigned other ifs.
 	 */
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 		if (ifa->ifa_addr->sa_family != AF_INET6)
 			continue;
 		if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)
 			continue; /* XXX: is there any case to allow anycast? */
 		if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY)
 			continue; /* don't use this interface */
 		if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED)
 			continue;
 		if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) {
 			if (V_ip6_use_deprecated)
 				dep[0] = (struct in6_ifaddr *)ifa;
 			continue;
 		}
 
 		if (dst_scope == in6_addrscope(IFA_IN6(ifa))) {
 			/*
 			 * call in6_matchlen() as few as possible
 			 */
 			if (besta) {
 				if (blen == -1)
 					blen = in6_matchlen(&besta->ia_addr.sin6_addr, dst);
 				tlen = in6_matchlen(IFA_IN6(ifa), dst);
 				if (tlen > blen) {
 					blen = tlen;
 					besta = (struct in6_ifaddr *)ifa;
 				}
 			} else
 				besta = (struct in6_ifaddr *)ifa;
 		}
 	}
 	if (besta) {
 		ifa_ref(&besta->ia_ifa);
 		return (besta);
 	}
 
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 		if (ifa->ifa_addr->sa_family != AF_INET6)
 			continue;
 		if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)
 			continue; /* XXX: is there any case to allow anycast? */
 		if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY)
 			continue; /* don't use this interface */
 		if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED)
 			continue;
 		if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) {
 			if (V_ip6_use_deprecated)
 				dep[1] = (struct in6_ifaddr *)ifa;
 			continue;
 		}
 
 		if (ifa != NULL)
 			ifa_ref(ifa);
 		return (struct in6_ifaddr *)ifa;
 	}
 
 	/* use the last-resort values, that are, deprecated addresses */
 	if (dep[0]) {
 		ifa_ref((struct ifaddr *)dep[0]);
 		return dep[0];
 	}
 	if (dep[1]) {
 		ifa_ref((struct ifaddr *)dep[1]);
 		return dep[1];
 	}
 
 	return NULL;
 }
 
 /*
  * perform DAD when interface becomes IFF_UP.
  */
 void
 in6_if_up(struct ifnet *ifp)
 {
 	struct epoch_tracker et;
 	struct ifaddr *ifa;
 	struct in6_ifaddr *ia;
 
 	NET_EPOCH_ENTER(et);
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 		if (ifa->ifa_addr->sa_family != AF_INET6)
 			continue;
 		ia = (struct in6_ifaddr *)ifa;
 		if (ia->ia6_flags & IN6_IFF_TENTATIVE) {
 			/*
 			 * The TENTATIVE flag was likely set by hand
 			 * beforehand, implicitly indicating the need for DAD.
 			 * We may be able to skip the random delay in this
 			 * case, but we impose delays just in case.
 			 */
 			nd6_dad_start(ifa,
 			    arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz));
 		}
 	}
 	NET_EPOCH_EXIT(et);
 
 	/*
 	 * special cases, like 6to4, are handled in in6_ifattach
 	 */
 	in6_ifattach(ifp, NULL);
 }
 
 int
 in6if_do_dad(struct ifnet *ifp)
 {
 
 	if ((ifp->if_flags & IFF_LOOPBACK) != 0)
 		return (0);
 	if ((ifp->if_flags & IFF_MULTICAST) == 0)
 		return (0);
 	if ((ND_IFINFO(ifp)->flags &
 	    (ND6_IFF_IFDISABLED | ND6_IFF_NO_DAD)) != 0)
 		return (0);
 	return (1);
 }
 
 /*
  * Calculate max IPv6 MTU through all the interfaces and store it
  * to in6_maxmtu.
  */
 void
 in6_setmaxmtu(void)
 {
 	struct epoch_tracker et;
 	unsigned long maxmtu = 0;
 	struct ifnet *ifp;
 
 	NET_EPOCH_ENTER(et);
 	CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
 		/* this function can be called during ifnet initialization */
 		if (!ifp->if_afdata[AF_INET6])
 			continue;
 		if ((ifp->if_flags & IFF_LOOPBACK) == 0 &&
 		    IN6_LINKMTU(ifp) > maxmtu)
 			maxmtu = IN6_LINKMTU(ifp);
 	}
 	NET_EPOCH_EXIT(et);
 	if (maxmtu)	/* update only when maxmtu is positive */
 		V_in6_maxmtu = maxmtu;
 }
 
 /*
  * Provide the length of interface identifiers to be used for the link attached
  * to the given interface.  The length should be defined in "IPv6 over
  * xxx-link" document.  Note that address architecture might also define
  * the length for a particular set of address prefixes, regardless of the
  * link type.  As clarified in rfc2462bis, those two definitions should be
  * consistent, and those really are as of August 2004.
  */
 int
 in6_if2idlen(struct ifnet *ifp)
 {
 	switch (ifp->if_type) {
 	case IFT_ETHER:		/* RFC2464 */
 	case IFT_PROPVIRTUAL:	/* XXX: no RFC. treat it as ether */
 	case IFT_L2VLAN:	/* ditto */
 	case IFT_BRIDGE:	/* bridge(4) only does Ethernet-like links */
 	case IFT_INFINIBAND:
 		return (64);
 	case IFT_PPP:		/* RFC2472 */
 		return (64);
 	case IFT_FRELAY:	/* RFC2590 */
 		return (64);
 	case IFT_IEEE1394:	/* RFC3146 */
 		return (64);
 	case IFT_GIF:
 		return (64);	/* draft-ietf-v6ops-mech-v2-07 */
 	case IFT_LOOP:
 		return (64);	/* XXX: is this really correct? */
 	default:
 		/*
 		 * Unknown link type:
 		 * It might be controversial to use the today's common constant
 		 * of 64 for these cases unconditionally.  For full compliance,
 		 * we should return an error in this case.  On the other hand,
 		 * if we simply miss the standard for the link type or a new
 		 * standard is defined for a new link type, the IFID length
 		 * is very likely to be the common constant.  As a compromise,
 		 * we always use the constant, but make an explicit notice
 		 * indicating the "unknown" case.
 		 */
 		printf("in6_if2idlen: unknown link type (%d)\n", ifp->if_type);
 		return (64);
 	}
 }
 
 struct in6_llentry {
 	struct llentry		base;
 };
 
 #define	IN6_LLTBL_DEFAULT_HSIZE	32
 #define	IN6_LLTBL_HASH(k, h) \
 	(((((((k >> 8) ^ k) >> 8) ^ k) >> 8) ^ k) & ((h) - 1))
 
 /*
  * Do actual deallocation of @lle.
  */
 static void
 in6_lltable_destroy_lle_unlocked(epoch_context_t ctx)
 {
 	struct llentry *lle;
 
 	lle = __containerof(ctx, struct llentry, lle_epoch_ctx);
 	LLE_LOCK_DESTROY(lle);
 	LLE_REQ_DESTROY(lle);
 	free(lle, M_LLTABLE);
 }
 
 /*
  * Called by LLE_FREE_LOCKED when number of references
  * drops to zero.
  */
 static void
 in6_lltable_destroy_lle(struct llentry *lle)
 {
 
 	LLE_WUNLOCK(lle);
 	NET_EPOCH_CALL(in6_lltable_destroy_lle_unlocked, &lle->lle_epoch_ctx);
 }
 
 static struct llentry *
 in6_lltable_new(const struct in6_addr *addr6, u_int flags)
 {
 	struct in6_llentry *lle;
 
 	lle = malloc(sizeof(struct in6_llentry), M_LLTABLE, M_NOWAIT | M_ZERO);
 	if (lle == NULL)		/* NB: caller generates msg */
 		return NULL;
 
 	lle->base.r_l3addr.addr6 = *addr6;
 	lle->base.lle_refcnt = 1;
 	lle->base.lle_free = in6_lltable_destroy_lle;
 	LLE_LOCK_INIT(&lle->base);
 	LLE_REQ_INIT(&lle->base);
 	callout_init(&lle->base.lle_timer, 1);
 
 	return (&lle->base);
 }
 
 static int
 in6_lltable_match_prefix(const struct sockaddr *saddr,
     const struct sockaddr *smask, u_int flags, struct llentry *lle)
 {
 	const struct in6_addr *addr, *mask, *lle_addr;
 
 	addr = &((const struct sockaddr_in6 *)saddr)->sin6_addr;
 	mask = &((const struct sockaddr_in6 *)smask)->sin6_addr;
 	lle_addr = &lle->r_l3addr.addr6;
 
 	if (IN6_ARE_MASKED_ADDR_EQUAL(lle_addr, addr, mask) == 0)
 		return (0);
 
 	if (lle->la_flags & LLE_IFADDR) {
 		/*
 		 * Delete LLE_IFADDR records IFF address & flag matches.
 		 * Note that addr is the interface address within prefix
 		 * being matched.
 		 */
 		if (IN6_ARE_ADDR_EQUAL(addr, lle_addr) &&
 		    (flags & LLE_STATIC) != 0)
 			return (1);
 		return (0);
 	}
 
 	/* flags & LLE_STATIC means deleting both dynamic and static entries */
 	if ((flags & LLE_STATIC) || !(lle->la_flags & LLE_STATIC))
 		return (1);
 
 	return (0);
 }
 
 static void
 in6_lltable_free_entry(struct lltable *llt, struct llentry *lle)
 {
 	struct ifnet *ifp;
 
 	LLE_WLOCK_ASSERT(lle);
 	KASSERT(llt != NULL, ("lltable is NULL"));
 
 	/* Unlink entry from table */
 	if ((lle->la_flags & LLE_LINKED) != 0) {
 		ifp = llt->llt_ifp;
 		IF_AFDATA_WLOCK_ASSERT(ifp);
 		lltable_unlink_entry(llt, lle);
 	}
 
 	llentry_free(lle);
 }
 
 static int
 in6_lltable_rtcheck(struct ifnet *ifp,
 		    u_int flags,
 		    const struct sockaddr *l3addr)
 {
 	const struct sockaddr_in6 *sin6;
 	struct nhop_object *nh;
 	struct in6_addr dst;
 	uint32_t scopeid;
 	char ip6buf[INET6_ADDRSTRLEN];
 	int fibnum;
 
 	NET_EPOCH_ASSERT();
 	KASSERT(l3addr->sa_family == AF_INET6,
 	    ("sin_family %d", l3addr->sa_family));
 
 	sin6 = (const struct sockaddr_in6 *)l3addr;
 	in6_splitscope(&sin6->sin6_addr, &dst, &scopeid);
 	fibnum = V_rt_add_addr_allfibs ? RT_DEFAULT_FIB : ifp->if_fib;
 	nh = fib6_lookup(fibnum, &dst, scopeid, NHR_NONE, 0);
 	if (nh && ((nh->nh_flags & NHF_GATEWAY) || nh->nh_ifp != ifp)) {
 		struct ifaddr *ifa;
 		/*
 		 * Create an ND6 cache for an IPv6 neighbor
 		 * that is not covered by our own prefix.
 		 */
 		ifa = ifaof_ifpforaddr(l3addr, ifp);
 		if (ifa != NULL) {
 			return 0;
 		}
 		log(LOG_INFO, "IPv6 address: \"%s\" is not on the network\n",
 		    ip6_sprintf(ip6buf, &sin6->sin6_addr));
 		return EINVAL;
 	}
 	return 0;
 }
 
 /*
  * Called by the datapath to indicate that the entry was used.
  */
 static void
 in6_lltable_mark_used(struct llentry *lle)
 {
 
 	LLE_REQ_LOCK(lle);
 	lle->r_skip_req = 0;
 
 	/*
 	 * Set the hit time so the callback function
 	 * can determine the remaining time before
 	 * transiting to the DELAY state.
 	 */
 	lle->lle_hittime = time_uptime;
 	LLE_REQ_UNLOCK(lle);
 }
 
 static inline uint32_t
 in6_lltable_hash_dst(const struct in6_addr *dst, uint32_t hsize)
 {
 
 	return (IN6_LLTBL_HASH(dst->s6_addr32[3], hsize));
 }
 
 static uint32_t
 in6_lltable_hash(const struct llentry *lle, uint32_t hsize)
 {
 
 	return (in6_lltable_hash_dst(&lle->r_l3addr.addr6, hsize));
 }
 
 static void
 in6_lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa)
 {
 	struct sockaddr_in6 *sin6;
 
 	sin6 = (struct sockaddr_in6 *)sa;
 	bzero(sin6, sizeof(*sin6));
 	sin6->sin6_family = AF_INET6;
 	sin6->sin6_len = sizeof(*sin6);
 	sin6->sin6_addr = lle->r_l3addr.addr6;
 }
 
 static inline struct llentry *
 in6_lltable_find_dst(struct lltable *llt, const struct in6_addr *dst)
 {
 	struct llentry *lle;
 	struct llentries *lleh;
 	u_int hashidx;
 
 	hashidx = in6_lltable_hash_dst(dst, llt->llt_hsize);
 	lleh = &llt->lle_head[hashidx];
 	CK_LIST_FOREACH(lle, lleh, lle_next) {
 		if (lle->la_flags & LLE_DELETED)
 			continue;
 		if (IN6_ARE_ADDR_EQUAL(&lle->r_l3addr.addr6, dst))
 			break;
 	}
 
 	return (lle);
 }
 
 static void
 in6_lltable_delete_entry(struct lltable *llt, struct llentry *lle)
 {
 
 	lle->la_flags |= LLE_DELETED;
 	EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_DELETED);
 #ifdef DIAGNOSTIC
 	log(LOG_INFO, "ifaddr cache = %p is deleted\n", lle);
 #endif
 	llentry_free(lle);
 }
 
 static struct llentry *
 in6_lltable_alloc(struct lltable *llt, u_int flags,
 	const struct sockaddr *l3addr)
 {
 	const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)l3addr;
 	struct ifnet *ifp = llt->llt_ifp;
 	struct llentry *lle;
 	char linkhdr[LLE_MAX_LINKHDR];
 	size_t linkhdrsize;
 	int lladdr_off;
 
 	KASSERT(l3addr->sa_family == AF_INET6,
 	    ("sin_family %d", l3addr->sa_family));
 
 	/*
 	 * A route that covers the given address must have
 	 * been installed 1st because we are doing a resolution,
 	 * verify this.
 	 */
 	if (!(flags & LLE_IFADDR) &&
 	    in6_lltable_rtcheck(ifp, flags, l3addr) != 0)
 		return (NULL);
 
 	lle = in6_lltable_new(&sin6->sin6_addr, flags);
 	if (lle == NULL) {
 		log(LOG_INFO, "lla_lookup: new lle malloc failed\n");
 		return (NULL);
 	}
 	lle->la_flags = flags;
 	if ((flags & LLE_IFADDR) == LLE_IFADDR) {
 		linkhdrsize = LLE_MAX_LINKHDR;
 		if (lltable_calc_llheader(ifp, AF_INET6, IF_LLADDR(ifp),
 		    linkhdr, &linkhdrsize, &lladdr_off) != 0) {
 			NET_EPOCH_CALL(in6_lltable_destroy_lle_unlocked, &lle->lle_epoch_ctx);
 			return (NULL);
 		}
 		lltable_set_entry_addr(ifp, lle, linkhdr, linkhdrsize,
 		    lladdr_off);
 		lle->la_flags |= LLE_STATIC;
 	}
 
 	if ((lle->la_flags & LLE_STATIC) != 0)
 		lle->ln_state = ND6_LLINFO_REACHABLE;
 
 	return (lle);
 }
 
 static struct llentry *
 in6_lltable_lookup(struct lltable *llt, u_int flags,
 	const struct sockaddr *l3addr)
 {
 	const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)l3addr;
 	struct llentry *lle;
 
 	IF_AFDATA_LOCK_ASSERT(llt->llt_ifp);
 	KASSERT(l3addr->sa_family == AF_INET6,
 	    ("sin_family %d", l3addr->sa_family));
 	KASSERT((flags & (LLE_UNLOCKED | LLE_EXCLUSIVE)) !=
 	    (LLE_UNLOCKED | LLE_EXCLUSIVE),
 	    ("wrong lle request flags: %#x", flags));
 
 	lle = in6_lltable_find_dst(llt, &sin6->sin6_addr);
 	if (lle == NULL)
 		return (NULL);
 	if (flags & LLE_UNLOCKED)
 		return (lle);
 
 	if (flags & LLE_EXCLUSIVE)
 		LLE_WLOCK(lle);
 	else
 		LLE_RLOCK(lle);
 
 	/*
 	 * If the afdata lock is not held, the LLE may have been unlinked while
 	 * we were blocked on the LLE lock.  Check for this case.
 	 */
 	if (__predict_false((lle->la_flags & LLE_LINKED) == 0)) {
 		if (flags & LLE_EXCLUSIVE)
 			LLE_WUNLOCK(lle);
 		else
 			LLE_RUNLOCK(lle);
 		return (NULL);
 	}
 	return (lle);
 }
 
 static int
 in6_lltable_dump_entry(struct lltable *llt, struct llentry *lle,
     struct sysctl_req *wr)
 {
 	struct ifnet *ifp = llt->llt_ifp;
 	/* XXX stack use */
 	struct {
 		struct rt_msghdr	rtm;
 		struct sockaddr_in6	sin6;
 		/*
 		 * ndp.c assumes that sdl is word aligned
 		 */
 #ifdef __LP64__
 		uint32_t		pad;
 #endif
 		struct sockaddr_dl	sdl;
 	} ndpc;
 	struct sockaddr_dl *sdl;
 	int error;
 
 	bzero(&ndpc, sizeof(ndpc));
 	/* skip deleted entries */
 	if ((lle->la_flags & LLE_DELETED) == LLE_DELETED)
 		return (0);
 	/* Skip if jailed and not a valid IP of the prison. */
 	lltable_fill_sa_entry(lle, (struct sockaddr *)&ndpc.sin6);
 	if (prison_if(wr->td->td_ucred, (struct sockaddr *)&ndpc.sin6) != 0)
 		return (0);
 	/*
 	 * produce a msg made of:
 	 *  struct rt_msghdr;
 	 *  struct sockaddr_in6 (IPv6)
 	 *  struct sockaddr_dl;
 	 */
 	ndpc.rtm.rtm_msglen = sizeof(ndpc);
 	ndpc.rtm.rtm_version = RTM_VERSION;
 	ndpc.rtm.rtm_type = RTM_GET;
 	ndpc.rtm.rtm_flags = RTF_UP;
 	ndpc.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY;
 	sa6_recoverscope(&ndpc.sin6);
 
 	/* publish */
 	if (lle->la_flags & LLE_PUB)
 		ndpc.rtm.rtm_flags |= RTF_ANNOUNCE;
 
 	sdl = &ndpc.sdl;
 	sdl->sdl_family = AF_LINK;
 	sdl->sdl_len = sizeof(*sdl);
 	sdl->sdl_index = ifp->if_index;
 	sdl->sdl_type = ifp->if_type;
 	if ((lle->la_flags & LLE_VALID) == LLE_VALID) {
 		sdl->sdl_alen = ifp->if_addrlen;
 		bcopy(lle->ll_addr, LLADDR(sdl), ifp->if_addrlen);
 	} else {
 		sdl->sdl_alen = 0;
 		bzero(LLADDR(sdl), ifp->if_addrlen);
 	}
 	if (lle->la_expire != 0)
 		ndpc.rtm.rtm_rmx.rmx_expire = lle->la_expire +
 		    lle->lle_remtime / hz + time_second - time_uptime;
 	ndpc.rtm.rtm_flags |= (RTF_HOST | RTF_LLDATA);
 	if (lle->la_flags & LLE_STATIC)
 		ndpc.rtm.rtm_flags |= RTF_STATIC;
 	if (lle->la_flags & LLE_IFADDR)
 		ndpc.rtm.rtm_flags |= RTF_PINNED;
 	if (lle->ln_router != 0)
 		ndpc.rtm.rtm_flags |= RTF_GATEWAY;
 	ndpc.rtm.rtm_rmx.rmx_pksent = lle->la_asked;
 	/* Store state in rmx_weight value */
 	ndpc.rtm.rtm_rmx.rmx_state = lle->ln_state;
 	ndpc.rtm.rtm_index = ifp->if_index;
 	error = SYSCTL_OUT(wr, &ndpc, sizeof(ndpc));
 
 	return (error);
 }
 
 static struct lltable *
 in6_lltattach(struct ifnet *ifp)
 {
 	struct lltable *llt;
 
 	llt = lltable_allocate_htbl(IN6_LLTBL_DEFAULT_HSIZE);
 	llt->llt_af = AF_INET6;
 	llt->llt_ifp = ifp;
 
 	llt->llt_lookup = in6_lltable_lookup;
 	llt->llt_alloc_entry = in6_lltable_alloc;
 	llt->llt_delete_entry = in6_lltable_delete_entry;
 	llt->llt_dump_entry = in6_lltable_dump_entry;
 	llt->llt_hash = in6_lltable_hash;
 	llt->llt_fill_sa_entry = in6_lltable_fill_sa_entry;
 	llt->llt_free_entry = in6_lltable_free_entry;
 	llt->llt_match_prefix = in6_lltable_match_prefix;
 	llt->llt_mark_used = in6_lltable_mark_used;
  	lltable_link(llt);
 
 	return (llt);
 }
 
 void *
 in6_domifattach(struct ifnet *ifp)
 {
 	struct in6_ifextra *ext;
 
 	/* There are not IPv6-capable interfaces. */
 	switch (ifp->if_type) {
 	case IFT_PFLOG:
 	case IFT_PFSYNC:
 	case IFT_USB:
 		return (NULL);
 	}
 	ext = (struct in6_ifextra *)malloc(sizeof(*ext), M_IFADDR, M_WAITOK);
 	bzero(ext, sizeof(*ext));
 
 	ext->in6_ifstat = malloc(sizeof(counter_u64_t) *
 	    sizeof(struct in6_ifstat) / sizeof(uint64_t), M_IFADDR, M_WAITOK);
 	COUNTER_ARRAY_ALLOC(ext->in6_ifstat,
 	    sizeof(struct in6_ifstat) / sizeof(uint64_t), M_WAITOK);
 
 	ext->icmp6_ifstat = malloc(sizeof(counter_u64_t) *
 	    sizeof(struct icmp6_ifstat) / sizeof(uint64_t), M_IFADDR,
 	    M_WAITOK);
 	COUNTER_ARRAY_ALLOC(ext->icmp6_ifstat,
 	    sizeof(struct icmp6_ifstat) / sizeof(uint64_t), M_WAITOK);
 
 	ext->nd_ifinfo = nd6_ifattach(ifp);
 	ext->scope6_id = scope6_ifattach(ifp);
 	ext->lltable = in6_lltattach(ifp);
 
 	ext->mld_ifinfo = mld_domifattach(ifp);
 
 	return ext;
 }
 
 int
 in6_domifmtu(struct ifnet *ifp)
 {
 	if (ifp->if_afdata[AF_INET6] == NULL)
 		return ifp->if_mtu;
 
 	return (IN6_LINKMTU(ifp));
 }
 
 void
 in6_domifdetach(struct ifnet *ifp, void *aux)
 {
 	struct in6_ifextra *ext = (struct in6_ifextra *)aux;
 
 	mld_domifdetach(ifp);
 	scope6_ifdetach(ext->scope6_id);
 	nd6_ifdetach(ifp, ext->nd_ifinfo);
 	lltable_free(ext->lltable);
 	COUNTER_ARRAY_FREE(ext->in6_ifstat,
 	    sizeof(struct in6_ifstat) / sizeof(uint64_t));
 	free(ext->in6_ifstat, M_IFADDR);
 	COUNTER_ARRAY_FREE(ext->icmp6_ifstat,
 	    sizeof(struct icmp6_ifstat) / sizeof(uint64_t));
 	free(ext->icmp6_ifstat, M_IFADDR);
 	free(ext, M_IFADDR);
 }
 
 /*
  * Convert sockaddr_in6 to sockaddr_in.  Original sockaddr_in6 must be
  * v4 mapped addr or v4 compat addr
  */
 void
 in6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6)
 {
 
 	bzero(sin, sizeof(*sin));
 	sin->sin_len = sizeof(struct sockaddr_in);
 	sin->sin_family = AF_INET;
 	sin->sin_port = sin6->sin6_port;
 	sin->sin_addr.s_addr = sin6->sin6_addr.s6_addr32[3];
 }
 
 /* Convert sockaddr_in to sockaddr_in6 in v4 mapped addr format. */
 void
 in6_sin_2_v4mapsin6(struct sockaddr_in *sin, struct sockaddr_in6 *sin6)
 {
 	bzero(sin6, sizeof(*sin6));
 	sin6->sin6_len = sizeof(struct sockaddr_in6);
 	sin6->sin6_family = AF_INET6;
 	sin6->sin6_port = sin->sin_port;
 	sin6->sin6_addr.s6_addr32[0] = 0;
 	sin6->sin6_addr.s6_addr32[1] = 0;
 	sin6->sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_SMP;
 	sin6->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr;
 }
 
 /* Convert sockaddr_in6 into sockaddr_in. */
 void
 in6_sin6_2_sin_in_sock(struct sockaddr *nam)
 {
 	struct sockaddr_in *sin_p;
 	struct sockaddr_in6 sin6;
 
 	/*
 	 * Save original sockaddr_in6 addr and convert it
 	 * to sockaddr_in.
 	 */
 	sin6 = *(struct sockaddr_in6 *)nam;
 	sin_p = (struct sockaddr_in *)nam;
 	in6_sin6_2_sin(sin_p, &sin6);
 }
 
 /* Convert sockaddr_in into sockaddr_in6 in v4 mapped addr format. */
 void
 in6_sin_2_v4mapsin6_in_sock(struct sockaddr **nam)
 {
 	struct sockaddr_in *sin_p;
 	struct sockaddr_in6 *sin6_p;
 
 	sin6_p = malloc(sizeof *sin6_p, M_SONAME, M_WAITOK);
 	sin_p = (struct sockaddr_in *)*nam;
 	in6_sin_2_v4mapsin6(sin_p, sin6_p);
 	free(*nam, M_SONAME);
 	*nam = (struct sockaddr *)sin6_p;
 }
diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
index c6317b1ea263..eca704dc2843 100644
--- a/sys/netinet6/nd6_rtr.c
+++ b/sys/netinet6/nd6_rtr.c
@@ -1,2626 +1,2579 @@
 /*-
  * SPDX-License-Identifier: BSD-3-Clause
  *
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. Neither the name of the project nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  *	$KAME: nd6_rtr.c,v 1.111 2001/04/27 01:37:15 jinmei Exp $
  */
 
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/malloc.h>
 #include <sys/mbuf.h>
 #include <sys/refcount.h>
 #include <sys/socket.h>
 #include <sys/sockio.h>
 #include <sys/time.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/errno.h>
 #include <sys/rmlock.h>
 #include <sys/rwlock.h>
 #include <sys/sysctl.h>
 #include <sys/syslog.h>
 #include <sys/queue.h>
 
 #include <net/if.h>
 #include <net/if_var.h>
 #include <net/if_types.h>
 #include <net/if_dl.h>
 #include <net/route.h>
 #include <net/route/nhop.h>
 #include <net/route/route_ctl.h>
 #include <net/route/route_var.h>
 #include <net/radix.h>
 #include <net/vnet.h>
 
 #include <netinet/in.h>
 #include <net/if_llatbl.h>
 #include <netinet6/in6_var.h>
 #include <netinet6/in6_ifattach.h>
 #include <netinet/ip6.h>
 #include <netinet6/ip6_var.h>
 #include <netinet6/nd6.h>
 #include <netinet/icmp6.h>
 #include <netinet6/scope6_var.h>
 
 static struct nd_defrouter *defrtrlist_update(struct nd_defrouter *);
 static int prelist_update(struct nd_prefixctl *, struct nd_defrouter *,
     struct mbuf *, int);
 static int nd6_prefix_onlink(struct nd_prefix *);
 
 TAILQ_HEAD(nd6_drhead, nd_defrouter);
 VNET_DEFINE_STATIC(struct nd6_drhead, nd6_defrouter);
 #define	V_nd6_defrouter			VNET(nd6_defrouter)
 
 VNET_DECLARE(int, nd6_recalc_reachtm_interval);
 #define	V_nd6_recalc_reachtm_interval	VNET(nd6_recalc_reachtm_interval)
 
 VNET_DEFINE_STATIC(struct ifnet *, nd6_defifp);
 VNET_DEFINE(int, nd6_defifindex);
 #define	V_nd6_defifp			VNET(nd6_defifp)
 
 VNET_DEFINE(int, ip6_use_tempaddr) = 0;
 
 VNET_DEFINE(int, ip6_desync_factor);
 VNET_DEFINE(u_int32_t, ip6_temp_preferred_lifetime) = DEF_TEMP_PREFERRED_LIFETIME;
 VNET_DEFINE(u_int32_t, ip6_temp_valid_lifetime) = DEF_TEMP_VALID_LIFETIME;
 
 VNET_DEFINE(int, ip6_temp_regen_advance) = TEMPADDR_REGEN_ADVANCE;
 
 #ifdef EXPERIMENTAL
 VNET_DEFINE(int, nd6_ignore_ipv6_only_ra) = 1;
 #endif
 
 SYSCTL_DECL(_net_inet6_icmp6);
 
 /* RTPREF_MEDIUM has to be 0! */
 #define RTPREF_HIGH	1
 #define RTPREF_MEDIUM	0
 #define RTPREF_LOW	(-1)
 #define RTPREF_RESERVED	(-2)
 #define RTPREF_INVALID	(-3)	/* internal */
 
 static void
 defrouter_ref(struct nd_defrouter *dr)
 {
 
 	refcount_acquire(&dr->refcnt);
 }
 
 void
 defrouter_rele(struct nd_defrouter *dr)
 {
 
 	if (refcount_release(&dr->refcnt))
 		free(dr, M_IP6NDP);
 }
 
 /*
  * Remove a router from the global list and optionally stash it in a
  * caller-supplied queue.
  */
 static void
 defrouter_unlink(struct nd_defrouter *dr, struct nd6_drhead *drq)
 {
 
 	ND6_WLOCK_ASSERT();
 
 	TAILQ_REMOVE(&V_nd6_defrouter, dr, dr_entry);
 	V_nd6_list_genid++;
 	if (drq != NULL)
 		TAILQ_INSERT_TAIL(drq, dr, dr_entry);
 }
 
 /*
  * Receive Router Solicitation Message - just for routers.
  * Router solicitation/advertisement is mostly managed by userland program
  * (rtadvd) so here we have no function like nd6_ra_output().
  *
  * Based on RFC 2461
  */
 void
 nd6_rs_input(struct mbuf *m, int off, int icmp6len)
 {
 	struct ifnet *ifp;
 	struct ip6_hdr *ip6;
 	struct nd_router_solicit *nd_rs;
 	struct in6_addr saddr6;
 	union nd_opts ndopts;
 	char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];
 	char *lladdr;
 	int lladdrlen;
 
 	ifp = m->m_pkthdr.rcvif;
 
 	/*
 	 * Accept RS only when V_ip6_forwarding=1 and the interface has
 	 * no ND6_IFF_ACCEPT_RTADV.
 	 */
 	if (!V_ip6_forwarding || ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV)
 		goto freeit;
 
 	/* RFC 6980: Nodes MUST silently ignore fragments */   
 	if(m->m_flags & M_FRAGMENTED)
 		goto freeit;
 
 	/* Sanity checks */
 	ip6 = mtod(m, struct ip6_hdr *);
 	if (__predict_false(ip6->ip6_hlim != 255)) {
 		ICMP6STAT_INC(icp6s_invlhlim);
 		nd6log((LOG_ERR,
 		    "%s: invalid hlim (%d) from %s to %s on %s\n", __func__,
 		    ip6->ip6_hlim, ip6_sprintf(ip6bufs, &ip6->ip6_src),
 		    ip6_sprintf(ip6bufd, &ip6->ip6_dst), if_name(ifp)));
 		goto bad;
 	}
 
 	/*
 	 * Don't update the neighbor cache, if src = ::.
 	 * This indicates that the src has no IP address assigned yet.
 	 */
 	saddr6 = ip6->ip6_src;
 	if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
 		goto freeit;
 
 	if (m->m_len < off + icmp6len) {
 		m = m_pullup(m, off + icmp6len);
 		if (m == NULL) {
 			IP6STAT_INC(ip6s_exthdrtoolong);
 			return;
 		}
 	}
 	ip6 = mtod(m, struct ip6_hdr *);
 	nd_rs = (struct nd_router_solicit *)((caddr_t)ip6 + off);
 
 	icmp6len -= sizeof(*nd_rs);
 	nd6_option_init(nd_rs + 1, icmp6len, &ndopts);
 	if (nd6_options(&ndopts) < 0) {
 		nd6log((LOG_INFO,
 		    "%s: invalid ND option, ignored\n", __func__));
 		/* nd6_options have incremented stats */
 		goto freeit;
 	}
 
 	lladdr = NULL;
 	lladdrlen = 0;
 	if (ndopts.nd_opts_src_lladdr) {
 		lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1);
 		lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
 	}
 
 	if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
 		nd6log((LOG_INFO,
 		    "%s: lladdrlen mismatch for %s (if %d, RS packet %d)\n",
 		    __func__, ip6_sprintf(ip6bufs, &saddr6),
 		    ifp->if_addrlen, lladdrlen - 2));
 		goto bad;
 	}
 
 	nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0);
 
  freeit:
 	m_freem(m);
 	return;
 
  bad:
 	ICMP6STAT_INC(icp6s_badrs);
 	m_freem(m);
 }
 
 #ifdef EXPERIMENTAL
 /*
  * An initial update routine for draft-ietf-6man-ipv6only-flag.
  * We need to iterate over all default routers for the given
  * interface to see whether they are all advertising the "S"
  * (IPv6-Only) flag.  If they do set, otherwise unset, the
  * interface flag we later use to filter on.
  */
 static void
 defrtr_ipv6_only_ifp(struct ifnet *ifp)
 {
 	struct nd_defrouter *dr;
 	bool ipv6_only, ipv6_only_old;
 #ifdef INET
 	struct epoch_tracker et;
 	struct ifaddr *ifa;
 	bool has_ipv4_addr;
 #endif
 
 	if (V_nd6_ignore_ipv6_only_ra != 0)
 		return;
 
 	ipv6_only = true;
 	ND6_RLOCK();
 	TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry)
 		if (dr->ifp == ifp &&
 		    (dr->raflags & ND_RA_FLAG_IPV6_ONLY) == 0)
 			ipv6_only = false;
 	ND6_RUNLOCK();
 
 	IF_AFDATA_WLOCK(ifp);
 	ipv6_only_old = ND_IFINFO(ifp)->flags & ND6_IFF_IPV6_ONLY;
 	IF_AFDATA_WUNLOCK(ifp);
 
 	/* If nothing changed, we have an early exit. */
 	if (ipv6_only == ipv6_only_old)
 		return;
 
 #ifdef INET
 	/*
 	 * Should we want to set the IPV6-ONLY flag, check if the
 	 * interface has a non-0/0 and non-link-local IPv4 address
 	 * configured on it.  If it has we will assume working
 	 * IPv4 operations and will clear the interface flag.
 	 */
 	has_ipv4_addr = false;
 	if (ipv6_only) {
 		NET_EPOCH_ENTER(et);
 		CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 			if (ifa->ifa_addr->sa_family != AF_INET)
 				continue;
 			if (in_canforward(
 			    satosin(ifa->ifa_addr)->sin_addr)) {
 				has_ipv4_addr = true;
 				break;
 			}
 		}
 		NET_EPOCH_EXIT(et);
 	}
 	if (ipv6_only && has_ipv4_addr) {
 		log(LOG_NOTICE, "%s rcvd RA w/ IPv6-Only flag set but has IPv4 "
 		    "configured, ignoring IPv6-Only flag.\n", ifp->if_xname);
 		ipv6_only = false;
 	}
 #endif
 
 	IF_AFDATA_WLOCK(ifp);
 	if (ipv6_only)
 		ND_IFINFO(ifp)->flags |= ND6_IFF_IPV6_ONLY;
 	else
 		ND_IFINFO(ifp)->flags &= ~ND6_IFF_IPV6_ONLY;
 	IF_AFDATA_WUNLOCK(ifp);
 
 #ifdef notyet
 	/* Send notification of flag change. */
 #endif
 }
 
 static void
 defrtr_ipv6_only_ipf_down(struct ifnet *ifp)
 {
 
 	IF_AFDATA_WLOCK(ifp);
 	ND_IFINFO(ifp)->flags &= ~ND6_IFF_IPV6_ONLY;
 	IF_AFDATA_WUNLOCK(ifp);
 }
 #endif	/* EXPERIMENTAL */
 
 void
 nd6_ifnet_link_event(void *arg __unused, struct ifnet *ifp, int linkstate)
 {
 
 	/*
 	 * XXX-BZ we might want to trigger re-evaluation of our default router
 	 * availability. E.g., on link down the default router might be
 	 * unreachable but a different interface might still have connectivity.
 	 */
 
 #ifdef EXPERIMENTAL
 	if (linkstate == LINK_STATE_DOWN)
 		defrtr_ipv6_only_ipf_down(ifp);
 #endif
 }
 
 /*
  * Receive Router Advertisement Message.
  *
  * Based on RFC 2461
  * TODO: on-link bit on prefix information
  * TODO: ND_RA_FLAG_{OTHER,MANAGED} processing
  */
 void
 nd6_ra_input(struct mbuf *m, int off, int icmp6len)
 {
 	struct ifnet *ifp;
 	struct nd_ifinfo *ndi;
 	struct ip6_hdr *ip6;
 	struct nd_router_advert *nd_ra;
 	struct in6_addr saddr6;
 	struct nd_defrouter *dr;
 	union nd_opts ndopts;
 	char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];
 	int mcast;
 
 	/*
 	 * We only accept RAs only when the per-interface flag
 	 * ND6_IFF_ACCEPT_RTADV is on the receiving interface.
 	 */
 	ifp = m->m_pkthdr.rcvif;
 	ndi = ND_IFINFO(ifp);
 	if (!(ndi->flags & ND6_IFF_ACCEPT_RTADV))
 		goto freeit;
 
 	/* RFC 6980: Nodes MUST silently ignore fragments */
 	if(m->m_flags & M_FRAGMENTED)
 		goto freeit;
 
 	ip6 = mtod(m, struct ip6_hdr *);
 	if (__predict_false(ip6->ip6_hlim != 255)) {
 		ICMP6STAT_INC(icp6s_invlhlim);
 		nd6log((LOG_ERR,
 		    "%s: invalid hlim (%d) from %s to %s on %s\n", __func__,
 		    ip6->ip6_hlim, ip6_sprintf(ip6bufs, &ip6->ip6_src),
 		    ip6_sprintf(ip6bufd, &ip6->ip6_dst), if_name(ifp)));
 		goto bad;
 	}
 
 	saddr6 = ip6->ip6_src;
 	if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) {
 		nd6log((LOG_ERR,
 		    "%s: src %s is not link-local\n", __func__,
 		    ip6_sprintf(ip6bufs, &saddr6)));
 		goto bad;
 	}
 
 	if (m->m_len < off + icmp6len) {
 		m = m_pullup(m, off + icmp6len);
 		if (m == NULL) {
 			IP6STAT_INC(ip6s_exthdrtoolong);
 			return;
 		}
 	}
 	ip6 = mtod(m, struct ip6_hdr *);
 	nd_ra = (struct nd_router_advert *)((caddr_t)ip6 + off);
 
 	icmp6len -= sizeof(*nd_ra);
 	nd6_option_init(nd_ra + 1, icmp6len, &ndopts);
 	if (nd6_options(&ndopts) < 0) {
 		nd6log((LOG_INFO,
 		    "%s: invalid ND option, ignored\n", __func__));
 		/* nd6_options have incremented stats */
 		goto freeit;
 	}
 
 	mcast = 0;
 	dr = NULL;
     {
 	struct nd_defrouter dr0;
 	u_int32_t advreachable = nd_ra->nd_ra_reachable;
 
 	/* remember if this is a multicasted advertisement */
 	if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst))
 		mcast = 1;
 
 	bzero(&dr0, sizeof(dr0));
 	dr0.rtaddr = saddr6;
 	dr0.raflags = nd_ra->nd_ra_flags_reserved;
 	/*
 	 * Effectively-disable routes from RA messages when
 	 * ND6_IFF_NO_RADR enabled on the receiving interface or
 	 * (ip6.forwarding == 1 && ip6.rfc6204w3 != 1).
 	 */
 	if (ndi->flags & ND6_IFF_NO_RADR)
 		dr0.rtlifetime = 0;
 	else if (V_ip6_forwarding && !V_ip6_rfc6204w3)
 		dr0.rtlifetime = 0;
 	else
 		dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime);
 	dr0.expire = time_uptime + dr0.rtlifetime;
 	dr0.ifp = ifp;
 	/* unspecified or not? (RFC 2461 6.3.4) */
 	if (advreachable) {
 		advreachable = ntohl(advreachable);
 		if (advreachable <= MAX_REACHABLE_TIME &&
 		    ndi->basereachable != advreachable) {
 			ndi->basereachable = advreachable;
 			ndi->reachable = ND_COMPUTE_RTIME(ndi->basereachable);
 			ndi->recalctm = V_nd6_recalc_reachtm_interval; /* reset */
 		}
 	}
 	if (nd_ra->nd_ra_retransmit)
 		ndi->retrans = ntohl(nd_ra->nd_ra_retransmit);
 	if (nd_ra->nd_ra_curhoplimit) {
 		if (ndi->chlim < nd_ra->nd_ra_curhoplimit)
 			ndi->chlim = nd_ra->nd_ra_curhoplimit;
 		else if (ndi->chlim != nd_ra->nd_ra_curhoplimit) {
 			log(LOG_ERR, "RA with a lower CurHopLimit sent from "
 			    "%s on %s (current = %d, received = %d). "
 			    "Ignored.\n", ip6_sprintf(ip6bufs, &ip6->ip6_src),
 			    if_name(ifp), ndi->chlim, nd_ra->nd_ra_curhoplimit);
 		}
 	}
 	dr = defrtrlist_update(&dr0);
 #ifdef EXPERIMENTAL
 	defrtr_ipv6_only_ifp(ifp);
 #endif
     }
 
 	/*
 	 * prefix
 	 */
 	if (ndopts.nd_opts_pi) {
 		struct nd_opt_hdr *pt;
 		struct nd_opt_prefix_info *pi = NULL;
 		struct nd_prefixctl pr;
 
 		for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi;
 		     pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end;
 		     pt = (struct nd_opt_hdr *)((caddr_t)pt +
 						(pt->nd_opt_len << 3))) {
 			if (pt->nd_opt_type != ND_OPT_PREFIX_INFORMATION)
 				continue;
 			pi = (struct nd_opt_prefix_info *)pt;
 
 			if (pi->nd_opt_pi_len != 4) {
 				nd6log((LOG_INFO,
 				    "%s: invalid option len %d for prefix "
 				    "information option, ignored\n", __func__,
 				    pi->nd_opt_pi_len));
 				continue;
 			}
 
 			if (128 < pi->nd_opt_pi_prefix_len) {
 				nd6log((LOG_INFO,
 				    "%s: invalid prefix len %d for prefix "
 				    "information option, ignored\n", __func__,
 				    pi->nd_opt_pi_prefix_len));
 				continue;
 			}
 
 			if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix)
 			 || IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) {
 				nd6log((LOG_INFO,
 				    "%s: invalid prefix %s, ignored\n",
 				    __func__, ip6_sprintf(ip6bufs,
 					&pi->nd_opt_pi_prefix)));
 				continue;
 			}
 
 			bzero(&pr, sizeof(pr));
 			pr.ndpr_prefix.sin6_family = AF_INET6;
 			pr.ndpr_prefix.sin6_len = sizeof(pr.ndpr_prefix);
 			pr.ndpr_prefix.sin6_addr = pi->nd_opt_pi_prefix;
 			pr.ndpr_ifp = (struct ifnet *)m->m_pkthdr.rcvif;
 
 			pr.ndpr_raf_onlink = (pi->nd_opt_pi_flags_reserved &
 			    ND_OPT_PI_FLAG_ONLINK) ? 1 : 0;
 			pr.ndpr_raf_auto = (pi->nd_opt_pi_flags_reserved &
 			    ND_OPT_PI_FLAG_AUTO) ? 1 : 0;
 			pr.ndpr_plen = pi->nd_opt_pi_prefix_len;
 			pr.ndpr_vltime = ntohl(pi->nd_opt_pi_valid_time);
 			pr.ndpr_pltime = ntohl(pi->nd_opt_pi_preferred_time);
 			(void)prelist_update(&pr, dr, m, mcast);
 		}
 	}
 	if (dr != NULL) {
 		defrouter_rele(dr);
 		dr = NULL;
 	}
 
 	/*
 	 * MTU
 	 */
 	if (ndopts.nd_opts_mtu && ndopts.nd_opts_mtu->nd_opt_mtu_len == 1) {
 		u_long mtu;
 		u_long maxmtu;
 
 		mtu = (u_long)ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu);
 
 		/* lower bound */
 		if (mtu < IPV6_MMTU) {
 			nd6log((LOG_INFO, "%s: bogus mtu option mtu=%lu sent "
 			    "from %s, ignoring\n", __func__,
 			    mtu, ip6_sprintf(ip6bufs, &ip6->ip6_src)));
 			goto skip;
 		}
 
 		/* upper bound */
 		maxmtu = (ndi->maxmtu && ndi->maxmtu < ifp->if_mtu)
 		    ? ndi->maxmtu : ifp->if_mtu;
 		if (mtu <= maxmtu) {
 			int change = (ndi->linkmtu != mtu);
 
 			ndi->linkmtu = mtu;
 			if (change) {
 				/* in6_maxmtu may change */
 				in6_setmaxmtu();
 				rt_updatemtu(ifp);
 			}
 		} else {
 			nd6log((LOG_INFO, "%s: bogus mtu=%lu sent from %s; "
 			    "exceeds maxmtu %lu, ignoring\n", __func__,
 			    mtu, ip6_sprintf(ip6bufs, &ip6->ip6_src), maxmtu));
 		}
 	}
 
  skip:
 
 	/*
 	 * Source link layer address
 	 */
     {
 	char *lladdr = NULL;
 	int lladdrlen = 0;
 
 	if (ndopts.nd_opts_src_lladdr) {
 		lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1);
 		lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
 	}
 
 	if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
 		nd6log((LOG_INFO,
 		    "%s: lladdrlen mismatch for %s (if %d, RA packet %d)\n",
 		    __func__, ip6_sprintf(ip6bufs, &saddr6),
 		    ifp->if_addrlen, lladdrlen - 2));
 		goto bad;
 	}
 
 	nd6_cache_lladdr(ifp, &saddr6, lladdr,
 	    lladdrlen, ND_ROUTER_ADVERT, 0);
 
 	/*
 	 * Installing a link-layer address might change the state of the
 	 * router's neighbor cache, which might also affect our on-link
 	 * detection of adveritsed prefixes.
 	 */
 	pfxlist_onlink_check();
     }
 
  freeit:
 	m_freem(m);
 	return;
 
  bad:
 	ICMP6STAT_INC(icp6s_badra);
 	m_freem(m);
 }
 
 /* PFXRTR */
 static struct nd_pfxrouter *
 pfxrtr_lookup(struct nd_prefix *pr, struct nd_defrouter *dr)
 {
 	struct nd_pfxrouter *search;
 
 	ND6_LOCK_ASSERT();
 
 	LIST_FOREACH(search, &pr->ndpr_advrtrs, pfr_entry) {
 		if (search->router == dr)
 			break;
 	}
 	return (search);
 }
 
 static void
 pfxrtr_add(struct nd_prefix *pr, struct nd_defrouter *dr)
 {
 	struct nd_pfxrouter *new;
 	bool update;
 
 	ND6_UNLOCK_ASSERT();
 
 	ND6_RLOCK();
 	if (pfxrtr_lookup(pr, dr) != NULL) {
 		ND6_RUNLOCK();
 		return;
 	}
 	ND6_RUNLOCK();
 
 	new = malloc(sizeof(*new), M_IP6NDP, M_NOWAIT | M_ZERO);
 	if (new == NULL)
 		return;
 	defrouter_ref(dr);
 	new->router = dr;
 
 	ND6_WLOCK();
 	if (pfxrtr_lookup(pr, dr) == NULL) {
 		LIST_INSERT_HEAD(&pr->ndpr_advrtrs, new, pfr_entry);
 		update = true;
 	} else {
 		/* We lost a race to add the reference. */
 		defrouter_rele(dr);
 		free(new, M_IP6NDP);
 		update = false;
 	}
 	ND6_WUNLOCK();
 
 	if (update)
 		pfxlist_onlink_check();
 }
 
 static void
 pfxrtr_del(struct nd_pfxrouter *pfr)
 {
 
 	ND6_WLOCK_ASSERT();
 
 	LIST_REMOVE(pfr, pfr_entry);
 	defrouter_rele(pfr->router);
 	free(pfr, M_IP6NDP);
 }
 
 /* Default router list processing sub routines. */
 static void
 defrouter_addreq(struct nd_defrouter *new)
 {
 	struct sockaddr_in6 def, mask, gate;
 	struct rt_addrinfo info;
 	struct rib_cmd_info rc;
 	unsigned int fibnum;
 	int error;
 
 	bzero(&def, sizeof(def));
 	bzero(&mask, sizeof(mask));
 	bzero(&gate, sizeof(gate));
 
 	def.sin6_len = mask.sin6_len = gate.sin6_len =
 	    sizeof(struct sockaddr_in6);
 	def.sin6_family = gate.sin6_family = AF_INET6;
 	gate.sin6_addr = new->rtaddr;
 	fibnum = new->ifp->if_fib;
 
 	bzero((caddr_t)&info, sizeof(info));
 	info.rti_flags = RTF_GATEWAY;
 	info.rti_info[RTAX_DST] = (struct sockaddr *)&def;
 	info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&gate;
 	info.rti_info[RTAX_NETMASK] = (struct sockaddr *)&mask;
 
 	NET_EPOCH_ASSERT();
 	error = rib_action(fibnum, RTM_ADD, &info, &rc);
 	if (rc.rc_rt != NULL) {
 		struct nhop_object *nh = nhop_select(rc.rc_nh_new, 0);
 		rt_routemsg(RTM_ADD, rc.rc_rt, nh, fibnum);
 	}
 	if (error == 0)
 		new->installed = 1;
 }
 
 /*
  * Remove the default route for a given router.
  * This is just a subroutine function for defrouter_select_fib(), and
  * should not be called from anywhere else.
  */
 static void
 defrouter_delreq(struct nd_defrouter *dr)
 {
 	struct sockaddr_in6 def, mask, gate;
 	struct rt_addrinfo info;
 	struct rib_cmd_info rc;
 	struct epoch_tracker et;
 	unsigned int fibnum;
 
 	bzero(&def, sizeof(def));
 	bzero(&mask, sizeof(mask));
 	bzero(&gate, sizeof(gate));
 
 	def.sin6_len = mask.sin6_len = gate.sin6_len =
 	    sizeof(struct sockaddr_in6);
 	def.sin6_family = gate.sin6_family = AF_INET6;
 	gate.sin6_addr = dr->rtaddr;
 	fibnum = dr->ifp->if_fib;
 
 	bzero((caddr_t)&info, sizeof(info));
 	info.rti_flags = RTF_GATEWAY;
 	info.rti_info[RTAX_DST] = (struct sockaddr *)&def;
 	info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&gate;
 	info.rti_info[RTAX_NETMASK] = (struct sockaddr *)&mask;
 
 	NET_EPOCH_ENTER(et);
 	rib_action(fibnum, RTM_DELETE, &info, &rc);
 	if (rc.rc_rt != NULL) {
 		struct nhop_object *nh = nhop_select(rc.rc_nh_old, 0);
 		rt_routemsg(RTM_DELETE, rc.rc_rt, nh, fibnum);
 	}
 	NET_EPOCH_EXIT(et);
 
 	dr->installed = 0;
 }
 
 static void
 defrouter_del(struct nd_defrouter *dr)
 {
 	struct nd_defrouter *deldr = NULL;
 	struct nd_prefix *pr;
 	struct nd_pfxrouter *pfxrtr;
 
 	ND6_UNLOCK_ASSERT();
 
 	/*
 	 * Flush all the routing table entries that use the router
 	 * as a next hop.
 	 */
 	if (ND_IFINFO(dr->ifp)->flags & ND6_IFF_ACCEPT_RTADV)
 		rt6_flush(&dr->rtaddr, dr->ifp);
 
 #ifdef EXPERIMENTAL
 	defrtr_ipv6_only_ifp(dr->ifp);
 #endif
 
 	if (dr->installed) {
 		deldr = dr;
 		defrouter_delreq(dr);
 	}
 
 	/*
 	 * Also delete all the pointers to the router in each prefix lists.
 	 */
 	ND6_WLOCK();
 	LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) {
 		if ((pfxrtr = pfxrtr_lookup(pr, dr)) != NULL)
 			pfxrtr_del(pfxrtr);
 	}
 	ND6_WUNLOCK();
 
 	pfxlist_onlink_check();
 
 	/*
 	 * If the router is the primary one, choose a new one.
 	 * Note that defrouter_select_fib() will remove the current
          * gateway from the routing table.
 	 */
 	if (deldr)
 		defrouter_select_fib(deldr->ifp->if_fib);
 
 	/*
 	 * Release the list reference.
 	 */
 	defrouter_rele(dr);
 }
 
 struct nd_defrouter *
 defrouter_lookup_locked(const struct in6_addr *addr, struct ifnet *ifp)
 {
 	struct nd_defrouter *dr;
 
 	ND6_LOCK_ASSERT();
 	TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry)
 		if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr)) {
 			defrouter_ref(dr);
 			return (dr);
 		}
 	return (NULL);
 }
 
 struct nd_defrouter *
 defrouter_lookup(const struct in6_addr *addr, struct ifnet *ifp)
 {
 	struct nd_defrouter *dr;
 
 	ND6_RLOCK();
 	dr = defrouter_lookup_locked(addr, ifp);
 	ND6_RUNLOCK();
 	return (dr);
 }
 
 /*
  * Remove all default routes from default router list.
  */
 void
 defrouter_reset(void)
 {
 	struct nd_defrouter *dr, **dra;
 	int count, i;
 
 	count = i = 0;
 
 	/*
 	 * We can't delete routes with the ND lock held, so make a copy of the
 	 * current default router list and use that when deleting routes.
 	 */
 	ND6_RLOCK();
 	TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry)
 		count++;
 	ND6_RUNLOCK();
 
 	dra = malloc(count * sizeof(*dra), M_TEMP, M_WAITOK | M_ZERO);
 
 	ND6_RLOCK();
 	TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry) {
 		if (i == count)
 			break;
 		defrouter_ref(dr);
 		dra[i++] = dr;
 	}
 	ND6_RUNLOCK();
 
 	for (i = 0; i < count && dra[i] != NULL; i++) {
 		defrouter_delreq(dra[i]);
 		defrouter_rele(dra[i]);
 	}
 	free(dra, M_TEMP);
 
 	/*
 	 * XXX should we also nuke any default routers in the kernel, by
 	 * going through them by rtalloc1()?
 	 */
 }
 
 /*
  * Look up a matching default router list entry and remove it. Returns true if a
  * matching entry was found, false otherwise.
  */
 bool
 defrouter_remove(struct in6_addr *addr, struct ifnet *ifp)
 {
 	struct nd_defrouter *dr;
 
 	ND6_WLOCK();
 	dr = defrouter_lookup_locked(addr, ifp);
 	if (dr == NULL) {
 		ND6_WUNLOCK();
 		return (false);
 	}
 
 	defrouter_unlink(dr, NULL);
 	ND6_WUNLOCK();
 	defrouter_del(dr);
 	defrouter_rele(dr);
 	return (true);
 }
 
 /*
  * for default router selection
  * regards router-preference field as a 2-bit signed integer
  */
 static int
 rtpref(struct nd_defrouter *dr)
 {
 	switch (dr->raflags & ND_RA_FLAG_RTPREF_MASK) {
 	case ND_RA_FLAG_RTPREF_HIGH:
 		return (RTPREF_HIGH);
 	case ND_RA_FLAG_RTPREF_MEDIUM:
 	case ND_RA_FLAG_RTPREF_RSV:
 		return (RTPREF_MEDIUM);
 	case ND_RA_FLAG_RTPREF_LOW:
 		return (RTPREF_LOW);
 	default:
 		/*
 		 * This case should never happen.  If it did, it would mean a
 		 * serious bug of kernel internal.  We thus always bark here.
 		 * Or, can we even panic?
 		 */
 		log(LOG_ERR, "rtpref: impossible RA flag %x\n", dr->raflags);
 		return (RTPREF_INVALID);
 	}
 	/* NOTREACHED */
 }
 
 /*
  * Default Router Selection according to Section 6.3.6 of RFC 2461 and
  * draft-ietf-ipngwg-router-selection:
  * 1) Routers that are reachable or probably reachable should be preferred.
  *    If we have more than one (probably) reachable router, prefer ones
  *    with the highest router preference.
  * 2) When no routers on the list are known to be reachable or
  *    probably reachable, routers SHOULD be selected in a round-robin
  *    fashion, regardless of router preference values.
  * 3) If the Default Router List is empty, assume that all
  *    destinations are on-link.
  *
  * We assume nd_defrouter is sorted by router preference value.
  * Since the code below covers both with and without router preference cases,
  * we do not need to classify the cases by ifdef.
  *
  * At this moment, we do not try to install more than one default router,
  * even when the multipath routing is available, because we're not sure about
  * the benefits for stub hosts comparing to the risk of making the code
  * complicated and the possibility of introducing bugs.
  *
  * We maintain a single list of routers for multiple FIBs, only considering one
  * at a time based on the receiving interface's FIB. If @fibnum is RT_ALL_FIBS,
  * we do the whole thing multiple times.
  */
 void
 defrouter_select_fib(int fibnum)
 {
 	struct epoch_tracker et;
 	struct nd_defrouter *dr, *selected_dr, *installed_dr;
 	struct llentry *ln = NULL;
 
 	if (fibnum == RT_ALL_FIBS) {
 		for (fibnum = 0; fibnum < rt_numfibs; fibnum++) {
 			defrouter_select_fib(fibnum);
 		}
 	}
 
 	ND6_RLOCK();
 	/*
 	 * Let's handle easy case (3) first:
 	 * If default router list is empty, there's nothing to be done.
 	 */
 	if (TAILQ_EMPTY(&V_nd6_defrouter)) {
 		ND6_RUNLOCK();
 		return;
 	}
 
 	/*
 	 * Search for a (probably) reachable router from the list.
 	 * We just pick up the first reachable one (if any), assuming that
 	 * the ordering rule of the list described in defrtrlist_update().
 	 */
 	selected_dr = installed_dr = NULL;
 	TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry) {
 		NET_EPOCH_ENTER(et);
 		if (selected_dr == NULL && dr->ifp->if_fib == fibnum &&
 		    (ln = nd6_lookup(&dr->rtaddr, 0, dr->ifp)) &&
 		    ND6_IS_LLINFO_PROBREACH(ln)) {
 			selected_dr = dr;
 			defrouter_ref(selected_dr);
 		}
 		NET_EPOCH_EXIT(et);
 		if (ln != NULL) {
 			LLE_RUNLOCK(ln);
 			ln = NULL;
 		}
 
 		if (dr->installed && dr->ifp->if_fib == fibnum) {
 			if (installed_dr == NULL) {
 				installed_dr = dr;
 				defrouter_ref(installed_dr);
 			} else {
 				/*
 				 * this should not happen.
 				 * warn for diagnosis.
 				 */
 				log(LOG_ERR, "defrouter_select_fib: more than "
 				             "one router is installed\n");
 			}
 		}
 	}
 	/*
 	 * If none of the default routers was found to be reachable,
 	 * round-robin the list regardless of preference.
 	 * Otherwise, if we have an installed router, check if the selected
 	 * (reachable) router should really be preferred to the installed one.
 	 * We only prefer the new router when the old one is not reachable
 	 * or when the new one has a really higher preference value.
 	 */
 	if (selected_dr == NULL) {
 		if (installed_dr == NULL ||
 		    TAILQ_NEXT(installed_dr, dr_entry) == NULL)
 			dr = TAILQ_FIRST(&V_nd6_defrouter);
 		else
 			dr = TAILQ_NEXT(installed_dr, dr_entry);
 
 		/* Ensure we select a router for this FIB. */
 		TAILQ_FOREACH_FROM(dr, &V_nd6_defrouter, dr_entry) {
 			if (dr->ifp->if_fib == fibnum) {
 				selected_dr = dr;
 				defrouter_ref(selected_dr);
 				break;
 			}
 		}
 	} else if (installed_dr != NULL) {
 		NET_EPOCH_ENTER(et);
 		if ((ln = nd6_lookup(&installed_dr->rtaddr, 0,
 		                     installed_dr->ifp)) &&
 		    ND6_IS_LLINFO_PROBREACH(ln) &&
 		    installed_dr->ifp->if_fib == fibnum &&
 		    rtpref(selected_dr) <= rtpref(installed_dr)) {
 			defrouter_rele(selected_dr);
 			selected_dr = installed_dr;
 		}
 		NET_EPOCH_EXIT(et);
 		if (ln != NULL)
 			LLE_RUNLOCK(ln);
 	}
 	ND6_RUNLOCK();
 
 	NET_EPOCH_ENTER(et);
 	/*
 	 * If we selected a router for this FIB and it's different
 	 * than the installed one, remove the installed router and
 	 * install the selected one in its place.
 	 */
 	if (installed_dr != selected_dr) {
 		if (installed_dr != NULL) {
 			defrouter_delreq(installed_dr);
 			defrouter_rele(installed_dr);
 		}
 		if (selected_dr != NULL)
 			defrouter_addreq(selected_dr);
 	}
 	if (selected_dr != NULL)
 		defrouter_rele(selected_dr);
 	NET_EPOCH_EXIT(et);
 }
 
 static struct nd_defrouter *
 defrtrlist_update(struct nd_defrouter *new)
 {
 	struct nd_defrouter *dr, *n;
 	uint64_t genid;
 	int oldpref;
 	bool writelocked;
 
 	if (new->rtlifetime == 0) {
 		defrouter_remove(&new->rtaddr, new->ifp);
 		return (NULL);
 	}
 
 	ND6_RLOCK();
 	writelocked = false;
 restart:
 	dr = defrouter_lookup_locked(&new->rtaddr, new->ifp);
 	if (dr != NULL) {
 		oldpref = rtpref(dr);
 
 		/* override */
 		dr->raflags = new->raflags; /* XXX flag check */
 		dr->rtlifetime = new->rtlifetime;
 		dr->expire = new->expire;
 
 		/*
 		 * If the preference does not change, there's no need
 		 * to sort the entries. Also make sure the selected
 		 * router is still installed in the kernel.
 		 */
 		if (dr->installed && rtpref(new) == oldpref) {
 			if (writelocked)
 				ND6_WUNLOCK();
 			else
 				ND6_RUNLOCK();
 			return (dr);
 		}
 	}
 
 	/*
 	 * The router needs to be reinserted into the default router
 	 * list, so upgrade to a write lock. If that fails and the list
 	 * has potentially changed while the lock was dropped, we'll
 	 * redo the lookup with the write lock held.
 	 */
 	if (!writelocked) {
 		writelocked = true;
 		if (!ND6_TRY_UPGRADE()) {
 			genid = V_nd6_list_genid;
 			ND6_RUNLOCK();
 			ND6_WLOCK();
 			if (genid != V_nd6_list_genid)
 				goto restart;
 		}
 	}
 
 	if (dr != NULL) {
 		/*
 		 * The preferred router may have changed, so relocate this
 		 * router.
 		 */
 		TAILQ_REMOVE(&V_nd6_defrouter, dr, dr_entry);
 		n = dr;
 	} else {
 		n = malloc(sizeof(*n), M_IP6NDP, M_NOWAIT | M_ZERO);
 		if (n == NULL) {
 			ND6_WUNLOCK();
 			return (NULL);
 		}
 		memcpy(n, new, sizeof(*n));
 		/* Initialize with an extra reference for the caller. */
 		refcount_init(&n->refcnt, 2);
 	}
 
 	/*
 	 * Insert the new router in the Default Router List;
 	 * The Default Router List should be in the descending order
 	 * of router-preferece.  Routers with the same preference are
 	 * sorted in the arriving time order.
 	 */
 
 	/* insert at the end of the group */
 	TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry) {
 		if (rtpref(n) > rtpref(dr))
 			break;
 	}
 	if (dr != NULL)
 		TAILQ_INSERT_BEFORE(dr, n, dr_entry);
 	else
 		TAILQ_INSERT_TAIL(&V_nd6_defrouter, n, dr_entry);
 	V_nd6_list_genid++;
 	ND6_WUNLOCK();
 
 	defrouter_select_fib(new->ifp->if_fib);
 
 	return (n);
 }
 
 static int
 in6_init_prefix_ltimes(struct nd_prefix *ndpr)
 {
 	if (ndpr->ndpr_pltime == ND6_INFINITE_LIFETIME)
 		ndpr->ndpr_preferred = 0;
 	else
 		ndpr->ndpr_preferred = time_uptime + ndpr->ndpr_pltime;
 	if (ndpr->ndpr_vltime == ND6_INFINITE_LIFETIME)
 		ndpr->ndpr_expire = 0;
 	else
 		ndpr->ndpr_expire = time_uptime + ndpr->ndpr_vltime;
 
 	return 0;
 }
 
 static void
 in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6)
 {
 	/* init ia6t_expire */
 	if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME)
 		lt6->ia6t_expire = 0;
 	else {
 		lt6->ia6t_expire = time_uptime;
 		lt6->ia6t_expire += lt6->ia6t_vltime;
 	}
 
 	/* init ia6t_preferred */
 	if (lt6->ia6t_pltime == ND6_INFINITE_LIFETIME)
 		lt6->ia6t_preferred = 0;
 	else {
 		lt6->ia6t_preferred = time_uptime;
 		lt6->ia6t_preferred += lt6->ia6t_pltime;
 	}
 }
 
 static struct in6_ifaddr *
 in6_ifadd(struct nd_prefixctl *pr, int mcast)
 {
 	struct ifnet *ifp = pr->ndpr_ifp;
 	struct ifaddr *ifa;
 	struct in6_aliasreq ifra;
 	struct in6_ifaddr *ia, *ib;
 	int error, plen0;
 	struct in6_addr mask;
 	int prefixlen = pr->ndpr_plen;
 	int updateflags;
 	char ip6buf[INET6_ADDRSTRLEN];
 
 	in6_prefixlen2mask(&mask, prefixlen);
 
 	/*
 	 * find a link-local address (will be interface ID).
 	 * Is it really mandatory? Theoretically, a global or a site-local
 	 * address can be configured without a link-local address, if we
 	 * have a unique interface identifier...
 	 *
 	 * it is not mandatory to have a link-local address, we can generate
 	 * interface identifier on the fly.  we do this because:
 	 * (1) it should be the easiest way to find interface identifier.
 	 * (2) RFC2462 5.4 suggesting the use of the same interface identifier
 	 * for multiple addresses on a single interface, and possible shortcut
 	 * of DAD.  we omitted DAD for this reason in the past.
 	 * (3) a user can prevent autoconfiguration of global address
 	 * by removing link-local address by hand (this is partly because we
 	 * don't have other way to control the use of IPv6 on an interface.
 	 * this has been our design choice - cf. NRL's "ifconfig auto").
 	 * (4) it is easier to manage when an interface has addresses
 	 * with the same interface identifier, than to have multiple addresses
 	 * with different interface identifiers.
 	 */
 	ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0); /* 0 is OK? */
 	if (ifa)
 		ib = (struct in6_ifaddr *)ifa;
 	else
 		return NULL;
 
 	/* prefixlen + ifidlen must be equal to 128 */
 	plen0 = in6_mask2len(&ib->ia_prefixmask.sin6_addr, NULL);
 	if (prefixlen != plen0) {
 		ifa_free(ifa);
 		nd6log((LOG_INFO,
 		    "%s: wrong prefixlen for %s (prefix=%d ifid=%d)\n",
 		    __func__, if_name(ifp), prefixlen, 128 - plen0));
 		return NULL;
 	}
 
 	/* make ifaddr */
 	in6_prepare_ifra(&ifra, &pr->ndpr_prefix.sin6_addr, &mask);
 
 	IN6_MASK_ADDR(&ifra.ifra_addr.sin6_addr, &mask);
 	/* interface ID */
 	ifra.ifra_addr.sin6_addr.s6_addr32[0] |=
 	    (ib->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]);
 	ifra.ifra_addr.sin6_addr.s6_addr32[1] |=
 	    (ib->ia_addr.sin6_addr.s6_addr32[1] & ~mask.s6_addr32[1]);
 	ifra.ifra_addr.sin6_addr.s6_addr32[2] |=
 	    (ib->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]);
 	ifra.ifra_addr.sin6_addr.s6_addr32[3] |=
 	    (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]);
 	ifa_free(ifa);
 
 	/* lifetimes. */
 	ifra.ifra_lifetime.ia6t_vltime = pr->ndpr_vltime;
 	ifra.ifra_lifetime.ia6t_pltime = pr->ndpr_pltime;
 
 	/* XXX: scope zone ID? */
 
 	ifra.ifra_flags |= IN6_IFF_AUTOCONF; /* obey autoconf */
 
 	/*
 	 * Make sure that we do not have this address already.  This should
 	 * usually not happen, but we can still see this case, e.g., if we
 	 * have manually configured the exact address to be configured.
 	 */
 	ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp,
 	    &ifra.ifra_addr.sin6_addr);
 	if (ifa != NULL) {
 		ifa_free(ifa);
 		/* this should be rare enough to make an explicit log */
 		log(LOG_INFO, "in6_ifadd: %s is already configured\n",
 		    ip6_sprintf(ip6buf, &ifra.ifra_addr.sin6_addr));
 		return (NULL);
 	}
 
 	/*
 	 * Allocate ifaddr structure, link into chain, etc.
 	 * If we are going to create a new address upon receiving a multicasted
 	 * RA, we need to impose a random delay before starting DAD.
 	 * [draft-ietf-ipv6-rfc2462bis-02.txt, Section 5.4.2]
 	 */
 	updateflags = 0;
 	if (mcast)
 		updateflags |= IN6_IFAUPDATE_DADDELAY;
 	if ((error = in6_update_ifa(ifp, &ifra, NULL, updateflags)) != 0) {
 		nd6log((LOG_ERR,
 		    "%s: failed to make ifaddr %s on %s (errno=%d)\n", __func__,
 		    ip6_sprintf(ip6buf, &ifra.ifra_addr.sin6_addr),
 		    if_name(ifp), error));
 		return (NULL);	/* ifaddr must not have been allocated. */
 	}
 
 	ia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr);
 	/*
 	 * XXXRW: Assumption of non-NULLness here might not be true with
 	 * fine-grained locking -- should we validate it?  Or just return
 	 * earlier ifa rather than looking it up again?
 	 */
 	return (ia);		/* this is always non-NULL  and referenced. */
 }
 
 static struct nd_prefix *
 nd6_prefix_lookup_locked(struct nd_prefixctl *key)
 {
 	struct nd_prefix *search;
 
 	ND6_LOCK_ASSERT();
 
 	LIST_FOREACH(search, &V_nd_prefix, ndpr_entry) {
 		if (key->ndpr_ifp == search->ndpr_ifp &&
 		    key->ndpr_plen == search->ndpr_plen &&
 		    in6_are_prefix_equal(&key->ndpr_prefix.sin6_addr,
 		    &search->ndpr_prefix.sin6_addr, key->ndpr_plen)) {
 			nd6_prefix_ref(search);
 			break;
 		}
 	}
 	return (search);
 }
 
 struct nd_prefix *
 nd6_prefix_lookup(struct nd_prefixctl *key)
 {
 	struct nd_prefix *search;
 
 	ND6_RLOCK();
 	search = nd6_prefix_lookup_locked(key);
 	ND6_RUNLOCK();
 	return (search);
 }
 
 void
 nd6_prefix_ref(struct nd_prefix *pr)
 {
 
 	refcount_acquire(&pr->ndpr_refcnt);
 }
 
 void
 nd6_prefix_rele(struct nd_prefix *pr)
 {
 
 	if (refcount_release(&pr->ndpr_refcnt)) {
 		KASSERT(LIST_EMPTY(&pr->ndpr_advrtrs),
 		    ("prefix %p has advertising routers", pr));
 		free(pr, M_IP6NDP);
 	}
 }
 
 int
 nd6_prelist_add(struct nd_prefixctl *pr, struct nd_defrouter *dr,
     struct nd_prefix **newp)
 {
 	struct nd_prefix *new;
 	char ip6buf[INET6_ADDRSTRLEN];
 	int error;
 
 	new = malloc(sizeof(*new), M_IP6NDP, M_NOWAIT | M_ZERO);
 	if (new == NULL)
 		return (ENOMEM);
 	refcount_init(&new->ndpr_refcnt, newp != NULL ? 2 : 1);
 	new->ndpr_ifp = pr->ndpr_ifp;
 	new->ndpr_prefix = pr->ndpr_prefix;
 	new->ndpr_plen = pr->ndpr_plen;
 	new->ndpr_vltime = pr->ndpr_vltime;
 	new->ndpr_pltime = pr->ndpr_pltime;
 	new->ndpr_flags = pr->ndpr_flags;
 	if ((error = in6_init_prefix_ltimes(new)) != 0) {
 		free(new, M_IP6NDP);
 		return (error);
 	}
 	new->ndpr_lastupdate = time_uptime;
 
 	/* initialization */
 	LIST_INIT(&new->ndpr_advrtrs);
 	in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen);
 	/* make prefix in the canonical form */
 	IN6_MASK_ADDR(&new->ndpr_prefix.sin6_addr, &new->ndpr_mask);
 
 	ND6_WLOCK();
 	LIST_INSERT_HEAD(&V_nd_prefix, new, ndpr_entry);
 	V_nd6_list_genid++;
 	ND6_WUNLOCK();
 
 	/* ND_OPT_PI_FLAG_ONLINK processing */
 	if (new->ndpr_raf_onlink) {
 		struct epoch_tracker et;
 
 		ND6_ONLINK_LOCK();
 		NET_EPOCH_ENTER(et);
 		if ((error = nd6_prefix_onlink(new)) != 0) {
 			nd6log((LOG_ERR, "%s: failed to make the prefix %s/%d "
 			    "on-link on %s (errno=%d)\n", __func__,
 			    ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr),
 			    pr->ndpr_plen, if_name(pr->ndpr_ifp), error));
 			/* proceed anyway. XXX: is it correct? */
 		}
 		NET_EPOCH_EXIT(et);
 		ND6_ONLINK_UNLOCK();
 	}
 
 	if (dr != NULL)
 		pfxrtr_add(new, dr);
 	if (newp != NULL)
 		*newp = new;
 	return (0);
 }
 
 /*
  * Remove a prefix from the prefix list and optionally stash it in a
  * caller-provided list.
  *
  * The ND6 lock must be held.
  */
 void
 nd6_prefix_unlink(struct nd_prefix *pr, struct nd_prhead *list)
 {
 
 	ND6_WLOCK_ASSERT();
 
 	LIST_REMOVE(pr, ndpr_entry);
 	V_nd6_list_genid++;
 	if (list != NULL)
 		LIST_INSERT_HEAD(list, pr, ndpr_entry);
 }
 
 /*
  * Free an unlinked prefix, first marking it off-link if necessary.
  */
 void
 nd6_prefix_del(struct nd_prefix *pr)
 {
 	struct nd_pfxrouter *pfr, *next;
 	int e;
 	char ip6buf[INET6_ADDRSTRLEN];
 
 	KASSERT(pr->ndpr_addrcnt == 0,
 	    ("prefix %p has referencing addresses", pr));
 	ND6_UNLOCK_ASSERT();
 
 	/*
 	 * Though these flags are now meaningless, we'd rather keep the value
 	 * of pr->ndpr_raf_onlink and pr->ndpr_raf_auto not to confuse users
 	 * when executing "ndp -p".
 	 */
 	if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) {
 		ND6_ONLINK_LOCK();
 		if ((e = nd6_prefix_offlink(pr)) != 0) {
 			nd6log((LOG_ERR,
 			    "%s: failed to make the prefix %s/%d offlink on %s "
 			    "(errno=%d)\n", __func__,
 			    ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr),
 			    pr->ndpr_plen, if_name(pr->ndpr_ifp), e));
 			/* what should we do? */
 		}
 		ND6_ONLINK_UNLOCK();
 	}
 
 	/* Release references to routers that have advertised this prefix. */
 	ND6_WLOCK();
 	LIST_FOREACH_SAFE(pfr, &pr->ndpr_advrtrs, pfr_entry, next)
 		pfxrtr_del(pfr);
 	ND6_WUNLOCK();
 
 	nd6_prefix_rele(pr);
 
 	pfxlist_onlink_check();
 }
 
 static int
 prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr,
     struct mbuf *m, int mcast)
 {
 	struct in6_ifaddr *ia6 = NULL, *ia6_match = NULL;
 	struct ifaddr *ifa;
 	struct ifnet *ifp = new->ndpr_ifp;
 	struct nd_prefix *pr;
 	int error = 0;
 	int auth;
 	struct in6_addrlifetime lt6_tmp;
 	char ip6buf[INET6_ADDRSTRLEN];
 
 	NET_EPOCH_ASSERT();
 
 	auth = 0;
 	if (m) {
 		/*
 		 * Authenticity for NA consists authentication for
 		 * both IP header and IP datagrams, doesn't it ?
 		 */
 #if defined(M_AUTHIPHDR) && defined(M_AUTHIPDGM)
 		auth = ((m->m_flags & M_AUTHIPHDR) &&
 		    (m->m_flags & M_AUTHIPDGM));
 #endif
 	}
 
 	if ((pr = nd6_prefix_lookup(new)) != NULL) {
 		/*
 		 * nd6_prefix_lookup() ensures that pr and new have the same
 		 * prefix on a same interface.
 		 */
 
 		/*
 		 * Update prefix information.  Note that the on-link (L) bit
 		 * and the autonomous (A) bit should NOT be changed from 1
 		 * to 0.
 		 */
 		if (new->ndpr_raf_onlink == 1)
 			pr->ndpr_raf_onlink = 1;
 		if (new->ndpr_raf_auto == 1)
 			pr->ndpr_raf_auto = 1;
 		if (new->ndpr_raf_onlink) {
 			pr->ndpr_vltime = new->ndpr_vltime;
 			pr->ndpr_pltime = new->ndpr_pltime;
 			(void)in6_init_prefix_ltimes(pr); /* XXX error case? */
 			pr->ndpr_lastupdate = time_uptime;
 		}
 
 		if (new->ndpr_raf_onlink &&
 		    (pr->ndpr_stateflags & NDPRF_ONLINK) == 0) {
 			ND6_ONLINK_LOCK();
 			if ((error = nd6_prefix_onlink(pr)) != 0) {
 				nd6log((LOG_ERR,
 				    "%s: failed to make the prefix %s/%d "
 				    "on-link on %s (errno=%d)\n", __func__,
 				    ip6_sprintf(ip6buf,
 				        &pr->ndpr_prefix.sin6_addr),
 				    pr->ndpr_plen, if_name(pr->ndpr_ifp),
 				    error));
 				/* proceed anyway. XXX: is it correct? */
 			}
 			ND6_ONLINK_UNLOCK();
 		}
 
 		if (dr != NULL)
 			pfxrtr_add(pr, dr);
 	} else {
 		if (new->ndpr_vltime == 0)
 			goto end;
 		if (new->ndpr_raf_onlink == 0 && new->ndpr_raf_auto == 0)
 			goto end;
 
 		error = nd6_prelist_add(new, dr, &pr);
 		if (error != 0) {
 			nd6log((LOG_NOTICE, "%s: nd6_prelist_add() failed for "
 			    "the prefix %s/%d on %s (errno=%d)\n", __func__,
 			    ip6_sprintf(ip6buf, &new->ndpr_prefix.sin6_addr),
 			    new->ndpr_plen, if_name(new->ndpr_ifp), error));
 			goto end; /* we should just give up in this case. */
 		}
 
 		/*
 		 * XXX: from the ND point of view, we can ignore a prefix
 		 * with the on-link bit being zero.  However, we need a
 		 * prefix structure for references from autoconfigured
 		 * addresses.  Thus, we explicitly make sure that the prefix
 		 * itself expires now.
 		 */
 		if (pr->ndpr_raf_onlink == 0) {
 			pr->ndpr_vltime = 0;
 			pr->ndpr_pltime = 0;
 			in6_init_prefix_ltimes(pr);
 		}
 	}
 
 	/*
 	 * Address autoconfiguration based on Section 5.5.3 of RFC 2462.
 	 * Note that pr must be non NULL at this point.
 	 */
 
 	/* 5.5.3 (a). Ignore the prefix without the A bit set. */
 	if (!new->ndpr_raf_auto)
 		goto end;
 
 	/*
 	 * 5.5.3 (b). the link-local prefix should have been ignored in
 	 * nd6_ra_input.
 	 */
 
 	/* 5.5.3 (c). Consistency check on lifetimes: pltime <= vltime. */
 	if (new->ndpr_pltime > new->ndpr_vltime) {
 		error = EINVAL;	/* XXX: won't be used */
 		goto end;
 	}
 
 	/*
 	 * 5.5.3 (d).  If the prefix advertised is not equal to the prefix of
 	 * an address configured by stateless autoconfiguration already in the
 	 * list of addresses associated with the interface, and the Valid
 	 * Lifetime is not 0, form an address.  We first check if we have
 	 * a matching prefix.
 	 * Note: we apply a clarification in rfc2462bis-02 here.  We only
 	 * consider autoconfigured addresses while RFC2462 simply said
 	 * "address".
 	 */
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 		struct in6_ifaddr *ifa6;
 		u_int32_t remaininglifetime;
 
 		if (ifa->ifa_addr->sa_family != AF_INET6)
 			continue;
 
 		ifa6 = (struct in6_ifaddr *)ifa;
 
 		/*
 		 * We only consider autoconfigured addresses as per rfc2462bis.
 		 */
 		if (!(ifa6->ia6_flags & IN6_IFF_AUTOCONF))
 			continue;
 
 		/*
 		 * Spec is not clear here, but I believe we should concentrate
 		 * on unicast (i.e. not anycast) addresses.
 		 * XXX: other ia6_flags? detached or duplicated?
 		 */
 		if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0)
 			continue;
 
 		/*
 		 * Ignore the address if it is not associated with a prefix
 		 * or is associated with a prefix that is different from this
 		 * one.  (pr is never NULL here)
 		 */
 		if (ifa6->ia6_ndpr != pr)
 			continue;
 
 		if (ia6_match == NULL) /* remember the first one */
 			ia6_match = ifa6;
 
 		/*
 		 * An already autoconfigured address matched.  Now that we
 		 * are sure there is at least one matched address, we can
 		 * proceed to 5.5.3. (e): update the lifetimes according to the
 		 * "two hours" rule and the privacy extension.
 		 * We apply some clarifications in rfc2462bis:
 		 * - use remaininglifetime instead of storedlifetime as a
 		 *   variable name
 		 * - remove the dead code in the "two-hour" rule
 		 */
 #define TWOHOUR		(120*60)
 		lt6_tmp = ifa6->ia6_lifetime;
 
 		if (lt6_tmp.ia6t_vltime == ND6_INFINITE_LIFETIME)
 			remaininglifetime = ND6_INFINITE_LIFETIME;
 		else if (time_uptime - ifa6->ia6_updatetime >
 			 lt6_tmp.ia6t_vltime) {
 			/*
 			 * The case of "invalid" address.  We should usually
 			 * not see this case.
 			 */
 			remaininglifetime = 0;
 		} else
 			remaininglifetime = lt6_tmp.ia6t_vltime -
 			    (time_uptime - ifa6->ia6_updatetime);
 
 		/* when not updating, keep the current stored lifetime. */
 		lt6_tmp.ia6t_vltime = remaininglifetime;
 
 		if (TWOHOUR < new->ndpr_vltime ||
 		    remaininglifetime < new->ndpr_vltime) {
 			lt6_tmp.ia6t_vltime = new->ndpr_vltime;
 		} else if (remaininglifetime <= TWOHOUR) {
 			if (auth) {
 				lt6_tmp.ia6t_vltime = new->ndpr_vltime;
 			}
 		} else {
 			/*
 			 * new->ndpr_vltime <= TWOHOUR &&
 			 * TWOHOUR < remaininglifetime
 			 */
 			lt6_tmp.ia6t_vltime = TWOHOUR;
 		}
 
 		/* The 2 hour rule is not imposed for preferred lifetime. */
 		lt6_tmp.ia6t_pltime = new->ndpr_pltime;
 
 		in6_init_address_ltimes(pr, &lt6_tmp);
 
 		/*
 		 * We need to treat lifetimes for temporary addresses
 		 * differently, according to
 		 * draft-ietf-ipv6-privacy-addrs-v2-01.txt 3.3 (1);
 		 * we only update the lifetimes when they are in the maximum
 		 * intervals.
 		 */
 		if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0) {
 			u_int32_t maxvltime, maxpltime;
 
 			if (V_ip6_temp_valid_lifetime >
 			    (u_int32_t)((time_uptime - ifa6->ia6_createtime) +
 			    V_ip6_desync_factor)) {
 				maxvltime = V_ip6_temp_valid_lifetime -
 				    (time_uptime - ifa6->ia6_createtime) -
 				    V_ip6_desync_factor;
 			} else
 				maxvltime = 0;
 			if (V_ip6_temp_preferred_lifetime >
 			    (u_int32_t)((time_uptime - ifa6->ia6_createtime) +
 			    V_ip6_desync_factor)) {
 				maxpltime = V_ip6_temp_preferred_lifetime -
 				    (time_uptime - ifa6->ia6_createtime) -
 				    V_ip6_desync_factor;
 			} else
 				maxpltime = 0;
 
 			if (lt6_tmp.ia6t_vltime == ND6_INFINITE_LIFETIME ||
 			    lt6_tmp.ia6t_vltime > maxvltime) {
 				lt6_tmp.ia6t_vltime = maxvltime;
 			}
 			if (lt6_tmp.ia6t_pltime == ND6_INFINITE_LIFETIME ||
 			    lt6_tmp.ia6t_pltime > maxpltime) {
 				lt6_tmp.ia6t_pltime = maxpltime;
 			}
 		}
 		ifa6->ia6_lifetime = lt6_tmp;
 		ifa6->ia6_updatetime = time_uptime;
 	}
 	if (ia6_match == NULL && new->ndpr_vltime) {
 		int ifidlen;
 
 		/*
 		 * 5.5.3 (d) (continued)
 		 * No address matched and the valid lifetime is non-zero.
 		 * Create a new address.
 		 */
 
 		/*
 		 * Prefix Length check:
 		 * If the sum of the prefix length and interface identifier
 		 * length does not equal 128 bits, the Prefix Information
 		 * option MUST be ignored.  The length of the interface
 		 * identifier is defined in a separate link-type specific
 		 * document.
 		 */
 		ifidlen = in6_if2idlen(ifp);
 		if (ifidlen < 0) {
 			/* this should not happen, so we always log it. */
 			log(LOG_ERR, "prelist_update: IFID undefined (%s)\n",
 			    if_name(ifp));
 			goto end;
 		}
 		if (ifidlen + pr->ndpr_plen != 128) {
 			nd6log((LOG_INFO,
 			    "%s: invalid prefixlen %d for %s, ignored\n",
 			    __func__, pr->ndpr_plen, if_name(ifp)));
 			goto end;
 		}
 
 		if ((ia6 = in6_ifadd(new, mcast)) != NULL) {
 			/*
 			 * note that we should use pr (not new) for reference.
 			 */
 			pr->ndpr_addrcnt++;
 			ia6->ia6_ndpr = pr;
 
 			/*
 			 * RFC 3041 3.3 (2).
 			 * When a new public address is created as described
 			 * in RFC2462, also create a new temporary address.
 			 *
 			 * RFC 3041 3.5.
 			 * When an interface connects to a new link, a new
 			 * randomized interface identifier should be generated
 			 * immediately together with a new set of temporary
 			 * addresses.  Thus, we specifiy 1 as the 2nd arg of
 			 * in6_tmpifadd().
 			 */
 			if (V_ip6_use_tempaddr) {
 				int e;
 				if ((e = in6_tmpifadd(ia6, 1, 1)) != 0) {
 					nd6log((LOG_NOTICE, "%s: failed to "
 					    "create a temporary address "
 					    "(errno=%d)\n", __func__, e));
 				}
 			}
 			ifa_free(&ia6->ia_ifa);
 
 			/*
 			 * A newly added address might affect the status
 			 * of other addresses, so we check and update it.
 			 * XXX: what if address duplication happens?
 			 */
 			pfxlist_onlink_check();
 		} else {
 			/* just set an error. do not bark here. */
 			error = EADDRNOTAVAIL; /* XXX: might be unused. */
 		}
 	}
 
 end:
 	if (pr != NULL)
 		nd6_prefix_rele(pr);
 	return (error);
 }
 
 /*
  * A supplement function used in the on-link detection below;
  * detect if a given prefix has a (probably) reachable advertising router.
  * XXX: lengthy function name...
  */
 static struct nd_pfxrouter *
 find_pfxlist_reachable_router(struct nd_prefix *pr)
 {
 	struct epoch_tracker et;
 	struct nd_pfxrouter *pfxrtr;
 	struct llentry *ln;
 	int canreach;
 
 	ND6_LOCK_ASSERT();
 
 	NET_EPOCH_ENTER(et);
 	LIST_FOREACH(pfxrtr, &pr->ndpr_advrtrs, pfr_entry) {
 		ln = nd6_lookup(&pfxrtr->router->rtaddr, 0, pfxrtr->router->ifp);
 		if (ln == NULL)
 			continue;
 		canreach = ND6_IS_LLINFO_PROBREACH(ln);
 		LLE_RUNLOCK(ln);
 		if (canreach)
 			break;
 	}
 	NET_EPOCH_EXIT(et);
 	return (pfxrtr);
 }
 
 /*
  * Check if each prefix in the prefix list has at least one available router
  * that advertised the prefix (a router is "available" if its neighbor cache
  * entry is reachable or probably reachable).
  * If the check fails, the prefix may be off-link, because, for example,
  * we have moved from the network but the lifetime of the prefix has not
  * expired yet.  So we should not use the prefix if there is another prefix
  * that has an available router.
  * But, if there is no prefix that has an available router, we still regard
  * all the prefixes as on-link.  This is because we can't tell if all the
  * routers are simply dead or if we really moved from the network and there
  * is no router around us.
  */
 void
 pfxlist_onlink_check(void)
 {
 	struct nd_prefix *pr;
 	struct in6_ifaddr *ifa;
 	struct nd_defrouter *dr;
 	struct nd_pfxrouter *pfxrtr = NULL;
 	struct rm_priotracker in6_ifa_tracker;
 	uint64_t genid;
 	uint32_t flags;
 
 	ND6_ONLINK_LOCK();
 	ND6_RLOCK();
 
 	/*
 	 * Check if there is a prefix that has a reachable advertising
 	 * router.
 	 */
 	LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) {
 		if (pr->ndpr_raf_onlink && find_pfxlist_reachable_router(pr))
 			break;
 	}
 
 	/*
 	 * If we have no such prefix, check whether we still have a router
 	 * that does not advertise any prefixes.
 	 */
 	if (pr == NULL) {
 		TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry) {
 			struct nd_prefix *pr0;
 
 			LIST_FOREACH(pr0, &V_nd_prefix, ndpr_entry) {
 				if ((pfxrtr = pfxrtr_lookup(pr0, dr)) != NULL)
 					break;
 			}
 			if (pfxrtr != NULL)
 				break;
 		}
 	}
 	if (pr != NULL || (!TAILQ_EMPTY(&V_nd6_defrouter) && pfxrtr == NULL)) {
 		/*
 		 * There is at least one prefix that has a reachable router,
 		 * or at least a router which probably does not advertise
 		 * any prefixes.  The latter would be the case when we move
 		 * to a new link where we have a router that does not provide
 		 * prefixes and we configure an address by hand.
 		 * Detach prefixes which have no reachable advertising
 		 * router, and attach other prefixes.
 		 */
 		LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) {
 			/* XXX: a link-local prefix should never be detached */
 			if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr) ||
 			    pr->ndpr_raf_onlink == 0 ||
 			    pr->ndpr_raf_auto == 0)
 				continue;
 
 			if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 &&
 			    find_pfxlist_reachable_router(pr) == NULL)
 				pr->ndpr_stateflags |= NDPRF_DETACHED;
 			else if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 &&
 			    find_pfxlist_reachable_router(pr) != NULL)
 				pr->ndpr_stateflags &= ~NDPRF_DETACHED;
 		}
 	} else {
 		/* there is no prefix that has a reachable router */
 		LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) {
 			if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr) ||
 			    pr->ndpr_raf_onlink == 0 ||
 			    pr->ndpr_raf_auto == 0)
 				continue;
 			pr->ndpr_stateflags &= ~NDPRF_DETACHED;
 		}
 	}
 
 	/*
 	 * Remove each interface route associated with a (just) detached
 	 * prefix, and reinstall the interface route for a (just) attached
 	 * prefix.  Note that all attempt of reinstallation does not
 	 * necessarily success, when a same prefix is shared among multiple
 	 * interfaces.  Such cases will be handled in nd6_prefix_onlink,
 	 * so we don't have to care about them.
 	 */
 restart:
 	LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) {
 		char ip6buf[INET6_ADDRSTRLEN];
 		int e;
 
 		if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr) ||
 		    pr->ndpr_raf_onlink == 0 ||
 		    pr->ndpr_raf_auto == 0)
 			continue;
 
 		flags = pr->ndpr_stateflags & (NDPRF_DETACHED | NDPRF_ONLINK);
 		if (flags == 0 || flags == (NDPRF_DETACHED | NDPRF_ONLINK)) {
 			genid = V_nd6_list_genid;
 			ND6_RUNLOCK();
 			if ((flags & NDPRF_ONLINK) != 0 &&
 			    (e = nd6_prefix_offlink(pr)) != 0) {
 				nd6log((LOG_ERR,
 				    "%s: failed to make %s/%d offlink "
 				    "(errno=%d)\n", __func__, 
 				    ip6_sprintf(ip6buf,
 					    &pr->ndpr_prefix.sin6_addr),
 					    pr->ndpr_plen, e));
 			} else if ((flags & NDPRF_ONLINK) == 0 &&
 			    (e = nd6_prefix_onlink(pr)) != 0) {
 				nd6log((LOG_ERR,
 				    "%s: failed to make %s/%d onlink "
 				    "(errno=%d)\n", __func__,
 				    ip6_sprintf(ip6buf,
 					    &pr->ndpr_prefix.sin6_addr),
 					    pr->ndpr_plen, e));
 			}
 			ND6_RLOCK();
 			if (genid != V_nd6_list_genid)
 				goto restart;
 		}
 	}
 
 	/*
 	 * Changes on the prefix status might affect address status as well.
 	 * Make sure that all addresses derived from an attached prefix are
 	 * attached, and that all addresses derived from a detached prefix are
 	 * detached.  Note, however, that a manually configured address should
 	 * always be attached.
 	 * The precise detection logic is same as the one for prefixes.
 	 */
 	IN6_IFADDR_RLOCK(&in6_ifa_tracker);
 	CK_STAILQ_FOREACH(ifa, &V_in6_ifaddrhead, ia_link) {
 		if (!(ifa->ia6_flags & IN6_IFF_AUTOCONF))
 			continue;
 
 		if (ifa->ia6_ndpr == NULL) {
 			/*
 			 * This can happen when we first configure the address
 			 * (i.e. the address exists, but the prefix does not).
 			 * XXX: complicated relationships...
 			 */
 			continue;
 		}
 
 		if (find_pfxlist_reachable_router(ifa->ia6_ndpr))
 			break;
 	}
 	if (ifa) {
 		CK_STAILQ_FOREACH(ifa, &V_in6_ifaddrhead, ia_link) {
 			if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0)
 				continue;
 
 			if (ifa->ia6_ndpr == NULL) /* XXX: see above. */
 				continue;
 
 			if (find_pfxlist_reachable_router(ifa->ia6_ndpr)) {
 				if (ifa->ia6_flags & IN6_IFF_DETACHED) {
 					ifa->ia6_flags &= ~IN6_IFF_DETACHED;
 					ifa->ia6_flags |= IN6_IFF_TENTATIVE;
 					nd6_dad_start((struct ifaddr *)ifa, 0);
 				}
 			} else {
 				ifa->ia6_flags |= IN6_IFF_DETACHED;
 			}
 		}
 	} else {
 		CK_STAILQ_FOREACH(ifa, &V_in6_ifaddrhead, ia_link) {
 			if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0)
 				continue;
 
 			if (ifa->ia6_flags & IN6_IFF_DETACHED) {
 				ifa->ia6_flags &= ~IN6_IFF_DETACHED;
 				ifa->ia6_flags |= IN6_IFF_TENTATIVE;
 				/* Do we need a delay in this case? */
 				nd6_dad_start((struct ifaddr *)ifa, 0);
 			}
 		}
 	}
 	IN6_IFADDR_RUNLOCK(&in6_ifa_tracker);
 	ND6_RUNLOCK();
 	ND6_ONLINK_UNLOCK();
 }
 
+/*
+ * Add or remove interface route specified by @dst, @netmask and @ifp.
+ * ifa can be NULL.
+ * Returns 0 on success
+ */
+static int
+nd6_prefix_rtrequest(uint32_t fibnum, int cmd, struct sockaddr_in6 *dst,
+    struct sockaddr_in6 *netmask, struct ifnet *ifp, struct ifaddr *ifa)
+{
+	struct epoch_tracker et;
+	int error;
+
+	/* Prepare gateway */
+	struct sockaddr_dl_short sdl = {
+		.sdl_family = AF_LINK,
+		.sdl_len = sizeof(struct sockaddr_dl_short),
+		.sdl_type = ifp->if_type,
+		.sdl_index = ifp->if_index,
+	};
+
+	struct rt_addrinfo info = {
+		.rti_ifa = ifa,
+		.rti_flags = RTF_PINNED | ((netmask != NULL) ? 0 : RTF_HOST),
+		.rti_info = {
+			[RTAX_DST] = (struct sockaddr *)dst,
+			[RTAX_NETMASK] = (struct sockaddr *)netmask,
+			[RTAX_GATEWAY] = (struct sockaddr *)&sdl,
+		},
+	};
+	/* Don't set additional per-gw filters on removal */
+
+	NET_EPOCH_ENTER(et);
+	error = rib_handle_ifaddr_info(fibnum, cmd, &info);
+	NET_EPOCH_EXIT(et);
+	return (error);
+}
+
 static int
 nd6_prefix_onlink_rtrequest(struct nd_prefix *pr, struct ifaddr *ifa)
 {
-	struct sockaddr_dl_short sdl;
-	struct sockaddr_in6 mask6;
-	u_long rtflags;
-	int error, a_failure, fibnum, maxfib;
-
-	bzero(&mask6, sizeof(mask6));
-	mask6.sin6_len = sizeof(mask6);
-	mask6.sin6_addr = pr->ndpr_mask;
-	rtflags = (ifa->ifa_flags & ~IFA_RTSELF) | RTF_UP;
-
-	bzero(&sdl, sizeof(struct sockaddr_dl_short));
-	sdl.sdl_len = sizeof(struct sockaddr_dl_short);
-	sdl.sdl_family = AF_LINK;
-	sdl.sdl_type = ifa->ifa_ifp->if_type;
-	sdl.sdl_index = ifa->ifa_ifp->if_index;
-
-	if(V_rt_add_addr_allfibs) {
-		fibnum = 0;
-		maxfib = rt_numfibs;
-	} else {
-		fibnum = ifa->ifa_ifp->if_fib;
-		maxfib = fibnum + 1;
-	}
-	a_failure = 0;
-	for (; fibnum < maxfib; fibnum++) {
-		struct rt_addrinfo info;
-		struct rib_cmd_info rc;
-
-		bzero((caddr_t)&info, sizeof(info));
-		info.rti_flags = rtflags;
-		info.rti_info[RTAX_DST] = (struct sockaddr *)&pr->ndpr_prefix;
-		info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&sdl;
-		info.rti_info[RTAX_NETMASK] = (struct sockaddr *)&mask6;
-
-		NET_EPOCH_ASSERT();
-		error = rib_action(fibnum, RTM_ADD, &info, &rc);
-		if (error != 0) {
-			char ip6buf[INET6_ADDRSTRLEN];
-			char ip6bufg[INET6_ADDRSTRLEN];
-			char ip6bufm[INET6_ADDRSTRLEN];
-			struct sockaddr_in6 *sin6;
-
-			sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
-			nd6log((LOG_ERR, "%s: failed to add "
-			    "route for a prefix (%s/%d) on %s, gw=%s, mask=%s, "
-			    "flags=%lx errno = %d\n", __func__,
-			    ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr),
-			    pr->ndpr_plen, if_name(pr->ndpr_ifp),
-			    ip6_sprintf(ip6bufg, &sin6->sin6_addr),
-			    ip6_sprintf(ip6bufm, &mask6.sin6_addr),
-			    rtflags, error));
+	int error;
 
-			/* Save last error to return, see rtinit(). */
-			a_failure = error;
-			continue;
-		}
+	struct sockaddr_in6 mask6 = {
+		.sin6_family = AF_INET6,
+		.sin6_len = sizeof(struct sockaddr_in6),
+		.sin6_addr = pr->ndpr_mask,
+	};
+	struct sockaddr_in6 *pmask6 = (pr->ndpr_plen != 128) ? &mask6 : NULL;
 
+	error = nd6_prefix_rtrequest(pr->ndpr_ifp->if_fib, RTM_ADD,
+	    &pr->ndpr_prefix, pmask6, pr->ndpr_ifp, ifa);
+	if (error == 0)
 		pr->ndpr_stateflags |= NDPRF_ONLINK;
-		struct nhop_object *nh = nhop_select(rc.rc_nh_new, 0);
-		rt_routemsg(RTM_ADD, rc.rc_rt, nh, fibnum);
-	}
 
-	/* Return the last error we got. */
-	return (a_failure);
+	return (error);
 }
 
 static int
 nd6_prefix_onlink(struct nd_prefix *pr)
 {
 	struct epoch_tracker et;
 	struct ifaddr *ifa;
 	struct ifnet *ifp = pr->ndpr_ifp;
 	struct nd_prefix *opr;
 	char ip6buf[INET6_ADDRSTRLEN];
 	int error;
 
 	ND6_ONLINK_LOCK_ASSERT();
 	ND6_UNLOCK_ASSERT();
 
 	if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0)
 		return (EEXIST);
 
 	/*
 	 * Add the interface route associated with the prefix.  Before
 	 * installing the route, check if there's the same prefix on another
 	 * interface, and the prefix has already installed the interface route.
 	 * Although such a configuration is expected to be rare, we explicitly
 	 * allow it.
 	 */
 	ND6_RLOCK();
 	LIST_FOREACH(opr, &V_nd_prefix, ndpr_entry) {
 		if (opr == pr)
 			continue;
 
 		if ((opr->ndpr_stateflags & NDPRF_ONLINK) == 0)
 			continue;
 
 		if (!V_rt_add_addr_allfibs &&
 		    opr->ndpr_ifp->if_fib != pr->ndpr_ifp->if_fib)
 			continue;
 
 		if (opr->ndpr_plen == pr->ndpr_plen &&
 		    in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr,
 		    &opr->ndpr_prefix.sin6_addr, pr->ndpr_plen)) {
 			ND6_RUNLOCK();
 			return (0);
 		}
 	}
 	ND6_RUNLOCK();
 
 	/*
 	 * We prefer link-local addresses as the associated interface address.
 	 */
 	/* search for a link-local addr */
 	NET_EPOCH_ENTER(et);
 	ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp,
 	    IN6_IFF_NOTREADY | IN6_IFF_ANYCAST);
 	if (ifa == NULL) {
 		/* XXX: freebsd does not have ifa_ifwithaf */
 		CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 			if (ifa->ifa_addr->sa_family == AF_INET6) {
 				ifa_ref(ifa);
 				break;
 			}
 		}
 		/* should we care about ia6_flags? */
 	}
 	if (ifa == NULL) {
 		/*
 		 * This can still happen, when, for example, we receive an RA
 		 * containing a prefix with the L bit set and the A bit clear,
 		 * after removing all IPv6 addresses on the receiving
 		 * interface.  This should, of course, be rare though.
 		 */
 		nd6log((LOG_NOTICE,
 		    "%s: failed to find any ifaddr to add route for a "
 		    "prefix(%s/%d) on %s\n", __func__,
 		    ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr),
 		    pr->ndpr_plen, if_name(ifp)));
 		error = 0;
 	} else {
 		error = nd6_prefix_onlink_rtrequest(pr, ifa);
 		ifa_free(ifa);
 	}
 	NET_EPOCH_EXIT(et);
 
 	return (error);
 }
 
 int
 nd6_prefix_offlink(struct nd_prefix *pr)
 {
 	int error = 0;
 	struct ifnet *ifp = pr->ndpr_ifp;
 	struct nd_prefix *opr;
-	struct sockaddr_in6 sa6, mask6;
+	struct sockaddr_in6 sa6;
 	char ip6buf[INET6_ADDRSTRLEN];
 	uint64_t genid;
-	int fibnum, maxfib, a_failure;
-	struct epoch_tracker et;
+	int a_failure;
 
 	ND6_ONLINK_LOCK_ASSERT();
 	ND6_UNLOCK_ASSERT();
 
 	if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0)
 		return (EEXIST);
 
-	bzero(&sa6, sizeof(sa6));
-	sa6.sin6_family = AF_INET6;
-	sa6.sin6_len = sizeof(sa6);
-	bcopy(&pr->ndpr_prefix.sin6_addr, &sa6.sin6_addr,
-	    sizeof(struct in6_addr));
-	bzero(&mask6, sizeof(mask6));
-	mask6.sin6_family = AF_INET6;
-	mask6.sin6_len = sizeof(sa6);
-	bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr));
-
-	if (V_rt_add_addr_allfibs) {
-		fibnum = 0;
-		maxfib = rt_numfibs;
-	} else {
-		fibnum = ifp->if_fib;
-		maxfib = fibnum + 1;
-	}
+	struct sockaddr_in6 mask6 = {
+		.sin6_family = AF_INET6,
+		.sin6_len = sizeof(struct sockaddr_in6),
+		.sin6_addr = pr->ndpr_mask,
+	};
+	struct sockaddr_in6 *pmask6 = (pr->ndpr_plen != 128) ? &mask6 : NULL;
 
-	a_failure = 0;
-	NET_EPOCH_ENTER(et);
-	for (; fibnum < maxfib; fibnum++) {
-		struct rt_addrinfo info;
-		struct rib_cmd_info rc;
-
-		bzero((caddr_t)&info, sizeof(info));
-		info.rti_flags = RTF_GATEWAY;
-		info.rti_info[RTAX_DST] = (struct sockaddr *)&sa6;
-		info.rti_info[RTAX_GATEWAY] = NULL;
-		info.rti_info[RTAX_NETMASK] = (struct sockaddr *)&mask6;
-
-		NET_EPOCH_ASSERT();
-		error = rib_action(fibnum, RTM_DELETE, &info, &rc);
-		if (error != 0) {
-			/* Save last error to return, see rtinit(). */
-			a_failure = error;
-			continue;
-		}
+	error = nd6_prefix_rtrequest(ifp->if_fib, RTM_DELETE,
+	    &pr->ndpr_prefix, pmask6, ifp, NULL);
 
-		/* report route deletion to the routing socket. */
-		struct nhop_object *nh = nhop_select(rc.rc_nh_old, 0);
-		rt_routemsg(RTM_DELETE, rc.rc_rt, nh, fibnum);
-	}
-	NET_EPOCH_EXIT(et);
-	error = a_failure;
 	a_failure = 1;
 	if (error == 0) {
 		pr->ndpr_stateflags &= ~NDPRF_ONLINK;
 
 		/*
 		 * There might be the same prefix on another interface,
 		 * the prefix which could not be on-link just because we have
 		 * the interface route (see comments in nd6_prefix_onlink).
 		 * If there's one, try to make the prefix on-link on the
 		 * interface.
 		 */
 		ND6_RLOCK();
 restart:
 		LIST_FOREACH(opr, &V_nd_prefix, ndpr_entry) {
 			/*
 			 * KAME specific: detached prefixes should not be
 			 * on-link.
 			 */
 			if (opr == pr || (opr->ndpr_stateflags &
 			    (NDPRF_ONLINK | NDPRF_DETACHED)) != 0)
 				continue;
 
 			if (opr->ndpr_plen == pr->ndpr_plen &&
 			    in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr,
 			    &opr->ndpr_prefix.sin6_addr, pr->ndpr_plen)) {
 				int e;
 
 				genid = V_nd6_list_genid;
 				ND6_RUNLOCK();
 				if ((e = nd6_prefix_onlink(opr)) != 0) {
 					nd6log((LOG_ERR,
 					    "%s: failed to recover a prefix "
 					    "%s/%d from %s to %s (errno=%d)\n",
 					    __func__, ip6_sprintf(ip6buf,
 						&opr->ndpr_prefix.sin6_addr),
 					    opr->ndpr_plen, if_name(ifp),
 					    if_name(opr->ndpr_ifp), e));
 				} else
 					a_failure = 0;
 				ND6_RLOCK();
 				if (genid != V_nd6_list_genid)
 					goto restart;
 			}
 		}
 		ND6_RUNLOCK();
 	} else {
 		/* XXX: can we still set the NDPRF_ONLINK flag? */
 		nd6log((LOG_ERR,
 		    "%s: failed to delete route: %s/%d on %s (errno=%d)\n",
-		    __func__, ip6_sprintf(ip6buf, &sa6.sin6_addr),
+		    __func__, ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr),
 		    pr->ndpr_plen, if_name(ifp), error));
 	}
 
 	if (a_failure)
 		lltable_prefix_free(AF_INET6, (struct sockaddr *)&sa6,
 		    (struct sockaddr *)&mask6, LLE_STATIC);
 
 	return (error);
 }
 
 /*
  * ia0 - corresponding public address
  */
 int
 in6_tmpifadd(const struct in6_ifaddr *ia0, int forcegen, int delay)
 {
 	struct ifnet *ifp = ia0->ia_ifa.ifa_ifp;
 	struct in6_ifaddr *newia;
 	struct in6_aliasreq ifra;
 	int error;
 	int trylimit = 3;	/* XXX: adhoc value */
 	int updateflags;
 	u_int32_t randid[2];
 	time_t vltime0, pltime0;
 
 	in6_prepare_ifra(&ifra, &ia0->ia_addr.sin6_addr,
 	    &ia0->ia_prefixmask.sin6_addr);
 
 	ifra.ifra_addr = ia0->ia_addr;	/* XXX: do we need this ? */
 	/* clear the old IFID */
 	IN6_MASK_ADDR(&ifra.ifra_addr.sin6_addr,
 	    &ifra.ifra_prefixmask.sin6_addr);
 
   again:
 	if (in6_get_tmpifid(ifp, (u_int8_t *)randid,
 	    (const u_int8_t *)&ia0->ia_addr.sin6_addr.s6_addr[8], forcegen)) {
 		nd6log((LOG_NOTICE, "%s: failed to find a good random IFID\n",
 		    __func__));
 		return (EINVAL);
 	}
 	ifra.ifra_addr.sin6_addr.s6_addr32[2] |=
 	    (randid[0] & ~(ifra.ifra_prefixmask.sin6_addr.s6_addr32[2]));
 	ifra.ifra_addr.sin6_addr.s6_addr32[3] |=
 	    (randid[1] & ~(ifra.ifra_prefixmask.sin6_addr.s6_addr32[3]));
 
 	/*
 	 * in6_get_tmpifid() quite likely provided a unique interface ID.
 	 * However, we may still have a chance to see collision, because
 	 * there may be a time lag between generation of the ID and generation
 	 * of the address.  So, we'll do one more sanity check.
 	 */
 
 	if (in6_localip(&ifra.ifra_addr.sin6_addr) != 0) {
 		if (trylimit-- > 0) {
 			forcegen = 1;
 			goto again;
 		}
 
 		/* Give up.  Something strange should have happened.  */
 		nd6log((LOG_NOTICE, "%s: failed to find a unique random IFID\n",
 		    __func__));
 		return (EEXIST);
 	}
 
 	/*
 	 * The Valid Lifetime is the lower of the Valid Lifetime of the
          * public address or TEMP_VALID_LIFETIME.
 	 * The Preferred Lifetime is the lower of the Preferred Lifetime
          * of the public address or TEMP_PREFERRED_LIFETIME -
          * DESYNC_FACTOR.
 	 */
 	if (ia0->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
 		vltime0 = IFA6_IS_INVALID(ia0) ? 0 :
 		    (ia0->ia6_lifetime.ia6t_vltime -
 		    (time_uptime - ia0->ia6_updatetime));
 		if (vltime0 > V_ip6_temp_valid_lifetime)
 			vltime0 = V_ip6_temp_valid_lifetime;
 	} else
 		vltime0 = V_ip6_temp_valid_lifetime;
 	if (ia0->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
 		pltime0 = IFA6_IS_DEPRECATED(ia0) ? 0 :
 		    (ia0->ia6_lifetime.ia6t_pltime -
 		    (time_uptime - ia0->ia6_updatetime));
 		if (pltime0 > V_ip6_temp_preferred_lifetime - V_ip6_desync_factor){
 			pltime0 = V_ip6_temp_preferred_lifetime -
 			    V_ip6_desync_factor;
 		}
 	} else
 		pltime0 = V_ip6_temp_preferred_lifetime - V_ip6_desync_factor;
 	ifra.ifra_lifetime.ia6t_vltime = vltime0;
 	ifra.ifra_lifetime.ia6t_pltime = pltime0;
 
 	/*
 	 * A temporary address is created only if this calculated Preferred
 	 * Lifetime is greater than REGEN_ADVANCE time units.
 	 */
 	if (ifra.ifra_lifetime.ia6t_pltime <= V_ip6_temp_regen_advance)
 		return (0);
 
 	/* XXX: scope zone ID? */
 
 	ifra.ifra_flags |= (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY);
 
 	/* allocate ifaddr structure, link into chain, etc. */
 	updateflags = 0;
 	if (delay)
 		updateflags |= IN6_IFAUPDATE_DADDELAY;
 	if ((error = in6_update_ifa(ifp, &ifra, NULL, updateflags)) != 0)
 		return (error);
 
 	newia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr);
 	if (newia == NULL) {	/* XXX: can it happen? */
 		nd6log((LOG_ERR,
 		    "%s: ifa update succeeded, but we got no ifaddr\n",
 		    __func__));
 		return (EINVAL); /* XXX */
 	}
 	newia->ia6_ndpr = ia0->ia6_ndpr;
 	newia->ia6_ndpr->ndpr_addrcnt++;
 	ifa_free(&newia->ia_ifa);
 
 	/*
 	 * A newly added address might affect the status of other addresses.
 	 * XXX: when the temporary address is generated with a new public
 	 * address, the onlink check is redundant.  However, it would be safe
 	 * to do the check explicitly everywhere a new address is generated,
 	 * and, in fact, we surely need the check when we create a new
 	 * temporary address due to deprecation of an old temporary address.
 	 */
 	pfxlist_onlink_check();
 
 	return (0);
 }
 
 static int
 rt6_deleteroute(const struct rtentry *rt, const struct nhop_object *nh,
     void *arg)
 {
 	struct in6_addr *gate = (struct in6_addr *)arg;
 	int nh_rt_flags;
 
 	if (nh->gw_sa.sa_family != AF_INET6)
 		return (0);
 
 	if (!IN6_ARE_ADDR_EQUAL(gate, &nh->gw6_sa.sin6_addr)) {
 		return (0);
 	}
 
 	/*
 	 * Do not delete a static route.
 	 * XXX: this seems to be a bit ad-hoc. Should we consider the
 	 * 'cloned' bit instead?
 	 */
 	nh_rt_flags = nhop_get_rtflags(nh);
 	if ((nh_rt_flags & RTF_STATIC) != 0)
 		return (0);
 
 	/*
 	 * We delete only host route. This means, in particular, we don't
 	 * delete default route.
 	 */
 	if ((nh_rt_flags & RTF_HOST) == 0)
 		return (0);
 
 	return (1);
 #undef SIN6
 }
 
 /*
  * Delete all the routing table entries that use the specified gateway.
  * XXX: this function causes search through all entries of routing table, so
  * it shouldn't be called when acting as a router.
  */
 void
 rt6_flush(struct in6_addr *gateway, struct ifnet *ifp)
 {
 
 	/* We'll care only link-local addresses */
 	if (!IN6_IS_ADDR_LINKLOCAL(gateway))
 		return;
 
 	/* XXX Do we really need to walk any but the default FIB? */
 	rib_foreach_table_walk_del(AF_INET6, rt6_deleteroute, (void *)gateway);
 }
 
 int
 nd6_setdefaultiface(int ifindex)
 {
 	int error = 0;
 
 	if (ifindex < 0 || V_if_index < ifindex)
 		return (EINVAL);
 	if (ifindex != 0 && !ifnet_byindex(ifindex))
 		return (EINVAL);
 
 	if (V_nd6_defifindex != ifindex) {
 		V_nd6_defifindex = ifindex;
 		if (V_nd6_defifindex > 0)
 			V_nd6_defifp = ifnet_byindex(V_nd6_defifindex);
 		else
 			V_nd6_defifp = NULL;
 
 		/*
 		 * Our current implementation assumes one-to-one maping between
 		 * interfaces and links, so it would be natural to use the
 		 * default interface as the default link.
 		 */
 		scope6_setdefault(V_nd6_defifp);
 	}
 
 	return (error);
 }
 
 bool
 nd6_defrouter_list_empty(void)
 {
 
 	return (TAILQ_EMPTY(&V_nd6_defrouter));
 }
 
 void
 nd6_defrouter_timer(void)
 {
 	struct nd_defrouter *dr, *ndr;
 	struct nd6_drhead drq;
 
 	TAILQ_INIT(&drq);
 
 	ND6_WLOCK();
 	TAILQ_FOREACH_SAFE(dr, &V_nd6_defrouter, dr_entry, ndr)
 		if (dr->expire && dr->expire < time_uptime)
 			defrouter_unlink(dr, &drq);
 	ND6_WUNLOCK();
 
 	while ((dr = TAILQ_FIRST(&drq)) != NULL) {
 		TAILQ_REMOVE(&drq, dr, dr_entry);
 		defrouter_del(dr);
 	}
 }
 
 /*
  * Nuke default router list entries toward ifp.
  * We defer removal of default router list entries that is installed in the
  * routing table, in order to keep additional side effects as small as possible.
  */
 void
 nd6_defrouter_purge(struct ifnet *ifp)
 {
 	struct nd_defrouter *dr, *ndr;
 	struct nd6_drhead drq;
 
 	TAILQ_INIT(&drq);
 
 	ND6_WLOCK();
 	TAILQ_FOREACH_SAFE(dr, &V_nd6_defrouter, dr_entry, ndr) {
 		if (dr->installed)
 			continue;
 		if (dr->ifp == ifp)
 			defrouter_unlink(dr, &drq);
 	}
 	TAILQ_FOREACH_SAFE(dr, &V_nd6_defrouter, dr_entry, ndr) {
 		if (!dr->installed)
 			continue;
 		if (dr->ifp == ifp)
 			defrouter_unlink(dr, &drq);
 	}
 	ND6_WUNLOCK();
 
 	/* Delete the unlinked router objects. */
 	while ((dr = TAILQ_FIRST(&drq)) != NULL) {
 		TAILQ_REMOVE(&drq, dr, dr_entry);
 		defrouter_del(dr);
 	}
 }
 
 void
 nd6_defrouter_flush_all(void)
 {
 	struct nd_defrouter *dr;
 	struct nd6_drhead drq;
 
 	TAILQ_INIT(&drq);
 
 	ND6_WLOCK();
 	while ((dr = TAILQ_FIRST(&V_nd6_defrouter)) != NULL)
 		defrouter_unlink(dr, &drq);
 	ND6_WUNLOCK();
 
 	while ((dr = TAILQ_FIRST(&drq)) != NULL) {
 		TAILQ_REMOVE(&drq, dr, dr_entry);
 		defrouter_del(dr);
 	}
 }
 
 void
 nd6_defrouter_init(void)
 {
 
 	TAILQ_INIT(&V_nd6_defrouter);
 }
 
 static int
 nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS)
 {
 	struct in6_defrouter d;
 	struct nd_defrouter *dr;
 	int error;
 
 	if (req->newptr != NULL)
 		return (EPERM);
 
 	error = sysctl_wire_old_buffer(req, 0);
 	if (error != 0)
 		return (error);
 
 	bzero(&d, sizeof(d));
 	d.rtaddr.sin6_family = AF_INET6;
 	d.rtaddr.sin6_len = sizeof(d.rtaddr);
 
 	ND6_RLOCK();
 	TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry) {
 		d.rtaddr.sin6_addr = dr->rtaddr;
 		error = sa6_recoverscope(&d.rtaddr);
 		if (error != 0)
 			break;
 		d.flags = dr->raflags;
 		d.rtlifetime = dr->rtlifetime;
 		d.expire = dr->expire + (time_second - time_uptime);
 		d.if_index = dr->ifp->if_index;
 		error = SYSCTL_OUT(req, &d, sizeof(d));
 		if (error != 0)
 			break;
 	}
 	ND6_RUNLOCK();
 	return (error);
 }
 SYSCTL_PROC(_net_inet6_icmp6, ICMPV6CTL_ND6_DRLIST, nd6_drlist,
 	CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE,
 	NULL, 0, nd6_sysctl_drlist, "S,in6_defrouter",
 	"NDP default router list");
diff --git a/tests/sys/net/routing/test_rtsock_l3.c b/tests/sys/net/routing/test_rtsock_l3.c
index daba6e5013e6..9486ac466965 100644
--- a/tests/sys/net/routing/test_rtsock_l3.c
+++ b/tests/sys/net/routing/test_rtsock_l3.c
@@ -1,1418 +1,1417 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  *
  * Copyright (c) 2019 Alexander V. Chernikov
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  * $FreeBSD$
  */
 
 #include "rtsock_common.h"
 #include "rtsock_config.h"
 #include "sys/types.h"
 #include <sys/time.h>
 #include <sys/ioctl.h>
 
 #include "net/bpf.h"
 
 static void
 jump_vnet(struct rtsock_test_config *c, const atf_tc_t *tc)
 {
 	char vnet_name[512];
 
 	snprintf(vnet_name, sizeof(vnet_name), "vt-%s", atf_tc_get_ident(tc));
 	RLOG("jumping to %s", vnet_name);
 
 	vnet_switch(vnet_name, c->ifnames, c->num_interfaces);
 
 	/* Update ifindex cache */
 	c->ifindex = if_nametoindex(c->ifname);
 }
 
 static inline struct rtsock_test_config *
 presetup_ipv6_iface(const atf_tc_t *tc)
 {
 	struct rtsock_test_config *c;
 	int ret;
 
 	c = config_setup(tc, NULL);
 
 	jump_vnet(c, tc);
 
 	ret = iface_turn_up(c->ifname);
 	ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifname);
 
 	ret = iface_enable_ipv6(c->ifname);
 	ATF_REQUIRE_MSG(ret == 0, "Unable to enable IPv6 on %s", c->ifname);
 
 	return (c);
 }
 
 static inline struct rtsock_test_config *
 presetup_ipv6(const atf_tc_t *tc)
 {
 	struct rtsock_test_config *c;
 	int ret;
 
 	c = presetup_ipv6_iface(tc);
 
 	ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6);
 
 	c->rtsock_fd = rtsock_setup_socket();
 
 	return (c);
 }
 
 static inline struct rtsock_test_config *
 presetup_ipv4_iface(const atf_tc_t *tc)
 {
 	struct rtsock_test_config *c;
 	int ret;
 
 	c = config_setup(tc, NULL);
 
 	jump_vnet(c, tc);
 
 	ret = iface_turn_up(c->ifname);
 	ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifname);
 
 	return (c);
 }
 
 static inline struct rtsock_test_config *
 presetup_ipv4(const atf_tc_t *tc)
 {
 	struct rtsock_test_config *c;
 	int ret;
 
 	c = presetup_ipv4_iface(tc);
 
 	/* assumes ifconfig doing IFF_UP */
 	ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4);
 	ATF_REQUIRE_MSG(ret == 0, "ifconfig failed");
 
 	c->rtsock_fd = rtsock_setup_socket();
 
 	return (c);
 }
 
 
 static void
 prepare_v4_network(struct rtsock_test_config *c, struct sockaddr_in *dst,
   struct sockaddr_in *mask, struct sockaddr_in *gw)
 {
 	/* Create IPv4 subnetwork with smaller prefix */
 	sa_fill_mask4(mask, c->plen4 + 1);
 	*dst = c->net4;
 	/* Calculate GW as last-net-address - 1 */
 	*gw = c->net4;
 	gw->sin_addr.s_addr = htonl((ntohl(c->net4.sin_addr.s_addr) | ~ntohl(c->mask4.sin_addr.s_addr)) - 1);
 	sa_print((struct sockaddr *)dst, 0);
 	sa_print((struct sockaddr *)mask, 0);
 	sa_print((struct sockaddr *)gw, 0);
 }
 
 static void
 prepare_v6_network(struct rtsock_test_config *c, struct sockaddr_in6 *dst,
   struct sockaddr_in6 *mask, struct sockaddr_in6 *gw)
 {
 	/* Create IPv6 subnetwork with smaller prefix */
 	sa_fill_mask6(mask, c->plen6 + 1);
 	*dst = c->net6;
 	/* Calculate GW as last-net-address - 1 */
 	*gw = c->net6;
 #define _s6_addr32 __u6_addr.__u6_addr32
 	gw->sin6_addr._s6_addr32[0] = htonl((ntohl(gw->sin6_addr._s6_addr32[0]) | ~ntohl(c->mask6.sin6_addr._s6_addr32[0])));
 	gw->sin6_addr._s6_addr32[1] = htonl((ntohl(gw->sin6_addr._s6_addr32[1]) | ~ntohl(c->mask6.sin6_addr._s6_addr32[1])));
 	gw->sin6_addr._s6_addr32[2] = htonl((ntohl(gw->sin6_addr._s6_addr32[2]) | ~ntohl(c->mask6.sin6_addr._s6_addr32[2])));
 	gw->sin6_addr._s6_addr32[3] = htonl((ntohl(gw->sin6_addr._s6_addr32[3]) | ~ntohl(c->mask6.sin6_addr._s6_addr32[3])) - 1);
 #undef _s6_addr32
 	sa_print((struct sockaddr *)dst, 0);
 	sa_print((struct sockaddr *)mask, 0);
 	sa_print((struct sockaddr *)gw, 0);
 }
 
 static void
 prepare_route_message(struct rt_msghdr *rtm, int cmd, struct sockaddr *dst,
   struct sockaddr *mask, struct sockaddr *gw)
 {
 
 	rtsock_prepare_route_message(rtm, cmd, dst, mask, gw);
 
 	if (cmd == RTM_ADD || cmd == RTM_CHANGE)
 		rtm->rtm_flags |= RTF_STATIC;
 }
 
 static void
 verify_route_message(struct rt_msghdr *rtm, int cmd, struct sockaddr *dst,
   struct sockaddr *mask, struct sockaddr *gw)
 {
 	char msg[512];
 	struct sockaddr *sa;
 	int ret;
 
 	RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_type == cmd,
 	    "expected %s message, got %d (%s)", rtsock_print_cmdtype(cmd),
 	    rtm->rtm_type, rtsock_print_cmdtype(rtm->rtm_type));
 	RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_errno == 0,
 	    "got got errno %d as message reply", rtm->rtm_errno);
 	RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->_rtm_spare1 == 0,
 	    "expected rtm_spare==0, got %d", rtm->_rtm_spare1);
 
 	/* kernel MAY return more sockaddrs, including RTA_IFP / RTA_IFA, so verify the needed ones */
 	if (dst != NULL) {
 		sa = rtsock_find_rtm_sa(rtm, RTA_DST);
 		RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "DST is not set");
 		ret = sa_equal_msg(sa, dst, msg, sizeof(msg));
 		RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg);
 	}
 
 	if (mask != NULL) {
 		sa = rtsock_find_rtm_sa(rtm, RTA_NETMASK);
 		RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "NETMASK is not set");
 		ret = sa_equal_msg(sa, mask, msg, sizeof(msg));
 		ret = 1;
 		RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "NETMASK sa diff: %s", msg);
 	}
 
 	if (gw != NULL) {
 		sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY);
 		RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "GATEWAY is not set");
 		ret = sa_equal_msg(sa, gw, msg, sizeof(msg));
 		RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg);
 	}
 }
 
 static void
 verify_route_message_extra(struct rt_msghdr *rtm, int ifindex, int rtm_flags)
 {
 	RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_index == ifindex,
 	    "expected ifindex %d, got %d", ifindex, rtm->rtm_index);
 
 	if (rtm->rtm_flags != rtm_flags) {
 		char got_flags[64], expected_flags[64];
 		rtsock_print_rtm_flags(got_flags, sizeof(got_flags),
 		    rtm->rtm_flags);
 		rtsock_print_rtm_flags(expected_flags, sizeof(expected_flags),
 		    rtm_flags);
 
 		RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_flags == rtm_flags,
 		    "expected flags: 0x%X %s, got 0x%X %s",
 		    rtm_flags, expected_flags,
 		    rtm->rtm_flags, got_flags);
 	}
 }
 
 static void
 verify_link_gateway(struct rt_msghdr *rtm, int ifindex)
 {
 	struct sockaddr *sa;
 	struct sockaddr_dl *sdl;
 
 	sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY);
 	RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "GATEWAY is not set");
 	RTSOCK_ATF_REQUIRE_MSG(rtm, sa->sa_family == AF_LINK, "GW sa family is %d", sa->sa_family);
 	sdl = (struct sockaddr_dl *)sa;
 	RTSOCK_ATF_REQUIRE_MSG(rtm, sdl->sdl_index == ifindex, "GW ifindex is %d", sdl->sdl_index);
 }
 
 /* TESTS */
 
 #define	DECLARE_TEST_VARS					\
 	char buffer[2048];					\
 	struct rtsock_test_config *c;				\
 	struct rt_msghdr *rtm = (struct rt_msghdr *)buffer;	\
 	struct sockaddr *sa;					\
 	int ret;						\
 								\
 
 #define	DESCRIBE_ROOT_TEST(_msg)	config_describe_root_test(tc, _msg)
 #define	CLEANUP_AFTER_TEST	config_generic_cleanup(tc)
 
 #define	RTM_DECLARE_ROOT_TEST(_name, _descr)			\
 ATF_TC_WITH_CLEANUP(_name);					\
 ATF_TC_HEAD(_name, tc)						\
 {								\
 	DESCRIBE_ROOT_TEST(_descr);				\
 }								\
 ATF_TC_CLEANUP(_name, tc)					\
 {								\
 	CLEANUP_AFTER_TEST;					\
 }
 
 ATF_TC_WITH_CLEANUP(rtm_get_v4_exact_success);
 ATF_TC_HEAD(rtm_get_v4_exact_success, tc)
 {
 	DESCRIBE_ROOT_TEST("Tests RTM_GET with exact prefix lookup on an interface prefix");
 }
 
 ATF_TC_BODY(rtm_get_v4_exact_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	c = presetup_ipv4(tc);
 
 	prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&c->net4,
 	    (struct sockaddr *)&c->mask4, NULL);
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	/*
 	 * RTM_GET: Report Metrics: len 240, pid: 45072, seq 42, errno 0, flags: <UP,DONE,PINNED>
 	 * sockaddrs: 0x7 <DST,GATEWAY,NETMASK>
 	 *  af=inet len=16 addr=192.0.2.0 hd={10, 02, 00{2}, C0, 00, 02, 00{9}}
 	 *  af=link len=54 sdl_index=3 if_name=tap4242 hd={36, 12, 03, 00, 06, 00{49}}
 	 *  af=inet len=16 addr=255.255.255.0 hd={10, 02, FF{5}, 00{9}}
 	 */
 
 	verify_route_message(rtm, RTM_GET, (struct sockaddr *)&c->net4,
 	    (struct sockaddr *)&c->mask4, NULL);
 
 	verify_route_message_extra(rtm, c->ifindex, RTF_UP | RTF_DONE | RTF_PINNED);
 
 	/* Explicitly verify gateway for the interface route */
 	verify_link_gateway(rtm, c->ifindex);
 	sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY);
 	RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "GATEWAY is not set");
 	RTSOCK_ATF_REQUIRE_MSG(rtm, sa->sa_family == AF_LINK, "GW sa family is %d", sa->sa_family);
 	struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
 	RTSOCK_ATF_REQUIRE_MSG(rtm, sdl->sdl_index == c->ifindex, "GW ifindex is %d", sdl->sdl_index);
 }
 
 ATF_TC_CLEANUP(rtm_get_v4_exact_success, tc)
 {
 	CLEANUP_AFTER_TEST;
 }
 
 ATF_TC_WITH_CLEANUP(rtm_get_v4_lpm_success);
 ATF_TC_HEAD(rtm_get_v4_lpm_success, tc)
 {
 	DESCRIBE_ROOT_TEST("Tests RTM_GET with address lookup on an existing prefix");
 }
 
 ATF_TC_BODY(rtm_get_v4_lpm_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	c = presetup_ipv4(tc);
 
 	prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&c->net4, NULL, NULL);
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	/*
 	 * RTM_GET: Report Metrics: len 312, pid: 67074, seq 1, errno 0, flags:<UP,DONE,PINNED>
 	 * locks:  inits:
 	 * sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA>
 	 * 10.0.0.0 link#1 255.255.255.0 vtnet0:52.54.0.42.f.ef 10.0.0.157
 	 */
 
 	verify_route_message(rtm, RTM_GET, (struct sockaddr *)&c->net4,
 	    (struct sockaddr *)&c->mask4, NULL);
 
 	verify_route_message_extra(rtm, c->ifindex, RTF_UP | RTF_DONE | RTF_PINNED);
 }
 
 ATF_TC_CLEANUP(rtm_get_v4_lpm_success, tc)
 {
 	CLEANUP_AFTER_TEST;
 }
 
 
 ATF_TC_WITH_CLEANUP(rtm_get_v4_empty_dst_failure);
 ATF_TC_HEAD(rtm_get_v4_empty_dst_failure, tc)
 {
 
 	DESCRIBE_ROOT_TEST("Tests RTM_GET with empty DST addr");
 }
 
 ATF_TC_BODY(rtm_get_v4_empty_dst_failure, tc)
 {
 	DECLARE_TEST_VARS;
 	struct rtsock_config_options co;
 
 	bzero(&co, sizeof(co));
 	co.num_interfaces = 0;
 	
 	c = config_setup(tc,&co);
 	c->rtsock_fd = rtsock_setup_socket();
 
 	rtsock_prepare_route_message(rtm, RTM_GET, NULL,
 	    (struct sockaddr *)&c->mask4, NULL);
 	rtsock_update_rtm_len(rtm);
 
 	ATF_CHECK_ERRNO(EINVAL, write(c->rtsock_fd, rtm, rtm->rtm_msglen));
 }
 
 ATF_TC_CLEANUP(rtm_get_v4_empty_dst_failure, tc)
 {
 	CLEANUP_AFTER_TEST;
 }
 
 ATF_TC_WITH_CLEANUP(rtm_get_v4_hostbits_failure);
 ATF_TC_HEAD(rtm_get_v4_hostbits_failure, tc)
 {
 	DESCRIBE_ROOT_TEST("Tests RTM_GET with prefix with some hosts-bits set");
 }
 
 ATF_TC_BODY(rtm_get_v4_hostbits_failure, tc)
 {
 	DECLARE_TEST_VARS;
 
 	c = presetup_ipv4(tc);
 
 	/* Q the same prefix */
 	rtsock_prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&c->addr4,
 	    (struct sockaddr *)&c->mask4, NULL);
 	rtsock_update_rtm_len(rtm);
 
 	ATF_CHECK_ERRNO(ESRCH, write(c->rtsock_fd, rtm, rtm->rtm_msglen));
 }
 
 ATF_TC_CLEANUP(rtm_get_v4_hostbits_failure, tc)
 {
 	CLEANUP_AFTER_TEST;
 }
 
 ATF_TC_WITH_CLEANUP(rtm_add_v4_gw_direct_success);
 ATF_TC_HEAD(rtm_add_v4_gw_direct_success, tc)
 {
 	DESCRIBE_ROOT_TEST("Tests IPv4 route addition with directly-reachable GW specified by IP");
 }
 
 ATF_TC_BODY(rtm_add_v4_gw_direct_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	c = presetup_ipv4(tc);
 
 	/* Create IPv4 subnetwork with smaller prefix */
 	struct sockaddr_in mask4;
 	struct sockaddr_in net4;
 	struct sockaddr_in gw4;
 	prepare_v4_network(c, &net4, &mask4, &gw4);
 
 	prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	/*
 	 * RTM_ADD: Add Route: len 200, pid: 46068, seq 42, errno 0, flags:<GATEWAY,DONE,STATIC>
 	 * locks:  inits:
 	 * sockaddrs: <DST,GATEWAY,NETMASK>
 	 *  192.0.2.0 192.0.2.254 255.255.255.128
 	 */
 
 	verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
 	verify_route_message_extra(rtm, c->ifindex,
 	    RTF_UP | RTF_DONE | RTF_GATEWAY | RTF_STATIC);
 }
 
 ATF_TC_CLEANUP(rtm_add_v4_gw_direct_success, tc)
 {
 	CLEANUP_AFTER_TEST;
 }
 
 RTM_DECLARE_ROOT_TEST(rtm_add_v4_no_rtf_host_failure,
     "Tests failure with netmask sa and RTF_HOST inconsistency");
 
 ATF_TC_BODY(rtm_add_v4_no_rtf_host_failure, tc)
 {
 	DECLARE_TEST_VARS;
 
 	c = presetup_ipv4(tc);
 
 	/* Create IPv4 subnetwork with smaller prefix */
 	struct sockaddr_in mask4;
 	struct sockaddr_in net4;
 	struct sockaddr_in gw4;
 	prepare_v4_network(c, &net4, &mask4, &gw4);
 
 	prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4,
 	    NULL, (struct sockaddr *)&gw4);
 	rtsock_update_rtm_len(rtm);
 
 	/* RTF_HOST is NOT specified, while netmask is empty */
 
 	ATF_CHECK_ERRNO(EINVAL, write(c->rtsock_fd, rtm, rtm->rtm_msglen));
 }
 
 ATF_TC_WITH_CLEANUP(rtm_del_v4_prefix_nogw_success);
 ATF_TC_HEAD(rtm_del_v4_prefix_nogw_success, tc)
 {
 	DESCRIBE_ROOT_TEST("Tests IPv4 route removal without specifying gateway");
 }
 
 ATF_TC_BODY(rtm_del_v4_prefix_nogw_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	c = presetup_ipv4(tc);
 
 	/* Create IPv4 subnetwork with smaller prefix */
 	struct sockaddr_in mask4;
 	struct sockaddr_in net4;
 	struct sockaddr_in gw4;
 	prepare_v4_network(c, &net4, &mask4, &gw4);
 
 	prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 
 	/* Route has been added successfully, try to delete it */
 	prepare_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, NULL);
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	/*
 	 * RTM_DELETE: Delete Route: len 200, pid: 46417, seq 43, errno 0, flags: <GATEWAY,DONE,STATIC>
 	 * sockaddrs: 0x7 <DST,GATEWAY,NETMASK>
 	 *  af=inet len=16 addr=192.0.2.0 hd={10, 02, 00{2}, C0, 00, 02, 00{9}}
 	 *  af=inet len=16 addr=192.0.2.254 hd={10, 02, 00{2}, C0, 00, 02, FE, 00{8}}
 	 *  af=inet len=16 addr=255.255.255.128 hd={10, 02, FF{5}, 80, 00{8}}
 	 */
 	verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
 
 	verify_route_message_extra(rtm, c->ifindex, RTF_DONE | RTF_GATEWAY | RTF_STATIC);
 }
 
 ATF_TC_CLEANUP(rtm_del_v4_prefix_nogw_success, tc)
 {
 	CLEANUP_AFTER_TEST;
 }
 
 RTM_DECLARE_ROOT_TEST(rtm_change_v4_gw_success,
     "Tests IPv4 gateway change");
 
 ATF_TC_BODY(rtm_change_v4_gw_success, tc)
 {
 	DECLARE_TEST_VARS;
 	struct rtsock_config_options co;
 
 	bzero(&co, sizeof(co));
 	co.num_interfaces = 2;
 
 	c = config_setup(tc, &co);
 	jump_vnet(c, tc);
 
 	ret = iface_turn_up(c->ifnames[0]);
 	ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifnames[0]);
 	ret = iface_turn_up(c->ifnames[1]);
 	ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifnames[1]);
 
 	ret = iface_setup_addr(c->ifnames[0], c->addr4_str, c->plen4);
 	ATF_REQUIRE_MSG(ret == 0, "ifconfig failed");
 
 	/* Use 198.51.100.0/24 "TEST-NET-2" for the second interface */
 	ret = iface_setup_addr(c->ifnames[1], "198.51.100.1", 24);
 	ATF_REQUIRE_MSG(ret == 0, "ifconfig failed");
 
 	c->rtsock_fd = rtsock_setup_socket();
 
 	/* Create IPv4 subnetwork with smaller prefix */
 	struct sockaddr_in mask4;
 	struct sockaddr_in net4;
 	struct sockaddr_in gw4;
 	prepare_v4_network(c, &net4, &mask4, &gw4);
 
 	prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
 
 	/* Change gateway to the one on desiding on the other interface */
 	inet_pton(AF_INET, "198.51.100.2", &gw4.sin_addr.s_addr);
 	prepare_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	verify_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
 
 	verify_route_message_extra(rtm, if_nametoindex(c->ifnames[1]),
 	    RTF_UP | RTF_DONE | RTF_GATEWAY | RTF_STATIC);
 
 	/* Verify the change has actually taken place */
 	prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, NULL);
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 
 	/*
 	 * RTM_GET: len 200, pid: 3894, seq 44, errno 0, flags: <UP,GATEWAY,DONE,STATIC>
 	 *  sockaddrs: 0x7 <DST,GATEWAY,NETMASK>
  	 *  af=inet len=16 addr=192.0.2.0 hd={x10, x02, x00{2}, xC0, x00, x02, x00{9}}
 	 *  af=inet len=16 addr=198.51.100.2 hd={x10, x02, x00{2}, xC6, x33, x64, x02, x00{8}}
 	 *  af=inet len=16 addr=255.255.255.128 hd={x10, x02, xFF, xFF, xFF, xFF, xFF, x80, x00{8}}
 	 */
 
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 	verify_route_message_extra(rtm, if_nametoindex(c->ifnames[1]),
 	    RTF_UP | RTF_DONE | RTF_GATEWAY | RTF_STATIC);
 
 }
 
 RTM_DECLARE_ROOT_TEST(rtm_change_v4_mtu_success,
     "Tests IPv4 path mtu change");
 
 ATF_TC_BODY(rtm_change_v4_mtu_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	unsigned long test_mtu = 1442;
 
 	c = presetup_ipv4(tc);
 
 	/* Create IPv4 subnetwork with smaller prefix */
 	struct sockaddr_in mask4;
 	struct sockaddr_in net4;
 	struct sockaddr_in gw4;
 	prepare_v4_network(c, &net4, &mask4, &gw4);
 
 	prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	/* Change MTU */
 	prepare_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, NULL);
 	rtm->rtm_inits |= RTV_MTU;
 	rtm->rtm_rmx.rmx_mtu = test_mtu;
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	verify_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, NULL);
 
 	RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_rmx.rmx_mtu == test_mtu,
 	    "expected mtu: %lu, got %lu", test_mtu, rtm->rtm_rmx.rmx_mtu);
 
 	/* Verify the change has actually taken place */
 	prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, NULL);
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_rmx.rmx_mtu == test_mtu,
 	    "expected mtu: %lu, got %lu", test_mtu, rtm->rtm_rmx.rmx_mtu);
 }
 
 RTM_DECLARE_ROOT_TEST(rtm_change_v4_flags_success,
     "Tests IPv4 path flags change");
 
 ATF_TC_BODY(rtm_change_v4_flags_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	uint32_t test_flags = RTF_PROTO1 | RTF_PROTO2 | RTF_PROTO3 | RTF_STATIC;
 	uint32_t desired_flags;
 
 	c = presetup_ipv4(tc);
 
 	/* Create IPv4 subnetwork with smaller prefix */
 	struct sockaddr_in mask4;
 	struct sockaddr_in net4;
 	struct sockaddr_in gw4;
 	prepare_v4_network(c, &net4, &mask4, &gw4);
 
 	prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
 
 	/* Set test flags during route addition */
 	desired_flags = RTF_UP | RTF_DONE | RTF_GATEWAY | test_flags;
 	rtm->rtm_flags |= test_flags;
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	/* Change flags */
 	prepare_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, NULL);
 	rtm->rtm_flags &= ~test_flags;
 	desired_flags &= ~test_flags;
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	/* Verify updated flags */
 	verify_route_message_extra(rtm, c->ifindex, desired_flags | RTF_DONE);
 
 	/* Verify the change has actually taken place */
 	prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, NULL);
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	verify_route_message_extra(rtm, c->ifindex, desired_flags | RTF_DONE);
 }
 
 
 ATF_TC_WITH_CLEANUP(rtm_add_v6_gu_gw_gu_direct_success);
 ATF_TC_HEAD(rtm_add_v6_gu_gw_gu_direct_success, tc)
 {
 	DESCRIBE_ROOT_TEST("Tests IPv6 global unicast prefix addition with directly-reachable GU GW");
 }
 
 ATF_TC_BODY(rtm_add_v6_gu_gw_gu_direct_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	c = presetup_ipv6(tc);
 
 	/* Create IPv6 subnetwork with smaller prefix */
 	struct sockaddr_in6 mask6;
 	struct sockaddr_in6 net6;
 	struct sockaddr_in6 gw6;
 	prepare_v6_network(c, &net6, &mask6, &gw6);
 
 	prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	/*
 	 * RTM_ADD: Add Route: len 200, pid: 46068, seq 42, errno 0, flags:<GATEWAY,DONE,STATIC>
 	 * locks:  inits:
 	 * sockaddrs: <DST,GATEWAY,NETMASK>
 	 *  192.0.2.0 192.0.2.254 255.255.255.128
 	 */
 
 	verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
 
 	verify_route_message_extra(rtm, c->ifindex,
 	    RTF_UP | RTF_DONE | RTF_GATEWAY | RTF_STATIC);
 }
 
 ATF_TC_CLEANUP(rtm_add_v6_gu_gw_gu_direct_success, tc)
 {
 	CLEANUP_AFTER_TEST;
 }
 
 ATF_TC_WITH_CLEANUP(rtm_del_v6_gu_prefix_nogw_success);
 ATF_TC_HEAD(rtm_del_v6_gu_prefix_nogw_success, tc)
 {
 
 	DESCRIBE_ROOT_TEST("Tests IPv6 global unicast prefix removal without specifying gateway");
 }
 
 ATF_TC_BODY(rtm_del_v6_gu_prefix_nogw_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	c = presetup_ipv6(tc);
 
 	/* Create IPv6 subnetwork with smaller prefix */
 	struct sockaddr_in6 mask6;
 	struct sockaddr_in6 net6;
 	struct sockaddr_in6 gw6;
 	prepare_v6_network(c, &net6, &mask6, &gw6);
 
 	prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 
 	/* Route has been added successfully, try to delete it */
 	prepare_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, NULL);
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	/*
 	 * RTM_DELETE: Delete Route: len 200, pid: 46417, seq 43, errno 0, flags: <GATEWAY,DONE,STATIC>
 	 * sockaddrs: 0x7 <DST,GATEWAY,NETMASK>
 	 *  af=inet len=16 addr=192.0.2.0 hd={10, 02, 00{2}, C0, 00, 02, 00{9}}
 	 *  af=inet len=16 addr=192.0.2.254 hd={10, 02, 00{2}, C0, 00, 02, FE, 00{8}}
 	 *  af=inet len=16 addr=255.255.255.128 hd={10, 02, FF{5}, 80, 00{8}}
 	 */
 
 	verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
 	verify_route_message_extra(rtm, c->ifindex, RTF_DONE | RTF_GATEWAY | RTF_STATIC);
 }
 
 ATF_TC_CLEANUP(rtm_del_v6_gu_prefix_nogw_success, tc)
 {
 	CLEANUP_AFTER_TEST;
 }
 
 RTM_DECLARE_ROOT_TEST(rtm_change_v6_gw_success,
     "Tests IPv6 gateway change");
 
 ATF_TC_BODY(rtm_change_v6_gw_success, tc)
 {
 	DECLARE_TEST_VARS;
 	struct rtsock_config_options co;
 
 	bzero(&co, sizeof(co));
 	co.num_interfaces = 2;
 
 	c = config_setup(tc, &co);
 	jump_vnet(c, tc);
 
 	ret = iface_turn_up(c->ifnames[0]);
 	ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifnames[0]);
 	ret = iface_turn_up(c->ifnames[1]);
 	ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifnames[1]);
 
 	ret = iface_enable_ipv6(c->ifnames[0]);
 	ATF_REQUIRE_MSG(ret == 0, "Unable to enable IPv6 on %s", c->ifnames[0]);
 	ret = iface_enable_ipv6(c->ifnames[1]);
 	ATF_REQUIRE_MSG(ret == 0, "Unable to enable IPv6 on %s", c->ifnames[1]);
 
 	ret = iface_setup_addr(c->ifnames[0], c->addr6_str, c->plen6);
 	ATF_REQUIRE_MSG(ret == 0, "ifconfig failed");
 
 	ret = iface_setup_addr(c->ifnames[1], "2001:DB8:4242::1", 64);
 	ATF_REQUIRE_MSG(ret == 0, "ifconfig failed");
 
 	c->rtsock_fd = rtsock_setup_socket();
 
 	/* Create IPv6 subnetwork with smaller prefix */
 	struct sockaddr_in6 mask6;
 	struct sockaddr_in6 net6;
 	struct sockaddr_in6 gw6;
 	prepare_v6_network(c, &net6, &mask6, &gw6);
 
 	prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
 
 	/* Change gateway to the one on residing on the other interface */
 	inet_pton(AF_INET6, "2001:DB8:4242::4242", &gw6.sin6_addr);
 	prepare_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	verify_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
 
 	verify_route_message_extra(rtm, if_nametoindex(c->ifnames[1]),
 	    RTF_UP | RTF_DONE | RTF_GATEWAY | RTF_STATIC);
 
 	/* Verify the change has actually taken place */
 	prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, NULL);
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 
 	/*
 	 * RTM_GET: len 248, pid: 2268, seq 44, errno 0, flags: <UP,GATEWAY,DONE,STATIC>
 	 *  sockaddrs: 0x7 <DST,GATEWAY,NETMASK>
 	 *  af=inet6 len=28 addr=2001:db8:: hd={x1C, x1C, x00{6}, x20, x01, x0D, xB8, x00{16}}
 	 *  af=inet6 len=28 addr=2001:db8:4242::4242 hd={x1C, x1C, x00{6}, x20, x01, x0D, xB8, x42, x42, x00{8}, x42, x42, x00{4}}
 	 *  af=inet6 len=28 addr=ffff:ffff:8000:: hd={x1C, x1C, xFF, xFF, xFF, xFF, xFF, xFF, xFF, xFF, xFF, xFF, x80, x00{15}}
 	 */
 
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 	verify_route_message_extra(rtm, if_nametoindex(c->ifnames[1]),
 	    RTF_UP | RTF_DONE | RTF_GATEWAY | RTF_STATIC);
 }
 
 RTM_DECLARE_ROOT_TEST(rtm_change_v6_mtu_success,
     "Tests IPv6 path mtu change");
 
 ATF_TC_BODY(rtm_change_v6_mtu_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	unsigned long test_mtu = 1442;
 
 	c = presetup_ipv6(tc);
 
 	/* Create IPv6 subnetwork with smaller prefix */
 	struct sockaddr_in6 mask6;
 	struct sockaddr_in6 net6;
 	struct sockaddr_in6 gw6;
 	prepare_v6_network(c, &net6, &mask6, &gw6);
 
 	prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
 
 	/* Send route add */
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	/* Change MTU */
 	prepare_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, NULL);
 	rtm->rtm_inits |= RTV_MTU;
 	rtm->rtm_rmx.rmx_mtu = test_mtu;
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	verify_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, NULL);
 
 	RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_rmx.rmx_mtu == test_mtu,
 	    "expected mtu: %lu, got %lu", test_mtu, rtm->rtm_rmx.rmx_mtu);
 
 	/* Verify the change has actually taken place */
 	prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, NULL);
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_rmx.rmx_mtu == test_mtu,
 	    "expected mtu: %lu, got %lu", test_mtu, rtm->rtm_rmx.rmx_mtu);
 }
 
 RTM_DECLARE_ROOT_TEST(rtm_change_v6_flags_success,
     "Tests IPv6 path flags change");
 
 ATF_TC_BODY(rtm_change_v6_flags_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	uint32_t test_flags = RTF_PROTO1 | RTF_PROTO2 | RTF_PROTO3 | RTF_STATIC;
 	uint32_t desired_flags;
 
 	c = presetup_ipv6(tc);
 
 	/* Create IPv6 subnetwork with smaller prefix */
 	struct sockaddr_in6 mask6;
 	struct sockaddr_in6 net6;
 	struct sockaddr_in6 gw6;
 	prepare_v6_network(c, &net6, &mask6, &gw6);
 
 	prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
 
 	/* Set test flags during route addition */
 	desired_flags = RTF_UP | RTF_DONE | RTF_GATEWAY | test_flags;
 	rtm->rtm_flags |= test_flags;
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	/* Change flags */
 	prepare_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, NULL);
 	rtm->rtm_flags &= ~test_flags;
 	desired_flags &= ~test_flags;
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	/* Verify updated flags */
 	verify_route_message_extra(rtm, c->ifindex, desired_flags | RTF_DONE);
 
 	/* Verify the change has actually taken place */
 	prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, NULL);
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 
 	verify_route_message_extra(rtm, c->ifindex, desired_flags | RTF_DONE);
 }
 
 ATF_TC_WITH_CLEANUP(rtm_add_v4_temporal1_success);
 ATF_TC_HEAD(rtm_add_v4_temporal1_success, tc)
 {
 	DESCRIBE_ROOT_TEST("Tests IPv4 route expiration with expire time set");
 }
 
 ATF_TC_BODY(rtm_add_v4_temporal1_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	c = presetup_ipv4(tc);
 
 	/* Create IPv4 subnetwork with smaller prefix */
 	struct sockaddr_in mask4;
 	struct sockaddr_in net4;
 	struct sockaddr_in gw4;
 	prepare_v4_network(c, &net4, &mask4, &gw4);
 
 	prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
 
 	/* Set expire time to now */
 	struct timeval tv;
 	gettimeofday(&tv, NULL);
 	rtm->rtm_rmx.rmx_expire = tv.tv_sec - 1;
 	rtm->rtm_inits |= RTV_EXPIRE;
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 	ATF_REQUIRE_MSG(rtm != NULL, "unable to get rtsock reply for RTM_ADD");
 	RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_inits & RTV_EXPIRE, "RTV_EXPIRE not set");
 
 	/* The next should be route deletion */
 	rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
 
 	verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net4,
 	    (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
 
 	verify_route_message_extra(rtm, c->ifindex,
 	    RTF_DONE | RTF_GATEWAY | RTF_STATIC);
 }
 
 ATF_TC_CLEANUP(rtm_add_v4_temporal1_success, tc)
 {
 	CLEANUP_AFTER_TEST;
 }
 
 ATF_TC_WITH_CLEANUP(rtm_add_v6_temporal1_success);
 ATF_TC_HEAD(rtm_add_v6_temporal1_success, tc)
 {
 	DESCRIBE_ROOT_TEST("Tests IPv6 global unicast prefix addition with directly-reachable GU GW");
 }
 
 ATF_TC_BODY(rtm_add_v6_temporal1_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	c = presetup_ipv6(tc);
 
 	/* Create IPv6 subnetwork with smaller prefix */
 	struct sockaddr_in6 mask6;
 	struct sockaddr_in6 net6;
 	struct sockaddr_in6 gw6;
 	prepare_v6_network(c, &net6, &mask6, &gw6);
 
 	prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
 
 	/* Set expire time to now */
 	struct timeval tv;
 	gettimeofday(&tv, NULL);
 	rtm->rtm_rmx.rmx_expire = tv.tv_sec - 1;
 	rtm->rtm_inits |= RTV_EXPIRE;
 
 	rtsock_send_rtm(c->rtsock_fd, rtm);
 	rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
 	ATF_REQUIRE_MSG(rtm != NULL, "unable to get rtsock reply for RTM_ADD");
 	RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_inits & RTV_EXPIRE, "RTV_EXPIRE not set");
 
 	/* The next should be route deletion */
 	rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
 
 	verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net6,
 	    (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
 
 	verify_route_message_extra(rtm, c->ifindex,
 	    RTF_DONE | RTF_GATEWAY | RTF_STATIC);
 }
 
 ATF_TC_CLEANUP(rtm_add_v6_temporal1_success, tc)
 {
 	CLEANUP_AFTER_TEST;
 }
 
 /* Interface address messages tests */
 
 RTM_DECLARE_ROOT_TEST(rtm_add_v6_gu_ifa_hostroute_success,
     "Tests validness for /128 host route announce after ifaddr assignment");
 
 ATF_TC_BODY(rtm_add_v6_gu_ifa_hostroute_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	c = presetup_ipv6_iface(tc);
 
 	c->rtsock_fd = rtsock_setup_socket();
 
 	ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6);
 
 	/*
 	 * There will be multiple.
 	 * RTM_ADD without llinfo.
 	 */
 
 	while (true) {
 		rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
 		if ((rtm->rtm_type == RTM_ADD) && ((rtm->rtm_flags & RTF_LLINFO) == 0))
 			break;
 	}
 	/* This should be a message for the host route */
 
 	verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&c->addr6, NULL, NULL);
 	rtsock_validate_pid_kernel(rtm);
 	/* No netmask should be set */
 	RTSOCK_ATF_REQUIRE_MSG(rtm, rtsock_find_rtm_sa(rtm, RTA_NETMASK) == NULL, "netmask is set");
 
 	/* gateway should be link sdl with ifindex of an address interface */
 	verify_link_gateway(rtm, c->ifindex);
 
 	int expected_rt_flags = RTF_UP | RTF_HOST | RTF_DONE | RTF_STATIC | RTF_PINNED;
 	verify_route_message_extra(rtm, if_nametoindex("lo0"), expected_rt_flags);
 }
 
 RTM_DECLARE_ROOT_TEST(rtm_add_v6_gu_ifa_prefixroute_success,
     "Tests validness for the prefix route announce after ifaddr assignment");
 
 ATF_TC_BODY(rtm_add_v6_gu_ifa_prefixroute_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	c = presetup_ipv6_iface(tc);
 
 	c->rtsock_fd = rtsock_setup_socket();
 
 	ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6);
 
 	/*
 	 * Multiple RTM_ADD messages will be generated:
 	 * 1) lladdr mapping (RTF_LLDATA)
 	 * 2) host route (one w/o netmask)
 	 * 3) prefix route
 	 */
 
 	while (true) {
 		rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
 		/* Find RTM_ADD with netmask - this should skip both host route and LLADDR */
 		if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK)))
 			break;
 	}
 
 	/* This should be a message for the prefix route */
 	verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&c->net6,
 	    (struct sockaddr *)&c->mask6, NULL);
 
 	/* gateway should be link sdl with ifindex of an address interface */
 	verify_link_gateway(rtm, c->ifindex);
 
-	/* TODO: PINNED? */
-	int expected_rt_flags = RTF_UP | RTF_DONE;
+	int expected_rt_flags = RTF_UP | RTF_DONE | RTF_PINNED;
 	verify_route_message_extra(rtm, c->ifindex, expected_rt_flags);
 }
 
 RTM_DECLARE_ROOT_TEST(rtm_add_v6_gu_ifa_ordered_success,
     "Tests ordering of the messages for IPv6 global unicast ifaddr assignment");
 
 ATF_TC_BODY(rtm_add_v6_gu_ifa_ordered_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	c = presetup_ipv6_iface(tc);
 
 	c->rtsock_fd = rtsock_setup_socket();
 
 	ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6);
 
 	int count = 0, tries = 0;
 
 	enum msgtype {
 		MSG_IFADDR,
 		MSG_HOSTROUTE,
 		MSG_PREFIXROUTE,
 		MSG_MAX,
 	};
 
 	int msg_array[MSG_MAX];
 
 	bzero(msg_array, sizeof(msg_array));
 
 	while (count < 3 && tries < 20) {
 		rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
 		tries++;
 		/* Classify */
 		if (rtm->rtm_type == RTM_NEWADDR) {
 			RLOG("MSG_IFADDR: %d", count);
 			msg_array[MSG_IFADDR] = count++;
 			continue;
 		}
 
 		/* Find RTM_ADD with netmask - this should skip both host route and LLADDR */
 		if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) {
 			RLOG("MSG_PREFIXROUTE: %d", count);
 			msg_array[MSG_PREFIXROUTE] = count++;
 			continue;
 		}
 
 		if ((rtm->rtm_type == RTM_ADD) && ((rtm->rtm_flags & RTF_LLDATA) == 0)) {
 			RLOG("MSG_HOSTROUTE: %d", count);
 			msg_array[MSG_HOSTROUTE] = count++;
 			continue;
 		}
 
 		RLOG("skipping msg type %s, try: %d", rtsock_print_cmdtype(rtm->rtm_type),
 		    tries);
 	}
 
 	/* TODO: verify multicast */
 	ATF_REQUIRE_MSG(count == 3, "Received only %d/3 messages", count);
 	ATF_REQUIRE_MSG(msg_array[MSG_IFADDR] == 0, "ifaddr message is not the first");
 }
 
 RTM_DECLARE_ROOT_TEST(rtm_del_v6_gu_ifa_hostroute_success,
     "Tests validness for /128 host route removal after ifaddr removal");
 
 ATF_TC_BODY(rtm_del_v6_gu_ifa_hostroute_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	c = presetup_ipv6_iface(tc);
 
 	ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6);
 
 	c->rtsock_fd = rtsock_setup_socket();
 
 	ret = iface_delete_addr(c->ifname, c->addr6_str);
 
 	while (true) {
 		rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
 		if ((rtm->rtm_type == RTM_DELETE) &&
 		    ((rtm->rtm_flags & RTF_LLINFO) == 0) &&
 		    rtsock_find_rtm_sa(rtm, RTA_NETMASK) == NULL)
 			break;
 	}
 	/* This should be a message for the host route */
 
 	verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&c->addr6, NULL, NULL);
 	rtsock_validate_pid_kernel(rtm);
 	/* No netmask should be set */
 	RTSOCK_ATF_REQUIRE_MSG(rtm, rtsock_find_rtm_sa(rtm, RTA_NETMASK) == NULL, "netmask is set");
 
 	/* gateway should be link sdl with ifindex of an address interface */
 	verify_link_gateway(rtm, c->ifindex);
 
 	/* XXX: consider passing ifindex in rtm_index as done in RTM_ADD. */
 	int expected_rt_flags = RTF_HOST | RTF_DONE | RTF_STATIC | RTF_PINNED;
 	RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_flags == expected_rt_flags,
 	    "expected rtm flags: 0x%X, got 0x%X", expected_rt_flags, rtm->rtm_flags);
 }
 
 RTM_DECLARE_ROOT_TEST(rtm_del_v6_gu_ifa_prefixroute_success,
     "Tests validness for the prefix route removal after ifaddr assignment");
 
 ATF_TC_BODY(rtm_del_v6_gu_ifa_prefixroute_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	c = presetup_ipv6_iface(tc);
 
 	ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6);
 
 	c->rtsock_fd = rtsock_setup_socket();
 
 	ret = iface_delete_addr(c->ifname, c->addr6_str);
 
 	while (true) {
 		rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
 		/* Find RTM_DELETE with netmask - this should skip both host route and LLADDR */
 		if ((rtm->rtm_type == RTM_DELETE) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK)))
 			break;
 	}
 
 	/* This should be a message for the prefix route */
 	verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&c->net6,
 	    (struct sockaddr *)&c->mask6, NULL);
 
 	/* gateway should be link sdl with ifindex of an address interface */
 	verify_link_gateway(rtm, c->ifindex);
 
-	int expected_rt_flags = RTF_DONE;
+	int expected_rt_flags = RTF_DONE | RTF_PINNED;
 	verify_route_message_extra(rtm, c->ifindex, expected_rt_flags);
 }
 
 RTM_DECLARE_ROOT_TEST(rtm_add_v4_gu_ifa_prefixroute_success,
     "Tests validness for the prefix route announce after ifaddr assignment");
 
 ATF_TC_BODY(rtm_add_v4_gu_ifa_prefixroute_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	c = presetup_ipv4_iface(tc);
 
 	c->rtsock_fd = rtsock_setup_socket();
 
 	ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6);
 
 	/*
 	 * Multiple RTM_ADD messages will be generated:
 	 * 1) lladdr mapping (RTF_LLDATA)
 	 * 3) prefix route
 	 */
 
 	while (true) {
 		rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
 		/* Find RTM_ADD with netmask - this should skip both host route and LLADDR */
 		if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK)))
 			break;
 	}
 
 	/* This should be a message for the prefix route */
 	verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&c->net4,
 	    (struct sockaddr *)&c->mask4, NULL);
 
 	/* gateway should be link sdl with ifindex of an address interface */
 	verify_link_gateway(rtm, c->ifindex);
 
 	int expected_rt_flags = RTF_UP | RTF_DONE | RTF_PINNED;
 	verify_route_message_extra(rtm, c->ifindex, expected_rt_flags);
 }
 
 RTM_DECLARE_ROOT_TEST(rtm_add_v4_gu_ifa_ordered_success,
     "Tests ordering of the messages for IPv4 unicast ifaddr assignment");
 
 ATF_TC_BODY(rtm_add_v4_gu_ifa_ordered_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	c = presetup_ipv4_iface(tc);
 
 	c->rtsock_fd = rtsock_setup_socket();
 
 	ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4);
 
 	int count = 0, tries = 0;
 
 	enum msgtype {
 		MSG_IFADDR,
 		MSG_PREFIXROUTE,
 		MSG_MAX,
 	};
 
 	int msg_array[MSG_MAX];
 
 	bzero(msg_array, sizeof(msg_array));
 
 	while (count < 2 && tries < 20) {
 		rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
 		tries++;
 		/* Classify */
 		if (rtm->rtm_type == RTM_NEWADDR) {
 			RLOG("MSG_IFADDR: %d", count);
 			msg_array[MSG_IFADDR] = count++;
 			continue;
 		}
 
 		/* Find RTM_ADD with netmask - this should skip both host route and LLADDR */
 		if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) {
 			RLOG("MSG_PREFIXROUTE: %d", count);
 			msg_array[MSG_PREFIXROUTE] = count++;
 			continue;
 		}
 
 		RLOG("skipping msg type %s, try: %d", rtsock_print_cmdtype(rtm->rtm_type),
 		    tries);
 	}
 
 	/* TODO: verify multicast */
 	ATF_REQUIRE_MSG(count == 2, "Received only %d/2 messages", count);
 	ATF_REQUIRE_MSG(msg_array[MSG_IFADDR] == 0, "ifaddr message is not the first");
 }
 
 RTM_DECLARE_ROOT_TEST(rtm_del_v4_gu_ifa_prefixroute_success,
     "Tests validness for the prefix route removal after ifaddr assignment");
 
 ATF_TC_BODY(rtm_del_v4_gu_ifa_prefixroute_success, tc)
 {
 	DECLARE_TEST_VARS;
 
 	c = presetup_ipv4_iface(tc);
 
 
 	ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4);
 
 	c->rtsock_fd = rtsock_setup_socket();
 
 	ret = iface_delete_addr(c->ifname, c->addr4_str);
 
 	while (true) {
 		rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
 		/* Find RTM_ADD with netmask - this should skip both host route and LLADDR */
 		if ((rtm->rtm_type == RTM_DELETE) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK)))
 			break;
 	}
 
 	/* This should be a message for the prefix route */
 	verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&c->net4,
 	    (struct sockaddr *)&c->mask4, NULL);
 
 	/* gateway should be link sdl with ifindex of an address interface */
 	verify_link_gateway(rtm, c->ifindex);
 
 	int expected_rt_flags = RTF_DONE | RTF_PINNED;
 	verify_route_message_extra(rtm, c->ifindex, expected_rt_flags);
 }
 
 
 ATF_TP_ADD_TCS(tp)
 {
 	ATF_TP_ADD_TC(tp, rtm_get_v4_exact_success);
 	ATF_TP_ADD_TC(tp, rtm_get_v4_lpm_success);
 	ATF_TP_ADD_TC(tp, rtm_get_v4_hostbits_failure);
 	ATF_TP_ADD_TC(tp, rtm_get_v4_empty_dst_failure);
 	ATF_TP_ADD_TC(tp, rtm_add_v4_no_rtf_host_failure);
 	ATF_TP_ADD_TC(tp, rtm_add_v4_gw_direct_success);
 	ATF_TP_ADD_TC(tp, rtm_del_v4_prefix_nogw_success);
 	ATF_TP_ADD_TC(tp, rtm_add_v6_gu_gw_gu_direct_success);
 	ATF_TP_ADD_TC(tp, rtm_del_v6_gu_prefix_nogw_success);
 	ATF_TP_ADD_TC(tp, rtm_change_v4_gw_success);
 	ATF_TP_ADD_TC(tp, rtm_change_v4_mtu_success);
 	ATF_TP_ADD_TC(tp, rtm_change_v4_flags_success);
 	ATF_TP_ADD_TC(tp, rtm_change_v6_gw_success);
 	ATF_TP_ADD_TC(tp, rtm_change_v6_mtu_success);
 	ATF_TP_ADD_TC(tp, rtm_change_v6_flags_success);
 	/* ifaddr tests */
 	ATF_TP_ADD_TC(tp, rtm_add_v6_gu_ifa_hostroute_success);
 	ATF_TP_ADD_TC(tp, rtm_add_v6_gu_ifa_prefixroute_success);
 	ATF_TP_ADD_TC(tp, rtm_add_v6_gu_ifa_ordered_success);
 	ATF_TP_ADD_TC(tp, rtm_del_v6_gu_ifa_hostroute_success);
 	ATF_TP_ADD_TC(tp, rtm_del_v6_gu_ifa_prefixroute_success);
 	ATF_TP_ADD_TC(tp, rtm_add_v4_gu_ifa_ordered_success);
 	ATF_TP_ADD_TC(tp, rtm_del_v4_gu_ifa_prefixroute_success);
 	/* temporal routes */
 	ATF_TP_ADD_TC(tp, rtm_add_v4_temporal1_success);
 	ATF_TP_ADD_TC(tp, rtm_add_v6_temporal1_success);
 
 	return (atf_no_error());
 }