10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51676Sjpk * Common Development and Distribution License (the "License"). 61676Sjpk * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 228485SPeter.Memishian@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate /* 260Sstevel@tonic-gate * Copyright (c) 1990 Mentat Inc. 270Sstevel@tonic-gate */ 280Sstevel@tonic-gate 290Sstevel@tonic-gate /* 300Sstevel@tonic-gate * This file contains the interface control functions for IPv6. 310Sstevel@tonic-gate */ 320Sstevel@tonic-gate 330Sstevel@tonic-gate #include <sys/types.h> 340Sstevel@tonic-gate #include <sys/sysmacros.h> 350Sstevel@tonic-gate #include <sys/stream.h> 360Sstevel@tonic-gate #include <sys/dlpi.h> 370Sstevel@tonic-gate #include <sys/stropts.h> 380Sstevel@tonic-gate #include <sys/ddi.h> 390Sstevel@tonic-gate #include <sys/cmn_err.h> 400Sstevel@tonic-gate #include <sys/kstat.h> 410Sstevel@tonic-gate #include <sys/debug.h> 420Sstevel@tonic-gate #include <sys/zone.h> 433448Sdh155122 #include <sys/policy.h> 440Sstevel@tonic-gate 450Sstevel@tonic-gate #include <sys/systm.h> 460Sstevel@tonic-gate #include <sys/param.h> 470Sstevel@tonic-gate #include <sys/socket.h> 480Sstevel@tonic-gate #include <sys/isa_defs.h> 490Sstevel@tonic-gate #include <net/if.h> 500Sstevel@tonic-gate #include <net/if_dl.h> 510Sstevel@tonic-gate #include <net/route.h> 520Sstevel@tonic-gate #include <netinet/in.h> 530Sstevel@tonic-gate #include <netinet/igmp_var.h> 540Sstevel@tonic-gate #include <netinet/ip6.h> 550Sstevel@tonic-gate #include <netinet/icmp6.h> 560Sstevel@tonic-gate 570Sstevel@tonic-gate #include <inet/common.h> 580Sstevel@tonic-gate #include <inet/nd.h> 590Sstevel@tonic-gate #include <inet/mib2.h> 600Sstevel@tonic-gate #include <inet/ip.h> 610Sstevel@tonic-gate #include <inet/ip6.h> 620Sstevel@tonic-gate #include <inet/ip_multi.h> 630Sstevel@tonic-gate #include <inet/ip_ire.h> 640Sstevel@tonic-gate #include <inet/ip_rts.h> 650Sstevel@tonic-gate #include <inet/ip_ndp.h> 660Sstevel@tonic-gate #include <inet/ip_if.h> 670Sstevel@tonic-gate #include <inet/ip6_asp.h> 680Sstevel@tonic-gate #include <inet/ipclassifier.h> 690Sstevel@tonic-gate #include <inet/sctp_ip.h> 700Sstevel@tonic-gate 711676Sjpk #include <sys/tsol/tndb.h> 721676Sjpk #include <sys/tsol/tnet.h> 730Sstevel@tonic-gate 740Sstevel@tonic-gate static in6_addr_t ipv6_ll_template = 750Sstevel@tonic-gate {(uint32_t)V6_LINKLOCAL, 0x0, 0x0, 0x0}; 760Sstevel@tonic-gate 770Sstevel@tonic-gate static ipif_t * 780Sstevel@tonic-gate ipif_lookup_interface_v6(const in6_addr_t *if_addr, const in6_addr_t *dst, 7911042SErik.Nordmark@Sun.COM ip_stack_t *ipst); 8011042SErik.Nordmark@Sun.COM 8111042SErik.Nordmark@Sun.COM static int ipif_add_ires_v6(ipif_t *, boolean_t); 820Sstevel@tonic-gate 830Sstevel@tonic-gate /* 8411042SErik.Nordmark@Sun.COM * This function is called when an application does not specify an interface 8511042SErik.Nordmark@Sun.COM * to be used for multicast traffic. It calls ire_lookup_multi_v6() to look 864459Skcpoon * for an interface route for the specified multicast group. Doing 874459Skcpoon * this allows the administrator to add prefix routes for multicast to 884459Skcpoon * indicate which interface to be used for multicast traffic in the above 894459Skcpoon * scenario. The route could be for all multicast (ff00::/8), for a single 904459Skcpoon * multicast group (a /128 route) or anything in between. If there is no 914459Skcpoon * such multicast route, we just find any multicast capable interface and 924459Skcpoon * return it. 9311042SErik.Nordmark@Sun.COM * 9411042SErik.Nordmark@Sun.COM * We support MULTIRT and RTF_SETSRC on the multicast routes added to the 9511042SErik.Nordmark@Sun.COM * unicast table. This is used by CGTP. 960Sstevel@tonic-gate */ 970Sstevel@tonic-gate ill_t * 9811042SErik.Nordmark@Sun.COM ill_lookup_group_v6(const in6_addr_t *group, zoneid_t zoneid, ip_stack_t *ipst, 9911042SErik.Nordmark@Sun.COM boolean_t *multirtp, in6_addr_t *setsrcp) 1000Sstevel@tonic-gate { 1010Sstevel@tonic-gate ill_t *ill; 1020Sstevel@tonic-gate 10311042SErik.Nordmark@Sun.COM ill = ire_lookup_multi_ill_v6(group, zoneid, ipst, multirtp, setsrcp); 10411042SErik.Nordmark@Sun.COM if (ill != NULL) 1054459Skcpoon return (ill); 1064459Skcpoon 10711042SErik.Nordmark@Sun.COM return (ill_lookup_multicast(ipst, zoneid, B_TRUE)); 1080Sstevel@tonic-gate } 1090Sstevel@tonic-gate 1100Sstevel@tonic-gate /* 1110Sstevel@tonic-gate * Look for an ipif with the specified interface address and destination. 1120Sstevel@tonic-gate * The destination address is used only for matching point-to-point interfaces. 1130Sstevel@tonic-gate */ 1140Sstevel@tonic-gate static ipif_t * 1150Sstevel@tonic-gate ipif_lookup_interface_v6(const in6_addr_t *if_addr, const in6_addr_t *dst, 11611042SErik.Nordmark@Sun.COM ip_stack_t *ipst) 1170Sstevel@tonic-gate { 1180Sstevel@tonic-gate ipif_t *ipif; 1190Sstevel@tonic-gate ill_t *ill; 1200Sstevel@tonic-gate ill_walk_context_t ctx; 1210Sstevel@tonic-gate 1220Sstevel@tonic-gate /* 1230Sstevel@tonic-gate * First match all the point-to-point interfaces 1240Sstevel@tonic-gate * before looking at non-point-to-point interfaces. 1250Sstevel@tonic-gate * This is done to avoid returning non-point-to-point 1260Sstevel@tonic-gate * ipif instead of unnumbered point-to-point ipif. 1270Sstevel@tonic-gate */ 1283448Sdh155122 rw_enter(&ipst->ips_ill_g_lock, RW_READER); 1293448Sdh155122 ill = ILL_START_WALK_V6(&ctx, ipst); 1300Sstevel@tonic-gate for (; ill != NULL; ill = ill_next(&ctx, ill)) { 1310Sstevel@tonic-gate mutex_enter(&ill->ill_lock); 1322733Snordmark for (ipif = ill->ill_ipif; ipif != NULL; 1332733Snordmark ipif = ipif->ipif_next) { 1340Sstevel@tonic-gate /* Allow the ipif to be down */ 1350Sstevel@tonic-gate if ((ipif->ipif_flags & IPIF_POINTOPOINT) && 1360Sstevel@tonic-gate (IN6_ARE_ADDR_EQUAL(&ipif->ipif_v6lcl_addr, 1370Sstevel@tonic-gate if_addr)) && 1380Sstevel@tonic-gate (IN6_ARE_ADDR_EQUAL(&ipif->ipif_v6pp_dst_addr, 1390Sstevel@tonic-gate dst))) { 14011042SErik.Nordmark@Sun.COM if (!IPIF_IS_CONDEMNED(ipif)) { 1410Sstevel@tonic-gate ipif_refhold_locked(ipif); 1420Sstevel@tonic-gate mutex_exit(&ill->ill_lock); 1433448Sdh155122 rw_exit(&ipst->ips_ill_g_lock); 1440Sstevel@tonic-gate return (ipif); 1450Sstevel@tonic-gate } 1460Sstevel@tonic-gate } 1470Sstevel@tonic-gate } 1480Sstevel@tonic-gate mutex_exit(&ill->ill_lock); 1490Sstevel@tonic-gate } 1503448Sdh155122 rw_exit(&ipst->ips_ill_g_lock); 1510Sstevel@tonic-gate /* lookup the ipif based on interface address */ 15211042SErik.Nordmark@Sun.COM ipif = ipif_lookup_addr_v6(if_addr, NULL, ALL_ZONES, ipst); 1530Sstevel@tonic-gate ASSERT(ipif == NULL || ipif->ipif_isv6); 1540Sstevel@tonic-gate return (ipif); 1550Sstevel@tonic-gate } 1560Sstevel@tonic-gate 1570Sstevel@tonic-gate /* 1588485SPeter.Memishian@Sun.COM * Common function for ipif_lookup_addr_v6() and ipif_lookup_addr_exact_v6(). 1590Sstevel@tonic-gate */ 1608485SPeter.Memishian@Sun.COM static ipif_t * 1618485SPeter.Memishian@Sun.COM ipif_lookup_addr_common_v6(const in6_addr_t *addr, ill_t *match_ill, 16211042SErik.Nordmark@Sun.COM uint32_t match_flags, zoneid_t zoneid, ip_stack_t *ipst) 1630Sstevel@tonic-gate { 1640Sstevel@tonic-gate ipif_t *ipif; 1650Sstevel@tonic-gate ill_t *ill; 1660Sstevel@tonic-gate boolean_t ptp = B_FALSE; 1670Sstevel@tonic-gate ill_walk_context_t ctx; 16811042SErik.Nordmark@Sun.COM boolean_t match_illgrp = (match_flags & IPIF_MATCH_ILLGRP); 16911042SErik.Nordmark@Sun.COM boolean_t no_duplicate = (match_flags & IPIF_MATCH_NONDUP); 1700Sstevel@tonic-gate 1713448Sdh155122 rw_enter(&ipst->ips_ill_g_lock, RW_READER); 1720Sstevel@tonic-gate /* 1730Sstevel@tonic-gate * Repeat twice, first based on local addresses and 1740Sstevel@tonic-gate * next time for pointopoint. 1750Sstevel@tonic-gate */ 1760Sstevel@tonic-gate repeat: 1773448Sdh155122 ill = ILL_START_WALK_V6(&ctx, ipst); 1780Sstevel@tonic-gate for (; ill != NULL; ill = ill_next(&ctx, ill)) { 1798485SPeter.Memishian@Sun.COM if (match_ill != NULL && ill != match_ill && 1808485SPeter.Memishian@Sun.COM (!match_illgrp || !IS_IN_SAME_ILLGRP(ill, match_ill))) { 1810Sstevel@tonic-gate continue; 1820Sstevel@tonic-gate } 1830Sstevel@tonic-gate mutex_enter(&ill->ill_lock); 1842733Snordmark for (ipif = ill->ill_ipif; ipif != NULL; 1852733Snordmark ipif = ipif->ipif_next) { 1861676Sjpk if (zoneid != ALL_ZONES && 1871676Sjpk ipif->ipif_zoneid != zoneid && 1881676Sjpk ipif->ipif_zoneid != ALL_ZONES) 1890Sstevel@tonic-gate continue; 19011042SErik.Nordmark@Sun.COM 19111042SErik.Nordmark@Sun.COM if (no_duplicate && 19211042SErik.Nordmark@Sun.COM !(ipif->ipif_flags & IPIF_UP)) { 19311042SErik.Nordmark@Sun.COM continue; 19411042SErik.Nordmark@Sun.COM } 19511042SErik.Nordmark@Sun.COM 1960Sstevel@tonic-gate /* Allow the ipif to be down */ 1970Sstevel@tonic-gate if ((!ptp && (IN6_ARE_ADDR_EQUAL( 1980Sstevel@tonic-gate &ipif->ipif_v6lcl_addr, addr) && 1990Sstevel@tonic-gate (ipif->ipif_flags & IPIF_UNNUMBERED) == 0)) || 2000Sstevel@tonic-gate (ptp && (ipif->ipif_flags & IPIF_POINTOPOINT) && 2010Sstevel@tonic-gate IN6_ARE_ADDR_EQUAL(&ipif->ipif_v6pp_dst_addr, 2020Sstevel@tonic-gate addr))) { 20311042SErik.Nordmark@Sun.COM if (!IPIF_IS_CONDEMNED(ipif)) { 2040Sstevel@tonic-gate ipif_refhold_locked(ipif); 2050Sstevel@tonic-gate mutex_exit(&ill->ill_lock); 2063448Sdh155122 rw_exit(&ipst->ips_ill_g_lock); 2070Sstevel@tonic-gate return (ipif); 2080Sstevel@tonic-gate } 2090Sstevel@tonic-gate } 2100Sstevel@tonic-gate } 2110Sstevel@tonic-gate mutex_exit(&ill->ill_lock); 2120Sstevel@tonic-gate } 2130Sstevel@tonic-gate 2142733Snordmark /* If we already did the ptp case, then we are done */ 2150Sstevel@tonic-gate if (ptp) { 2163448Sdh155122 rw_exit(&ipst->ips_ill_g_lock); 2170Sstevel@tonic-gate return (NULL); 2180Sstevel@tonic-gate } 2190Sstevel@tonic-gate ptp = B_TRUE; 2200Sstevel@tonic-gate goto repeat; 2210Sstevel@tonic-gate } 2220Sstevel@tonic-gate 2230Sstevel@tonic-gate /* 2248485SPeter.Memishian@Sun.COM * Lookup an ipif with the specified address. For point-to-point links we 2258485SPeter.Memishian@Sun.COM * look for matches on either the destination address or the local address, 2268485SPeter.Memishian@Sun.COM * but we skip the local address check if IPIF_UNNUMBERED is set. If the 2278485SPeter.Memishian@Sun.COM * `match_ill' argument is non-NULL, the lookup is restricted to that ill 2288485SPeter.Memishian@Sun.COM * (or illgrp if `match_ill' is in an IPMP group). 2298485SPeter.Memishian@Sun.COM */ 2308485SPeter.Memishian@Sun.COM ipif_t * 2318485SPeter.Memishian@Sun.COM ipif_lookup_addr_v6(const in6_addr_t *addr, ill_t *match_ill, zoneid_t zoneid, 23211042SErik.Nordmark@Sun.COM ip_stack_t *ipst) 2338485SPeter.Memishian@Sun.COM { 23411042SErik.Nordmark@Sun.COM return (ipif_lookup_addr_common_v6(addr, match_ill, IPIF_MATCH_ILLGRP, 23511042SErik.Nordmark@Sun.COM zoneid, ipst)); 23611042SErik.Nordmark@Sun.COM } 23711042SErik.Nordmark@Sun.COM 23811042SErik.Nordmark@Sun.COM /* 23911042SErik.Nordmark@Sun.COM * Lookup an ipif with the specified address. Similar to ipif_lookup_addr, 24011042SErik.Nordmark@Sun.COM * except that we will only return an address if it is not marked as 24111042SErik.Nordmark@Sun.COM * IPIF_DUPLICATE 24211042SErik.Nordmark@Sun.COM */ 24311042SErik.Nordmark@Sun.COM ipif_t * 24411042SErik.Nordmark@Sun.COM ipif_lookup_addr_nondup_v6(const in6_addr_t *addr, ill_t *match_ill, 24511042SErik.Nordmark@Sun.COM zoneid_t zoneid, ip_stack_t *ipst) 24611042SErik.Nordmark@Sun.COM { 24711042SErik.Nordmark@Sun.COM return (ipif_lookup_addr_common_v6(addr, match_ill, 24811042SErik.Nordmark@Sun.COM (IPIF_MATCH_ILLGRP | IPIF_MATCH_NONDUP), zoneid, 24911042SErik.Nordmark@Sun.COM ipst)); 2508485SPeter.Memishian@Sun.COM } 2518485SPeter.Memishian@Sun.COM 2528485SPeter.Memishian@Sun.COM /* 2538485SPeter.Memishian@Sun.COM * Special abbreviated version of ipif_lookup_addr_v6() that doesn't match 2548485SPeter.Memishian@Sun.COM * `match_ill' across the IPMP group. This function is only needed in some 2558485SPeter.Memishian@Sun.COM * corner-cases; almost everything should use ipif_lookup_addr_v6(). 2568485SPeter.Memishian@Sun.COM */ 2578485SPeter.Memishian@Sun.COM ipif_t * 2588485SPeter.Memishian@Sun.COM ipif_lookup_addr_exact_v6(const in6_addr_t *addr, ill_t *match_ill, 2598485SPeter.Memishian@Sun.COM ip_stack_t *ipst) 2608485SPeter.Memishian@Sun.COM { 2618485SPeter.Memishian@Sun.COM ASSERT(match_ill != NULL); 26211042SErik.Nordmark@Sun.COM return (ipif_lookup_addr_common_v6(addr, match_ill, 0, ALL_ZONES, 26311042SErik.Nordmark@Sun.COM ipst)); 2648485SPeter.Memishian@Sun.COM } 2658485SPeter.Memishian@Sun.COM 2668485SPeter.Memishian@Sun.COM /* 2672733Snordmark * Look for an ipif with the specified address. For point-point links 2682733Snordmark * we look for matches on either the destination address and the local 2692733Snordmark * address, but we ignore the check on the local address if IPIF_UNNUMBERED 2702733Snordmark * is set. 2718485SPeter.Memishian@Sun.COM * If the `match_ill' argument is non-NULL, the lookup is restricted to that 2728485SPeter.Memishian@Sun.COM * ill (or illgrp if `match_ill' is in an IPMP group). 2732733Snordmark * Return the zoneid for the ipif. ALL_ZONES if none found. 2742733Snordmark */ 2752733Snordmark zoneid_t 2763448Sdh155122 ipif_lookup_addr_zoneid_v6(const in6_addr_t *addr, ill_t *match_ill, 2773448Sdh155122 ip_stack_t *ipst) 2782733Snordmark { 2792733Snordmark ipif_t *ipif; 2802733Snordmark ill_t *ill; 2812733Snordmark boolean_t ptp = B_FALSE; 2822733Snordmark ill_walk_context_t ctx; 2832733Snordmark zoneid_t zoneid; 2842733Snordmark 2853448Sdh155122 rw_enter(&ipst->ips_ill_g_lock, RW_READER); 2862733Snordmark /* 2872733Snordmark * Repeat twice, first based on local addresses and 2882733Snordmark * next time for pointopoint. 2892733Snordmark */ 2902733Snordmark repeat: 2913448Sdh155122 ill = ILL_START_WALK_V6(&ctx, ipst); 2922733Snordmark for (; ill != NULL; ill = ill_next(&ctx, ill)) { 2938485SPeter.Memishian@Sun.COM if (match_ill != NULL && ill != match_ill && 2948485SPeter.Memishian@Sun.COM !IS_IN_SAME_ILLGRP(ill, match_ill)) { 2952733Snordmark continue; 2962733Snordmark } 2972733Snordmark mutex_enter(&ill->ill_lock); 2982733Snordmark for (ipif = ill->ill_ipif; ipif != NULL; 2992733Snordmark ipif = ipif->ipif_next) { 3002733Snordmark /* Allow the ipif to be down */ 3012733Snordmark if ((!ptp && (IN6_ARE_ADDR_EQUAL( 3022733Snordmark &ipif->ipif_v6lcl_addr, addr) && 3032733Snordmark (ipif->ipif_flags & IPIF_UNNUMBERED) == 0)) || 3042733Snordmark (ptp && (ipif->ipif_flags & IPIF_POINTOPOINT) && 3052733Snordmark IN6_ARE_ADDR_EQUAL(&ipif->ipif_v6pp_dst_addr, 3062733Snordmark addr)) && 3072733Snordmark !(ipif->ipif_state_flags & IPIF_CONDEMNED)) { 3082733Snordmark zoneid = ipif->ipif_zoneid; 3092733Snordmark mutex_exit(&ill->ill_lock); 3103448Sdh155122 rw_exit(&ipst->ips_ill_g_lock); 3112733Snordmark /* 3122733Snordmark * If ipif_zoneid was ALL_ZONES then we have 3132733Snordmark * a trusted extensions shared IP address. 3142733Snordmark * In that case GLOBAL_ZONEID works to send. 3152733Snordmark */ 3162733Snordmark if (zoneid == ALL_ZONES) 3172733Snordmark zoneid = GLOBAL_ZONEID; 3182733Snordmark return (zoneid); 3192733Snordmark } 3202733Snordmark } 3212733Snordmark mutex_exit(&ill->ill_lock); 3222733Snordmark } 3232733Snordmark 3242733Snordmark /* If we already did the ptp case, then we are done */ 3252733Snordmark if (ptp) { 3263448Sdh155122 rw_exit(&ipst->ips_ill_g_lock); 3272733Snordmark return (ALL_ZONES); 3282733Snordmark } 3292733Snordmark ptp = B_TRUE; 3302733Snordmark goto repeat; 3312733Snordmark } 3322733Snordmark 3332733Snordmark /* 3340Sstevel@tonic-gate * Perform various checks to verify that an address would make sense as a local 3350Sstevel@tonic-gate * interface address. This is currently only called when an attempt is made 3360Sstevel@tonic-gate * to set a local address. 3370Sstevel@tonic-gate * 3380Sstevel@tonic-gate * Does not allow a v4-mapped address, an address that equals the subnet 3390Sstevel@tonic-gate * anycast address, ... a multicast address, ... 3400Sstevel@tonic-gate */ 3410Sstevel@tonic-gate boolean_t 3420Sstevel@tonic-gate ip_local_addr_ok_v6(const in6_addr_t *addr, const in6_addr_t *subnet_mask) 3430Sstevel@tonic-gate { 3440Sstevel@tonic-gate in6_addr_t subnet; 3450Sstevel@tonic-gate 3460Sstevel@tonic-gate if (IN6_IS_ADDR_UNSPECIFIED(addr)) 3470Sstevel@tonic-gate return (B_TRUE); /* Allow all zeros */ 3480Sstevel@tonic-gate 3490Sstevel@tonic-gate /* 3500Sstevel@tonic-gate * Don't allow all zeroes or host part, but allow 3510Sstevel@tonic-gate * all ones netmask. 3520Sstevel@tonic-gate */ 3530Sstevel@tonic-gate V6_MASK_COPY(*addr, *subnet_mask, subnet); 3540Sstevel@tonic-gate if (IN6_IS_ADDR_V4MAPPED(addr) || 3550Sstevel@tonic-gate (IN6_ARE_ADDR_EQUAL(addr, &subnet) && 3560Sstevel@tonic-gate !IN6_ARE_ADDR_EQUAL(subnet_mask, &ipv6_all_ones)) || 3570Sstevel@tonic-gate (IN6_IS_ADDR_V4COMPAT(addr) && CLASSD(V4_PART_OF_V6((*addr)))) || 3580Sstevel@tonic-gate IN6_IS_ADDR_MULTICAST(addr)) 3590Sstevel@tonic-gate return (B_FALSE); 3600Sstevel@tonic-gate 3610Sstevel@tonic-gate return (B_TRUE); 3620Sstevel@tonic-gate } 3630Sstevel@tonic-gate 3640Sstevel@tonic-gate /* 3650Sstevel@tonic-gate * Perform various checks to verify that an address would make sense as a 3660Sstevel@tonic-gate * remote/subnet interface address. 3670Sstevel@tonic-gate */ 3680Sstevel@tonic-gate boolean_t 3690Sstevel@tonic-gate ip_remote_addr_ok_v6(const in6_addr_t *addr, const in6_addr_t *subnet_mask) 3700Sstevel@tonic-gate { 3710Sstevel@tonic-gate in6_addr_t subnet; 3720Sstevel@tonic-gate 3730Sstevel@tonic-gate if (IN6_IS_ADDR_UNSPECIFIED(addr)) 3740Sstevel@tonic-gate return (B_TRUE); /* Allow all zeros */ 3750Sstevel@tonic-gate 3760Sstevel@tonic-gate V6_MASK_COPY(*addr, *subnet_mask, subnet); 3770Sstevel@tonic-gate if (IN6_IS_ADDR_V4MAPPED(addr) || 3780Sstevel@tonic-gate (IN6_ARE_ADDR_EQUAL(addr, &subnet) && 3790Sstevel@tonic-gate !IN6_ARE_ADDR_EQUAL(subnet_mask, &ipv6_all_ones)) || 3800Sstevel@tonic-gate IN6_IS_ADDR_MULTICAST(addr) || 3810Sstevel@tonic-gate (IN6_IS_ADDR_V4COMPAT(addr) && CLASSD(V4_PART_OF_V6((*addr))))) 3820Sstevel@tonic-gate return (B_FALSE); 3830Sstevel@tonic-gate 3840Sstevel@tonic-gate return (B_TRUE); 3850Sstevel@tonic-gate } 3860Sstevel@tonic-gate 3870Sstevel@tonic-gate /* 3880Sstevel@tonic-gate * ip_rt_add_v6 is called to add an IPv6 route to the forwarding table. 38911042SErik.Nordmark@Sun.COM * ill is passed in to associate it with the correct interface 3900Sstevel@tonic-gate * (for link-local destinations and gateways). 39111042SErik.Nordmark@Sun.COM * If ire_arg is set, then we return the held IRE in that location. 3920Sstevel@tonic-gate */ 3930Sstevel@tonic-gate /* ARGSUSED1 */ 3940Sstevel@tonic-gate int 3950Sstevel@tonic-gate ip_rt_add_v6(const in6_addr_t *dst_addr, const in6_addr_t *mask, 3960Sstevel@tonic-gate const in6_addr_t *gw_addr, const in6_addr_t *src_addr, int flags, 39711042SErik.Nordmark@Sun.COM ill_t *ill, ire_t **ire_arg, struct rtsa_s *sp, ip_stack_t *ipst, 39811042SErik.Nordmark@Sun.COM zoneid_t zoneid) 3990Sstevel@tonic-gate { 40011042SErik.Nordmark@Sun.COM ire_t *ire, *nire; 4010Sstevel@tonic-gate ire_t *gw_ire = NULL; 4020Sstevel@tonic-gate ipif_t *ipif; 4030Sstevel@tonic-gate uint_t type; 4040Sstevel@tonic-gate int match_flags = MATCH_IRE_TYPE; 4051676Sjpk tsol_gc_t *gc = NULL; 4061676Sjpk tsol_gcgrp_t *gcgrp = NULL; 4071676Sjpk boolean_t gcgrp_xtraref = B_FALSE; 4080Sstevel@tonic-gate 4090Sstevel@tonic-gate if (ire_arg != NULL) 4100Sstevel@tonic-gate *ire_arg = NULL; 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate /* 4130Sstevel@tonic-gate * Prevent routes with a zero gateway from being created (since 4140Sstevel@tonic-gate * interfaces can currently be plumbed and brought up with no assigned 4150Sstevel@tonic-gate * address). 4160Sstevel@tonic-gate */ 4170Sstevel@tonic-gate if (IN6_IS_ADDR_UNSPECIFIED(gw_addr)) 4180Sstevel@tonic-gate return (ENETUNREACH); 4190Sstevel@tonic-gate 4200Sstevel@tonic-gate /* 4210Sstevel@tonic-gate * If this is the case of RTF_HOST being set, then we set the netmask 4220Sstevel@tonic-gate * to all ones (regardless if one was supplied). 4230Sstevel@tonic-gate */ 4240Sstevel@tonic-gate if (flags & RTF_HOST) 4250Sstevel@tonic-gate mask = &ipv6_all_ones; 4260Sstevel@tonic-gate 4270Sstevel@tonic-gate /* 4280Sstevel@tonic-gate * Get the ipif, if any, corresponding to the gw_addr 42911042SErik.Nordmark@Sun.COM * If -ifp was specified we restrict ourselves to the ill, otherwise 43011042SErik.Nordmark@Sun.COM * we match on the gatway and destination to handle unnumbered pt-pt 43111042SErik.Nordmark@Sun.COM * interfaces. 4320Sstevel@tonic-gate */ 43311042SErik.Nordmark@Sun.COM if (ill != NULL) 43411042SErik.Nordmark@Sun.COM ipif = ipif_lookup_addr_v6(gw_addr, ill, ALL_ZONES, ipst); 43511042SErik.Nordmark@Sun.COM else 43611042SErik.Nordmark@Sun.COM ipif = ipif_lookup_interface_v6(gw_addr, dst_addr, ipst); 43711042SErik.Nordmark@Sun.COM if (ipif != NULL) { 43811042SErik.Nordmark@Sun.COM if (IS_VNI(ipif->ipif_ill)) { 43911042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 44011042SErik.Nordmark@Sun.COM return (EINVAL); 44111042SErik.Nordmark@Sun.COM } 4420Sstevel@tonic-gate } 4430Sstevel@tonic-gate 4440Sstevel@tonic-gate /* 4450Sstevel@tonic-gate * GateD will attempt to create routes with a loopback interface 4460Sstevel@tonic-gate * address as the gateway and with RTF_GATEWAY set. We allow 4470Sstevel@tonic-gate * these routes to be added, but create them as interface routes 4480Sstevel@tonic-gate * since the gateway is an interface address. 4490Sstevel@tonic-gate */ 4501822Srk129064 if ((ipif != NULL) && (ipif->ipif_ire_type == IRE_LOOPBACK)) { 4510Sstevel@tonic-gate flags &= ~RTF_GATEWAY; 4521822Srk129064 if (IN6_ARE_ADDR_EQUAL(gw_addr, &ipv6_loopback) && 4531822Srk129064 IN6_ARE_ADDR_EQUAL(dst_addr, &ipv6_loopback) && 4541822Srk129064 IN6_ARE_ADDR_EQUAL(mask, &ipv6_all_ones)) { 45511042SErik.Nordmark@Sun.COM ire = ire_ftable_lookup_v6(dst_addr, 0, 0, IRE_LOOPBACK, 45611042SErik.Nordmark@Sun.COM NULL, ALL_ZONES, NULL, MATCH_IRE_TYPE, 0, ipst, 45711042SErik.Nordmark@Sun.COM NULL); 4581822Srk129064 if (ire != NULL) { 4591822Srk129064 ire_refrele(ire); 46011042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 4611822Srk129064 return (EEXIST); 4621822Srk129064 } 46311042SErik.Nordmark@Sun.COM ip1dbg(("ip_rt_add_v6: 0x%p creating IRE 0x%x" 4641822Srk129064 "for 0x%x\n", (void *)ipif, 4651822Srk129064 ipif->ipif_ire_type, 4661822Srk129064 ntohl(ipif->ipif_lcl_addr))); 4671822Srk129064 ire = ire_create_v6( 4681822Srk129064 dst_addr, 4691822Srk129064 mask, 4701822Srk129064 NULL, 47111042SErik.Nordmark@Sun.COM ipif->ipif_ire_type, /* LOOPBACK */ 47211042SErik.Nordmark@Sun.COM ipif->ipif_ill, 47311042SErik.Nordmark@Sun.COM zoneid, 47411042SErik.Nordmark@Sun.COM (ipif->ipif_flags & IPIF_PRIVATE) ? RTF_PRIVATE : 0, 4753448Sdh155122 NULL, 4763448Sdh155122 ipst); 47711042SErik.Nordmark@Sun.COM 4781822Srk129064 if (ire == NULL) { 47911042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 48011042SErik.Nordmark@Sun.COM return (ENOMEM); 48111042SErik.Nordmark@Sun.COM } 48211042SErik.Nordmark@Sun.COM /* src address assigned by the caller? */ 48311042SErik.Nordmark@Sun.COM if ((flags & RTF_SETSRC) && 48411042SErik.Nordmark@Sun.COM !IN6_IS_ADDR_UNSPECIFIED(src_addr)) 48511042SErik.Nordmark@Sun.COM ire->ire_setsrc_addr_v6 = *src_addr; 48611042SErik.Nordmark@Sun.COM 48711042SErik.Nordmark@Sun.COM nire = ire_add(ire); 48811042SErik.Nordmark@Sun.COM if (nire == NULL) { 48911042SErik.Nordmark@Sun.COM /* 49011042SErik.Nordmark@Sun.COM * In the result of failure, ire_add() will have 49111042SErik.Nordmark@Sun.COM * already deleted the ire in question, so there 49211042SErik.Nordmark@Sun.COM * is no need to do that here. 49311042SErik.Nordmark@Sun.COM */ 49411042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 4951822Srk129064 return (ENOMEM); 4961822Srk129064 } 4971822Srk129064 /* 49811042SErik.Nordmark@Sun.COM * Check if it was a duplicate entry. This handles 49911042SErik.Nordmark@Sun.COM * the case of two racing route adds for the same route 5001822Srk129064 */ 50111042SErik.Nordmark@Sun.COM if (nire != ire) { 50211042SErik.Nordmark@Sun.COM ASSERT(nire->ire_identical_ref > 1); 50311042SErik.Nordmark@Sun.COM ire_delete(nire); 50411042SErik.Nordmark@Sun.COM ire_refrele(nire); 5051822Srk129064 ipif_refrele(ipif); 50611042SErik.Nordmark@Sun.COM return (EEXIST); 50711042SErik.Nordmark@Sun.COM } 50811042SErik.Nordmark@Sun.COM ire = nire; 50911042SErik.Nordmark@Sun.COM goto save_ire; 5101822Srk129064 } 5111822Srk129064 } 5120Sstevel@tonic-gate 5130Sstevel@tonic-gate /* 51411042SErik.Nordmark@Sun.COM * The routes for multicast with CGTP are quite special in that 51511042SErik.Nordmark@Sun.COM * the gateway is the local interface address, yet RTF_GATEWAY 51611042SErik.Nordmark@Sun.COM * is set. We turn off RTF_GATEWAY to provide compatibility with 51711042SErik.Nordmark@Sun.COM * this undocumented and unusual use of multicast routes. 51811042SErik.Nordmark@Sun.COM */ 51911042SErik.Nordmark@Sun.COM if ((flags & RTF_MULTIRT) && ipif != NULL) 52011042SErik.Nordmark@Sun.COM flags &= ~RTF_GATEWAY; 52111042SErik.Nordmark@Sun.COM 52211042SErik.Nordmark@Sun.COM /* 5230Sstevel@tonic-gate * Traditionally, interface routes are ones where RTF_GATEWAY isn't set 5240Sstevel@tonic-gate * and the gateway address provided is one of the system's interface 5250Sstevel@tonic-gate * addresses. By using the routing socket interface and supplying an 5260Sstevel@tonic-gate * RTA_IFP sockaddr with an interface index, an alternate method of 5270Sstevel@tonic-gate * specifying an interface route to be created is available which uses 5280Sstevel@tonic-gate * the interface index that specifies the outgoing interface rather than 5290Sstevel@tonic-gate * the address of an outgoing interface (which may not be able to 5300Sstevel@tonic-gate * uniquely identify an interface). When coupled with the RTF_GATEWAY 5310Sstevel@tonic-gate * flag, routes can be specified which not only specify the next-hop to 5320Sstevel@tonic-gate * be used when routing to a certain prefix, but also which outgoing 5330Sstevel@tonic-gate * interface should be used. 5340Sstevel@tonic-gate * 5350Sstevel@tonic-gate * Previously, interfaces would have unique addresses assigned to them 5360Sstevel@tonic-gate * and so the address assigned to a particular interface could be used 5370Sstevel@tonic-gate * to identify a particular interface. One exception to this was the 5380Sstevel@tonic-gate * case of an unnumbered interface (where IPIF_UNNUMBERED was set). 5390Sstevel@tonic-gate * 5400Sstevel@tonic-gate * With the advent of IPv6 and its link-local addresses, this 5410Sstevel@tonic-gate * restriction was relaxed and interfaces could share addresses between 5420Sstevel@tonic-gate * themselves. In fact, typically all of the link-local interfaces on 5430Sstevel@tonic-gate * an IPv6 node or router will have the same link-local address. In 5440Sstevel@tonic-gate * order to differentiate between these interfaces, the use of an 5450Sstevel@tonic-gate * interface index is necessary and this index can be carried inside a 5460Sstevel@tonic-gate * RTA_IFP sockaddr (which is actually a sockaddr_dl). One restriction 5470Sstevel@tonic-gate * of using the interface index, however, is that all of the ipif's that 5480Sstevel@tonic-gate * are part of an ill have the same index and so the RTA_IFP sockaddr 5490Sstevel@tonic-gate * cannot be used to differentiate between ipif's (or logical 5500Sstevel@tonic-gate * interfaces) that belong to the same ill (physical interface). 5510Sstevel@tonic-gate * 5520Sstevel@tonic-gate * For example, in the following case involving IPv4 interfaces and 5530Sstevel@tonic-gate * logical interfaces 5540Sstevel@tonic-gate * 5550Sstevel@tonic-gate * 192.0.2.32 255.255.255.224 192.0.2.33 U if0 55611042SErik.Nordmark@Sun.COM * 192.0.2.32 255.255.255.224 192.0.2.34 U if0 55711042SErik.Nordmark@Sun.COM * 192.0.2.32 255.255.255.224 192.0.2.35 U if0 5580Sstevel@tonic-gate * 5590Sstevel@tonic-gate * the ipif's corresponding to each of these interface routes can be 5600Sstevel@tonic-gate * uniquely identified by the "gateway" (actually interface address). 5610Sstevel@tonic-gate * 5620Sstevel@tonic-gate * In this case involving multiple IPv6 default routes to a particular 5630Sstevel@tonic-gate * link-local gateway, the use of RTA_IFP is necessary to specify which 5640Sstevel@tonic-gate * default route is of interest: 5650Sstevel@tonic-gate * 5660Sstevel@tonic-gate * default fe80::123:4567:89ab:cdef U if0 5670Sstevel@tonic-gate * default fe80::123:4567:89ab:cdef U if1 5680Sstevel@tonic-gate */ 5690Sstevel@tonic-gate 5700Sstevel@tonic-gate /* RTF_GATEWAY not set */ 5710Sstevel@tonic-gate if (!(flags & RTF_GATEWAY)) { 5721676Sjpk if (sp != NULL) { 5731676Sjpk ip2dbg(("ip_rt_add_v6: gateway security attributes " 5741676Sjpk "cannot be set with interface route\n")); 57511042SErik.Nordmark@Sun.COM if (ipif != NULL) 5761676Sjpk ipif_refrele(ipif); 5771676Sjpk return (EINVAL); 5781676Sjpk } 5791676Sjpk 5800Sstevel@tonic-gate /* 58111042SErik.Nordmark@Sun.COM * Whether or not ill (RTA_IFP) is set, we require that 58211042SErik.Nordmark@Sun.COM * the gateway is one of our local addresses. 5830Sstevel@tonic-gate */ 58411042SErik.Nordmark@Sun.COM if (ipif == NULL) 58511042SErik.Nordmark@Sun.COM return (ENETUNREACH); 58611042SErik.Nordmark@Sun.COM 58711042SErik.Nordmark@Sun.COM /* 58811042SErik.Nordmark@Sun.COM * We use MATCH_IRE_ILL here. If the caller specified an 58911042SErik.Nordmark@Sun.COM * interface (from the RTA_IFP sockaddr) we use it, otherwise 59011042SErik.Nordmark@Sun.COM * we use the ill derived from the gateway address. 59111042SErik.Nordmark@Sun.COM * We can always match the gateway address since we record it 59211042SErik.Nordmark@Sun.COM * in ire_gateway_addr. 59311042SErik.Nordmark@Sun.COM * We don't allow RTA_IFP to specify a different ill than the 59411042SErik.Nordmark@Sun.COM * one matching the ipif to make sure we can delete the route. 59511042SErik.Nordmark@Sun.COM */ 59611042SErik.Nordmark@Sun.COM match_flags |= MATCH_IRE_GW | MATCH_IRE_ILL; 59711042SErik.Nordmark@Sun.COM if (ill == NULL) { 59811042SErik.Nordmark@Sun.COM ill = ipif->ipif_ill; 59911042SErik.Nordmark@Sun.COM } else if (ill != ipif->ipif_ill) { 60011042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 60111042SErik.Nordmark@Sun.COM return (EINVAL); 6020Sstevel@tonic-gate } 6030Sstevel@tonic-gate 6040Sstevel@tonic-gate /* 6050Sstevel@tonic-gate * We check for an existing entry at this point. 6060Sstevel@tonic-gate */ 6070Sstevel@tonic-gate match_flags |= MATCH_IRE_MASK; 60811042SErik.Nordmark@Sun.COM ire = ire_ftable_lookup_v6(dst_addr, mask, gw_addr, 60911042SErik.Nordmark@Sun.COM IRE_INTERFACE, ill, ALL_ZONES, NULL, match_flags, 0, ipst, 61011042SErik.Nordmark@Sun.COM NULL); 6110Sstevel@tonic-gate if (ire != NULL) { 6120Sstevel@tonic-gate ire_refrele(ire); 61311042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 6140Sstevel@tonic-gate return (EEXIST); 6150Sstevel@tonic-gate } 6160Sstevel@tonic-gate 6170Sstevel@tonic-gate /* 6180Sstevel@tonic-gate * Create a copy of the IRE_LOOPBACK, IRE_IF_NORESOLVER or 61911042SErik.Nordmark@Sun.COM * IRE_IF_RESOLVER with the modified address, netmask, and 62011042SErik.Nordmark@Sun.COM * gateway. 6210Sstevel@tonic-gate */ 6220Sstevel@tonic-gate ire = ire_create_v6( 6230Sstevel@tonic-gate dst_addr, 6240Sstevel@tonic-gate mask, 62511042SErik.Nordmark@Sun.COM gw_addr, 62611042SErik.Nordmark@Sun.COM ill->ill_net_type, 62711042SErik.Nordmark@Sun.COM ill, 62811042SErik.Nordmark@Sun.COM zoneid, 6290Sstevel@tonic-gate flags, 6303448Sdh155122 NULL, 6313448Sdh155122 ipst); 6320Sstevel@tonic-gate if (ire == NULL) { 63311042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 6340Sstevel@tonic-gate return (ENOMEM); 6350Sstevel@tonic-gate } 6360Sstevel@tonic-gate 6370Sstevel@tonic-gate /* 6380Sstevel@tonic-gate * Some software (for example, GateD and Sun Cluster) attempts 6390Sstevel@tonic-gate * to create (what amount to) IRE_PREFIX routes with the 6400Sstevel@tonic-gate * loopback address as the gateway. This is primarily done to 6410Sstevel@tonic-gate * set up prefixes with the RTF_REJECT flag set (for example, 6425907Sja97890 * when generating aggregate routes). We also OR in the 6435907Sja97890 * RTF_BLACKHOLE flag as these interface routes, by 6445907Sja97890 * definition, can only be that. 6450Sstevel@tonic-gate * 64611042SErik.Nordmark@Sun.COM * If the IRE type (as defined by ill->ill_net_type) is 6470Sstevel@tonic-gate * IRE_LOOPBACK, then we map the request into a 6480Sstevel@tonic-gate * IRE_IF_NORESOLVER. 6490Sstevel@tonic-gate * 6500Sstevel@tonic-gate * Needless to say, the real IRE_LOOPBACK is NOT created by this 6510Sstevel@tonic-gate * routine, but rather using ire_create_v6() directly. 6520Sstevel@tonic-gate */ 65311042SErik.Nordmark@Sun.COM if (ill->ill_net_type == IRE_LOOPBACK) { 6540Sstevel@tonic-gate ire->ire_type = IRE_IF_NORESOLVER; 6555907Sja97890 ire->ire_flags |= RTF_BLACKHOLE; 6565907Sja97890 } 65711042SErik.Nordmark@Sun.COM /* src address assigned by the caller? */ 65811042SErik.Nordmark@Sun.COM if ((flags & RTF_SETSRC) && !IN6_IS_ADDR_UNSPECIFIED(src_addr)) 65911042SErik.Nordmark@Sun.COM ire->ire_setsrc_addr_v6 = *src_addr; 66011042SErik.Nordmark@Sun.COM 66111042SErik.Nordmark@Sun.COM nire = ire_add(ire); 66211042SErik.Nordmark@Sun.COM if (nire == NULL) { 66311042SErik.Nordmark@Sun.COM /* 66411042SErik.Nordmark@Sun.COM * In the result of failure, ire_add() will have 66511042SErik.Nordmark@Sun.COM * already deleted the ire in question, so there 66611042SErik.Nordmark@Sun.COM * is no need to do that here. 66711042SErik.Nordmark@Sun.COM */ 66811042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 66911042SErik.Nordmark@Sun.COM return (ENOMEM); 67011042SErik.Nordmark@Sun.COM } 6710Sstevel@tonic-gate /* 67211042SErik.Nordmark@Sun.COM * Check if it was a duplicate entry. This handles 67311042SErik.Nordmark@Sun.COM * the case of two racing route adds for the same route 6740Sstevel@tonic-gate */ 67511042SErik.Nordmark@Sun.COM if (nire != ire) { 67611042SErik.Nordmark@Sun.COM ASSERT(nire->ire_identical_ref > 1); 67711042SErik.Nordmark@Sun.COM ire_delete(nire); 67811042SErik.Nordmark@Sun.COM ire_refrele(nire); 6790Sstevel@tonic-gate ipif_refrele(ipif); 68011042SErik.Nordmark@Sun.COM return (EEXIST); 68111042SErik.Nordmark@Sun.COM } 68211042SErik.Nordmark@Sun.COM ire = nire; 68311042SErik.Nordmark@Sun.COM goto save_ire; 6840Sstevel@tonic-gate } 6850Sstevel@tonic-gate 6860Sstevel@tonic-gate /* 6870Sstevel@tonic-gate * Get an interface IRE for the specified gateway. 6880Sstevel@tonic-gate * If we don't have an IRE_IF_NORESOLVER or IRE_IF_RESOLVER for the 6890Sstevel@tonic-gate * gateway, it is currently unreachable and we fail the request 6900Sstevel@tonic-gate * accordingly. 69111042SErik.Nordmark@Sun.COM * If RTA_IFP was specified we look on that particular ill. 6920Sstevel@tonic-gate */ 69311042SErik.Nordmark@Sun.COM if (ill != NULL) 6940Sstevel@tonic-gate match_flags |= MATCH_IRE_ILL; 69511042SErik.Nordmark@Sun.COM 69611042SErik.Nordmark@Sun.COM /* Check whether the gateway is reachable. */ 69711042SErik.Nordmark@Sun.COM type = IRE_INTERFACE; 69811042SErik.Nordmark@Sun.COM if (flags & RTF_INDIRECT) 69911042SErik.Nordmark@Sun.COM type |= IRE_OFFLINK; 70011042SErik.Nordmark@Sun.COM 70111042SErik.Nordmark@Sun.COM gw_ire = ire_ftable_lookup_v6(gw_addr, 0, 0, type, ill, 70211042SErik.Nordmark@Sun.COM ALL_ZONES, NULL, match_flags, 0, ipst, NULL); 70311042SErik.Nordmark@Sun.COM if (gw_ire == NULL) { 70411042SErik.Nordmark@Sun.COM if (ipif != NULL) 70511042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 7060Sstevel@tonic-gate return (ENETUNREACH); 70711042SErik.Nordmark@Sun.COM } 7080Sstevel@tonic-gate 7090Sstevel@tonic-gate /* 7100Sstevel@tonic-gate * We create one of three types of IREs as a result of this request 7110Sstevel@tonic-gate * based on the netmask. A netmask of all ones (which is automatically 7120Sstevel@tonic-gate * assumed when RTF_HOST is set) results in an IRE_HOST being created. 7130Sstevel@tonic-gate * An all zeroes netmask implies a default route so an IRE_DEFAULT is 7140Sstevel@tonic-gate * created. Otherwise, an IRE_PREFIX route is created for the 7150Sstevel@tonic-gate * destination prefix. 7160Sstevel@tonic-gate */ 7170Sstevel@tonic-gate if (IN6_ARE_ADDR_EQUAL(mask, &ipv6_all_ones)) 7180Sstevel@tonic-gate type = IRE_HOST; 7190Sstevel@tonic-gate else if (IN6_IS_ADDR_UNSPECIFIED(mask)) 7200Sstevel@tonic-gate type = IRE_DEFAULT; 7210Sstevel@tonic-gate else 7220Sstevel@tonic-gate type = IRE_PREFIX; 7230Sstevel@tonic-gate 7240Sstevel@tonic-gate /* check for a duplicate entry */ 72511042SErik.Nordmark@Sun.COM ire = ire_ftable_lookup_v6(dst_addr, mask, gw_addr, type, ill, 72611042SErik.Nordmark@Sun.COM ALL_ZONES, NULL, 72711042SErik.Nordmark@Sun.COM match_flags | MATCH_IRE_MASK | MATCH_IRE_GW, 0, ipst, NULL); 7280Sstevel@tonic-gate if (ire != NULL) { 72911042SErik.Nordmark@Sun.COM if (ipif != NULL) 73011042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 7310Sstevel@tonic-gate ire_refrele(gw_ire); 7320Sstevel@tonic-gate ire_refrele(ire); 7330Sstevel@tonic-gate return (EEXIST); 7340Sstevel@tonic-gate } 7350Sstevel@tonic-gate 7361676Sjpk /* Security attribute exists */ 7371676Sjpk if (sp != NULL) { 7381676Sjpk tsol_gcgrp_addr_t ga; 7391676Sjpk 7401676Sjpk /* find or create the gateway credentials group */ 7411676Sjpk ga.ga_af = AF_INET6; 7421676Sjpk ga.ga_addr = *gw_addr; 7431676Sjpk 7441676Sjpk /* we hold reference to it upon success */ 7451676Sjpk gcgrp = gcgrp_lookup(&ga, B_TRUE); 7461676Sjpk if (gcgrp == NULL) { 74711042SErik.Nordmark@Sun.COM if (ipif != NULL) 74811042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 7491676Sjpk ire_refrele(gw_ire); 7501676Sjpk return (ENOMEM); 7511676Sjpk } 7521676Sjpk 7531676Sjpk /* 7541676Sjpk * Create and add the security attribute to the group; a 7551676Sjpk * reference to the group is made upon allocating a new 7561676Sjpk * entry successfully. If it finds an already-existing 7571676Sjpk * entry for the security attribute in the group, it simply 7581676Sjpk * returns it and no new reference is made to the group. 7591676Sjpk */ 7601676Sjpk gc = gc_create(sp, gcgrp, &gcgrp_xtraref); 7611676Sjpk if (gc == NULL) { 7621676Sjpk /* release reference held by gcgrp_lookup */ 7631676Sjpk GCGRP_REFRELE(gcgrp); 76411042SErik.Nordmark@Sun.COM if (ipif != NULL) 76511042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 7661676Sjpk ire_refrele(gw_ire); 7671676Sjpk return (ENOMEM); 7681676Sjpk } 7691676Sjpk } 7701676Sjpk 7710Sstevel@tonic-gate /* Create the IRE. */ 7720Sstevel@tonic-gate ire = ire_create_v6( 7730Sstevel@tonic-gate dst_addr, /* dest address */ 7740Sstevel@tonic-gate mask, /* mask */ 7750Sstevel@tonic-gate gw_addr, /* gateway address */ 7760Sstevel@tonic-gate (ushort_t)type, /* IRE type */ 77711042SErik.Nordmark@Sun.COM ill, 77811042SErik.Nordmark@Sun.COM zoneid, 7790Sstevel@tonic-gate flags, 7801676Sjpk gc, /* security attribute */ 7813448Sdh155122 ipst); 7823448Sdh155122 7831676Sjpk /* 7841676Sjpk * The ire holds a reference to the 'gc' and the 'gc' holds a 7851676Sjpk * reference to the 'gcgrp'. We can now release the extra reference 7861676Sjpk * the 'gcgrp' acquired in the gcgrp_lookup, if it was not used. 7871676Sjpk */ 7881676Sjpk if (gcgrp_xtraref) 7891676Sjpk GCGRP_REFRELE(gcgrp); 7900Sstevel@tonic-gate if (ire == NULL) { 7911676Sjpk if (gc != NULL) 7921676Sjpk GC_REFRELE(gc); 79311042SErik.Nordmark@Sun.COM if (ipif != NULL) 79411042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 7950Sstevel@tonic-gate ire_refrele(gw_ire); 7960Sstevel@tonic-gate return (ENOMEM); 7970Sstevel@tonic-gate } 7980Sstevel@tonic-gate 79911042SErik.Nordmark@Sun.COM /* src address assigned by the caller? */ 80011042SErik.Nordmark@Sun.COM if ((flags & RTF_SETSRC) && !IN6_IS_ADDR_UNSPECIFIED(src_addr)) 80111042SErik.Nordmark@Sun.COM ire->ire_setsrc_addr_v6 = *src_addr; 80211042SErik.Nordmark@Sun.COM 8030Sstevel@tonic-gate /* 8040Sstevel@tonic-gate * POLICY: should we allow an RTF_HOST with address INADDR_ANY? 8050Sstevel@tonic-gate * SUN/OS socket stuff does but do we really want to allow ::0 ? 8060Sstevel@tonic-gate */ 8070Sstevel@tonic-gate 8080Sstevel@tonic-gate /* Add the new IRE. */ 80911042SErik.Nordmark@Sun.COM nire = ire_add(ire); 81011042SErik.Nordmark@Sun.COM if (nire == NULL) { 81111042SErik.Nordmark@Sun.COM /* 81211042SErik.Nordmark@Sun.COM * In the result of failure, ire_add() will have 81311042SErik.Nordmark@Sun.COM * already deleted the ire in question, so there 81411042SErik.Nordmark@Sun.COM * is no need to do that here. 81511042SErik.Nordmark@Sun.COM */ 81611042SErik.Nordmark@Sun.COM if (ipif != NULL) 81711042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 81811042SErik.Nordmark@Sun.COM ire_refrele(gw_ire); 81911042SErik.Nordmark@Sun.COM return (ENOMEM); 82011042SErik.Nordmark@Sun.COM } 8210Sstevel@tonic-gate /* 82211042SErik.Nordmark@Sun.COM * Check if it was a duplicate entry. This handles 82311042SErik.Nordmark@Sun.COM * the case of two racing route adds for the same route 8240Sstevel@tonic-gate */ 82511042SErik.Nordmark@Sun.COM if (nire != ire) { 82611042SErik.Nordmark@Sun.COM ASSERT(nire->ire_identical_ref > 1); 82711042SErik.Nordmark@Sun.COM ire_delete(nire); 82811042SErik.Nordmark@Sun.COM ire_refrele(nire); 82911042SErik.Nordmark@Sun.COM if (ipif != NULL) 83011042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 8310Sstevel@tonic-gate ire_refrele(gw_ire); 83211042SErik.Nordmark@Sun.COM return (EEXIST); 8330Sstevel@tonic-gate } 83411042SErik.Nordmark@Sun.COM ire = nire; 8350Sstevel@tonic-gate 8360Sstevel@tonic-gate if (flags & RTF_MULTIRT) { 8370Sstevel@tonic-gate /* 8380Sstevel@tonic-gate * Invoke the CGTP (multirouting) filtering module 8390Sstevel@tonic-gate * to add the dst address in the filtering database. 8400Sstevel@tonic-gate * Replicated inbound packets coming from that address 8410Sstevel@tonic-gate * will be filtered to discard the duplicates. 8420Sstevel@tonic-gate * It is not necessary to call the CGTP filter hook 8430Sstevel@tonic-gate * when the dst address is a multicast, because an 8440Sstevel@tonic-gate * IP source address cannot be a multicast. 8450Sstevel@tonic-gate */ 8464961Snordmark if (ipst->ips_ip_cgtp_filter_ops != NULL && 8470Sstevel@tonic-gate !IN6_IS_ADDR_MULTICAST(&(ire->ire_addr_v6))) { 8484961Snordmark int res; 84911042SErik.Nordmark@Sun.COM ipif_t *src_ipif; 8504961Snordmark 85111042SErik.Nordmark@Sun.COM /* Find the source address corresponding to gw_ire */ 85211042SErik.Nordmark@Sun.COM src_ipif = ipif_lookup_addr_v6( 85311042SErik.Nordmark@Sun.COM &gw_ire->ire_gateway_addr_v6, NULL, zoneid, ipst); 85411042SErik.Nordmark@Sun.COM if (src_ipif != NULL) { 85511042SErik.Nordmark@Sun.COM res = ipst->ips_ip_cgtp_filter_ops-> 85611042SErik.Nordmark@Sun.COM cfo_add_dest_v6( 85711042SErik.Nordmark@Sun.COM ipst->ips_netstack->netstack_stackid, 85811042SErik.Nordmark@Sun.COM &ire->ire_addr_v6, 85911042SErik.Nordmark@Sun.COM &ire->ire_gateway_addr_v6, 86011042SErik.Nordmark@Sun.COM &ire->ire_setsrc_addr_v6, 86111042SErik.Nordmark@Sun.COM &src_ipif->ipif_v6lcl_addr); 86211042SErik.Nordmark@Sun.COM ipif_refrele(src_ipif); 86311042SErik.Nordmark@Sun.COM } else { 86411042SErik.Nordmark@Sun.COM res = EADDRNOTAVAIL; 86511042SErik.Nordmark@Sun.COM } 8660Sstevel@tonic-gate if (res != 0) { 86711042SErik.Nordmark@Sun.COM if (ipif != NULL) 86811042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 8690Sstevel@tonic-gate ire_refrele(gw_ire); 8700Sstevel@tonic-gate ire_delete(ire); 87111042SErik.Nordmark@Sun.COM ire_refrele(ire); /* Held in ire_add */ 8720Sstevel@tonic-gate return (res); 8730Sstevel@tonic-gate } 8740Sstevel@tonic-gate } 8750Sstevel@tonic-gate } 8760Sstevel@tonic-gate 8770Sstevel@tonic-gate save_ire: 8780Sstevel@tonic-gate if (gw_ire != NULL) { 8790Sstevel@tonic-gate ire_refrele(gw_ire); 88011042SErik.Nordmark@Sun.COM gw_ire = NULL; 8810Sstevel@tonic-gate } 88211042SErik.Nordmark@Sun.COM if (ire->ire_ill != NULL) { 8830Sstevel@tonic-gate /* 8840Sstevel@tonic-gate * Save enough information so that we can recreate the IRE if 88511042SErik.Nordmark@Sun.COM * the ILL goes down and then up. The metrics associated 8860Sstevel@tonic-gate * with the route will be saved as well when rts_setmetrics() is 8870Sstevel@tonic-gate * called after the IRE has been created. In the case where 8880Sstevel@tonic-gate * memory cannot be allocated, none of this information will be 8890Sstevel@tonic-gate * saved. 8900Sstevel@tonic-gate */ 89111042SErik.Nordmark@Sun.COM ill_save_ire(ire->ire_ill, ire); 89211042SErik.Nordmark@Sun.COM } 8930Sstevel@tonic-gate 8940Sstevel@tonic-gate if (ire_arg != NULL) { 8950Sstevel@tonic-gate /* 8960Sstevel@tonic-gate * Store the ire that was successfully added into where ire_arg 8970Sstevel@tonic-gate * points to so that callers don't have to look it up 8980Sstevel@tonic-gate * themselves (but they are responsible for ire_refrele()ing 8990Sstevel@tonic-gate * the ire when they are finished with it). 9000Sstevel@tonic-gate */ 9010Sstevel@tonic-gate *ire_arg = ire; 9020Sstevel@tonic-gate } else { 9030Sstevel@tonic-gate ire_refrele(ire); /* Held in ire_add */ 9040Sstevel@tonic-gate } 90511042SErik.Nordmark@Sun.COM if (ipif != NULL) 9060Sstevel@tonic-gate ipif_refrele(ipif); 9070Sstevel@tonic-gate return (0); 9080Sstevel@tonic-gate } 9090Sstevel@tonic-gate 9100Sstevel@tonic-gate /* 9110Sstevel@tonic-gate * ip_rt_delete_v6 is called to delete an IPv6 route. 91211042SErik.Nordmark@Sun.COM * ill is passed in to associate it with the correct interface. 9130Sstevel@tonic-gate * (for link-local destinations and gateways). 9140Sstevel@tonic-gate */ 9150Sstevel@tonic-gate /* ARGSUSED4 */ 9160Sstevel@tonic-gate int 9170Sstevel@tonic-gate ip_rt_delete_v6(const in6_addr_t *dst_addr, const in6_addr_t *mask, 91811042SErik.Nordmark@Sun.COM const in6_addr_t *gw_addr, uint_t rtm_addrs, int flags, ill_t *ill, 91911042SErik.Nordmark@Sun.COM ip_stack_t *ipst, zoneid_t zoneid) 9200Sstevel@tonic-gate { 9210Sstevel@tonic-gate ire_t *ire = NULL; 9220Sstevel@tonic-gate ipif_t *ipif; 9230Sstevel@tonic-gate uint_t type; 9240Sstevel@tonic-gate uint_t match_flags = MATCH_IRE_TYPE; 9250Sstevel@tonic-gate int err = 0; 9260Sstevel@tonic-gate 9270Sstevel@tonic-gate /* 9280Sstevel@tonic-gate * If this is the case of RTF_HOST being set, then we set the netmask 9290Sstevel@tonic-gate * to all ones. Otherwise, we use the netmask if one was supplied. 9300Sstevel@tonic-gate */ 9310Sstevel@tonic-gate if (flags & RTF_HOST) { 9320Sstevel@tonic-gate mask = &ipv6_all_ones; 9330Sstevel@tonic-gate match_flags |= MATCH_IRE_MASK; 9340Sstevel@tonic-gate } else if (rtm_addrs & RTA_NETMASK) { 9350Sstevel@tonic-gate match_flags |= MATCH_IRE_MASK; 9360Sstevel@tonic-gate } 9370Sstevel@tonic-gate 9380Sstevel@tonic-gate /* 9390Sstevel@tonic-gate * Note that RTF_GATEWAY is never set on a delete, therefore 9400Sstevel@tonic-gate * we check if the gateway address is one of our interfaces first, 9410Sstevel@tonic-gate * and fall back on RTF_GATEWAY routes. 9420Sstevel@tonic-gate * 9430Sstevel@tonic-gate * This makes it possible to delete an original 9440Sstevel@tonic-gate * IRE_IF_NORESOLVER/IRE_IF_RESOLVER - consistent with SunOS 4.1. 94511042SErik.Nordmark@Sun.COM * However, we have RTF_KERNEL set on the ones created by ipif_up 94611042SErik.Nordmark@Sun.COM * and those can not be deleted here. 9470Sstevel@tonic-gate * 94811042SErik.Nordmark@Sun.COM * We use MATCH_IRE_ILL if we know the interface. If the caller 94911042SErik.Nordmark@Sun.COM * specified an interface (from the RTA_IFP sockaddr) we use it, 95011042SErik.Nordmark@Sun.COM * otherwise we use the ill derived from the gateway address. 95111042SErik.Nordmark@Sun.COM * We can always match the gateway address since we record it 95211042SErik.Nordmark@Sun.COM * in ire_gateway_addr. 9530Sstevel@tonic-gate * 9540Sstevel@tonic-gate * For more detail on specifying routes by gateway address and by 9550Sstevel@tonic-gate * interface index, see the comments in ip_rt_add_v6(). 9560Sstevel@tonic-gate */ 95711042SErik.Nordmark@Sun.COM ipif = ipif_lookup_interface_v6(gw_addr, dst_addr, ipst); 9580Sstevel@tonic-gate if (ipif != NULL) { 95911042SErik.Nordmark@Sun.COM ill_t *ill_match; 96011042SErik.Nordmark@Sun.COM 96111042SErik.Nordmark@Sun.COM if (ill != NULL) 96211042SErik.Nordmark@Sun.COM ill_match = ill; 96311042SErik.Nordmark@Sun.COM else 96411042SErik.Nordmark@Sun.COM ill_match = ipif->ipif_ill; 96511042SErik.Nordmark@Sun.COM 96611042SErik.Nordmark@Sun.COM match_flags |= MATCH_IRE_ILL; 96711042SErik.Nordmark@Sun.COM if (ipif->ipif_ire_type == IRE_LOOPBACK) { 96811042SErik.Nordmark@Sun.COM ire = ire_ftable_lookup_v6(dst_addr, 0, 0, IRE_LOOPBACK, 96911042SErik.Nordmark@Sun.COM ill_match, ALL_ZONES, NULL, match_flags, 0, ipst, 97011042SErik.Nordmark@Sun.COM NULL); 97111042SErik.Nordmark@Sun.COM } 97211042SErik.Nordmark@Sun.COM if (ire == NULL) { 97311042SErik.Nordmark@Sun.COM match_flags |= MATCH_IRE_GW; 97411042SErik.Nordmark@Sun.COM ire = ire_ftable_lookup_v6(dst_addr, mask, gw_addr, 97511042SErik.Nordmark@Sun.COM IRE_INTERFACE, ill_match, ALL_ZONES, NULL, 97611042SErik.Nordmark@Sun.COM match_flags, 0, ipst, NULL); 97711042SErik.Nordmark@Sun.COM } 97811042SErik.Nordmark@Sun.COM /* Avoid deleting routes created by kernel from an ipif */ 97911042SErik.Nordmark@Sun.COM if (ire != NULL && (ire->ire_flags & RTF_KERNEL)) { 98011042SErik.Nordmark@Sun.COM ire_refrele(ire); 98111042SErik.Nordmark@Sun.COM ire = NULL; 9820Sstevel@tonic-gate } 9830Sstevel@tonic-gate 98411042SErik.Nordmark@Sun.COM /* Restore in case we didn't find a match */ 98511042SErik.Nordmark@Sun.COM match_flags &= ~(MATCH_IRE_GW|MATCH_IRE_ILL); 9860Sstevel@tonic-gate } 98711042SErik.Nordmark@Sun.COM 9880Sstevel@tonic-gate if (ire == NULL) { 9890Sstevel@tonic-gate /* 9900Sstevel@tonic-gate * At this point, the gateway address is not one of our own 9910Sstevel@tonic-gate * addresses or a matching interface route was not found. We 9920Sstevel@tonic-gate * set the IRE type to lookup based on whether 9930Sstevel@tonic-gate * this is a host route, a default route or just a prefix. 9940Sstevel@tonic-gate * 99511042SErik.Nordmark@Sun.COM * If an ill was passed in, then the lookup is based on an 9960Sstevel@tonic-gate * interface index so MATCH_IRE_ILL is added to match_flags. 9970Sstevel@tonic-gate */ 9980Sstevel@tonic-gate match_flags |= MATCH_IRE_GW; 99911042SErik.Nordmark@Sun.COM if (ill != NULL) 10000Sstevel@tonic-gate match_flags |= MATCH_IRE_ILL; 10010Sstevel@tonic-gate if (IN6_ARE_ADDR_EQUAL(mask, &ipv6_all_ones)) 10020Sstevel@tonic-gate type = IRE_HOST; 10030Sstevel@tonic-gate else if (IN6_IS_ADDR_UNSPECIFIED(mask)) 10040Sstevel@tonic-gate type = IRE_DEFAULT; 10050Sstevel@tonic-gate else 10060Sstevel@tonic-gate type = IRE_PREFIX; 10070Sstevel@tonic-gate ire = ire_ftable_lookup_v6(dst_addr, mask, gw_addr, type, 100811042SErik.Nordmark@Sun.COM ill, ALL_ZONES, NULL, match_flags, 0, ipst, NULL); 10090Sstevel@tonic-gate } 10100Sstevel@tonic-gate 101111042SErik.Nordmark@Sun.COM if (ipif != NULL) { 10120Sstevel@tonic-gate ipif_refrele(ipif); 101311042SErik.Nordmark@Sun.COM ipif = NULL; 10140Sstevel@tonic-gate } 10150Sstevel@tonic-gate if (ire == NULL) 10160Sstevel@tonic-gate return (ESRCH); 10170Sstevel@tonic-gate 10180Sstevel@tonic-gate if (ire->ire_flags & RTF_MULTIRT) { 10190Sstevel@tonic-gate /* 10200Sstevel@tonic-gate * Invoke the CGTP (multirouting) filtering module 10210Sstevel@tonic-gate * to remove the dst address from the filtering database. 10220Sstevel@tonic-gate * Packets coming from that address will no longer be 10230Sstevel@tonic-gate * filtered to remove duplicates. 10240Sstevel@tonic-gate */ 10254961Snordmark if (ipst->ips_ip_cgtp_filter_ops != NULL) { 10264961Snordmark err = ipst->ips_ip_cgtp_filter_ops->cfo_del_dest_v6( 10274961Snordmark ipst->ips_netstack->netstack_stackid, 10280Sstevel@tonic-gate &ire->ire_addr_v6, &ire->ire_gateway_addr_v6); 10290Sstevel@tonic-gate } 10300Sstevel@tonic-gate } 10310Sstevel@tonic-gate 103211042SErik.Nordmark@Sun.COM ill = ire->ire_ill; 103311042SErik.Nordmark@Sun.COM if (ill != NULL) 103411042SErik.Nordmark@Sun.COM ill_remove_saved_ire(ill, ire); 10350Sstevel@tonic-gate ire_delete(ire); 10360Sstevel@tonic-gate ire_refrele(ire); 10370Sstevel@tonic-gate return (err); 10380Sstevel@tonic-gate } 10390Sstevel@tonic-gate 10400Sstevel@tonic-gate /* 104110616SSebastien.Roy@Sun.COM * Derive an interface id from the link layer address. 10420Sstevel@tonic-gate */ 104310616SSebastien.Roy@Sun.COM void 10440Sstevel@tonic-gate ill_setdefaulttoken(ill_t *ill) 10450Sstevel@tonic-gate { 104610773SSebastien.Roy@Sun.COM if (!ill->ill_manual_token) { 104710616SSebastien.Roy@Sun.COM bzero(&ill->ill_token, sizeof (ill->ill_token)); 104810616SSebastien.Roy@Sun.COM MEDIA_V6INTFID(ill->ill_media, ill, &ill->ill_token); 104910616SSebastien.Roy@Sun.COM ill->ill_token_length = IPV6_TOKEN_LEN; 105010616SSebastien.Roy@Sun.COM } 105110616SSebastien.Roy@Sun.COM } 105210616SSebastien.Roy@Sun.COM 105310616SSebastien.Roy@Sun.COM void 105410616SSebastien.Roy@Sun.COM ill_setdesttoken(ill_t *ill) 105510616SSebastien.Roy@Sun.COM { 105610616SSebastien.Roy@Sun.COM bzero(&ill->ill_dest_token, sizeof (ill->ill_dest_token)); 105710616SSebastien.Roy@Sun.COM MEDIA_V6DESTINTFID(ill->ill_media, ill, &ill->ill_dest_token); 10580Sstevel@tonic-gate } 10590Sstevel@tonic-gate 10600Sstevel@tonic-gate /* 10610Sstevel@tonic-gate * Create a link-local address from a token. 10620Sstevel@tonic-gate */ 10630Sstevel@tonic-gate static void 10640Sstevel@tonic-gate ipif_get_linklocal(in6_addr_t *dest, const in6_addr_t *token) 10650Sstevel@tonic-gate { 10660Sstevel@tonic-gate int i; 10670Sstevel@tonic-gate 10680Sstevel@tonic-gate for (i = 0; i < 4; i++) { 10690Sstevel@tonic-gate dest->s6_addr32[i] = 10700Sstevel@tonic-gate token->s6_addr32[i] | ipv6_ll_template.s6_addr32[i]; 10710Sstevel@tonic-gate } 10720Sstevel@tonic-gate } 10730Sstevel@tonic-gate 10740Sstevel@tonic-gate /* 107510616SSebastien.Roy@Sun.COM * Set a default IPv6 address for a 6to4 tunnel interface 2002:<tsrc>::1/16 10760Sstevel@tonic-gate */ 10770Sstevel@tonic-gate static void 107810616SSebastien.Roy@Sun.COM ipif_set6to4addr(ipif_t *ipif) 10790Sstevel@tonic-gate { 108010616SSebastien.Roy@Sun.COM ill_t *ill = ipif->ipif_ill; 108110616SSebastien.Roy@Sun.COM struct in_addr v4phys; 108210616SSebastien.Roy@Sun.COM 108310616SSebastien.Roy@Sun.COM ASSERT(ill->ill_mactype == DL_6TO4); 108410616SSebastien.Roy@Sun.COM ASSERT(ill->ill_phys_addr_length == sizeof (struct in_addr)); 108510616SSebastien.Roy@Sun.COM ASSERT(ipif->ipif_isv6); 108610616SSebastien.Roy@Sun.COM 108710616SSebastien.Roy@Sun.COM if (ipif->ipif_flags & IPIF_UP) 10880Sstevel@tonic-gate return; 108910616SSebastien.Roy@Sun.COM 109010616SSebastien.Roy@Sun.COM (void) ip_plen_to_mask_v6(16, &ipif->ipif_v6net_mask); 109110616SSebastien.Roy@Sun.COM bcopy(ill->ill_phys_addr, &v4phys, sizeof (struct in_addr)); 109210616SSebastien.Roy@Sun.COM IN6_V4ADDR_TO_6TO4(&v4phys, &ipif->ipif_v6lcl_addr); 109310616SSebastien.Roy@Sun.COM V6_MASK_COPY(ipif->ipif_v6lcl_addr, ipif->ipif_v6net_mask, 109410616SSebastien.Roy@Sun.COM ipif->ipif_v6subnet); 10950Sstevel@tonic-gate } 10960Sstevel@tonic-gate 10970Sstevel@tonic-gate /* 10980Sstevel@tonic-gate * Is it not possible to set the link local address? 10990Sstevel@tonic-gate * The address can be set if the token is set, and the token 11000Sstevel@tonic-gate * isn't too long. 11010Sstevel@tonic-gate * Return B_TRUE if the address can't be set, or B_FALSE if it can. 11020Sstevel@tonic-gate */ 11030Sstevel@tonic-gate boolean_t 11040Sstevel@tonic-gate ipif_cant_setlinklocal(ipif_t *ipif) 11050Sstevel@tonic-gate { 11060Sstevel@tonic-gate ill_t *ill = ipif->ipif_ill; 11070Sstevel@tonic-gate 11080Sstevel@tonic-gate if (IN6_IS_ADDR_UNSPECIFIED(&ill->ill_token) || 11090Sstevel@tonic-gate ill->ill_token_length > IPV6_ABITS - IPV6_LL_PREFIXLEN) 11100Sstevel@tonic-gate return (B_TRUE); 11110Sstevel@tonic-gate 11120Sstevel@tonic-gate return (B_FALSE); 11130Sstevel@tonic-gate } 11140Sstevel@tonic-gate 11150Sstevel@tonic-gate /* 11160Sstevel@tonic-gate * Generate a link-local address from the token. 11170Sstevel@tonic-gate */ 111810616SSebastien.Roy@Sun.COM void 11190Sstevel@tonic-gate ipif_setlinklocal(ipif_t *ipif) 11200Sstevel@tonic-gate { 11213706Svi117747 ill_t *ill = ipif->ipif_ill; 11223706Svi117747 in6_addr_t ov6addr; 11230Sstevel@tonic-gate 11240Sstevel@tonic-gate ASSERT(IAM_WRITER_ILL(ill)); 11250Sstevel@tonic-gate 112610616SSebastien.Roy@Sun.COM /* 112710616SSebastien.Roy@Sun.COM * ill_manual_linklocal is set when the link-local address was 112810616SSebastien.Roy@Sun.COM * manually configured. 112910616SSebastien.Roy@Sun.COM */ 113010616SSebastien.Roy@Sun.COM if (ill->ill_manual_linklocal) 113110616SSebastien.Roy@Sun.COM return; 113210616SSebastien.Roy@Sun.COM 113310616SSebastien.Roy@Sun.COM /* 113410616SSebastien.Roy@Sun.COM * IPv6 interfaces over 6to4 tunnels are special. They do not have 113510616SSebastien.Roy@Sun.COM * link-local addresses, but instead have a single automatically 113610616SSebastien.Roy@Sun.COM * generated global address. 113710616SSebastien.Roy@Sun.COM */ 113810616SSebastien.Roy@Sun.COM if (ill->ill_mactype == DL_6TO4) { 113910616SSebastien.Roy@Sun.COM ipif_set6to4addr(ipif); 114010616SSebastien.Roy@Sun.COM return; 114110616SSebastien.Roy@Sun.COM } 114210616SSebastien.Roy@Sun.COM 11430Sstevel@tonic-gate if (ipif_cant_setlinklocal(ipif)) 114410616SSebastien.Roy@Sun.COM return; 11450Sstevel@tonic-gate 11463706Svi117747 ov6addr = ipif->ipif_v6lcl_addr; 11470Sstevel@tonic-gate ipif_get_linklocal(&ipif->ipif_v6lcl_addr, &ill->ill_token); 11483706Svi117747 sctp_update_ipif_addr(ipif, ov6addr); 11490Sstevel@tonic-gate (void) ip_plen_to_mask_v6(IPV6_LL_PREFIXLEN, &ipif->ipif_v6net_mask); 115010616SSebastien.Roy@Sun.COM if (IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6pp_dst_addr)) { 115110616SSebastien.Roy@Sun.COM V6_MASK_COPY(ipif->ipif_v6lcl_addr, ipif->ipif_v6net_mask, 115210616SSebastien.Roy@Sun.COM ipif->ipif_v6subnet); 115310616SSebastien.Roy@Sun.COM } 11540Sstevel@tonic-gate 115511076SCathy.Zhou@Sun.COM ip_rts_newaddrmsg(RTM_CHGADDR, 0, ipif, RTSQ_DEFAULT); 115610616SSebastien.Roy@Sun.COM } 115710616SSebastien.Roy@Sun.COM 115810616SSebastien.Roy@Sun.COM /* 115910616SSebastien.Roy@Sun.COM * Set the destination link-local address for a point-to-point IPv6 116010616SSebastien.Roy@Sun.COM * interface with a destination interface id (IP tunnels are such 116110616SSebastien.Roy@Sun.COM * interfaces). 116210616SSebastien.Roy@Sun.COM */ 116310616SSebastien.Roy@Sun.COM void 116410616SSebastien.Roy@Sun.COM ipif_setdestlinklocal(ipif_t *ipif) 116510616SSebastien.Roy@Sun.COM { 116610616SSebastien.Roy@Sun.COM ill_t *ill = ipif->ipif_ill; 116710616SSebastien.Roy@Sun.COM 116810616SSebastien.Roy@Sun.COM ASSERT(IAM_WRITER_ILL(ill)); 116910616SSebastien.Roy@Sun.COM if (IN6_IS_ADDR_UNSPECIFIED(&ill->ill_dest_token)) 117010616SSebastien.Roy@Sun.COM return; 117111042SErik.Nordmark@Sun.COM /* Skip if we've already set the pp_dst_addr */ 117211042SErik.Nordmark@Sun.COM if (!IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6pp_dst_addr)) 117311042SErik.Nordmark@Sun.COM return; 117411042SErik.Nordmark@Sun.COM 117510616SSebastien.Roy@Sun.COM ipif_get_linklocal(&ipif->ipif_v6pp_dst_addr, &ill->ill_dest_token); 117610616SSebastien.Roy@Sun.COM ipif->ipif_v6subnet = ipif->ipif_v6pp_dst_addr; 11770Sstevel@tonic-gate } 11780Sstevel@tonic-gate 11790Sstevel@tonic-gate /* 11804972Smeem * Get the resolver set up for a new ipif. (Always called as writer.) 11810Sstevel@tonic-gate */ 11820Sstevel@tonic-gate int 11838485SPeter.Memishian@Sun.COM ipif_ndp_up(ipif_t *ipif, boolean_t initial) 11840Sstevel@tonic-gate { 11850Sstevel@tonic-gate ill_t *ill = ipif->ipif_ill; 11860Sstevel@tonic-gate int err = 0; 11870Sstevel@tonic-gate nce_t *nce = NULL; 11888485SPeter.Memishian@Sun.COM boolean_t added_ipif = B_FALSE; 11898485SPeter.Memishian@Sun.COM 119011042SErik.Nordmark@Sun.COM DTRACE_PROBE3(ipif__downup, char *, "ipif_ndp_up", 119111042SErik.Nordmark@Sun.COM ill_t *, ill, ipif_t *, ipif); 11924972Smeem ip1dbg(("ipif_ndp_up(%s:%u)\n", ill->ill_name, ipif->ipif_id)); 11930Sstevel@tonic-gate 119411042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6lcl_addr) || 11952546Scarlsonj (!(ill->ill_net_type & IRE_INTERFACE))) { 11962546Scarlsonj ipif->ipif_addr_ready = 1; 11970Sstevel@tonic-gate return (0); 11982546Scarlsonj } 11990Sstevel@tonic-gate 12000Sstevel@tonic-gate if ((ipif->ipif_flags & (IPIF_UNNUMBERED|IPIF_NOLOCAL)) == 0) { 12010Sstevel@tonic-gate uint16_t flags; 12028485SPeter.Memishian@Sun.COM uint16_t state; 120311042SErik.Nordmark@Sun.COM uchar_t *hw_addr; 12048485SPeter.Memishian@Sun.COM ill_t *bound_ill; 12058485SPeter.Memishian@Sun.COM ipmp_illgrp_t *illg = ill->ill_grp; 120611042SErik.Nordmark@Sun.COM uint_t hw_addr_len; 12070Sstevel@tonic-gate 120811042SErik.Nordmark@Sun.COM flags = NCE_F_MYADDR | NCE_F_NONUD | NCE_F_PUBLISH | 120911042SErik.Nordmark@Sun.COM NCE_F_AUTHORITY; 12100Sstevel@tonic-gate if (ill->ill_flags & ILLF_ROUTER) 12110Sstevel@tonic-gate flags |= NCE_F_ISROUTER; 12120Sstevel@tonic-gate 12130Sstevel@tonic-gate if (ipif->ipif_flags & IPIF_ANYCAST) 12140Sstevel@tonic-gate flags |= NCE_F_ANYCAST; 12150Sstevel@tonic-gate 12168485SPeter.Memishian@Sun.COM if (IS_IPMP(ill)) { 12178485SPeter.Memishian@Sun.COM ASSERT(ill->ill_net_type == IRE_IF_RESOLVER); 12188485SPeter.Memishian@Sun.COM /* 12198485SPeter.Memishian@Sun.COM * If we're here via ipif_up(), then the ipif won't be 12208485SPeter.Memishian@Sun.COM * bound yet -- add it to the group, which will bind 12218485SPeter.Memishian@Sun.COM * it if possible. (We would add it in ipif_up(), but 12228485SPeter.Memishian@Sun.COM * deleting on failure there is gruesome.) If we're 12238485SPeter.Memishian@Sun.COM * here via ipmp_ill_bind_ipif(), then the ipif has 12248485SPeter.Memishian@Sun.COM * already been added to the group and we just need to 12258485SPeter.Memishian@Sun.COM * use the binding. 12268485SPeter.Memishian@Sun.COM */ 12278485SPeter.Memishian@Sun.COM if ((bound_ill = ipmp_ipif_bound_ill(ipif)) == NULL) { 12288485SPeter.Memishian@Sun.COM bound_ill = ipmp_illgrp_add_ipif(illg, ipif); 12298485SPeter.Memishian@Sun.COM if (bound_ill == NULL) { 12308485SPeter.Memishian@Sun.COM /* 12318485SPeter.Memishian@Sun.COM * We couldn't bind the ipif to an ill 12328485SPeter.Memishian@Sun.COM * yet, so we have nothing to publish. 12338485SPeter.Memishian@Sun.COM * Set ipif_addr_ready so that this 12348485SPeter.Memishian@Sun.COM * address can be used locally for now. 12358485SPeter.Memishian@Sun.COM * The routing socket message will be 12368485SPeter.Memishian@Sun.COM * sent from ipif_up_done_v6(). 12378485SPeter.Memishian@Sun.COM */ 12388485SPeter.Memishian@Sun.COM ipif->ipif_addr_ready = 1; 12398485SPeter.Memishian@Sun.COM return (0); 12408485SPeter.Memishian@Sun.COM } 12418485SPeter.Memishian@Sun.COM added_ipif = B_TRUE; 12420Sstevel@tonic-gate } 12438485SPeter.Memishian@Sun.COM hw_addr = bound_ill->ill_nd_lla; 124411042SErik.Nordmark@Sun.COM hw_addr_len = bound_ill->ill_phys_addr_length; 12458485SPeter.Memishian@Sun.COM } else { 12468485SPeter.Memishian@Sun.COM bound_ill = ill; 124711042SErik.Nordmark@Sun.COM if (ill->ill_net_type == IRE_IF_RESOLVER) { 12488485SPeter.Memishian@Sun.COM hw_addr = ill->ill_nd_lla; 124911042SErik.Nordmark@Sun.COM hw_addr_len = ill->ill_phys_addr_length; 125011042SErik.Nordmark@Sun.COM } else { 125111042SErik.Nordmark@Sun.COM hw_addr = NULL; 125211042SErik.Nordmark@Sun.COM hw_addr_len = 0; 125311042SErik.Nordmark@Sun.COM } 12540Sstevel@tonic-gate } 12558485SPeter.Memishian@Sun.COM 12568485SPeter.Memishian@Sun.COM /* 12578485SPeter.Memishian@Sun.COM * If this is an initial bring-up (or the ipif was never 12588485SPeter.Memishian@Sun.COM * completely brought up), do DAD. Otherwise, we're here 12598485SPeter.Memishian@Sun.COM * because IPMP has rebound an address to this ill: send 12608485SPeter.Memishian@Sun.COM * unsolicited advertisements to inform others. 12618485SPeter.Memishian@Sun.COM */ 12628485SPeter.Memishian@Sun.COM if (initial || !ipif->ipif_addr_ready) { 126311042SErik.Nordmark@Sun.COM /* Causes Duplicate Address Detection to run */ 12648485SPeter.Memishian@Sun.COM state = ND_PROBE; 12658485SPeter.Memishian@Sun.COM } else { 12668485SPeter.Memishian@Sun.COM state = ND_REACHABLE; 12678485SPeter.Memishian@Sun.COM flags |= NCE_F_UNSOL_ADV; 12688485SPeter.Memishian@Sun.COM } 126911042SErik.Nordmark@Sun.COM 12709814SSowmini.Varadhan@Sun.COM retry: 127111042SErik.Nordmark@Sun.COM err = nce_lookup_then_add_v6(ill, hw_addr, hw_addr_len, 127211042SErik.Nordmark@Sun.COM &ipif->ipif_v6lcl_addr, flags, state, &nce); 12730Sstevel@tonic-gate switch (err) { 12740Sstevel@tonic-gate case 0: 12750Sstevel@tonic-gate ip1dbg(("ipif_ndp_up: NCE created for %s\n", 12760Sstevel@tonic-gate ill->ill_name)); 12772546Scarlsonj ipif->ipif_addr_ready = 1; 12789287SSowmini.Varadhan@Sun.COM ipif->ipif_added_nce = 1; 12799571SSowmini.Varadhan@Sun.COM nce->nce_ipif_cnt++; 12802546Scarlsonj break; 12812546Scarlsonj case EINPROGRESS: 12822546Scarlsonj ip1dbg(("ipif_ndp_up: running DAD now for %s\n", 12832546Scarlsonj ill->ill_name)); 12849287SSowmini.Varadhan@Sun.COM ipif->ipif_added_nce = 1; 12859571SSowmini.Varadhan@Sun.COM nce->nce_ipif_cnt++; 12860Sstevel@tonic-gate break; 12870Sstevel@tonic-gate case EEXIST: 12880Sstevel@tonic-gate ip1dbg(("ipif_ndp_up: NCE already exists for %s\n", 12890Sstevel@tonic-gate ill->ill_name)); 129011042SErik.Nordmark@Sun.COM if (!NCE_MYADDR(nce->nce_common)) { 129111042SErik.Nordmark@Sun.COM /* 129211042SErik.Nordmark@Sun.COM * A leftover nce from before this address 129311042SErik.Nordmark@Sun.COM * existed 129411042SErik.Nordmark@Sun.COM */ 129511042SErik.Nordmark@Sun.COM ncec_delete(nce->nce_common); 129611042SErik.Nordmark@Sun.COM nce_refrele(nce); 12979814SSowmini.Varadhan@Sun.COM nce = NULL; 12989814SSowmini.Varadhan@Sun.COM goto retry; 12999814SSowmini.Varadhan@Sun.COM } 13009571SSowmini.Varadhan@Sun.COM if ((ipif->ipif_flags & IPIF_POINTOPOINT) == 0) { 130111042SErik.Nordmark@Sun.COM nce_refrele(nce); 130211042SErik.Nordmark@Sun.COM nce = NULL; 130311042SErik.Nordmark@Sun.COM ip1dbg(("ipif_ndp_up: NCE already exists " 130411042SErik.Nordmark@Sun.COM "for %s\n", ill->ill_name)); 13059571SSowmini.Varadhan@Sun.COM goto fail; 13069571SSowmini.Varadhan@Sun.COM } 13079571SSowmini.Varadhan@Sun.COM /* 13089571SSowmini.Varadhan@Sun.COM * Duplicate local addresses are permissible for 13099571SSowmini.Varadhan@Sun.COM * IPIF_POINTOPOINT interfaces which will get marked 13109571SSowmini.Varadhan@Sun.COM * IPIF_UNNUMBERED later in 13119571SSowmini.Varadhan@Sun.COM * ip_addr_availability_check(). 13129571SSowmini.Varadhan@Sun.COM * 13139571SSowmini.Varadhan@Sun.COM * The nce_ipif_cnt field tracks the number of 13149571SSowmini.Varadhan@Sun.COM * ipifs that have nce_addr as their local address. 13159571SSowmini.Varadhan@Sun.COM */ 13169571SSowmini.Varadhan@Sun.COM ipif->ipif_addr_ready = 1; 13179571SSowmini.Varadhan@Sun.COM ipif->ipif_added_nce = 1; 13189571SSowmini.Varadhan@Sun.COM nce->nce_ipif_cnt++; 131911042SErik.Nordmark@Sun.COM err = 0; 13209571SSowmini.Varadhan@Sun.COM break; 13210Sstevel@tonic-gate default: 13228485SPeter.Memishian@Sun.COM ip1dbg(("ipif_ndp_up: NCE creation failed for %s\n", 13230Sstevel@tonic-gate ill->ill_name)); 13248485SPeter.Memishian@Sun.COM goto fail; 13250Sstevel@tonic-gate } 13262546Scarlsonj } else { 13272546Scarlsonj /* No local NCE for this entry */ 13282546Scarlsonj ipif->ipif_addr_ready = 1; 13290Sstevel@tonic-gate } 13300Sstevel@tonic-gate if (nce != NULL) 133111042SErik.Nordmark@Sun.COM nce_refrele(nce); 13320Sstevel@tonic-gate return (0); 13338485SPeter.Memishian@Sun.COM fail: 13348485SPeter.Memishian@Sun.COM if (added_ipif) 13358485SPeter.Memishian@Sun.COM ipmp_illgrp_del_ipif(ill->ill_grp, ipif); 13368485SPeter.Memishian@Sun.COM 13378485SPeter.Memishian@Sun.COM return (err); 13380Sstevel@tonic-gate } 13390Sstevel@tonic-gate 13400Sstevel@tonic-gate /* Remove all cache entries for this logical interface */ 13410Sstevel@tonic-gate void 13420Sstevel@tonic-gate ipif_ndp_down(ipif_t *ipif) 13430Sstevel@tonic-gate { 134411042SErik.Nordmark@Sun.COM ipif_nce_down(ipif); 13450Sstevel@tonic-gate } 13460Sstevel@tonic-gate 13470Sstevel@tonic-gate /* 13480Sstevel@tonic-gate * Return the scope of the given IPv6 address. If the address is an 13490Sstevel@tonic-gate * IPv4 mapped IPv6 address, return the scope of the corresponding 13500Sstevel@tonic-gate * IPv4 address. 13510Sstevel@tonic-gate */ 13520Sstevel@tonic-gate in6addr_scope_t 13530Sstevel@tonic-gate ip_addr_scope_v6(const in6_addr_t *addr) 13540Sstevel@tonic-gate { 13550Sstevel@tonic-gate static in6_addr_t ipv6loopback = IN6ADDR_LOOPBACK_INIT; 13560Sstevel@tonic-gate 13570Sstevel@tonic-gate if (IN6_IS_ADDR_V4MAPPED(addr)) { 13580Sstevel@tonic-gate in_addr_t v4addr_h = ntohl(V4_PART_OF_V6((*addr))); 13590Sstevel@tonic-gate if ((v4addr_h >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET || 13600Sstevel@tonic-gate (v4addr_h & IN_AUTOCONF_MASK) == IN_AUTOCONF_NET) 13610Sstevel@tonic-gate return (IP6_SCOPE_LINKLOCAL); 13620Sstevel@tonic-gate if ((v4addr_h & IN_PRIVATE8_MASK) == IN_PRIVATE8_NET || 13630Sstevel@tonic-gate (v4addr_h & IN_PRIVATE12_MASK) == IN_PRIVATE12_NET || 13640Sstevel@tonic-gate (v4addr_h & IN_PRIVATE16_MASK) == IN_PRIVATE16_NET) 13650Sstevel@tonic-gate return (IP6_SCOPE_SITELOCAL); 13660Sstevel@tonic-gate return (IP6_SCOPE_GLOBAL); 13670Sstevel@tonic-gate } 13680Sstevel@tonic-gate 13690Sstevel@tonic-gate if (IN6_IS_ADDR_MULTICAST(addr)) 13700Sstevel@tonic-gate return (IN6_ADDR_MC_SCOPE(addr)); 13710Sstevel@tonic-gate 13720Sstevel@tonic-gate /* link-local and loopback addresses are of link-local scope */ 13730Sstevel@tonic-gate if (IN6_IS_ADDR_LINKLOCAL(addr) || 13740Sstevel@tonic-gate IN6_ARE_ADDR_EQUAL(addr, &ipv6loopback)) 13750Sstevel@tonic-gate return (IP6_SCOPE_LINKLOCAL); 13760Sstevel@tonic-gate if (IN6_IS_ADDR_SITELOCAL(addr)) 13770Sstevel@tonic-gate return (IP6_SCOPE_SITELOCAL); 13780Sstevel@tonic-gate return (IP6_SCOPE_GLOBAL); 13790Sstevel@tonic-gate } 13800Sstevel@tonic-gate 13810Sstevel@tonic-gate 13820Sstevel@tonic-gate /* 13833431Scarlsonj * Returns the length of the common prefix of a1 and a2, as per 13843431Scarlsonj * CommonPrefixLen() defined in RFC 3484. 13850Sstevel@tonic-gate */ 13863431Scarlsonj static int 13873431Scarlsonj ip_common_prefix_v6(const in6_addr_t *a1, const in6_addr_t *a2) 13880Sstevel@tonic-gate { 13890Sstevel@tonic-gate int i; 13903431Scarlsonj uint32_t a1val, a2val, mask; 13910Sstevel@tonic-gate 13923431Scarlsonj for (i = 0; i < 4; i++) { 13933431Scarlsonj if ((a1val = a1->s6_addr32[i]) != (a2val = a2->s6_addr32[i])) { 13943431Scarlsonj a1val ^= a2val; 13953431Scarlsonj i *= 32; 13963431Scarlsonj mask = 0x80000000u; 13973431Scarlsonj while (!(a1val & mask)) { 13983431Scarlsonj mask >>= 1; 13993431Scarlsonj i++; 14003431Scarlsonj } 14013431Scarlsonj return (i); 14023431Scarlsonj } 14033431Scarlsonj } 14043431Scarlsonj return (IPV6_ABITS); 14050Sstevel@tonic-gate } 14060Sstevel@tonic-gate 14070Sstevel@tonic-gate #define IPIF_VALID_IPV6_SOURCE(ipif) \ 14080Sstevel@tonic-gate (((ipif)->ipif_flags & IPIF_UP) && \ 140911076SCathy.Zhou@Sun.COM !((ipif)->ipif_flags & (IPIF_NOLOCAL|IPIF_ANYCAST)) && \ 141011076SCathy.Zhou@Sun.COM !((ipif)->ipif_ill->ill_flags & ILLF_NOACCEPT)) 14110Sstevel@tonic-gate 14120Sstevel@tonic-gate /* source address candidate */ 14130Sstevel@tonic-gate typedef struct candidate { 14140Sstevel@tonic-gate ipif_t *cand_ipif; 14150Sstevel@tonic-gate /* The properties of this candidate */ 14160Sstevel@tonic-gate boolean_t cand_isdst; 14170Sstevel@tonic-gate boolean_t cand_isdst_set; 14180Sstevel@tonic-gate in6addr_scope_t cand_scope; 14190Sstevel@tonic-gate boolean_t cand_scope_set; 14200Sstevel@tonic-gate boolean_t cand_isdeprecated; 14210Sstevel@tonic-gate boolean_t cand_isdeprecated_set; 14220Sstevel@tonic-gate boolean_t cand_ispreferred; 14230Sstevel@tonic-gate boolean_t cand_ispreferred_set; 14240Sstevel@tonic-gate boolean_t cand_matchedinterface; 14250Sstevel@tonic-gate boolean_t cand_matchedinterface_set; 14260Sstevel@tonic-gate boolean_t cand_matchedlabel; 14270Sstevel@tonic-gate boolean_t cand_matchedlabel_set; 14280Sstevel@tonic-gate boolean_t cand_istmp; 14290Sstevel@tonic-gate boolean_t cand_istmp_set; 14303431Scarlsonj int cand_common_pref; 14313431Scarlsonj boolean_t cand_common_pref_set; 14323431Scarlsonj boolean_t cand_pref_eq; 14333431Scarlsonj boolean_t cand_pref_eq_set; 14343431Scarlsonj int cand_pref_len; 14353431Scarlsonj boolean_t cand_pref_len_set; 14360Sstevel@tonic-gate } cand_t; 14370Sstevel@tonic-gate #define cand_srcaddr cand_ipif->ipif_v6lcl_addr 14383431Scarlsonj #define cand_mask cand_ipif->ipif_v6net_mask 14390Sstevel@tonic-gate #define cand_flags cand_ipif->ipif_flags 14400Sstevel@tonic-gate #define cand_ill cand_ipif->ipif_ill 14411676Sjpk #define cand_zoneid cand_ipif->ipif_zoneid 14420Sstevel@tonic-gate 14430Sstevel@tonic-gate /* information about the destination for source address selection */ 14440Sstevel@tonic-gate typedef struct dstinfo { 14450Sstevel@tonic-gate const in6_addr_t *dst_addr; 14460Sstevel@tonic-gate ill_t *dst_ill; 14472202Srk129064 uint_t dst_restrict_ill; 14480Sstevel@tonic-gate boolean_t dst_prefer_src_tmp; 14490Sstevel@tonic-gate in6addr_scope_t dst_scope; 14500Sstevel@tonic-gate char *dst_label; 14510Sstevel@tonic-gate } dstinfo_t; 14520Sstevel@tonic-gate 14530Sstevel@tonic-gate /* 14540Sstevel@tonic-gate * The following functions are rules used to select a source address in 14550Sstevel@tonic-gate * ipif_select_source_v6(). Each rule compares a current candidate (cc) 14560Sstevel@tonic-gate * against the best candidate (bc). Each rule has three possible outcomes; 14570Sstevel@tonic-gate * the candidate is preferred over the best candidate (CAND_PREFER), the 14580Sstevel@tonic-gate * candidate is not preferred over the best candidate (CAND_AVOID), or the 14590Sstevel@tonic-gate * candidate is of equal value as the best candidate (CAND_TIE). 14600Sstevel@tonic-gate * 14610Sstevel@tonic-gate * These rules are part of a greater "Default Address Selection for IPv6" 14620Sstevel@tonic-gate * sheme, which is standards based work coming out of the IETF ipv6 working 14630Sstevel@tonic-gate * group. The IETF document defines both IPv6 source address selection and 14640Sstevel@tonic-gate * destination address ordering. The rules defined here implement the IPv6 14650Sstevel@tonic-gate * source address selection. Destination address ordering is done by 14660Sstevel@tonic-gate * libnsl, and uses a similar set of rules to implement the sorting. 14673431Scarlsonj * 14683431Scarlsonj * Most of the rules are defined by the RFC and are not typically altered. The 14693431Scarlsonj * last rule, number 8, has language that allows for local preferences. In the 14703431Scarlsonj * scheme below, this means that new Solaris rules should normally go between 14713431Scarlsonj * rule_ifprefix and rule_prefix. 14720Sstevel@tonic-gate */ 14730Sstevel@tonic-gate typedef enum {CAND_AVOID, CAND_TIE, CAND_PREFER} rule_res_t; 14743448Sdh155122 typedef rule_res_t (*rulef_t)(cand_t *, cand_t *, const dstinfo_t *, 14753448Sdh155122 ip_stack_t *); 14760Sstevel@tonic-gate 14770Sstevel@tonic-gate /* Prefer an address if it is equal to the destination address. */ 14783448Sdh155122 /* ARGSUSED3 */ 14790Sstevel@tonic-gate static rule_res_t 14803448Sdh155122 rule_isdst(cand_t *bc, cand_t *cc, const dstinfo_t *dstinfo, ip_stack_t *ipst) 14810Sstevel@tonic-gate { 14820Sstevel@tonic-gate if (!bc->cand_isdst_set) { 14830Sstevel@tonic-gate bc->cand_isdst = 14840Sstevel@tonic-gate IN6_ARE_ADDR_EQUAL(&bc->cand_srcaddr, dstinfo->dst_addr); 14850Sstevel@tonic-gate bc->cand_isdst_set = B_TRUE; 14860Sstevel@tonic-gate } 14870Sstevel@tonic-gate 14880Sstevel@tonic-gate cc->cand_isdst = 14890Sstevel@tonic-gate IN6_ARE_ADDR_EQUAL(&cc->cand_srcaddr, dstinfo->dst_addr); 14900Sstevel@tonic-gate cc->cand_isdst_set = B_TRUE; 14910Sstevel@tonic-gate 14920Sstevel@tonic-gate if (cc->cand_isdst == bc->cand_isdst) 14930Sstevel@tonic-gate return (CAND_TIE); 14940Sstevel@tonic-gate else if (cc->cand_isdst) 14950Sstevel@tonic-gate return (CAND_PREFER); 14960Sstevel@tonic-gate else 14970Sstevel@tonic-gate return (CAND_AVOID); 14980Sstevel@tonic-gate } 14990Sstevel@tonic-gate 15000Sstevel@tonic-gate /* 15010Sstevel@tonic-gate * Prefer addresses that are of closest scope to the destination. Always 15020Sstevel@tonic-gate * prefer addresses that are of greater scope than the destination over 15030Sstevel@tonic-gate * those that are of lesser scope than the destination. 15040Sstevel@tonic-gate */ 15053448Sdh155122 /* ARGSUSED3 */ 15060Sstevel@tonic-gate static rule_res_t 15073448Sdh155122 rule_scope(cand_t *bc, cand_t *cc, const dstinfo_t *dstinfo, ip_stack_t *ipst) 15080Sstevel@tonic-gate { 15090Sstevel@tonic-gate if (!bc->cand_scope_set) { 15100Sstevel@tonic-gate bc->cand_scope = ip_addr_scope_v6(&bc->cand_srcaddr); 15110Sstevel@tonic-gate bc->cand_scope_set = B_TRUE; 15120Sstevel@tonic-gate } 15130Sstevel@tonic-gate 15140Sstevel@tonic-gate cc->cand_scope = ip_addr_scope_v6(&cc->cand_srcaddr); 15150Sstevel@tonic-gate cc->cand_scope_set = B_TRUE; 15160Sstevel@tonic-gate 15170Sstevel@tonic-gate if (cc->cand_scope < bc->cand_scope) { 15180Sstevel@tonic-gate if (cc->cand_scope < dstinfo->dst_scope) 15190Sstevel@tonic-gate return (CAND_AVOID); 15200Sstevel@tonic-gate else 15210Sstevel@tonic-gate return (CAND_PREFER); 15220Sstevel@tonic-gate } else if (bc->cand_scope < cc->cand_scope) { 15230Sstevel@tonic-gate if (bc->cand_scope < dstinfo->dst_scope) 15240Sstevel@tonic-gate return (CAND_PREFER); 15250Sstevel@tonic-gate else 15260Sstevel@tonic-gate return (CAND_AVOID); 15270Sstevel@tonic-gate } else { 15280Sstevel@tonic-gate return (CAND_TIE); 15290Sstevel@tonic-gate } 15300Sstevel@tonic-gate } 15310Sstevel@tonic-gate 15320Sstevel@tonic-gate /* 15330Sstevel@tonic-gate * Prefer non-deprecated source addresses. 15340Sstevel@tonic-gate */ 15350Sstevel@tonic-gate /* ARGSUSED2 */ 15360Sstevel@tonic-gate static rule_res_t 15373448Sdh155122 rule_deprecated(cand_t *bc, cand_t *cc, const dstinfo_t *dstinfo, 15383448Sdh155122 ip_stack_t *ipst) 15390Sstevel@tonic-gate { 15400Sstevel@tonic-gate if (!bc->cand_isdeprecated_set) { 15410Sstevel@tonic-gate bc->cand_isdeprecated = 15420Sstevel@tonic-gate ((bc->cand_flags & IPIF_DEPRECATED) != 0); 15430Sstevel@tonic-gate bc->cand_isdeprecated_set = B_TRUE; 15440Sstevel@tonic-gate } 15450Sstevel@tonic-gate 15460Sstevel@tonic-gate cc->cand_isdeprecated = ((cc->cand_flags & IPIF_DEPRECATED) != 0); 15470Sstevel@tonic-gate cc->cand_isdeprecated_set = B_TRUE; 15480Sstevel@tonic-gate 15490Sstevel@tonic-gate if (bc->cand_isdeprecated == cc->cand_isdeprecated) 15500Sstevel@tonic-gate return (CAND_TIE); 15510Sstevel@tonic-gate else if (cc->cand_isdeprecated) 15520Sstevel@tonic-gate return (CAND_AVOID); 15530Sstevel@tonic-gate else 15540Sstevel@tonic-gate return (CAND_PREFER); 15550Sstevel@tonic-gate } 15560Sstevel@tonic-gate 15570Sstevel@tonic-gate /* 15580Sstevel@tonic-gate * Prefer source addresses that have the IPIF_PREFERRED flag set. This 15590Sstevel@tonic-gate * rule must be before rule_interface because the flag could be set on any 15600Sstevel@tonic-gate * interface, not just the interface being used for outgoing packets (for 15610Sstevel@tonic-gate * example, the IFF_PREFERRED could be set on an address assigned to the 15620Sstevel@tonic-gate * loopback interface). 15630Sstevel@tonic-gate */ 15640Sstevel@tonic-gate /* ARGSUSED2 */ 15650Sstevel@tonic-gate static rule_res_t 15663448Sdh155122 rule_preferred(cand_t *bc, cand_t *cc, const dstinfo_t *dstinfo, 15673448Sdh155122 ip_stack_t *ipst) 15680Sstevel@tonic-gate { 15690Sstevel@tonic-gate if (!bc->cand_ispreferred_set) { 15700Sstevel@tonic-gate bc->cand_ispreferred = ((bc->cand_flags & IPIF_PREFERRED) != 0); 15710Sstevel@tonic-gate bc->cand_ispreferred_set = B_TRUE; 15720Sstevel@tonic-gate } 15730Sstevel@tonic-gate 15740Sstevel@tonic-gate cc->cand_ispreferred = ((cc->cand_flags & IPIF_PREFERRED) != 0); 15750Sstevel@tonic-gate cc->cand_ispreferred_set = B_TRUE; 15760Sstevel@tonic-gate 15770Sstevel@tonic-gate if (bc->cand_ispreferred == cc->cand_ispreferred) 15780Sstevel@tonic-gate return (CAND_TIE); 15790Sstevel@tonic-gate else if (cc->cand_ispreferred) 15800Sstevel@tonic-gate return (CAND_PREFER); 15810Sstevel@tonic-gate else 15820Sstevel@tonic-gate return (CAND_AVOID); 15830Sstevel@tonic-gate } 15840Sstevel@tonic-gate 15850Sstevel@tonic-gate /* 15868485SPeter.Memishian@Sun.COM * Prefer source addresses that are assigned to the outgoing interface. 15870Sstevel@tonic-gate */ 15883448Sdh155122 /* ARGSUSED3 */ 15890Sstevel@tonic-gate static rule_res_t 15903448Sdh155122 rule_interface(cand_t *bc, cand_t *cc, const dstinfo_t *dstinfo, 15913448Sdh155122 ip_stack_t *ipst) 15920Sstevel@tonic-gate { 15930Sstevel@tonic-gate ill_t *dstill = dstinfo->dst_ill; 15940Sstevel@tonic-gate 15950Sstevel@tonic-gate /* 15960Sstevel@tonic-gate * If dstinfo->dst_restrict_ill is set, this rule is unnecessary 15970Sstevel@tonic-gate * since we know all candidates will be on the same link. 15980Sstevel@tonic-gate */ 15990Sstevel@tonic-gate if (dstinfo->dst_restrict_ill) 16000Sstevel@tonic-gate return (CAND_TIE); 16010Sstevel@tonic-gate 16020Sstevel@tonic-gate if (!bc->cand_matchedinterface_set) { 16038485SPeter.Memishian@Sun.COM bc->cand_matchedinterface = bc->cand_ill == dstill; 16040Sstevel@tonic-gate bc->cand_matchedinterface_set = B_TRUE; 16050Sstevel@tonic-gate } 16060Sstevel@tonic-gate 16078485SPeter.Memishian@Sun.COM cc->cand_matchedinterface = cc->cand_ill == dstill; 16080Sstevel@tonic-gate cc->cand_matchedinterface_set = B_TRUE; 16090Sstevel@tonic-gate 16100Sstevel@tonic-gate if (bc->cand_matchedinterface == cc->cand_matchedinterface) 16110Sstevel@tonic-gate return (CAND_TIE); 16120Sstevel@tonic-gate else if (cc->cand_matchedinterface) 16130Sstevel@tonic-gate return (CAND_PREFER); 16140Sstevel@tonic-gate else 16150Sstevel@tonic-gate return (CAND_AVOID); 16160Sstevel@tonic-gate } 16170Sstevel@tonic-gate 16180Sstevel@tonic-gate /* 16190Sstevel@tonic-gate * Prefer source addresses whose label matches the destination's label. 16200Sstevel@tonic-gate */ 16210Sstevel@tonic-gate static rule_res_t 16223448Sdh155122 rule_label(cand_t *bc, cand_t *cc, const dstinfo_t *dstinfo, ip_stack_t *ipst) 16230Sstevel@tonic-gate { 16240Sstevel@tonic-gate char *label; 16250Sstevel@tonic-gate 16260Sstevel@tonic-gate if (!bc->cand_matchedlabel_set) { 16273448Sdh155122 label = ip6_asp_lookup(&bc->cand_srcaddr, NULL, ipst); 16280Sstevel@tonic-gate bc->cand_matchedlabel = 16290Sstevel@tonic-gate ip6_asp_labelcmp(label, dstinfo->dst_label); 16300Sstevel@tonic-gate bc->cand_matchedlabel_set = B_TRUE; 16310Sstevel@tonic-gate } 16320Sstevel@tonic-gate 16333448Sdh155122 label = ip6_asp_lookup(&cc->cand_srcaddr, NULL, ipst); 16340Sstevel@tonic-gate cc->cand_matchedlabel = ip6_asp_labelcmp(label, dstinfo->dst_label); 16350Sstevel@tonic-gate cc->cand_matchedlabel_set = B_TRUE; 16360Sstevel@tonic-gate 16370Sstevel@tonic-gate if (bc->cand_matchedlabel == cc->cand_matchedlabel) 16380Sstevel@tonic-gate return (CAND_TIE); 16390Sstevel@tonic-gate else if (cc->cand_matchedlabel) 16400Sstevel@tonic-gate return (CAND_PREFER); 16410Sstevel@tonic-gate else 16420Sstevel@tonic-gate return (CAND_AVOID); 16430Sstevel@tonic-gate } 16440Sstevel@tonic-gate 16450Sstevel@tonic-gate /* 16460Sstevel@tonic-gate * Prefer public addresses over temporary ones. An application can reverse 16470Sstevel@tonic-gate * the logic of this rule and prefer temporary addresses by using the 16480Sstevel@tonic-gate * IPV6_SRC_PREFERENCES socket option. 16490Sstevel@tonic-gate */ 16503448Sdh155122 /* ARGSUSED3 */ 16510Sstevel@tonic-gate static rule_res_t 16523448Sdh155122 rule_temporary(cand_t *bc, cand_t *cc, const dstinfo_t *dstinfo, 16533448Sdh155122 ip_stack_t *ipst) 16540Sstevel@tonic-gate { 16550Sstevel@tonic-gate if (!bc->cand_istmp_set) { 16560Sstevel@tonic-gate bc->cand_istmp = ((bc->cand_flags & IPIF_TEMPORARY) != 0); 16570Sstevel@tonic-gate bc->cand_istmp_set = B_TRUE; 16580Sstevel@tonic-gate } 16590Sstevel@tonic-gate 16600Sstevel@tonic-gate cc->cand_istmp = ((cc->cand_flags & IPIF_TEMPORARY) != 0); 16610Sstevel@tonic-gate cc->cand_istmp_set = B_TRUE; 16620Sstevel@tonic-gate 16630Sstevel@tonic-gate if (bc->cand_istmp == cc->cand_istmp) 16640Sstevel@tonic-gate return (CAND_TIE); 16650Sstevel@tonic-gate 16660Sstevel@tonic-gate if (dstinfo->dst_prefer_src_tmp && cc->cand_istmp) 16670Sstevel@tonic-gate return (CAND_PREFER); 16680Sstevel@tonic-gate else if (!dstinfo->dst_prefer_src_tmp && !cc->cand_istmp) 16690Sstevel@tonic-gate return (CAND_PREFER); 16700Sstevel@tonic-gate else 16710Sstevel@tonic-gate return (CAND_AVOID); 16720Sstevel@tonic-gate } 16730Sstevel@tonic-gate 16740Sstevel@tonic-gate /* 16753431Scarlsonj * Prefer source addresses with longer matching prefix with the destination 16763431Scarlsonj * under the interface mask. This gets us on the same subnet before applying 16773431Scarlsonj * any Solaris-specific rules. 16780Sstevel@tonic-gate */ 16793448Sdh155122 /* ARGSUSED3 */ 16800Sstevel@tonic-gate static rule_res_t 16813448Sdh155122 rule_ifprefix(cand_t *bc, cand_t *cc, const dstinfo_t *dstinfo, 16823448Sdh155122 ip_stack_t *ipst) 16830Sstevel@tonic-gate { 16843431Scarlsonj if (!bc->cand_pref_eq_set) { 16853431Scarlsonj bc->cand_pref_eq = V6_MASK_EQ_2(bc->cand_srcaddr, 16863431Scarlsonj bc->cand_mask, *dstinfo->dst_addr); 16873431Scarlsonj bc->cand_pref_eq_set = B_TRUE; 16880Sstevel@tonic-gate } 16890Sstevel@tonic-gate 16903431Scarlsonj cc->cand_pref_eq = V6_MASK_EQ_2(cc->cand_srcaddr, cc->cand_mask, 16913431Scarlsonj *dstinfo->dst_addr); 16923431Scarlsonj cc->cand_pref_eq_set = B_TRUE; 16930Sstevel@tonic-gate 16943431Scarlsonj if (bc->cand_pref_eq) { 16953431Scarlsonj if (cc->cand_pref_eq) { 16963431Scarlsonj if (!bc->cand_pref_len_set) { 16973431Scarlsonj bc->cand_pref_len = 16983431Scarlsonj ip_mask_to_plen_v6(&bc->cand_mask); 16993431Scarlsonj bc->cand_pref_len_set = B_TRUE; 17003431Scarlsonj } 17013431Scarlsonj cc->cand_pref_len = ip_mask_to_plen_v6(&cc->cand_mask); 17023431Scarlsonj cc->cand_pref_len_set = B_TRUE; 17033431Scarlsonj if (bc->cand_pref_len == cc->cand_pref_len) 17043431Scarlsonj return (CAND_TIE); 17053431Scarlsonj else if (bc->cand_pref_len > cc->cand_pref_len) 17063431Scarlsonj return (CAND_AVOID); 17073431Scarlsonj else 17083431Scarlsonj return (CAND_PREFER); 17093431Scarlsonj } else { 17103431Scarlsonj return (CAND_AVOID); 17113431Scarlsonj } 17123431Scarlsonj } else { 17133431Scarlsonj if (cc->cand_pref_eq) 17143431Scarlsonj return (CAND_PREFER); 17153431Scarlsonj else 17163431Scarlsonj return (CAND_TIE); 17170Sstevel@tonic-gate } 17180Sstevel@tonic-gate } 17190Sstevel@tonic-gate 17200Sstevel@tonic-gate /* 17211676Sjpk * Prefer to use zone-specific addresses when possible instead of all-zones 17221676Sjpk * addresses. 17231676Sjpk */ 17241676Sjpk /* ARGSUSED2 */ 17251676Sjpk static rule_res_t 17263448Sdh155122 rule_zone_specific(cand_t *bc, cand_t *cc, const dstinfo_t *dstinfo, 17273448Sdh155122 ip_stack_t *ipst) 17281676Sjpk { 17291676Sjpk if ((bc->cand_zoneid == ALL_ZONES) == 17301676Sjpk (cc->cand_zoneid == ALL_ZONES)) 17311676Sjpk return (CAND_TIE); 17321676Sjpk else if (cc->cand_zoneid == ALL_ZONES) 17331676Sjpk return (CAND_AVOID); 17341676Sjpk else 17351676Sjpk return (CAND_PREFER); 17361676Sjpk } 17371676Sjpk 17381676Sjpk /* 17393431Scarlsonj * Prefer to use DHCPv6 (first) and static addresses (second) when possible 17403431Scarlsonj * instead of statelessly autoconfigured addresses. 17413431Scarlsonj * 17423431Scarlsonj * This is done after trying all other preferences (and before the final tie 17433431Scarlsonj * breaker) so that, if all else is equal, we select addresses configured by 17443431Scarlsonj * DHCPv6 over other addresses. We presume that DHCPv6 addresses, unlike 17453431Scarlsonj * stateless autoconfigured addresses, are deliberately configured by an 17463431Scarlsonj * administrator, and thus are correctly set up in DNS and network packet 17473431Scarlsonj * filters. 17483431Scarlsonj */ 17493431Scarlsonj /* ARGSUSED2 */ 17503431Scarlsonj static rule_res_t 17513448Sdh155122 rule_addr_type(cand_t *bc, cand_t *cc, const dstinfo_t *dstinfo, 17523448Sdh155122 ip_stack_t *ipst) 17533431Scarlsonj { 17543431Scarlsonj #define ATYPE(x) \ 17553431Scarlsonj ((x) & IPIF_DHCPRUNNING) ? 1 : ((x) & IPIF_ADDRCONF) ? 3 : 2 17563431Scarlsonj int bcval = ATYPE(bc->cand_flags); 17573431Scarlsonj int ccval = ATYPE(cc->cand_flags); 17583431Scarlsonj #undef ATYPE 17593431Scarlsonj 17603431Scarlsonj if (bcval == ccval) 17613431Scarlsonj return (CAND_TIE); 17623431Scarlsonj else if (ccval < bcval) 17633431Scarlsonj return (CAND_PREFER); 17643431Scarlsonj else 17653431Scarlsonj return (CAND_AVOID); 17663431Scarlsonj } 17673431Scarlsonj 17683431Scarlsonj /* 17693431Scarlsonj * Prefer source addresses with longer matching prefix with the destination. 17703431Scarlsonj * We do the longest matching prefix calculation by doing an xor of both 17713431Scarlsonj * addresses with the destination, and pick the address with the longest string 17723431Scarlsonj * of leading zeros, as per CommonPrefixLen() defined in RFC 3484. 17733431Scarlsonj */ 17743448Sdh155122 /* ARGSUSED3 */ 17753431Scarlsonj static rule_res_t 17763448Sdh155122 rule_prefix(cand_t *bc, cand_t *cc, const dstinfo_t *dstinfo, ip_stack_t *ipst) 17773431Scarlsonj { 17783431Scarlsonj if (!bc->cand_common_pref_set) { 17793431Scarlsonj bc->cand_common_pref = ip_common_prefix_v6(&bc->cand_srcaddr, 17803431Scarlsonj dstinfo->dst_addr); 17813431Scarlsonj bc->cand_common_pref_set = B_TRUE; 17823431Scarlsonj } 17833431Scarlsonj 17843431Scarlsonj cc->cand_common_pref = ip_common_prefix_v6(&cc->cand_srcaddr, 17853431Scarlsonj dstinfo->dst_addr); 17863431Scarlsonj cc->cand_common_pref_set = B_TRUE; 17873431Scarlsonj 17883431Scarlsonj if (bc->cand_common_pref == cc->cand_common_pref) 17893431Scarlsonj return (CAND_TIE); 17903431Scarlsonj else if (bc->cand_common_pref > cc->cand_common_pref) 17913431Scarlsonj return (CAND_AVOID); 17923431Scarlsonj else 17933431Scarlsonj return (CAND_PREFER); 17943431Scarlsonj } 17953431Scarlsonj 17963431Scarlsonj /* 17973431Scarlsonj * Last rule: we must pick something, so just prefer the current best 17983431Scarlsonj * candidate. 17993431Scarlsonj */ 18003431Scarlsonj /* ARGSUSED */ 18013431Scarlsonj static rule_res_t 18023448Sdh155122 rule_must_be_last(cand_t *bc, cand_t *cc, const dstinfo_t *dstinfo, 18033448Sdh155122 ip_stack_t *ipst) 18043431Scarlsonj { 18053431Scarlsonj return (CAND_AVOID); 18063431Scarlsonj } 18073431Scarlsonj 18083431Scarlsonj /* 18090Sstevel@tonic-gate * Determine the best source address given a destination address and a 18100Sstevel@tonic-gate * destination ill. If no suitable source address is found, it returns 18110Sstevel@tonic-gate * NULL. If there is a usable address pointed to by the usesrc 18120Sstevel@tonic-gate * (i.e ill_usesrc_ifindex != 0) then return that first since it is more 18130Sstevel@tonic-gate * fine grained (i.e per interface) 18140Sstevel@tonic-gate * 18150Sstevel@tonic-gate * This implementation is based on the "Default Address Selection for IPv6" 18160Sstevel@tonic-gate * specification produced by the IETF IPv6 working group. It has been 18170Sstevel@tonic-gate * implemented so that the list of addresses is only traversed once (the 18180Sstevel@tonic-gate * specification's algorithm could traverse the list of addresses once for 18190Sstevel@tonic-gate * every rule). 18200Sstevel@tonic-gate * 18218485SPeter.Memishian@Sun.COM * The restrict_ill argument restricts the algorithm to choose a source 18228485SPeter.Memishian@Sun.COM * address that is assigned to the destination ill. This is used when 18238485SPeter.Memishian@Sun.COM * the destination address is a link-local or multicast address, and when 18240Sstevel@tonic-gate * ipv6_strict_dst_multihoming is turned on. 18250Sstevel@tonic-gate * 18260Sstevel@tonic-gate * src_prefs is the caller's set of source address preferences. If source 18270Sstevel@tonic-gate * address selection is being called to determine the source address of a 182811042SErik.Nordmark@Sun.COM * connected socket (from ip_set_destination_v6()), then the preferences are 182911042SErik.Nordmark@Sun.COM * taken from conn_ixa->ixa_src_preferences. These preferences can be set on a 18300Sstevel@tonic-gate * per-socket basis using the IPV6_SRC_PREFERENCES socket option. The only 18310Sstevel@tonic-gate * preference currently implemented is for rfc3041 temporary addresses. 18320Sstevel@tonic-gate */ 18330Sstevel@tonic-gate ipif_t * 18340Sstevel@tonic-gate ipif_select_source_v6(ill_t *dstill, const in6_addr_t *dst, 183511042SErik.Nordmark@Sun.COM boolean_t restrict_ill, uint32_t src_prefs, zoneid_t zoneid, 183611042SErik.Nordmark@Sun.COM boolean_t allow_usesrc, boolean_t *notreadyp) 18370Sstevel@tonic-gate { 18380Sstevel@tonic-gate dstinfo_t dstinfo; 18390Sstevel@tonic-gate char dstr[INET6_ADDRSTRLEN]; 18400Sstevel@tonic-gate char sstr[INET6_ADDRSTRLEN]; 18418485SPeter.Memishian@Sun.COM ipif_t *ipif, *start_ipif, *next_ipif; 18428485SPeter.Memishian@Sun.COM ill_t *ill, *usesrc_ill = NULL, *ipmp_ill = NULL; 18430Sstevel@tonic-gate ill_walk_context_t ctx; 18440Sstevel@tonic-gate cand_t best_c; /* The best candidate */ 18450Sstevel@tonic-gate cand_t curr_c; /* The current candidate */ 18460Sstevel@tonic-gate uint_t index; 18470Sstevel@tonic-gate boolean_t first_candidate = B_TRUE; 18480Sstevel@tonic-gate rule_res_t rule_result; 18491676Sjpk tsol_tpc_t *src_rhtp, *dst_rhtp; 18503448Sdh155122 ip_stack_t *ipst = dstill->ill_ipst; 18510Sstevel@tonic-gate 18520Sstevel@tonic-gate /* 18530Sstevel@tonic-gate * The list of ordering rules. They are applied in the order they 18540Sstevel@tonic-gate * appear in the list. 18550Sstevel@tonic-gate * 18563431Scarlsonj * Solaris doesn't currently support Mobile IPv6, so there's no 18573431Scarlsonj * rule_mipv6 corresponding to rule 4 in the specification. 18580Sstevel@tonic-gate */ 18590Sstevel@tonic-gate rulef_t rules[] = { 18600Sstevel@tonic-gate rule_isdst, 18610Sstevel@tonic-gate rule_scope, 18620Sstevel@tonic-gate rule_deprecated, 18630Sstevel@tonic-gate rule_preferred, 18640Sstevel@tonic-gate rule_interface, 18650Sstevel@tonic-gate rule_label, 18660Sstevel@tonic-gate rule_temporary, 18673431Scarlsonj rule_ifprefix, /* local rules after this */ 18681676Sjpk rule_zone_specific, 18693431Scarlsonj rule_addr_type, 18703431Scarlsonj rule_prefix, /* local rules before this */ 18713431Scarlsonj rule_must_be_last, /* must always be last */ 18720Sstevel@tonic-gate NULL 18730Sstevel@tonic-gate }; 18740Sstevel@tonic-gate 18750Sstevel@tonic-gate ASSERT(dstill->ill_isv6); 18760Sstevel@tonic-gate ASSERT(!IN6_IS_ADDR_V4MAPPED(dst)); 18770Sstevel@tonic-gate 18780Sstevel@tonic-gate /* 18790Sstevel@tonic-gate * Check if there is a usable src address pointed to by the 18800Sstevel@tonic-gate * usesrc ifindex. This has higher precedence since it is 18810Sstevel@tonic-gate * finer grained (i.e per interface) v/s being system wide. 18820Sstevel@tonic-gate */ 188311042SErik.Nordmark@Sun.COM if (dstill->ill_usesrc_ifindex != 0 && allow_usesrc) { 18840Sstevel@tonic-gate if ((usesrc_ill = 18850Sstevel@tonic-gate ill_lookup_on_ifindex(dstill->ill_usesrc_ifindex, B_TRUE, 188611042SErik.Nordmark@Sun.COM ipst)) != NULL) { 18870Sstevel@tonic-gate dstinfo.dst_ill = usesrc_ill; 18880Sstevel@tonic-gate } else { 18890Sstevel@tonic-gate return (NULL); 18900Sstevel@tonic-gate } 18918485SPeter.Memishian@Sun.COM } else if (IS_UNDER_IPMP(dstill)) { 18928485SPeter.Memishian@Sun.COM /* 18938485SPeter.Memishian@Sun.COM * Test addresses should never be used for source address 18948485SPeter.Memishian@Sun.COM * selection, so if we were passed an underlying ill, switch 18958485SPeter.Memishian@Sun.COM * to the IPMP meta-interface. 18968485SPeter.Memishian@Sun.COM */ 18978485SPeter.Memishian@Sun.COM if ((ipmp_ill = ipmp_ill_hold_ipmp_ill(dstill)) != NULL) 18988485SPeter.Memishian@Sun.COM dstinfo.dst_ill = ipmp_ill; 18998485SPeter.Memishian@Sun.COM else 19008485SPeter.Memishian@Sun.COM return (NULL); 19010Sstevel@tonic-gate } else { 19020Sstevel@tonic-gate dstinfo.dst_ill = dstill; 19030Sstevel@tonic-gate } 19040Sstevel@tonic-gate 19051676Sjpk /* 19061676Sjpk * If we're dealing with an unlabeled destination on a labeled system, 19071676Sjpk * make sure that we ignore source addresses that are incompatible with 19081676Sjpk * the destination's default label. That destination's default label 19091676Sjpk * must dominate the minimum label on the source address. 19101676Sjpk * 19111676Sjpk * (Note that this has to do with Trusted Solaris. It's not related to 19121676Sjpk * the labels described by ip6_asp_lookup.) 19131676Sjpk */ 19141676Sjpk dst_rhtp = NULL; 19151676Sjpk if (is_system_labeled()) { 19161676Sjpk dst_rhtp = find_tpc(dst, IPV6_VERSION, B_FALSE); 19171676Sjpk if (dst_rhtp == NULL) 19181676Sjpk return (NULL); 19191676Sjpk if (dst_rhtp->tpc_tp.host_type != UNLABELED) { 19201676Sjpk TPC_RELE(dst_rhtp); 19211676Sjpk dst_rhtp = NULL; 19221676Sjpk } 19231676Sjpk } 19241676Sjpk 19250Sstevel@tonic-gate dstinfo.dst_addr = dst; 19260Sstevel@tonic-gate dstinfo.dst_scope = ip_addr_scope_v6(dst); 19273448Sdh155122 dstinfo.dst_label = ip6_asp_lookup(dst, NULL, ipst); 19280Sstevel@tonic-gate dstinfo.dst_prefer_src_tmp = ((src_prefs & IPV6_PREFER_SRC_TMP) != 0); 19293448Sdh155122 rw_enter(&ipst->ips_ill_g_lock, RW_READER); 19300Sstevel@tonic-gate /* 19310Sstevel@tonic-gate * Section three of the I-D states that for multicast and 19320Sstevel@tonic-gate * link-local destinations, the candidate set must be restricted to 19330Sstevel@tonic-gate * an interface that is on the same link as the outgoing interface. 19340Sstevel@tonic-gate * Also, when ipv6_strict_dst_multihoming is turned on, always 19350Sstevel@tonic-gate * restrict the source address to the destination link as doing 19360Sstevel@tonic-gate * otherwise will almost certainly cause problems. 19370Sstevel@tonic-gate */ 19380Sstevel@tonic-gate if (IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MULTICAST(dst) || 19393448Sdh155122 ipst->ips_ipv6_strict_dst_multihoming || usesrc_ill != NULL) { 19408485SPeter.Memishian@Sun.COM dstinfo.dst_restrict_ill = B_TRUE; 19412202Srk129064 } else { 19420Sstevel@tonic-gate dstinfo.dst_restrict_ill = restrict_ill; 19432202Srk129064 } 19440Sstevel@tonic-gate 19450Sstevel@tonic-gate bzero(&best_c, sizeof (cand_t)); 19460Sstevel@tonic-gate 19470Sstevel@tonic-gate /* 19488485SPeter.Memishian@Sun.COM * Take a pass through the list of IPv6 interfaces to choose the best 19498485SPeter.Memishian@Sun.COM * possible source address. If restrict_ill is set, just use dst_ill. 19500Sstevel@tonic-gate */ 19518485SPeter.Memishian@Sun.COM if (dstinfo.dst_restrict_ill) 19528485SPeter.Memishian@Sun.COM ill = dstinfo.dst_ill; 19538485SPeter.Memishian@Sun.COM else 19543448Sdh155122 ill = ILL_START_WALK_V6(&ctx, ipst); 19558485SPeter.Memishian@Sun.COM 19568485SPeter.Memishian@Sun.COM for (; ill != NULL; ill = ill_next(&ctx, ill)) { 19570Sstevel@tonic-gate ASSERT(ill->ill_isv6); 19580Sstevel@tonic-gate 19592202Srk129064 /* 19608485SPeter.Memishian@Sun.COM * Test addresses should never be used for source address 19618485SPeter.Memishian@Sun.COM * selection, so ignore underlying ills. 19622202Srk129064 */ 19638485SPeter.Memishian@Sun.COM if (IS_UNDER_IPMP(ill)) 19648485SPeter.Memishian@Sun.COM continue; 19658485SPeter.Memishian@Sun.COM 19669658SSowmini.Varadhan@Sun.COM if (ill->ill_ipif == NULL) 19679658SSowmini.Varadhan@Sun.COM continue; 19688485SPeter.Memishian@Sun.COM /* 19698485SPeter.Memishian@Sun.COM * For source address selection, we treat the ipif list as 19708485SPeter.Memishian@Sun.COM * circular and continue until we get back to where we 19718485SPeter.Memishian@Sun.COM * started. This allows IPMP to vary source address selection 19728485SPeter.Memishian@Sun.COM * (which improves inbound load spreading) by caching its last 19738485SPeter.Memishian@Sun.COM * ending point and starting from there. NOTE: we don't have 19748485SPeter.Memishian@Sun.COM * to worry about ill_src_ipif changing ills since that can't 19758485SPeter.Memishian@Sun.COM * happen on the IPMP ill. 19768485SPeter.Memishian@Sun.COM */ 19778485SPeter.Memishian@Sun.COM start_ipif = ill->ill_ipif; 19788485SPeter.Memishian@Sun.COM if (IS_IPMP(ill) && ill->ill_src_ipif != NULL) 19798485SPeter.Memishian@Sun.COM start_ipif = ill->ill_src_ipif; 19808485SPeter.Memishian@Sun.COM 19818485SPeter.Memishian@Sun.COM ipif = start_ipif; 19828485SPeter.Memishian@Sun.COM do { 19838485SPeter.Memishian@Sun.COM if ((next_ipif = ipif->ipif_next) == NULL) 19848485SPeter.Memishian@Sun.COM next_ipif = ill->ill_ipif; 19850Sstevel@tonic-gate 19860Sstevel@tonic-gate if (!IPIF_VALID_IPV6_SOURCE(ipif)) 19870Sstevel@tonic-gate continue; 19880Sstevel@tonic-gate 198911042SErik.Nordmark@Sun.COM if (!ipif->ipif_addr_ready) { 199011042SErik.Nordmark@Sun.COM if (notreadyp != NULL) 199111042SErik.Nordmark@Sun.COM *notreadyp = B_TRUE; 199211042SErik.Nordmark@Sun.COM continue; 199311042SErik.Nordmark@Sun.COM } 199411042SErik.Nordmark@Sun.COM 19951676Sjpk if (zoneid != ALL_ZONES && 19961676Sjpk ipif->ipif_zoneid != zoneid && 19971676Sjpk ipif->ipif_zoneid != ALL_ZONES) 19980Sstevel@tonic-gate continue; 19990Sstevel@tonic-gate 20001676Sjpk /* 20011676Sjpk * Check compatibility of local address for 20021676Sjpk * destination's default label if we're on a labeled 20031676Sjpk * system. Incompatible addresses can't be used at 20041676Sjpk * all and must be skipped over. 20051676Sjpk */ 20061676Sjpk if (dst_rhtp != NULL) { 20071676Sjpk boolean_t incompat; 20081676Sjpk 20091676Sjpk src_rhtp = find_tpc(&ipif->ipif_v6lcl_addr, 20101676Sjpk IPV6_VERSION, B_FALSE); 20111676Sjpk if (src_rhtp == NULL) 20121676Sjpk continue; 20131676Sjpk incompat = 20141676Sjpk src_rhtp->tpc_tp.host_type != SUN_CIPSO || 20151676Sjpk src_rhtp->tpc_tp.tp_doi != 20161676Sjpk dst_rhtp->tpc_tp.tp_doi || 20171676Sjpk (!_blinrange(&dst_rhtp->tpc_tp.tp_def_label, 20181676Sjpk &src_rhtp->tpc_tp.tp_sl_range_cipso) && 20191676Sjpk !blinlset(&dst_rhtp->tpc_tp.tp_def_label, 20201676Sjpk src_rhtp->tpc_tp.tp_sl_set_cipso)); 20211676Sjpk TPC_RELE(src_rhtp); 20221676Sjpk if (incompat) 20231676Sjpk continue; 20241676Sjpk } 20251676Sjpk 20260Sstevel@tonic-gate if (first_candidate) { 20270Sstevel@tonic-gate /* 20280Sstevel@tonic-gate * This is first valid address in the list. 20290Sstevel@tonic-gate * It is automatically the best candidate 20300Sstevel@tonic-gate * so far. 20310Sstevel@tonic-gate */ 20320Sstevel@tonic-gate best_c.cand_ipif = ipif; 20330Sstevel@tonic-gate first_candidate = B_FALSE; 20340Sstevel@tonic-gate continue; 20350Sstevel@tonic-gate } 20360Sstevel@tonic-gate 20370Sstevel@tonic-gate bzero(&curr_c, sizeof (cand_t)); 20380Sstevel@tonic-gate curr_c.cand_ipif = ipif; 20390Sstevel@tonic-gate 20400Sstevel@tonic-gate /* 20410Sstevel@tonic-gate * Compare this current candidate (curr_c) with the 20420Sstevel@tonic-gate * best candidate (best_c) by applying the 20430Sstevel@tonic-gate * comparison rules in order until one breaks the 20440Sstevel@tonic-gate * tie. 20450Sstevel@tonic-gate */ 20460Sstevel@tonic-gate for (index = 0; rules[index] != NULL; index++) { 20470Sstevel@tonic-gate /* Apply a comparison rule. */ 20488485SPeter.Memishian@Sun.COM rule_result = (rules[index])(&best_c, &curr_c, 20498485SPeter.Memishian@Sun.COM &dstinfo, ipst); 20500Sstevel@tonic-gate if (rule_result == CAND_AVOID) { 20510Sstevel@tonic-gate /* 20520Sstevel@tonic-gate * The best candidate is still the 20530Sstevel@tonic-gate * best candidate. Forget about 20540Sstevel@tonic-gate * this current candidate and go on 20550Sstevel@tonic-gate * to the next one. 20560Sstevel@tonic-gate */ 20570Sstevel@tonic-gate break; 20580Sstevel@tonic-gate } else if (rule_result == CAND_PREFER) { 20590Sstevel@tonic-gate /* 20600Sstevel@tonic-gate * This candidate is prefered. It 20610Sstevel@tonic-gate * becomes the best candidate so 20620Sstevel@tonic-gate * far. Go on to the next address. 20630Sstevel@tonic-gate */ 20640Sstevel@tonic-gate best_c = curr_c; 20650Sstevel@tonic-gate break; 20660Sstevel@tonic-gate } 20670Sstevel@tonic-gate /* We have a tie, apply the next rule. */ 20680Sstevel@tonic-gate } 20690Sstevel@tonic-gate 20700Sstevel@tonic-gate /* 20710Sstevel@tonic-gate * The last rule must be a tie breaker rule and 20720Sstevel@tonic-gate * must never produce a tie. At this point, the 20730Sstevel@tonic-gate * candidate should have either been rejected, or 20740Sstevel@tonic-gate * have been prefered as the best candidate so far. 20750Sstevel@tonic-gate */ 20760Sstevel@tonic-gate ASSERT(rule_result != CAND_TIE); 20778485SPeter.Memishian@Sun.COM } while ((ipif = next_ipif) != start_ipif); 20788485SPeter.Memishian@Sun.COM 20798485SPeter.Memishian@Sun.COM /* 20808485SPeter.Memishian@Sun.COM * For IPMP, update the source ipif rotor to the next ipif, 20818485SPeter.Memishian@Sun.COM * provided we can look it up. (We must not use it if it's 20828485SPeter.Memishian@Sun.COM * IPIF_CONDEMNED since we may have grabbed ill_g_lock after 20838485SPeter.Memishian@Sun.COM * ipif_free() checked ill_src_ipif.) 20848485SPeter.Memishian@Sun.COM */ 20858485SPeter.Memishian@Sun.COM if (IS_IPMP(ill) && ipif != NULL) { 20868485SPeter.Memishian@Sun.COM mutex_enter(&ipif->ipif_ill->ill_lock); 20878485SPeter.Memishian@Sun.COM next_ipif = ipif->ipif_next; 208811042SErik.Nordmark@Sun.COM if (next_ipif != NULL && !IPIF_IS_CONDEMNED(next_ipif)) 20898485SPeter.Memishian@Sun.COM ill->ill_src_ipif = next_ipif; 20908485SPeter.Memishian@Sun.COM else 20918485SPeter.Memishian@Sun.COM ill->ill_src_ipif = NULL; 20928485SPeter.Memishian@Sun.COM mutex_exit(&ipif->ipif_ill->ill_lock); 20930Sstevel@tonic-gate } 20940Sstevel@tonic-gate 20950Sstevel@tonic-gate /* 20968485SPeter.Memishian@Sun.COM * Only one ill to consider if dst_restrict_ill is set. 20970Sstevel@tonic-gate */ 20988485SPeter.Memishian@Sun.COM if (dstinfo.dst_restrict_ill) 20998485SPeter.Memishian@Sun.COM break; 21000Sstevel@tonic-gate } 21010Sstevel@tonic-gate 21020Sstevel@tonic-gate ipif = best_c.cand_ipif; 21030Sstevel@tonic-gate ip1dbg(("ipif_select_source_v6(%s, %s) -> %s\n", 21040Sstevel@tonic-gate dstinfo.dst_ill->ill_name, 21050Sstevel@tonic-gate inet_ntop(AF_INET6, dstinfo.dst_addr, dstr, sizeof (dstr)), 21060Sstevel@tonic-gate (ipif == NULL ? "NULL" : 21070Sstevel@tonic-gate inet_ntop(AF_INET6, &ipif->ipif_v6lcl_addr, sstr, sizeof (sstr))))); 21080Sstevel@tonic-gate 21090Sstevel@tonic-gate if (usesrc_ill != NULL) 21100Sstevel@tonic-gate ill_refrele(usesrc_ill); 21110Sstevel@tonic-gate 21128485SPeter.Memishian@Sun.COM if (ipmp_ill != NULL) 21138485SPeter.Memishian@Sun.COM ill_refrele(ipmp_ill); 21148485SPeter.Memishian@Sun.COM 21151676Sjpk if (dst_rhtp != NULL) 21161676Sjpk TPC_RELE(dst_rhtp); 21171676Sjpk 21180Sstevel@tonic-gate if (ipif == NULL) { 21193448Sdh155122 rw_exit(&ipst->ips_ill_g_lock); 21200Sstevel@tonic-gate return (NULL); 21210Sstevel@tonic-gate } 21220Sstevel@tonic-gate 21230Sstevel@tonic-gate mutex_enter(&ipif->ipif_ill->ill_lock); 212411042SErik.Nordmark@Sun.COM if (!IPIF_IS_CONDEMNED(ipif)) { 21250Sstevel@tonic-gate ipif_refhold_locked(ipif); 21260Sstevel@tonic-gate mutex_exit(&ipif->ipif_ill->ill_lock); 21273448Sdh155122 rw_exit(&ipst->ips_ill_g_lock); 21280Sstevel@tonic-gate return (ipif); 21290Sstevel@tonic-gate } 21300Sstevel@tonic-gate mutex_exit(&ipif->ipif_ill->ill_lock); 21313448Sdh155122 rw_exit(&ipst->ips_ill_g_lock); 21320Sstevel@tonic-gate ip1dbg(("ipif_select_source_v6 cannot lookup ipif %p" 21330Sstevel@tonic-gate " returning null \n", (void *)ipif)); 21340Sstevel@tonic-gate 21350Sstevel@tonic-gate return (NULL); 21360Sstevel@tonic-gate } 21370Sstevel@tonic-gate 21380Sstevel@tonic-gate /* 213911042SErik.Nordmark@Sun.COM * Pick a source address based on the destination ill and an optional setsrc 214011042SErik.Nordmark@Sun.COM * address. 214111042SErik.Nordmark@Sun.COM * The result is stored in srcp. If generation is set, then put the source 214211042SErik.Nordmark@Sun.COM * generation number there before we look for the source address (to avoid 214311042SErik.Nordmark@Sun.COM * missing changes in the set of source addresses. 214411042SErik.Nordmark@Sun.COM * If flagsp is set, then us it to pass back ipif_flags. 214511042SErik.Nordmark@Sun.COM * 214611042SErik.Nordmark@Sun.COM * If the caller wants to cache the returned source address and detect when 214711042SErik.Nordmark@Sun.COM * that might be stale, the caller should pass in a generation argument, 214811042SErik.Nordmark@Sun.COM * which the caller can later compare against ips_src_generation 21490Sstevel@tonic-gate * 215011042SErik.Nordmark@Sun.COM * The precedence order for selecting an IPv6 source address is: 215111042SErik.Nordmark@Sun.COM * - RTF_SETSRC on the first ire in the recursive lookup always wins. 215211042SErik.Nordmark@Sun.COM * - If usrsrc is set, swap the ill to be the usesrc one. 215311042SErik.Nordmark@Sun.COM * - If IPMP is used on the ill, select a random address from the most 215411042SErik.Nordmark@Sun.COM * preferred ones below: 215511042SErik.Nordmark@Sun.COM * That is followed by the long list of IPv6 source address selection rules 215611042SErik.Nordmark@Sun.COM * starting with rule_isdst(), rule_scope(), etc. 215711042SErik.Nordmark@Sun.COM * 215811042SErik.Nordmark@Sun.COM * We have lower preference for ALL_ZONES IP addresses, 215911042SErik.Nordmark@Sun.COM * as they pose problems with unlabeled destinations. 216011042SErik.Nordmark@Sun.COM * 216111042SErik.Nordmark@Sun.COM * Note that when multiple IP addresses match e.g., with rule_scope() we pick 216211042SErik.Nordmark@Sun.COM * the first one if IPMP is not in use. With IPMP we randomize. 21630Sstevel@tonic-gate */ 216411042SErik.Nordmark@Sun.COM int 216511042SErik.Nordmark@Sun.COM ip_select_source_v6(ill_t *ill, const in6_addr_t *setsrc, const in6_addr_t *dst, 216611042SErik.Nordmark@Sun.COM zoneid_t zoneid, ip_stack_t *ipst, uint_t restrict_ill, uint32_t src_prefs, 216711042SErik.Nordmark@Sun.COM in6_addr_t *srcp, uint32_t *generation, uint64_t *flagsp) 21680Sstevel@tonic-gate { 216911042SErik.Nordmark@Sun.COM ipif_t *ipif; 217011042SErik.Nordmark@Sun.COM boolean_t notready = B_FALSE; /* Set if !ipif_addr_ready found */ 21710Sstevel@tonic-gate 217211042SErik.Nordmark@Sun.COM if (flagsp != NULL) 217311042SErik.Nordmark@Sun.COM *flagsp = 0; 21740Sstevel@tonic-gate 21750Sstevel@tonic-gate /* 217611042SErik.Nordmark@Sun.COM * Need to grab the generation number before we check to 217711042SErik.Nordmark@Sun.COM * avoid a race with a change to the set of local addresses. 217811042SErik.Nordmark@Sun.COM * No lock needed since the thread which updates the set of local 217911042SErik.Nordmark@Sun.COM * addresses use ipif/ill locks and exit those (hence a store memory 218011042SErik.Nordmark@Sun.COM * barrier) before doing the atomic increase of ips_src_generation. 21810Sstevel@tonic-gate */ 218211042SErik.Nordmark@Sun.COM if (generation != NULL) { 218311042SErik.Nordmark@Sun.COM *generation = ipst->ips_src_generation; 21840Sstevel@tonic-gate } 21850Sstevel@tonic-gate 218611042SErik.Nordmark@Sun.COM /* Was RTF_SETSRC set on the first IRE in the recursive lookup? */ 218711042SErik.Nordmark@Sun.COM if (setsrc != NULL && !IN6_IS_ADDR_UNSPECIFIED(setsrc)) { 218811042SErik.Nordmark@Sun.COM *srcp = *setsrc; 218911042SErik.Nordmark@Sun.COM return (0); 21900Sstevel@tonic-gate } 21910Sstevel@tonic-gate 219211042SErik.Nordmark@Sun.COM ipif = ipif_select_source_v6(ill, dst, restrict_ill, src_prefs, zoneid, 219311042SErik.Nordmark@Sun.COM B_TRUE, ¬ready); 219411042SErik.Nordmark@Sun.COM if (ipif == NULL) { 219511042SErik.Nordmark@Sun.COM if (notready) 219611042SErik.Nordmark@Sun.COM return (ENETDOWN); 219711042SErik.Nordmark@Sun.COM else 219811042SErik.Nordmark@Sun.COM return (EADDRNOTAVAIL); 21990Sstevel@tonic-gate } 220011042SErik.Nordmark@Sun.COM *srcp = ipif->ipif_v6lcl_addr; 220111042SErik.Nordmark@Sun.COM if (flagsp != NULL) 220211042SErik.Nordmark@Sun.COM *flagsp = ipif->ipif_flags; 220311042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 220411042SErik.Nordmark@Sun.COM return (0); 22050Sstevel@tonic-gate } 22060Sstevel@tonic-gate 22070Sstevel@tonic-gate /* 22080Sstevel@tonic-gate * Perform an attach and bind to get phys addr plus info_req for 22090Sstevel@tonic-gate * the physical device. 22100Sstevel@tonic-gate * q and mp represents an ioctl which will be queued waiting for 22110Sstevel@tonic-gate * completion of the DLPI message exchange. 221211042SErik.Nordmark@Sun.COM * MUST be called on an ill queue. 22130Sstevel@tonic-gate * 221411042SErik.Nordmark@Sun.COM * Returns EINPROGRESS when mp has been consumed by queueing it. 221511042SErik.Nordmark@Sun.COM * The ioctl will complete in ip_rput. 22160Sstevel@tonic-gate */ 22170Sstevel@tonic-gate int 22180Sstevel@tonic-gate ill_dl_phys(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q) 22190Sstevel@tonic-gate { 22200Sstevel@tonic-gate mblk_t *v6token_mp = NULL; 22210Sstevel@tonic-gate mblk_t *v6lla_mp = NULL; 222210616SSebastien.Roy@Sun.COM mblk_t *dest_mp = NULL; 22230Sstevel@tonic-gate mblk_t *phys_mp = NULL; 22240Sstevel@tonic-gate mblk_t *info_mp = NULL; 22250Sstevel@tonic-gate mblk_t *attach_mp = NULL; 22260Sstevel@tonic-gate mblk_t *bind_mp = NULL; 22270Sstevel@tonic-gate mblk_t *unbind_mp = NULL; 22280Sstevel@tonic-gate mblk_t *notify_mp = NULL; 222911076SCathy.Zhou@Sun.COM mblk_t *capab_mp = NULL; 22300Sstevel@tonic-gate 22310Sstevel@tonic-gate ip1dbg(("ill_dl_phys(%s:%u)\n", ill->ill_name, ipif->ipif_id)); 22320Sstevel@tonic-gate ASSERT(ill->ill_dlpi_style_set); 22330Sstevel@tonic-gate ASSERT(WR(q)->q_next != NULL); 22340Sstevel@tonic-gate 22350Sstevel@tonic-gate if (ill->ill_isv6) { 22360Sstevel@tonic-gate v6token_mp = ip_dlpi_alloc(sizeof (dl_phys_addr_req_t) + 22370Sstevel@tonic-gate sizeof (t_scalar_t), DL_PHYS_ADDR_REQ); 22380Sstevel@tonic-gate if (v6token_mp == NULL) 22390Sstevel@tonic-gate goto bad; 22400Sstevel@tonic-gate ((dl_phys_addr_req_t *)v6token_mp->b_rptr)->dl_addr_type = 22410Sstevel@tonic-gate DL_IPV6_TOKEN; 22420Sstevel@tonic-gate 22430Sstevel@tonic-gate v6lla_mp = ip_dlpi_alloc(sizeof (dl_phys_addr_req_t) + 22440Sstevel@tonic-gate sizeof (t_scalar_t), DL_PHYS_ADDR_REQ); 22450Sstevel@tonic-gate if (v6lla_mp == NULL) 22460Sstevel@tonic-gate goto bad; 22470Sstevel@tonic-gate ((dl_phys_addr_req_t *)v6lla_mp->b_rptr)->dl_addr_type = 22480Sstevel@tonic-gate DL_IPV6_LINK_LAYER_ADDR; 22490Sstevel@tonic-gate } 22500Sstevel@tonic-gate 225110616SSebastien.Roy@Sun.COM if (ill->ill_mactype == DL_IPV4 || ill->ill_mactype == DL_IPV6) { 225210616SSebastien.Roy@Sun.COM dest_mp = ip_dlpi_alloc(sizeof (dl_phys_addr_req_t) + 225310616SSebastien.Roy@Sun.COM sizeof (t_scalar_t), DL_PHYS_ADDR_REQ); 225410616SSebastien.Roy@Sun.COM if (dest_mp == NULL) 225510616SSebastien.Roy@Sun.COM goto bad; 225610616SSebastien.Roy@Sun.COM ((dl_phys_addr_req_t *)dest_mp->b_rptr)->dl_addr_type = 225710616SSebastien.Roy@Sun.COM DL_CURR_DEST_ADDR; 225810616SSebastien.Roy@Sun.COM } 225910616SSebastien.Roy@Sun.COM 22600Sstevel@tonic-gate /* 22610Sstevel@tonic-gate * Allocate a DL_NOTIFY_REQ and set the notifications we want. 22620Sstevel@tonic-gate */ 22630Sstevel@tonic-gate notify_mp = ip_dlpi_alloc(sizeof (dl_notify_req_t) + sizeof (long), 22640Sstevel@tonic-gate DL_NOTIFY_REQ); 22650Sstevel@tonic-gate if (notify_mp == NULL) 22660Sstevel@tonic-gate goto bad; 22670Sstevel@tonic-gate ((dl_notify_req_t *)notify_mp->b_rptr)->dl_notifications = 22680Sstevel@tonic-gate (DL_NOTE_PHYS_ADDR | DL_NOTE_SDU_SIZE | DL_NOTE_FASTPATH_FLUSH | 22699073SCathy.Zhou@Sun.COM DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN | DL_NOTE_CAPAB_RENEG | 22709743SGirish.Moodalbail@Sun.COM DL_NOTE_PROMISC_ON_PHYS | DL_NOTE_PROMISC_OFF_PHYS | 22719073SCathy.Zhou@Sun.COM DL_NOTE_REPLUMB); 22720Sstevel@tonic-gate 22730Sstevel@tonic-gate phys_mp = ip_dlpi_alloc(sizeof (dl_phys_addr_req_t) + 22740Sstevel@tonic-gate sizeof (t_scalar_t), DL_PHYS_ADDR_REQ); 22750Sstevel@tonic-gate if (phys_mp == NULL) 22760Sstevel@tonic-gate goto bad; 22770Sstevel@tonic-gate ((dl_phys_addr_req_t *)phys_mp->b_rptr)->dl_addr_type = 22780Sstevel@tonic-gate DL_CURR_PHYS_ADDR; 22790Sstevel@tonic-gate 22800Sstevel@tonic-gate info_mp = ip_dlpi_alloc( 22810Sstevel@tonic-gate sizeof (dl_info_req_t) + sizeof (dl_info_ack_t), 22820Sstevel@tonic-gate DL_INFO_REQ); 22830Sstevel@tonic-gate if (info_mp == NULL) 22840Sstevel@tonic-gate goto bad; 22850Sstevel@tonic-gate 228611076SCathy.Zhou@Sun.COM ASSERT(ill->ill_dlpi_capab_state == IDCS_UNKNOWN); 228711076SCathy.Zhou@Sun.COM capab_mp = ip_dlpi_alloc(sizeof (dl_capability_req_t), 228811076SCathy.Zhou@Sun.COM DL_CAPABILITY_REQ); 228911076SCathy.Zhou@Sun.COM if (capab_mp == NULL) 229011076SCathy.Zhou@Sun.COM goto bad; 229111076SCathy.Zhou@Sun.COM 22920Sstevel@tonic-gate bind_mp = ip_dlpi_alloc(sizeof (dl_bind_req_t) + sizeof (long), 22930Sstevel@tonic-gate DL_BIND_REQ); 22940Sstevel@tonic-gate if (bind_mp == NULL) 22950Sstevel@tonic-gate goto bad; 22960Sstevel@tonic-gate ((dl_bind_req_t *)bind_mp->b_rptr)->dl_sap = ill->ill_sap; 22970Sstevel@tonic-gate ((dl_bind_req_t *)bind_mp->b_rptr)->dl_service_mode = DL_CLDLS; 22980Sstevel@tonic-gate 22990Sstevel@tonic-gate unbind_mp = ip_dlpi_alloc(sizeof (dl_unbind_req_t), DL_UNBIND_REQ); 23000Sstevel@tonic-gate if (unbind_mp == NULL) 23010Sstevel@tonic-gate goto bad; 23020Sstevel@tonic-gate 23034360Smeem /* If we need to attach, pre-alloc and initialize the mblk */ 23040Sstevel@tonic-gate if (ill->ill_needs_attach) { 23050Sstevel@tonic-gate attach_mp = ip_dlpi_alloc(sizeof (dl_attach_req_t), 23060Sstevel@tonic-gate DL_ATTACH_REQ); 23070Sstevel@tonic-gate if (attach_mp == NULL) 23080Sstevel@tonic-gate goto bad; 23090Sstevel@tonic-gate ((dl_attach_req_t *)attach_mp->b_rptr)->dl_ppa = ill->ill_ppa; 23100Sstevel@tonic-gate } 23110Sstevel@tonic-gate 23120Sstevel@tonic-gate /* 23130Sstevel@tonic-gate * Here we are going to delay the ioctl ack until after 23140Sstevel@tonic-gate * ACKs from DL_PHYS_ADDR_REQ. So need to save the 23150Sstevel@tonic-gate * original ioctl message before sending the requests 23160Sstevel@tonic-gate */ 23170Sstevel@tonic-gate mutex_enter(&ill->ill_lock); 23180Sstevel@tonic-gate /* ipsq_pending_mp_add won't fail since we pass in a NULL connp */ 23190Sstevel@tonic-gate (void) ipsq_pending_mp_add(NULL, ipif, ill->ill_wq, mp, 0); 23200Sstevel@tonic-gate /* 23210Sstevel@tonic-gate * Set ill_phys_addr_pend to zero. It will be set to the addr_type of 23220Sstevel@tonic-gate * the DL_PHYS_ADDR_REQ in ill_dlpi_send() and ill_dlpi_done(). It will 23230Sstevel@tonic-gate * be used to track which DL_PHYS_ADDR_REQ is being ACK'd/NAK'd. 23240Sstevel@tonic-gate */ 23250Sstevel@tonic-gate ill->ill_phys_addr_pend = 0; 23260Sstevel@tonic-gate mutex_exit(&ill->ill_lock); 23270Sstevel@tonic-gate 23280Sstevel@tonic-gate if (attach_mp != NULL) { 23290Sstevel@tonic-gate ip1dbg(("ill_dl_phys: attach\n")); 23300Sstevel@tonic-gate ill_dlpi_send(ill, attach_mp); 23310Sstevel@tonic-gate } 23320Sstevel@tonic-gate ill_dlpi_send(ill, bind_mp); 23330Sstevel@tonic-gate ill_dlpi_send(ill, info_mp); 233411076SCathy.Zhou@Sun.COM 233511076SCathy.Zhou@Sun.COM /* 233611076SCathy.Zhou@Sun.COM * Send the capability request to get the VRRP capability information. 233711076SCathy.Zhou@Sun.COM */ 233811076SCathy.Zhou@Sun.COM ill_capability_send(ill, capab_mp); 233911076SCathy.Zhou@Sun.COM 234010616SSebastien.Roy@Sun.COM if (v6token_mp != NULL) 23410Sstevel@tonic-gate ill_dlpi_send(ill, v6token_mp); 234210616SSebastien.Roy@Sun.COM if (v6lla_mp != NULL) 23430Sstevel@tonic-gate ill_dlpi_send(ill, v6lla_mp); 234410616SSebastien.Roy@Sun.COM if (dest_mp != NULL) 234510616SSebastien.Roy@Sun.COM ill_dlpi_send(ill, dest_mp); 23460Sstevel@tonic-gate ill_dlpi_send(ill, phys_mp); 23470Sstevel@tonic-gate ill_dlpi_send(ill, notify_mp); 23480Sstevel@tonic-gate ill_dlpi_send(ill, unbind_mp); 23490Sstevel@tonic-gate 23500Sstevel@tonic-gate /* 23510Sstevel@tonic-gate * This operation will complete in ip_rput_dlpi_writer with either 23520Sstevel@tonic-gate * a DL_PHYS_ADDR_ACK or DL_ERROR_ACK. 23530Sstevel@tonic-gate */ 23540Sstevel@tonic-gate return (EINPROGRESS); 23550Sstevel@tonic-gate bad: 23564360Smeem freemsg(v6token_mp); 23574360Smeem freemsg(v6lla_mp); 235810616SSebastien.Roy@Sun.COM freemsg(dest_mp); 23594360Smeem freemsg(phys_mp); 23604360Smeem freemsg(info_mp); 23614360Smeem freemsg(attach_mp); 23624360Smeem freemsg(bind_mp); 236311076SCathy.Zhou@Sun.COM freemsg(capab_mp); 23644360Smeem freemsg(unbind_mp); 23654360Smeem freemsg(notify_mp); 23660Sstevel@tonic-gate return (ENOMEM); 23670Sstevel@tonic-gate } 23680Sstevel@tonic-gate 236911042SErik.Nordmark@Sun.COM /* Add room for tcp+ip headers */ 23700Sstevel@tonic-gate uint_t ip_loopback_mtu_v6plus = IP_LOOPBACK_MTU + IPV6_HDR_LEN + 20; 23710Sstevel@tonic-gate 23720Sstevel@tonic-gate /* 23730Sstevel@tonic-gate * DLPI is up. 23740Sstevel@tonic-gate * Create all the IREs associated with an interface bring up multicast. 23750Sstevel@tonic-gate * Set the interface flag and finish other initialization 23760Sstevel@tonic-gate * that potentially had to be differed to after DL_BIND_ACK. 23770Sstevel@tonic-gate */ 23780Sstevel@tonic-gate int 23790Sstevel@tonic-gate ipif_up_done_v6(ipif_t *ipif) 23800Sstevel@tonic-gate { 23810Sstevel@tonic-gate ill_t *ill = ipif->ipif_ill; 23820Sstevel@tonic-gate int err; 23830Sstevel@tonic-gate boolean_t loopback = B_FALSE; 23840Sstevel@tonic-gate 23850Sstevel@tonic-gate ip1dbg(("ipif_up_done_v6(%s:%u)\n", 23864459Skcpoon ipif->ipif_ill->ill_name, ipif->ipif_id)); 238711042SErik.Nordmark@Sun.COM DTRACE_PROBE3(ipif__downup, char *, "ipif_up_done_v6", 238811042SErik.Nordmark@Sun.COM ill_t *, ill, ipif_t *, ipif); 23890Sstevel@tonic-gate 23900Sstevel@tonic-gate /* Check if this is a loopback interface */ 23910Sstevel@tonic-gate if (ipif->ipif_ill->ill_wq == NULL) 23920Sstevel@tonic-gate loopback = B_TRUE; 23930Sstevel@tonic-gate 23940Sstevel@tonic-gate ASSERT(ipif->ipif_isv6); 23950Sstevel@tonic-gate ASSERT(!MUTEX_HELD(&ipif->ipif_ill->ill_lock)); 23960Sstevel@tonic-gate 239711042SErik.Nordmark@Sun.COM if (IS_LOOPBACK(ill) || ill->ill_net_type == IRE_IF_NORESOLVER) { 239811042SErik.Nordmark@Sun.COM nce_t *loop_nce = NULL; 239911042SErik.Nordmark@Sun.COM uint16_t flags = (NCE_F_MYADDR | NCE_F_NONUD | NCE_F_AUTHORITY); 24000Sstevel@tonic-gate 24010Sstevel@tonic-gate /* 24020Sstevel@tonic-gate * lo0:1 and subsequent ipifs were marked IRE_LOCAL in 24030Sstevel@tonic-gate * ipif_lookup_on_name(), but in the case of zones we can have 24040Sstevel@tonic-gate * several loopback addresses on lo0. So all the interfaces with 24050Sstevel@tonic-gate * loopback addresses need to be marked IRE_LOOPBACK. 24060Sstevel@tonic-gate */ 24070Sstevel@tonic-gate if (IN6_ARE_ADDR_EQUAL(&ipif->ipif_v6lcl_addr, &ipv6_loopback)) 24080Sstevel@tonic-gate ipif->ipif_ire_type = IRE_LOOPBACK; 24090Sstevel@tonic-gate else 24100Sstevel@tonic-gate ipif->ipif_ire_type = IRE_LOCAL; 241111042SErik.Nordmark@Sun.COM if (ill->ill_net_type != IRE_LOOPBACK) 241211042SErik.Nordmark@Sun.COM flags |= NCE_F_PUBLISH; 241311042SErik.Nordmark@Sun.COM err = nce_lookup_then_add_v6(ill, NULL, 241411042SErik.Nordmark@Sun.COM ill->ill_phys_addr_length, 241511042SErik.Nordmark@Sun.COM &ipif->ipif_v6lcl_addr, flags, ND_REACHABLE, &loop_nce); 241611042SErik.Nordmark@Sun.COM 241711042SErik.Nordmark@Sun.COM /* A shared-IP zone sees EEXIST for lo0:N */ 241811042SErik.Nordmark@Sun.COM if (err == 0 || err == EEXIST) { 241911042SErik.Nordmark@Sun.COM ipif->ipif_added_nce = 1; 242011042SErik.Nordmark@Sun.COM loop_nce->nce_ipif_cnt++; 242111042SErik.Nordmark@Sun.COM nce_refrele(loop_nce); 242211042SErik.Nordmark@Sun.COM err = 0; 242311042SErik.Nordmark@Sun.COM } else { 242411042SErik.Nordmark@Sun.COM ASSERT(loop_nce == NULL); 242511042SErik.Nordmark@Sun.COM return (err); 242611042SErik.Nordmark@Sun.COM } 242711042SErik.Nordmark@Sun.COM } 242811042SErik.Nordmark@Sun.COM 242911042SErik.Nordmark@Sun.COM err = ipif_add_ires_v6(ipif, loopback); 243011042SErik.Nordmark@Sun.COM if (err != 0) { 243111042SErik.Nordmark@Sun.COM /* 243211042SErik.Nordmark@Sun.COM * See comments about return value from 243311042SErik.Nordmark@Sun.COM * ipif_addr_availability_check() in ipif_add_ires_v6(). 243411042SErik.Nordmark@Sun.COM */ 243511042SErik.Nordmark@Sun.COM if (err != EADDRINUSE) { 243611042SErik.Nordmark@Sun.COM ipif_ndp_down(ipif); 243711042SErik.Nordmark@Sun.COM } else { 243811042SErik.Nordmark@Sun.COM /* 243911042SErik.Nordmark@Sun.COM * Make IPMP aware of the deleted ipif so that 244011042SErik.Nordmark@Sun.COM * the needed ipmp cleanup (e.g., of ipif_bound_ill) 244111042SErik.Nordmark@Sun.COM * can be completed. Note that we do not want to 244211042SErik.Nordmark@Sun.COM * destroy the nce that was created on the ipmp_ill 244311042SErik.Nordmark@Sun.COM * for the active copy of the duplicate address in 244411042SErik.Nordmark@Sun.COM * use. 244511042SErik.Nordmark@Sun.COM */ 244611042SErik.Nordmark@Sun.COM if (IS_IPMP(ill)) 244711042SErik.Nordmark@Sun.COM ipmp_illgrp_del_ipif(ill->ill_grp, ipif); 244811042SErik.Nordmark@Sun.COM err = EADDRNOTAVAIL; 244911042SErik.Nordmark@Sun.COM } 245011042SErik.Nordmark@Sun.COM return (err); 24510Sstevel@tonic-gate } 24520Sstevel@tonic-gate 245311042SErik.Nordmark@Sun.COM if (ill->ill_ipif_up_count == 1 && !loopback) { 245411042SErik.Nordmark@Sun.COM /* Recover any additional IREs entries for this ill */ 245511042SErik.Nordmark@Sun.COM (void) ill_recover_saved_ire(ill); 245611042SErik.Nordmark@Sun.COM } 245711042SErik.Nordmark@Sun.COM 245811042SErik.Nordmark@Sun.COM if (ill->ill_need_recover_multicast) { 24590Sstevel@tonic-gate /* 246011042SErik.Nordmark@Sun.COM * Need to recover all multicast memberships in the driver. 246111042SErik.Nordmark@Sun.COM * This had to be deferred until we had attached. 246211042SErik.Nordmark@Sun.COM */ 246311042SErik.Nordmark@Sun.COM ill_recover_multicast(ill); 246411042SErik.Nordmark@Sun.COM } 246511042SErik.Nordmark@Sun.COM 246611042SErik.Nordmark@Sun.COM if (ill->ill_ipif_up_count == 1) { 246711042SErik.Nordmark@Sun.COM /* 246811042SErik.Nordmark@Sun.COM * Since the interface is now up, it may now be active. 24690Sstevel@tonic-gate */ 247011042SErik.Nordmark@Sun.COM if (IS_UNDER_IPMP(ill)) 247111042SErik.Nordmark@Sun.COM ipmp_ill_refresh_active(ill); 24720Sstevel@tonic-gate } 24730Sstevel@tonic-gate 247411042SErik.Nordmark@Sun.COM /* Join the allhosts multicast address and the solicited node MC */ 247511042SErik.Nordmark@Sun.COM ipif_multicast_up(ipif); 247611042SErik.Nordmark@Sun.COM 247711042SErik.Nordmark@Sun.COM /* Perhaps ilgs should use this ill */ 247811042SErik.Nordmark@Sun.COM update_conn_ill(NULL, ill->ill_ipst); 247911042SErik.Nordmark@Sun.COM 248011042SErik.Nordmark@Sun.COM if (ipif->ipif_addr_ready) 248111042SErik.Nordmark@Sun.COM ipif_up_notify(ipif); 248211042SErik.Nordmark@Sun.COM 248311042SErik.Nordmark@Sun.COM return (0); 248411042SErik.Nordmark@Sun.COM } 248511042SErik.Nordmark@Sun.COM 248611042SErik.Nordmark@Sun.COM /* 248711042SErik.Nordmark@Sun.COM * Add the IREs associated with the ipif. 248811042SErik.Nordmark@Sun.COM * Those MUST be explicitly removed in ipif_delete_ires_v6. 248911042SErik.Nordmark@Sun.COM */ 249011042SErik.Nordmark@Sun.COM static int 249111042SErik.Nordmark@Sun.COM ipif_add_ires_v6(ipif_t *ipif, boolean_t loopback) 249211042SErik.Nordmark@Sun.COM { 249311042SErik.Nordmark@Sun.COM ill_t *ill = ipif->ipif_ill; 249411042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst; 249511042SErik.Nordmark@Sun.COM in6_addr_t v6addr; 249611042SErik.Nordmark@Sun.COM in6_addr_t route_mask; 249711042SErik.Nordmark@Sun.COM int err; 249811042SErik.Nordmark@Sun.COM char buf[INET6_ADDRSTRLEN]; 249911042SErik.Nordmark@Sun.COM ire_t *ire_local = NULL; /* LOCAL or LOOPBACK */ 2500*11077SErik.Nordmark@Sun.COM ire_t *ire_if = NULL; 250111042SErik.Nordmark@Sun.COM 25020Sstevel@tonic-gate if (!IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6lcl_addr) && 25030Sstevel@tonic-gate !(ipif->ipif_flags & IPIF_NOLOCAL)) { 25041676Sjpk 25051676Sjpk /* 25061676Sjpk * If we're on a labeled system then make sure that zone- 25071676Sjpk * private addresses have proper remote host database entries. 25081676Sjpk */ 25091676Sjpk if (is_system_labeled() && 25101676Sjpk ipif->ipif_ire_type != IRE_LOOPBACK) { 25111676Sjpk if (ip6opt_ls == 0) { 25121676Sjpk cmn_err(CE_WARN, "IPv6 not enabled " 25131676Sjpk "via /etc/system"); 25141676Sjpk return (EINVAL); 25151676Sjpk } 25161676Sjpk if (!tsol_check_interface_address(ipif)) 25171676Sjpk return (EINVAL); 25181676Sjpk } 25191676Sjpk 25200Sstevel@tonic-gate /* Register the source address for __sin6_src_id */ 25210Sstevel@tonic-gate err = ip_srcid_insert(&ipif->ipif_v6lcl_addr, 25223448Sdh155122 ipif->ipif_zoneid, ipst); 25230Sstevel@tonic-gate if (err != 0) { 252411042SErik.Nordmark@Sun.COM ip0dbg(("ipif_add_ires_v6: srcid_insert %d\n", err)); 25250Sstevel@tonic-gate return (err); 25260Sstevel@tonic-gate } 25270Sstevel@tonic-gate /* 25280Sstevel@tonic-gate * If the interface address is set, create the LOCAL 25290Sstevel@tonic-gate * or LOOPBACK IRE. 25300Sstevel@tonic-gate */ 253111042SErik.Nordmark@Sun.COM ip1dbg(("ipif_add_ires_v6: creating IRE %d for %s\n", 25320Sstevel@tonic-gate ipif->ipif_ire_type, 25330Sstevel@tonic-gate inet_ntop(AF_INET6, &ipif->ipif_v6lcl_addr, 25340Sstevel@tonic-gate buf, sizeof (buf)))); 25350Sstevel@tonic-gate 253611042SErik.Nordmark@Sun.COM ire_local = ire_create_v6( 25370Sstevel@tonic-gate &ipif->ipif_v6lcl_addr, /* dest address */ 25380Sstevel@tonic-gate &ipv6_all_ones, /* mask */ 25390Sstevel@tonic-gate NULL, /* no gateway */ 25400Sstevel@tonic-gate ipif->ipif_ire_type, /* LOCAL or LOOPBACK */ 254111042SErik.Nordmark@Sun.COM ipif->ipif_ill, /* interface */ 254211042SErik.Nordmark@Sun.COM ipif->ipif_zoneid, 254311042SErik.Nordmark@Sun.COM ((ipif->ipif_flags & IPIF_PRIVATE) ? 254411042SErik.Nordmark@Sun.COM RTF_PRIVATE : 0) | RTF_KERNEL, 25453448Sdh155122 NULL, 25463448Sdh155122 ipst); 254711042SErik.Nordmark@Sun.COM if (ire_local == NULL) { 254811042SErik.Nordmark@Sun.COM ip1dbg(("ipif_up_done_v6: NULL ire_local\n")); 254911042SErik.Nordmark@Sun.COM err = ENOMEM; 255011042SErik.Nordmark@Sun.COM goto bad; 255111042SErik.Nordmark@Sun.COM } 25520Sstevel@tonic-gate } 25530Sstevel@tonic-gate 255410616SSebastien.Roy@Sun.COM /* Set up the IRE_IF_RESOLVER or IRE_IF_NORESOLVER, as appropriate. */ 255511042SErik.Nordmark@Sun.COM if (!loopback && !(ipif->ipif_flags & IPIF_NOXMIT) && 25560Sstevel@tonic-gate !(IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6subnet) && 25570Sstevel@tonic-gate IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6net_mask))) { 25580Sstevel@tonic-gate /* ipif_v6subnet is ipif_v6pp_dst_addr for pt-pt */ 25590Sstevel@tonic-gate v6addr = ipif->ipif_v6subnet; 25600Sstevel@tonic-gate 25610Sstevel@tonic-gate if (ipif->ipif_flags & IPIF_POINTOPOINT) { 25620Sstevel@tonic-gate route_mask = ipv6_all_ones; 25630Sstevel@tonic-gate } else { 25640Sstevel@tonic-gate route_mask = ipif->ipif_v6net_mask; 25650Sstevel@tonic-gate } 25660Sstevel@tonic-gate 256711042SErik.Nordmark@Sun.COM ip1dbg(("ipif_add_ires_v6: creating if IRE %d for %s\n", 25680Sstevel@tonic-gate ill->ill_net_type, 25690Sstevel@tonic-gate inet_ntop(AF_INET6, &v6addr, buf, sizeof (buf)))); 25700Sstevel@tonic-gate 2571*11077SErik.Nordmark@Sun.COM ire_if = ire_create_v6( 25720Sstevel@tonic-gate &v6addr, /* dest pref */ 25730Sstevel@tonic-gate &route_mask, /* mask */ 257411042SErik.Nordmark@Sun.COM &ipif->ipif_v6lcl_addr, /* gateway */ 25750Sstevel@tonic-gate ill->ill_net_type, /* IF_[NO]RESOLVER */ 257611042SErik.Nordmark@Sun.COM ipif->ipif_ill, 257711042SErik.Nordmark@Sun.COM ipif->ipif_zoneid, 257811042SErik.Nordmark@Sun.COM ((ipif->ipif_flags & IPIF_PRIVATE) ? 257911042SErik.Nordmark@Sun.COM RTF_PRIVATE : 0) | RTF_KERNEL, 25803448Sdh155122 NULL, 25813448Sdh155122 ipst); 2582*11077SErik.Nordmark@Sun.COM if (ire_if == NULL) { 2583*11077SErik.Nordmark@Sun.COM ip1dbg(("ipif_up_done: NULL ire_if\n")); 25840Sstevel@tonic-gate err = ENOMEM; 25850Sstevel@tonic-gate goto bad; 25860Sstevel@tonic-gate } 25870Sstevel@tonic-gate } 25880Sstevel@tonic-gate 25890Sstevel@tonic-gate /* 25908485SPeter.Memishian@Sun.COM * Need to atomically check for IP address availability under 25918485SPeter.Memishian@Sun.COM * ip_addr_avail_lock. ill_g_lock is held as reader to ensure no new 25928485SPeter.Memishian@Sun.COM * ills or new ipifs can be added while we are checking availability. 25930Sstevel@tonic-gate */ 25943448Sdh155122 rw_enter(&ipst->ips_ill_g_lock, RW_READER); 25953448Sdh155122 mutex_enter(&ipst->ips_ip_addr_avail_lock); 25960Sstevel@tonic-gate ill->ill_ipif_up_count++; 25970Sstevel@tonic-gate ipif->ipif_flags |= IPIF_UP; 25980Sstevel@tonic-gate err = ip_addr_availability_check(ipif); 25993448Sdh155122 mutex_exit(&ipst->ips_ip_addr_avail_lock); 26003448Sdh155122 rw_exit(&ipst->ips_ill_g_lock); 26010Sstevel@tonic-gate 26020Sstevel@tonic-gate if (err != 0) { 26030Sstevel@tonic-gate /* 26040Sstevel@tonic-gate * Our address may already be up on the same ill. In this case, 26050Sstevel@tonic-gate * the external resolver entry for our ipif replaced the one for 26060Sstevel@tonic-gate * the other ipif. So we don't want to delete it (otherwise the 26070Sstevel@tonic-gate * other ipif would be unable to send packets). 26080Sstevel@tonic-gate * ip_addr_availability_check() identifies this case for us and 260911042SErik.Nordmark@Sun.COM * returns EADDRINUSE; Caller must turn it into EADDRNOTAVAIL 26100Sstevel@tonic-gate * which is the expected error code. 26119287SSowmini.Varadhan@Sun.COM * 261211042SErik.Nordmark@Sun.COM * Note that ipif_ndp_down() will only delete the nce in the 261311042SErik.Nordmark@Sun.COM * case when the nce_ipif_cnt drops to 0. 26140Sstevel@tonic-gate */ 26150Sstevel@tonic-gate ill->ill_ipif_up_count--; 26160Sstevel@tonic-gate ipif->ipif_flags &= ~IPIF_UP; 26170Sstevel@tonic-gate goto bad; 26180Sstevel@tonic-gate } 26190Sstevel@tonic-gate 26200Sstevel@tonic-gate /* 26218485SPeter.Memishian@Sun.COM * Add in all newly created IREs. 2622*11077SErik.Nordmark@Sun.COM * We add the IRE_INTERFACE before the IRE_LOCAL to ensure 2623*11077SErik.Nordmark@Sun.COM * that lookups find the IRE_LOCAL even if the IRE_INTERFACE is 2624*11077SErik.Nordmark@Sun.COM * a /128 route. 26250Sstevel@tonic-gate */ 2626*11077SErik.Nordmark@Sun.COM if (ire_if != NULL) { 2627*11077SErik.Nordmark@Sun.COM ire_if = ire_add(ire_if); 2628*11077SErik.Nordmark@Sun.COM if (ire_if == NULL) { 2629*11077SErik.Nordmark@Sun.COM err = ENOMEM; 2630*11077SErik.Nordmark@Sun.COM goto bad2; 2631*11077SErik.Nordmark@Sun.COM } 2632*11077SErik.Nordmark@Sun.COM #ifdef DEBUG 2633*11077SErik.Nordmark@Sun.COM ire_refhold_notr(ire_if); 2634*11077SErik.Nordmark@Sun.COM ire_refrele(ire_if); 2635*11077SErik.Nordmark@Sun.COM #endif 2636*11077SErik.Nordmark@Sun.COM } 263711042SErik.Nordmark@Sun.COM if (ire_local != NULL) { 263811042SErik.Nordmark@Sun.COM ire_local = ire_add(ire_local); 2639*11077SErik.Nordmark@Sun.COM if (ire_local == NULL) { 2640*11077SErik.Nordmark@Sun.COM err = ENOMEM; 2641*11077SErik.Nordmark@Sun.COM goto bad2; 2642*11077SErik.Nordmark@Sun.COM } 264311042SErik.Nordmark@Sun.COM #ifdef DEBUG 2644*11077SErik.Nordmark@Sun.COM ire_refhold_notr(ire_local); 2645*11077SErik.Nordmark@Sun.COM ire_refrele(ire_local); 264611042SErik.Nordmark@Sun.COM #endif 264711042SErik.Nordmark@Sun.COM } 264811042SErik.Nordmark@Sun.COM rw_enter(&ipst->ips_ill_g_lock, RW_WRITER); 264911042SErik.Nordmark@Sun.COM if (ire_local != NULL) 265011042SErik.Nordmark@Sun.COM ipif->ipif_ire_local = ire_local; 2651*11077SErik.Nordmark@Sun.COM if (ire_if != NULL) 2652*11077SErik.Nordmark@Sun.COM ipif->ipif_ire_if = ire_if; 265311042SErik.Nordmark@Sun.COM rw_exit(&ipst->ips_ill_g_lock); 265411042SErik.Nordmark@Sun.COM ire_local = NULL; 2655*11077SErik.Nordmark@Sun.COM ire_if = NULL; 26560Sstevel@tonic-gate 26578023SPhil.Kirk@Sun.COM if (ipif->ipif_addr_ready) 26588023SPhil.Kirk@Sun.COM ipif_up_notify(ipif); 26590Sstevel@tonic-gate return (0); 26600Sstevel@tonic-gate 2661*11077SErik.Nordmark@Sun.COM bad2: 2662*11077SErik.Nordmark@Sun.COM ill->ill_ipif_up_count--; 2663*11077SErik.Nordmark@Sun.COM ipif->ipif_flags &= ~IPIF_UP; 2664*11077SErik.Nordmark@Sun.COM 26650Sstevel@tonic-gate bad: 266611042SErik.Nordmark@Sun.COM if (ire_local != NULL) 266711042SErik.Nordmark@Sun.COM ire_delete(ire_local); 2668*11077SErik.Nordmark@Sun.COM if (ire_if != NULL) 2669*11077SErik.Nordmark@Sun.COM ire_delete(ire_if); 2670*11077SErik.Nordmark@Sun.COM 2671*11077SErik.Nordmark@Sun.COM rw_enter(&ipst->ips_ill_g_lock, RW_WRITER); 2672*11077SErik.Nordmark@Sun.COM ire_local = ipif->ipif_ire_local; 2673*11077SErik.Nordmark@Sun.COM ipif->ipif_ire_local = NULL; 2674*11077SErik.Nordmark@Sun.COM ire_if = ipif->ipif_ire_if; 2675*11077SErik.Nordmark@Sun.COM ipif->ipif_ire_if = NULL; 2676*11077SErik.Nordmark@Sun.COM rw_exit(&ipst->ips_ill_g_lock); 2677*11077SErik.Nordmark@Sun.COM if (ire_local != NULL) { 2678*11077SErik.Nordmark@Sun.COM ire_delete(ire_local); 2679*11077SErik.Nordmark@Sun.COM ire_refrele_notr(ire_local); 2680*11077SErik.Nordmark@Sun.COM } 2681*11077SErik.Nordmark@Sun.COM if (ire_if != NULL) { 2682*11077SErik.Nordmark@Sun.COM ire_delete(ire_if); 2683*11077SErik.Nordmark@Sun.COM ire_refrele_notr(ire_if); 26840Sstevel@tonic-gate } 26853448Sdh155122 (void) ip_srcid_remove(&ipif->ipif_v6lcl_addr, ipif->ipif_zoneid, ipst); 26860Sstevel@tonic-gate 26870Sstevel@tonic-gate return (err); 26880Sstevel@tonic-gate } 26890Sstevel@tonic-gate 269011042SErik.Nordmark@Sun.COM /* Remove all the IREs created by ipif_add_ires_v6 */ 269111042SErik.Nordmark@Sun.COM void 269211042SErik.Nordmark@Sun.COM ipif_delete_ires_v6(ipif_t *ipif) 269311042SErik.Nordmark@Sun.COM { 269411042SErik.Nordmark@Sun.COM ill_t *ill = ipif->ipif_ill; 269511042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst; 269611042SErik.Nordmark@Sun.COM ire_t *ire; 269711042SErik.Nordmark@Sun.COM 269811042SErik.Nordmark@Sun.COM rw_enter(&ipst->ips_ill_g_lock, RW_WRITER); 2699*11077SErik.Nordmark@Sun.COM ire = ipif->ipif_ire_local; 2700*11077SErik.Nordmark@Sun.COM ipif->ipif_ire_local = NULL; 2701*11077SErik.Nordmark@Sun.COM rw_exit(&ipst->ips_ill_g_lock); 2702*11077SErik.Nordmark@Sun.COM if (ire != NULL) { 270311042SErik.Nordmark@Sun.COM /* 270411042SErik.Nordmark@Sun.COM * Move count to ipif so we don't loose the count due to 270511042SErik.Nordmark@Sun.COM * a down/up dance. 270611042SErik.Nordmark@Sun.COM */ 270711042SErik.Nordmark@Sun.COM atomic_add_32(&ipif->ipif_ib_pkt_count, ire->ire_ib_pkt_count); 270811042SErik.Nordmark@Sun.COM 270911042SErik.Nordmark@Sun.COM ire_delete(ire); 271011042SErik.Nordmark@Sun.COM ire_refrele_notr(ire); 271111042SErik.Nordmark@Sun.COM } 2712*11077SErik.Nordmark@Sun.COM rw_enter(&ipst->ips_ill_g_lock, RW_WRITER); 2713*11077SErik.Nordmark@Sun.COM ire = ipif->ipif_ire_if; 2714*11077SErik.Nordmark@Sun.COM ipif->ipif_ire_if = NULL; 2715*11077SErik.Nordmark@Sun.COM rw_exit(&ipst->ips_ill_g_lock); 2716*11077SErik.Nordmark@Sun.COM if (ire != NULL) { 271711042SErik.Nordmark@Sun.COM ire_delete(ire); 2718*11077SErik.Nordmark@Sun.COM ire_refrele_notr(ire); 271911042SErik.Nordmark@Sun.COM } 272011042SErik.Nordmark@Sun.COM } 272111042SErik.Nordmark@Sun.COM 27220Sstevel@tonic-gate /* 272311042SErik.Nordmark@Sun.COM * Delete an ND entry if it exists. 27240Sstevel@tonic-gate */ 27250Sstevel@tonic-gate /* ARGSUSED */ 27260Sstevel@tonic-gate int 27270Sstevel@tonic-gate ip_siocdelndp_v6(ipif_t *ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp, 27280Sstevel@tonic-gate ip_ioctl_cmd_t *ipip, void *dummy_ifreq) 27290Sstevel@tonic-gate { 27300Sstevel@tonic-gate sin6_t *sin6; 27310Sstevel@tonic-gate struct lifreq *lifr; 27320Sstevel@tonic-gate lif_nd_req_t *lnr; 27338485SPeter.Memishian@Sun.COM ill_t *ill = ipif->ipif_ill; 273411042SErik.Nordmark@Sun.COM nce_t *nce; 27358485SPeter.Memishian@Sun.COM 27368485SPeter.Memishian@Sun.COM lifr = (struct lifreq *)mp->b_cont->b_cont->b_rptr; 27370Sstevel@tonic-gate lnr = &lifr->lifr_nd; 27380Sstevel@tonic-gate /* Only allow for logical unit zero i.e. not on "le0:17" */ 27390Sstevel@tonic-gate if (ipif->ipif_id != 0) 27400Sstevel@tonic-gate return (EINVAL); 27410Sstevel@tonic-gate 27420Sstevel@tonic-gate if (!ipif->ipif_isv6) 27430Sstevel@tonic-gate return (EINVAL); 27440Sstevel@tonic-gate 27450Sstevel@tonic-gate if (lnr->lnr_addr.ss_family != AF_INET6) 27460Sstevel@tonic-gate return (EAFNOSUPPORT); 27470Sstevel@tonic-gate 27480Sstevel@tonic-gate sin6 = (sin6_t *)&lnr->lnr_addr; 27498485SPeter.Memishian@Sun.COM 27508485SPeter.Memishian@Sun.COM /* 27518485SPeter.Memishian@Sun.COM * Since ND mappings must be consistent across an IPMP group, prohibit 275211042SErik.Nordmark@Sun.COM * deleting ND mappings on underlying interfaces. 275311042SErik.Nordmark@Sun.COM * Don't allow deletion of mappings for local addresses. 27548485SPeter.Memishian@Sun.COM */ 27558485SPeter.Memishian@Sun.COM if (IS_UNDER_IPMP(ill)) 27568485SPeter.Memishian@Sun.COM return (EPERM); 27578485SPeter.Memishian@Sun.COM 275811042SErik.Nordmark@Sun.COM nce = nce_lookup_v6(ill, &sin6->sin6_addr); 275911042SErik.Nordmark@Sun.COM if (nce == NULL) 276011042SErik.Nordmark@Sun.COM return (ESRCH); 276111042SErik.Nordmark@Sun.COM 276211042SErik.Nordmark@Sun.COM if (NCE_MYADDR(nce->nce_common)) { 276311042SErik.Nordmark@Sun.COM nce_refrele(nce); 276411042SErik.Nordmark@Sun.COM return (EPERM); 27658485SPeter.Memishian@Sun.COM } 27668485SPeter.Memishian@Sun.COM 276711042SErik.Nordmark@Sun.COM /* 276811042SErik.Nordmark@Sun.COM * delete the nce_common which will also delete the nces on any 276911042SErik.Nordmark@Sun.COM * under_ill in the case of ipmp. 277011042SErik.Nordmark@Sun.COM */ 277111042SErik.Nordmark@Sun.COM ncec_delete(nce->nce_common); 277211042SErik.Nordmark@Sun.COM nce_refrele(nce); 27730Sstevel@tonic-gate return (0); 27740Sstevel@tonic-gate } 27750Sstevel@tonic-gate 27760Sstevel@tonic-gate /* 27770Sstevel@tonic-gate * Return nbr cache info. 27780Sstevel@tonic-gate */ 27790Sstevel@tonic-gate /* ARGSUSED */ 27800Sstevel@tonic-gate int 27810Sstevel@tonic-gate ip_siocqueryndp_v6(ipif_t *ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp, 27820Sstevel@tonic-gate ip_ioctl_cmd_t *ipip, void *dummy_ifreq) 27830Sstevel@tonic-gate { 27840Sstevel@tonic-gate ill_t *ill = ipif->ipif_ill; 27850Sstevel@tonic-gate struct lifreq *lifr; 27860Sstevel@tonic-gate lif_nd_req_t *lnr; 27870Sstevel@tonic-gate 27880Sstevel@tonic-gate lifr = (struct lifreq *)mp->b_cont->b_cont->b_rptr; 27890Sstevel@tonic-gate lnr = &lifr->lifr_nd; 27900Sstevel@tonic-gate /* Only allow for logical unit zero i.e. not on "le0:17" */ 27910Sstevel@tonic-gate if (ipif->ipif_id != 0) 27920Sstevel@tonic-gate return (EINVAL); 27930Sstevel@tonic-gate 27940Sstevel@tonic-gate if (!ipif->ipif_isv6) 27950Sstevel@tonic-gate return (EINVAL); 27960Sstevel@tonic-gate 27970Sstevel@tonic-gate if (lnr->lnr_addr.ss_family != AF_INET6) 27980Sstevel@tonic-gate return (EAFNOSUPPORT); 27990Sstevel@tonic-gate 28000Sstevel@tonic-gate if (ill->ill_phys_addr_length > sizeof (lnr->lnr_hdw_addr)) 28010Sstevel@tonic-gate return (EINVAL); 28020Sstevel@tonic-gate 28030Sstevel@tonic-gate return (ndp_query(ill, lnr)); 28040Sstevel@tonic-gate } 28050Sstevel@tonic-gate 28060Sstevel@tonic-gate /* 28070Sstevel@tonic-gate * Perform an update of the nd entry for the specified address. 28080Sstevel@tonic-gate */ 28090Sstevel@tonic-gate /* ARGSUSED */ 28100Sstevel@tonic-gate int 28110Sstevel@tonic-gate ip_siocsetndp_v6(ipif_t *ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp, 28120Sstevel@tonic-gate ip_ioctl_cmd_t *ipip, void *dummy_ifreq) 28130Sstevel@tonic-gate { 28148485SPeter.Memishian@Sun.COM sin6_t *sin6; 28150Sstevel@tonic-gate ill_t *ill = ipif->ipif_ill; 28160Sstevel@tonic-gate struct lifreq *lifr; 28170Sstevel@tonic-gate lif_nd_req_t *lnr; 28188485SPeter.Memishian@Sun.COM ire_t *ire; 28193448Sdh155122 28200Sstevel@tonic-gate lifr = (struct lifreq *)mp->b_cont->b_cont->b_rptr; 28210Sstevel@tonic-gate lnr = &lifr->lifr_nd; 28220Sstevel@tonic-gate /* Only allow for logical unit zero i.e. not on "le0:17" */ 28230Sstevel@tonic-gate if (ipif->ipif_id != 0) 28240Sstevel@tonic-gate return (EINVAL); 28250Sstevel@tonic-gate 28260Sstevel@tonic-gate if (!ipif->ipif_isv6) 28270Sstevel@tonic-gate return (EINVAL); 28280Sstevel@tonic-gate 28290Sstevel@tonic-gate if (lnr->lnr_addr.ss_family != AF_INET6) 28300Sstevel@tonic-gate return (EAFNOSUPPORT); 28310Sstevel@tonic-gate 28328485SPeter.Memishian@Sun.COM sin6 = (sin6_t *)&lnr->lnr_addr; 28338485SPeter.Memishian@Sun.COM 28348485SPeter.Memishian@Sun.COM /* 28358485SPeter.Memishian@Sun.COM * Since ND mappings must be consistent across an IPMP group, prohibit 28368485SPeter.Memishian@Sun.COM * updating ND mappings on underlying interfaces. Also, since ND 28378485SPeter.Memishian@Sun.COM * mappings for IPMP data addresses are owned by IP itself, prohibit 28388485SPeter.Memishian@Sun.COM * updating them. 28398485SPeter.Memishian@Sun.COM */ 28408485SPeter.Memishian@Sun.COM if (IS_UNDER_IPMP(ill)) 28418485SPeter.Memishian@Sun.COM return (EPERM); 28428485SPeter.Memishian@Sun.COM 28438485SPeter.Memishian@Sun.COM if (IS_IPMP(ill)) { 284411042SErik.Nordmark@Sun.COM ire = ire_ftable_lookup_v6(&sin6->sin6_addr, NULL, NULL, 284511042SErik.Nordmark@Sun.COM IRE_LOCAL, ill, ALL_ZONES, NULL, 284611042SErik.Nordmark@Sun.COM MATCH_IRE_TYPE | MATCH_IRE_ILL, 0, ill->ill_ipst, NULL); 28478485SPeter.Memishian@Sun.COM if (ire != NULL) { 28488485SPeter.Memishian@Sun.COM ire_refrele(ire); 28498485SPeter.Memishian@Sun.COM return (EPERM); 28508485SPeter.Memishian@Sun.COM } 28518485SPeter.Memishian@Sun.COM } 28528485SPeter.Memishian@Sun.COM 28530Sstevel@tonic-gate return (ndp_sioc_update(ill, lnr)); 28540Sstevel@tonic-gate } 2855