17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 59ba4fd8dSrshoaib * Common Development and Distribution License (the "License"). 69ba4fd8dSrshoaib * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 219ba4fd8dSrshoaib 227c478bd9Sstevel@tonic-gate /* 23*d04ccbb3Scarlsonj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate #include "defs.h" 307c478bd9Sstevel@tonic-gate #include "tables.h" 317c478bd9Sstevel@tonic-gate 327c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 337c478bd9Sstevel@tonic-gate 34*d04ccbb3Scarlsonj #include <dhcpagent_ipc.h> 35*d04ccbb3Scarlsonj #include <dhcpagent_util.h> 36*d04ccbb3Scarlsonj 377c478bd9Sstevel@tonic-gate static boolean_t verify_opt_len(struct nd_opt_hdr *opt, int optlen, 387c478bd9Sstevel@tonic-gate struct phyint *pi, struct sockaddr_in6 *from); 397c478bd9Sstevel@tonic-gate 407c478bd9Sstevel@tonic-gate static void incoming_rs(struct phyint *pi, struct nd_router_solicit *rs, 417c478bd9Sstevel@tonic-gate int len, struct sockaddr_in6 *from); 427c478bd9Sstevel@tonic-gate 437c478bd9Sstevel@tonic-gate void incoming_ra(struct phyint *pi, struct nd_router_advert *ra, 447c478bd9Sstevel@tonic-gate int len, struct sockaddr_in6 *from, boolean_t loopback); 457c478bd9Sstevel@tonic-gate static void incoming_prefix_opt(struct phyint *pi, uchar_t *opt, 467c478bd9Sstevel@tonic-gate struct sockaddr_in6 *from, boolean_t loopback); 47*d04ccbb3Scarlsonj static void incoming_prefix_onlink(struct phyint *pi, uchar_t *opt); 487c478bd9Sstevel@tonic-gate void incoming_prefix_onlink_process(struct prefix *pr, 497c478bd9Sstevel@tonic-gate uchar_t *opt); 50*d04ccbb3Scarlsonj static void incoming_prefix_stateful(struct phyint *, uchar_t *); 517c478bd9Sstevel@tonic-gate static boolean_t incoming_prefix_addrconf(struct phyint *pi, 527c478bd9Sstevel@tonic-gate uchar_t *opt, struct sockaddr_in6 *from, 537c478bd9Sstevel@tonic-gate boolean_t loopback); 547c478bd9Sstevel@tonic-gate boolean_t incoming_prefix_addrconf_process(struct phyint *pi, 557c478bd9Sstevel@tonic-gate struct prefix *pr, uchar_t *opt, 567c478bd9Sstevel@tonic-gate struct sockaddr_in6 *from, boolean_t loopback, 577c478bd9Sstevel@tonic-gate boolean_t new_prefix); 587c478bd9Sstevel@tonic-gate static void incoming_mtu_opt(struct phyint *pi, uchar_t *opt, 597c478bd9Sstevel@tonic-gate struct sockaddr_in6 *from); 607c478bd9Sstevel@tonic-gate static void incoming_lla_opt(struct phyint *pi, uchar_t *opt, 617c478bd9Sstevel@tonic-gate struct sockaddr_in6 *from, int isrouter); 627c478bd9Sstevel@tonic-gate 637c478bd9Sstevel@tonic-gate static void verify_ra_consistency(struct phyint *pi, 647c478bd9Sstevel@tonic-gate struct nd_router_advert *ra, 657c478bd9Sstevel@tonic-gate int len, struct sockaddr_in6 *from); 667c478bd9Sstevel@tonic-gate static void verify_prefix_opt(struct phyint *pi, uchar_t *opt, 677c478bd9Sstevel@tonic-gate char *frombuf); 687c478bd9Sstevel@tonic-gate static void verify_mtu_opt(struct phyint *pi, uchar_t *opt, 697c478bd9Sstevel@tonic-gate char *frombuf); 707c478bd9Sstevel@tonic-gate 71b0f490f4Smh static void update_ra_flag(const struct phyint *pi, 726b27086dSdd const struct sockaddr_in6 *from, int isrouter); 736b27086dSdd 747c478bd9Sstevel@tonic-gate /* 757c478bd9Sstevel@tonic-gate * Return a pointer to the specified option buffer. 767c478bd9Sstevel@tonic-gate * If not found return NULL. 777c478bd9Sstevel@tonic-gate */ 787c478bd9Sstevel@tonic-gate static void * 797c478bd9Sstevel@tonic-gate find_ancillary(struct msghdr *msg, int cmsg_type) 807c478bd9Sstevel@tonic-gate { 817c478bd9Sstevel@tonic-gate struct cmsghdr *cmsg; 827c478bd9Sstevel@tonic-gate 837c478bd9Sstevel@tonic-gate for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; 847c478bd9Sstevel@tonic-gate cmsg = CMSG_NXTHDR(msg, cmsg)) { 857c478bd9Sstevel@tonic-gate if (cmsg->cmsg_level == IPPROTO_IPV6 && 867c478bd9Sstevel@tonic-gate cmsg->cmsg_type == cmsg_type) { 877c478bd9Sstevel@tonic-gate return (CMSG_DATA(cmsg)); 887c478bd9Sstevel@tonic-gate } 897c478bd9Sstevel@tonic-gate } 907c478bd9Sstevel@tonic-gate return (NULL); 917c478bd9Sstevel@tonic-gate } 927c478bd9Sstevel@tonic-gate 937c478bd9Sstevel@tonic-gate void 947c478bd9Sstevel@tonic-gate in_data(struct phyint *pi) 957c478bd9Sstevel@tonic-gate { 967c478bd9Sstevel@tonic-gate struct sockaddr_in6 from; 977c478bd9Sstevel@tonic-gate struct icmp6_hdr *icmp; 987c478bd9Sstevel@tonic-gate struct nd_router_solicit *rs; 997c478bd9Sstevel@tonic-gate struct nd_router_advert *ra; 1007c478bd9Sstevel@tonic-gate static uint64_t in_packet[(IP_MAXPACKET + 1)/8]; 1017c478bd9Sstevel@tonic-gate static uint64_t ancillary_data[(IP_MAXPACKET + 1)/8]; 1027c478bd9Sstevel@tonic-gate int len; 1037c478bd9Sstevel@tonic-gate char abuf[INET6_ADDRSTRLEN]; 1047c478bd9Sstevel@tonic-gate const char *msgbuf; 1057c478bd9Sstevel@tonic-gate struct msghdr msg; 1067c478bd9Sstevel@tonic-gate struct iovec iov; 1077c478bd9Sstevel@tonic-gate uchar_t *opt; 1087c478bd9Sstevel@tonic-gate uint_t hoplimit; 1097c478bd9Sstevel@tonic-gate 1107c478bd9Sstevel@tonic-gate iov.iov_base = (char *)in_packet; 1117c478bd9Sstevel@tonic-gate iov.iov_len = sizeof (in_packet); 1127c478bd9Sstevel@tonic-gate msg.msg_iov = &iov; 1137c478bd9Sstevel@tonic-gate msg.msg_iovlen = 1; 1147c478bd9Sstevel@tonic-gate msg.msg_name = (struct sockaddr *)&from; 1157c478bd9Sstevel@tonic-gate msg.msg_namelen = sizeof (from); 1167c478bd9Sstevel@tonic-gate msg.msg_control = ancillary_data; 1177c478bd9Sstevel@tonic-gate msg.msg_controllen = sizeof (ancillary_data); 1187c478bd9Sstevel@tonic-gate 1197c478bd9Sstevel@tonic-gate if ((len = recvmsg(pi->pi_sock, &msg, 0)) < 0) { 1207c478bd9Sstevel@tonic-gate logperror_pi(pi, "in_data: recvfrom"); 1217c478bd9Sstevel@tonic-gate return; 1227c478bd9Sstevel@tonic-gate } 1237c478bd9Sstevel@tonic-gate if (len == 0) 1247c478bd9Sstevel@tonic-gate return; 1257c478bd9Sstevel@tonic-gate 1267c478bd9Sstevel@tonic-gate if (inet_ntop(AF_INET6, (void *)&from.sin6_addr, 1277c478bd9Sstevel@tonic-gate abuf, sizeof (abuf)) == NULL) 1287c478bd9Sstevel@tonic-gate msgbuf = "Unspecified Router"; 1297c478bd9Sstevel@tonic-gate else 1307c478bd9Sstevel@tonic-gate msgbuf = abuf; 1317c478bd9Sstevel@tonic-gate 1327c478bd9Sstevel@tonic-gate /* Ignore packets > 64k or control buffers that don't fit */ 1337c478bd9Sstevel@tonic-gate if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) { 1347c478bd9Sstevel@tonic-gate if (debug & D_PKTBAD) { 1357c478bd9Sstevel@tonic-gate logmsg(LOG_DEBUG, "Truncated message: msg_flags 0x%x " 1367c478bd9Sstevel@tonic-gate "from %s\n", msg.msg_flags, msgbuf); 1377c478bd9Sstevel@tonic-gate } 1387c478bd9Sstevel@tonic-gate return; 1397c478bd9Sstevel@tonic-gate } 1407c478bd9Sstevel@tonic-gate 1417c478bd9Sstevel@tonic-gate icmp = (struct icmp6_hdr *)in_packet; 1427c478bd9Sstevel@tonic-gate 1437c478bd9Sstevel@tonic-gate if (len < ICMP6_MINLEN) { 1447c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "Too short ICMP packet: %d bytes " 1457c478bd9Sstevel@tonic-gate "from %s on %s\n", 1467c478bd9Sstevel@tonic-gate len, msgbuf, pi->pi_name); 1477c478bd9Sstevel@tonic-gate return; 1487c478bd9Sstevel@tonic-gate } 1497c478bd9Sstevel@tonic-gate 1507c478bd9Sstevel@tonic-gate opt = find_ancillary(&msg, IPV6_HOPLIMIT); 1517c478bd9Sstevel@tonic-gate if (opt == NULL) { 1527c478bd9Sstevel@tonic-gate /* Unknown hoplimit - must drop */ 1537c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "Unknown hop limit from %s on %s\n", 1547c478bd9Sstevel@tonic-gate msgbuf, pi->pi_name); 1557c478bd9Sstevel@tonic-gate return; 1567c478bd9Sstevel@tonic-gate } 1577c478bd9Sstevel@tonic-gate hoplimit = *(uint_t *)opt; 1587c478bd9Sstevel@tonic-gate opt = find_ancillary(&msg, IPV6_RTHDR); 1597c478bd9Sstevel@tonic-gate if (opt != NULL) { 1607c478bd9Sstevel@tonic-gate /* Can't allow routing headers in ND messages */ 1617c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "ND message with routing header " 1627c478bd9Sstevel@tonic-gate "from %s on %s\n", 1637c478bd9Sstevel@tonic-gate msgbuf, pi->pi_name); 1647c478bd9Sstevel@tonic-gate return; 1657c478bd9Sstevel@tonic-gate } 1667c478bd9Sstevel@tonic-gate switch (icmp->icmp6_type) { 1677c478bd9Sstevel@tonic-gate case ND_ROUTER_SOLICIT: 1687c478bd9Sstevel@tonic-gate if (!pi->pi_AdvSendAdvertisements) 1697c478bd9Sstevel@tonic-gate return; 1707c478bd9Sstevel@tonic-gate if (pi->pi_flags & IFF_NORTEXCH) { 1717c478bd9Sstevel@tonic-gate if (debug & D_PKTIN) { 1727c478bd9Sstevel@tonic-gate logmsg(LOG_DEBUG, "Ignore received RS packet " 1737c478bd9Sstevel@tonic-gate "on %s (no route exchange on interface)\n", 1747c478bd9Sstevel@tonic-gate pi->pi_name); 1757c478bd9Sstevel@tonic-gate } 1767c478bd9Sstevel@tonic-gate return; 1777c478bd9Sstevel@tonic-gate } 1787c478bd9Sstevel@tonic-gate 1797c478bd9Sstevel@tonic-gate /* 1807c478bd9Sstevel@tonic-gate * Assumes that the kernel has verified the AH (if present) 1817c478bd9Sstevel@tonic-gate * and the ICMP checksum. 1827c478bd9Sstevel@tonic-gate */ 1837c478bd9Sstevel@tonic-gate if (hoplimit != IPV6_MAX_HOPS) { 1849ba4fd8dSrshoaib logmsg(LOG_DEBUG, "RS hop limit: %d from %s on %s\n", 1857c478bd9Sstevel@tonic-gate hoplimit, msgbuf, pi->pi_name); 1867c478bd9Sstevel@tonic-gate return; 1877c478bd9Sstevel@tonic-gate } 1887c478bd9Sstevel@tonic-gate 1897c478bd9Sstevel@tonic-gate if (icmp->icmp6_code != 0) { 1907c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RS code: %d from %s on %s\n", 1917c478bd9Sstevel@tonic-gate icmp->icmp6_code, msgbuf, pi->pi_name); 1927c478bd9Sstevel@tonic-gate return; 1937c478bd9Sstevel@tonic-gate } 1947c478bd9Sstevel@tonic-gate 1957c478bd9Sstevel@tonic-gate if (len < sizeof (struct nd_router_solicit)) { 1967c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RS too short: %d bytes " 1977c478bd9Sstevel@tonic-gate "from %s on %s\n", 1987c478bd9Sstevel@tonic-gate len, msgbuf, pi->pi_name); 1997c478bd9Sstevel@tonic-gate return; 2007c478bd9Sstevel@tonic-gate } 2017c478bd9Sstevel@tonic-gate rs = (struct nd_router_solicit *)icmp; 2027c478bd9Sstevel@tonic-gate if (len > sizeof (struct nd_router_solicit)) { 2037c478bd9Sstevel@tonic-gate if (!verify_opt_len((struct nd_opt_hdr *)&rs[1], 2047c478bd9Sstevel@tonic-gate len - sizeof (struct nd_router_solicit), pi, &from)) 2057c478bd9Sstevel@tonic-gate return; 2067c478bd9Sstevel@tonic-gate } 2077c478bd9Sstevel@tonic-gate if (debug & D_PKTIN) { 2087c478bd9Sstevel@tonic-gate print_route_sol("Received valid solicit from ", pi, 2097c478bd9Sstevel@tonic-gate rs, len, &from); 2107c478bd9Sstevel@tonic-gate } 2117c478bd9Sstevel@tonic-gate incoming_rs(pi, rs, len, &from); 2127c478bd9Sstevel@tonic-gate break; 2137c478bd9Sstevel@tonic-gate 2147c478bd9Sstevel@tonic-gate case ND_ROUTER_ADVERT: 2157c478bd9Sstevel@tonic-gate if (IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr)) { 2167c478bd9Sstevel@tonic-gate /* 2177c478bd9Sstevel@tonic-gate * Router advt. must have address! 2187c478bd9Sstevel@tonic-gate * Logging the news and returning. 2197c478bd9Sstevel@tonic-gate */ 2207c478bd9Sstevel@tonic-gate logmsg(LOG_DEBUG, 2217c478bd9Sstevel@tonic-gate "Router's address unspecified in advertisement\n"); 2227c478bd9Sstevel@tonic-gate return; 2237c478bd9Sstevel@tonic-gate } 2247c478bd9Sstevel@tonic-gate if (pi->pi_flags & IFF_NORTEXCH) { 2257c478bd9Sstevel@tonic-gate if (debug & D_PKTIN) { 2267c478bd9Sstevel@tonic-gate logmsg(LOG_DEBUG, "Ignore received RA packet " 2277c478bd9Sstevel@tonic-gate "on %s (no route exchange on interface)\n", 2287c478bd9Sstevel@tonic-gate pi->pi_name); 2297c478bd9Sstevel@tonic-gate } 2307c478bd9Sstevel@tonic-gate return; 2317c478bd9Sstevel@tonic-gate } 2327c478bd9Sstevel@tonic-gate 2337c478bd9Sstevel@tonic-gate /* 2347c478bd9Sstevel@tonic-gate * Assumes that the kernel has verified the AH (if present) 2357c478bd9Sstevel@tonic-gate * and the ICMP checksum. 2367c478bd9Sstevel@tonic-gate */ 2377c478bd9Sstevel@tonic-gate if (!IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) { 2389ba4fd8dSrshoaib logmsg(LOG_DEBUG, "RA from %s - not link local on %s\n", 2397c478bd9Sstevel@tonic-gate msgbuf, pi->pi_name); 2407c478bd9Sstevel@tonic-gate return; 2417c478bd9Sstevel@tonic-gate } 2427c478bd9Sstevel@tonic-gate 2437c478bd9Sstevel@tonic-gate if (hoplimit != IPV6_MAX_HOPS) { 2447c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RA hop limit: %d from %s on %s\n", 2457c478bd9Sstevel@tonic-gate hoplimit, msgbuf, pi->pi_name); 2467c478bd9Sstevel@tonic-gate return; 2477c478bd9Sstevel@tonic-gate } 2487c478bd9Sstevel@tonic-gate 2497c478bd9Sstevel@tonic-gate if (icmp->icmp6_code != 0) { 2507c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RA code: %d from %s on %s\n", 2517c478bd9Sstevel@tonic-gate icmp->icmp6_code, msgbuf, pi->pi_name); 2527c478bd9Sstevel@tonic-gate return; 2537c478bd9Sstevel@tonic-gate } 2547c478bd9Sstevel@tonic-gate 2557c478bd9Sstevel@tonic-gate if (len < sizeof (struct nd_router_advert)) { 2567c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RA too short: %d bytes " 2577c478bd9Sstevel@tonic-gate "from %s on %s\n", 2587c478bd9Sstevel@tonic-gate len, msgbuf, pi->pi_name); 2597c478bd9Sstevel@tonic-gate return; 2607c478bd9Sstevel@tonic-gate } 2617c478bd9Sstevel@tonic-gate ra = (struct nd_router_advert *)icmp; 2627c478bd9Sstevel@tonic-gate if (len > sizeof (struct nd_router_advert)) { 2637c478bd9Sstevel@tonic-gate if (!verify_opt_len((struct nd_opt_hdr *)&ra[1], 2647c478bd9Sstevel@tonic-gate len - sizeof (struct nd_router_advert), pi, &from)) 2657c478bd9Sstevel@tonic-gate return; 2667c478bd9Sstevel@tonic-gate } 2677c478bd9Sstevel@tonic-gate if (debug & D_PKTIN) { 2687c478bd9Sstevel@tonic-gate print_route_adv("Received valid advert from ", pi, 2697c478bd9Sstevel@tonic-gate ra, len, &from); 2707c478bd9Sstevel@tonic-gate } 2717c478bd9Sstevel@tonic-gate if (pi->pi_AdvSendAdvertisements) 2727c478bd9Sstevel@tonic-gate verify_ra_consistency(pi, ra, len, &from); 2737c478bd9Sstevel@tonic-gate else 2747c478bd9Sstevel@tonic-gate incoming_ra(pi, ra, len, &from, _B_FALSE); 2757c478bd9Sstevel@tonic-gate break; 2767c478bd9Sstevel@tonic-gate } 2777c478bd9Sstevel@tonic-gate } 2787c478bd9Sstevel@tonic-gate 2797c478bd9Sstevel@tonic-gate /* 2807c478bd9Sstevel@tonic-gate * Process a received router solicitation. 2817c478bd9Sstevel@tonic-gate * Check for source link-layer address option and check if it 2827c478bd9Sstevel@tonic-gate * is time to advertise. 2837c478bd9Sstevel@tonic-gate */ 2847c478bd9Sstevel@tonic-gate static void 2857c478bd9Sstevel@tonic-gate incoming_rs(struct phyint *pi, struct nd_router_solicit *rs, int len, 2867c478bd9Sstevel@tonic-gate struct sockaddr_in6 *from) 2877c478bd9Sstevel@tonic-gate { 2887c478bd9Sstevel@tonic-gate struct nd_opt_hdr *opt; 2897c478bd9Sstevel@tonic-gate int optlen; 2907c478bd9Sstevel@tonic-gate 2917c478bd9Sstevel@tonic-gate /* Process any options */ 2927c478bd9Sstevel@tonic-gate len -= sizeof (struct nd_router_solicit); 2937c478bd9Sstevel@tonic-gate opt = (struct nd_opt_hdr *)&rs[1]; 2947c478bd9Sstevel@tonic-gate while (len >= sizeof (struct nd_opt_hdr)) { 2957c478bd9Sstevel@tonic-gate optlen = opt->nd_opt_len * 8; 2967c478bd9Sstevel@tonic-gate switch (opt->nd_opt_type) { 2977c478bd9Sstevel@tonic-gate case ND_OPT_SOURCE_LINKADDR: 2987c478bd9Sstevel@tonic-gate incoming_lla_opt(pi, (uchar_t *)opt, 2997c478bd9Sstevel@tonic-gate from, NDF_ISROUTER_OFF); 3007c478bd9Sstevel@tonic-gate break; 3017c478bd9Sstevel@tonic-gate default: 3027c478bd9Sstevel@tonic-gate break; 3037c478bd9Sstevel@tonic-gate } 3047c478bd9Sstevel@tonic-gate opt = (struct nd_opt_hdr *)((char *)opt + optlen); 3057c478bd9Sstevel@tonic-gate len -= optlen; 3067c478bd9Sstevel@tonic-gate } 3077c478bd9Sstevel@tonic-gate /* Simple algorithm: treat unicast and multicast RSs the same */ 3087c478bd9Sstevel@tonic-gate check_to_advertise(pi, RECEIVED_SOLICIT); 3097c478bd9Sstevel@tonic-gate } 3107c478bd9Sstevel@tonic-gate 311*d04ccbb3Scarlsonj /* 312*d04ccbb3Scarlsonj * Start up DHCPv6 on a given physical interface. Does not wait for a message 313*d04ccbb3Scarlsonj * to be returned from the daemon. 314*d04ccbb3Scarlsonj */ 315*d04ccbb3Scarlsonj void 316*d04ccbb3Scarlsonj start_dhcp(struct phyint *pi) 317*d04ccbb3Scarlsonj { 318*d04ccbb3Scarlsonj dhcp_ipc_request_t *request; 319*d04ccbb3Scarlsonj dhcp_ipc_reply_t *reply = NULL; 320*d04ccbb3Scarlsonj int error; 321*d04ccbb3Scarlsonj int type; 322*d04ccbb3Scarlsonj 323*d04ccbb3Scarlsonj if (dhcp_start_agent(DHCP_IPC_MAX_WAIT) == -1) { 324*d04ccbb3Scarlsonj logmsg(LOG_ERR, "start_dhcp: unable to start %s\n", 325*d04ccbb3Scarlsonj DHCP_AGENT_PATH); 326*d04ccbb3Scarlsonj /* make sure we try again next time there's a chance */ 327*d04ccbb3Scarlsonj pi->pi_ra_flags &= ~ND_RA_FLAG_MANAGED & ~ND_RA_FLAG_OTHER; 328*d04ccbb3Scarlsonj return; 329*d04ccbb3Scarlsonj } 330*d04ccbb3Scarlsonj 331*d04ccbb3Scarlsonj type = (pi->pi_ra_flags & ND_RA_FLAG_MANAGED) ? DHCP_START : 332*d04ccbb3Scarlsonj DHCP_INFORM; 333*d04ccbb3Scarlsonj 334*d04ccbb3Scarlsonj request = dhcp_ipc_alloc_request(type | DHCP_V6, pi->pi_name, NULL, 0, 335*d04ccbb3Scarlsonj DHCP_TYPE_NONE); 336*d04ccbb3Scarlsonj if (request == NULL) { 337*d04ccbb3Scarlsonj logmsg(LOG_ERR, "start_dhcp: out of memory\n"); 338*d04ccbb3Scarlsonj /* make sure we try again next time there's a chance */ 339*d04ccbb3Scarlsonj pi->pi_ra_flags &= ~ND_RA_FLAG_MANAGED & ~ND_RA_FLAG_OTHER; 340*d04ccbb3Scarlsonj return; 341*d04ccbb3Scarlsonj } 342*d04ccbb3Scarlsonj 343*d04ccbb3Scarlsonj error = dhcp_ipc_make_request(request, &reply, 0); 344*d04ccbb3Scarlsonj free(request); 345*d04ccbb3Scarlsonj if (error != 0) { 346*d04ccbb3Scarlsonj logmsg(LOG_ERR, "start_dhcp: err: %s: %s\n", pi->pi_name, 347*d04ccbb3Scarlsonj dhcp_ipc_strerror(error)); 348*d04ccbb3Scarlsonj return; 349*d04ccbb3Scarlsonj } 350*d04ccbb3Scarlsonj 351*d04ccbb3Scarlsonj error = reply->return_code; 352*d04ccbb3Scarlsonj free(reply); 353*d04ccbb3Scarlsonj 354*d04ccbb3Scarlsonj /* 355*d04ccbb3Scarlsonj * Timeout is considered to be "success" because we don't wait for DHCP 356*d04ccbb3Scarlsonj * to do its exchange. 357*d04ccbb3Scarlsonj */ 358*d04ccbb3Scarlsonj if (error != DHCP_IPC_SUCCESS && error != DHCP_IPC_E_RUNNING && 359*d04ccbb3Scarlsonj error != DHCP_IPC_E_TIMEOUT) { 360*d04ccbb3Scarlsonj logmsg(LOG_ERR, "start_dhcp: ret: %s: %s\n", pi->pi_name, 361*d04ccbb3Scarlsonj dhcp_ipc_strerror(error)); 362*d04ccbb3Scarlsonj return; 363*d04ccbb3Scarlsonj } 364*d04ccbb3Scarlsonj } 365*d04ccbb3Scarlsonj 3667c478bd9Sstevel@tonic-gate /* 3677c478bd9Sstevel@tonic-gate * Process a received router advertisement. 3687c478bd9Sstevel@tonic-gate * Called both when packets arrive as well as when we send RAs. 3697c478bd9Sstevel@tonic-gate * In the latter case 'loopback' is set. 3707c478bd9Sstevel@tonic-gate */ 3717c478bd9Sstevel@tonic-gate void 3727c478bd9Sstevel@tonic-gate incoming_ra(struct phyint *pi, struct nd_router_advert *ra, int len, 3737c478bd9Sstevel@tonic-gate struct sockaddr_in6 *from, boolean_t loopback) 3747c478bd9Sstevel@tonic-gate { 3757c478bd9Sstevel@tonic-gate struct nd_opt_hdr *opt; 3767c478bd9Sstevel@tonic-gate int optlen; 3777c478bd9Sstevel@tonic-gate struct lifreq lifr; 3787c478bd9Sstevel@tonic-gate boolean_t set_needed = _B_FALSE; 3797c478bd9Sstevel@tonic-gate struct router *dr; 3807c478bd9Sstevel@tonic-gate uint16_t router_lifetime; 3817c478bd9Sstevel@tonic-gate uint_t reachable, retrans; 3827c478bd9Sstevel@tonic-gate boolean_t reachable_time_changed = _B_FALSE; 3836b27086dSdd boolean_t slla_opt_present = _B_FALSE; 3847c478bd9Sstevel@tonic-gate 3857c478bd9Sstevel@tonic-gate if (no_loopback && loopback) 3867c478bd9Sstevel@tonic-gate return; 3877c478bd9Sstevel@tonic-gate 3887c478bd9Sstevel@tonic-gate /* 3897c478bd9Sstevel@tonic-gate * If the interface is FAILED or INACTIVE or OFFLINE, don't 3907c478bd9Sstevel@tonic-gate * create any addresses on them. in.mpathd assumes that no new 3917c478bd9Sstevel@tonic-gate * addresses will appear on these. This implies that we 3927c478bd9Sstevel@tonic-gate * won't create any new prefixes advertised by the router 3937c478bd9Sstevel@tonic-gate * on FAILED/INACTIVE/OFFLINE interfaces. When the state changes, 3947c478bd9Sstevel@tonic-gate * the next RA will create the prefix on this interface. 3957c478bd9Sstevel@tonic-gate */ 3967c478bd9Sstevel@tonic-gate if (pi->pi_flags & (IFF_FAILED|IFF_INACTIVE|IFF_OFFLINE)) 3977c478bd9Sstevel@tonic-gate return; 3987c478bd9Sstevel@tonic-gate 3997c478bd9Sstevel@tonic-gate (void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name)); 4007c478bd9Sstevel@tonic-gate lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0'; 4017c478bd9Sstevel@tonic-gate if (ioctl(pi->pi_sock, SIOCGLIFLNKINFO, (char *)&lifr) < 0) { 4027c478bd9Sstevel@tonic-gate if (errno == ENXIO) 4037c478bd9Sstevel@tonic-gate return; 4047c478bd9Sstevel@tonic-gate logperror_pi(pi, "incoming_ra: SIOCGLIFLNKINFO"); 4057c478bd9Sstevel@tonic-gate return; 4067c478bd9Sstevel@tonic-gate } 4070a12cda4Sdd if (ra->nd_ra_curhoplimit != CURHOP_UNSPECIFIED && 4080a12cda4Sdd ra->nd_ra_curhoplimit != pi->pi_CurHopLimit) { 4097c478bd9Sstevel@tonic-gate pi->pi_CurHopLimit = ra->nd_ra_curhoplimit; 4107c478bd9Sstevel@tonic-gate 4117c478bd9Sstevel@tonic-gate lifr.lifr_ifinfo.lir_maxhops = pi->pi_CurHopLimit; 4127c478bd9Sstevel@tonic-gate set_needed = _B_TRUE; 4137c478bd9Sstevel@tonic-gate } 4147c478bd9Sstevel@tonic-gate 4157c478bd9Sstevel@tonic-gate reachable = ntohl(ra->nd_ra_reachable); 4167c478bd9Sstevel@tonic-gate if (reachable != 0 && 4177c478bd9Sstevel@tonic-gate reachable != pi->pi_BaseReachableTime) { 4187c478bd9Sstevel@tonic-gate pi->pi_BaseReachableTime = reachable; 4197c478bd9Sstevel@tonic-gate reachable_time_changed = _B_TRUE; 4207c478bd9Sstevel@tonic-gate } 4217c478bd9Sstevel@tonic-gate 4227c478bd9Sstevel@tonic-gate if (pi->pi_reach_time_since_random < MIN_REACH_RANDOM_INTERVAL || 4237c478bd9Sstevel@tonic-gate reachable_time_changed) { 4247c478bd9Sstevel@tonic-gate phyint_reach_random(pi, _B_FALSE); 4257c478bd9Sstevel@tonic-gate set_needed = _B_TRUE; 4267c478bd9Sstevel@tonic-gate } 4277c478bd9Sstevel@tonic-gate lifr.lifr_ifinfo.lir_reachtime = pi->pi_ReachableTime; 4287c478bd9Sstevel@tonic-gate 4297c478bd9Sstevel@tonic-gate retrans = ntohl(ra->nd_ra_retransmit); 4307c478bd9Sstevel@tonic-gate if (retrans != 0 && 4317c478bd9Sstevel@tonic-gate pi->pi_RetransTimer != retrans) { 4327c478bd9Sstevel@tonic-gate pi->pi_RetransTimer = retrans; 4337c478bd9Sstevel@tonic-gate lifr.lifr_ifinfo.lir_reachretrans = pi->pi_RetransTimer; 4347c478bd9Sstevel@tonic-gate set_needed = _B_TRUE; 4357c478bd9Sstevel@tonic-gate } 4367c478bd9Sstevel@tonic-gate 4377c478bd9Sstevel@tonic-gate if (set_needed) { 4387c478bd9Sstevel@tonic-gate if (ioctl(pi->pi_sock, SIOCSLIFLNKINFO, (char *)&lifr) < 0) { 4397c478bd9Sstevel@tonic-gate logperror_pi(pi, "incoming_ra: SIOCSLIFLNKINFO"); 4407c478bd9Sstevel@tonic-gate return; 4417c478bd9Sstevel@tonic-gate } 4427c478bd9Sstevel@tonic-gate } 4437c478bd9Sstevel@tonic-gate 444*d04ccbb3Scarlsonj /* 445*d04ccbb3Scarlsonj * If the "managed" flag is set, then just assume that the "other" flag 446*d04ccbb3Scarlsonj * is set as well. It's not legal to get addresses alone without 447*d04ccbb3Scarlsonj * getting other data. 448*d04ccbb3Scarlsonj */ 449*d04ccbb3Scarlsonj if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) 450*d04ccbb3Scarlsonj ra->nd_ra_flags_reserved |= ND_RA_FLAG_OTHER; 451*d04ccbb3Scarlsonj 452*d04ccbb3Scarlsonj /* 453*d04ccbb3Scarlsonj * If either the "managed" or "other" bits have turned on, then it's 454*d04ccbb3Scarlsonj * now time to invoke DHCP. If only the "other" bit is set, then don't 455*d04ccbb3Scarlsonj * get addresses via DHCP; only "other" data. If "managed" is set, 456*d04ccbb3Scarlsonj * then we must always get both addresses and "other" data. 457*d04ccbb3Scarlsonj */ 458*d04ccbb3Scarlsonj if (pi->pi_StatefulAddrConf && 459*d04ccbb3Scarlsonj (ra->nd_ra_flags_reserved & ~pi->pi_ra_flags & 460*d04ccbb3Scarlsonj (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER))) { 461*d04ccbb3Scarlsonj if (debug & D_DHCP) { 462*d04ccbb3Scarlsonj logmsg(LOG_DEBUG, 463*d04ccbb3Scarlsonj "incoming_ra: trigger dhcp %s on %s\n", 464*d04ccbb3Scarlsonj (ra->nd_ra_flags_reserved & ~pi->pi_ra_flags & 465*d04ccbb3Scarlsonj ND_RA_FLAG_MANAGED) ? "MANAGED" : "OTHER", 466*d04ccbb3Scarlsonj pi->pi_name); 4677c478bd9Sstevel@tonic-gate } 468*d04ccbb3Scarlsonj pi->pi_ra_flags |= ra->nd_ra_flags_reserved; 469*d04ccbb3Scarlsonj start_dhcp(pi); 4707c478bd9Sstevel@tonic-gate } 471*d04ccbb3Scarlsonj 4727c478bd9Sstevel@tonic-gate /* Skip default router code if sent from ourselves */ 4737c478bd9Sstevel@tonic-gate if (!loopback) { 4747c478bd9Sstevel@tonic-gate /* Find and update or add default router in list */ 4757c478bd9Sstevel@tonic-gate dr = router_lookup(pi, from->sin6_addr); 4767c478bd9Sstevel@tonic-gate router_lifetime = ntohs(ra->nd_ra_router_lifetime); 4777c478bd9Sstevel@tonic-gate if (dr == NULL) { 4787c478bd9Sstevel@tonic-gate if (router_lifetime != 0) { 4797c478bd9Sstevel@tonic-gate dr = router_create(pi, from->sin6_addr, 4807c478bd9Sstevel@tonic-gate MILLISEC * router_lifetime); 4817c478bd9Sstevel@tonic-gate timer_schedule(dr->dr_lifetime); 4827c478bd9Sstevel@tonic-gate } 4837c478bd9Sstevel@tonic-gate } else { 4847c478bd9Sstevel@tonic-gate dr->dr_lifetime = MILLISEC * router_lifetime; 4857c478bd9Sstevel@tonic-gate if (dr->dr_lifetime != 0) 4867c478bd9Sstevel@tonic-gate timer_schedule(dr->dr_lifetime); 4877c478bd9Sstevel@tonic-gate if ((dr->dr_lifetime != 0 && !dr->dr_inkernel) || 4887c478bd9Sstevel@tonic-gate (dr->dr_lifetime == 0 && dr->dr_inkernel)) 4897c478bd9Sstevel@tonic-gate router_update_k(dr); 4907c478bd9Sstevel@tonic-gate } 4917c478bd9Sstevel@tonic-gate } 4927c478bd9Sstevel@tonic-gate /* Process any options */ 4937c478bd9Sstevel@tonic-gate len -= sizeof (struct nd_router_advert); 4947c478bd9Sstevel@tonic-gate opt = (struct nd_opt_hdr *)&ra[1]; 4957c478bd9Sstevel@tonic-gate while (len >= sizeof (struct nd_opt_hdr)) { 4967c478bd9Sstevel@tonic-gate optlen = opt->nd_opt_len * 8; 4977c478bd9Sstevel@tonic-gate switch (opt->nd_opt_type) { 4987c478bd9Sstevel@tonic-gate case ND_OPT_PREFIX_INFORMATION: 4997c478bd9Sstevel@tonic-gate incoming_prefix_opt(pi, (uchar_t *)opt, from, 5007c478bd9Sstevel@tonic-gate loopback); 5017c478bd9Sstevel@tonic-gate break; 5027c478bd9Sstevel@tonic-gate case ND_OPT_MTU: 5037c478bd9Sstevel@tonic-gate incoming_mtu_opt(pi, (uchar_t *)opt, from); 5047c478bd9Sstevel@tonic-gate break; 5057c478bd9Sstevel@tonic-gate case ND_OPT_SOURCE_LINKADDR: 5067c478bd9Sstevel@tonic-gate /* skip lla option if sent from ourselves! */ 5077c478bd9Sstevel@tonic-gate if (!loopback) { 5087c478bd9Sstevel@tonic-gate incoming_lla_opt(pi, (uchar_t *)opt, 5097c478bd9Sstevel@tonic-gate from, NDF_ISROUTER_ON); 5106b27086dSdd slla_opt_present = _B_TRUE; 5117c478bd9Sstevel@tonic-gate } 5127c478bd9Sstevel@tonic-gate break; 5137c478bd9Sstevel@tonic-gate default: 5147c478bd9Sstevel@tonic-gate break; 5157c478bd9Sstevel@tonic-gate } 5167c478bd9Sstevel@tonic-gate opt = (struct nd_opt_hdr *)((char *)opt + optlen); 5177c478bd9Sstevel@tonic-gate len -= optlen; 5187c478bd9Sstevel@tonic-gate } 519b0f490f4Smh if (!loopback && !slla_opt_present) 5206b27086dSdd update_ra_flag(pi, from, NDF_ISROUTER_ON); 5217c478bd9Sstevel@tonic-gate /* Stop sending solicitations */ 5227c478bd9Sstevel@tonic-gate check_to_solicit(pi, SOLICIT_DONE); 5237c478bd9Sstevel@tonic-gate } 5247c478bd9Sstevel@tonic-gate 5257c478bd9Sstevel@tonic-gate /* 5267c478bd9Sstevel@tonic-gate * Process a received prefix option. 5277c478bd9Sstevel@tonic-gate * Unless addrconf is turned off we process both the addrconf and the 5287c478bd9Sstevel@tonic-gate * onlink aspects of the prefix option. 5297c478bd9Sstevel@tonic-gate * 5307c478bd9Sstevel@tonic-gate * Note that when a flag (onlink or auto) is turned off we do nothing - 5317c478bd9Sstevel@tonic-gate * the prefix will time out. 5327c478bd9Sstevel@tonic-gate */ 5337c478bd9Sstevel@tonic-gate static void 5347c478bd9Sstevel@tonic-gate incoming_prefix_opt(struct phyint *pi, uchar_t *opt, 5357c478bd9Sstevel@tonic-gate struct sockaddr_in6 *from, boolean_t loopback) 5367c478bd9Sstevel@tonic-gate { 5377c478bd9Sstevel@tonic-gate struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt; 5387c478bd9Sstevel@tonic-gate boolean_t good_prefix = _B_TRUE; 5397c478bd9Sstevel@tonic-gate 5407c478bd9Sstevel@tonic-gate if (8 * po->nd_opt_pi_len != sizeof (*po)) { 5417c478bd9Sstevel@tonic-gate char abuf[INET6_ADDRSTRLEN]; 5427c478bd9Sstevel@tonic-gate 5437c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, (void *)&from->sin6_addr, 5447c478bd9Sstevel@tonic-gate abuf, sizeof (abuf)); 5457c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "prefix option from %s on %s wrong size " 5467c478bd9Sstevel@tonic-gate "(%d bytes)\n", 5477c478bd9Sstevel@tonic-gate abuf, pi->pi_name, 5487c478bd9Sstevel@tonic-gate 8 * (int)po->nd_opt_pi_len); 5497c478bd9Sstevel@tonic-gate return; 5507c478bd9Sstevel@tonic-gate } 5517c478bd9Sstevel@tonic-gate if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) { 5527c478bd9Sstevel@tonic-gate char abuf[INET6_ADDRSTRLEN]; 5537c478bd9Sstevel@tonic-gate 5547c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, (void *)&from->sin6_addr, 5557c478bd9Sstevel@tonic-gate abuf, sizeof (abuf)); 5567c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RA from %s on %s contains link-local prefix " 5577c478bd9Sstevel@tonic-gate "- ignored\n", 5587c478bd9Sstevel@tonic-gate abuf, pi->pi_name); 5597c478bd9Sstevel@tonic-gate return; 5607c478bd9Sstevel@tonic-gate } 5617c478bd9Sstevel@tonic-gate if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) && 5627c478bd9Sstevel@tonic-gate pi->pi_StatelessAddrConf) { 5637c478bd9Sstevel@tonic-gate good_prefix = incoming_prefix_addrconf(pi, opt, from, loopback); 5647c478bd9Sstevel@tonic-gate } 5657c478bd9Sstevel@tonic-gate if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) && 5667c478bd9Sstevel@tonic-gate good_prefix) { 567*d04ccbb3Scarlsonj incoming_prefix_onlink(pi, opt); 5687c478bd9Sstevel@tonic-gate } 569*d04ccbb3Scarlsonj if (pi->pi_StatefulAddrConf) 570*d04ccbb3Scarlsonj incoming_prefix_stateful(pi, opt); 5717c478bd9Sstevel@tonic-gate } 5727c478bd9Sstevel@tonic-gate 5737c478bd9Sstevel@tonic-gate /* 5747c478bd9Sstevel@tonic-gate * Process prefix options with the onlink flag set. 5757c478bd9Sstevel@tonic-gate * 5767c478bd9Sstevel@tonic-gate * If there are no routers ndpd will add an onlink 5777c478bd9Sstevel@tonic-gate * default route which will allow communication 5787c478bd9Sstevel@tonic-gate * between neighbors. 5797c478bd9Sstevel@tonic-gate * 5807c478bd9Sstevel@tonic-gate * This function needs to loop to find the same prefix multiple times 5817c478bd9Sstevel@tonic-gate * as if a failover happened earlier, the addresses belonging to 5827c478bd9Sstevel@tonic-gate * a different interface may be found here on this interface. 5837c478bd9Sstevel@tonic-gate */ 5847c478bd9Sstevel@tonic-gate static void 585*d04ccbb3Scarlsonj incoming_prefix_onlink(struct phyint *pi, uchar_t *opt) 5867c478bd9Sstevel@tonic-gate { 5877c478bd9Sstevel@tonic-gate struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt; 5887c478bd9Sstevel@tonic-gate int plen; 5897c478bd9Sstevel@tonic-gate struct prefix *pr; 5907c478bd9Sstevel@tonic-gate uint32_t validtime; /* Without 2 hour rule */ 5917c478bd9Sstevel@tonic-gate boolean_t found_one = _B_FALSE; 5927c478bd9Sstevel@tonic-gate 5937c478bd9Sstevel@tonic-gate plen = po->nd_opt_pi_prefix_len; 5947c478bd9Sstevel@tonic-gate for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) { 5957c478bd9Sstevel@tonic-gate if (pr->pr_prefix_len == plen && 5967c478bd9Sstevel@tonic-gate prefix_equal(po->nd_opt_pi_prefix, pr->pr_prefix, plen)) { 5977c478bd9Sstevel@tonic-gate /* Exclude static prefixes */ 5987c478bd9Sstevel@tonic-gate if (pr->pr_state & PR_STATIC) 5997c478bd9Sstevel@tonic-gate continue; 6007c478bd9Sstevel@tonic-gate found_one = _B_TRUE; 6017c478bd9Sstevel@tonic-gate incoming_prefix_onlink_process(pr, opt); 6027c478bd9Sstevel@tonic-gate } 6037c478bd9Sstevel@tonic-gate } 6047c478bd9Sstevel@tonic-gate 6057c478bd9Sstevel@tonic-gate validtime = ntohl(po->nd_opt_pi_valid_time); 6067c478bd9Sstevel@tonic-gate /* 6077c478bd9Sstevel@tonic-gate * If we have found a matching prefix already or validtime 6087c478bd9Sstevel@tonic-gate * is zero, we have nothing to do. 6097c478bd9Sstevel@tonic-gate */ 6107c478bd9Sstevel@tonic-gate if (validtime == 0 || found_one) 6117c478bd9Sstevel@tonic-gate return; 6127c478bd9Sstevel@tonic-gate pr = prefix_create(pi, po->nd_opt_pi_prefix, plen, 0); 6137c478bd9Sstevel@tonic-gate if (pr == NULL) 6147c478bd9Sstevel@tonic-gate return; 6157c478bd9Sstevel@tonic-gate incoming_prefix_onlink_process(pr, opt); 6167c478bd9Sstevel@tonic-gate } 6177c478bd9Sstevel@tonic-gate 6187c478bd9Sstevel@tonic-gate void 6197c478bd9Sstevel@tonic-gate incoming_prefix_onlink_process(struct prefix *pr, uchar_t *opt) 6207c478bd9Sstevel@tonic-gate { 6217c478bd9Sstevel@tonic-gate struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt; 6227c478bd9Sstevel@tonic-gate uint32_t validtime; /* Without 2 hour rule */ 6237c478bd9Sstevel@tonic-gate char abuf[INET6_ADDRSTRLEN]; 6247c478bd9Sstevel@tonic-gate 6257c478bd9Sstevel@tonic-gate validtime = ntohl(po->nd_opt_pi_valid_time); 6267c478bd9Sstevel@tonic-gate if (validtime != 0) 6277c478bd9Sstevel@tonic-gate pr->pr_state |= PR_ONLINK; 6287c478bd9Sstevel@tonic-gate else 6297c478bd9Sstevel@tonic-gate pr->pr_state &= ~PR_ONLINK; 6307c478bd9Sstevel@tonic-gate 6317c478bd9Sstevel@tonic-gate /* 6327c478bd9Sstevel@tonic-gate * Convert from seconds to milliseconds avoiding overflow. 6337c478bd9Sstevel@tonic-gate * If the lifetime in the packet is e.g. PREFIX_INFINITY - 1 6347c478bd9Sstevel@tonic-gate * (4 billion seconds - about 130 years) we will in fact time 6357c478bd9Sstevel@tonic-gate * out the prefix after 4 billion milliseconds - 46 days). 6367c478bd9Sstevel@tonic-gate * Thus the longest lifetime (apart from infinity) is 46 days. 6377c478bd9Sstevel@tonic-gate * Note that this ensures that PREFIX_INFINITY still means "forever". 6387c478bd9Sstevel@tonic-gate */ 6397c478bd9Sstevel@tonic-gate if (pr->pr_flags & IFF_TEMPORARY) { 6407c478bd9Sstevel@tonic-gate pr->pr_OnLinkLifetime = pr->pr_ValidLifetime; 6417c478bd9Sstevel@tonic-gate } else { 6427c478bd9Sstevel@tonic-gate if (validtime >= PREFIX_INFINITY / MILLISEC) 6437c478bd9Sstevel@tonic-gate pr->pr_OnLinkLifetime = PREFIX_INFINITY - 1; 6447c478bd9Sstevel@tonic-gate else 6457c478bd9Sstevel@tonic-gate pr->pr_OnLinkLifetime = validtime * MILLISEC; 6467c478bd9Sstevel@tonic-gate } 6477c478bd9Sstevel@tonic-gate pr->pr_OnLinkFlag = _B_TRUE; 6487c478bd9Sstevel@tonic-gate if (debug & (D_PREFIX|D_TMP)) { 6497c478bd9Sstevel@tonic-gate logmsg(LOG_DEBUG, "incoming_prefix_onlink_process(%s, %s/%u) " 6507c478bd9Sstevel@tonic-gate "onlink %u state 0x%x, kstate 0x%x\n", 6517c478bd9Sstevel@tonic-gate pr->pr_name, inet_ntop(AF_INET6, (void *)&pr->pr_prefix, 6527c478bd9Sstevel@tonic-gate abuf, sizeof (abuf)), pr->pr_prefix_len, 6537c478bd9Sstevel@tonic-gate pr->pr_OnLinkLifetime, pr->pr_state, pr->pr_kernel_state); 6547c478bd9Sstevel@tonic-gate } 6557c478bd9Sstevel@tonic-gate 6567c478bd9Sstevel@tonic-gate if (pr->pr_kernel_state != pr->pr_state) { 6577c478bd9Sstevel@tonic-gate prefix_update_k(pr); 6587c478bd9Sstevel@tonic-gate } 6597c478bd9Sstevel@tonic-gate 6607c478bd9Sstevel@tonic-gate if (pr->pr_OnLinkLifetime != 0) 6617c478bd9Sstevel@tonic-gate timer_schedule(pr->pr_OnLinkLifetime); 6627c478bd9Sstevel@tonic-gate } 6637c478bd9Sstevel@tonic-gate 664*d04ccbb3Scarlsonj /* 665*d04ccbb3Scarlsonj * Process all prefix options by locating the DHCPv6-configured interfaces, and 666*d04ccbb3Scarlsonj * applying the netmasks as needed. 667*d04ccbb3Scarlsonj */ 668*d04ccbb3Scarlsonj static void 669*d04ccbb3Scarlsonj incoming_prefix_stateful(struct phyint *pi, uchar_t *opt) 670*d04ccbb3Scarlsonj { 671*d04ccbb3Scarlsonj struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt; 672*d04ccbb3Scarlsonj struct prefix *pr; 673*d04ccbb3Scarlsonj boolean_t foundpref; 674*d04ccbb3Scarlsonj char abuf[INET6_ADDRSTRLEN]; 675*d04ccbb3Scarlsonj 676*d04ccbb3Scarlsonj /* Make sure it's a valid prefix. */ 677*d04ccbb3Scarlsonj if (ntohl(po->nd_opt_pi_valid_time) == 0) { 678*d04ccbb3Scarlsonj if (debug & D_DHCP) 679*d04ccbb3Scarlsonj logmsg(LOG_DEBUG, "incoming_prefix_stateful: ignoring " 680*d04ccbb3Scarlsonj "prefix with no valid time\n"); 681*d04ccbb3Scarlsonj return; 682*d04ccbb3Scarlsonj } 683*d04ccbb3Scarlsonj 684*d04ccbb3Scarlsonj if (debug & D_DHCP) 685*d04ccbb3Scarlsonj logmsg(LOG_DEBUG, "incoming_prefix_stateful(%s, %s/%d)\n", 686*d04ccbb3Scarlsonj pi->pi_name, inet_ntop(AF_INET6, 687*d04ccbb3Scarlsonj (void *)&po->nd_opt_pi_prefix, abuf, sizeof (abuf)), 688*d04ccbb3Scarlsonj po->nd_opt_pi_prefix_len); 689*d04ccbb3Scarlsonj foundpref = _B_FALSE; 690*d04ccbb3Scarlsonj for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) { 691*d04ccbb3Scarlsonj if (prefix_equal(po->nd_opt_pi_prefix, pr->pr_address, 692*d04ccbb3Scarlsonj po->nd_opt_pi_prefix_len)) { 693*d04ccbb3Scarlsonj if ((pr->pr_flags & IFF_DHCPRUNNING) && 694*d04ccbb3Scarlsonj pr->pr_prefix_len != po->nd_opt_pi_prefix_len) { 695*d04ccbb3Scarlsonj pr->pr_prefix_len = po->nd_opt_pi_prefix_len; 696*d04ccbb3Scarlsonj if (pr->pr_flags & IFF_UP) { 697*d04ccbb3Scarlsonj if (debug & D_DHCP) 698*d04ccbb3Scarlsonj logmsg(LOG_DEBUG, 699*d04ccbb3Scarlsonj "incoming_prefix_stateful:" 700*d04ccbb3Scarlsonj " set mask on DHCP %s\n", 701*d04ccbb3Scarlsonj pr->pr_name); 702*d04ccbb3Scarlsonj prefix_update_dhcp(pr); 703*d04ccbb3Scarlsonj } 704*d04ccbb3Scarlsonj } 705*d04ccbb3Scarlsonj if (pr->pr_prefix_len == po->nd_opt_pi_prefix_len && 706*d04ccbb3Scarlsonj (!(pr->pr_state & PR_STATIC) || 707*d04ccbb3Scarlsonj (pr->pr_flags & IFF_DHCPRUNNING))) 708*d04ccbb3Scarlsonj foundpref = _B_TRUE; 709*d04ccbb3Scarlsonj } 710*d04ccbb3Scarlsonj } 711*d04ccbb3Scarlsonj /* 712*d04ccbb3Scarlsonj * If there's no matching DHCPv6 prefix present, then create an empty 713*d04ccbb3Scarlsonj * one so that we'll be able to configure it later. 714*d04ccbb3Scarlsonj */ 715*d04ccbb3Scarlsonj if (!foundpref) { 716*d04ccbb3Scarlsonj pr = prefix_create(pi, po->nd_opt_pi_prefix, 717*d04ccbb3Scarlsonj po->nd_opt_pi_prefix_len, IFF_DHCPRUNNING); 718*d04ccbb3Scarlsonj if (pr != NULL) { 719*d04ccbb3Scarlsonj pr->pr_state = PR_STATIC; 720*d04ccbb3Scarlsonj if (debug & D_DHCP) 721*d04ccbb3Scarlsonj logmsg(LOG_DEBUG, 722*d04ccbb3Scarlsonj "incoming_prefix_stateful: created dummy " 723*d04ccbb3Scarlsonj "prefix for later\n"); 724*d04ccbb3Scarlsonj } 725*d04ccbb3Scarlsonj } 726*d04ccbb3Scarlsonj } 727*d04ccbb3Scarlsonj 7287c478bd9Sstevel@tonic-gate /* 7297c478bd9Sstevel@tonic-gate * Process prefix options with the autonomous flag set. 7307c478bd9Sstevel@tonic-gate * Returns false if this prefix results in a bad address (duplicate) 7317c478bd9Sstevel@tonic-gate * This function needs to loop to find the same prefix multiple times 7327c478bd9Sstevel@tonic-gate * as if a failover happened earlier, the addresses belonging to 7337c478bd9Sstevel@tonic-gate * a different interface may be found here on this interface. 7347c478bd9Sstevel@tonic-gate */ 7357c478bd9Sstevel@tonic-gate static boolean_t 7367c478bd9Sstevel@tonic-gate incoming_prefix_addrconf(struct phyint *pi, uchar_t *opt, 7377c478bd9Sstevel@tonic-gate struct sockaddr_in6 *from, boolean_t loopback) 7387c478bd9Sstevel@tonic-gate { 7397c478bd9Sstevel@tonic-gate struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt; 7407c478bd9Sstevel@tonic-gate int plen; 7417c478bd9Sstevel@tonic-gate struct prefix *pr; 7427c478bd9Sstevel@tonic-gate uint32_t validtime, preftime; /* In seconds */ 7437c478bd9Sstevel@tonic-gate char abuf[INET6_ADDRSTRLEN]; 7447c478bd9Sstevel@tonic-gate char pbuf[INET6_ADDRSTRLEN]; 7457c478bd9Sstevel@tonic-gate boolean_t found_pub = _B_FALSE; 7467c478bd9Sstevel@tonic-gate boolean_t found_tmp = _B_FALSE; 7477c478bd9Sstevel@tonic-gate boolean_t ret; 7487c478bd9Sstevel@tonic-gate 7497c478bd9Sstevel@tonic-gate validtime = ntohl(po->nd_opt_pi_valid_time); 7507c478bd9Sstevel@tonic-gate preftime = ntohl(po->nd_opt_pi_preferred_time); 7517c478bd9Sstevel@tonic-gate plen = po->nd_opt_pi_prefix_len; 7527c478bd9Sstevel@tonic-gate 7537c478bd9Sstevel@tonic-gate /* Sanity checks */ 7547c478bd9Sstevel@tonic-gate if (validtime < preftime) { 7557c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, (void *)&from->sin6_addr, 7567c478bd9Sstevel@tonic-gate abuf, sizeof (abuf)); 7577c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, 7587c478bd9Sstevel@tonic-gate (void *)&po->nd_opt_pi_prefix, 7597c478bd9Sstevel@tonic-gate pbuf, sizeof (pbuf)); 7607c478bd9Sstevel@tonic-gate logmsg(LOG_WARNING, "prefix option %s/%u from %s on %s: " 7617c478bd9Sstevel@tonic-gate "valid %u < pref %u ignored\n", 7627c478bd9Sstevel@tonic-gate pbuf, plen, abuf, pi->pi_name, 7637c478bd9Sstevel@tonic-gate validtime, preftime); 7647c478bd9Sstevel@tonic-gate return (_B_FALSE); 7657c478bd9Sstevel@tonic-gate } 7667c478bd9Sstevel@tonic-gate 7677c478bd9Sstevel@tonic-gate for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) { 7687c478bd9Sstevel@tonic-gate if (pr->pr_prefix_len == plen && 7697c478bd9Sstevel@tonic-gate prefix_equal(po->nd_opt_pi_prefix, pr->pr_prefix, plen)) { 7707c478bd9Sstevel@tonic-gate 771*d04ccbb3Scarlsonj /* Exclude static prefixes and DHCP */ 772*d04ccbb3Scarlsonj if ((pr->pr_state & PR_STATIC) || 773*d04ccbb3Scarlsonj (pr->pr_flags & IFF_DHCPRUNNING)) 7747c478bd9Sstevel@tonic-gate continue; 7757c478bd9Sstevel@tonic-gate if (pr->pr_flags & IFF_TEMPORARY) { 7767c478bd9Sstevel@tonic-gate /* 7777c478bd9Sstevel@tonic-gate * If this address is deprecated and its token 7787c478bd9Sstevel@tonic-gate * doesn't match the current tmp token, we want 7797c478bd9Sstevel@tonic-gate * to create a new address with the current 7807c478bd9Sstevel@tonic-gate * token. So don't count this addr as a match. 7817c478bd9Sstevel@tonic-gate */ 7827c478bd9Sstevel@tonic-gate if (!((pr->pr_flags & IFF_DEPRECATED) && 7837c478bd9Sstevel@tonic-gate !token_equal(pi->pi_tmp_token, 7847c478bd9Sstevel@tonic-gate pr->pr_address, TMP_TOKEN_BITS))) 7857c478bd9Sstevel@tonic-gate found_tmp = _B_TRUE; 7867c478bd9Sstevel@tonic-gate } else { 7877c478bd9Sstevel@tonic-gate found_pub = _B_TRUE; 7887c478bd9Sstevel@tonic-gate } 7897c478bd9Sstevel@tonic-gate (void) incoming_prefix_addrconf_process(pi, pr, opt, 7907c478bd9Sstevel@tonic-gate from, loopback, _B_FALSE); 7917c478bd9Sstevel@tonic-gate } 7927c478bd9Sstevel@tonic-gate } 7937c478bd9Sstevel@tonic-gate 7947c478bd9Sstevel@tonic-gate /* 7957c478bd9Sstevel@tonic-gate * If we have found a matching prefix (for public and, if temp addrs 7967c478bd9Sstevel@tonic-gate * are enabled, for temporary) already or validtime is zero, we have 7977c478bd9Sstevel@tonic-gate * nothing to do. 7987c478bd9Sstevel@tonic-gate */ 7997c478bd9Sstevel@tonic-gate if (validtime == 0 || 8007c478bd9Sstevel@tonic-gate (found_pub && (!pi->pi_TmpAddrsEnabled || found_tmp))) 8017c478bd9Sstevel@tonic-gate return (_B_TRUE); 8027c478bd9Sstevel@tonic-gate 8037c478bd9Sstevel@tonic-gate if (!found_pub) { 8047c478bd9Sstevel@tonic-gate pr = prefix_create(pi, po->nd_opt_pi_prefix, plen, 0); 8057c478bd9Sstevel@tonic-gate if (pr == NULL) 8067c478bd9Sstevel@tonic-gate return (_B_TRUE); 8077c478bd9Sstevel@tonic-gate ret = incoming_prefix_addrconf_process(pi, pr, opt, from, 8087c478bd9Sstevel@tonic-gate loopback, _B_TRUE); 8097c478bd9Sstevel@tonic-gate } 8107c478bd9Sstevel@tonic-gate /* 8117c478bd9Sstevel@tonic-gate * if processing of the public address failed, 8127c478bd9Sstevel@tonic-gate * don't bother with the temporary address. 8137c478bd9Sstevel@tonic-gate */ 8147c478bd9Sstevel@tonic-gate if (ret == _B_FALSE) 8157c478bd9Sstevel@tonic-gate return (_B_FALSE); 8167c478bd9Sstevel@tonic-gate 8177c478bd9Sstevel@tonic-gate if (pi->pi_TmpAddrsEnabled && !found_tmp) { 8187c478bd9Sstevel@tonic-gate pr = prefix_create(pi, po->nd_opt_pi_prefix, plen, 8197c478bd9Sstevel@tonic-gate IFF_TEMPORARY); 8207c478bd9Sstevel@tonic-gate if (pr == NULL) 8217c478bd9Sstevel@tonic-gate return (_B_TRUE); 8227c478bd9Sstevel@tonic-gate ret = incoming_prefix_addrconf_process(pi, pr, opt, from, 8237c478bd9Sstevel@tonic-gate loopback, _B_TRUE); 8247c478bd9Sstevel@tonic-gate } 8257c478bd9Sstevel@tonic-gate 8267c478bd9Sstevel@tonic-gate return (ret); 8277c478bd9Sstevel@tonic-gate } 8287c478bd9Sstevel@tonic-gate 8297c478bd9Sstevel@tonic-gate boolean_t 8307c478bd9Sstevel@tonic-gate incoming_prefix_addrconf_process(struct phyint *pi, struct prefix *pr, 8317c478bd9Sstevel@tonic-gate uchar_t *opt, struct sockaddr_in6 *from, boolean_t loopback, 8327c478bd9Sstevel@tonic-gate boolean_t new_prefix) 8337c478bd9Sstevel@tonic-gate { 8347c478bd9Sstevel@tonic-gate struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt; 8357c478bd9Sstevel@tonic-gate char abuf[INET6_ADDRSTRLEN]; 8367c478bd9Sstevel@tonic-gate char pbuf[INET6_ADDRSTRLEN]; 8377c478bd9Sstevel@tonic-gate uint32_t validtime, preftime; /* In seconds */ 8387c478bd9Sstevel@tonic-gate uint32_t recorded_validtime; /* In seconds */ 83969bb4bb4Scarlsonj int plen; 8407c478bd9Sstevel@tonic-gate struct prefix *other_pr; 8417c478bd9Sstevel@tonic-gate 8427c478bd9Sstevel@tonic-gate validtime = ntohl(po->nd_opt_pi_valid_time); 8437c478bd9Sstevel@tonic-gate preftime = ntohl(po->nd_opt_pi_preferred_time); 8447c478bd9Sstevel@tonic-gate plen = po->nd_opt_pi_prefix_len; 8457c478bd9Sstevel@tonic-gate if (!new_prefix) { 8467c478bd9Sstevel@tonic-gate /* 8477c478bd9Sstevel@tonic-gate * Check 2 hour rule on valid lifetime. 8487c478bd9Sstevel@tonic-gate * Follows: RFC 2462 8497c478bd9Sstevel@tonic-gate * If we advertised this prefix ourselves we skip 8507c478bd9Sstevel@tonic-gate * these checks. They are also skipped if we did not 8517c478bd9Sstevel@tonic-gate * previously do addrconf on this prefix. 8527c478bd9Sstevel@tonic-gate */ 8537c478bd9Sstevel@tonic-gate recorded_validtime = pr->pr_ValidLifetime / MILLISEC; 8547c478bd9Sstevel@tonic-gate 8557c478bd9Sstevel@tonic-gate if (loopback || !(pr->pr_state & PR_AUTO) || 8567c478bd9Sstevel@tonic-gate validtime >= MIN_VALID_LIFETIME || 8577c478bd9Sstevel@tonic-gate /* LINTED - statement has no consequent */ 8587c478bd9Sstevel@tonic-gate validtime >= recorded_validtime) { 8597c478bd9Sstevel@tonic-gate /* OK */ 8607c478bd9Sstevel@tonic-gate } else if (recorded_validtime < MIN_VALID_LIFETIME && 8617c478bd9Sstevel@tonic-gate validtime < recorded_validtime) { 8627c478bd9Sstevel@tonic-gate /* Ignore the prefix */ 8637c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, 8647c478bd9Sstevel@tonic-gate (void *)&from->sin6_addr, 8657c478bd9Sstevel@tonic-gate abuf, sizeof (abuf)); 8667c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, 8677c478bd9Sstevel@tonic-gate (void *)&po->nd_opt_pi_prefix, 8687c478bd9Sstevel@tonic-gate pbuf, sizeof (pbuf)); 8697c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: " 8707c478bd9Sstevel@tonic-gate "too short valid lifetime %u stored %u " 8717c478bd9Sstevel@tonic-gate "- ignored\n", 8727c478bd9Sstevel@tonic-gate pbuf, plen, abuf, pi->pi_name, 8737c478bd9Sstevel@tonic-gate validtime, recorded_validtime); 8747c478bd9Sstevel@tonic-gate return (_B_TRUE); 8757c478bd9Sstevel@tonic-gate } else { 8767c478bd9Sstevel@tonic-gate /* 8777c478bd9Sstevel@tonic-gate * If the router clock runs slower than the 8787c478bd9Sstevel@tonic-gate * host by 1 second over 2 hours then this 8797c478bd9Sstevel@tonic-gate * test will set the lifetime back to 2 hours 8807c478bd9Sstevel@tonic-gate * once i.e. a lifetime decrementing in 8817c478bd9Sstevel@tonic-gate * realtime might cause the prefix to live an 8827c478bd9Sstevel@tonic-gate * extra 2 hours on the host. 8837c478bd9Sstevel@tonic-gate */ 8847c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, 8857c478bd9Sstevel@tonic-gate (void *)&from->sin6_addr, 8867c478bd9Sstevel@tonic-gate abuf, sizeof (abuf)); 8877c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, 8887c478bd9Sstevel@tonic-gate (void *)&po->nd_opt_pi_prefix, 8897c478bd9Sstevel@tonic-gate pbuf, sizeof (pbuf)); 8907c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: " 8917c478bd9Sstevel@tonic-gate "valid time %u stored %u rounded up " 8927c478bd9Sstevel@tonic-gate "to %u\n", 8937c478bd9Sstevel@tonic-gate pbuf, plen, abuf, pi->pi_name, 8947c478bd9Sstevel@tonic-gate validtime, recorded_validtime, 8957c478bd9Sstevel@tonic-gate MIN_VALID_LIFETIME); 8967c478bd9Sstevel@tonic-gate validtime = MIN_VALID_LIFETIME; 8977c478bd9Sstevel@tonic-gate } 8987c478bd9Sstevel@tonic-gate } 8997c478bd9Sstevel@tonic-gate 9007c478bd9Sstevel@tonic-gate /* 9017c478bd9Sstevel@tonic-gate * For RFC3041 addresses, need to take token lifetime 9027c478bd9Sstevel@tonic-gate * into account, too. 9037c478bd9Sstevel@tonic-gate */ 9047c478bd9Sstevel@tonic-gate if (pr->pr_flags & IFF_TEMPORARY) { 9057c478bd9Sstevel@tonic-gate uint_t cur_tpreftime = 9067c478bd9Sstevel@tonic-gate pi->pi_TmpPreferredLifetime - pi->pi_TmpDesyncFactor; 9077c478bd9Sstevel@tonic-gate 9087c478bd9Sstevel@tonic-gate if (new_prefix) { 9097c478bd9Sstevel@tonic-gate validtime = MIN(validtime, pi->pi_TmpValidLifetime); 9107c478bd9Sstevel@tonic-gate preftime = MIN(preftime, cur_tpreftime); 9117c478bd9Sstevel@tonic-gate } else { 9127c478bd9Sstevel@tonic-gate uint_t cur_vexp, cur_pexp, curtime; 9137c478bd9Sstevel@tonic-gate curtime = getcurrenttime() / MILLISEC; 9147c478bd9Sstevel@tonic-gate 9157c478bd9Sstevel@tonic-gate cur_vexp = pr->pr_CreateTime + pi->pi_TmpValidLifetime; 9167c478bd9Sstevel@tonic-gate cur_pexp = pr->pr_CreateTime + cur_tpreftime; 9177c478bd9Sstevel@tonic-gate if (curtime > cur_vexp) 9187c478bd9Sstevel@tonic-gate validtime = 0; 9197c478bd9Sstevel@tonic-gate else if ((curtime + validtime) > cur_vexp) 9207c478bd9Sstevel@tonic-gate validtime = cur_vexp - curtime; 9217c478bd9Sstevel@tonic-gate /* 9227c478bd9Sstevel@tonic-gate * If this is an existing address which was deprecated 9237c478bd9Sstevel@tonic-gate * because of a bad token, we don't want to update its 9247c478bd9Sstevel@tonic-gate * preferred lifetime! 9257c478bd9Sstevel@tonic-gate */ 9267c478bd9Sstevel@tonic-gate if ((pr->pr_PreferredLifetime == 0) && 9277c478bd9Sstevel@tonic-gate !token_equal(pr->pr_address, pi->pi_tmp_token, 9287c478bd9Sstevel@tonic-gate TMP_TOKEN_BITS)) 9297c478bd9Sstevel@tonic-gate preftime = 0; 9307c478bd9Sstevel@tonic-gate else if (curtime > cur_pexp) 9317c478bd9Sstevel@tonic-gate preftime = 0; 9327c478bd9Sstevel@tonic-gate else if ((curtime + preftime) > cur_pexp) 9337c478bd9Sstevel@tonic-gate preftime = cur_pexp - curtime; 9347c478bd9Sstevel@tonic-gate } 9357c478bd9Sstevel@tonic-gate if ((preftime != 0) && (preftime <= pi->pi_TmpRegenAdvance)) { 9367c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, 9377c478bd9Sstevel@tonic-gate (void *)&from->sin6_addr, 9387c478bd9Sstevel@tonic-gate abuf, sizeof (abuf)); 9397c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, 9407c478bd9Sstevel@tonic-gate (void *)&po->nd_opt_pi_prefix, 9417c478bd9Sstevel@tonic-gate pbuf, sizeof (pbuf)); 9427c478bd9Sstevel@tonic-gate logmsg(LOG_WARNING, "prefix opt %s/%u from %s on %s: " 9437c478bd9Sstevel@tonic-gate "preferred lifetime(%d) <= TmpRegenAdvance(%d)\n", 9447c478bd9Sstevel@tonic-gate pbuf, plen, abuf, pi->pi_name, preftime, 9457c478bd9Sstevel@tonic-gate pi->pi_TmpRegenAdvance); 9467c478bd9Sstevel@tonic-gate if (new_prefix) 9477c478bd9Sstevel@tonic-gate prefix_delete(pr); 9487c478bd9Sstevel@tonic-gate return (_B_TRUE); 9497c478bd9Sstevel@tonic-gate } 9507c478bd9Sstevel@tonic-gate } 9517c478bd9Sstevel@tonic-gate if (debug & D_TMP) 9527c478bd9Sstevel@tonic-gate logmsg(LOG_DEBUG, "calculated lifetimes(%s, 0x%llx): v %d, " 9537c478bd9Sstevel@tonic-gate "p %d\n", pr->pr_name, pr->pr_flags, validtime, preftime); 9547c478bd9Sstevel@tonic-gate 9557c478bd9Sstevel@tonic-gate if (!(pr->pr_state & PR_AUTO)) { 9567c478bd9Sstevel@tonic-gate int i, tokenlen; 9577c478bd9Sstevel@tonic-gate in6_addr_t *token; 9587c478bd9Sstevel@tonic-gate /* 9597c478bd9Sstevel@tonic-gate * Form a new local address if the lengths match. 9607c478bd9Sstevel@tonic-gate */ 9617c478bd9Sstevel@tonic-gate if (pr->pr_flags && IFF_TEMPORARY) { 9627c478bd9Sstevel@tonic-gate if (IN6_IS_ADDR_UNSPECIFIED(&pi->pi_tmp_token)) { 9637c478bd9Sstevel@tonic-gate if (!tmptoken_create(pi)) { 9647c478bd9Sstevel@tonic-gate prefix_delete(pr); 9657c478bd9Sstevel@tonic-gate return (_B_TRUE); 9667c478bd9Sstevel@tonic-gate } 9677c478bd9Sstevel@tonic-gate } 9687c478bd9Sstevel@tonic-gate tokenlen = TMP_TOKEN_BITS; 9697c478bd9Sstevel@tonic-gate token = &pi->pi_tmp_token; 9707c478bd9Sstevel@tonic-gate } else { 9717c478bd9Sstevel@tonic-gate tokenlen = pi->pi_token_length; 9727c478bd9Sstevel@tonic-gate token = &pi->pi_token; 9737c478bd9Sstevel@tonic-gate } 9747c478bd9Sstevel@tonic-gate if (pr->pr_prefix_len + tokenlen != IPV6_ABITS) { 9757c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, 9767c478bd9Sstevel@tonic-gate (void *)&from->sin6_addr, 9777c478bd9Sstevel@tonic-gate abuf, sizeof (abuf)); 9787c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, 9797c478bd9Sstevel@tonic-gate (void *)&po->nd_opt_pi_prefix, 9807c478bd9Sstevel@tonic-gate pbuf, sizeof (pbuf)); 9817c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: " 9827c478bd9Sstevel@tonic-gate "mismatched length %d token length %d\n", 9837c478bd9Sstevel@tonic-gate pbuf, plen, abuf, pi->pi_name, 9847c478bd9Sstevel@tonic-gate pr->pr_prefix_len, tokenlen); 9857c478bd9Sstevel@tonic-gate return (_B_TRUE); 9867c478bd9Sstevel@tonic-gate } 9877c478bd9Sstevel@tonic-gate for (i = 0; i < 16; i++) { 9887c478bd9Sstevel@tonic-gate /* 9897c478bd9Sstevel@tonic-gate * prefix_create ensures that pr_prefix has all-zero 9907c478bd9Sstevel@tonic-gate * bits after prefixlen. 9917c478bd9Sstevel@tonic-gate */ 9927c478bd9Sstevel@tonic-gate pr->pr_address.s6_addr[i] = pr->pr_prefix.s6_addr[i] | 9937c478bd9Sstevel@tonic-gate token->s6_addr[i]; 9947c478bd9Sstevel@tonic-gate } 9957c478bd9Sstevel@tonic-gate /* 9967c478bd9Sstevel@tonic-gate * Check if any other physical interface has the same 9977c478bd9Sstevel@tonic-gate * address configured already 9987c478bd9Sstevel@tonic-gate */ 9997c478bd9Sstevel@tonic-gate if ((other_pr = prefix_lookup_addr_match(pr)) != NULL) { 10007c478bd9Sstevel@tonic-gate /* 10017c478bd9Sstevel@tonic-gate * Delete this prefix structure as kernel 10027c478bd9Sstevel@tonic-gate * does not allow duplicated addresses 10037c478bd9Sstevel@tonic-gate */ 10047c478bd9Sstevel@tonic-gate 10057c478bd9Sstevel@tonic-gate logmsg(LOG_ERR, "incoming_prefix_addrconf_process: " 10067c478bd9Sstevel@tonic-gate "Duplicate prefix %s received on interface %s\n", 10077c478bd9Sstevel@tonic-gate inet_ntop(AF_INET6, 10087c478bd9Sstevel@tonic-gate (void *)&po->nd_opt_pi_prefix, abuf, 10097c478bd9Sstevel@tonic-gate sizeof (abuf)), pi->pi_name); 10107c478bd9Sstevel@tonic-gate logmsg(LOG_ERR, "incoming_prefix_addrconf_process: " 10117c478bd9Sstevel@tonic-gate "Prefix already exists in interface %s\n", 10127c478bd9Sstevel@tonic-gate other_pr->pr_physical->pi_name); 10137c478bd9Sstevel@tonic-gate if (new_prefix) { 10147c478bd9Sstevel@tonic-gate prefix_delete(pr); 10157c478bd9Sstevel@tonic-gate return (_B_FALSE); 10167c478bd9Sstevel@tonic-gate } 10177c478bd9Sstevel@tonic-gate /* Ignore for addrconf purposes */ 10187c478bd9Sstevel@tonic-gate validtime = preftime = 0; 10197c478bd9Sstevel@tonic-gate } 10207c478bd9Sstevel@tonic-gate if ((pr->pr_flags & IFF_TEMPORARY) && new_prefix) { 10217c478bd9Sstevel@tonic-gate pr->pr_CreateTime = getcurrenttime() / MILLISEC; 10227c478bd9Sstevel@tonic-gate if (debug & D_TMP) 10237c478bd9Sstevel@tonic-gate logmsg(LOG_DEBUG, 10247c478bd9Sstevel@tonic-gate "created tmp addr(%s v %d p %d)\n", 10257c478bd9Sstevel@tonic-gate pr->pr_name, validtime, preftime); 10267c478bd9Sstevel@tonic-gate } 10277c478bd9Sstevel@tonic-gate } 10287c478bd9Sstevel@tonic-gate 10297c478bd9Sstevel@tonic-gate if (validtime != 0) 10307c478bd9Sstevel@tonic-gate pr->pr_state |= PR_AUTO; 10317c478bd9Sstevel@tonic-gate else 10327c478bd9Sstevel@tonic-gate pr->pr_state &= ~(PR_AUTO|PR_DEPRECATED); 10337c478bd9Sstevel@tonic-gate if (preftime != 0 || !(pr->pr_state & PR_AUTO)) 10347c478bd9Sstevel@tonic-gate pr->pr_state &= ~PR_DEPRECATED; 10357c478bd9Sstevel@tonic-gate else 10367c478bd9Sstevel@tonic-gate pr->pr_state |= PR_DEPRECATED; 10377c478bd9Sstevel@tonic-gate 10387c478bd9Sstevel@tonic-gate /* 10397c478bd9Sstevel@tonic-gate * Convert from seconds to milliseconds avoiding overflow. 10407c478bd9Sstevel@tonic-gate * If the lifetime in the packet is e.g. PREFIX_INFINITY - 1 10417c478bd9Sstevel@tonic-gate * (4 billion seconds - about 130 years) we will in fact time 10427c478bd9Sstevel@tonic-gate * out the prefix after 4 billion milliseconds - 46 days). 10437c478bd9Sstevel@tonic-gate * Thus the longest lifetime (apart from infinity) is 46 days. 10447c478bd9Sstevel@tonic-gate * Note that this ensures that PREFIX_INFINITY still means "forever". 10457c478bd9Sstevel@tonic-gate */ 10467c478bd9Sstevel@tonic-gate if (validtime >= PREFIX_INFINITY / MILLISEC) 10477c478bd9Sstevel@tonic-gate pr->pr_ValidLifetime = PREFIX_INFINITY - 1; 10487c478bd9Sstevel@tonic-gate else 10497c478bd9Sstevel@tonic-gate pr->pr_ValidLifetime = validtime * MILLISEC; 10507c478bd9Sstevel@tonic-gate if (preftime >= PREFIX_INFINITY / MILLISEC) 10517c478bd9Sstevel@tonic-gate pr->pr_PreferredLifetime = PREFIX_INFINITY - 1; 10527c478bd9Sstevel@tonic-gate else 10537c478bd9Sstevel@tonic-gate pr->pr_PreferredLifetime = preftime * MILLISEC; 10547c478bd9Sstevel@tonic-gate pr->pr_AutonomousFlag = _B_TRUE; 10557c478bd9Sstevel@tonic-gate 10567c478bd9Sstevel@tonic-gate if (debug & D_PREFIX) { 10577c478bd9Sstevel@tonic-gate logmsg(LOG_DEBUG, "incoming_prefix_addrconf_process(%s, %s/%u) " 10587c478bd9Sstevel@tonic-gate "valid %u pref %u\n", 10597c478bd9Sstevel@tonic-gate pr->pr_physical->pi_name, 10607c478bd9Sstevel@tonic-gate inet_ntop(AF_INET6, (void *)&pr->pr_prefix, 10617c478bd9Sstevel@tonic-gate abuf, sizeof (abuf)), pr->pr_prefix_len, 10627c478bd9Sstevel@tonic-gate pr->pr_ValidLifetime, pr->pr_PreferredLifetime); 10637c478bd9Sstevel@tonic-gate } 10647c478bd9Sstevel@tonic-gate 10657c478bd9Sstevel@tonic-gate if (pr->pr_state & PR_AUTO) { 10667c478bd9Sstevel@tonic-gate /* Take the min of the two timeouts by calling it twice */ 10677c478bd9Sstevel@tonic-gate if (pr->pr_ValidLifetime != 0) 10687c478bd9Sstevel@tonic-gate timer_schedule(pr->pr_ValidLifetime); 10697c478bd9Sstevel@tonic-gate if (pr->pr_PreferredLifetime != 0) 10707c478bd9Sstevel@tonic-gate timer_schedule(pr->pr_PreferredLifetime); 10717c478bd9Sstevel@tonic-gate } 10727c478bd9Sstevel@tonic-gate if (pr->pr_kernel_state != pr->pr_state) { 10737c478bd9Sstevel@tonic-gate /* Log a message when an addrconf prefix goes away */ 10747c478bd9Sstevel@tonic-gate if ((pr->pr_kernel_state & PR_AUTO) && 10757c478bd9Sstevel@tonic-gate !(pr->pr_state & PR_AUTO)) { 10767c478bd9Sstevel@tonic-gate char abuf[INET6_ADDRSTRLEN]; 10777c478bd9Sstevel@tonic-gate 10787c478bd9Sstevel@tonic-gate logmsg(LOG_WARNING, "Address removed due to zero " 10797c478bd9Sstevel@tonic-gate "valid lifetime %s\n", 10807c478bd9Sstevel@tonic-gate inet_ntop(AF_INET6, (void *)&pr->pr_address, 10817c478bd9Sstevel@tonic-gate abuf, sizeof (abuf))); 10827c478bd9Sstevel@tonic-gate } 10837c478bd9Sstevel@tonic-gate prefix_update_k(pr); 10847c478bd9Sstevel@tonic-gate } 10857c478bd9Sstevel@tonic-gate return (_B_TRUE); 10867c478bd9Sstevel@tonic-gate } 10877c478bd9Sstevel@tonic-gate 10887c478bd9Sstevel@tonic-gate /* 10897c478bd9Sstevel@tonic-gate * Process an MTU option received in a router advertisement. 10907c478bd9Sstevel@tonic-gate */ 10917c478bd9Sstevel@tonic-gate static void 10927c478bd9Sstevel@tonic-gate incoming_mtu_opt(struct phyint *pi, uchar_t *opt, 10937c478bd9Sstevel@tonic-gate struct sockaddr_in6 *from) 10947c478bd9Sstevel@tonic-gate { 10957c478bd9Sstevel@tonic-gate struct nd_opt_mtu *mo = (struct nd_opt_mtu *)opt; 10967c478bd9Sstevel@tonic-gate struct lifreq lifr; 10977c478bd9Sstevel@tonic-gate uint32_t mtu; 10987c478bd9Sstevel@tonic-gate 10997c478bd9Sstevel@tonic-gate if (8 * mo->nd_opt_mtu_len != sizeof (*mo)) { 11007c478bd9Sstevel@tonic-gate char abuf[INET6_ADDRSTRLEN]; 11017c478bd9Sstevel@tonic-gate 11027c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, (void *)&from->sin6_addr, 11037c478bd9Sstevel@tonic-gate abuf, sizeof (abuf)); 11047c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "mtu option from %s on %s wrong size " 11057c478bd9Sstevel@tonic-gate "(%d bytes)\n", 11067c478bd9Sstevel@tonic-gate abuf, pi->pi_name, 11077c478bd9Sstevel@tonic-gate 8 * (int)mo->nd_opt_mtu_len); 11087c478bd9Sstevel@tonic-gate return; 11097c478bd9Sstevel@tonic-gate } 11107c478bd9Sstevel@tonic-gate mtu = ntohl(mo->nd_opt_mtu_mtu); 11117c478bd9Sstevel@tonic-gate if (pi->pi_LinkMTU == mtu) 11127c478bd9Sstevel@tonic-gate return; /* No change */ 11137c478bd9Sstevel@tonic-gate if (mtu > pi->pi_mtu) { 11147c478bd9Sstevel@tonic-gate /* Can't exceed physical MTU */ 11157c478bd9Sstevel@tonic-gate char abuf[INET6_ADDRSTRLEN]; 11167c478bd9Sstevel@tonic-gate 11177c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, (void *)&from->sin6_addr, 11187c478bd9Sstevel@tonic-gate abuf, sizeof (abuf)); 11197c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "mtu option from %s on %s too large " 11207c478bd9Sstevel@tonic-gate "MTU %d - %d\n", abuf, pi->pi_name, mtu, pi->pi_mtu); 11217c478bd9Sstevel@tonic-gate return; 11227c478bd9Sstevel@tonic-gate } 11237c478bd9Sstevel@tonic-gate if (mtu < IPV6_MIN_MTU) { 11247c478bd9Sstevel@tonic-gate char abuf[INET6_ADDRSTRLEN]; 11257c478bd9Sstevel@tonic-gate 11267c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, (void *)&from->sin6_addr, 11277c478bd9Sstevel@tonic-gate abuf, sizeof (abuf)); 11287c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "mtu option from %s on %s too small " 11297c478bd9Sstevel@tonic-gate "MTU (%d)\n", abuf, pi->pi_name, mtu); 11307c478bd9Sstevel@tonic-gate return; 11317c478bd9Sstevel@tonic-gate } 11327c478bd9Sstevel@tonic-gate 11337c478bd9Sstevel@tonic-gate pi->pi_LinkMTU = mtu; 11347c478bd9Sstevel@tonic-gate (void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name)); 11357c478bd9Sstevel@tonic-gate lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0'; 11367c478bd9Sstevel@tonic-gate if (ioctl(pi->pi_sock, SIOCGLIFLNKINFO, (char *)&lifr) < 0) { 11377c478bd9Sstevel@tonic-gate logperror_pi(pi, "incoming_mtu_opt: SIOCGLIFLNKINFO"); 11387c478bd9Sstevel@tonic-gate return; 11397c478bd9Sstevel@tonic-gate } 11407c478bd9Sstevel@tonic-gate lifr.lifr_ifinfo.lir_maxmtu = pi->pi_LinkMTU; 11417c478bd9Sstevel@tonic-gate if (ioctl(pi->pi_sock, SIOCSLIFLNKINFO, (char *)&lifr) < 0) { 11427c478bd9Sstevel@tonic-gate logperror_pi(pi, "incoming_mtu_opt: SIOCSLIFLNKINFO"); 11437c478bd9Sstevel@tonic-gate return; 11447c478bd9Sstevel@tonic-gate } 11457c478bd9Sstevel@tonic-gate } 11467c478bd9Sstevel@tonic-gate 11477c478bd9Sstevel@tonic-gate /* 11487c478bd9Sstevel@tonic-gate * Process a source link-layer address option received in a router 11497c478bd9Sstevel@tonic-gate * advertisement or solicitation. 11507c478bd9Sstevel@tonic-gate */ 11517c478bd9Sstevel@tonic-gate static void 11527c478bd9Sstevel@tonic-gate incoming_lla_opt(struct phyint *pi, uchar_t *opt, 11537c478bd9Sstevel@tonic-gate struct sockaddr_in6 *from, int isrouter) 11547c478bd9Sstevel@tonic-gate { 11557c478bd9Sstevel@tonic-gate struct nd_opt_lla *lo = (struct nd_opt_lla *)opt; 11567c478bd9Sstevel@tonic-gate struct lifreq lifr; 11577c478bd9Sstevel@tonic-gate struct sockaddr_in6 *sin6; 11587c478bd9Sstevel@tonic-gate int max_content_len; 11597c478bd9Sstevel@tonic-gate 11607c478bd9Sstevel@tonic-gate if (pi->pi_hdw_addr_len == 0) 11617c478bd9Sstevel@tonic-gate return; 11627c478bd9Sstevel@tonic-gate 11637c478bd9Sstevel@tonic-gate /* 11647c478bd9Sstevel@tonic-gate * Can't remove padding since it is link type specific. 11657c478bd9Sstevel@tonic-gate * However, we check against the length of our link-layer 11667c478bd9Sstevel@tonic-gate * address. 11677c478bd9Sstevel@tonic-gate * Note: assumes that all links have a fixed lengh address. 11687c478bd9Sstevel@tonic-gate */ 11697c478bd9Sstevel@tonic-gate max_content_len = lo->nd_opt_lla_len * 8 - sizeof (struct nd_opt_hdr); 11707c478bd9Sstevel@tonic-gate if (max_content_len < pi->pi_hdw_addr_len || 11717c478bd9Sstevel@tonic-gate (max_content_len >= 8 && 11727c478bd9Sstevel@tonic-gate max_content_len - 7 > pi->pi_hdw_addr_len)) { 11737c478bd9Sstevel@tonic-gate char abuf[INET6_ADDRSTRLEN]; 11747c478bd9Sstevel@tonic-gate 11757c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, (void *)&from->sin6_addr, 11767c478bd9Sstevel@tonic-gate abuf, sizeof (abuf)); 11777c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "lla option from %s on %s too long with bad " 11787c478bd9Sstevel@tonic-gate "physaddr length (%d vs. %d bytes)\n", 11797c478bd9Sstevel@tonic-gate abuf, pi->pi_name, 11807c478bd9Sstevel@tonic-gate max_content_len, pi->pi_hdw_addr_len); 11817c478bd9Sstevel@tonic-gate return; 11827c478bd9Sstevel@tonic-gate } 11837c478bd9Sstevel@tonic-gate 11847c478bd9Sstevel@tonic-gate lifr.lifr_nd.lnr_hdw_len = pi->pi_hdw_addr_len; 11857c478bd9Sstevel@tonic-gate bcopy((char *)lo->nd_opt_lla_hdw_addr, 11867c478bd9Sstevel@tonic-gate (char *)lifr.lifr_nd.lnr_hdw_addr, 11877c478bd9Sstevel@tonic-gate lifr.lifr_nd.lnr_hdw_len); 11887c478bd9Sstevel@tonic-gate 11897c478bd9Sstevel@tonic-gate sin6 = (struct sockaddr_in6 *)&lifr.lifr_nd.lnr_addr; 11907c478bd9Sstevel@tonic-gate bzero(sin6, sizeof (struct sockaddr_in6)); 11917c478bd9Sstevel@tonic-gate sin6->sin6_family = AF_INET6; 11927c478bd9Sstevel@tonic-gate sin6->sin6_addr = from->sin6_addr; 11937c478bd9Sstevel@tonic-gate 11947c478bd9Sstevel@tonic-gate /* 11957c478bd9Sstevel@tonic-gate * Set IsRouter flag if RA; clear if RS. 11967c478bd9Sstevel@tonic-gate */ 11977c478bd9Sstevel@tonic-gate lifr.lifr_nd.lnr_state_create = ND_STALE; 11987c478bd9Sstevel@tonic-gate lifr.lifr_nd.lnr_state_same_lla = ND_UNCHANGED; 11997c478bd9Sstevel@tonic-gate lifr.lifr_nd.lnr_state_diff_lla = ND_STALE; 12007c478bd9Sstevel@tonic-gate lifr.lifr_nd.lnr_flags = isrouter; 12017c478bd9Sstevel@tonic-gate (void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name)); 12027c478bd9Sstevel@tonic-gate lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0'; 12037c478bd9Sstevel@tonic-gate if (ioctl(pi->pi_sock, SIOCLIFSETND, (char *)&lifr) < 0) { 12047c478bd9Sstevel@tonic-gate logperror_pi(pi, "incoming_lla_opt: SIOCLIFSETND"); 12057c478bd9Sstevel@tonic-gate return; 12067c478bd9Sstevel@tonic-gate } 12077c478bd9Sstevel@tonic-gate } 12087c478bd9Sstevel@tonic-gate 12097c478bd9Sstevel@tonic-gate /* 12107c478bd9Sstevel@tonic-gate * Verify the content of the received router advertisement against our 12117c478bd9Sstevel@tonic-gate * own configuration as specified in RFC 2461. 12127c478bd9Sstevel@tonic-gate */ 12137c478bd9Sstevel@tonic-gate static void 12147c478bd9Sstevel@tonic-gate verify_ra_consistency(struct phyint *pi, struct nd_router_advert *ra, int len, 12157c478bd9Sstevel@tonic-gate struct sockaddr_in6 *from) 12167c478bd9Sstevel@tonic-gate { 12177c478bd9Sstevel@tonic-gate char frombuf[INET6_ADDRSTRLEN]; 12187c478bd9Sstevel@tonic-gate struct nd_opt_hdr *opt; 12197c478bd9Sstevel@tonic-gate int optlen; 12207c478bd9Sstevel@tonic-gate uint_t reachable, retrans; 12217c478bd9Sstevel@tonic-gate boolean_t pktflag, myflag; 12227c478bd9Sstevel@tonic-gate 12237c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, (void *)&from->sin6_addr, 12247c478bd9Sstevel@tonic-gate frombuf, sizeof (frombuf)); 12257c478bd9Sstevel@tonic-gate 12267c478bd9Sstevel@tonic-gate if (ra->nd_ra_curhoplimit != 0 && 12277c478bd9Sstevel@tonic-gate pi->pi_AdvCurHopLimit != 0 && 12287c478bd9Sstevel@tonic-gate ra->nd_ra_curhoplimit != pi->pi_AdvCurHopLimit) { 12297c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RA from %s on %s inconsistent cur hop " 12307c478bd9Sstevel@tonic-gate "limit:\n\treceived %d configuration %d\n", 12317c478bd9Sstevel@tonic-gate frombuf, pi->pi_name, 12327c478bd9Sstevel@tonic-gate ra->nd_ra_curhoplimit, pi->pi_AdvCurHopLimit); 12337c478bd9Sstevel@tonic-gate } 12347c478bd9Sstevel@tonic-gate 12357c478bd9Sstevel@tonic-gate reachable = ntohl(ra->nd_ra_reachable); 12367c478bd9Sstevel@tonic-gate if (reachable != 0 && pi->pi_AdvReachableTime != 0 && 12377c478bd9Sstevel@tonic-gate reachable != pi->pi_AdvReachableTime) { 12387c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RA from %s on %s inconsistent reachable " 12397c478bd9Sstevel@tonic-gate "time:\n\treceived %d configuration %d\n", 12407c478bd9Sstevel@tonic-gate frombuf, pi->pi_name, 12417c478bd9Sstevel@tonic-gate reachable, pi->pi_AdvReachableTime); 12427c478bd9Sstevel@tonic-gate } 12437c478bd9Sstevel@tonic-gate 12447c478bd9Sstevel@tonic-gate retrans = ntohl(ra->nd_ra_retransmit); 12457c478bd9Sstevel@tonic-gate if (retrans != 0 && pi->pi_AdvRetransTimer != 0 && 12467c478bd9Sstevel@tonic-gate retrans != pi->pi_AdvRetransTimer) { 12477c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RA from %s on %s inconsistent retransmit " 12487c478bd9Sstevel@tonic-gate "timer:\n\treceived %d configuration %d\n", 12497c478bd9Sstevel@tonic-gate frombuf, pi->pi_name, 12507c478bd9Sstevel@tonic-gate retrans, pi->pi_AdvRetransTimer); 12517c478bd9Sstevel@tonic-gate } 12527c478bd9Sstevel@tonic-gate 12537c478bd9Sstevel@tonic-gate pktflag = ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) != 0); 12547c478bd9Sstevel@tonic-gate myflag = (pi->pi_AdvManagedFlag != 0); 12557c478bd9Sstevel@tonic-gate if (pktflag != myflag) { 12567c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RA from %s on %s inconsistent managed " 12577c478bd9Sstevel@tonic-gate "flag:\n\treceived %s configuration %s\n", 12587c478bd9Sstevel@tonic-gate frombuf, pi->pi_name, 12597c478bd9Sstevel@tonic-gate (pktflag ? "ON" : "OFF"), 12607c478bd9Sstevel@tonic-gate (myflag ? "ON" : "OFF")); 12617c478bd9Sstevel@tonic-gate } 12627c478bd9Sstevel@tonic-gate pktflag = ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) != 0); 12637c478bd9Sstevel@tonic-gate myflag = (pi->pi_AdvOtherConfigFlag != 0); 12647c478bd9Sstevel@tonic-gate if (pktflag != myflag) { 12657c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RA from %s on %s inconsistent other config " 12667c478bd9Sstevel@tonic-gate "flag:\n\treceived %s configuration %s\n", 12677c478bd9Sstevel@tonic-gate frombuf, pi->pi_name, 12687c478bd9Sstevel@tonic-gate (pktflag ? "ON" : "OFF"), 12697c478bd9Sstevel@tonic-gate (myflag ? "ON" : "OFF")); 12707c478bd9Sstevel@tonic-gate } 12717c478bd9Sstevel@tonic-gate 12727c478bd9Sstevel@tonic-gate /* Process any options */ 12737c478bd9Sstevel@tonic-gate len -= sizeof (struct nd_router_advert); 12747c478bd9Sstevel@tonic-gate opt = (struct nd_opt_hdr *)&ra[1]; 12757c478bd9Sstevel@tonic-gate while (len >= sizeof (struct nd_opt_hdr)) { 12767c478bd9Sstevel@tonic-gate optlen = opt->nd_opt_len * 8; 12777c478bd9Sstevel@tonic-gate switch (opt->nd_opt_type) { 12787c478bd9Sstevel@tonic-gate case ND_OPT_PREFIX_INFORMATION: 12797c478bd9Sstevel@tonic-gate verify_prefix_opt(pi, (uchar_t *)opt, frombuf); 12807c478bd9Sstevel@tonic-gate break; 12817c478bd9Sstevel@tonic-gate case ND_OPT_MTU: 12827c478bd9Sstevel@tonic-gate verify_mtu_opt(pi, (uchar_t *)opt, frombuf); 12837c478bd9Sstevel@tonic-gate break; 12847c478bd9Sstevel@tonic-gate default: 12857c478bd9Sstevel@tonic-gate break; 12867c478bd9Sstevel@tonic-gate } 12877c478bd9Sstevel@tonic-gate opt = (struct nd_opt_hdr *)((char *)opt + optlen); 12887c478bd9Sstevel@tonic-gate len -= optlen; 12897c478bd9Sstevel@tonic-gate } 12907c478bd9Sstevel@tonic-gate } 12917c478bd9Sstevel@tonic-gate 12927c478bd9Sstevel@tonic-gate /* 12937c478bd9Sstevel@tonic-gate * Verify that the lifetimes and onlink/auto flags are consistent 12947c478bd9Sstevel@tonic-gate * with our settings. 12957c478bd9Sstevel@tonic-gate */ 12967c478bd9Sstevel@tonic-gate static void 12977c478bd9Sstevel@tonic-gate verify_prefix_opt(struct phyint *pi, uchar_t *opt, char *frombuf) 12987c478bd9Sstevel@tonic-gate { 12997c478bd9Sstevel@tonic-gate struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt; 13007c478bd9Sstevel@tonic-gate int plen; 13017c478bd9Sstevel@tonic-gate struct adv_prefix *adv_pr; 13027c478bd9Sstevel@tonic-gate uint32_t validtime, preftime; 13037c478bd9Sstevel@tonic-gate char prefixbuf[INET6_ADDRSTRLEN]; 13047c478bd9Sstevel@tonic-gate int pktflag, myflag; 13057c478bd9Sstevel@tonic-gate 13067c478bd9Sstevel@tonic-gate if (8 * po->nd_opt_pi_len != sizeof (*po)) { 13077c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RA prefix option from %s on %s wrong size " 13087c478bd9Sstevel@tonic-gate "(%d bytes)\n", 13097c478bd9Sstevel@tonic-gate frombuf, pi->pi_name, 13107c478bd9Sstevel@tonic-gate 8 * (int)po->nd_opt_pi_len); 13117c478bd9Sstevel@tonic-gate return; 13127c478bd9Sstevel@tonic-gate } 13137c478bd9Sstevel@tonic-gate if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) { 13147c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RA from %s on %s contains link-local " 13157c478bd9Sstevel@tonic-gate "prefix - ignored\n", 13167c478bd9Sstevel@tonic-gate frombuf, pi->pi_name); 13177c478bd9Sstevel@tonic-gate return; 13187c478bd9Sstevel@tonic-gate } 13197c478bd9Sstevel@tonic-gate plen = po->nd_opt_pi_prefix_len; 13207c478bd9Sstevel@tonic-gate adv_pr = adv_prefix_lookup(pi, po->nd_opt_pi_prefix, plen); 13217c478bd9Sstevel@tonic-gate if (adv_pr == NULL) 13227c478bd9Sstevel@tonic-gate return; 13237c478bd9Sstevel@tonic-gate 13247c478bd9Sstevel@tonic-gate /* Ignore prefixes which we do not advertise */ 13257c478bd9Sstevel@tonic-gate if (!adv_pr->adv_pr_AdvAutonomousFlag && !adv_pr->adv_pr_AdvOnLinkFlag) 13267c478bd9Sstevel@tonic-gate return; 13277c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, (void *)&adv_pr->adv_pr_prefix, 13287c478bd9Sstevel@tonic-gate prefixbuf, sizeof (prefixbuf)); 13297c478bd9Sstevel@tonic-gate pktflag = ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) != 0); 13307c478bd9Sstevel@tonic-gate myflag = (adv_pr->adv_pr_AdvAutonomousFlag != 0); 13317c478bd9Sstevel@tonic-gate if (pktflag != myflag) { 13327c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, 1333*d04ccbb3Scarlsonj "RA from %s on %s inconsistent autonomous flag for \n\t" 13347c478bd9Sstevel@tonic-gate "prefix %s/%u: received %s configuration %s\n", 13357c478bd9Sstevel@tonic-gate frombuf, pi->pi_name, prefixbuf, adv_pr->adv_pr_prefix_len, 13367c478bd9Sstevel@tonic-gate (pktflag ? "ON" : "OFF"), 13377c478bd9Sstevel@tonic-gate (myflag ? "ON" : "OFF")); 13387c478bd9Sstevel@tonic-gate } 13397c478bd9Sstevel@tonic-gate 13407c478bd9Sstevel@tonic-gate pktflag = ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) != 0); 13417c478bd9Sstevel@tonic-gate myflag = (adv_pr->adv_pr_AdvOnLinkFlag != 0); 13427c478bd9Sstevel@tonic-gate if (pktflag != myflag) { 13437c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RA from %s on %s inconsistent on link flag " 13447c478bd9Sstevel@tonic-gate "for \n\tprefix %s/%u: received %s configuration %s\n", 13457c478bd9Sstevel@tonic-gate frombuf, pi->pi_name, prefixbuf, adv_pr->adv_pr_prefix_len, 13467c478bd9Sstevel@tonic-gate (pktflag ? "ON" : "OFF"), 13477c478bd9Sstevel@tonic-gate (myflag ? "ON" : "OFF")); 13487c478bd9Sstevel@tonic-gate } 13497c478bd9Sstevel@tonic-gate validtime = ntohl(po->nd_opt_pi_valid_time); 13507c478bd9Sstevel@tonic-gate preftime = ntohl(po->nd_opt_pi_preferred_time); 13517c478bd9Sstevel@tonic-gate 13527c478bd9Sstevel@tonic-gate /* 13537c478bd9Sstevel@tonic-gate * Take into account variation for lifetimes decrementing 13547c478bd9Sstevel@tonic-gate * in real time. Allow +/- 10 percent and +/- 10 seconds. 13557c478bd9Sstevel@tonic-gate */ 13567c478bd9Sstevel@tonic-gate #define LOWER_LIMIT(val) ((val) - (val)/10 - 10) 13577c478bd9Sstevel@tonic-gate #define UPPER_LIMIT(val) ((val) + (val)/10 + 10) 13587c478bd9Sstevel@tonic-gate if (adv_pr->adv_pr_AdvValidRealTime) { 13597c478bd9Sstevel@tonic-gate if (adv_pr->adv_pr_AdvValidExpiration > 0 && 13607c478bd9Sstevel@tonic-gate (validtime < 13617c478bd9Sstevel@tonic-gate LOWER_LIMIT(adv_pr->adv_pr_AdvValidExpiration) || 13627c478bd9Sstevel@tonic-gate validtime > 13637c478bd9Sstevel@tonic-gate UPPER_LIMIT(adv_pr->adv_pr_AdvValidExpiration))) { 13647c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RA from %s on %s inconsistent valid " 13657c478bd9Sstevel@tonic-gate "lifetime for\n\tprefix %s/%u: received %d " 13667c478bd9Sstevel@tonic-gate "configuration %d\n", 13677c478bd9Sstevel@tonic-gate frombuf, pi->pi_name, prefixbuf, 13687c478bd9Sstevel@tonic-gate adv_pr->adv_pr_prefix_len, 13697c478bd9Sstevel@tonic-gate validtime, adv_pr->adv_pr_AdvValidExpiration); 13707c478bd9Sstevel@tonic-gate } 13717c478bd9Sstevel@tonic-gate } else { 13727c478bd9Sstevel@tonic-gate if (validtime != adv_pr->adv_pr_AdvValidLifetime) { 13737c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RA from %s on %s inconsistent valid " 13747c478bd9Sstevel@tonic-gate "lifetime for\n\tprefix %s/%u: received %d " 13757c478bd9Sstevel@tonic-gate "configuration %d\n", 13767c478bd9Sstevel@tonic-gate frombuf, pi->pi_name, prefixbuf, 13777c478bd9Sstevel@tonic-gate adv_pr->adv_pr_prefix_len, 13787c478bd9Sstevel@tonic-gate validtime, adv_pr->adv_pr_AdvValidLifetime); 13797c478bd9Sstevel@tonic-gate } 13807c478bd9Sstevel@tonic-gate } 13817c478bd9Sstevel@tonic-gate 13827c478bd9Sstevel@tonic-gate if (adv_pr->adv_pr_AdvPreferredRealTime) { 13837c478bd9Sstevel@tonic-gate if (adv_pr->adv_pr_AdvPreferredExpiration > 0 && 13847c478bd9Sstevel@tonic-gate (preftime < 13857c478bd9Sstevel@tonic-gate LOWER_LIMIT(adv_pr->adv_pr_AdvPreferredExpiration) || 13867c478bd9Sstevel@tonic-gate preftime > 13877c478bd9Sstevel@tonic-gate UPPER_LIMIT(adv_pr->adv_pr_AdvPreferredExpiration))) { 13887c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RA from %s on %s inconsistent " 13897c478bd9Sstevel@tonic-gate "preferred lifetime for\n\tprefix %s/%u: " 13907c478bd9Sstevel@tonic-gate "received %d configuration %d\n", 13917c478bd9Sstevel@tonic-gate frombuf, pi->pi_name, prefixbuf, 13927c478bd9Sstevel@tonic-gate adv_pr->adv_pr_prefix_len, 13937c478bd9Sstevel@tonic-gate preftime, adv_pr->adv_pr_AdvPreferredExpiration); 13947c478bd9Sstevel@tonic-gate } 13957c478bd9Sstevel@tonic-gate } else { 13967c478bd9Sstevel@tonic-gate if (preftime != adv_pr->adv_pr_AdvPreferredLifetime) { 13977c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RA from %s on %s inconsistent " 13987c478bd9Sstevel@tonic-gate "preferred lifetime for\n\tprefix %s/%u: " 13997c478bd9Sstevel@tonic-gate "received %d configuration %d\n", 14007c478bd9Sstevel@tonic-gate frombuf, pi->pi_name, prefixbuf, 14017c478bd9Sstevel@tonic-gate adv_pr->adv_pr_prefix_len, 14027c478bd9Sstevel@tonic-gate preftime, adv_pr->adv_pr_AdvPreferredLifetime); 14037c478bd9Sstevel@tonic-gate } 14047c478bd9Sstevel@tonic-gate } 14057c478bd9Sstevel@tonic-gate } 14067c478bd9Sstevel@tonic-gate 14077c478bd9Sstevel@tonic-gate /* 14087c478bd9Sstevel@tonic-gate * Verify the received MTU against our own configuration. 14097c478bd9Sstevel@tonic-gate */ 14107c478bd9Sstevel@tonic-gate static void 14117c478bd9Sstevel@tonic-gate verify_mtu_opt(struct phyint *pi, uchar_t *opt, char *frombuf) 14127c478bd9Sstevel@tonic-gate { 14137c478bd9Sstevel@tonic-gate struct nd_opt_mtu *mo = (struct nd_opt_mtu *)opt; 14147c478bd9Sstevel@tonic-gate uint32_t mtu; 14157c478bd9Sstevel@tonic-gate 14167c478bd9Sstevel@tonic-gate if (8 * mo->nd_opt_mtu_len != sizeof (*mo)) { 14177c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "mtu option from %s on %s wrong size " 14187c478bd9Sstevel@tonic-gate "(%d bytes)\n", 14197c478bd9Sstevel@tonic-gate frombuf, pi->pi_name, 14207c478bd9Sstevel@tonic-gate 8 * (int)mo->nd_opt_mtu_len); 14217c478bd9Sstevel@tonic-gate return; 14227c478bd9Sstevel@tonic-gate } 14237c478bd9Sstevel@tonic-gate mtu = ntohl(mo->nd_opt_mtu_mtu); 14247c478bd9Sstevel@tonic-gate if (pi->pi_AdvLinkMTU != 0 && 14257c478bd9Sstevel@tonic-gate pi->pi_AdvLinkMTU != mtu) { 14267c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "RA from %s on %s inconsistent MTU: " 14277c478bd9Sstevel@tonic-gate "received %d configuration %d\n", 14287c478bd9Sstevel@tonic-gate frombuf, pi->pi_name, 14297c478bd9Sstevel@tonic-gate mtu, pi->pi_AdvLinkMTU); 14307c478bd9Sstevel@tonic-gate } 14317c478bd9Sstevel@tonic-gate } 14327c478bd9Sstevel@tonic-gate 14337c478bd9Sstevel@tonic-gate /* 14347c478bd9Sstevel@tonic-gate * Verify that all options have a non-zero length and that 14357c478bd9Sstevel@tonic-gate * the options fit within the total length of the packet (optlen). 14367c478bd9Sstevel@tonic-gate */ 14377c478bd9Sstevel@tonic-gate static boolean_t 14387c478bd9Sstevel@tonic-gate verify_opt_len(struct nd_opt_hdr *opt, int optlen, 14397c478bd9Sstevel@tonic-gate struct phyint *pi, struct sockaddr_in6 *from) 14407c478bd9Sstevel@tonic-gate { 14417c478bd9Sstevel@tonic-gate while (optlen > 0) { 14427c478bd9Sstevel@tonic-gate if (opt->nd_opt_len == 0) { 14437c478bd9Sstevel@tonic-gate char abuf[INET6_ADDRSTRLEN]; 14447c478bd9Sstevel@tonic-gate 14457c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, 14467c478bd9Sstevel@tonic-gate (void *)&from->sin6_addr, 14477c478bd9Sstevel@tonic-gate abuf, sizeof (abuf)); 14487c478bd9Sstevel@tonic-gate 14497c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "Zero length option type 0x%x " 14507c478bd9Sstevel@tonic-gate "from %s on %s\n", 14517c478bd9Sstevel@tonic-gate opt->nd_opt_type, abuf, pi->pi_name); 14527c478bd9Sstevel@tonic-gate return (_B_FALSE); 14537c478bd9Sstevel@tonic-gate } 14547c478bd9Sstevel@tonic-gate optlen -= 8 * opt->nd_opt_len; 14557c478bd9Sstevel@tonic-gate if (optlen < 0) { 14567c478bd9Sstevel@tonic-gate char abuf[INET6_ADDRSTRLEN]; 14577c478bd9Sstevel@tonic-gate 14587c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, 14597c478bd9Sstevel@tonic-gate (void *)&from->sin6_addr, 14607c478bd9Sstevel@tonic-gate abuf, sizeof (abuf)); 14617c478bd9Sstevel@tonic-gate 14627c478bd9Sstevel@tonic-gate logmsg(LOG_INFO, "Too large option: type 0x%x len %u " 14637c478bd9Sstevel@tonic-gate "from %s on %s\n", 14647c478bd9Sstevel@tonic-gate opt->nd_opt_type, opt->nd_opt_len, 14657c478bd9Sstevel@tonic-gate abuf, pi->pi_name); 14667c478bd9Sstevel@tonic-gate return (_B_FALSE); 14677c478bd9Sstevel@tonic-gate } 14687c478bd9Sstevel@tonic-gate opt = (struct nd_opt_hdr *)((char *)opt + 14697c478bd9Sstevel@tonic-gate 8 * opt->nd_opt_len); 14707c478bd9Sstevel@tonic-gate } 14717c478bd9Sstevel@tonic-gate return (_B_TRUE); 14727c478bd9Sstevel@tonic-gate } 14736b27086dSdd 14746b27086dSdd /* 14756b27086dSdd * Update IsRouter Flag for Host turning into a router or vice-versa. 14766b27086dSdd */ 14776b27086dSdd static void 1478b0f490f4Smh update_ra_flag(const struct phyint *pi, const struct sockaddr_in6 *from, 1479b0f490f4Smh int isrouter) 14806b27086dSdd { 14816b27086dSdd struct lifreq lifr; 14826b27086dSdd char abuf[INET6_ADDRSTRLEN]; 14836b27086dSdd struct sockaddr_in6 *sin6; 14846b27086dSdd 14856b27086dSdd /* check if valid flag is being set */ 14866b27086dSdd if ((isrouter != NDF_ISROUTER_ON) && 14876b27086dSdd (isrouter != NDF_ISROUTER_OFF)) { 14886b27086dSdd logmsg(LOG_ERR, "update_ra_flag: Invalid IsRouter " 14896b27086dSdd "flag %d\n", isrouter); 14906b27086dSdd return; 14916b27086dSdd } 14926b27086dSdd 14936b27086dSdd sin6 = (struct sockaddr_in6 *)&lifr.lifr_nd.lnr_addr; 14946b27086dSdd bzero(sin6, sizeof (*sin6)); 14956b27086dSdd sin6->sin6_family = AF_INET6; 14966b27086dSdd sin6->sin6_addr = from->sin6_addr; 14976b27086dSdd 14986b27086dSdd (void) strlcpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name)); 14996b27086dSdd 15006b27086dSdd if (ioctl(pi->pi_sock, SIOCLIFGETND, (char *)&lifr) < 0) { 1501b0f490f4Smh if (errno == ESRCH) { 1502b0f490f4Smh if (debug & D_IFSCAN) { 1503b0f490f4Smh logmsg(LOG_DEBUG, 1504b0f490f4Smh "update_ra_flag: SIOCLIFGETND: nce doesn't exist, not setting IFF_ROUTER"); 1505b0f490f4Smh } 1506b0f490f4Smh } else { 1507b0f490f4Smh logperror_pi(pi, "update_ra_flag: SIOCLIFGETND"); 1508b0f490f4Smh } 15096b27086dSdd } else { 15106b27086dSdd /* 15116b27086dSdd * The lif_nd_req structure has three state values to be used 15126b27086dSdd * when changing/updating nces : 15136b27086dSdd * lnr_state_create, lnr_state_same_lla, and lnr_state_diff_lla. 15146b27086dSdd * 15156b27086dSdd * In this case, we're updating an nce, without changing lla; 15166b27086dSdd * so we set lnr_state_same_lla to ND_UNCHANGED, indicating that 15176b27086dSdd * nce's state should not be affected by our flag change. 15186b27086dSdd * 15196b27086dSdd * The kernel implementation also expects the lnr_state_create 15206b27086dSdd * field be always set, before processing ioctl request for NCE 15216b27086dSdd * update. 15226b27086dSdd * We use the state as STALE, while addressing the possibility 15236b27086dSdd * of NCE deletion when ioctl with SIOCLIFGETND argument 15246b27086dSdd * in earlier step is returned - further in such case we don't 15256b27086dSdd * want to re-create the entry in the reachable state. 15266b27086dSdd */ 15276b27086dSdd lifr.lifr_nd.lnr_state_create = ND_STALE; 15286b27086dSdd lifr.lifr_nd.lnr_state_same_lla = ND_UNCHANGED; 15296b27086dSdd lifr.lifr_nd.lnr_flags = isrouter; 15306b27086dSdd if ((ioctl(pi->pi_sock, SIOCLIFSETND, (char *)&lifr)) < 0) { 15316b27086dSdd logperror_pi(pi, "update_ra_flag: SIOCLIFSETND"); 15326b27086dSdd } else { 15336b27086dSdd (void) inet_ntop(AF_INET6, (void *)&from->sin6_addr, 15346b27086dSdd abuf, sizeof (abuf)); 15356b27086dSdd logmsg(LOG_INFO, "update_ra_flag: IsRouter flag " 15366b27086dSdd "updated for %s\n", abuf); 15376b27086dSdd } 15386b27086dSdd } 15396b27086dSdd } 1540