1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate #include "defs.h"
30*7c478bd9Sstevel@tonic-gate #include "tables.h"
31*7c478bd9Sstevel@tonic-gate 
32*7c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
33*7c478bd9Sstevel@tonic-gate 
34*7c478bd9Sstevel@tonic-gate static boolean_t verify_opt_len(struct nd_opt_hdr *opt, int optlen,
35*7c478bd9Sstevel@tonic-gate 		    struct phyint *pi, struct sockaddr_in6 *from);
36*7c478bd9Sstevel@tonic-gate 
37*7c478bd9Sstevel@tonic-gate static void	incoming_rs(struct phyint *pi, struct nd_router_solicit *rs,
38*7c478bd9Sstevel@tonic-gate 		    int len, struct sockaddr_in6 *from);
39*7c478bd9Sstevel@tonic-gate 
40*7c478bd9Sstevel@tonic-gate void		incoming_ra(struct phyint *pi, struct nd_router_advert *ra,
41*7c478bd9Sstevel@tonic-gate 		    int len, struct sockaddr_in6 *from, boolean_t loopback);
42*7c478bd9Sstevel@tonic-gate static void	incoming_prefix_opt(struct phyint *pi, uchar_t *opt,
43*7c478bd9Sstevel@tonic-gate 		    struct sockaddr_in6 *from, boolean_t loopback);
44*7c478bd9Sstevel@tonic-gate static void	incoming_prefix_onlink(struct phyint *pi, uchar_t *opt,
45*7c478bd9Sstevel@tonic-gate 		    struct sockaddr_in6 *from, boolean_t loopback);
46*7c478bd9Sstevel@tonic-gate void		incoming_prefix_onlink_process(struct prefix *pr,
47*7c478bd9Sstevel@tonic-gate 		    uchar_t *opt);
48*7c478bd9Sstevel@tonic-gate static boolean_t	incoming_prefix_addrconf(struct phyint *pi,
49*7c478bd9Sstevel@tonic-gate 		    uchar_t *opt, struct sockaddr_in6 *from,
50*7c478bd9Sstevel@tonic-gate 		    boolean_t loopback);
51*7c478bd9Sstevel@tonic-gate boolean_t	incoming_prefix_addrconf_process(struct phyint *pi,
52*7c478bd9Sstevel@tonic-gate 		    struct prefix *pr, uchar_t *opt,
53*7c478bd9Sstevel@tonic-gate 		    struct sockaddr_in6 *from, boolean_t loopback,
54*7c478bd9Sstevel@tonic-gate 		    boolean_t new_prefix);
55*7c478bd9Sstevel@tonic-gate static void	incoming_mtu_opt(struct phyint *pi, uchar_t *opt,
56*7c478bd9Sstevel@tonic-gate 		    struct sockaddr_in6 *from);
57*7c478bd9Sstevel@tonic-gate static void	incoming_lla_opt(struct phyint *pi, uchar_t *opt,
58*7c478bd9Sstevel@tonic-gate 		    struct sockaddr_in6 *from, int isrouter);
59*7c478bd9Sstevel@tonic-gate 
60*7c478bd9Sstevel@tonic-gate static void	verify_ra_consistency(struct phyint *pi,
61*7c478bd9Sstevel@tonic-gate 		    struct nd_router_advert *ra,
62*7c478bd9Sstevel@tonic-gate 		    int len, struct sockaddr_in6 *from);
63*7c478bd9Sstevel@tonic-gate static void	verify_prefix_opt(struct phyint *pi, uchar_t *opt,
64*7c478bd9Sstevel@tonic-gate 		    char *frombuf);
65*7c478bd9Sstevel@tonic-gate static void	verify_mtu_opt(struct phyint *pi, uchar_t *opt,
66*7c478bd9Sstevel@tonic-gate 		    char *frombuf);
67*7c478bd9Sstevel@tonic-gate 
68*7c478bd9Sstevel@tonic-gate static uint_t	ra_flags;	/* Global to detect when to trigger DHCP */
69*7c478bd9Sstevel@tonic-gate 
70*7c478bd9Sstevel@tonic-gate /*
71*7c478bd9Sstevel@tonic-gate  * Return a pointer to the specified option buffer.
72*7c478bd9Sstevel@tonic-gate  * If not found return NULL.
73*7c478bd9Sstevel@tonic-gate  */
74*7c478bd9Sstevel@tonic-gate static void *
75*7c478bd9Sstevel@tonic-gate find_ancillary(struct msghdr *msg, int cmsg_type)
76*7c478bd9Sstevel@tonic-gate {
77*7c478bd9Sstevel@tonic-gate 	struct cmsghdr *cmsg;
78*7c478bd9Sstevel@tonic-gate 
79*7c478bd9Sstevel@tonic-gate 	for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
80*7c478bd9Sstevel@tonic-gate 	    cmsg = CMSG_NXTHDR(msg, cmsg)) {
81*7c478bd9Sstevel@tonic-gate 		if (cmsg->cmsg_level == IPPROTO_IPV6 &&
82*7c478bd9Sstevel@tonic-gate 		    cmsg->cmsg_type == cmsg_type) {
83*7c478bd9Sstevel@tonic-gate 			return (CMSG_DATA(cmsg));
84*7c478bd9Sstevel@tonic-gate 		}
85*7c478bd9Sstevel@tonic-gate 	}
86*7c478bd9Sstevel@tonic-gate 	return (NULL);
87*7c478bd9Sstevel@tonic-gate }
88*7c478bd9Sstevel@tonic-gate 
89*7c478bd9Sstevel@tonic-gate void
90*7c478bd9Sstevel@tonic-gate in_data(struct phyint *pi)
91*7c478bd9Sstevel@tonic-gate {
92*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 from;
93*7c478bd9Sstevel@tonic-gate 	struct icmp6_hdr *icmp;
94*7c478bd9Sstevel@tonic-gate 	struct nd_router_solicit *rs;
95*7c478bd9Sstevel@tonic-gate 	struct nd_router_advert *ra;
96*7c478bd9Sstevel@tonic-gate 	static uint64_t in_packet[(IP_MAXPACKET + 1)/8];
97*7c478bd9Sstevel@tonic-gate 	static uint64_t ancillary_data[(IP_MAXPACKET + 1)/8];
98*7c478bd9Sstevel@tonic-gate 	int len;
99*7c478bd9Sstevel@tonic-gate 	char abuf[INET6_ADDRSTRLEN];
100*7c478bd9Sstevel@tonic-gate 	const char *msgbuf;
101*7c478bd9Sstevel@tonic-gate 	struct msghdr msg;
102*7c478bd9Sstevel@tonic-gate 	struct iovec iov;
103*7c478bd9Sstevel@tonic-gate 	uchar_t *opt;
104*7c478bd9Sstevel@tonic-gate 	uint_t hoplimit;
105*7c478bd9Sstevel@tonic-gate 
106*7c478bd9Sstevel@tonic-gate 	iov.iov_base = (char *)in_packet;
107*7c478bd9Sstevel@tonic-gate 	iov.iov_len = sizeof (in_packet);
108*7c478bd9Sstevel@tonic-gate 	msg.msg_iov = &iov;
109*7c478bd9Sstevel@tonic-gate 	msg.msg_iovlen = 1;
110*7c478bd9Sstevel@tonic-gate 	msg.msg_name = (struct sockaddr *)&from;
111*7c478bd9Sstevel@tonic-gate 	msg.msg_namelen = sizeof (from);
112*7c478bd9Sstevel@tonic-gate 	msg.msg_control = ancillary_data;
113*7c478bd9Sstevel@tonic-gate 	msg.msg_controllen = sizeof (ancillary_data);
114*7c478bd9Sstevel@tonic-gate 
115*7c478bd9Sstevel@tonic-gate 	if ((len = recvmsg(pi->pi_sock, &msg, 0)) < 0) {
116*7c478bd9Sstevel@tonic-gate 		logperror_pi(pi, "in_data: recvfrom");
117*7c478bd9Sstevel@tonic-gate 		return;
118*7c478bd9Sstevel@tonic-gate 	}
119*7c478bd9Sstevel@tonic-gate 	if (len == 0)
120*7c478bd9Sstevel@tonic-gate 		return;
121*7c478bd9Sstevel@tonic-gate 
122*7c478bd9Sstevel@tonic-gate 	if (inet_ntop(AF_INET6, (void *)&from.sin6_addr,
123*7c478bd9Sstevel@tonic-gate 	    abuf, sizeof (abuf)) == NULL)
124*7c478bd9Sstevel@tonic-gate 		msgbuf = "Unspecified Router";
125*7c478bd9Sstevel@tonic-gate 	else
126*7c478bd9Sstevel@tonic-gate 		msgbuf = abuf;
127*7c478bd9Sstevel@tonic-gate 
128*7c478bd9Sstevel@tonic-gate 	/* Ignore packets > 64k or control buffers that don't fit */
129*7c478bd9Sstevel@tonic-gate 	if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
130*7c478bd9Sstevel@tonic-gate 		if (debug & D_PKTBAD) {
131*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_DEBUG, "Truncated message: msg_flags 0x%x "
132*7c478bd9Sstevel@tonic-gate 			    "from %s\n", msg.msg_flags, msgbuf);
133*7c478bd9Sstevel@tonic-gate 		}
134*7c478bd9Sstevel@tonic-gate 		return;
135*7c478bd9Sstevel@tonic-gate 	}
136*7c478bd9Sstevel@tonic-gate 
137*7c478bd9Sstevel@tonic-gate 	icmp = (struct icmp6_hdr *)in_packet;
138*7c478bd9Sstevel@tonic-gate 
139*7c478bd9Sstevel@tonic-gate 	if (len < ICMP6_MINLEN) {
140*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "Too short ICMP packet: %d bytes "
141*7c478bd9Sstevel@tonic-gate 		    "from %s on %s\n",
142*7c478bd9Sstevel@tonic-gate 		    len, msgbuf, pi->pi_name);
143*7c478bd9Sstevel@tonic-gate 		return;
144*7c478bd9Sstevel@tonic-gate 	}
145*7c478bd9Sstevel@tonic-gate 
146*7c478bd9Sstevel@tonic-gate 	opt = find_ancillary(&msg, IPV6_HOPLIMIT);
147*7c478bd9Sstevel@tonic-gate 	if (opt == NULL) {
148*7c478bd9Sstevel@tonic-gate 		/* Unknown hoplimit - must drop */
149*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "Unknown hop limit from %s on %s\n",
150*7c478bd9Sstevel@tonic-gate 		    msgbuf, pi->pi_name);
151*7c478bd9Sstevel@tonic-gate 		return;
152*7c478bd9Sstevel@tonic-gate 	}
153*7c478bd9Sstevel@tonic-gate 	hoplimit = *(uint_t *)opt;
154*7c478bd9Sstevel@tonic-gate 	opt = find_ancillary(&msg, IPV6_RTHDR);
155*7c478bd9Sstevel@tonic-gate 	if (opt != NULL) {
156*7c478bd9Sstevel@tonic-gate 		/* Can't allow routing headers in ND messages */
157*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "ND message with routing header "
158*7c478bd9Sstevel@tonic-gate 		    "from %s on %s\n",
159*7c478bd9Sstevel@tonic-gate 		    msgbuf, pi->pi_name);
160*7c478bd9Sstevel@tonic-gate 		return;
161*7c478bd9Sstevel@tonic-gate 	}
162*7c478bd9Sstevel@tonic-gate 	switch (icmp->icmp6_type) {
163*7c478bd9Sstevel@tonic-gate 	case ND_ROUTER_SOLICIT:
164*7c478bd9Sstevel@tonic-gate 		if (!pi->pi_AdvSendAdvertisements)
165*7c478bd9Sstevel@tonic-gate 			return;
166*7c478bd9Sstevel@tonic-gate 		if (pi->pi_flags & IFF_NORTEXCH) {
167*7c478bd9Sstevel@tonic-gate 			if (debug & D_PKTIN) {
168*7c478bd9Sstevel@tonic-gate 				logmsg(LOG_DEBUG, "Ignore received RS packet "
169*7c478bd9Sstevel@tonic-gate 				    "on %s (no route exchange on interface)\n",
170*7c478bd9Sstevel@tonic-gate 				    pi->pi_name);
171*7c478bd9Sstevel@tonic-gate 			}
172*7c478bd9Sstevel@tonic-gate 			return;
173*7c478bd9Sstevel@tonic-gate 		}
174*7c478bd9Sstevel@tonic-gate 
175*7c478bd9Sstevel@tonic-gate 		/*
176*7c478bd9Sstevel@tonic-gate 		 * Assumes that the kernel has verified the AH (if present)
177*7c478bd9Sstevel@tonic-gate 		 * and the ICMP checksum.
178*7c478bd9Sstevel@tonic-gate 		 */
179*7c478bd9Sstevel@tonic-gate 		if (hoplimit != IPV6_MAX_HOPS) {
180*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RS hop limit: %d from %s on %s\n",
181*7c478bd9Sstevel@tonic-gate 			    hoplimit, msgbuf, pi->pi_name);
182*7c478bd9Sstevel@tonic-gate 			return;
183*7c478bd9Sstevel@tonic-gate 		}
184*7c478bd9Sstevel@tonic-gate 
185*7c478bd9Sstevel@tonic-gate 		if (icmp->icmp6_code != 0) {
186*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RS code: %d from %s on %s\n",
187*7c478bd9Sstevel@tonic-gate 			    icmp->icmp6_code, msgbuf, pi->pi_name);
188*7c478bd9Sstevel@tonic-gate 			return;
189*7c478bd9Sstevel@tonic-gate 		}
190*7c478bd9Sstevel@tonic-gate 
191*7c478bd9Sstevel@tonic-gate 		if (len < sizeof (struct nd_router_solicit)) {
192*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RS too short: %d bytes "
193*7c478bd9Sstevel@tonic-gate 			    "from %s on %s\n",
194*7c478bd9Sstevel@tonic-gate 			    len, msgbuf, pi->pi_name);
195*7c478bd9Sstevel@tonic-gate 			return;
196*7c478bd9Sstevel@tonic-gate 		}
197*7c478bd9Sstevel@tonic-gate 		rs = (struct nd_router_solicit *)icmp;
198*7c478bd9Sstevel@tonic-gate 		if (len > sizeof (struct nd_router_solicit)) {
199*7c478bd9Sstevel@tonic-gate 			if (!verify_opt_len((struct nd_opt_hdr *)&rs[1],
200*7c478bd9Sstevel@tonic-gate 			    len - sizeof (struct nd_router_solicit), pi, &from))
201*7c478bd9Sstevel@tonic-gate 				return;
202*7c478bd9Sstevel@tonic-gate 		}
203*7c478bd9Sstevel@tonic-gate 		if (debug & D_PKTIN) {
204*7c478bd9Sstevel@tonic-gate 			print_route_sol("Received valid solicit from ", pi,
205*7c478bd9Sstevel@tonic-gate 			    rs, len, &from);
206*7c478bd9Sstevel@tonic-gate 		}
207*7c478bd9Sstevel@tonic-gate 		incoming_rs(pi, rs, len, &from);
208*7c478bd9Sstevel@tonic-gate 		break;
209*7c478bd9Sstevel@tonic-gate 
210*7c478bd9Sstevel@tonic-gate 	case ND_ROUTER_ADVERT:
211*7c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr)) {
212*7c478bd9Sstevel@tonic-gate 			/*
213*7c478bd9Sstevel@tonic-gate 			 * Router advt. must have address!
214*7c478bd9Sstevel@tonic-gate 			 * Logging the news and returning.
215*7c478bd9Sstevel@tonic-gate 			 */
216*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_DEBUG,
217*7c478bd9Sstevel@tonic-gate 			    "Router's address unspecified in advertisement\n");
218*7c478bd9Sstevel@tonic-gate 			return;
219*7c478bd9Sstevel@tonic-gate 		}
220*7c478bd9Sstevel@tonic-gate 		if (pi->pi_flags & IFF_NORTEXCH) {
221*7c478bd9Sstevel@tonic-gate 			if (debug & D_PKTIN) {
222*7c478bd9Sstevel@tonic-gate 				logmsg(LOG_DEBUG, "Ignore received RA packet "
223*7c478bd9Sstevel@tonic-gate 				    "on %s (no route exchange on interface)\n",
224*7c478bd9Sstevel@tonic-gate 				    pi->pi_name);
225*7c478bd9Sstevel@tonic-gate 			}
226*7c478bd9Sstevel@tonic-gate 			return;
227*7c478bd9Sstevel@tonic-gate 		}
228*7c478bd9Sstevel@tonic-gate 
229*7c478bd9Sstevel@tonic-gate 		/*
230*7c478bd9Sstevel@tonic-gate 		 * Assumes that the kernel has verified the AH (if present)
231*7c478bd9Sstevel@tonic-gate 		 * and the ICMP checksum.
232*7c478bd9Sstevel@tonic-gate 		 */
233*7c478bd9Sstevel@tonic-gate 		if (!IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) {
234*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RA from %s - not link local on %s\n",
235*7c478bd9Sstevel@tonic-gate 			    msgbuf, pi->pi_name);
236*7c478bd9Sstevel@tonic-gate 			return;
237*7c478bd9Sstevel@tonic-gate 		}
238*7c478bd9Sstevel@tonic-gate 
239*7c478bd9Sstevel@tonic-gate 		if (hoplimit != IPV6_MAX_HOPS) {
240*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RA hop limit: %d from %s on %s\n",
241*7c478bd9Sstevel@tonic-gate 			    hoplimit, msgbuf, pi->pi_name);
242*7c478bd9Sstevel@tonic-gate 			return;
243*7c478bd9Sstevel@tonic-gate 		}
244*7c478bd9Sstevel@tonic-gate 
245*7c478bd9Sstevel@tonic-gate 		if (icmp->icmp6_code != 0) {
246*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RA code: %d from %s on %s\n",
247*7c478bd9Sstevel@tonic-gate 			    icmp->icmp6_code, msgbuf, pi->pi_name);
248*7c478bd9Sstevel@tonic-gate 			return;
249*7c478bd9Sstevel@tonic-gate 		}
250*7c478bd9Sstevel@tonic-gate 
251*7c478bd9Sstevel@tonic-gate 		if (len < sizeof (struct nd_router_advert)) {
252*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RA too short: %d bytes "
253*7c478bd9Sstevel@tonic-gate 			    "from %s on %s\n",
254*7c478bd9Sstevel@tonic-gate 			    len, msgbuf, pi->pi_name);
255*7c478bd9Sstevel@tonic-gate 			return;
256*7c478bd9Sstevel@tonic-gate 		}
257*7c478bd9Sstevel@tonic-gate 		ra = (struct nd_router_advert *)icmp;
258*7c478bd9Sstevel@tonic-gate 		if (len > sizeof (struct nd_router_advert)) {
259*7c478bd9Sstevel@tonic-gate 			if (!verify_opt_len((struct nd_opt_hdr *)&ra[1],
260*7c478bd9Sstevel@tonic-gate 			    len - sizeof (struct nd_router_advert), pi, &from))
261*7c478bd9Sstevel@tonic-gate 				return;
262*7c478bd9Sstevel@tonic-gate 		}
263*7c478bd9Sstevel@tonic-gate 		if (debug & D_PKTIN) {
264*7c478bd9Sstevel@tonic-gate 			print_route_adv("Received valid advert from ", pi,
265*7c478bd9Sstevel@tonic-gate 			    ra, len, &from);
266*7c478bd9Sstevel@tonic-gate 		}
267*7c478bd9Sstevel@tonic-gate 		if (pi->pi_AdvSendAdvertisements)
268*7c478bd9Sstevel@tonic-gate 			verify_ra_consistency(pi, ra, len, &from);
269*7c478bd9Sstevel@tonic-gate 		else
270*7c478bd9Sstevel@tonic-gate 			incoming_ra(pi, ra, len, &from, _B_FALSE);
271*7c478bd9Sstevel@tonic-gate 		break;
272*7c478bd9Sstevel@tonic-gate 	}
273*7c478bd9Sstevel@tonic-gate }
274*7c478bd9Sstevel@tonic-gate 
275*7c478bd9Sstevel@tonic-gate /*
276*7c478bd9Sstevel@tonic-gate  * Process a received router solicitation.
277*7c478bd9Sstevel@tonic-gate  * Check for source link-layer address option and check if it
278*7c478bd9Sstevel@tonic-gate  * is time to advertise.
279*7c478bd9Sstevel@tonic-gate  */
280*7c478bd9Sstevel@tonic-gate static void
281*7c478bd9Sstevel@tonic-gate incoming_rs(struct phyint *pi, struct nd_router_solicit *rs, int len,
282*7c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from)
283*7c478bd9Sstevel@tonic-gate {
284*7c478bd9Sstevel@tonic-gate 	struct nd_opt_hdr *opt;
285*7c478bd9Sstevel@tonic-gate 	int optlen;
286*7c478bd9Sstevel@tonic-gate 
287*7c478bd9Sstevel@tonic-gate 	/* Process any options */
288*7c478bd9Sstevel@tonic-gate 	len -= sizeof (struct nd_router_solicit);
289*7c478bd9Sstevel@tonic-gate 	opt = (struct nd_opt_hdr *)&rs[1];
290*7c478bd9Sstevel@tonic-gate 	while (len >= sizeof (struct nd_opt_hdr)) {
291*7c478bd9Sstevel@tonic-gate 		optlen = opt->nd_opt_len * 8;
292*7c478bd9Sstevel@tonic-gate 		switch (opt->nd_opt_type) {
293*7c478bd9Sstevel@tonic-gate 		case ND_OPT_SOURCE_LINKADDR:
294*7c478bd9Sstevel@tonic-gate 			incoming_lla_opt(pi, (uchar_t *)opt,
295*7c478bd9Sstevel@tonic-gate 			    from, NDF_ISROUTER_OFF);
296*7c478bd9Sstevel@tonic-gate 			break;
297*7c478bd9Sstevel@tonic-gate 		default:
298*7c478bd9Sstevel@tonic-gate 			break;
299*7c478bd9Sstevel@tonic-gate 		}
300*7c478bd9Sstevel@tonic-gate 		opt = (struct nd_opt_hdr *)((char *)opt + optlen);
301*7c478bd9Sstevel@tonic-gate 		len -= optlen;
302*7c478bd9Sstevel@tonic-gate 	}
303*7c478bd9Sstevel@tonic-gate 	/* Simple algorithm: treat unicast and multicast RSs the same */
304*7c478bd9Sstevel@tonic-gate 	check_to_advertise(pi, RECEIVED_SOLICIT);
305*7c478bd9Sstevel@tonic-gate }
306*7c478bd9Sstevel@tonic-gate 
307*7c478bd9Sstevel@tonic-gate /*
308*7c478bd9Sstevel@tonic-gate  * Process a received router advertisement.
309*7c478bd9Sstevel@tonic-gate  * Called both when packets arrive as well as when we send RAs.
310*7c478bd9Sstevel@tonic-gate  * In the latter case 'loopback' is set.
311*7c478bd9Sstevel@tonic-gate  */
312*7c478bd9Sstevel@tonic-gate void
313*7c478bd9Sstevel@tonic-gate incoming_ra(struct phyint *pi, struct nd_router_advert *ra, int len,
314*7c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from, boolean_t loopback)
315*7c478bd9Sstevel@tonic-gate {
316*7c478bd9Sstevel@tonic-gate 	struct nd_opt_hdr *opt;
317*7c478bd9Sstevel@tonic-gate 	int optlen;
318*7c478bd9Sstevel@tonic-gate 	struct lifreq lifr;
319*7c478bd9Sstevel@tonic-gate 	boolean_t set_needed = _B_FALSE;
320*7c478bd9Sstevel@tonic-gate 	struct router *dr;
321*7c478bd9Sstevel@tonic-gate 	uint16_t router_lifetime;
322*7c478bd9Sstevel@tonic-gate 	uint_t reachable, retrans;
323*7c478bd9Sstevel@tonic-gate 	boolean_t reachable_time_changed = _B_FALSE;
324*7c478bd9Sstevel@tonic-gate 
325*7c478bd9Sstevel@tonic-gate 	if (no_loopback && loopback)
326*7c478bd9Sstevel@tonic-gate 		return;
327*7c478bd9Sstevel@tonic-gate 
328*7c478bd9Sstevel@tonic-gate 	/*
329*7c478bd9Sstevel@tonic-gate 	 * If the interface is FAILED or INACTIVE or OFFLINE, don't
330*7c478bd9Sstevel@tonic-gate 	 * create any addresses on them. in.mpathd assumes that no new
331*7c478bd9Sstevel@tonic-gate 	 * addresses will appear on these. This implies that we
332*7c478bd9Sstevel@tonic-gate 	 * won't create any new prefixes advertised by the router
333*7c478bd9Sstevel@tonic-gate 	 * on FAILED/INACTIVE/OFFLINE interfaces. When the state changes,
334*7c478bd9Sstevel@tonic-gate 	 * the next RA will create the prefix on this interface.
335*7c478bd9Sstevel@tonic-gate 	 */
336*7c478bd9Sstevel@tonic-gate 	if (pi->pi_flags & (IFF_FAILED|IFF_INACTIVE|IFF_OFFLINE))
337*7c478bd9Sstevel@tonic-gate 		return;
338*7c478bd9Sstevel@tonic-gate 
339*7c478bd9Sstevel@tonic-gate 	(void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
340*7c478bd9Sstevel@tonic-gate 	lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
341*7c478bd9Sstevel@tonic-gate 	if (ioctl(pi->pi_sock, SIOCGLIFLNKINFO, (char *)&lifr) < 0) {
342*7c478bd9Sstevel@tonic-gate 		if (errno == ENXIO)
343*7c478bd9Sstevel@tonic-gate 			return;
344*7c478bd9Sstevel@tonic-gate 		logperror_pi(pi, "incoming_ra: SIOCGLIFLNKINFO");
345*7c478bd9Sstevel@tonic-gate 		return;
346*7c478bd9Sstevel@tonic-gate 	}
347*7c478bd9Sstevel@tonic-gate 	if (ra->nd_ra_curhoplimit != pi->pi_CurHopLimit) {
348*7c478bd9Sstevel@tonic-gate 		pi->pi_CurHopLimit = ra->nd_ra_curhoplimit;
349*7c478bd9Sstevel@tonic-gate 
350*7c478bd9Sstevel@tonic-gate 		lifr.lifr_ifinfo.lir_maxhops = pi->pi_CurHopLimit;
351*7c478bd9Sstevel@tonic-gate 		set_needed = _B_TRUE;
352*7c478bd9Sstevel@tonic-gate 	}
353*7c478bd9Sstevel@tonic-gate 
354*7c478bd9Sstevel@tonic-gate 	reachable = ntohl(ra->nd_ra_reachable);
355*7c478bd9Sstevel@tonic-gate 	if (reachable != 0 &&
356*7c478bd9Sstevel@tonic-gate 	    reachable != pi->pi_BaseReachableTime) {
357*7c478bd9Sstevel@tonic-gate 		pi->pi_BaseReachableTime = reachable;
358*7c478bd9Sstevel@tonic-gate 		reachable_time_changed = _B_TRUE;
359*7c478bd9Sstevel@tonic-gate 	}
360*7c478bd9Sstevel@tonic-gate 
361*7c478bd9Sstevel@tonic-gate 	if (pi->pi_reach_time_since_random < MIN_REACH_RANDOM_INTERVAL ||
362*7c478bd9Sstevel@tonic-gate 	    reachable_time_changed) {
363*7c478bd9Sstevel@tonic-gate 		phyint_reach_random(pi, _B_FALSE);
364*7c478bd9Sstevel@tonic-gate 		set_needed = _B_TRUE;
365*7c478bd9Sstevel@tonic-gate 	}
366*7c478bd9Sstevel@tonic-gate 	lifr.lifr_ifinfo.lir_reachtime = pi->pi_ReachableTime;
367*7c478bd9Sstevel@tonic-gate 
368*7c478bd9Sstevel@tonic-gate 	retrans = ntohl(ra->nd_ra_retransmit);
369*7c478bd9Sstevel@tonic-gate 	if (retrans != 0 &&
370*7c478bd9Sstevel@tonic-gate 	    pi->pi_RetransTimer != retrans) {
371*7c478bd9Sstevel@tonic-gate 		pi->pi_RetransTimer = retrans;
372*7c478bd9Sstevel@tonic-gate 		lifr.lifr_ifinfo.lir_reachretrans = pi->pi_RetransTimer;
373*7c478bd9Sstevel@tonic-gate 		set_needed = _B_TRUE;
374*7c478bd9Sstevel@tonic-gate 	}
375*7c478bd9Sstevel@tonic-gate 
376*7c478bd9Sstevel@tonic-gate 	if (set_needed) {
377*7c478bd9Sstevel@tonic-gate 		if (ioctl(pi->pi_sock, SIOCSLIFLNKINFO, (char *)&lifr) < 0) {
378*7c478bd9Sstevel@tonic-gate 			logperror_pi(pi, "incoming_ra: SIOCSLIFLNKINFO");
379*7c478bd9Sstevel@tonic-gate 			return;
380*7c478bd9Sstevel@tonic-gate 		}
381*7c478bd9Sstevel@tonic-gate 	}
382*7c478bd9Sstevel@tonic-gate 
383*7c478bd9Sstevel@tonic-gate 	if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) &&
384*7c478bd9Sstevel@tonic-gate 	    !(ra_flags & ND_RA_FLAG_MANAGED)) {
385*7c478bd9Sstevel@tonic-gate 		ra_flags |= ND_RA_FLAG_MANAGED;
386*7c478bd9Sstevel@tonic-gate 		/* TODO trigger dhcpv6 */
387*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "incoming_ra: trigger dhcp MANAGED\n");
388*7c478bd9Sstevel@tonic-gate 	}
389*7c478bd9Sstevel@tonic-gate 	if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) &&
390*7c478bd9Sstevel@tonic-gate 	    !(ra_flags & ND_RA_FLAG_OTHER)) {
391*7c478bd9Sstevel@tonic-gate 		ra_flags |= ND_RA_FLAG_OTHER;
392*7c478bd9Sstevel@tonic-gate 		if (!(ra_flags & ND_RA_FLAG_MANAGED)) {
393*7c478bd9Sstevel@tonic-gate 			/* TODO trigger dhcpv6 for non-address info */
394*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "incoming_ra: trigger dhcp OTHER\n");
395*7c478bd9Sstevel@tonic-gate 		}
396*7c478bd9Sstevel@tonic-gate 	}
397*7c478bd9Sstevel@tonic-gate 	/* Skip default router code if sent from ourselves */
398*7c478bd9Sstevel@tonic-gate 	if (!loopback) {
399*7c478bd9Sstevel@tonic-gate 		/* Find and update or add default router in list */
400*7c478bd9Sstevel@tonic-gate 		dr = router_lookup(pi, from->sin6_addr);
401*7c478bd9Sstevel@tonic-gate 		router_lifetime = ntohs(ra->nd_ra_router_lifetime);
402*7c478bd9Sstevel@tonic-gate 		if (dr == NULL) {
403*7c478bd9Sstevel@tonic-gate 			if (router_lifetime != 0) {
404*7c478bd9Sstevel@tonic-gate 				dr = router_create(pi, from->sin6_addr,
405*7c478bd9Sstevel@tonic-gate 				    MILLISEC * router_lifetime);
406*7c478bd9Sstevel@tonic-gate 				timer_schedule(dr->dr_lifetime);
407*7c478bd9Sstevel@tonic-gate 			}
408*7c478bd9Sstevel@tonic-gate 		} else {
409*7c478bd9Sstevel@tonic-gate 			dr->dr_lifetime = MILLISEC * router_lifetime;
410*7c478bd9Sstevel@tonic-gate 			if (dr->dr_lifetime != 0)
411*7c478bd9Sstevel@tonic-gate 				timer_schedule(dr->dr_lifetime);
412*7c478bd9Sstevel@tonic-gate 			if ((dr->dr_lifetime != 0 && !dr->dr_inkernel) ||
413*7c478bd9Sstevel@tonic-gate 			    (dr->dr_lifetime == 0 && dr->dr_inkernel))
414*7c478bd9Sstevel@tonic-gate 				router_update_k(dr);
415*7c478bd9Sstevel@tonic-gate 		}
416*7c478bd9Sstevel@tonic-gate 	}
417*7c478bd9Sstevel@tonic-gate 	/* Process any options */
418*7c478bd9Sstevel@tonic-gate 	len -= sizeof (struct nd_router_advert);
419*7c478bd9Sstevel@tonic-gate 	opt = (struct nd_opt_hdr *)&ra[1];
420*7c478bd9Sstevel@tonic-gate 	while (len >= sizeof (struct nd_opt_hdr)) {
421*7c478bd9Sstevel@tonic-gate 		optlen = opt->nd_opt_len * 8;
422*7c478bd9Sstevel@tonic-gate 		switch (opt->nd_opt_type) {
423*7c478bd9Sstevel@tonic-gate 		case ND_OPT_PREFIX_INFORMATION:
424*7c478bd9Sstevel@tonic-gate 			incoming_prefix_opt(pi, (uchar_t *)opt, from,
425*7c478bd9Sstevel@tonic-gate 			    loopback);
426*7c478bd9Sstevel@tonic-gate 			break;
427*7c478bd9Sstevel@tonic-gate 		case ND_OPT_MTU:
428*7c478bd9Sstevel@tonic-gate 			incoming_mtu_opt(pi, (uchar_t *)opt, from);
429*7c478bd9Sstevel@tonic-gate 			break;
430*7c478bd9Sstevel@tonic-gate 		case ND_OPT_SOURCE_LINKADDR:
431*7c478bd9Sstevel@tonic-gate 			/* skip lla option if sent from ourselves! */
432*7c478bd9Sstevel@tonic-gate 			if (!loopback) {
433*7c478bd9Sstevel@tonic-gate 				incoming_lla_opt(pi, (uchar_t *)opt,
434*7c478bd9Sstevel@tonic-gate 				    from, NDF_ISROUTER_ON);
435*7c478bd9Sstevel@tonic-gate 			}
436*7c478bd9Sstevel@tonic-gate 			break;
437*7c478bd9Sstevel@tonic-gate 		default:
438*7c478bd9Sstevel@tonic-gate 			break;
439*7c478bd9Sstevel@tonic-gate 		}
440*7c478bd9Sstevel@tonic-gate 		opt = (struct nd_opt_hdr *)((char *)opt + optlen);
441*7c478bd9Sstevel@tonic-gate 		len -= optlen;
442*7c478bd9Sstevel@tonic-gate 	}
443*7c478bd9Sstevel@tonic-gate 	/* Stop sending solicitations */
444*7c478bd9Sstevel@tonic-gate 	check_to_solicit(pi, SOLICIT_DONE);
445*7c478bd9Sstevel@tonic-gate }
446*7c478bd9Sstevel@tonic-gate 
447*7c478bd9Sstevel@tonic-gate /*
448*7c478bd9Sstevel@tonic-gate  * Process a received prefix option.
449*7c478bd9Sstevel@tonic-gate  * Unless addrconf is turned off we process both the addrconf and the
450*7c478bd9Sstevel@tonic-gate  * onlink aspects of the prefix option.
451*7c478bd9Sstevel@tonic-gate  *
452*7c478bd9Sstevel@tonic-gate  * Note that when a flag (onlink or auto) is turned off we do nothing -
453*7c478bd9Sstevel@tonic-gate  * the prefix will time out.
454*7c478bd9Sstevel@tonic-gate  */
455*7c478bd9Sstevel@tonic-gate static void
456*7c478bd9Sstevel@tonic-gate incoming_prefix_opt(struct phyint *pi, uchar_t *opt,
457*7c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from, boolean_t loopback)
458*7c478bd9Sstevel@tonic-gate {
459*7c478bd9Sstevel@tonic-gate 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
460*7c478bd9Sstevel@tonic-gate 	boolean_t	good_prefix = _B_TRUE;
461*7c478bd9Sstevel@tonic-gate 
462*7c478bd9Sstevel@tonic-gate 	if (8 * po->nd_opt_pi_len != sizeof (*po)) {
463*7c478bd9Sstevel@tonic-gate 		char abuf[INET6_ADDRSTRLEN];
464*7c478bd9Sstevel@tonic-gate 
465*7c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
466*7c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf));
467*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "prefix option from %s on %s wrong size "
468*7c478bd9Sstevel@tonic-gate 		    "(%d bytes)\n",
469*7c478bd9Sstevel@tonic-gate 		    abuf, pi->pi_name,
470*7c478bd9Sstevel@tonic-gate 		    8 * (int)po->nd_opt_pi_len);
471*7c478bd9Sstevel@tonic-gate 		return;
472*7c478bd9Sstevel@tonic-gate 	}
473*7c478bd9Sstevel@tonic-gate 	if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) {
474*7c478bd9Sstevel@tonic-gate 		char abuf[INET6_ADDRSTRLEN];
475*7c478bd9Sstevel@tonic-gate 
476*7c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
477*7c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf));
478*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s contains link-local prefix "
479*7c478bd9Sstevel@tonic-gate 		    "- ignored\n",
480*7c478bd9Sstevel@tonic-gate 		    abuf, pi->pi_name);
481*7c478bd9Sstevel@tonic-gate 		return;
482*7c478bd9Sstevel@tonic-gate 	}
483*7c478bd9Sstevel@tonic-gate 	if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) &&
484*7c478bd9Sstevel@tonic-gate 	    pi->pi_StatelessAddrConf) {
485*7c478bd9Sstevel@tonic-gate 		good_prefix = incoming_prefix_addrconf(pi, opt, from, loopback);
486*7c478bd9Sstevel@tonic-gate 	}
487*7c478bd9Sstevel@tonic-gate 	if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) &&
488*7c478bd9Sstevel@tonic-gate 	    good_prefix) {
489*7c478bd9Sstevel@tonic-gate 		incoming_prefix_onlink(pi, opt, from, loopback);
490*7c478bd9Sstevel@tonic-gate 	}
491*7c478bd9Sstevel@tonic-gate }
492*7c478bd9Sstevel@tonic-gate 
493*7c478bd9Sstevel@tonic-gate /*
494*7c478bd9Sstevel@tonic-gate  * Process prefix options with the onlink flag set.
495*7c478bd9Sstevel@tonic-gate  *
496*7c478bd9Sstevel@tonic-gate  * If there are no routers ndpd will add an onlink
497*7c478bd9Sstevel@tonic-gate  * default route which will allow communication
498*7c478bd9Sstevel@tonic-gate  * between neighbors.
499*7c478bd9Sstevel@tonic-gate  *
500*7c478bd9Sstevel@tonic-gate  * This function needs to loop to find the same prefix multiple times
501*7c478bd9Sstevel@tonic-gate  * as if a failover happened earlier, the addresses belonging to
502*7c478bd9Sstevel@tonic-gate  * a different interface may be found here on this interface.
503*7c478bd9Sstevel@tonic-gate  */
504*7c478bd9Sstevel@tonic-gate /* ARGSUSED2 */
505*7c478bd9Sstevel@tonic-gate static void
506*7c478bd9Sstevel@tonic-gate incoming_prefix_onlink(struct phyint *pi, uchar_t *opt,
507*7c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from, boolean_t loopback)
508*7c478bd9Sstevel@tonic-gate {
509*7c478bd9Sstevel@tonic-gate 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
510*7c478bd9Sstevel@tonic-gate 	int plen;
511*7c478bd9Sstevel@tonic-gate 	struct prefix *pr;
512*7c478bd9Sstevel@tonic-gate 	uint32_t validtime;	/* Without 2 hour rule */
513*7c478bd9Sstevel@tonic-gate 	boolean_t found_one = _B_FALSE;
514*7c478bd9Sstevel@tonic-gate 
515*7c478bd9Sstevel@tonic-gate 	plen = po->nd_opt_pi_prefix_len;
516*7c478bd9Sstevel@tonic-gate 	for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
517*7c478bd9Sstevel@tonic-gate 		if (pr->pr_prefix_len == plen &&
518*7c478bd9Sstevel@tonic-gate 		    prefix_equal(po->nd_opt_pi_prefix, pr->pr_prefix, plen)) {
519*7c478bd9Sstevel@tonic-gate 			/* Exclude static prefixes */
520*7c478bd9Sstevel@tonic-gate 			if (pr->pr_state & PR_STATIC)
521*7c478bd9Sstevel@tonic-gate 				continue;
522*7c478bd9Sstevel@tonic-gate 			found_one = _B_TRUE;
523*7c478bd9Sstevel@tonic-gate 			incoming_prefix_onlink_process(pr, opt);
524*7c478bd9Sstevel@tonic-gate 		}
525*7c478bd9Sstevel@tonic-gate 	}
526*7c478bd9Sstevel@tonic-gate 
527*7c478bd9Sstevel@tonic-gate 	validtime = ntohl(po->nd_opt_pi_valid_time);
528*7c478bd9Sstevel@tonic-gate 	/*
529*7c478bd9Sstevel@tonic-gate 	 * If we have found a matching prefix already or validtime
530*7c478bd9Sstevel@tonic-gate 	 * is zero, we have nothing to do.
531*7c478bd9Sstevel@tonic-gate 	 */
532*7c478bd9Sstevel@tonic-gate 	if (validtime == 0 || found_one)
533*7c478bd9Sstevel@tonic-gate 		return;
534*7c478bd9Sstevel@tonic-gate 	pr = prefix_create(pi, po->nd_opt_pi_prefix, plen, 0);
535*7c478bd9Sstevel@tonic-gate 	if (pr == NULL)
536*7c478bd9Sstevel@tonic-gate 		return;
537*7c478bd9Sstevel@tonic-gate 	incoming_prefix_onlink_process(pr, opt);
538*7c478bd9Sstevel@tonic-gate }
539*7c478bd9Sstevel@tonic-gate 
540*7c478bd9Sstevel@tonic-gate void
541*7c478bd9Sstevel@tonic-gate incoming_prefix_onlink_process(struct prefix *pr, uchar_t *opt)
542*7c478bd9Sstevel@tonic-gate {
543*7c478bd9Sstevel@tonic-gate 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
544*7c478bd9Sstevel@tonic-gate 	uint32_t validtime;	/* Without 2 hour rule */
545*7c478bd9Sstevel@tonic-gate 	char abuf[INET6_ADDRSTRLEN];
546*7c478bd9Sstevel@tonic-gate 
547*7c478bd9Sstevel@tonic-gate 	validtime = ntohl(po->nd_opt_pi_valid_time);
548*7c478bd9Sstevel@tonic-gate 	if (validtime != 0)
549*7c478bd9Sstevel@tonic-gate 		pr->pr_state |= PR_ONLINK;
550*7c478bd9Sstevel@tonic-gate 	else
551*7c478bd9Sstevel@tonic-gate 		pr->pr_state &= ~PR_ONLINK;
552*7c478bd9Sstevel@tonic-gate 
553*7c478bd9Sstevel@tonic-gate 	/*
554*7c478bd9Sstevel@tonic-gate 	 * Convert from seconds to milliseconds avoiding overflow.
555*7c478bd9Sstevel@tonic-gate 	 * If the lifetime in the packet is e.g. PREFIX_INFINITY - 1
556*7c478bd9Sstevel@tonic-gate 	 * (4 billion seconds - about 130 years) we will in fact time
557*7c478bd9Sstevel@tonic-gate 	 * out the prefix after 4 billion milliseconds - 46 days).
558*7c478bd9Sstevel@tonic-gate 	 * Thus the longest lifetime (apart from infinity) is 46 days.
559*7c478bd9Sstevel@tonic-gate 	 * Note that this ensures that PREFIX_INFINITY still means "forever".
560*7c478bd9Sstevel@tonic-gate 	 */
561*7c478bd9Sstevel@tonic-gate 	if (pr->pr_flags & IFF_TEMPORARY) {
562*7c478bd9Sstevel@tonic-gate 		pr->pr_OnLinkLifetime = pr->pr_ValidLifetime;
563*7c478bd9Sstevel@tonic-gate 	} else {
564*7c478bd9Sstevel@tonic-gate 		if (validtime >= PREFIX_INFINITY / MILLISEC)
565*7c478bd9Sstevel@tonic-gate 			pr->pr_OnLinkLifetime = PREFIX_INFINITY - 1;
566*7c478bd9Sstevel@tonic-gate 		else
567*7c478bd9Sstevel@tonic-gate 			pr->pr_OnLinkLifetime = validtime * MILLISEC;
568*7c478bd9Sstevel@tonic-gate 	}
569*7c478bd9Sstevel@tonic-gate 	pr->pr_OnLinkFlag = _B_TRUE;
570*7c478bd9Sstevel@tonic-gate 	if (debug & (D_PREFIX|D_TMP)) {
571*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_DEBUG, "incoming_prefix_onlink_process(%s, %s/%u) "
572*7c478bd9Sstevel@tonic-gate 		    "onlink %u state 0x%x, kstate 0x%x\n",
573*7c478bd9Sstevel@tonic-gate 		    pr->pr_name, inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
574*7c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf)), pr->pr_prefix_len,
575*7c478bd9Sstevel@tonic-gate 		    pr->pr_OnLinkLifetime, pr->pr_state, pr->pr_kernel_state);
576*7c478bd9Sstevel@tonic-gate 	}
577*7c478bd9Sstevel@tonic-gate 
578*7c478bd9Sstevel@tonic-gate 	if (pr->pr_kernel_state != pr->pr_state) {
579*7c478bd9Sstevel@tonic-gate 		prefix_update_k(pr);
580*7c478bd9Sstevel@tonic-gate 	}
581*7c478bd9Sstevel@tonic-gate 
582*7c478bd9Sstevel@tonic-gate 	if (pr->pr_OnLinkLifetime != 0)
583*7c478bd9Sstevel@tonic-gate 		timer_schedule(pr->pr_OnLinkLifetime);
584*7c478bd9Sstevel@tonic-gate }
585*7c478bd9Sstevel@tonic-gate 
586*7c478bd9Sstevel@tonic-gate /*
587*7c478bd9Sstevel@tonic-gate  * Process prefix options with the autonomous flag set.
588*7c478bd9Sstevel@tonic-gate  * Returns false if this prefix results in a bad address (duplicate)
589*7c478bd9Sstevel@tonic-gate  * This function needs to loop to find the same prefix multiple times
590*7c478bd9Sstevel@tonic-gate  * as if a failover happened earlier, the addresses belonging to
591*7c478bd9Sstevel@tonic-gate  * a different interface may be found here on this interface.
592*7c478bd9Sstevel@tonic-gate  */
593*7c478bd9Sstevel@tonic-gate /* ARGSUSED2 */
594*7c478bd9Sstevel@tonic-gate static boolean_t
595*7c478bd9Sstevel@tonic-gate incoming_prefix_addrconf(struct phyint *pi, uchar_t *opt,
596*7c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from, boolean_t loopback)
597*7c478bd9Sstevel@tonic-gate {
598*7c478bd9Sstevel@tonic-gate 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
599*7c478bd9Sstevel@tonic-gate 	int plen;
600*7c478bd9Sstevel@tonic-gate 	struct prefix *pr;
601*7c478bd9Sstevel@tonic-gate 	uint32_t validtime, preftime;	/* In seconds */
602*7c478bd9Sstevel@tonic-gate 	char abuf[INET6_ADDRSTRLEN];
603*7c478bd9Sstevel@tonic-gate 	char pbuf[INET6_ADDRSTRLEN];
604*7c478bd9Sstevel@tonic-gate 	boolean_t found_pub = _B_FALSE;
605*7c478bd9Sstevel@tonic-gate 	boolean_t found_tmp = _B_FALSE;
606*7c478bd9Sstevel@tonic-gate 	boolean_t ret;
607*7c478bd9Sstevel@tonic-gate 
608*7c478bd9Sstevel@tonic-gate 	validtime = ntohl(po->nd_opt_pi_valid_time);
609*7c478bd9Sstevel@tonic-gate 	preftime = ntohl(po->nd_opt_pi_preferred_time);
610*7c478bd9Sstevel@tonic-gate 	plen = po->nd_opt_pi_prefix_len;
611*7c478bd9Sstevel@tonic-gate 
612*7c478bd9Sstevel@tonic-gate 	/* Sanity checks */
613*7c478bd9Sstevel@tonic-gate 	if (validtime < preftime) {
614*7c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
615*7c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf));
616*7c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6,
617*7c478bd9Sstevel@tonic-gate 		    (void *)&po->nd_opt_pi_prefix,
618*7c478bd9Sstevel@tonic-gate 		    pbuf, sizeof (pbuf));
619*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_WARNING, "prefix option %s/%u from %s on %s: "
620*7c478bd9Sstevel@tonic-gate 		    "valid %u < pref %u ignored\n",
621*7c478bd9Sstevel@tonic-gate 		    pbuf, plen, abuf, pi->pi_name,
622*7c478bd9Sstevel@tonic-gate 		    validtime, preftime);
623*7c478bd9Sstevel@tonic-gate 		return (_B_FALSE);
624*7c478bd9Sstevel@tonic-gate 	}
625*7c478bd9Sstevel@tonic-gate 
626*7c478bd9Sstevel@tonic-gate 	for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
627*7c478bd9Sstevel@tonic-gate 		if (pr->pr_prefix_len == plen &&
628*7c478bd9Sstevel@tonic-gate 		    prefix_equal(po->nd_opt_pi_prefix, pr->pr_prefix, plen)) {
629*7c478bd9Sstevel@tonic-gate 
630*7c478bd9Sstevel@tonic-gate 			/* Exclude static prefixes */
631*7c478bd9Sstevel@tonic-gate 			if (pr->pr_state & PR_STATIC)
632*7c478bd9Sstevel@tonic-gate 				continue;
633*7c478bd9Sstevel@tonic-gate 			if (pr->pr_flags & IFF_TEMPORARY) {
634*7c478bd9Sstevel@tonic-gate 				/*
635*7c478bd9Sstevel@tonic-gate 				 * If this address is deprecated and its token
636*7c478bd9Sstevel@tonic-gate 				 * doesn't match the current tmp token, we want
637*7c478bd9Sstevel@tonic-gate 				 * to create a new address with the current
638*7c478bd9Sstevel@tonic-gate 				 * token.  So don't count this addr as a match.
639*7c478bd9Sstevel@tonic-gate 				 */
640*7c478bd9Sstevel@tonic-gate 				if (!((pr->pr_flags & IFF_DEPRECATED) &&
641*7c478bd9Sstevel@tonic-gate 				    !token_equal(pi->pi_tmp_token,
642*7c478bd9Sstevel@tonic-gate 				    pr->pr_address, TMP_TOKEN_BITS)))
643*7c478bd9Sstevel@tonic-gate 					found_tmp = _B_TRUE;
644*7c478bd9Sstevel@tonic-gate 			} else {
645*7c478bd9Sstevel@tonic-gate 				found_pub = _B_TRUE;
646*7c478bd9Sstevel@tonic-gate 			}
647*7c478bd9Sstevel@tonic-gate 			(void) incoming_prefix_addrconf_process(pi, pr, opt,
648*7c478bd9Sstevel@tonic-gate 			    from, loopback, _B_FALSE);
649*7c478bd9Sstevel@tonic-gate 		}
650*7c478bd9Sstevel@tonic-gate 	}
651*7c478bd9Sstevel@tonic-gate 
652*7c478bd9Sstevel@tonic-gate 	/*
653*7c478bd9Sstevel@tonic-gate 	 * If we have found a matching prefix (for public and, if temp addrs
654*7c478bd9Sstevel@tonic-gate 	 * are enabled, for temporary) already or validtime is zero, we have
655*7c478bd9Sstevel@tonic-gate 	 * nothing to do.
656*7c478bd9Sstevel@tonic-gate 	 */
657*7c478bd9Sstevel@tonic-gate 	if (validtime == 0 ||
658*7c478bd9Sstevel@tonic-gate 	    (found_pub && (!pi->pi_TmpAddrsEnabled || found_tmp)))
659*7c478bd9Sstevel@tonic-gate 		return (_B_TRUE);
660*7c478bd9Sstevel@tonic-gate 
661*7c478bd9Sstevel@tonic-gate 	if (!found_pub) {
662*7c478bd9Sstevel@tonic-gate 		pr = prefix_create(pi, po->nd_opt_pi_prefix, plen, 0);
663*7c478bd9Sstevel@tonic-gate 		if (pr == NULL)
664*7c478bd9Sstevel@tonic-gate 			return (_B_TRUE);
665*7c478bd9Sstevel@tonic-gate 		ret = incoming_prefix_addrconf_process(pi, pr, opt, from,
666*7c478bd9Sstevel@tonic-gate 		    loopback, _B_TRUE);
667*7c478bd9Sstevel@tonic-gate 	}
668*7c478bd9Sstevel@tonic-gate 	/*
669*7c478bd9Sstevel@tonic-gate 	 * if processing of the public address failed,
670*7c478bd9Sstevel@tonic-gate 	 * don't bother with the temporary address.
671*7c478bd9Sstevel@tonic-gate 	 */
672*7c478bd9Sstevel@tonic-gate 	if (ret == _B_FALSE)
673*7c478bd9Sstevel@tonic-gate 		return (_B_FALSE);
674*7c478bd9Sstevel@tonic-gate 
675*7c478bd9Sstevel@tonic-gate 	if (pi->pi_TmpAddrsEnabled && !found_tmp) {
676*7c478bd9Sstevel@tonic-gate 		pr = prefix_create(pi, po->nd_opt_pi_prefix, plen,
677*7c478bd9Sstevel@tonic-gate 		    IFF_TEMPORARY);
678*7c478bd9Sstevel@tonic-gate 		if (pr == NULL)
679*7c478bd9Sstevel@tonic-gate 			return (_B_TRUE);
680*7c478bd9Sstevel@tonic-gate 		ret = incoming_prefix_addrconf_process(pi, pr, opt, from,
681*7c478bd9Sstevel@tonic-gate 		    loopback, _B_TRUE);
682*7c478bd9Sstevel@tonic-gate 	}
683*7c478bd9Sstevel@tonic-gate 
684*7c478bd9Sstevel@tonic-gate 	return (ret);
685*7c478bd9Sstevel@tonic-gate }
686*7c478bd9Sstevel@tonic-gate 
687*7c478bd9Sstevel@tonic-gate boolean_t
688*7c478bd9Sstevel@tonic-gate incoming_prefix_addrconf_process(struct phyint *pi, struct prefix *pr,
689*7c478bd9Sstevel@tonic-gate     uchar_t *opt, struct sockaddr_in6 *from, boolean_t loopback,
690*7c478bd9Sstevel@tonic-gate     boolean_t new_prefix)
691*7c478bd9Sstevel@tonic-gate {
692*7c478bd9Sstevel@tonic-gate 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
693*7c478bd9Sstevel@tonic-gate 	char abuf[INET6_ADDRSTRLEN];
694*7c478bd9Sstevel@tonic-gate 	char pbuf[INET6_ADDRSTRLEN];
695*7c478bd9Sstevel@tonic-gate 	uint32_t validtime, preftime;	/* In seconds */
696*7c478bd9Sstevel@tonic-gate 	uint32_t recorded_validtime;	/* In seconds */
697*7c478bd9Sstevel@tonic-gate 	int plen, dadfails = 0;
698*7c478bd9Sstevel@tonic-gate 	struct prefix *other_pr;
699*7c478bd9Sstevel@tonic-gate 
700*7c478bd9Sstevel@tonic-gate 	validtime = ntohl(po->nd_opt_pi_valid_time);
701*7c478bd9Sstevel@tonic-gate 	preftime = ntohl(po->nd_opt_pi_preferred_time);
702*7c478bd9Sstevel@tonic-gate 	plen = po->nd_opt_pi_prefix_len;
703*7c478bd9Sstevel@tonic-gate 	if (!new_prefix) {
704*7c478bd9Sstevel@tonic-gate 		/*
705*7c478bd9Sstevel@tonic-gate 		 * Check 2 hour rule on valid lifetime.
706*7c478bd9Sstevel@tonic-gate 		 * Follows: RFC 2462
707*7c478bd9Sstevel@tonic-gate 		 * If we advertised this prefix ourselves we skip
708*7c478bd9Sstevel@tonic-gate 		 * these checks. They are also skipped if we did not
709*7c478bd9Sstevel@tonic-gate 		 * previously do addrconf on this prefix.
710*7c478bd9Sstevel@tonic-gate 		 */
711*7c478bd9Sstevel@tonic-gate 		recorded_validtime = pr->pr_ValidLifetime / MILLISEC;
712*7c478bd9Sstevel@tonic-gate 
713*7c478bd9Sstevel@tonic-gate 		if (loopback || !(pr->pr_state & PR_AUTO) ||
714*7c478bd9Sstevel@tonic-gate 		    validtime >= MIN_VALID_LIFETIME ||
715*7c478bd9Sstevel@tonic-gate 		    /* LINTED - statement has no consequent */
716*7c478bd9Sstevel@tonic-gate 		    validtime >= recorded_validtime) {
717*7c478bd9Sstevel@tonic-gate 			/* OK */
718*7c478bd9Sstevel@tonic-gate 		} else if (recorded_validtime < MIN_VALID_LIFETIME &&
719*7c478bd9Sstevel@tonic-gate 		    validtime < recorded_validtime) {
720*7c478bd9Sstevel@tonic-gate 			/* Ignore the prefix */
721*7c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
722*7c478bd9Sstevel@tonic-gate 			    (void *)&from->sin6_addr,
723*7c478bd9Sstevel@tonic-gate 			    abuf, sizeof (abuf));
724*7c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
725*7c478bd9Sstevel@tonic-gate 			    (void *)&po->nd_opt_pi_prefix,
726*7c478bd9Sstevel@tonic-gate 			    pbuf, sizeof (pbuf));
727*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: "
728*7c478bd9Sstevel@tonic-gate 			    "too short valid lifetime %u stored %u "
729*7c478bd9Sstevel@tonic-gate 			    "- ignored\n",
730*7c478bd9Sstevel@tonic-gate 			    pbuf, plen, abuf, pi->pi_name,
731*7c478bd9Sstevel@tonic-gate 			    validtime, recorded_validtime);
732*7c478bd9Sstevel@tonic-gate 			return (_B_TRUE);
733*7c478bd9Sstevel@tonic-gate 		} else {
734*7c478bd9Sstevel@tonic-gate 			/*
735*7c478bd9Sstevel@tonic-gate 			 * If the router clock runs slower than the
736*7c478bd9Sstevel@tonic-gate 			 * host by 1 second over 2 hours then this
737*7c478bd9Sstevel@tonic-gate 			 * test will set the lifetime back to 2 hours
738*7c478bd9Sstevel@tonic-gate 			 * once i.e. a lifetime decrementing in
739*7c478bd9Sstevel@tonic-gate 			 * realtime might cause the prefix to live an
740*7c478bd9Sstevel@tonic-gate 			 * extra 2 hours on the host.
741*7c478bd9Sstevel@tonic-gate 			 */
742*7c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
743*7c478bd9Sstevel@tonic-gate 			    (void *)&from->sin6_addr,
744*7c478bd9Sstevel@tonic-gate 			    abuf, sizeof (abuf));
745*7c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
746*7c478bd9Sstevel@tonic-gate 			    (void *)&po->nd_opt_pi_prefix,
747*7c478bd9Sstevel@tonic-gate 			    pbuf, sizeof (pbuf));
748*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: "
749*7c478bd9Sstevel@tonic-gate 			    "valid time %u stored %u rounded up "
750*7c478bd9Sstevel@tonic-gate 			    "to %u\n",
751*7c478bd9Sstevel@tonic-gate 			    pbuf, plen, abuf, pi->pi_name,
752*7c478bd9Sstevel@tonic-gate 			    validtime, recorded_validtime,
753*7c478bd9Sstevel@tonic-gate 			    MIN_VALID_LIFETIME);
754*7c478bd9Sstevel@tonic-gate 			validtime = MIN_VALID_LIFETIME;
755*7c478bd9Sstevel@tonic-gate 		}
756*7c478bd9Sstevel@tonic-gate 	}
757*7c478bd9Sstevel@tonic-gate 
758*7c478bd9Sstevel@tonic-gate 	/*
759*7c478bd9Sstevel@tonic-gate 	 * For RFC3041 addresses, need to take token lifetime
760*7c478bd9Sstevel@tonic-gate 	 * into account, too.
761*7c478bd9Sstevel@tonic-gate 	 */
762*7c478bd9Sstevel@tonic-gate 	if (pr->pr_flags & IFF_TEMPORARY) {
763*7c478bd9Sstevel@tonic-gate 		uint_t	cur_tpreftime =
764*7c478bd9Sstevel@tonic-gate 		    pi->pi_TmpPreferredLifetime - pi->pi_TmpDesyncFactor;
765*7c478bd9Sstevel@tonic-gate 
766*7c478bd9Sstevel@tonic-gate 		if (new_prefix) {
767*7c478bd9Sstevel@tonic-gate 			validtime = MIN(validtime, pi->pi_TmpValidLifetime);
768*7c478bd9Sstevel@tonic-gate 			preftime = MIN(preftime, cur_tpreftime);
769*7c478bd9Sstevel@tonic-gate 		} else {
770*7c478bd9Sstevel@tonic-gate 			uint_t cur_vexp, cur_pexp, curtime;
771*7c478bd9Sstevel@tonic-gate 			curtime = getcurrenttime() / MILLISEC;
772*7c478bd9Sstevel@tonic-gate 
773*7c478bd9Sstevel@tonic-gate 			cur_vexp = pr->pr_CreateTime + pi->pi_TmpValidLifetime;
774*7c478bd9Sstevel@tonic-gate 			cur_pexp = pr->pr_CreateTime + cur_tpreftime;
775*7c478bd9Sstevel@tonic-gate 			if (curtime > cur_vexp)
776*7c478bd9Sstevel@tonic-gate 				validtime = 0;
777*7c478bd9Sstevel@tonic-gate 			else if ((curtime + validtime) > cur_vexp)
778*7c478bd9Sstevel@tonic-gate 				validtime = cur_vexp - curtime;
779*7c478bd9Sstevel@tonic-gate 			/*
780*7c478bd9Sstevel@tonic-gate 			 * If this is an existing address which was deprecated
781*7c478bd9Sstevel@tonic-gate 			 * because of a bad token, we don't want to update its
782*7c478bd9Sstevel@tonic-gate 			 * preferred lifetime!
783*7c478bd9Sstevel@tonic-gate 			 */
784*7c478bd9Sstevel@tonic-gate 			if ((pr->pr_PreferredLifetime == 0) &&
785*7c478bd9Sstevel@tonic-gate 			    !token_equal(pr->pr_address, pi->pi_tmp_token,
786*7c478bd9Sstevel@tonic-gate 			    TMP_TOKEN_BITS))
787*7c478bd9Sstevel@tonic-gate 				preftime = 0;
788*7c478bd9Sstevel@tonic-gate 			else if (curtime > cur_pexp)
789*7c478bd9Sstevel@tonic-gate 				preftime = 0;
790*7c478bd9Sstevel@tonic-gate 			else if ((curtime + preftime) > cur_pexp)
791*7c478bd9Sstevel@tonic-gate 				preftime = cur_pexp - curtime;
792*7c478bd9Sstevel@tonic-gate 		}
793*7c478bd9Sstevel@tonic-gate 		if ((preftime != 0) && (preftime <= pi->pi_TmpRegenAdvance)) {
794*7c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
795*7c478bd9Sstevel@tonic-gate 			    (void *)&from->sin6_addr,
796*7c478bd9Sstevel@tonic-gate 			    abuf, sizeof (abuf));
797*7c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
798*7c478bd9Sstevel@tonic-gate 			    (void *)&po->nd_opt_pi_prefix,
799*7c478bd9Sstevel@tonic-gate 			    pbuf, sizeof (pbuf));
800*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_WARNING, "prefix opt %s/%u from %s on %s: "
801*7c478bd9Sstevel@tonic-gate 			    "preferred lifetime(%d) <= TmpRegenAdvance(%d)\n",
802*7c478bd9Sstevel@tonic-gate 			    pbuf, plen, abuf, pi->pi_name, preftime,
803*7c478bd9Sstevel@tonic-gate 			    pi->pi_TmpRegenAdvance);
804*7c478bd9Sstevel@tonic-gate 			if (new_prefix)
805*7c478bd9Sstevel@tonic-gate 				prefix_delete(pr);
806*7c478bd9Sstevel@tonic-gate 			return (_B_TRUE);
807*7c478bd9Sstevel@tonic-gate 		}
808*7c478bd9Sstevel@tonic-gate 	}
809*7c478bd9Sstevel@tonic-gate 	if (debug & D_TMP)
810*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_DEBUG, "calculated lifetimes(%s, 0x%llx): v %d, "
811*7c478bd9Sstevel@tonic-gate 		    "p %d\n", pr->pr_name, pr->pr_flags, validtime, preftime);
812*7c478bd9Sstevel@tonic-gate 
813*7c478bd9Sstevel@tonic-gate 	if (!(pr->pr_state & PR_AUTO)) {
814*7c478bd9Sstevel@tonic-gate 		int i, tokenlen;
815*7c478bd9Sstevel@tonic-gate 		in6_addr_t *token;
816*7c478bd9Sstevel@tonic-gate 		/*
817*7c478bd9Sstevel@tonic-gate 		 * Form a new local address if the lengths match.
818*7c478bd9Sstevel@tonic-gate 		 */
819*7c478bd9Sstevel@tonic-gate 		if (pr->pr_flags && IFF_TEMPORARY) {
820*7c478bd9Sstevel@tonic-gate RETRY_TOKEN:
821*7c478bd9Sstevel@tonic-gate 			if (IN6_IS_ADDR_UNSPECIFIED(&pi->pi_tmp_token)) {
822*7c478bd9Sstevel@tonic-gate 				if (!tmptoken_create(pi)) {
823*7c478bd9Sstevel@tonic-gate 					prefix_delete(pr);
824*7c478bd9Sstevel@tonic-gate 					return (_B_TRUE);
825*7c478bd9Sstevel@tonic-gate 				}
826*7c478bd9Sstevel@tonic-gate 			}
827*7c478bd9Sstevel@tonic-gate 			tokenlen = TMP_TOKEN_BITS;
828*7c478bd9Sstevel@tonic-gate 			token = &pi->pi_tmp_token;
829*7c478bd9Sstevel@tonic-gate 		} else {
830*7c478bd9Sstevel@tonic-gate 			tokenlen = pi->pi_token_length;
831*7c478bd9Sstevel@tonic-gate 			token = &pi->pi_token;
832*7c478bd9Sstevel@tonic-gate 		}
833*7c478bd9Sstevel@tonic-gate 		if (pr->pr_prefix_len + tokenlen != IPV6_ABITS) {
834*7c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
835*7c478bd9Sstevel@tonic-gate 			    (void *)&from->sin6_addr,
836*7c478bd9Sstevel@tonic-gate 			    abuf, sizeof (abuf));
837*7c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
838*7c478bd9Sstevel@tonic-gate 			    (void *)&po->nd_opt_pi_prefix,
839*7c478bd9Sstevel@tonic-gate 			    pbuf, sizeof (pbuf));
840*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: "
841*7c478bd9Sstevel@tonic-gate 			    "mismatched length %d token length %d\n",
842*7c478bd9Sstevel@tonic-gate 			    pbuf, plen, abuf, pi->pi_name,
843*7c478bd9Sstevel@tonic-gate 			    pr->pr_prefix_len, tokenlen);
844*7c478bd9Sstevel@tonic-gate 			return (_B_TRUE);
845*7c478bd9Sstevel@tonic-gate 		}
846*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < 16; i++) {
847*7c478bd9Sstevel@tonic-gate 			/*
848*7c478bd9Sstevel@tonic-gate 			 * prefix_create ensures that pr_prefix has all-zero
849*7c478bd9Sstevel@tonic-gate 			 * bits after prefixlen.
850*7c478bd9Sstevel@tonic-gate 			 */
851*7c478bd9Sstevel@tonic-gate 			pr->pr_address.s6_addr[i] = pr->pr_prefix.s6_addr[i] |
852*7c478bd9Sstevel@tonic-gate 			    token->s6_addr[i];
853*7c478bd9Sstevel@tonic-gate 		}
854*7c478bd9Sstevel@tonic-gate 		/*
855*7c478bd9Sstevel@tonic-gate 		 * Check if any other physical interface has the same
856*7c478bd9Sstevel@tonic-gate 		 * address configured already
857*7c478bd9Sstevel@tonic-gate 		 */
858*7c478bd9Sstevel@tonic-gate 		if ((other_pr = prefix_lookup_addr_match(pr)) != NULL) {
859*7c478bd9Sstevel@tonic-gate 			/*
860*7c478bd9Sstevel@tonic-gate 			 * Delete this prefix structure as kernel
861*7c478bd9Sstevel@tonic-gate 			 * does not allow duplicated addresses
862*7c478bd9Sstevel@tonic-gate 			 */
863*7c478bd9Sstevel@tonic-gate 
864*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_ERR, "incoming_prefix_addrconf_process: "
865*7c478bd9Sstevel@tonic-gate 			    "Duplicate prefix  %s received on interface %s\n",
866*7c478bd9Sstevel@tonic-gate 			    inet_ntop(AF_INET6,
867*7c478bd9Sstevel@tonic-gate 			    (void *)&po->nd_opt_pi_prefix, abuf,
868*7c478bd9Sstevel@tonic-gate 			    sizeof (abuf)), pi->pi_name);
869*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_ERR, "incoming_prefix_addrconf_process: "
870*7c478bd9Sstevel@tonic-gate 			    "Prefix already exists in interface %s\n",
871*7c478bd9Sstevel@tonic-gate 			    other_pr->pr_physical->pi_name);
872*7c478bd9Sstevel@tonic-gate 			if (new_prefix) {
873*7c478bd9Sstevel@tonic-gate 				prefix_delete(pr);
874*7c478bd9Sstevel@tonic-gate 				return (_B_FALSE);
875*7c478bd9Sstevel@tonic-gate 			}
876*7c478bd9Sstevel@tonic-gate 			/* Ignore for addrconf purposes */
877*7c478bd9Sstevel@tonic-gate 			validtime = preftime = 0;
878*7c478bd9Sstevel@tonic-gate 		}
879*7c478bd9Sstevel@tonic-gate 		if ((pr->pr_flags & IFF_TEMPORARY) && new_prefix) {
880*7c478bd9Sstevel@tonic-gate 			struct sockaddr_in6 sin6;
881*7c478bd9Sstevel@tonic-gate 			sin6.sin6_family = AF_INET6;
882*7c478bd9Sstevel@tonic-gate 			sin6.sin6_addr = pr->pr_address;
883*7c478bd9Sstevel@tonic-gate 			if (do_dad(pi->pi_name, &sin6) != 0) {
884*7c478bd9Sstevel@tonic-gate 				/* DAD failed, need a new token */
885*7c478bd9Sstevel@tonic-gate 				dadfails++;
886*7c478bd9Sstevel@tonic-gate 				logmsg(LOG_WARNING,
887*7c478bd9Sstevel@tonic-gate 				    "incoming_prefix_addrconf_process: "
888*7c478bd9Sstevel@tonic-gate 				    "deprecating temporary token %s\n",
889*7c478bd9Sstevel@tonic-gate 				    inet_ntop(AF_INET6,
890*7c478bd9Sstevel@tonic-gate 				    (void *)&pi->pi_tmp_token, abuf,
891*7c478bd9Sstevel@tonic-gate 				    sizeof (abuf)));
892*7c478bd9Sstevel@tonic-gate 				tmptoken_delete(pi);
893*7c478bd9Sstevel@tonic-gate 				if (dadfails == MAX_DAD_FAILURES) {
894*7c478bd9Sstevel@tonic-gate 					logmsg(LOG_ERR, "Too many DAD "
895*7c478bd9Sstevel@tonic-gate 					    "failures; disabling temporary "
896*7c478bd9Sstevel@tonic-gate 					    "addresses on %s\n", pi->pi_name);
897*7c478bd9Sstevel@tonic-gate 					pi->pi_TmpAddrsEnabled = 0;
898*7c478bd9Sstevel@tonic-gate 					prefix_delete(pr);
899*7c478bd9Sstevel@tonic-gate 					return (_B_TRUE);
900*7c478bd9Sstevel@tonic-gate 				}
901*7c478bd9Sstevel@tonic-gate 				goto RETRY_TOKEN;
902*7c478bd9Sstevel@tonic-gate 			}
903*7c478bd9Sstevel@tonic-gate 			pr->pr_CreateTime = getcurrenttime() / MILLISEC;
904*7c478bd9Sstevel@tonic-gate 			if (debug & D_TMP)
905*7c478bd9Sstevel@tonic-gate 				logmsg(LOG_DEBUG,
906*7c478bd9Sstevel@tonic-gate 				    "created tmp addr(%s v %d p %d)\n",
907*7c478bd9Sstevel@tonic-gate 				    pr->pr_name, validtime, preftime);
908*7c478bd9Sstevel@tonic-gate 		}
909*7c478bd9Sstevel@tonic-gate 	}
910*7c478bd9Sstevel@tonic-gate 
911*7c478bd9Sstevel@tonic-gate 	if (validtime != 0)
912*7c478bd9Sstevel@tonic-gate 		pr->pr_state |= PR_AUTO;
913*7c478bd9Sstevel@tonic-gate 	else
914*7c478bd9Sstevel@tonic-gate 		pr->pr_state &= ~(PR_AUTO|PR_DEPRECATED);
915*7c478bd9Sstevel@tonic-gate 	if (preftime != 0 || !(pr->pr_state & PR_AUTO))
916*7c478bd9Sstevel@tonic-gate 		pr->pr_state &= ~PR_DEPRECATED;
917*7c478bd9Sstevel@tonic-gate 	else
918*7c478bd9Sstevel@tonic-gate 		pr->pr_state |= PR_DEPRECATED;
919*7c478bd9Sstevel@tonic-gate 
920*7c478bd9Sstevel@tonic-gate 	/*
921*7c478bd9Sstevel@tonic-gate 	 * Convert from seconds to milliseconds avoiding overflow.
922*7c478bd9Sstevel@tonic-gate 	 * If the lifetime in the packet is e.g. PREFIX_INFINITY - 1
923*7c478bd9Sstevel@tonic-gate 	 * (4 billion seconds - about 130 years) we will in fact time
924*7c478bd9Sstevel@tonic-gate 	 * out the prefix after 4 billion milliseconds - 46 days).
925*7c478bd9Sstevel@tonic-gate 	 * Thus the longest lifetime (apart from infinity) is 46 days.
926*7c478bd9Sstevel@tonic-gate 	 * Note that this ensures that PREFIX_INFINITY still means "forever".
927*7c478bd9Sstevel@tonic-gate 	 */
928*7c478bd9Sstevel@tonic-gate 	if (validtime >= PREFIX_INFINITY / MILLISEC)
929*7c478bd9Sstevel@tonic-gate 		pr->pr_ValidLifetime = PREFIX_INFINITY - 1;
930*7c478bd9Sstevel@tonic-gate 	else
931*7c478bd9Sstevel@tonic-gate 		pr->pr_ValidLifetime = validtime * MILLISEC;
932*7c478bd9Sstevel@tonic-gate 	if (preftime >= PREFIX_INFINITY / MILLISEC)
933*7c478bd9Sstevel@tonic-gate 		pr->pr_PreferredLifetime = PREFIX_INFINITY - 1;
934*7c478bd9Sstevel@tonic-gate 	else
935*7c478bd9Sstevel@tonic-gate 		pr->pr_PreferredLifetime = preftime * MILLISEC;
936*7c478bd9Sstevel@tonic-gate 	pr->pr_AutonomousFlag = _B_TRUE;
937*7c478bd9Sstevel@tonic-gate 
938*7c478bd9Sstevel@tonic-gate 	if (debug & D_PREFIX) {
939*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_DEBUG, "incoming_prefix_addrconf_process(%s, %s/%u) "
940*7c478bd9Sstevel@tonic-gate 		    "valid %u pref %u\n",
941*7c478bd9Sstevel@tonic-gate 		    pr->pr_physical->pi_name,
942*7c478bd9Sstevel@tonic-gate 		    inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
943*7c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf)), pr->pr_prefix_len,
944*7c478bd9Sstevel@tonic-gate 		    pr->pr_ValidLifetime, pr->pr_PreferredLifetime);
945*7c478bd9Sstevel@tonic-gate 	}
946*7c478bd9Sstevel@tonic-gate 
947*7c478bd9Sstevel@tonic-gate 	if (pr->pr_state & PR_AUTO) {
948*7c478bd9Sstevel@tonic-gate 		/* Take the min of the two timeouts by calling it twice */
949*7c478bd9Sstevel@tonic-gate 		if (pr->pr_ValidLifetime != 0)
950*7c478bd9Sstevel@tonic-gate 			timer_schedule(pr->pr_ValidLifetime);
951*7c478bd9Sstevel@tonic-gate 		if (pr->pr_PreferredLifetime != 0)
952*7c478bd9Sstevel@tonic-gate 			timer_schedule(pr->pr_PreferredLifetime);
953*7c478bd9Sstevel@tonic-gate 	}
954*7c478bd9Sstevel@tonic-gate 	if (pr->pr_kernel_state != pr->pr_state) {
955*7c478bd9Sstevel@tonic-gate 		/* Log a message when an addrconf prefix goes away */
956*7c478bd9Sstevel@tonic-gate 		if ((pr->pr_kernel_state & PR_AUTO) &&
957*7c478bd9Sstevel@tonic-gate 		    !(pr->pr_state & PR_AUTO)) {
958*7c478bd9Sstevel@tonic-gate 			char abuf[INET6_ADDRSTRLEN];
959*7c478bd9Sstevel@tonic-gate 
960*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_WARNING, "Address removed due to zero "
961*7c478bd9Sstevel@tonic-gate 			    "valid lifetime %s\n",
962*7c478bd9Sstevel@tonic-gate 			    inet_ntop(AF_INET6, (void *)&pr->pr_address,
963*7c478bd9Sstevel@tonic-gate 			    abuf, sizeof (abuf)));
964*7c478bd9Sstevel@tonic-gate 		}
965*7c478bd9Sstevel@tonic-gate 		prefix_update_k(pr);
966*7c478bd9Sstevel@tonic-gate 	}
967*7c478bd9Sstevel@tonic-gate 	return (_B_TRUE);
968*7c478bd9Sstevel@tonic-gate }
969*7c478bd9Sstevel@tonic-gate 
970*7c478bd9Sstevel@tonic-gate /*
971*7c478bd9Sstevel@tonic-gate  * Process an MTU option received in a router advertisement.
972*7c478bd9Sstevel@tonic-gate  */
973*7c478bd9Sstevel@tonic-gate static void
974*7c478bd9Sstevel@tonic-gate incoming_mtu_opt(struct phyint *pi, uchar_t *opt,
975*7c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from)
976*7c478bd9Sstevel@tonic-gate {
977*7c478bd9Sstevel@tonic-gate 	struct nd_opt_mtu *mo = (struct nd_opt_mtu *)opt;
978*7c478bd9Sstevel@tonic-gate 	struct lifreq lifr;
979*7c478bd9Sstevel@tonic-gate 	uint32_t mtu;
980*7c478bd9Sstevel@tonic-gate 
981*7c478bd9Sstevel@tonic-gate 	if (8 * mo->nd_opt_mtu_len != sizeof (*mo)) {
982*7c478bd9Sstevel@tonic-gate 		char abuf[INET6_ADDRSTRLEN];
983*7c478bd9Sstevel@tonic-gate 
984*7c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
985*7c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf));
986*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "mtu option from %s on %s wrong size "
987*7c478bd9Sstevel@tonic-gate 		    "(%d bytes)\n",
988*7c478bd9Sstevel@tonic-gate 		    abuf, pi->pi_name,
989*7c478bd9Sstevel@tonic-gate 		    8 * (int)mo->nd_opt_mtu_len);
990*7c478bd9Sstevel@tonic-gate 		return;
991*7c478bd9Sstevel@tonic-gate 	}
992*7c478bd9Sstevel@tonic-gate 	mtu = ntohl(mo->nd_opt_mtu_mtu);
993*7c478bd9Sstevel@tonic-gate 	if (pi->pi_LinkMTU == mtu)
994*7c478bd9Sstevel@tonic-gate 		return;	/* No change */
995*7c478bd9Sstevel@tonic-gate 	if (mtu > pi->pi_mtu) {
996*7c478bd9Sstevel@tonic-gate 		/* Can't exceed physical MTU */
997*7c478bd9Sstevel@tonic-gate 		char abuf[INET6_ADDRSTRLEN];
998*7c478bd9Sstevel@tonic-gate 
999*7c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
1000*7c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf));
1001*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "mtu option from %s on %s too large "
1002*7c478bd9Sstevel@tonic-gate 		    "MTU %d - %d\n", abuf, pi->pi_name, mtu, pi->pi_mtu);
1003*7c478bd9Sstevel@tonic-gate 		return;
1004*7c478bd9Sstevel@tonic-gate 	}
1005*7c478bd9Sstevel@tonic-gate 	if (mtu < IPV6_MIN_MTU) {
1006*7c478bd9Sstevel@tonic-gate 		char abuf[INET6_ADDRSTRLEN];
1007*7c478bd9Sstevel@tonic-gate 
1008*7c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
1009*7c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf));
1010*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "mtu option from %s on %s too small "
1011*7c478bd9Sstevel@tonic-gate 		    "MTU (%d)\n", abuf, pi->pi_name, mtu);
1012*7c478bd9Sstevel@tonic-gate 		return;
1013*7c478bd9Sstevel@tonic-gate 	}
1014*7c478bd9Sstevel@tonic-gate 
1015*7c478bd9Sstevel@tonic-gate 	pi->pi_LinkMTU = mtu;
1016*7c478bd9Sstevel@tonic-gate 	(void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
1017*7c478bd9Sstevel@tonic-gate 	lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
1018*7c478bd9Sstevel@tonic-gate 	if (ioctl(pi->pi_sock, SIOCGLIFLNKINFO, (char *)&lifr) < 0) {
1019*7c478bd9Sstevel@tonic-gate 		logperror_pi(pi, "incoming_mtu_opt: SIOCGLIFLNKINFO");
1020*7c478bd9Sstevel@tonic-gate 		return;
1021*7c478bd9Sstevel@tonic-gate 	}
1022*7c478bd9Sstevel@tonic-gate 	lifr.lifr_ifinfo.lir_maxmtu = pi->pi_LinkMTU;
1023*7c478bd9Sstevel@tonic-gate 	if (ioctl(pi->pi_sock, SIOCSLIFLNKINFO, (char *)&lifr) < 0) {
1024*7c478bd9Sstevel@tonic-gate 		logperror_pi(pi, "incoming_mtu_opt: SIOCSLIFLNKINFO");
1025*7c478bd9Sstevel@tonic-gate 		return;
1026*7c478bd9Sstevel@tonic-gate 	}
1027*7c478bd9Sstevel@tonic-gate }
1028*7c478bd9Sstevel@tonic-gate 
1029*7c478bd9Sstevel@tonic-gate /*
1030*7c478bd9Sstevel@tonic-gate  * Process a source link-layer address option received in a router
1031*7c478bd9Sstevel@tonic-gate  * advertisement or solicitation.
1032*7c478bd9Sstevel@tonic-gate  */
1033*7c478bd9Sstevel@tonic-gate static void
1034*7c478bd9Sstevel@tonic-gate incoming_lla_opt(struct phyint *pi, uchar_t *opt,
1035*7c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from, int isrouter)
1036*7c478bd9Sstevel@tonic-gate {
1037*7c478bd9Sstevel@tonic-gate 	struct nd_opt_lla *lo = (struct nd_opt_lla *)opt;
1038*7c478bd9Sstevel@tonic-gate 	struct lifreq lifr;
1039*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
1040*7c478bd9Sstevel@tonic-gate 	int max_content_len;
1041*7c478bd9Sstevel@tonic-gate 
1042*7c478bd9Sstevel@tonic-gate 	if (pi->pi_hdw_addr_len == 0)
1043*7c478bd9Sstevel@tonic-gate 		return;
1044*7c478bd9Sstevel@tonic-gate 
1045*7c478bd9Sstevel@tonic-gate 	/*
1046*7c478bd9Sstevel@tonic-gate 	 * Can't remove padding since it is link type specific.
1047*7c478bd9Sstevel@tonic-gate 	 * However, we check against the length of our link-layer
1048*7c478bd9Sstevel@tonic-gate 	 * address.
1049*7c478bd9Sstevel@tonic-gate 	 * Note: assumes that all links have a fixed lengh address.
1050*7c478bd9Sstevel@tonic-gate 	 */
1051*7c478bd9Sstevel@tonic-gate 	max_content_len = lo->nd_opt_lla_len * 8 - sizeof (struct nd_opt_hdr);
1052*7c478bd9Sstevel@tonic-gate 	if (max_content_len < pi->pi_hdw_addr_len ||
1053*7c478bd9Sstevel@tonic-gate 	    (max_content_len >= 8 &&
1054*7c478bd9Sstevel@tonic-gate 	    max_content_len - 7 > pi->pi_hdw_addr_len)) {
1055*7c478bd9Sstevel@tonic-gate 		char abuf[INET6_ADDRSTRLEN];
1056*7c478bd9Sstevel@tonic-gate 
1057*7c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
1058*7c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf));
1059*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "lla option from %s on %s too long with bad "
1060*7c478bd9Sstevel@tonic-gate 		    "physaddr length (%d vs. %d bytes)\n",
1061*7c478bd9Sstevel@tonic-gate 		    abuf, pi->pi_name,
1062*7c478bd9Sstevel@tonic-gate 		    max_content_len, pi->pi_hdw_addr_len);
1063*7c478bd9Sstevel@tonic-gate 		return;
1064*7c478bd9Sstevel@tonic-gate 	}
1065*7c478bd9Sstevel@tonic-gate 
1066*7c478bd9Sstevel@tonic-gate 	lifr.lifr_nd.lnr_hdw_len = pi->pi_hdw_addr_len;
1067*7c478bd9Sstevel@tonic-gate 	bcopy((char *)lo->nd_opt_lla_hdw_addr,
1068*7c478bd9Sstevel@tonic-gate 	    (char *)lifr.lifr_nd.lnr_hdw_addr,
1069*7c478bd9Sstevel@tonic-gate 	    lifr.lifr_nd.lnr_hdw_len);
1070*7c478bd9Sstevel@tonic-gate 
1071*7c478bd9Sstevel@tonic-gate 	sin6 = (struct sockaddr_in6 *)&lifr.lifr_nd.lnr_addr;
1072*7c478bd9Sstevel@tonic-gate 	bzero(sin6, sizeof (struct sockaddr_in6));
1073*7c478bd9Sstevel@tonic-gate 	sin6->sin6_family = AF_INET6;
1074*7c478bd9Sstevel@tonic-gate 	sin6->sin6_addr = from->sin6_addr;
1075*7c478bd9Sstevel@tonic-gate 
1076*7c478bd9Sstevel@tonic-gate 	/*
1077*7c478bd9Sstevel@tonic-gate 	 * Set IsRouter flag if RA; clear if RS.
1078*7c478bd9Sstevel@tonic-gate 	 */
1079*7c478bd9Sstevel@tonic-gate 	lifr.lifr_nd.lnr_state_create = ND_STALE;
1080*7c478bd9Sstevel@tonic-gate 	lifr.lifr_nd.lnr_state_same_lla = ND_UNCHANGED;
1081*7c478bd9Sstevel@tonic-gate 	lifr.lifr_nd.lnr_state_diff_lla = ND_STALE;
1082*7c478bd9Sstevel@tonic-gate 	lifr.lifr_nd.lnr_flags = isrouter;
1083*7c478bd9Sstevel@tonic-gate 	(void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
1084*7c478bd9Sstevel@tonic-gate 	lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
1085*7c478bd9Sstevel@tonic-gate 	if (ioctl(pi->pi_sock, SIOCLIFSETND, (char *)&lifr) < 0) {
1086*7c478bd9Sstevel@tonic-gate 		logperror_pi(pi, "incoming_lla_opt: SIOCLIFSETND");
1087*7c478bd9Sstevel@tonic-gate 		return;
1088*7c478bd9Sstevel@tonic-gate 	}
1089*7c478bd9Sstevel@tonic-gate }
1090*7c478bd9Sstevel@tonic-gate 
1091*7c478bd9Sstevel@tonic-gate /*
1092*7c478bd9Sstevel@tonic-gate  * Verify the content of the received router advertisement against our
1093*7c478bd9Sstevel@tonic-gate  * own configuration as specified in RFC 2461.
1094*7c478bd9Sstevel@tonic-gate  */
1095*7c478bd9Sstevel@tonic-gate static void
1096*7c478bd9Sstevel@tonic-gate verify_ra_consistency(struct phyint *pi, struct nd_router_advert *ra, int len,
1097*7c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from)
1098*7c478bd9Sstevel@tonic-gate {
1099*7c478bd9Sstevel@tonic-gate 	char frombuf[INET6_ADDRSTRLEN];
1100*7c478bd9Sstevel@tonic-gate 	struct nd_opt_hdr *opt;
1101*7c478bd9Sstevel@tonic-gate 	int optlen;
1102*7c478bd9Sstevel@tonic-gate 	uint_t reachable, retrans;
1103*7c478bd9Sstevel@tonic-gate 	boolean_t pktflag, myflag;
1104*7c478bd9Sstevel@tonic-gate 
1105*7c478bd9Sstevel@tonic-gate 	(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
1106*7c478bd9Sstevel@tonic-gate 	    frombuf, sizeof (frombuf));
1107*7c478bd9Sstevel@tonic-gate 
1108*7c478bd9Sstevel@tonic-gate 	if (ra->nd_ra_curhoplimit != 0 &&
1109*7c478bd9Sstevel@tonic-gate 	    pi->pi_AdvCurHopLimit != 0 &&
1110*7c478bd9Sstevel@tonic-gate 	    ra->nd_ra_curhoplimit != pi->pi_AdvCurHopLimit) {
1111*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s inconsistent cur hop "
1112*7c478bd9Sstevel@tonic-gate 		    "limit:\n\treceived %d configuration %d\n",
1113*7c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name,
1114*7c478bd9Sstevel@tonic-gate 		    ra->nd_ra_curhoplimit, pi->pi_AdvCurHopLimit);
1115*7c478bd9Sstevel@tonic-gate 	}
1116*7c478bd9Sstevel@tonic-gate 
1117*7c478bd9Sstevel@tonic-gate 	reachable = ntohl(ra->nd_ra_reachable);
1118*7c478bd9Sstevel@tonic-gate 	if (reachable != 0 && pi->pi_AdvReachableTime != 0 &&
1119*7c478bd9Sstevel@tonic-gate 	    reachable != pi->pi_AdvReachableTime) {
1120*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s inconsistent reachable "
1121*7c478bd9Sstevel@tonic-gate 		    "time:\n\treceived %d configuration %d\n",
1122*7c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name,
1123*7c478bd9Sstevel@tonic-gate 		    reachable, pi->pi_AdvReachableTime);
1124*7c478bd9Sstevel@tonic-gate 	}
1125*7c478bd9Sstevel@tonic-gate 
1126*7c478bd9Sstevel@tonic-gate 	retrans = ntohl(ra->nd_ra_retransmit);
1127*7c478bd9Sstevel@tonic-gate 	if (retrans != 0 && pi->pi_AdvRetransTimer != 0 &&
1128*7c478bd9Sstevel@tonic-gate 	    retrans != pi->pi_AdvRetransTimer) {
1129*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s inconsistent retransmit "
1130*7c478bd9Sstevel@tonic-gate 		    "timer:\n\treceived %d configuration %d\n",
1131*7c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name,
1132*7c478bd9Sstevel@tonic-gate 		    retrans, pi->pi_AdvRetransTimer);
1133*7c478bd9Sstevel@tonic-gate 	}
1134*7c478bd9Sstevel@tonic-gate 
1135*7c478bd9Sstevel@tonic-gate 	pktflag = ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) != 0);
1136*7c478bd9Sstevel@tonic-gate 	myflag = (pi->pi_AdvManagedFlag != 0);
1137*7c478bd9Sstevel@tonic-gate 	if (pktflag != myflag) {
1138*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s inconsistent managed "
1139*7c478bd9Sstevel@tonic-gate 		    "flag:\n\treceived %s configuration %s\n",
1140*7c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name,
1141*7c478bd9Sstevel@tonic-gate 		    (pktflag ? "ON" : "OFF"),
1142*7c478bd9Sstevel@tonic-gate 		    (myflag ? "ON" : "OFF"));
1143*7c478bd9Sstevel@tonic-gate 	}
1144*7c478bd9Sstevel@tonic-gate 	pktflag = ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) != 0);
1145*7c478bd9Sstevel@tonic-gate 	myflag = (pi->pi_AdvOtherConfigFlag != 0);
1146*7c478bd9Sstevel@tonic-gate 	if (pktflag != myflag) {
1147*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s inconsistent other config "
1148*7c478bd9Sstevel@tonic-gate 		    "flag:\n\treceived %s configuration %s\n",
1149*7c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name,
1150*7c478bd9Sstevel@tonic-gate 		    (pktflag ? "ON" : "OFF"),
1151*7c478bd9Sstevel@tonic-gate 		    (myflag ? "ON" : "OFF"));
1152*7c478bd9Sstevel@tonic-gate 	}
1153*7c478bd9Sstevel@tonic-gate 
1154*7c478bd9Sstevel@tonic-gate 	/* Process any options */
1155*7c478bd9Sstevel@tonic-gate 	len -= sizeof (struct nd_router_advert);
1156*7c478bd9Sstevel@tonic-gate 	opt = (struct nd_opt_hdr *)&ra[1];
1157*7c478bd9Sstevel@tonic-gate 	while (len >= sizeof (struct nd_opt_hdr)) {
1158*7c478bd9Sstevel@tonic-gate 		optlen = opt->nd_opt_len * 8;
1159*7c478bd9Sstevel@tonic-gate 		switch (opt->nd_opt_type) {
1160*7c478bd9Sstevel@tonic-gate 		case ND_OPT_PREFIX_INFORMATION:
1161*7c478bd9Sstevel@tonic-gate 			verify_prefix_opt(pi, (uchar_t *)opt, frombuf);
1162*7c478bd9Sstevel@tonic-gate 			break;
1163*7c478bd9Sstevel@tonic-gate 		case ND_OPT_MTU:
1164*7c478bd9Sstevel@tonic-gate 			verify_mtu_opt(pi, (uchar_t *)opt, frombuf);
1165*7c478bd9Sstevel@tonic-gate 			break;
1166*7c478bd9Sstevel@tonic-gate 		default:
1167*7c478bd9Sstevel@tonic-gate 			break;
1168*7c478bd9Sstevel@tonic-gate 		}
1169*7c478bd9Sstevel@tonic-gate 		opt = (struct nd_opt_hdr *)((char *)opt + optlen);
1170*7c478bd9Sstevel@tonic-gate 		len -= optlen;
1171*7c478bd9Sstevel@tonic-gate 	}
1172*7c478bd9Sstevel@tonic-gate }
1173*7c478bd9Sstevel@tonic-gate 
1174*7c478bd9Sstevel@tonic-gate /*
1175*7c478bd9Sstevel@tonic-gate  * Verify that the lifetimes and onlink/auto flags are consistent
1176*7c478bd9Sstevel@tonic-gate  * with our settings.
1177*7c478bd9Sstevel@tonic-gate  */
1178*7c478bd9Sstevel@tonic-gate static void
1179*7c478bd9Sstevel@tonic-gate verify_prefix_opt(struct phyint *pi, uchar_t *opt, char *frombuf)
1180*7c478bd9Sstevel@tonic-gate {
1181*7c478bd9Sstevel@tonic-gate 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
1182*7c478bd9Sstevel@tonic-gate 	int plen;
1183*7c478bd9Sstevel@tonic-gate 	struct adv_prefix *adv_pr;
1184*7c478bd9Sstevel@tonic-gate 	uint32_t validtime, preftime;
1185*7c478bd9Sstevel@tonic-gate 	char prefixbuf[INET6_ADDRSTRLEN];
1186*7c478bd9Sstevel@tonic-gate 	int pktflag, myflag;
1187*7c478bd9Sstevel@tonic-gate 
1188*7c478bd9Sstevel@tonic-gate 	if (8 * po->nd_opt_pi_len != sizeof (*po)) {
1189*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA prefix option from %s on %s wrong size "
1190*7c478bd9Sstevel@tonic-gate 		    "(%d bytes)\n",
1191*7c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name,
1192*7c478bd9Sstevel@tonic-gate 		    8 * (int)po->nd_opt_pi_len);
1193*7c478bd9Sstevel@tonic-gate 		return;
1194*7c478bd9Sstevel@tonic-gate 	}
1195*7c478bd9Sstevel@tonic-gate 	if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) {
1196*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s contains link-local "
1197*7c478bd9Sstevel@tonic-gate 		    "prefix - ignored\n",
1198*7c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name);
1199*7c478bd9Sstevel@tonic-gate 		return;
1200*7c478bd9Sstevel@tonic-gate 	}
1201*7c478bd9Sstevel@tonic-gate 	plen = po->nd_opt_pi_prefix_len;
1202*7c478bd9Sstevel@tonic-gate 	adv_pr = adv_prefix_lookup(pi, po->nd_opt_pi_prefix, plen);
1203*7c478bd9Sstevel@tonic-gate 	if (adv_pr == NULL)
1204*7c478bd9Sstevel@tonic-gate 		return;
1205*7c478bd9Sstevel@tonic-gate 
1206*7c478bd9Sstevel@tonic-gate 	/* Ignore prefixes which we do not advertise */
1207*7c478bd9Sstevel@tonic-gate 	if (!adv_pr->adv_pr_AdvAutonomousFlag && !adv_pr->adv_pr_AdvOnLinkFlag)
1208*7c478bd9Sstevel@tonic-gate 		return;
1209*7c478bd9Sstevel@tonic-gate 	(void) inet_ntop(AF_INET6, (void *)&adv_pr->adv_pr_prefix,
1210*7c478bd9Sstevel@tonic-gate 	    prefixbuf, sizeof (prefixbuf));
1211*7c478bd9Sstevel@tonic-gate 	pktflag = ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) != 0);
1212*7c478bd9Sstevel@tonic-gate 	myflag = (adv_pr->adv_pr_AdvAutonomousFlag != 0);
1213*7c478bd9Sstevel@tonic-gate 	if (pktflag != myflag) {
1214*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO,
1215*7c478bd9Sstevel@tonic-gate 		    "RA from %s on %s inconsistent autonumous flag for \n\t"
1216*7c478bd9Sstevel@tonic-gate 		    "prefix %s/%u: received %s configuration %s\n",
1217*7c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name, prefixbuf, adv_pr->adv_pr_prefix_len,
1218*7c478bd9Sstevel@tonic-gate 		    (pktflag ? "ON" : "OFF"),
1219*7c478bd9Sstevel@tonic-gate 		    (myflag ? "ON" : "OFF"));
1220*7c478bd9Sstevel@tonic-gate 	}
1221*7c478bd9Sstevel@tonic-gate 
1222*7c478bd9Sstevel@tonic-gate 	pktflag = ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) != 0);
1223*7c478bd9Sstevel@tonic-gate 	myflag = (adv_pr->adv_pr_AdvOnLinkFlag != 0);
1224*7c478bd9Sstevel@tonic-gate 	if (pktflag != myflag) {
1225*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s inconsistent on link flag "
1226*7c478bd9Sstevel@tonic-gate 		    "for \n\tprefix %s/%u: received %s configuration %s\n",
1227*7c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name, prefixbuf, adv_pr->adv_pr_prefix_len,
1228*7c478bd9Sstevel@tonic-gate 		    (pktflag ? "ON" : "OFF"),
1229*7c478bd9Sstevel@tonic-gate 		    (myflag ? "ON" : "OFF"));
1230*7c478bd9Sstevel@tonic-gate 	}
1231*7c478bd9Sstevel@tonic-gate 	validtime = ntohl(po->nd_opt_pi_valid_time);
1232*7c478bd9Sstevel@tonic-gate 	preftime = ntohl(po->nd_opt_pi_preferred_time);
1233*7c478bd9Sstevel@tonic-gate 
1234*7c478bd9Sstevel@tonic-gate 	/*
1235*7c478bd9Sstevel@tonic-gate 	 * Take into account variation for lifetimes decrementing
1236*7c478bd9Sstevel@tonic-gate 	 * in real time. Allow +/- 10 percent and +/- 10 seconds.
1237*7c478bd9Sstevel@tonic-gate 	 */
1238*7c478bd9Sstevel@tonic-gate #define	LOWER_LIMIT(val)	((val) - (val)/10 - 10)
1239*7c478bd9Sstevel@tonic-gate #define	UPPER_LIMIT(val)	((val) + (val)/10 + 10)
1240*7c478bd9Sstevel@tonic-gate 	if (adv_pr->adv_pr_AdvValidRealTime) {
1241*7c478bd9Sstevel@tonic-gate 		if (adv_pr->adv_pr_AdvValidExpiration > 0 &&
1242*7c478bd9Sstevel@tonic-gate 		    (validtime <
1243*7c478bd9Sstevel@tonic-gate 		    LOWER_LIMIT(adv_pr->adv_pr_AdvValidExpiration) ||
1244*7c478bd9Sstevel@tonic-gate 		    validtime >
1245*7c478bd9Sstevel@tonic-gate 		    UPPER_LIMIT(adv_pr->adv_pr_AdvValidExpiration))) {
1246*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RA from %s on %s inconsistent valid "
1247*7c478bd9Sstevel@tonic-gate 			    "lifetime for\n\tprefix %s/%u: received %d "
1248*7c478bd9Sstevel@tonic-gate 			    "configuration %d\n",
1249*7c478bd9Sstevel@tonic-gate 			    frombuf, pi->pi_name, prefixbuf,
1250*7c478bd9Sstevel@tonic-gate 			    adv_pr->adv_pr_prefix_len,
1251*7c478bd9Sstevel@tonic-gate 			    validtime, adv_pr->adv_pr_AdvValidExpiration);
1252*7c478bd9Sstevel@tonic-gate 		}
1253*7c478bd9Sstevel@tonic-gate 	} else {
1254*7c478bd9Sstevel@tonic-gate 		if (validtime != adv_pr->adv_pr_AdvValidLifetime) {
1255*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RA from %s on %s inconsistent valid "
1256*7c478bd9Sstevel@tonic-gate 			    "lifetime for\n\tprefix %s/%u: received %d "
1257*7c478bd9Sstevel@tonic-gate 			    "configuration %d\n",
1258*7c478bd9Sstevel@tonic-gate 			    frombuf, pi->pi_name, prefixbuf,
1259*7c478bd9Sstevel@tonic-gate 			    adv_pr->adv_pr_prefix_len,
1260*7c478bd9Sstevel@tonic-gate 			    validtime, adv_pr->adv_pr_AdvValidLifetime);
1261*7c478bd9Sstevel@tonic-gate 		}
1262*7c478bd9Sstevel@tonic-gate 	}
1263*7c478bd9Sstevel@tonic-gate 
1264*7c478bd9Sstevel@tonic-gate 	if (adv_pr->adv_pr_AdvPreferredRealTime) {
1265*7c478bd9Sstevel@tonic-gate 		if (adv_pr->adv_pr_AdvPreferredExpiration > 0 &&
1266*7c478bd9Sstevel@tonic-gate 		    (preftime <
1267*7c478bd9Sstevel@tonic-gate 		    LOWER_LIMIT(adv_pr->adv_pr_AdvPreferredExpiration) ||
1268*7c478bd9Sstevel@tonic-gate 		    preftime >
1269*7c478bd9Sstevel@tonic-gate 		    UPPER_LIMIT(adv_pr->adv_pr_AdvPreferredExpiration))) {
1270*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RA from %s on %s inconsistent "
1271*7c478bd9Sstevel@tonic-gate 			    "preferred lifetime for\n\tprefix %s/%u: "
1272*7c478bd9Sstevel@tonic-gate 			    "received %d configuration %d\n",
1273*7c478bd9Sstevel@tonic-gate 			    frombuf, pi->pi_name, prefixbuf,
1274*7c478bd9Sstevel@tonic-gate 			    adv_pr->adv_pr_prefix_len,
1275*7c478bd9Sstevel@tonic-gate 			    preftime, adv_pr->adv_pr_AdvPreferredExpiration);
1276*7c478bd9Sstevel@tonic-gate 		}
1277*7c478bd9Sstevel@tonic-gate 	} else {
1278*7c478bd9Sstevel@tonic-gate 		if (preftime != adv_pr->adv_pr_AdvPreferredLifetime) {
1279*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RA from %s on %s inconsistent "
1280*7c478bd9Sstevel@tonic-gate 			    "preferred lifetime for\n\tprefix %s/%u: "
1281*7c478bd9Sstevel@tonic-gate 			    "received %d configuration %d\n",
1282*7c478bd9Sstevel@tonic-gate 			    frombuf, pi->pi_name, prefixbuf,
1283*7c478bd9Sstevel@tonic-gate 			    adv_pr->adv_pr_prefix_len,
1284*7c478bd9Sstevel@tonic-gate 			    preftime, adv_pr->adv_pr_AdvPreferredLifetime);
1285*7c478bd9Sstevel@tonic-gate 		}
1286*7c478bd9Sstevel@tonic-gate 	}
1287*7c478bd9Sstevel@tonic-gate }
1288*7c478bd9Sstevel@tonic-gate 
1289*7c478bd9Sstevel@tonic-gate /*
1290*7c478bd9Sstevel@tonic-gate  * Verify the received MTU against our own configuration.
1291*7c478bd9Sstevel@tonic-gate  */
1292*7c478bd9Sstevel@tonic-gate static void
1293*7c478bd9Sstevel@tonic-gate verify_mtu_opt(struct phyint *pi, uchar_t *opt, char *frombuf)
1294*7c478bd9Sstevel@tonic-gate {
1295*7c478bd9Sstevel@tonic-gate 	struct nd_opt_mtu *mo = (struct nd_opt_mtu *)opt;
1296*7c478bd9Sstevel@tonic-gate 	uint32_t mtu;
1297*7c478bd9Sstevel@tonic-gate 
1298*7c478bd9Sstevel@tonic-gate 	if (8 * mo->nd_opt_mtu_len != sizeof (*mo)) {
1299*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "mtu option from %s on %s wrong size "
1300*7c478bd9Sstevel@tonic-gate 		    "(%d bytes)\n",
1301*7c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name,
1302*7c478bd9Sstevel@tonic-gate 		    8 * (int)mo->nd_opt_mtu_len);
1303*7c478bd9Sstevel@tonic-gate 		return;
1304*7c478bd9Sstevel@tonic-gate 	}
1305*7c478bd9Sstevel@tonic-gate 	mtu = ntohl(mo->nd_opt_mtu_mtu);
1306*7c478bd9Sstevel@tonic-gate 	if (pi->pi_AdvLinkMTU != 0 &&
1307*7c478bd9Sstevel@tonic-gate 	    pi->pi_AdvLinkMTU != mtu) {
1308*7c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s inconsistent MTU: "
1309*7c478bd9Sstevel@tonic-gate 		    "received %d configuration %d\n",
1310*7c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name,
1311*7c478bd9Sstevel@tonic-gate 		    mtu, pi->pi_AdvLinkMTU);
1312*7c478bd9Sstevel@tonic-gate 	}
1313*7c478bd9Sstevel@tonic-gate }
1314*7c478bd9Sstevel@tonic-gate 
1315*7c478bd9Sstevel@tonic-gate /*
1316*7c478bd9Sstevel@tonic-gate  * Verify that all options have a non-zero length and that
1317*7c478bd9Sstevel@tonic-gate  * the options fit within the total length of the packet (optlen).
1318*7c478bd9Sstevel@tonic-gate  */
1319*7c478bd9Sstevel@tonic-gate static boolean_t
1320*7c478bd9Sstevel@tonic-gate verify_opt_len(struct nd_opt_hdr *opt, int optlen,
1321*7c478bd9Sstevel@tonic-gate     struct phyint *pi, struct sockaddr_in6 *from)
1322*7c478bd9Sstevel@tonic-gate {
1323*7c478bd9Sstevel@tonic-gate 	while (optlen > 0) {
1324*7c478bd9Sstevel@tonic-gate 		if (opt->nd_opt_len == 0) {
1325*7c478bd9Sstevel@tonic-gate 			char abuf[INET6_ADDRSTRLEN];
1326*7c478bd9Sstevel@tonic-gate 
1327*7c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
1328*7c478bd9Sstevel@tonic-gate 			    (void *)&from->sin6_addr,
1329*7c478bd9Sstevel@tonic-gate 			    abuf, sizeof (abuf));
1330*7c478bd9Sstevel@tonic-gate 
1331*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "Zero length option type 0x%x "
1332*7c478bd9Sstevel@tonic-gate 			    "from %s on %s\n",
1333*7c478bd9Sstevel@tonic-gate 			    opt->nd_opt_type, abuf, pi->pi_name);
1334*7c478bd9Sstevel@tonic-gate 			return (_B_FALSE);
1335*7c478bd9Sstevel@tonic-gate 		}
1336*7c478bd9Sstevel@tonic-gate 		optlen -= 8 * opt->nd_opt_len;
1337*7c478bd9Sstevel@tonic-gate 		if (optlen < 0) {
1338*7c478bd9Sstevel@tonic-gate 			char abuf[INET6_ADDRSTRLEN];
1339*7c478bd9Sstevel@tonic-gate 
1340*7c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
1341*7c478bd9Sstevel@tonic-gate 			    (void *)&from->sin6_addr,
1342*7c478bd9Sstevel@tonic-gate 			    abuf, sizeof (abuf));
1343*7c478bd9Sstevel@tonic-gate 
1344*7c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "Too large option: type 0x%x len %u "
1345*7c478bd9Sstevel@tonic-gate 			    "from %s on %s\n",
1346*7c478bd9Sstevel@tonic-gate 			    opt->nd_opt_type, opt->nd_opt_len,
1347*7c478bd9Sstevel@tonic-gate 			    abuf, pi->pi_name);
1348*7c478bd9Sstevel@tonic-gate 			return (_B_FALSE);
1349*7c478bd9Sstevel@tonic-gate 		}
1350*7c478bd9Sstevel@tonic-gate 		opt = (struct nd_opt_hdr *)((char *)opt +
1351*7c478bd9Sstevel@tonic-gate 		    8 * opt->nd_opt_len);
1352*7c478bd9Sstevel@tonic-gate 	}
1353*7c478bd9Sstevel@tonic-gate 	return (_B_TRUE);
1354*7c478bd9Sstevel@tonic-gate }
1355