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 51473Sja97890 * Common Development and Distribution License (the "License"). 61473Sja97890 * 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 #include <sys/types.h> 300Sstevel@tonic-gate #include <sys/stream.h> 310Sstevel@tonic-gate #include <sys/dlpi.h> 320Sstevel@tonic-gate #include <sys/stropts.h> 330Sstevel@tonic-gate #include <sys/sysmacros.h> 340Sstevel@tonic-gate #include <sys/strsun.h> 350Sstevel@tonic-gate #include <sys/strlog.h> 360Sstevel@tonic-gate #include <sys/strsubr.h> 370Sstevel@tonic-gate #define _SUN_TPI_VERSION 2 380Sstevel@tonic-gate #include <sys/tihdr.h> 390Sstevel@tonic-gate #include <sys/ddi.h> 400Sstevel@tonic-gate #include <sys/sunddi.h> 410Sstevel@tonic-gate #include <sys/cmn_err.h> 420Sstevel@tonic-gate #include <sys/debug.h> 432958Sdr146992 #include <sys/sdt.h> 440Sstevel@tonic-gate #include <sys/kobj.h> 450Sstevel@tonic-gate #include <sys/zone.h> 462958Sdr146992 #include <sys/neti.h> 472958Sdr146992 #include <sys/hook.h> 480Sstevel@tonic-gate 490Sstevel@tonic-gate #include <sys/kmem.h> 500Sstevel@tonic-gate #include <sys/systm.h> 510Sstevel@tonic-gate #include <sys/param.h> 520Sstevel@tonic-gate #include <sys/socket.h> 530Sstevel@tonic-gate #include <sys/vtrace.h> 540Sstevel@tonic-gate #include <sys/isa_defs.h> 550Sstevel@tonic-gate #include <sys/atomic.h> 560Sstevel@tonic-gate #include <sys/policy.h> 5711042SErik.Nordmark@Sun.COM #include <sys/mac.h> 580Sstevel@tonic-gate #include <net/if.h> 59741Smasputra #include <net/if_types.h> 600Sstevel@tonic-gate #include <net/route.h> 610Sstevel@tonic-gate #include <net/if_dl.h> 620Sstevel@tonic-gate #include <sys/sockio.h> 630Sstevel@tonic-gate #include <netinet/in.h> 640Sstevel@tonic-gate #include <netinet/ip6.h> 650Sstevel@tonic-gate #include <netinet/icmp6.h> 660Sstevel@tonic-gate #include <netinet/sctp.h> 670Sstevel@tonic-gate 680Sstevel@tonic-gate #include <inet/common.h> 690Sstevel@tonic-gate #include <inet/mi.h> 705240Snordmark #include <inet/optcom.h> 710Sstevel@tonic-gate #include <inet/mib2.h> 720Sstevel@tonic-gate #include <inet/nd.h> 730Sstevel@tonic-gate #include <inet/arp.h> 740Sstevel@tonic-gate 750Sstevel@tonic-gate #include <inet/ip.h> 76741Smasputra #include <inet/ip_impl.h> 770Sstevel@tonic-gate #include <inet/ip6.h> 780Sstevel@tonic-gate #include <inet/ip6_asp.h> 790Sstevel@tonic-gate #include <inet/tcp.h> 80741Smasputra #include <inet/tcp_impl.h> 81741Smasputra #include <inet/udp_impl.h> 820Sstevel@tonic-gate #include <inet/ipp_common.h> 830Sstevel@tonic-gate 840Sstevel@tonic-gate #include <inet/ip_multi.h> 850Sstevel@tonic-gate #include <inet/ip_if.h> 860Sstevel@tonic-gate #include <inet/ip_ire.h> 870Sstevel@tonic-gate #include <inet/ip_rts.h> 880Sstevel@tonic-gate #include <inet/ip_ndp.h> 890Sstevel@tonic-gate #include <net/pfkeyv2.h> 900Sstevel@tonic-gate #include <inet/sadb.h> 910Sstevel@tonic-gate #include <inet/ipsec_impl.h> 9210616SSebastien.Roy@Sun.COM #include <inet/iptun/iptun_impl.h> 930Sstevel@tonic-gate #include <inet/sctp_ip.h> 940Sstevel@tonic-gate #include <sys/pattr.h> 950Sstevel@tonic-gate #include <inet/ipclassifier.h> 960Sstevel@tonic-gate #include <inet/ipsecah.h> 975240Snordmark #include <inet/rawip_impl.h> 985240Snordmark #include <inet/rts_impl.h> 998275SEric Cheng #include <sys/squeue_impl.h> 1000Sstevel@tonic-gate #include <sys/squeue.h> 1010Sstevel@tonic-gate 1021676Sjpk #include <sys/tsol/label.h> 1031676Sjpk #include <sys/tsol/tnet.h> 1041676Sjpk 1052546Scarlsonj /* Temporary; for CR 6451644 work-around */ 1062546Scarlsonj #include <sys/ethernet.h> 1072546Scarlsonj 1080Sstevel@tonic-gate /* 1090Sstevel@tonic-gate * Naming conventions: 1100Sstevel@tonic-gate * These rules should be judiciously applied 1110Sstevel@tonic-gate * if there is a need to identify something as IPv6 versus IPv4 1120Sstevel@tonic-gate * IPv6 funcions will end with _v6 in the ip module. 1130Sstevel@tonic-gate * IPv6 funcions will end with _ipv6 in the transport modules. 1140Sstevel@tonic-gate * IPv6 macros: 1150Sstevel@tonic-gate * Some macros end with _V6; e.g. ILL_FRAG_HASH_V6 1160Sstevel@tonic-gate * Some macros start with V6_; e.g. V6_OR_V4_INADDR_ANY 1170Sstevel@tonic-gate * And then there are ..V4_PART_OF_V6. 1180Sstevel@tonic-gate * The intent is that macros in the ip module end with _V6. 1190Sstevel@tonic-gate * IPv6 global variables will start with ipv6_ 1200Sstevel@tonic-gate * IPv6 structures will start with ipv6 1210Sstevel@tonic-gate * IPv6 defined constants should start with IPV6_ 1220Sstevel@tonic-gate * (but then there are NDP_DEFAULT_VERS_PRI_AND_FLOW, etc) 1230Sstevel@tonic-gate */ 1240Sstevel@tonic-gate 1250Sstevel@tonic-gate /* 1261676Sjpk * ip6opt_ls is used to enable IPv6 (via /etc/system on TX systems). 1271676Sjpk * We need to do this because we didn't obtain the IP6OPT_LS (0x0a) 1281676Sjpk * from IANA. This mechanism will remain in effect until an official 1291676Sjpk * number is obtained. 1301676Sjpk */ 1311676Sjpk uchar_t ip6opt_ls; 1321676Sjpk 1330Sstevel@tonic-gate const in6_addr_t ipv6_all_ones = 1340Sstevel@tonic-gate { 0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU }; 1350Sstevel@tonic-gate const in6_addr_t ipv6_all_zeros = { 0, 0, 0, 0 }; 1360Sstevel@tonic-gate 1370Sstevel@tonic-gate #ifdef _BIG_ENDIAN 1380Sstevel@tonic-gate const in6_addr_t ipv6_unspecified_group = { 0xff000000U, 0, 0, 0 }; 1390Sstevel@tonic-gate #else /* _BIG_ENDIAN */ 1400Sstevel@tonic-gate const in6_addr_t ipv6_unspecified_group = { 0x000000ffU, 0, 0, 0 }; 1410Sstevel@tonic-gate #endif /* _BIG_ENDIAN */ 1420Sstevel@tonic-gate 1430Sstevel@tonic-gate #ifdef _BIG_ENDIAN 1440Sstevel@tonic-gate const in6_addr_t ipv6_loopback = { 0, 0, 0, 0x00000001U }; 1450Sstevel@tonic-gate #else /* _BIG_ENDIAN */ 1460Sstevel@tonic-gate const in6_addr_t ipv6_loopback = { 0, 0, 0, 0x01000000U }; 1470Sstevel@tonic-gate #endif /* _BIG_ENDIAN */ 1480Sstevel@tonic-gate 1490Sstevel@tonic-gate #ifdef _BIG_ENDIAN 1500Sstevel@tonic-gate const in6_addr_t ipv6_all_hosts_mcast = { 0xff020000U, 0, 0, 0x00000001U }; 1510Sstevel@tonic-gate #else /* _BIG_ENDIAN */ 1520Sstevel@tonic-gate const in6_addr_t ipv6_all_hosts_mcast = { 0x000002ffU, 0, 0, 0x01000000U }; 1530Sstevel@tonic-gate #endif /* _BIG_ENDIAN */ 1540Sstevel@tonic-gate 1550Sstevel@tonic-gate #ifdef _BIG_ENDIAN 1560Sstevel@tonic-gate const in6_addr_t ipv6_all_rtrs_mcast = { 0xff020000U, 0, 0, 0x00000002U }; 1570Sstevel@tonic-gate #else /* _BIG_ENDIAN */ 1580Sstevel@tonic-gate const in6_addr_t ipv6_all_rtrs_mcast = { 0x000002ffU, 0, 0, 0x02000000U }; 1590Sstevel@tonic-gate #endif /* _BIG_ENDIAN */ 1600Sstevel@tonic-gate 1610Sstevel@tonic-gate #ifdef _BIG_ENDIAN 1620Sstevel@tonic-gate const in6_addr_t ipv6_all_v2rtrs_mcast = { 0xff020000U, 0, 0, 0x00000016U }; 1630Sstevel@tonic-gate #else /* _BIG_ENDIAN */ 1640Sstevel@tonic-gate const in6_addr_t ipv6_all_v2rtrs_mcast = { 0x000002ffU, 0, 0, 0x16000000U }; 1650Sstevel@tonic-gate #endif /* _BIG_ENDIAN */ 1660Sstevel@tonic-gate 1670Sstevel@tonic-gate #ifdef _BIG_ENDIAN 1680Sstevel@tonic-gate const in6_addr_t ipv6_solicited_node_mcast = 1690Sstevel@tonic-gate { 0xff020000U, 0, 0x00000001U, 0xff000000U }; 1700Sstevel@tonic-gate #else /* _BIG_ENDIAN */ 1710Sstevel@tonic-gate const in6_addr_t ipv6_solicited_node_mcast = 1720Sstevel@tonic-gate { 0x000002ffU, 0, 0x01000000U, 0x000000ffU }; 1730Sstevel@tonic-gate #endif /* _BIG_ENDIAN */ 1740Sstevel@tonic-gate 17511042SErik.Nordmark@Sun.COM static boolean_t icmp_inbound_verify_v6(mblk_t *, icmp6_t *, ip_recv_attr_t *); 17611042SErik.Nordmark@Sun.COM static void icmp_inbound_too_big_v6(icmp6_t *, ip_recv_attr_t *); 17711042SErik.Nordmark@Sun.COM static void icmp_pkt_v6(mblk_t *, void *, size_t, const in6_addr_t *, 17811042SErik.Nordmark@Sun.COM ip_recv_attr_t *); 17911042SErik.Nordmark@Sun.COM static void icmp_redirect_v6(mblk_t *, ip6_t *, nd_redirect_t *, 18011042SErik.Nordmark@Sun.COM ip_recv_attr_t *); 18111042SErik.Nordmark@Sun.COM static void icmp_send_redirect_v6(mblk_t *, in6_addr_t *, 18211042SErik.Nordmark@Sun.COM in6_addr_t *, ip_recv_attr_t *); 18311042SErik.Nordmark@Sun.COM static void icmp_send_reply_v6(mblk_t *, ip6_t *, icmp6_t *, 18411042SErik.Nordmark@Sun.COM ip_recv_attr_t *); 1853448Sdh155122 static boolean_t ip_source_routed_v6(ip6_t *, mblk_t *, ip_stack_t *); 1860Sstevel@tonic-gate 1870Sstevel@tonic-gate /* 18811042SErik.Nordmark@Sun.COM * icmp_inbound_v6 deals with ICMP messages that are handled by IP. 18911042SErik.Nordmark@Sun.COM * If the ICMP message is consumed by IP, i.e., it should not be delivered 19011042SErik.Nordmark@Sun.COM * to any IPPROTO_ICMP raw sockets, then it returns NULL. 19111042SErik.Nordmark@Sun.COM * Likewise, if the ICMP error is misformed (too short, etc), then it 19211042SErik.Nordmark@Sun.COM * returns NULL. The caller uses this to determine whether or not to send 19311042SErik.Nordmark@Sun.COM * to raw sockets. 1940Sstevel@tonic-gate * 1950Sstevel@tonic-gate * All error messages are passed to the matching transport stream. 1960Sstevel@tonic-gate * 19711042SErik.Nordmark@Sun.COM * See comment for icmp_inbound_v4() on how IPsec is handled. 1980Sstevel@tonic-gate */ 19911042SErik.Nordmark@Sun.COM mblk_t * 20011042SErik.Nordmark@Sun.COM icmp_inbound_v6(mblk_t *mp, ip_recv_attr_t *ira) 2010Sstevel@tonic-gate { 2020Sstevel@tonic-gate icmp6_t *icmp6; 20311042SErik.Nordmark@Sun.COM ip6_t *ip6h; /* Outer header */ 20411042SErik.Nordmark@Sun.COM int ip_hdr_length; /* Outer header length */ 2050Sstevel@tonic-gate boolean_t interested; 20611042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; 2073448Sdh155122 ip_stack_t *ipst = ill->ill_ipst; 20811042SErik.Nordmark@Sun.COM mblk_t *mp_ret = NULL; 2090Sstevel@tonic-gate 2100Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 2110Sstevel@tonic-gate 2120Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInMsgs); 2130Sstevel@tonic-gate 21411042SErik.Nordmark@Sun.COM /* Make sure ira_l2src is set for ndp_input */ 21511042SErik.Nordmark@Sun.COM if (!(ira->ira_flags & IRAF_L2SRC_SET)) 21611042SErik.Nordmark@Sun.COM ip_setl2src(mp, ira, ira->ira_rill); 21711042SErik.Nordmark@Sun.COM 21811042SErik.Nordmark@Sun.COM ip_hdr_length = ira->ira_ip_hdr_length; 21911042SErik.Nordmark@Sun.COM if ((mp->b_wptr - mp->b_rptr) < (ip_hdr_length + ICMP6_MINLEN)) { 22011042SErik.Nordmark@Sun.COM if (ira->ira_pktlen < (ip_hdr_length + ICMP6_MINLEN)) { 22111042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInTruncatedPkts); 22211042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInTruncatedPkts", mp, ill); 22311042SErik.Nordmark@Sun.COM freemsg(mp); 22411042SErik.Nordmark@Sun.COM return (NULL); 2250Sstevel@tonic-gate } 22611042SErik.Nordmark@Sun.COM ip6h = ip_pullup(mp, ip_hdr_length + ICMP6_MINLEN, ira); 22711042SErik.Nordmark@Sun.COM if (ip6h == NULL) { 22811042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors); 22911042SErik.Nordmark@Sun.COM freemsg(mp); 23011042SErik.Nordmark@Sun.COM return (NULL); 23111042SErik.Nordmark@Sun.COM } 2320Sstevel@tonic-gate } 23311042SErik.Nordmark@Sun.COM 23411042SErik.Nordmark@Sun.COM icmp6 = (icmp6_t *)(&mp->b_rptr[ip_hdr_length]); 23511042SErik.Nordmark@Sun.COM DTRACE_PROBE2(icmp__inbound__v6, ip6_t *, ip6h, icmp6_t *, icmp6); 23611042SErik.Nordmark@Sun.COM ip2dbg(("icmp_inbound_v6: type %d code %d\n", icmp6->icmp6_type, 23711042SErik.Nordmark@Sun.COM icmp6->icmp6_code)); 2380Sstevel@tonic-gate 2391676Sjpk /* 24011042SErik.Nordmark@Sun.COM * We will set "interested" to "true" if we should pass a copy to 24111042SErik.Nordmark@Sun.COM * the transport i.e., if it is an error message. 2421676Sjpk */ 2430Sstevel@tonic-gate interested = !(icmp6->icmp6_type & ICMP6_INFOMSG_MASK); 2440Sstevel@tonic-gate 2450Sstevel@tonic-gate switch (icmp6->icmp6_type) { 2460Sstevel@tonic-gate case ICMP6_DST_UNREACH: 2470Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInDestUnreachs); 2480Sstevel@tonic-gate if (icmp6->icmp6_code == ICMP6_DST_UNREACH_ADMIN) 2490Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInAdminProhibs); 2500Sstevel@tonic-gate break; 2510Sstevel@tonic-gate 2520Sstevel@tonic-gate case ICMP6_TIME_EXCEEDED: 2530Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInTimeExcds); 2540Sstevel@tonic-gate break; 2550Sstevel@tonic-gate 2560Sstevel@tonic-gate case ICMP6_PARAM_PROB: 2570Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInParmProblems); 2580Sstevel@tonic-gate break; 2590Sstevel@tonic-gate 2600Sstevel@tonic-gate case ICMP6_PACKET_TOO_BIG: 26111042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInPktTooBigs); 26211042SErik.Nordmark@Sun.COM break; 26311042SErik.Nordmark@Sun.COM 2640Sstevel@tonic-gate case ICMP6_ECHO_REQUEST: 2650Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInEchos); 2660Sstevel@tonic-gate if (IN6_IS_ADDR_MULTICAST(&ip6h->ip6_dst) && 2673448Sdh155122 !ipst->ips_ipv6_resp_echo_mcast) 2680Sstevel@tonic-gate break; 2690Sstevel@tonic-gate 2700Sstevel@tonic-gate /* 2710Sstevel@tonic-gate * We must have exclusive use of the mblk to convert it to 2720Sstevel@tonic-gate * a response. 2730Sstevel@tonic-gate * If not, we copy it. 2740Sstevel@tonic-gate */ 2750Sstevel@tonic-gate if (mp->b_datap->db_ref > 1) { 2760Sstevel@tonic-gate mblk_t *mp1; 2770Sstevel@tonic-gate 2780Sstevel@tonic-gate mp1 = copymsg(mp); 27911042SErik.Nordmark@Sun.COM if (mp1 == NULL) { 28011042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 28111042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards - copymsg", 28211042SErik.Nordmark@Sun.COM mp, ill); 28311042SErik.Nordmark@Sun.COM freemsg(mp); 28411042SErik.Nordmark@Sun.COM return (NULL); 28511042SErik.Nordmark@Sun.COM } 2860Sstevel@tonic-gate freemsg(mp); 2870Sstevel@tonic-gate mp = mp1; 2880Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 28911042SErik.Nordmark@Sun.COM icmp6 = (icmp6_t *)(&mp->b_rptr[ip_hdr_length]); 2900Sstevel@tonic-gate } 2910Sstevel@tonic-gate 2920Sstevel@tonic-gate icmp6->icmp6_type = ICMP6_ECHO_REPLY; 29311042SErik.Nordmark@Sun.COM icmp_send_reply_v6(mp, ip6h, icmp6, ira); 29411042SErik.Nordmark@Sun.COM return (NULL); 2950Sstevel@tonic-gate 2960Sstevel@tonic-gate case ICMP6_ECHO_REPLY: 2970Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInEchoReplies); 2980Sstevel@tonic-gate break; 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate case ND_ROUTER_SOLICIT: 3010Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInRouterSolicits); 3020Sstevel@tonic-gate break; 3030Sstevel@tonic-gate 3040Sstevel@tonic-gate case ND_ROUTER_ADVERT: 3050Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInRouterAdvertisements); 3060Sstevel@tonic-gate break; 3070Sstevel@tonic-gate 3080Sstevel@tonic-gate case ND_NEIGHBOR_SOLICIT: 3090Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInNeighborSolicits); 31011042SErik.Nordmark@Sun.COM ndp_input(mp, ira); 31111042SErik.Nordmark@Sun.COM return (NULL); 3120Sstevel@tonic-gate 3130Sstevel@tonic-gate case ND_NEIGHBOR_ADVERT: 3140Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, 3150Sstevel@tonic-gate ipv6IfIcmpInNeighborAdvertisements); 31611042SErik.Nordmark@Sun.COM ndp_input(mp, ira); 31711042SErik.Nordmark@Sun.COM return (NULL); 31811042SErik.Nordmark@Sun.COM 31911042SErik.Nordmark@Sun.COM case ND_REDIRECT: 3200Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInRedirects); 3210Sstevel@tonic-gate 3223448Sdh155122 if (ipst->ips_ipv6_ignore_redirect) 3230Sstevel@tonic-gate break; 3240Sstevel@tonic-gate 32511042SErik.Nordmark@Sun.COM /* We now allow a RAW socket to receive this. */ 32611042SErik.Nordmark@Sun.COM interested = B_TRUE; 32711042SErik.Nordmark@Sun.COM break; 3280Sstevel@tonic-gate 3290Sstevel@tonic-gate /* 3300Sstevel@tonic-gate * The next three icmp messages will be handled by MLD. 3310Sstevel@tonic-gate * Pass all valid MLD packets up to any process(es) 33211042SErik.Nordmark@Sun.COM * listening on a raw ICMP socket. 3330Sstevel@tonic-gate */ 3340Sstevel@tonic-gate case MLD_LISTENER_QUERY: 3350Sstevel@tonic-gate case MLD_LISTENER_REPORT: 3360Sstevel@tonic-gate case MLD_LISTENER_REDUCTION: 33711042SErik.Nordmark@Sun.COM mp = mld_input(mp, ira); 33811042SErik.Nordmark@Sun.COM return (mp); 3390Sstevel@tonic-gate default: 3400Sstevel@tonic-gate break; 3410Sstevel@tonic-gate } 34211042SErik.Nordmark@Sun.COM /* 34311042SErik.Nordmark@Sun.COM * See if there is an ICMP client to avoid an extra copymsg/freemsg 34411042SErik.Nordmark@Sun.COM * if there isn't one. 34511042SErik.Nordmark@Sun.COM */ 34611042SErik.Nordmark@Sun.COM if (ipst->ips_ipcl_proto_fanout_v6[IPPROTO_ICMPV6].connf_head != NULL) { 34711042SErik.Nordmark@Sun.COM /* If there is an ICMP client and we want one too, copy it. */ 34811042SErik.Nordmark@Sun.COM 34911042SErik.Nordmark@Sun.COM if (!interested) { 35011042SErik.Nordmark@Sun.COM /* Caller will deliver to RAW sockets */ 35111042SErik.Nordmark@Sun.COM return (mp); 35211042SErik.Nordmark@Sun.COM } 35311042SErik.Nordmark@Sun.COM mp_ret = copymsg(mp); 35411042SErik.Nordmark@Sun.COM if (mp_ret == NULL) { 35511042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 35611042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards - copymsg", mp, ill); 35711042SErik.Nordmark@Sun.COM } 35811042SErik.Nordmark@Sun.COM } else if (!interested) { 35911042SErik.Nordmark@Sun.COM /* Neither we nor raw sockets are interested. Drop packet now */ 36011042SErik.Nordmark@Sun.COM freemsg(mp); 36111042SErik.Nordmark@Sun.COM return (NULL); 3620Sstevel@tonic-gate } 36311042SErik.Nordmark@Sun.COM 3640Sstevel@tonic-gate /* 36511042SErik.Nordmark@Sun.COM * ICMP error or redirect packet. Make sure we have enough of 36611042SErik.Nordmark@Sun.COM * the header and that db_ref == 1 since we might end up modifying 36711042SErik.Nordmark@Sun.COM * the packet. 3680Sstevel@tonic-gate */ 36911042SErik.Nordmark@Sun.COM if (mp->b_cont != NULL) { 37011042SErik.Nordmark@Sun.COM if (ip_pullup(mp, -1, ira) == NULL) { 37111042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 37211042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards - ip_pullup", 37311042SErik.Nordmark@Sun.COM mp, ill); 37411042SErik.Nordmark@Sun.COM freemsg(mp); 37511042SErik.Nordmark@Sun.COM return (mp_ret); 37611042SErik.Nordmark@Sun.COM } 37711042SErik.Nordmark@Sun.COM } 37811042SErik.Nordmark@Sun.COM 3790Sstevel@tonic-gate if (mp->b_datap->db_ref > 1) { 3800Sstevel@tonic-gate mblk_t *mp1; 3810Sstevel@tonic-gate 3820Sstevel@tonic-gate mp1 = copymsg(mp); 3830Sstevel@tonic-gate if (mp1 == NULL) { 3843284Sapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 38511042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards - copymsg", mp, ill); 38611042SErik.Nordmark@Sun.COM freemsg(mp); 38711042SErik.Nordmark@Sun.COM return (mp_ret); 3880Sstevel@tonic-gate } 38911042SErik.Nordmark@Sun.COM freemsg(mp); 3900Sstevel@tonic-gate mp = mp1; 3910Sstevel@tonic-gate } 3920Sstevel@tonic-gate 3930Sstevel@tonic-gate /* 39411042SErik.Nordmark@Sun.COM * In case mp has changed, verify the message before any further 39511042SErik.Nordmark@Sun.COM * processes. 39611042SErik.Nordmark@Sun.COM */ 39711042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr; 39811042SErik.Nordmark@Sun.COM icmp6 = (icmp6_t *)(&mp->b_rptr[ip_hdr_length]); 39911042SErik.Nordmark@Sun.COM if (!icmp_inbound_verify_v6(mp, icmp6, ira)) { 40011042SErik.Nordmark@Sun.COM freemsg(mp); 40111042SErik.Nordmark@Sun.COM return (mp_ret); 40211042SErik.Nordmark@Sun.COM } 40311042SErik.Nordmark@Sun.COM 40411042SErik.Nordmark@Sun.COM switch (icmp6->icmp6_type) { 40511042SErik.Nordmark@Sun.COM case ND_REDIRECT: 40611042SErik.Nordmark@Sun.COM icmp_redirect_v6(mp, ip6h, (nd_redirect_t *)icmp6, ira); 40711042SErik.Nordmark@Sun.COM break; 40811042SErik.Nordmark@Sun.COM case ICMP6_PACKET_TOO_BIG: 40911042SErik.Nordmark@Sun.COM /* Update DCE and adjust MTU is icmp header if needed */ 41011042SErik.Nordmark@Sun.COM icmp_inbound_too_big_v6(icmp6, ira); 41111042SErik.Nordmark@Sun.COM /* FALLTHRU */ 41211042SErik.Nordmark@Sun.COM default: 41311042SErik.Nordmark@Sun.COM icmp_inbound_error_fanout_v6(mp, icmp6, ira); 41411042SErik.Nordmark@Sun.COM break; 41511042SErik.Nordmark@Sun.COM } 41611042SErik.Nordmark@Sun.COM 41711042SErik.Nordmark@Sun.COM return (mp_ret); 41811042SErik.Nordmark@Sun.COM } 41911042SErik.Nordmark@Sun.COM 42011042SErik.Nordmark@Sun.COM /* 42111042SErik.Nordmark@Sun.COM * Send an ICMP echo reply. 42211042SErik.Nordmark@Sun.COM * The caller has already updated the payload part of the packet. 42311042SErik.Nordmark@Sun.COM * We handle the ICMP checksum, IP source address selection and feed 42411042SErik.Nordmark@Sun.COM * the packet into ip_output_simple. 42511042SErik.Nordmark@Sun.COM */ 42611042SErik.Nordmark@Sun.COM static void 42711042SErik.Nordmark@Sun.COM icmp_send_reply_v6(mblk_t *mp, ip6_t *ip6h, icmp6_t *icmp6, 42811042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira) 42911042SErik.Nordmark@Sun.COM { 43011042SErik.Nordmark@Sun.COM uint_t ip_hdr_length = ira->ira_ip_hdr_length; 43111042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; 43211042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst; 43311042SErik.Nordmark@Sun.COM ip_xmit_attr_t ixas; 43411042SErik.Nordmark@Sun.COM in6_addr_t origsrc; 43511042SErik.Nordmark@Sun.COM 43611042SErik.Nordmark@Sun.COM /* 43711042SErik.Nordmark@Sun.COM * Remove any extension headers (do not reverse a source route) 43811042SErik.Nordmark@Sun.COM * and clear the flow id (keep traffic class for now). 43911042SErik.Nordmark@Sun.COM */ 44011042SErik.Nordmark@Sun.COM if (ip_hdr_length != IPV6_HDR_LEN) { 44111042SErik.Nordmark@Sun.COM int i; 44211042SErik.Nordmark@Sun.COM 44311042SErik.Nordmark@Sun.COM for (i = 0; i < IPV6_HDR_LEN; i++) { 44411042SErik.Nordmark@Sun.COM mp->b_rptr[ip_hdr_length - i - 1] = 44511042SErik.Nordmark@Sun.COM mp->b_rptr[IPV6_HDR_LEN - i - 1]; 44611042SErik.Nordmark@Sun.COM } 44711042SErik.Nordmark@Sun.COM mp->b_rptr += (ip_hdr_length - IPV6_HDR_LEN); 44811042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr; 44911042SErik.Nordmark@Sun.COM ip6h->ip6_nxt = IPPROTO_ICMPV6; 45011042SErik.Nordmark@Sun.COM i = ntohs(ip6h->ip6_plen); 45111042SErik.Nordmark@Sun.COM i -= (ip_hdr_length - IPV6_HDR_LEN); 45211042SErik.Nordmark@Sun.COM ip6h->ip6_plen = htons(i); 45311042SErik.Nordmark@Sun.COM ip_hdr_length = IPV6_HDR_LEN; 45411042SErik.Nordmark@Sun.COM ASSERT(ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN == msgdsize(mp)); 45511042SErik.Nordmark@Sun.COM } 45611042SErik.Nordmark@Sun.COM ip6h->ip6_vcf &= ~IPV6_FLOWINFO_FLOWLABEL; 45711042SErik.Nordmark@Sun.COM 45811042SErik.Nordmark@Sun.COM /* Reverse the source and destination addresses. */ 45911042SErik.Nordmark@Sun.COM origsrc = ip6h->ip6_src; 46011042SErik.Nordmark@Sun.COM ip6h->ip6_src = ip6h->ip6_dst; 46111042SErik.Nordmark@Sun.COM ip6h->ip6_dst = origsrc; 46211042SErik.Nordmark@Sun.COM 46311042SErik.Nordmark@Sun.COM /* set the hop limit */ 46411042SErik.Nordmark@Sun.COM ip6h->ip6_hops = ipst->ips_ipv6_def_hops; 46511042SErik.Nordmark@Sun.COM 46611042SErik.Nordmark@Sun.COM /* 46711042SErik.Nordmark@Sun.COM * Prepare for checksum by putting icmp length in the icmp 46811042SErik.Nordmark@Sun.COM * checksum field. The checksum is calculated in ip_output 46911042SErik.Nordmark@Sun.COM */ 47011042SErik.Nordmark@Sun.COM icmp6->icmp6_cksum = ip6h->ip6_plen; 47111042SErik.Nordmark@Sun.COM 47211042SErik.Nordmark@Sun.COM bzero(&ixas, sizeof (ixas)); 47311042SErik.Nordmark@Sun.COM ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6; 47411042SErik.Nordmark@Sun.COM ixas.ixa_zoneid = ira->ira_zoneid; 47511042SErik.Nordmark@Sun.COM ixas.ixa_cred = kcred; 47611042SErik.Nordmark@Sun.COM ixas.ixa_cpid = NOPID; 47711042SErik.Nordmark@Sun.COM ixas.ixa_tsl = ira->ira_tsl; /* Behave as a multi-level responder */ 47811042SErik.Nordmark@Sun.COM ixas.ixa_ifindex = 0; 47911042SErik.Nordmark@Sun.COM ixas.ixa_ipst = ipst; 48011042SErik.Nordmark@Sun.COM ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; 48111042SErik.Nordmark@Sun.COM 48211042SErik.Nordmark@Sun.COM if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) { 48311042SErik.Nordmark@Sun.COM /* 48411042SErik.Nordmark@Sun.COM * This packet should go out the same way as it 48511042SErik.Nordmark@Sun.COM * came in i.e in clear, independent of the IPsec 48611042SErik.Nordmark@Sun.COM * policy for transmitting packets. 48711042SErik.Nordmark@Sun.COM */ 48811042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_NO_IPSEC; 48911042SErik.Nordmark@Sun.COM } else { 49011042SErik.Nordmark@Sun.COM if (!ipsec_in_to_out(ira, &ixas, mp, NULL, ip6h)) { 49111042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 49211042SErik.Nordmark@Sun.COM /* Note: mp already consumed and ip_drop_packet done */ 49311042SErik.Nordmark@Sun.COM return; 49411042SErik.Nordmark@Sun.COM } 49511042SErik.Nordmark@Sun.COM } 49611042SErik.Nordmark@Sun.COM 49711042SErik.Nordmark@Sun.COM /* Was the destination (now source) link-local? Send out same group */ 49811042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_src)) { 49911042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_SCOPEID_SET; 50011042SErik.Nordmark@Sun.COM if (IS_UNDER_IPMP(ill)) 50111042SErik.Nordmark@Sun.COM ixas.ixa_scopeid = ill_get_upper_ifindex(ill); 50211042SErik.Nordmark@Sun.COM else 50311042SErik.Nordmark@Sun.COM ixas.ixa_scopeid = ill->ill_phyint->phyint_ifindex; 50411042SErik.Nordmark@Sun.COM } 50511042SErik.Nordmark@Sun.COM 50611042SErik.Nordmark@Sun.COM if (ira->ira_flags & IRAF_MULTIBROADCAST) { 50711042SErik.Nordmark@Sun.COM /* 50811042SErik.Nordmark@Sun.COM * Not one or our addresses (IRE_LOCALs), thus we let 50911042SErik.Nordmark@Sun.COM * ip_output_simple pick the source. 51011042SErik.Nordmark@Sun.COM */ 51111042SErik.Nordmark@Sun.COM ip6h->ip6_src = ipv6_all_zeros; 51211042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_SET_SOURCE; 51311042SErik.Nordmark@Sun.COM } 51411042SErik.Nordmark@Sun.COM 51511042SErik.Nordmark@Sun.COM /* Should we send using dce_pmtu? */ 51611042SErik.Nordmark@Sun.COM if (ipst->ips_ipv6_icmp_return_pmtu) 51711042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_PMTU_DISCOVERY; 51811042SErik.Nordmark@Sun.COM 51911042SErik.Nordmark@Sun.COM (void) ip_output_simple(mp, &ixas); 52011042SErik.Nordmark@Sun.COM ixa_cleanup(&ixas); 52111042SErik.Nordmark@Sun.COM 52211042SErik.Nordmark@Sun.COM } 52311042SErik.Nordmark@Sun.COM 52411042SErik.Nordmark@Sun.COM /* 52511042SErik.Nordmark@Sun.COM * Verify the ICMP messages for either for ICMP error or redirect packet. 52611042SErik.Nordmark@Sun.COM * The caller should have fully pulled up the message. If it's a redirect 52711042SErik.Nordmark@Sun.COM * packet, only basic checks on IP header will be done; otherwise, verify 52811042SErik.Nordmark@Sun.COM * the packet by looking at the included ULP header. 52911042SErik.Nordmark@Sun.COM * 53011042SErik.Nordmark@Sun.COM * Called before icmp_inbound_error_fanout_v6 is called. 53111042SErik.Nordmark@Sun.COM */ 53211042SErik.Nordmark@Sun.COM static boolean_t 53311042SErik.Nordmark@Sun.COM icmp_inbound_verify_v6(mblk_t *mp, icmp6_t *icmp6, ip_recv_attr_t *ira) 53411042SErik.Nordmark@Sun.COM { 53511042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; 53611042SErik.Nordmark@Sun.COM uint16_t hdr_length; 53711042SErik.Nordmark@Sun.COM uint8_t *nexthdrp; 53811042SErik.Nordmark@Sun.COM uint8_t nexthdr; 53911042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst; 54011042SErik.Nordmark@Sun.COM conn_t *connp; 54111042SErik.Nordmark@Sun.COM ip6_t *ip6h; /* Inner header */ 54211042SErik.Nordmark@Sun.COM 54311042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)&icmp6[1]; 54411042SErik.Nordmark@Sun.COM if ((uchar_t *)ip6h + IPV6_HDR_LEN > mp->b_wptr) 54511042SErik.Nordmark@Sun.COM goto truncated; 54611042SErik.Nordmark@Sun.COM 54711042SErik.Nordmark@Sun.COM if (icmp6->icmp6_type == ND_REDIRECT) { 54811042SErik.Nordmark@Sun.COM hdr_length = sizeof (nd_redirect_t); 54911042SErik.Nordmark@Sun.COM } else { 55011042SErik.Nordmark@Sun.COM if ((IPH_HDR_VERSION(ip6h) != IPV6_VERSION)) 55111042SErik.Nordmark@Sun.COM goto discard_pkt; 55211042SErik.Nordmark@Sun.COM hdr_length = IPV6_HDR_LEN; 55311042SErik.Nordmark@Sun.COM } 55411042SErik.Nordmark@Sun.COM 55511042SErik.Nordmark@Sun.COM if ((uchar_t *)ip6h + hdr_length > mp->b_wptr) 55611042SErik.Nordmark@Sun.COM goto truncated; 55711042SErik.Nordmark@Sun.COM 55811042SErik.Nordmark@Sun.COM /* 55911042SErik.Nordmark@Sun.COM * Stop here for ICMP_REDIRECT. 56011042SErik.Nordmark@Sun.COM */ 56111042SErik.Nordmark@Sun.COM if (icmp6->icmp6_type == ND_REDIRECT) 56211042SErik.Nordmark@Sun.COM return (B_TRUE); 56311042SErik.Nordmark@Sun.COM 56411042SErik.Nordmark@Sun.COM /* 56511042SErik.Nordmark@Sun.COM * ICMP errors only. 56611042SErik.Nordmark@Sun.COM */ 56711042SErik.Nordmark@Sun.COM if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp)) 56811042SErik.Nordmark@Sun.COM goto discard_pkt; 56911042SErik.Nordmark@Sun.COM nexthdr = *nexthdrp; 57011042SErik.Nordmark@Sun.COM 57111042SErik.Nordmark@Sun.COM /* Try to pass the ICMP message to clients who need it */ 57211042SErik.Nordmark@Sun.COM switch (nexthdr) { 57311042SErik.Nordmark@Sun.COM case IPPROTO_UDP: 57411042SErik.Nordmark@Sun.COM /* 57511042SErik.Nordmark@Sun.COM * Verify we have at least ICMP_MIN_TP_HDR_LEN bytes of 57611042SErik.Nordmark@Sun.COM * transport header. 57711042SErik.Nordmark@Sun.COM */ 57811042SErik.Nordmark@Sun.COM if ((uchar_t *)ip6h + hdr_length + ICMP_MIN_TP_HDR_LEN > 57911042SErik.Nordmark@Sun.COM mp->b_wptr) 58011042SErik.Nordmark@Sun.COM goto truncated; 58111042SErik.Nordmark@Sun.COM break; 58211042SErik.Nordmark@Sun.COM case IPPROTO_TCP: { 58311042SErik.Nordmark@Sun.COM tcpha_t *tcpha; 58411042SErik.Nordmark@Sun.COM 58511042SErik.Nordmark@Sun.COM /* 58611042SErik.Nordmark@Sun.COM * Verify we have at least ICMP_MIN_TP_HDR_LEN bytes of 58711042SErik.Nordmark@Sun.COM * transport header. 58811042SErik.Nordmark@Sun.COM */ 58911042SErik.Nordmark@Sun.COM if ((uchar_t *)ip6h + hdr_length + ICMP_MIN_TP_HDR_LEN > 59011042SErik.Nordmark@Sun.COM mp->b_wptr) 59111042SErik.Nordmark@Sun.COM goto truncated; 59211042SErik.Nordmark@Sun.COM 59311042SErik.Nordmark@Sun.COM tcpha = (tcpha_t *)((uchar_t *)ip6h + hdr_length); 59411042SErik.Nordmark@Sun.COM /* 59511042SErik.Nordmark@Sun.COM * With IPMP we need to match across group, which we do 59611042SErik.Nordmark@Sun.COM * since we have the upper ill from ira_ill. 59711042SErik.Nordmark@Sun.COM */ 59811042SErik.Nordmark@Sun.COM connp = ipcl_tcp_lookup_reversed_ipv6(ip6h, tcpha, TCPS_LISTEN, 59911042SErik.Nordmark@Sun.COM ill->ill_phyint->phyint_ifindex, ipst); 60011042SErik.Nordmark@Sun.COM if (connp == NULL) 60111042SErik.Nordmark@Sun.COM goto discard_pkt; 60211042SErik.Nordmark@Sun.COM 60311042SErik.Nordmark@Sun.COM if ((connp->conn_verifyicmp != NULL) && 60411042SErik.Nordmark@Sun.COM !connp->conn_verifyicmp(connp, tcpha, NULL, icmp6, ira)) { 60511042SErik.Nordmark@Sun.COM CONN_DEC_REF(connp); 60611042SErik.Nordmark@Sun.COM goto discard_pkt; 60711042SErik.Nordmark@Sun.COM } 60811042SErik.Nordmark@Sun.COM CONN_DEC_REF(connp); 60911042SErik.Nordmark@Sun.COM break; 61011042SErik.Nordmark@Sun.COM } 61111042SErik.Nordmark@Sun.COM case IPPROTO_SCTP: 61211042SErik.Nordmark@Sun.COM /* 61311042SErik.Nordmark@Sun.COM * Verify we have at least ICMP_MIN_TP_HDR_LEN bytes of 61411042SErik.Nordmark@Sun.COM * transport header. 61511042SErik.Nordmark@Sun.COM */ 61611042SErik.Nordmark@Sun.COM if ((uchar_t *)ip6h + hdr_length + ICMP_MIN_TP_HDR_LEN > 61711042SErik.Nordmark@Sun.COM mp->b_wptr) 61811042SErik.Nordmark@Sun.COM goto truncated; 61911042SErik.Nordmark@Sun.COM break; 62011042SErik.Nordmark@Sun.COM case IPPROTO_ESP: 62111042SErik.Nordmark@Sun.COM case IPPROTO_AH: 62211042SErik.Nordmark@Sun.COM break; 62311042SErik.Nordmark@Sun.COM case IPPROTO_ENCAP: 62411042SErik.Nordmark@Sun.COM case IPPROTO_IPV6: { 62511042SErik.Nordmark@Sun.COM /* Look for self-encapsulated packets that caused an error */ 62611042SErik.Nordmark@Sun.COM ip6_t *in_ip6h; 62711042SErik.Nordmark@Sun.COM 62811042SErik.Nordmark@Sun.COM in_ip6h = (ip6_t *)((uint8_t *)ip6h + hdr_length); 62911042SErik.Nordmark@Sun.COM if ((uint8_t *)in_ip6h + (nexthdr == IPPROTO_ENCAP ? 63011042SErik.Nordmark@Sun.COM sizeof (ipha_t) : sizeof (ip6_t)) > mp->b_wptr) 63111042SErik.Nordmark@Sun.COM goto truncated; 63211042SErik.Nordmark@Sun.COM break; 63311042SErik.Nordmark@Sun.COM } 63411042SErik.Nordmark@Sun.COM default: 63511042SErik.Nordmark@Sun.COM break; 63611042SErik.Nordmark@Sun.COM } 63711042SErik.Nordmark@Sun.COM 63811042SErik.Nordmark@Sun.COM return (B_TRUE); 63911042SErik.Nordmark@Sun.COM 64011042SErik.Nordmark@Sun.COM discard_pkt: 64111042SErik.Nordmark@Sun.COM /* Bogus ICMP error. */ 64211042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 64311042SErik.Nordmark@Sun.COM return (B_FALSE); 64411042SErik.Nordmark@Sun.COM 64511042SErik.Nordmark@Sun.COM truncated: 64611042SErik.Nordmark@Sun.COM /* We pulled up everthing already. Must be truncated */ 64711042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors); 64811042SErik.Nordmark@Sun.COM return (B_FALSE); 64911042SErik.Nordmark@Sun.COM } 65011042SErik.Nordmark@Sun.COM 65111042SErik.Nordmark@Sun.COM /* 65211042SErik.Nordmark@Sun.COM * Process received IPv6 ICMP Packet too big. 65311042SErik.Nordmark@Sun.COM * The caller is responsible for validating the packet before passing it in 65411042SErik.Nordmark@Sun.COM * and also to fanout the ICMP error to any matching transport conns. Assumes 65511042SErik.Nordmark@Sun.COM * the message has been fully pulled up. 65611042SErik.Nordmark@Sun.COM * 65711042SErik.Nordmark@Sun.COM * Before getting here, the caller has called icmp_inbound_verify_v6() 65811042SErik.Nordmark@Sun.COM * that should have verified with ULP to prevent undoing the changes we're 65911042SErik.Nordmark@Sun.COM * going to make to DCE. For example, TCP might have verified that the packet 66011042SErik.Nordmark@Sun.COM * which generated error is in the send window. 66111042SErik.Nordmark@Sun.COM * 66211042SErik.Nordmark@Sun.COM * In some cases modified this MTU in the ICMP header packet; the caller 66311042SErik.Nordmark@Sun.COM * should pass to the matching ULP after this returns. 66411042SErik.Nordmark@Sun.COM */ 66511042SErik.Nordmark@Sun.COM static void 66611042SErik.Nordmark@Sun.COM icmp_inbound_too_big_v6(icmp6_t *icmp6, ip_recv_attr_t *ira) 66711042SErik.Nordmark@Sun.COM { 66811042SErik.Nordmark@Sun.COM uint32_t mtu; 66911042SErik.Nordmark@Sun.COM dce_t *dce; 67011042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; /* Upper ill if IPMP */ 67111042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst; 67211042SErik.Nordmark@Sun.COM int old_max_frag; 67311042SErik.Nordmark@Sun.COM in6_addr_t final_dst; 67411042SErik.Nordmark@Sun.COM ip6_t *ip6h; /* Inner IP header */ 67511042SErik.Nordmark@Sun.COM 67611042SErik.Nordmark@Sun.COM /* Caller has already pulled up everything. */ 67711042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)&icmp6[1]; 67811042SErik.Nordmark@Sun.COM final_dst = ip_get_dst_v6(ip6h, NULL, NULL); 67911042SErik.Nordmark@Sun.COM 68011042SErik.Nordmark@Sun.COM /* 68111042SErik.Nordmark@Sun.COM * For link local destinations matching simply on address is not 6820Sstevel@tonic-gate * sufficient. Same link local addresses for different ILL's is 6830Sstevel@tonic-gate * possible. 6840Sstevel@tonic-gate */ 68511042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_LINKSCOPE(&final_dst)) { 68611042SErik.Nordmark@Sun.COM dce = dce_lookup_and_add_v6(&final_dst, 68711042SErik.Nordmark@Sun.COM ill->ill_phyint->phyint_ifindex, ipst); 68811042SErik.Nordmark@Sun.COM } else { 68911042SErik.Nordmark@Sun.COM dce = dce_lookup_and_add_v6(&final_dst, 0, ipst); 69011042SErik.Nordmark@Sun.COM } 69111042SErik.Nordmark@Sun.COM if (dce == NULL) { 69211042SErik.Nordmark@Sun.COM /* Couldn't add a unique one - ENOMEM */ 69311042SErik.Nordmark@Sun.COM if (ip_debug > 2) { 69411042SErik.Nordmark@Sun.COM /* ip1dbg */ 69511042SErik.Nordmark@Sun.COM pr_addr_dbg("icmp_inbound_too_big_v6:" 69611042SErik.Nordmark@Sun.COM "no dce for dst %s\n", AF_INET6, 69711042SErik.Nordmark@Sun.COM &final_dst); 6980Sstevel@tonic-gate } 69911042SErik.Nordmark@Sun.COM return; 70011042SErik.Nordmark@Sun.COM } 70111042SErik.Nordmark@Sun.COM 70211042SErik.Nordmark@Sun.COM mtu = ntohl(icmp6->icmp6_mtu); 70311042SErik.Nordmark@Sun.COM 70411042SErik.Nordmark@Sun.COM mutex_enter(&dce->dce_lock); 70511042SErik.Nordmark@Sun.COM if (dce->dce_flags & DCEF_PMTU) 70611042SErik.Nordmark@Sun.COM old_max_frag = dce->dce_pmtu; 70711042SErik.Nordmark@Sun.COM else 70811042SErik.Nordmark@Sun.COM old_max_frag = ill->ill_mtu; 70911042SErik.Nordmark@Sun.COM 71011042SErik.Nordmark@Sun.COM if (mtu < IPV6_MIN_MTU) { 71111042SErik.Nordmark@Sun.COM ip1dbg(("Received mtu less than IPv6 " 71211042SErik.Nordmark@Sun.COM "min mtu %d: %d\n", IPV6_MIN_MTU, mtu)); 71311042SErik.Nordmark@Sun.COM mtu = IPV6_MIN_MTU; 7140Sstevel@tonic-gate /* 71511042SErik.Nordmark@Sun.COM * If an mtu less than IPv6 min mtu is received, 71611042SErik.Nordmark@Sun.COM * we must include a fragment header in 71711042SErik.Nordmark@Sun.COM * subsequent packets. 7180Sstevel@tonic-gate */ 71911042SErik.Nordmark@Sun.COM dce->dce_flags |= DCEF_TOO_SMALL_PMTU; 72011042SErik.Nordmark@Sun.COM } else { 72111042SErik.Nordmark@Sun.COM dce->dce_flags &= ~DCEF_TOO_SMALL_PMTU; 7220Sstevel@tonic-gate } 72311042SErik.Nordmark@Sun.COM ip1dbg(("Received mtu from router: %d\n", mtu)); 72411042SErik.Nordmark@Sun.COM dce->dce_pmtu = MIN(old_max_frag, mtu); 72511042SErik.Nordmark@Sun.COM 72611042SErik.Nordmark@Sun.COM /* Prepare to send the new max frag size for the ULP. */ 72711042SErik.Nordmark@Sun.COM if (dce->dce_flags & DCEF_TOO_SMALL_PMTU) { 72811042SErik.Nordmark@Sun.COM /* 72911042SErik.Nordmark@Sun.COM * If we need a fragment header in every packet 73011042SErik.Nordmark@Sun.COM * (above case or multirouting), make sure the 73111042SErik.Nordmark@Sun.COM * ULP takes it into account when computing the 73211042SErik.Nordmark@Sun.COM * payload size. 73311042SErik.Nordmark@Sun.COM */ 73411042SErik.Nordmark@Sun.COM icmp6->icmp6_mtu = htonl(dce->dce_pmtu - sizeof (ip6_frag_t)); 73511042SErik.Nordmark@Sun.COM } else { 73611042SErik.Nordmark@Sun.COM icmp6->icmp6_mtu = htonl(dce->dce_pmtu); 73711042SErik.Nordmark@Sun.COM } 73811042SErik.Nordmark@Sun.COM /* We now have a PMTU for sure */ 73911042SErik.Nordmark@Sun.COM dce->dce_flags |= DCEF_PMTU; 74011066Srafael.vanoni@sun.com dce->dce_last_change_time = TICK_TO_SEC(ddi_get_lbolt64()); 74111042SErik.Nordmark@Sun.COM mutex_exit(&dce->dce_lock); 74211042SErik.Nordmark@Sun.COM /* 74311042SErik.Nordmark@Sun.COM * After dropping the lock the new value is visible to everyone. 74411042SErik.Nordmark@Sun.COM * Then we bump the generation number so any cached values reinspect 74511042SErik.Nordmark@Sun.COM * the dce_t. 74611042SErik.Nordmark@Sun.COM */ 74711042SErik.Nordmark@Sun.COM dce_increment_generation(dce); 74811042SErik.Nordmark@Sun.COM dce_refrele(dce); 74910616SSebastien.Roy@Sun.COM } 75010616SSebastien.Roy@Sun.COM 75110616SSebastien.Roy@Sun.COM /* 7520Sstevel@tonic-gate * Fanout received ICMPv6 error packets to the transports. 7530Sstevel@tonic-gate * Assumes the IPv6 plus ICMPv6 headers have been pulled up but nothing else. 75411042SErik.Nordmark@Sun.COM * 75511042SErik.Nordmark@Sun.COM * The caller must have called icmp_inbound_verify_v6. 7560Sstevel@tonic-gate */ 7570Sstevel@tonic-gate void 75811042SErik.Nordmark@Sun.COM icmp_inbound_error_fanout_v6(mblk_t *mp, icmp6_t *icmp6, ip_recv_attr_t *ira) 7590Sstevel@tonic-gate { 76011042SErik.Nordmark@Sun.COM uint16_t *up; /* Pointer to ports in ULP header */ 76111042SErik.Nordmark@Sun.COM uint32_t ports; /* reversed ports for fanout */ 76211042SErik.Nordmark@Sun.COM ip6_t rip6h; /* With reversed addresses */ 76311042SErik.Nordmark@Sun.COM ip6_t *ip6h; /* Inner IP header */ 76411042SErik.Nordmark@Sun.COM uint16_t hdr_length; /* Inner IP header length */ 7650Sstevel@tonic-gate uint8_t *nexthdrp; 7660Sstevel@tonic-gate uint8_t nexthdr; 76711042SErik.Nordmark@Sun.COM tcpha_t *tcpha; 76811042SErik.Nordmark@Sun.COM conn_t *connp; 76911042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; /* Upper in the case of IPMP */ 7703448Sdh155122 ip_stack_t *ipst = ill->ill_ipst; 77111042SErik.Nordmark@Sun.COM ipsec_stack_t *ipss = ipst->ips_netstack->netstack_ipsec; 77211042SErik.Nordmark@Sun.COM 77311042SErik.Nordmark@Sun.COM /* Caller has already pulled up everything. */ 77411042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)&icmp6[1]; 77511042SErik.Nordmark@Sun.COM ASSERT(mp->b_cont == NULL); 77611042SErik.Nordmark@Sun.COM ASSERT((uchar_t *)&ip6h[1] <= mp->b_wptr); 7770Sstevel@tonic-gate 7780Sstevel@tonic-gate if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp)) 7790Sstevel@tonic-gate goto drop_pkt; 7800Sstevel@tonic-gate nexthdr = *nexthdrp; 78111042SErik.Nordmark@Sun.COM ira->ira_protocol = nexthdr; 7820Sstevel@tonic-gate 78310616SSebastien.Roy@Sun.COM /* 78410616SSebastien.Roy@Sun.COM * We need a separate IP header with the source and destination 78510616SSebastien.Roy@Sun.COM * addresses reversed to do fanout/classification because the ip6h in 78610616SSebastien.Roy@Sun.COM * the ICMPv6 error is in the form we sent it out. 78710616SSebastien.Roy@Sun.COM */ 78810616SSebastien.Roy@Sun.COM rip6h.ip6_src = ip6h->ip6_dst; 78910616SSebastien.Roy@Sun.COM rip6h.ip6_dst = ip6h->ip6_src; 79010616SSebastien.Roy@Sun.COM rip6h.ip6_nxt = nexthdr; 79110616SSebastien.Roy@Sun.COM 7920Sstevel@tonic-gate /* Try to pass the ICMP message to clients who need it */ 7930Sstevel@tonic-gate switch (nexthdr) { 7940Sstevel@tonic-gate case IPPROTO_UDP: { 79510616SSebastien.Roy@Sun.COM /* Attempt to find a client stream based on port. */ 7960Sstevel@tonic-gate up = (uint16_t *)((uchar_t *)ip6h + hdr_length); 79711042SErik.Nordmark@Sun.COM 79811042SErik.Nordmark@Sun.COM /* Note that we send error to all matches. */ 79911042SErik.Nordmark@Sun.COM ira->ira_flags |= IRAF_ICMP_ERROR; 80011042SErik.Nordmark@Sun.COM ip_fanout_udp_multi_v6(mp, &rip6h, up[0], up[1], ira); 80111042SErik.Nordmark@Sun.COM ira->ira_flags &= ~IRAF_ICMP_ERROR; 8020Sstevel@tonic-gate return; 8030Sstevel@tonic-gate } 8040Sstevel@tonic-gate case IPPROTO_TCP: { 8050Sstevel@tonic-gate /* 8060Sstevel@tonic-gate * Attempt to find a client stream based on port. 8070Sstevel@tonic-gate * Note that we do a reverse lookup since the header is 8080Sstevel@tonic-gate * in the form we sent it out. 80910616SSebastien.Roy@Sun.COM */ 81011042SErik.Nordmark@Sun.COM tcpha = (tcpha_t *)((uchar_t *)ip6h + hdr_length); 81111042SErik.Nordmark@Sun.COM /* 81211042SErik.Nordmark@Sun.COM * With IPMP we need to match across group, which we do 81311042SErik.Nordmark@Sun.COM * since we have the upper ill from ira_ill. 81411042SErik.Nordmark@Sun.COM */ 8150Sstevel@tonic-gate connp = ipcl_tcp_lookup_reversed_ipv6(ip6h, tcpha, 8163448Sdh155122 TCPS_LISTEN, ill->ill_phyint->phyint_ifindex, ipst); 8170Sstevel@tonic-gate if (connp == NULL) { 8180Sstevel@tonic-gate goto drop_pkt; 8190Sstevel@tonic-gate } 8200Sstevel@tonic-gate 82111042SErik.Nordmark@Sun.COM if (CONN_INBOUND_POLICY_PRESENT_V6(connp, ipss) || 82211042SErik.Nordmark@Sun.COM (ira->ira_flags & IRAF_IPSEC_SECURE)) { 82311042SErik.Nordmark@Sun.COM mp = ipsec_check_inbound_policy(mp, connp, 82411042SErik.Nordmark@Sun.COM NULL, ip6h, ira); 82511042SErik.Nordmark@Sun.COM if (mp == NULL) { 82611042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 82711042SErik.Nordmark@Sun.COM /* Note that mp is NULL */ 82811042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards", mp, ill); 82911042SErik.Nordmark@Sun.COM CONN_DEC_REF(connp); 83011042SErik.Nordmark@Sun.COM return; 83111042SErik.Nordmark@Sun.COM } 83211042SErik.Nordmark@Sun.COM } 83311042SErik.Nordmark@Sun.COM 83411042SErik.Nordmark@Sun.COM ira->ira_flags |= IRAF_ICMP_ERROR; 83511042SErik.Nordmark@Sun.COM if (IPCL_IS_TCP(connp)) { 83611042SErik.Nordmark@Sun.COM SQUEUE_ENTER_ONE(connp->conn_sqp, mp, 83711042SErik.Nordmark@Sun.COM connp->conn_recvicmp, connp, ira, SQ_FILL, 83811042SErik.Nordmark@Sun.COM SQTAG_TCP6_INPUT_ICMP_ERR); 83911042SErik.Nordmark@Sun.COM } else { 84011042SErik.Nordmark@Sun.COM /* Not TCP; must be SOCK_RAW, IPPROTO_TCP */ 84111042SErik.Nordmark@Sun.COM ill_t *rill = ira->ira_rill; 84211042SErik.Nordmark@Sun.COM 84311042SErik.Nordmark@Sun.COM ira->ira_ill = ira->ira_rill = NULL; 84411042SErik.Nordmark@Sun.COM (connp->conn_recv)(connp, mp, NULL, ira); 84511042SErik.Nordmark@Sun.COM CONN_DEC_REF(connp); 84611042SErik.Nordmark@Sun.COM ira->ira_ill = ill; 84711042SErik.Nordmark@Sun.COM ira->ira_rill = rill; 84811042SErik.Nordmark@Sun.COM } 84911042SErik.Nordmark@Sun.COM ira->ira_flags &= ~IRAF_ICMP_ERROR; 8500Sstevel@tonic-gate return; 8510Sstevel@tonic-gate 8520Sstevel@tonic-gate } 8530Sstevel@tonic-gate case IPPROTO_SCTP: 8540Sstevel@tonic-gate up = (uint16_t *)((uchar_t *)ip6h + hdr_length); 85511042SErik.Nordmark@Sun.COM /* Find a SCTP client stream for this packet. */ 8560Sstevel@tonic-gate ((uint16_t *)&ports)[0] = up[1]; 8570Sstevel@tonic-gate ((uint16_t *)&ports)[1] = up[0]; 85811042SErik.Nordmark@Sun.COM 85911042SErik.Nordmark@Sun.COM ira->ira_flags |= IRAF_ICMP_ERROR; 86011042SErik.Nordmark@Sun.COM ip_fanout_sctp(mp, NULL, &rip6h, ports, ira); 86111042SErik.Nordmark@Sun.COM ira->ira_flags &= ~IRAF_ICMP_ERROR; 8620Sstevel@tonic-gate return; 86311042SErik.Nordmark@Sun.COM 8640Sstevel@tonic-gate case IPPROTO_ESP: 86511042SErik.Nordmark@Sun.COM case IPPROTO_AH: 8663448Sdh155122 if (!ipsec_loaded(ipss)) { 86711042SErik.Nordmark@Sun.COM ip_proto_not_sup(mp, ira); 8680Sstevel@tonic-gate return; 8690Sstevel@tonic-gate } 8700Sstevel@tonic-gate 8710Sstevel@tonic-gate if (nexthdr == IPPROTO_ESP) 87211042SErik.Nordmark@Sun.COM mp = ipsecesp_icmp_error(mp, ira); 8730Sstevel@tonic-gate else 87411042SErik.Nordmark@Sun.COM mp = ipsecah_icmp_error(mp, ira); 87511042SErik.Nordmark@Sun.COM if (mp == NULL) 8760Sstevel@tonic-gate return; 8770Sstevel@tonic-gate 87811042SErik.Nordmark@Sun.COM /* Just in case ipsec didn't preserve the NULL b_cont */ 87911042SErik.Nordmark@Sun.COM if (mp->b_cont != NULL) { 88011042SErik.Nordmark@Sun.COM if (!pullupmsg(mp, -1)) 88111042SErik.Nordmark@Sun.COM goto drop_pkt; 8824459Skcpoon } 8830Sstevel@tonic-gate 88411042SErik.Nordmark@Sun.COM /* 88511042SErik.Nordmark@Sun.COM * If succesful, the mp has been modified to not include 88611042SErik.Nordmark@Sun.COM * the ESP/AH header so we can fanout to the ULP's icmp 88711042SErik.Nordmark@Sun.COM * error handler. 88811042SErik.Nordmark@Sun.COM */ 88911042SErik.Nordmark@Sun.COM if (mp->b_wptr - mp->b_rptr < IPV6_HDR_LEN) 89011042SErik.Nordmark@Sun.COM goto drop_pkt; 89111042SErik.Nordmark@Sun.COM 89211042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr; 89311042SErik.Nordmark@Sun.COM /* Don't call hdr_length_v6() unless you have to. */ 89411042SErik.Nordmark@Sun.COM if (ip6h->ip6_nxt != IPPROTO_ICMPV6) 89511042SErik.Nordmark@Sun.COM hdr_length = ip_hdr_length_v6(mp, ip6h); 89611042SErik.Nordmark@Sun.COM else 89711042SErik.Nordmark@Sun.COM hdr_length = IPV6_HDR_LEN; 89811042SErik.Nordmark@Sun.COM 89911042SErik.Nordmark@Sun.COM /* Verify the modified message before any further processes. */ 90011042SErik.Nordmark@Sun.COM icmp6 = (icmp6_t *)(&mp->b_rptr[hdr_length]); 90111042SErik.Nordmark@Sun.COM if (!icmp_inbound_verify_v6(mp, icmp6, ira)) { 90211042SErik.Nordmark@Sun.COM freemsg(mp); 90311042SErik.Nordmark@Sun.COM return; 90411042SErik.Nordmark@Sun.COM } 90511042SErik.Nordmark@Sun.COM 90611042SErik.Nordmark@Sun.COM icmp_inbound_error_fanout_v6(mp, icmp6, ira); 90711042SErik.Nordmark@Sun.COM return; 90811042SErik.Nordmark@Sun.COM 90911042SErik.Nordmark@Sun.COM case IPPROTO_IPV6: { 91011042SErik.Nordmark@Sun.COM /* Look for self-encapsulated packets that caused an error */ 91111042SErik.Nordmark@Sun.COM ip6_t *in_ip6h; 91211042SErik.Nordmark@Sun.COM 91311042SErik.Nordmark@Sun.COM in_ip6h = (ip6_t *)((uint8_t *)ip6h + hdr_length); 91411042SErik.Nordmark@Sun.COM 91511042SErik.Nordmark@Sun.COM if (IN6_ARE_ADDR_EQUAL(&in_ip6h->ip6_src, &ip6h->ip6_src) && 91611042SErik.Nordmark@Sun.COM IN6_ARE_ADDR_EQUAL(&in_ip6h->ip6_dst, &ip6h->ip6_dst)) { 9170Sstevel@tonic-gate /* 9180Sstevel@tonic-gate * Self-encapsulated case. As in the ipv4 case, 9190Sstevel@tonic-gate * we need to strip the 2nd IP header. Since mp 9200Sstevel@tonic-gate * is already pulled-up, we can simply bcopy 9210Sstevel@tonic-gate * the 3rd header + data over the 2nd header. 9220Sstevel@tonic-gate */ 9230Sstevel@tonic-gate uint16_t unused_len; 9240Sstevel@tonic-gate 9250Sstevel@tonic-gate /* 9260Sstevel@tonic-gate * Make sure we don't do recursion more than once. 9270Sstevel@tonic-gate */ 92811042SErik.Nordmark@Sun.COM if (!ip_hdr_length_nexthdr_v6(mp, in_ip6h, 9290Sstevel@tonic-gate &unused_len, &nexthdrp) || 9300Sstevel@tonic-gate *nexthdrp == IPPROTO_IPV6) { 9310Sstevel@tonic-gate goto drop_pkt; 9320Sstevel@tonic-gate } 9330Sstevel@tonic-gate 9340Sstevel@tonic-gate /* 9350Sstevel@tonic-gate * Copy the 3rd header + remaining data on top 9360Sstevel@tonic-gate * of the 2nd header. 9370Sstevel@tonic-gate */ 93811042SErik.Nordmark@Sun.COM bcopy(in_ip6h, ip6h, mp->b_wptr - (uchar_t *)in_ip6h); 9390Sstevel@tonic-gate 9400Sstevel@tonic-gate /* 9410Sstevel@tonic-gate * Subtract length of the 2nd header. 9420Sstevel@tonic-gate */ 9430Sstevel@tonic-gate mp->b_wptr -= hdr_length; 9440Sstevel@tonic-gate 94511042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr; 94611042SErik.Nordmark@Sun.COM /* Don't call hdr_length_v6() unless you have to. */ 94711042SErik.Nordmark@Sun.COM if (ip6h->ip6_nxt != IPPROTO_ICMPV6) 94811042SErik.Nordmark@Sun.COM hdr_length = ip_hdr_length_v6(mp, ip6h); 94911042SErik.Nordmark@Sun.COM else 95011042SErik.Nordmark@Sun.COM hdr_length = IPV6_HDR_LEN; 95111042SErik.Nordmark@Sun.COM 95211042SErik.Nordmark@Sun.COM /* 95311042SErik.Nordmark@Sun.COM * Verify the modified message before any further 95411042SErik.Nordmark@Sun.COM * processes. 95511042SErik.Nordmark@Sun.COM */ 95611042SErik.Nordmark@Sun.COM icmp6 = (icmp6_t *)(&mp->b_rptr[hdr_length]); 95711042SErik.Nordmark@Sun.COM if (!icmp_inbound_verify_v6(mp, icmp6, ira)) { 95811042SErik.Nordmark@Sun.COM freemsg(mp); 95911042SErik.Nordmark@Sun.COM return; 96011042SErik.Nordmark@Sun.COM } 96111042SErik.Nordmark@Sun.COM 9620Sstevel@tonic-gate /* 9630Sstevel@tonic-gate * Now recurse, and see what I _really_ should be 9640Sstevel@tonic-gate * doing here. 9650Sstevel@tonic-gate */ 96611042SErik.Nordmark@Sun.COM icmp_inbound_error_fanout_v6(mp, icmp6, ira); 9670Sstevel@tonic-gate return; 9680Sstevel@tonic-gate } 96911042SErik.Nordmark@Sun.COM /* FALLTHRU */ 97011042SErik.Nordmark@Sun.COM } 97111042SErik.Nordmark@Sun.COM case IPPROTO_ENCAP: 97211042SErik.Nordmark@Sun.COM if ((connp = ipcl_iptun_classify_v6(&rip6h.ip6_src, 97311042SErik.Nordmark@Sun.COM &rip6h.ip6_dst, ipst)) != NULL) { 97411042SErik.Nordmark@Sun.COM ira->ira_flags |= IRAF_ICMP_ERROR; 97511042SErik.Nordmark@Sun.COM connp->conn_recvicmp(connp, mp, NULL, ira); 97611042SErik.Nordmark@Sun.COM CONN_DEC_REF(connp); 97711042SErik.Nordmark@Sun.COM ira->ira_flags &= ~IRAF_ICMP_ERROR; 97810616SSebastien.Roy@Sun.COM return; 97911042SErik.Nordmark@Sun.COM } 98010616SSebastien.Roy@Sun.COM /* 98111042SErik.Nordmark@Sun.COM * No IP tunnel is interested, fallthrough and see 98211042SErik.Nordmark@Sun.COM * if a raw socket will want it. 98310616SSebastien.Roy@Sun.COM */ 9840Sstevel@tonic-gate /* FALLTHRU */ 9850Sstevel@tonic-gate default: 98611042SErik.Nordmark@Sun.COM ira->ira_flags |= IRAF_ICMP_ERROR; 98711042SErik.Nordmark@Sun.COM ASSERT(ira->ira_protocol == nexthdr); 98811042SErik.Nordmark@Sun.COM ip_fanout_proto_v6(mp, &rip6h, ira); 98911042SErik.Nordmark@Sun.COM ira->ira_flags &= ~IRAF_ICMP_ERROR; 9900Sstevel@tonic-gate return; 9910Sstevel@tonic-gate } 9920Sstevel@tonic-gate /* NOTREACHED */ 9930Sstevel@tonic-gate drop_pkt: 9940Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors); 9950Sstevel@tonic-gate ip1dbg(("icmp_inbound_error_fanout_v6: drop pkt\n")); 99611042SErik.Nordmark@Sun.COM freemsg(mp); 9970Sstevel@tonic-gate } 9980Sstevel@tonic-gate 9990Sstevel@tonic-gate /* 10000Sstevel@tonic-gate * Process received IPv6 ICMP Redirect messages. 100111042SErik.Nordmark@Sun.COM * Assumes the caller has verified that the headers are in the pulled up mblk. 100211042SErik.Nordmark@Sun.COM * Consumes mp. 10030Sstevel@tonic-gate */ 10040Sstevel@tonic-gate /* ARGSUSED */ 10050Sstevel@tonic-gate static void 100611042SErik.Nordmark@Sun.COM icmp_redirect_v6(mblk_t *mp, ip6_t *ip6h, nd_redirect_t *rd, 100711042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira) 10080Sstevel@tonic-gate { 100911042SErik.Nordmark@Sun.COM ire_t *ire, *nire; 101011042SErik.Nordmark@Sun.COM ire_t *prev_ire = NULL; 10110Sstevel@tonic-gate ire_t *redir_ire; 10120Sstevel@tonic-gate in6_addr_t *src, *dst, *gateway; 10130Sstevel@tonic-gate nd_opt_hdr_t *opt; 10140Sstevel@tonic-gate nce_t *nce; 101511042SErik.Nordmark@Sun.COM int ncec_flags = 0; 10160Sstevel@tonic-gate int err = 0; 10170Sstevel@tonic-gate boolean_t redirect_to_router = B_FALSE; 10180Sstevel@tonic-gate int len; 10193004Sdd193516 int optlen; 102011042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_rill; 102111042SErik.Nordmark@Sun.COM ill_t *rill = ira->ira_rill; 10223448Sdh155122 ip_stack_t *ipst = ill->ill_ipst; 10230Sstevel@tonic-gate 102411042SErik.Nordmark@Sun.COM /* 102511042SErik.Nordmark@Sun.COM * Since ira_ill is where the IRE_LOCAL was hosted we use ira_rill 102611042SErik.Nordmark@Sun.COM * and make it be the IPMP upper so avoid being confused by a packet 102711042SErik.Nordmark@Sun.COM * addressed to a unicast address on a different ill. 102811042SErik.Nordmark@Sun.COM */ 102911042SErik.Nordmark@Sun.COM if (IS_UNDER_IPMP(rill)) { 103011042SErik.Nordmark@Sun.COM rill = ipmp_ill_hold_ipmp_ill(rill); 103111042SErik.Nordmark@Sun.COM if (rill == NULL) { 103211042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects); 103311042SErik.Nordmark@Sun.COM ip_drop_input("ipv6IfIcmpInBadRedirects - IPMP ill", 103411042SErik.Nordmark@Sun.COM mp, ill); 103511042SErik.Nordmark@Sun.COM freemsg(mp); 103611042SErik.Nordmark@Sun.COM return; 103711042SErik.Nordmark@Sun.COM } 103811042SErik.Nordmark@Sun.COM ASSERT(rill != ira->ira_rill); 103911042SErik.Nordmark@Sun.COM } 104011042SErik.Nordmark@Sun.COM 104111042SErik.Nordmark@Sun.COM len = mp->b_wptr - (uchar_t *)rd; 10420Sstevel@tonic-gate src = &ip6h->ip6_src; 10430Sstevel@tonic-gate dst = &rd->nd_rd_dst; 10440Sstevel@tonic-gate gateway = &rd->nd_rd_target; 10453004Sdd193516 10463004Sdd193516 /* Verify if it is a valid redirect */ 10473004Sdd193516 if (!IN6_IS_ADDR_LINKLOCAL(src) || 10483004Sdd193516 (ip6h->ip6_hops != IPV6_MAX_HOPS) || 10493004Sdd193516 (rd->nd_rd_code != 0) || 10503004Sdd193516 (len < sizeof (nd_redirect_t)) || 10513004Sdd193516 (IN6_IS_ADDR_V4MAPPED(dst)) || 10523004Sdd193516 (IN6_IS_ADDR_MULTICAST(dst))) { 10533004Sdd193516 BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects); 105411042SErik.Nordmark@Sun.COM ip_drop_input("ipv6IfIcmpInBadRedirects - addr/len", mp, ill); 105511042SErik.Nordmark@Sun.COM goto fail_redirect; 10563004Sdd193516 } 10573004Sdd193516 10583004Sdd193516 if (!(IN6_IS_ADDR_LINKLOCAL(gateway) || 10593004Sdd193516 IN6_ARE_ADDR_EQUAL(gateway, dst))) { 10603004Sdd193516 BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects); 106111042SErik.Nordmark@Sun.COM ip_drop_input("ipv6IfIcmpInBadRedirects - bad gateway", 106211042SErik.Nordmark@Sun.COM mp, ill); 106311042SErik.Nordmark@Sun.COM goto fail_redirect; 10643004Sdd193516 } 10653004Sdd193516 106611042SErik.Nordmark@Sun.COM optlen = len - sizeof (nd_redirect_t); 106711042SErik.Nordmark@Sun.COM if (optlen != 0) { 106811042SErik.Nordmark@Sun.COM if (!ndp_verify_optlen((nd_opt_hdr_t *)&rd[1], optlen)) { 10693004Sdd193516 BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects); 107011042SErik.Nordmark@Sun.COM ip_drop_input("ipv6IfIcmpInBadRedirects - options", 107111042SErik.Nordmark@Sun.COM mp, ill); 107211042SErik.Nordmark@Sun.COM goto fail_redirect; 10733004Sdd193516 } 10743004Sdd193516 } 10753004Sdd193516 10760Sstevel@tonic-gate if (!IN6_ARE_ADDR_EQUAL(gateway, dst)) { 10770Sstevel@tonic-gate redirect_to_router = B_TRUE; 107811042SErik.Nordmark@Sun.COM ncec_flags |= NCE_F_ISROUTER; 107911042SErik.Nordmark@Sun.COM } else { 108011042SErik.Nordmark@Sun.COM gateway = dst; /* Add nce for dst */ 10810Sstevel@tonic-gate } 10823004Sdd193516 10833004Sdd193516 10843004Sdd193516 /* 10853004Sdd193516 * Verify that the IP source address of the redirect is 10863004Sdd193516 * the same as the current first-hop router for the specified 10873004Sdd193516 * ICMP destination address. 10883004Sdd193516 * Also, Make sure we had a route for the dest in question and 10893004Sdd193516 * that route was pointing to the old gateway (the source of the 10903004Sdd193516 * redirect packet.) 109111042SErik.Nordmark@Sun.COM * Note: this merely says that there is some IRE which matches that 109211042SErik.Nordmark@Sun.COM * gateway; not that the longest match matches that gateway. 10933004Sdd193516 */ 109411042SErik.Nordmark@Sun.COM prev_ire = ire_ftable_lookup_v6(dst, 0, src, 0, rill, 109511042SErik.Nordmark@Sun.COM ALL_ZONES, NULL, MATCH_IRE_GW | MATCH_IRE_ILL, 0, ipst, NULL); 10963004Sdd193516 10970Sstevel@tonic-gate /* 10980Sstevel@tonic-gate * Check that 10990Sstevel@tonic-gate * the redirect was not from ourselves 11000Sstevel@tonic-gate * old gateway is still directly reachable 11010Sstevel@tonic-gate */ 11020Sstevel@tonic-gate if (prev_ire == NULL || 110311042SErik.Nordmark@Sun.COM (prev_ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK)) || 110411042SErik.Nordmark@Sun.COM (prev_ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE))) { 11050Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects); 110611042SErik.Nordmark@Sun.COM ip_drop_input("ipv6IfIcmpInBadRedirects - ire", mp, ill); 11070Sstevel@tonic-gate goto fail_redirect; 11080Sstevel@tonic-gate } 110911042SErik.Nordmark@Sun.COM 111011042SErik.Nordmark@Sun.COM ASSERT(prev_ire->ire_ill != NULL); 111111042SErik.Nordmark@Sun.COM if (prev_ire->ire_ill->ill_flags & ILLF_NONUD) 111211042SErik.Nordmark@Sun.COM ncec_flags |= NCE_F_NONUD; 111311042SErik.Nordmark@Sun.COM 11140Sstevel@tonic-gate opt = (nd_opt_hdr_t *)&rd[1]; 11153004Sdd193516 opt = ndp_get_option(opt, optlen, ND_OPT_TARGET_LINKADDR); 11160Sstevel@tonic-gate if (opt != NULL) { 111711042SErik.Nordmark@Sun.COM err = nce_lookup_then_add_v6(rill, 11180Sstevel@tonic-gate (uchar_t *)&opt[1], /* Link layer address */ 111911042SErik.Nordmark@Sun.COM rill->ill_phys_addr_length, 112011042SErik.Nordmark@Sun.COM gateway, ncec_flags, ND_STALE, &nce); 11210Sstevel@tonic-gate switch (err) { 11220Sstevel@tonic-gate case 0: 112311042SErik.Nordmark@Sun.COM nce_refrele(nce); 11240Sstevel@tonic-gate break; 11250Sstevel@tonic-gate case EEXIST: 11260Sstevel@tonic-gate /* 11270Sstevel@tonic-gate * Check to see if link layer address has changed and 112811042SErik.Nordmark@Sun.COM * process the ncec_state accordingly. 11290Sstevel@tonic-gate */ 113011042SErik.Nordmark@Sun.COM nce_process(nce->nce_common, 113111042SErik.Nordmark@Sun.COM (uchar_t *)&opt[1], 0, B_FALSE); 113211042SErik.Nordmark@Sun.COM nce_refrele(nce); 11330Sstevel@tonic-gate break; 11340Sstevel@tonic-gate default: 11350Sstevel@tonic-gate ip1dbg(("icmp_redirect_v6: NCE create failed %d\n", 11360Sstevel@tonic-gate err)); 11370Sstevel@tonic-gate goto fail_redirect; 11380Sstevel@tonic-gate } 11390Sstevel@tonic-gate } 11400Sstevel@tonic-gate if (redirect_to_router) { 11410Sstevel@tonic-gate ASSERT(IN6_IS_ADDR_LINKLOCAL(gateway)); 11420Sstevel@tonic-gate 11430Sstevel@tonic-gate /* 11440Sstevel@tonic-gate * Create a Route Association. This will allow us to remember 11450Sstevel@tonic-gate * a router told us to use the particular gateway. 11460Sstevel@tonic-gate */ 11470Sstevel@tonic-gate ire = ire_create_v6( 11480Sstevel@tonic-gate dst, 11490Sstevel@tonic-gate &ipv6_all_ones, /* mask */ 11500Sstevel@tonic-gate gateway, /* gateway addr */ 11513004Sdd193516 IRE_HOST, 115211042SErik.Nordmark@Sun.COM prev_ire->ire_ill, 115311042SErik.Nordmark@Sun.COM ALL_ZONES, 11540Sstevel@tonic-gate (RTF_DYNAMIC | RTF_GATEWAY | RTF_HOST), 11553448Sdh155122 NULL, 11563448Sdh155122 ipst); 11570Sstevel@tonic-gate } else { 115811042SErik.Nordmark@Sun.COM ipif_t *ipif; 115911042SErik.Nordmark@Sun.COM in6_addr_t gw; 11603004Sdd193516 11613004Sdd193516 /* 11623004Sdd193516 * Just create an on link entry, i.e. interface route. 116311042SErik.Nordmark@Sun.COM * The gateway field is our link-local on the ill. 11640Sstevel@tonic-gate */ 116511042SErik.Nordmark@Sun.COM mutex_enter(&rill->ill_lock); 116611042SErik.Nordmark@Sun.COM for (ipif = rill->ill_ipif; ipif != NULL; 116711042SErik.Nordmark@Sun.COM ipif = ipif->ipif_next) { 116811042SErik.Nordmark@Sun.COM if (!(ipif->ipif_state_flags & IPIF_CONDEMNED) && 116911042SErik.Nordmark@Sun.COM IN6_IS_ADDR_LINKLOCAL(&ipif->ipif_v6lcl_addr)) 117011042SErik.Nordmark@Sun.COM break; 117111042SErik.Nordmark@Sun.COM } 117211042SErik.Nordmark@Sun.COM if (ipif == NULL) { 117311042SErik.Nordmark@Sun.COM /* We have no link-local address! */ 117411042SErik.Nordmark@Sun.COM mutex_exit(&rill->ill_lock); 117511042SErik.Nordmark@Sun.COM goto fail_redirect; 117611042SErik.Nordmark@Sun.COM } 117711042SErik.Nordmark@Sun.COM gw = ipif->ipif_v6lcl_addr; 117811042SErik.Nordmark@Sun.COM mutex_exit(&rill->ill_lock); 117911042SErik.Nordmark@Sun.COM 11800Sstevel@tonic-gate ire = ire_create_v6( 11810Sstevel@tonic-gate dst, /* gateway == dst */ 11820Sstevel@tonic-gate &ipv6_all_ones, /* mask */ 118311042SErik.Nordmark@Sun.COM &gw, /* gateway addr */ 118411042SErik.Nordmark@Sun.COM rill->ill_net_type, /* IF_[NO]RESOLVER */ 118511042SErik.Nordmark@Sun.COM prev_ire->ire_ill, 118611042SErik.Nordmark@Sun.COM ALL_ZONES, 11873004Sdd193516 (RTF_DYNAMIC | RTF_HOST), 11883448Sdh155122 NULL, 11893448Sdh155122 ipst); 11900Sstevel@tonic-gate } 11913004Sdd193516 11920Sstevel@tonic-gate if (ire == NULL) 11930Sstevel@tonic-gate goto fail_redirect; 11940Sstevel@tonic-gate 119511042SErik.Nordmark@Sun.COM nire = ire_add(ire); 119611042SErik.Nordmark@Sun.COM /* Check if it was a duplicate entry */ 119711042SErik.Nordmark@Sun.COM if (nire != NULL && nire != ire) { 119811042SErik.Nordmark@Sun.COM ASSERT(nire->ire_identical_ref > 1); 119911042SErik.Nordmark@Sun.COM ire_delete(nire); 120011042SErik.Nordmark@Sun.COM ire_refrele(nire); 120111042SErik.Nordmark@Sun.COM nire = NULL; 120211042SErik.Nordmark@Sun.COM } 120311042SErik.Nordmark@Sun.COM ire = nire; 120411042SErik.Nordmark@Sun.COM if (ire != NULL) { 120511042SErik.Nordmark@Sun.COM ire_refrele(ire); /* Held in ire_add */ 12060Sstevel@tonic-gate 12070Sstevel@tonic-gate /* tell routing sockets that we received a redirect */ 12080Sstevel@tonic-gate ip_rts_change_v6(RTM_REDIRECT, 12090Sstevel@tonic-gate &rd->nd_rd_dst, 12100Sstevel@tonic-gate &rd->nd_rd_target, 121111042SErik.Nordmark@Sun.COM &ipv6_all_ones, 0, src, 12120Sstevel@tonic-gate (RTF_DYNAMIC | RTF_GATEWAY | RTF_HOST), 0, 12133448Sdh155122 (RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_AUTHOR), ipst); 12140Sstevel@tonic-gate 12150Sstevel@tonic-gate /* 12163004Sdd193516 * Delete any existing IRE_HOST type ires for this destination. 12170Sstevel@tonic-gate * This together with the added IRE has the effect of 12180Sstevel@tonic-gate * modifying an existing redirect. 12190Sstevel@tonic-gate */ 12203004Sdd193516 redir_ire = ire_ftable_lookup_v6(dst, 0, src, IRE_HOST, 122111042SErik.Nordmark@Sun.COM prev_ire->ire_ill, ALL_ZONES, NULL, 122211042SErik.Nordmark@Sun.COM (MATCH_IRE_GW | MATCH_IRE_TYPE | MATCH_IRE_ILL), 0, ipst, 122311042SErik.Nordmark@Sun.COM NULL); 12240Sstevel@tonic-gate 12250Sstevel@tonic-gate if (redir_ire != NULL) { 12263004Sdd193516 if (redir_ire->ire_flags & RTF_DYNAMIC) 12273004Sdd193516 ire_delete(redir_ire); 12280Sstevel@tonic-gate ire_refrele(redir_ire); 12290Sstevel@tonic-gate } 12300Sstevel@tonic-gate } 12310Sstevel@tonic-gate 12320Sstevel@tonic-gate ire_refrele(prev_ire); 12330Sstevel@tonic-gate prev_ire = NULL; 12340Sstevel@tonic-gate 12350Sstevel@tonic-gate fail_redirect: 12360Sstevel@tonic-gate if (prev_ire != NULL) 12370Sstevel@tonic-gate ire_refrele(prev_ire); 12380Sstevel@tonic-gate freemsg(mp); 123911042SErik.Nordmark@Sun.COM if (rill != ira->ira_rill) 124011042SErik.Nordmark@Sun.COM ill_refrele(rill); 12410Sstevel@tonic-gate } 12420Sstevel@tonic-gate 12430Sstevel@tonic-gate /* 12440Sstevel@tonic-gate * Build and ship an IPv6 ICMP message using the packet data in mp, 12450Sstevel@tonic-gate * and the ICMP header pointed to by "stuff". (May be called as 12460Sstevel@tonic-gate * writer.) 12470Sstevel@tonic-gate * Note: assumes that icmp_pkt_err_ok_v6 has been called to 12480Sstevel@tonic-gate * verify that an icmp error packet can be sent. 12490Sstevel@tonic-gate * 12500Sstevel@tonic-gate * If v6src_ptr is set use it as a source. Otherwise select a reasonable 12510Sstevel@tonic-gate * source address (see above function). 12520Sstevel@tonic-gate */ 12530Sstevel@tonic-gate static void 125411042SErik.Nordmark@Sun.COM icmp_pkt_v6(mblk_t *mp, void *stuff, size_t len, 125511042SErik.Nordmark@Sun.COM const in6_addr_t *v6src_ptr, ip_recv_attr_t *ira) 12560Sstevel@tonic-gate { 12570Sstevel@tonic-gate ip6_t *ip6h; 12580Sstevel@tonic-gate in6_addr_t v6dst; 12590Sstevel@tonic-gate size_t len_needed; 12600Sstevel@tonic-gate size_t msg_len; 12610Sstevel@tonic-gate mblk_t *mp1; 12620Sstevel@tonic-gate icmp6_t *icmp6; 12630Sstevel@tonic-gate in6_addr_t v6src; 126411042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; 126511042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst; 126611042SErik.Nordmark@Sun.COM ip_xmit_attr_t ixas; 126711042SErik.Nordmark@Sun.COM 126811042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr; 126911042SErik.Nordmark@Sun.COM 127011042SErik.Nordmark@Sun.COM bzero(&ixas, sizeof (ixas)); 127111042SErik.Nordmark@Sun.COM ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6; 127211042SErik.Nordmark@Sun.COM ixas.ixa_zoneid = ira->ira_zoneid; 127311042SErik.Nordmark@Sun.COM ixas.ixa_ifindex = 0; 127411042SErik.Nordmark@Sun.COM ixas.ixa_ipst = ipst; 127511042SErik.Nordmark@Sun.COM ixas.ixa_cred = kcred; 127611042SErik.Nordmark@Sun.COM ixas.ixa_cpid = NOPID; 127711042SErik.Nordmark@Sun.COM ixas.ixa_tsl = ira->ira_tsl; /* Behave as a multi-level responder */ 127811042SErik.Nordmark@Sun.COM ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; 127911042SErik.Nordmark@Sun.COM 128011042SErik.Nordmark@Sun.COM /* 128111042SErik.Nordmark@Sun.COM * If the source of the original packet was link-local, then 128211042SErik.Nordmark@Sun.COM * make sure we send on the same ill (group) as we received it on. 128311042SErik.Nordmark@Sun.COM */ 128411042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_src)) { 128511042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_SCOPEID_SET; 128611042SErik.Nordmark@Sun.COM if (IS_UNDER_IPMP(ill)) 128711042SErik.Nordmark@Sun.COM ixas.ixa_scopeid = ill_get_upper_ifindex(ill); 128811042SErik.Nordmark@Sun.COM else 128911042SErik.Nordmark@Sun.COM ixas.ixa_scopeid = ill->ill_phyint->phyint_ifindex; 12900Sstevel@tonic-gate } 12910Sstevel@tonic-gate 129211042SErik.Nordmark@Sun.COM if (ira->ira_flags & IRAF_IPSEC_SECURE) { 12930Sstevel@tonic-gate /* 129411042SErik.Nordmark@Sun.COM * Apply IPsec based on how IPsec was applied to 129511042SErik.Nordmark@Sun.COM * the packet that had the error. 12960Sstevel@tonic-gate * 129711042SErik.Nordmark@Sun.COM * If it was an outbound packet that caused the ICMP 129811042SErik.Nordmark@Sun.COM * error, then the caller will have setup the IRA 129911042SErik.Nordmark@Sun.COM * appropriately. 13000Sstevel@tonic-gate */ 130111042SErik.Nordmark@Sun.COM if (!ipsec_in_to_out(ira, &ixas, mp, NULL, ip6h)) { 130211042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards); 130311042SErik.Nordmark@Sun.COM /* Note: mp already consumed and ip_drop_packet done */ 130411042SErik.Nordmark@Sun.COM return; 13050Sstevel@tonic-gate } 13060Sstevel@tonic-gate } else { 13070Sstevel@tonic-gate /* 13080Sstevel@tonic-gate * This is in clear. The icmp message we are building 130911042SErik.Nordmark@Sun.COM * here should go out in clear, independent of our policy. 13100Sstevel@tonic-gate */ 131111042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_NO_IPSEC; 13120Sstevel@tonic-gate } 131311042SErik.Nordmark@Sun.COM 131411042SErik.Nordmark@Sun.COM /* 131511042SErik.Nordmark@Sun.COM * If the caller specified the source we use that. 131611042SErik.Nordmark@Sun.COM * Otherwise, if the packet was for one of our unicast addresses, make 131711042SErik.Nordmark@Sun.COM * sure we respond with that as the source. Otherwise 131811042SErik.Nordmark@Sun.COM * have ip_output_simple pick the source address. 131911042SErik.Nordmark@Sun.COM */ 13200Sstevel@tonic-gate if (v6src_ptr != NULL) { 13210Sstevel@tonic-gate v6src = *v6src_ptr; 13220Sstevel@tonic-gate } else { 132311042SErik.Nordmark@Sun.COM ire_t *ire; 132411042SErik.Nordmark@Sun.COM uint_t match_flags = MATCH_IRE_TYPE | MATCH_IRE_ZONEONLY; 132511042SErik.Nordmark@Sun.COM 132611042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src) || 132711042SErik.Nordmark@Sun.COM IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_dst)) 132811042SErik.Nordmark@Sun.COM match_flags |= MATCH_IRE_ILL; 132911042SErik.Nordmark@Sun.COM 133011042SErik.Nordmark@Sun.COM ire = ire_ftable_lookup_v6(&ip6h->ip6_dst, 0, 0, 133111042SErik.Nordmark@Sun.COM (IRE_LOCAL|IRE_LOOPBACK), ill, ira->ira_zoneid, NULL, 133211042SErik.Nordmark@Sun.COM match_flags, 0, ipst, NULL); 133311042SErik.Nordmark@Sun.COM if (ire != NULL) { 133411042SErik.Nordmark@Sun.COM v6src = ip6h->ip6_dst; 133511042SErik.Nordmark@Sun.COM ire_refrele(ire); 133611042SErik.Nordmark@Sun.COM } else { 133711042SErik.Nordmark@Sun.COM v6src = ipv6_all_zeros; 133811042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_SET_SOURCE; 13390Sstevel@tonic-gate } 13400Sstevel@tonic-gate } 13410Sstevel@tonic-gate v6dst = ip6h->ip6_src; 13423448Sdh155122 len_needed = ipst->ips_ipv6_icmp_return - IPV6_HDR_LEN - len; 13430Sstevel@tonic-gate msg_len = msgdsize(mp); 13440Sstevel@tonic-gate if (msg_len > len_needed) { 13450Sstevel@tonic-gate if (!adjmsg(mp, len_needed - msg_len)) { 13460Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutErrors); 134711042SErik.Nordmark@Sun.COM freemsg(mp); 13480Sstevel@tonic-gate return; 13490Sstevel@tonic-gate } 13500Sstevel@tonic-gate msg_len = len_needed; 13510Sstevel@tonic-gate } 135211042SErik.Nordmark@Sun.COM mp1 = allocb(IPV6_HDR_LEN + len, BPRI_MED); 13530Sstevel@tonic-gate if (mp1 == NULL) { 13540Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutErrors); 135511042SErik.Nordmark@Sun.COM freemsg(mp); 13560Sstevel@tonic-gate return; 13570Sstevel@tonic-gate } 13580Sstevel@tonic-gate mp1->b_cont = mp; 13590Sstevel@tonic-gate mp = mp1; 13600Sstevel@tonic-gate 13610Sstevel@tonic-gate /* 136211042SErik.Nordmark@Sun.COM * Set IXAF_TRUSTED_ICMP so we can let the ICMP messages this 13630Sstevel@tonic-gate * node generates be accepted in peace by all on-host destinations. 13640Sstevel@tonic-gate * If we do NOT assume that all on-host destinations trust 136511042SErik.Nordmark@Sun.COM * self-generated ICMP messages, then rework here, ip6.c, and spd.c. 136611042SErik.Nordmark@Sun.COM * (Look for IXAF_TRUSTED_ICMP). 13670Sstevel@tonic-gate */ 136811042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_TRUSTED_ICMP; 13690Sstevel@tonic-gate 13700Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 13710Sstevel@tonic-gate mp1->b_wptr = (uchar_t *)ip6h + (IPV6_HDR_LEN + len); 13720Sstevel@tonic-gate 13730Sstevel@tonic-gate ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW; 13740Sstevel@tonic-gate ip6h->ip6_nxt = IPPROTO_ICMPV6; 13753448Sdh155122 ip6h->ip6_hops = ipst->ips_ipv6_def_hops; 13760Sstevel@tonic-gate ip6h->ip6_dst = v6dst; 13770Sstevel@tonic-gate ip6h->ip6_src = v6src; 13780Sstevel@tonic-gate msg_len += IPV6_HDR_LEN + len; 13790Sstevel@tonic-gate if (msg_len > IP_MAXPACKET + IPV6_HDR_LEN) { 13800Sstevel@tonic-gate (void) adjmsg(mp, IP_MAXPACKET + IPV6_HDR_LEN - msg_len); 13810Sstevel@tonic-gate msg_len = IP_MAXPACKET + IPV6_HDR_LEN; 13820Sstevel@tonic-gate } 13830Sstevel@tonic-gate ip6h->ip6_plen = htons((uint16_t)(msgdsize(mp) - IPV6_HDR_LEN)); 13840Sstevel@tonic-gate icmp6 = (icmp6_t *)&ip6h[1]; 13850Sstevel@tonic-gate bcopy(stuff, (char *)icmp6, len); 13860Sstevel@tonic-gate /* 13870Sstevel@tonic-gate * Prepare for checksum by putting icmp length in the icmp 138811042SErik.Nordmark@Sun.COM * checksum field. The checksum is calculated in ip_output_wire_v6. 13890Sstevel@tonic-gate */ 13900Sstevel@tonic-gate icmp6->icmp6_cksum = ip6h->ip6_plen; 13910Sstevel@tonic-gate if (icmp6->icmp6_type == ND_REDIRECT) { 13920Sstevel@tonic-gate ip6h->ip6_hops = IPV6_MAX_HOPS; 13930Sstevel@tonic-gate } 139411042SErik.Nordmark@Sun.COM 139511042SErik.Nordmark@Sun.COM (void) ip_output_simple(mp, &ixas); 139611042SErik.Nordmark@Sun.COM ixa_cleanup(&ixas); 13970Sstevel@tonic-gate } 13980Sstevel@tonic-gate 13990Sstevel@tonic-gate /* 14000Sstevel@tonic-gate * Update the output mib when ICMPv6 packets are sent. 14010Sstevel@tonic-gate */ 140211042SErik.Nordmark@Sun.COM void 14030Sstevel@tonic-gate icmp_update_out_mib_v6(ill_t *ill, icmp6_t *icmp6) 14040Sstevel@tonic-gate { 14050Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutMsgs); 14060Sstevel@tonic-gate 14070Sstevel@tonic-gate switch (icmp6->icmp6_type) { 14080Sstevel@tonic-gate case ICMP6_DST_UNREACH: 14090Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutDestUnreachs); 14100Sstevel@tonic-gate if (icmp6->icmp6_code == ICMP6_DST_UNREACH_ADMIN) 14110Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutAdminProhibs); 14120Sstevel@tonic-gate break; 14130Sstevel@tonic-gate 14140Sstevel@tonic-gate case ICMP6_TIME_EXCEEDED: 14150Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutTimeExcds); 14160Sstevel@tonic-gate break; 14170Sstevel@tonic-gate 14180Sstevel@tonic-gate case ICMP6_PARAM_PROB: 14190Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutParmProblems); 14200Sstevel@tonic-gate break; 14210Sstevel@tonic-gate 14220Sstevel@tonic-gate case ICMP6_PACKET_TOO_BIG: 14230Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutPktTooBigs); 14240Sstevel@tonic-gate break; 14250Sstevel@tonic-gate 14260Sstevel@tonic-gate case ICMP6_ECHO_REQUEST: 14270Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutEchos); 14280Sstevel@tonic-gate break; 14290Sstevel@tonic-gate 14300Sstevel@tonic-gate case ICMP6_ECHO_REPLY: 14310Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutEchoReplies); 14320Sstevel@tonic-gate break; 14330Sstevel@tonic-gate 14340Sstevel@tonic-gate case ND_ROUTER_SOLICIT: 14350Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutRouterSolicits); 14360Sstevel@tonic-gate break; 14370Sstevel@tonic-gate 14380Sstevel@tonic-gate case ND_ROUTER_ADVERT: 14390Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutRouterAdvertisements); 14400Sstevel@tonic-gate break; 14410Sstevel@tonic-gate 14420Sstevel@tonic-gate case ND_NEIGHBOR_SOLICIT: 14430Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutNeighborSolicits); 14440Sstevel@tonic-gate break; 14450Sstevel@tonic-gate 14460Sstevel@tonic-gate case ND_NEIGHBOR_ADVERT: 14470Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, 14480Sstevel@tonic-gate ipv6IfIcmpOutNeighborAdvertisements); 14490Sstevel@tonic-gate break; 14500Sstevel@tonic-gate 14510Sstevel@tonic-gate case ND_REDIRECT: 14520Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutRedirects); 14530Sstevel@tonic-gate break; 14540Sstevel@tonic-gate 14550Sstevel@tonic-gate case MLD_LISTENER_QUERY: 14560Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutGroupMembQueries); 14570Sstevel@tonic-gate break; 14580Sstevel@tonic-gate 14590Sstevel@tonic-gate case MLD_LISTENER_REPORT: 14600Sstevel@tonic-gate case MLD_V2_LISTENER_REPORT: 14610Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutGroupMembResponses); 14620Sstevel@tonic-gate break; 14630Sstevel@tonic-gate 14640Sstevel@tonic-gate case MLD_LISTENER_REDUCTION: 14650Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutGroupMembReductions); 14660Sstevel@tonic-gate break; 14670Sstevel@tonic-gate } 14680Sstevel@tonic-gate } 14690Sstevel@tonic-gate 14700Sstevel@tonic-gate /* 14710Sstevel@tonic-gate * Check if it is ok to send an ICMPv6 error packet in 14720Sstevel@tonic-gate * response to the IP packet in mp. 14730Sstevel@tonic-gate * Free the message and return null if no 14740Sstevel@tonic-gate * ICMP error packet should be sent. 14750Sstevel@tonic-gate */ 14760Sstevel@tonic-gate static mblk_t * 147711042SErik.Nordmark@Sun.COM icmp_pkt_err_ok_v6(mblk_t *mp, boolean_t mcast_ok, ip_recv_attr_t *ira) 14780Sstevel@tonic-gate { 147911042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; 148011042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst; 148111042SErik.Nordmark@Sun.COM boolean_t llbcast; 148211042SErik.Nordmark@Sun.COM ip6_t *ip6h; 14830Sstevel@tonic-gate 14840Sstevel@tonic-gate if (!mp) 14850Sstevel@tonic-gate return (NULL); 14860Sstevel@tonic-gate 148711042SErik.Nordmark@Sun.COM /* We view multicast and broadcast as the same.. */ 148811042SErik.Nordmark@Sun.COM llbcast = (ira->ira_flags & 148911042SErik.Nordmark@Sun.COM (IRAF_L2DST_MULTICAST|IRAF_L2DST_BROADCAST)) != 0; 14900Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 14910Sstevel@tonic-gate 14920Sstevel@tonic-gate /* Check if source address uniquely identifies the host */ 14930Sstevel@tonic-gate 14940Sstevel@tonic-gate if (IN6_IS_ADDR_MULTICAST(&ip6h->ip6_src) || 14950Sstevel@tonic-gate IN6_IS_ADDR_V4MAPPED(&ip6h->ip6_src) || 14960Sstevel@tonic-gate IN6_IS_ADDR_UNSPECIFIED(&ip6h->ip6_src)) { 14970Sstevel@tonic-gate freemsg(mp); 14980Sstevel@tonic-gate return (NULL); 14990Sstevel@tonic-gate } 15000Sstevel@tonic-gate 15010Sstevel@tonic-gate if (ip6h->ip6_nxt == IPPROTO_ICMPV6) { 15020Sstevel@tonic-gate size_t len_needed = IPV6_HDR_LEN + ICMP6_MINLEN; 15030Sstevel@tonic-gate icmp6_t *icmp6; 15040Sstevel@tonic-gate 15050Sstevel@tonic-gate if (mp->b_wptr - mp->b_rptr < len_needed) { 15060Sstevel@tonic-gate if (!pullupmsg(mp, len_needed)) { 150711042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_icmp6_mib, 150811042SErik.Nordmark@Sun.COM ipv6IfIcmpInErrors); 15090Sstevel@tonic-gate freemsg(mp); 15100Sstevel@tonic-gate return (NULL); 15110Sstevel@tonic-gate } 15120Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 15130Sstevel@tonic-gate } 15140Sstevel@tonic-gate icmp6 = (icmp6_t *)&ip6h[1]; 15150Sstevel@tonic-gate /* Explicitly do not generate errors in response to redirects */ 15160Sstevel@tonic-gate if (ICMP6_IS_ERROR(icmp6->icmp6_type) || 15170Sstevel@tonic-gate icmp6->icmp6_type == ND_REDIRECT) { 15180Sstevel@tonic-gate freemsg(mp); 15190Sstevel@tonic-gate return (NULL); 15200Sstevel@tonic-gate } 15210Sstevel@tonic-gate } 15220Sstevel@tonic-gate /* 15230Sstevel@tonic-gate * Check that the destination is not multicast and that the packet 15240Sstevel@tonic-gate * was not sent on link layer broadcast or multicast. (Exception 15250Sstevel@tonic-gate * is Packet too big message as per the draft - when mcast_ok is set.) 15260Sstevel@tonic-gate */ 15270Sstevel@tonic-gate if (!mcast_ok && 15280Sstevel@tonic-gate (llbcast || IN6_IS_ADDR_MULTICAST(&ip6h->ip6_dst))) { 15290Sstevel@tonic-gate freemsg(mp); 15300Sstevel@tonic-gate return (NULL); 15310Sstevel@tonic-gate } 153211042SErik.Nordmark@Sun.COM /* 153311042SErik.Nordmark@Sun.COM * If this is a labeled system, then check to see if we're allowed to 153411042SErik.Nordmark@Sun.COM * send a response to this particular sender. If not, then just drop. 153511042SErik.Nordmark@Sun.COM */ 153611042SErik.Nordmark@Sun.COM if (is_system_labeled() && !tsol_can_reply_error(mp, ira)) { 153711042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutErrors); 153811042SErik.Nordmark@Sun.COM freemsg(mp); 153911042SErik.Nordmark@Sun.COM return (NULL); 154011042SErik.Nordmark@Sun.COM } 154111042SErik.Nordmark@Sun.COM 15423448Sdh155122 if (icmp_err_rate_limit(ipst)) { 15430Sstevel@tonic-gate /* 15440Sstevel@tonic-gate * Only send ICMP error packets every so often. 15450Sstevel@tonic-gate * This should be done on a per port/source basis, 15460Sstevel@tonic-gate * but for now this will suffice. 15470Sstevel@tonic-gate */ 15480Sstevel@tonic-gate freemsg(mp); 15490Sstevel@tonic-gate return (NULL); 15500Sstevel@tonic-gate } 15510Sstevel@tonic-gate return (mp); 15520Sstevel@tonic-gate } 15530Sstevel@tonic-gate 15540Sstevel@tonic-gate /* 155511042SErik.Nordmark@Sun.COM * Called when a packet was sent out the same link that it arrived on. 155611042SErik.Nordmark@Sun.COM * Check if it is ok to send a redirect and then send it. 155711042SErik.Nordmark@Sun.COM */ 155811042SErik.Nordmark@Sun.COM void 155911042SErik.Nordmark@Sun.COM ip_send_potential_redirect_v6(mblk_t *mp, ip6_t *ip6h, ire_t *ire, 156011042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira) 156111042SErik.Nordmark@Sun.COM { 156211042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; 156311042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst; 156411042SErik.Nordmark@Sun.COM in6_addr_t *v6targ; 156511042SErik.Nordmark@Sun.COM ire_t *src_ire_v6 = NULL; 156611042SErik.Nordmark@Sun.COM mblk_t *mp1; 156711042SErik.Nordmark@Sun.COM ire_t *nhop_ire = NULL; 156811042SErik.Nordmark@Sun.COM 156911042SErik.Nordmark@Sun.COM /* 157011042SErik.Nordmark@Sun.COM * Don't send a redirect when forwarding a source 157111042SErik.Nordmark@Sun.COM * routed packet. 157211042SErik.Nordmark@Sun.COM */ 157311042SErik.Nordmark@Sun.COM if (ip_source_routed_v6(ip6h, mp, ipst)) 157411042SErik.Nordmark@Sun.COM return; 157511042SErik.Nordmark@Sun.COM 157611042SErik.Nordmark@Sun.COM if (ire->ire_type & IRE_ONLINK) { 157711042SErik.Nordmark@Sun.COM /* Target is directly connected */ 157811042SErik.Nordmark@Sun.COM v6targ = &ip6h->ip6_dst; 157911042SErik.Nordmark@Sun.COM } else { 158011042SErik.Nordmark@Sun.COM /* Determine the most specific IRE used to send the packets */ 158111042SErik.Nordmark@Sun.COM nhop_ire = ire_nexthop(ire); 158211042SErik.Nordmark@Sun.COM if (nhop_ire == NULL) 158311042SErik.Nordmark@Sun.COM return; 158411042SErik.Nordmark@Sun.COM 158511042SErik.Nordmark@Sun.COM /* 158611042SErik.Nordmark@Sun.COM * We won't send redirects to a router 158711042SErik.Nordmark@Sun.COM * that doesn't have a link local 158811042SErik.Nordmark@Sun.COM * address, but will forward. 158911042SErik.Nordmark@Sun.COM */ 159011042SErik.Nordmark@Sun.COM if (!IN6_IS_ADDR_LINKLOCAL(&nhop_ire->ire_addr_v6)) { 159111042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInAddrErrors); 159211042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInAddrErrors", mp, ill); 159311042SErik.Nordmark@Sun.COM ire_refrele(nhop_ire); 159411042SErik.Nordmark@Sun.COM return; 159511042SErik.Nordmark@Sun.COM } 159611042SErik.Nordmark@Sun.COM v6targ = &nhop_ire->ire_addr_v6; 159711042SErik.Nordmark@Sun.COM } 159811042SErik.Nordmark@Sun.COM src_ire_v6 = ire_ftable_lookup_v6(&ip6h->ip6_src, 159911042SErik.Nordmark@Sun.COM NULL, NULL, IRE_INTERFACE, ire->ire_ill, ALL_ZONES, NULL, 160011042SErik.Nordmark@Sun.COM MATCH_IRE_ILL | MATCH_IRE_TYPE, 0, ipst, NULL); 160111042SErik.Nordmark@Sun.COM 160211042SErik.Nordmark@Sun.COM if (src_ire_v6 == NULL) { 160311042SErik.Nordmark@Sun.COM if (nhop_ire != NULL) 160411042SErik.Nordmark@Sun.COM ire_refrele(nhop_ire); 160511042SErik.Nordmark@Sun.COM return; 160611042SErik.Nordmark@Sun.COM } 160711042SErik.Nordmark@Sun.COM 160811042SErik.Nordmark@Sun.COM /* 160911042SErik.Nordmark@Sun.COM * The source is directly connected. 161011042SErik.Nordmark@Sun.COM */ 161111042SErik.Nordmark@Sun.COM mp1 = copymsg(mp); 161211042SErik.Nordmark@Sun.COM if (mp1 != NULL) 161311042SErik.Nordmark@Sun.COM icmp_send_redirect_v6(mp1, v6targ, &ip6h->ip6_dst, ira); 161411042SErik.Nordmark@Sun.COM 161511042SErik.Nordmark@Sun.COM if (nhop_ire != NULL) 161611042SErik.Nordmark@Sun.COM ire_refrele(nhop_ire); 161711042SErik.Nordmark@Sun.COM ire_refrele(src_ire_v6); 161811042SErik.Nordmark@Sun.COM } 161911042SErik.Nordmark@Sun.COM 162011042SErik.Nordmark@Sun.COM /* 16210Sstevel@tonic-gate * Generate an ICMPv6 redirect message. 16220Sstevel@tonic-gate * Include target link layer address option if it exits. 16230Sstevel@tonic-gate * Always include redirect header. 16240Sstevel@tonic-gate */ 16250Sstevel@tonic-gate static void 162611042SErik.Nordmark@Sun.COM icmp_send_redirect_v6(mblk_t *mp, in6_addr_t *targetp, in6_addr_t *dest, 162711042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira) 16280Sstevel@tonic-gate { 16290Sstevel@tonic-gate nd_redirect_t *rd; 16300Sstevel@tonic-gate nd_opt_rd_hdr_t *rdh; 16310Sstevel@tonic-gate uchar_t *buf; 163211042SErik.Nordmark@Sun.COM ncec_t *ncec = NULL; 16330Sstevel@tonic-gate nd_opt_hdr_t *opt; 16340Sstevel@tonic-gate int len; 16350Sstevel@tonic-gate int ll_opt_len = 0; 16360Sstevel@tonic-gate int max_redir_hdr_data_len; 16370Sstevel@tonic-gate int pkt_len; 16380Sstevel@tonic-gate in6_addr_t *srcp; 163911042SErik.Nordmark@Sun.COM ill_t *ill; 164011042SErik.Nordmark@Sun.COM boolean_t need_refrele; 164111042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ira->ira_ill->ill_ipst; 164211042SErik.Nordmark@Sun.COM 164311042SErik.Nordmark@Sun.COM mp = icmp_pkt_err_ok_v6(mp, B_FALSE, ira); 16440Sstevel@tonic-gate if (mp == NULL) 16450Sstevel@tonic-gate return; 164611042SErik.Nordmark@Sun.COM 164711042SErik.Nordmark@Sun.COM if (IS_UNDER_IPMP(ira->ira_ill)) { 164811042SErik.Nordmark@Sun.COM ill = ipmp_ill_hold_ipmp_ill(ira->ira_ill); 164911042SErik.Nordmark@Sun.COM if (ill == NULL) { 165011042SErik.Nordmark@Sun.COM ill = ira->ira_ill; 165111042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects); 165211042SErik.Nordmark@Sun.COM ip_drop_output("no IPMP ill for sending redirect", 165311042SErik.Nordmark@Sun.COM mp, ill); 165411042SErik.Nordmark@Sun.COM freemsg(mp); 165511042SErik.Nordmark@Sun.COM return; 165611042SErik.Nordmark@Sun.COM } 165711042SErik.Nordmark@Sun.COM need_refrele = B_TRUE; 165811042SErik.Nordmark@Sun.COM } else { 165911042SErik.Nordmark@Sun.COM ill = ira->ira_ill; 166011042SErik.Nordmark@Sun.COM need_refrele = B_FALSE; 166111042SErik.Nordmark@Sun.COM } 166211042SErik.Nordmark@Sun.COM 166311042SErik.Nordmark@Sun.COM ncec = ncec_lookup_illgrp_v6(ill, targetp); 166411042SErik.Nordmark@Sun.COM if (ncec != NULL && ncec->ncec_state != ND_INCOMPLETE && 166511042SErik.Nordmark@Sun.COM ncec->ncec_lladdr != NULL) { 16660Sstevel@tonic-gate ll_opt_len = (sizeof (nd_opt_hdr_t) + 16670Sstevel@tonic-gate ill->ill_phys_addr_length + 7)/8 * 8; 16680Sstevel@tonic-gate } 16690Sstevel@tonic-gate len = sizeof (nd_redirect_t) + sizeof (nd_opt_rd_hdr_t) + ll_opt_len; 16700Sstevel@tonic-gate ASSERT(len % 4 == 0); 16710Sstevel@tonic-gate buf = kmem_alloc(len, KM_NOSLEEP); 16720Sstevel@tonic-gate if (buf == NULL) { 167311042SErik.Nordmark@Sun.COM if (ncec != NULL) 167411042SErik.Nordmark@Sun.COM ncec_refrele(ncec); 167511042SErik.Nordmark@Sun.COM if (need_refrele) 167611042SErik.Nordmark@Sun.COM ill_refrele(ill); 16770Sstevel@tonic-gate freemsg(mp); 16780Sstevel@tonic-gate return; 16790Sstevel@tonic-gate } 16800Sstevel@tonic-gate 16810Sstevel@tonic-gate rd = (nd_redirect_t *)buf; 16820Sstevel@tonic-gate rd->nd_rd_type = (uint8_t)ND_REDIRECT; 16830Sstevel@tonic-gate rd->nd_rd_code = 0; 16840Sstevel@tonic-gate rd->nd_rd_reserved = 0; 16850Sstevel@tonic-gate rd->nd_rd_target = *targetp; 16860Sstevel@tonic-gate rd->nd_rd_dst = *dest; 16870Sstevel@tonic-gate 16880Sstevel@tonic-gate opt = (nd_opt_hdr_t *)(buf + sizeof (nd_redirect_t)); 168911042SErik.Nordmark@Sun.COM if (ncec != NULL && ll_opt_len != 0) { 16900Sstevel@tonic-gate opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; 16910Sstevel@tonic-gate opt->nd_opt_len = ll_opt_len/8; 169211042SErik.Nordmark@Sun.COM bcopy((char *)ncec->ncec_lladdr, &opt[1], 16930Sstevel@tonic-gate ill->ill_phys_addr_length); 16940Sstevel@tonic-gate } 169511042SErik.Nordmark@Sun.COM if (ncec != NULL) 169611042SErik.Nordmark@Sun.COM ncec_refrele(ncec); 16970Sstevel@tonic-gate rdh = (nd_opt_rd_hdr_t *)(buf + sizeof (nd_redirect_t) + ll_opt_len); 16980Sstevel@tonic-gate rdh->nd_opt_rh_type = (uint8_t)ND_OPT_REDIRECTED_HEADER; 16990Sstevel@tonic-gate /* max_redir_hdr_data_len and nd_opt_rh_len must be multiple of 8 */ 17003448Sdh155122 max_redir_hdr_data_len = 17013448Sdh155122 (ipst->ips_ipv6_icmp_return - IPV6_HDR_LEN - len)/8*8; 17020Sstevel@tonic-gate pkt_len = msgdsize(mp); 17030Sstevel@tonic-gate /* Make sure mp is 8 byte aligned */ 17040Sstevel@tonic-gate if (pkt_len > max_redir_hdr_data_len) { 17050Sstevel@tonic-gate rdh->nd_opt_rh_len = (max_redir_hdr_data_len + 17060Sstevel@tonic-gate sizeof (nd_opt_rd_hdr_t))/8; 17070Sstevel@tonic-gate (void) adjmsg(mp, max_redir_hdr_data_len - pkt_len); 17080Sstevel@tonic-gate } else { 17090Sstevel@tonic-gate rdh->nd_opt_rh_len = (pkt_len + sizeof (nd_opt_rd_hdr_t))/8; 17100Sstevel@tonic-gate (void) adjmsg(mp, -(pkt_len % 8)); 17110Sstevel@tonic-gate } 17120Sstevel@tonic-gate rdh->nd_opt_rh_reserved1 = 0; 17130Sstevel@tonic-gate rdh->nd_opt_rh_reserved2 = 0; 171411042SErik.Nordmark@Sun.COM /* ipif_v6lcl_addr contains the link-local source address */ 171511042SErik.Nordmark@Sun.COM srcp = &ill->ill_ipif->ipif_v6lcl_addr; 17168485SPeter.Memishian@Sun.COM 17172733Snordmark /* Redirects sent by router, and router is global zone */ 171811042SErik.Nordmark@Sun.COM ASSERT(ira->ira_zoneid == ALL_ZONES); 171911042SErik.Nordmark@Sun.COM ira->ira_zoneid = GLOBAL_ZONEID; 172011042SErik.Nordmark@Sun.COM icmp_pkt_v6(mp, buf, len, srcp, ira); 17210Sstevel@tonic-gate kmem_free(buf, len); 172211042SErik.Nordmark@Sun.COM if (need_refrele) 172311042SErik.Nordmark@Sun.COM ill_refrele(ill); 17240Sstevel@tonic-gate } 17250Sstevel@tonic-gate 17260Sstevel@tonic-gate 17270Sstevel@tonic-gate /* Generate an ICMP time exceeded message. (May be called as writer.) */ 17280Sstevel@tonic-gate void 172911042SErik.Nordmark@Sun.COM icmp_time_exceeded_v6(mblk_t *mp, uint8_t code, boolean_t mcast_ok, 173011042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira) 17310Sstevel@tonic-gate { 17320Sstevel@tonic-gate icmp6_t icmp6; 173311042SErik.Nordmark@Sun.COM 173411042SErik.Nordmark@Sun.COM mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira); 173511042SErik.Nordmark@Sun.COM if (mp == NULL) 17360Sstevel@tonic-gate return; 173711042SErik.Nordmark@Sun.COM 17380Sstevel@tonic-gate bzero(&icmp6, sizeof (icmp6_t)); 17390Sstevel@tonic-gate icmp6.icmp6_type = ICMP6_TIME_EXCEEDED; 17400Sstevel@tonic-gate icmp6.icmp6_code = code; 174111042SErik.Nordmark@Sun.COM icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira); 17420Sstevel@tonic-gate } 17430Sstevel@tonic-gate 17440Sstevel@tonic-gate /* 17450Sstevel@tonic-gate * Generate an ICMP unreachable message. 174611042SErik.Nordmark@Sun.COM * When called from ip_output side a minimal ip_recv_attr_t needs to be 174711042SErik.Nordmark@Sun.COM * constructed by the caller. 17480Sstevel@tonic-gate */ 17490Sstevel@tonic-gate void 175011042SErik.Nordmark@Sun.COM icmp_unreachable_v6(mblk_t *mp, uint8_t code, boolean_t mcast_ok, 175111042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira) 17520Sstevel@tonic-gate { 17530Sstevel@tonic-gate icmp6_t icmp6; 175411042SErik.Nordmark@Sun.COM 175511042SErik.Nordmark@Sun.COM mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira); 175611042SErik.Nordmark@Sun.COM if (mp == NULL) 17570Sstevel@tonic-gate return; 175811042SErik.Nordmark@Sun.COM 17590Sstevel@tonic-gate bzero(&icmp6, sizeof (icmp6_t)); 17600Sstevel@tonic-gate icmp6.icmp6_type = ICMP6_DST_UNREACH; 17610Sstevel@tonic-gate icmp6.icmp6_code = code; 176211042SErik.Nordmark@Sun.COM icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira); 17630Sstevel@tonic-gate } 17640Sstevel@tonic-gate 17650Sstevel@tonic-gate /* 17660Sstevel@tonic-gate * Generate an ICMP pkt too big message. 176711042SErik.Nordmark@Sun.COM * When called from ip_output side a minimal ip_recv_attr_t needs to be 176811042SErik.Nordmark@Sun.COM * constructed by the caller. 17690Sstevel@tonic-gate */ 177011042SErik.Nordmark@Sun.COM void 177111042SErik.Nordmark@Sun.COM icmp_pkt2big_v6(mblk_t *mp, uint32_t mtu, boolean_t mcast_ok, 177211042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira) 17730Sstevel@tonic-gate { 17740Sstevel@tonic-gate icmp6_t icmp6; 177511042SErik.Nordmark@Sun.COM 177611042SErik.Nordmark@Sun.COM mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira); 177711042SErik.Nordmark@Sun.COM if (mp == NULL) 17780Sstevel@tonic-gate return; 177911042SErik.Nordmark@Sun.COM 17800Sstevel@tonic-gate bzero(&icmp6, sizeof (icmp6_t)); 17810Sstevel@tonic-gate icmp6.icmp6_type = ICMP6_PACKET_TOO_BIG; 17820Sstevel@tonic-gate icmp6.icmp6_code = 0; 17830Sstevel@tonic-gate icmp6.icmp6_mtu = htonl(mtu); 17840Sstevel@tonic-gate 178511042SErik.Nordmark@Sun.COM icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira); 17860Sstevel@tonic-gate } 17870Sstevel@tonic-gate 17880Sstevel@tonic-gate /* 17890Sstevel@tonic-gate * Generate an ICMP parameter problem message. (May be called as writer.) 17900Sstevel@tonic-gate * 'offset' is the offset from the beginning of the packet in error. 179111042SErik.Nordmark@Sun.COM * When called from ip_output side a minimal ip_recv_attr_t needs to be 179211042SErik.Nordmark@Sun.COM * constructed by the caller. 17930Sstevel@tonic-gate */ 17940Sstevel@tonic-gate static void 179511042SErik.Nordmark@Sun.COM icmp_param_problem_v6(mblk_t *mp, uint8_t code, uint32_t offset, 179611042SErik.Nordmark@Sun.COM boolean_t mcast_ok, ip_recv_attr_t *ira) 17970Sstevel@tonic-gate { 17980Sstevel@tonic-gate icmp6_t icmp6; 179911042SErik.Nordmark@Sun.COM 180011042SErik.Nordmark@Sun.COM mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira); 180111042SErik.Nordmark@Sun.COM if (mp == NULL) 18020Sstevel@tonic-gate return; 180311042SErik.Nordmark@Sun.COM 18040Sstevel@tonic-gate bzero((char *)&icmp6, sizeof (icmp6_t)); 18050Sstevel@tonic-gate icmp6.icmp6_type = ICMP6_PARAM_PROB; 18060Sstevel@tonic-gate icmp6.icmp6_code = code; 18070Sstevel@tonic-gate icmp6.icmp6_pptr = htonl(offset); 180811042SErik.Nordmark@Sun.COM icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira); 180911042SErik.Nordmark@Sun.COM } 181011042SErik.Nordmark@Sun.COM 181111042SErik.Nordmark@Sun.COM void 181211042SErik.Nordmark@Sun.COM icmp_param_problem_nexthdr_v6(mblk_t *mp, boolean_t mcast_ok, 181311042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira) 181411042SErik.Nordmark@Sun.COM { 181511042SErik.Nordmark@Sun.COM ip6_t *ip6h = (ip6_t *)mp->b_rptr; 181611042SErik.Nordmark@Sun.COM uint16_t hdr_length; 181711042SErik.Nordmark@Sun.COM uint8_t *nexthdrp; 181811042SErik.Nordmark@Sun.COM uint32_t offset; 181911042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; 182011042SErik.Nordmark@Sun.COM 182111042SErik.Nordmark@Sun.COM /* Determine the offset of the bad nexthdr value */ 182211042SErik.Nordmark@Sun.COM if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp)) { 182311042SErik.Nordmark@Sun.COM /* Malformed packet */ 182411042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 182511042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards", mp, ill); 182611042SErik.Nordmark@Sun.COM freemsg(mp); 182711042SErik.Nordmark@Sun.COM return; 182811042SErik.Nordmark@Sun.COM } 182911042SErik.Nordmark@Sun.COM 183011042SErik.Nordmark@Sun.COM offset = nexthdrp - mp->b_rptr; 183111042SErik.Nordmark@Sun.COM icmp_param_problem_v6(mp, ICMP6_PARAMPROB_NEXTHEADER, offset, 183211042SErik.Nordmark@Sun.COM mcast_ok, ira); 18330Sstevel@tonic-gate } 18340Sstevel@tonic-gate 18350Sstevel@tonic-gate /* 183611042SErik.Nordmark@Sun.COM * Verify whether or not the IP address is a valid local address. 183711042SErik.Nordmark@Sun.COM * Could be a unicast, including one for a down interface. 183811042SErik.Nordmark@Sun.COM * If allow_mcbc then a multicast or broadcast address is also 183911042SErik.Nordmark@Sun.COM * acceptable. 184011042SErik.Nordmark@Sun.COM * 18410Sstevel@tonic-gate * In the case of a multicast address, however, the 18420Sstevel@tonic-gate * upper protocol is expected to reset the src address 184311042SErik.Nordmark@Sun.COM * to zero when we return IPVL_MCAST so that 18440Sstevel@tonic-gate * no packets are emitted with multicast address as 18450Sstevel@tonic-gate * source address. 18460Sstevel@tonic-gate * The addresses valid for bind are: 18470Sstevel@tonic-gate * (1) - in6addr_any 18480Sstevel@tonic-gate * (2) - IP address of an UP interface 18490Sstevel@tonic-gate * (3) - IP address of a DOWN interface 18500Sstevel@tonic-gate * (4) - a multicast address. In this case 18510Sstevel@tonic-gate * the conn will only receive packets destined to 18520Sstevel@tonic-gate * the specified multicast address. Note: the 18530Sstevel@tonic-gate * application still has to issue an 18540Sstevel@tonic-gate * IPV6_JOIN_GROUP socket option. 18550Sstevel@tonic-gate * 18560Sstevel@tonic-gate * In all the above cases, the bound address must be valid in the current zone. 18570Sstevel@tonic-gate * When the address is loopback or multicast, there might be many matching IREs 18580Sstevel@tonic-gate * so bind has to look up based on the zone. 18590Sstevel@tonic-gate */ 186011042SErik.Nordmark@Sun.COM ip_laddr_t 186111042SErik.Nordmark@Sun.COM ip_laddr_verify_v6(const in6_addr_t *v6src, zoneid_t zoneid, 186211042SErik.Nordmark@Sun.COM ip_stack_t *ipst, boolean_t allow_mcbc, uint_t scopeid) 186311042SErik.Nordmark@Sun.COM { 186411042SErik.Nordmark@Sun.COM ire_t *src_ire; 186511042SErik.Nordmark@Sun.COM uint_t match_flags; 186611042SErik.Nordmark@Sun.COM ill_t *ill = NULL; 186711042SErik.Nordmark@Sun.COM 186811042SErik.Nordmark@Sun.COM ASSERT(!IN6_IS_ADDR_V4MAPPED(v6src)); 186911042SErik.Nordmark@Sun.COM ASSERT(!IN6_IS_ADDR_UNSPECIFIED(v6src)); 187011042SErik.Nordmark@Sun.COM 187111042SErik.Nordmark@Sun.COM match_flags = MATCH_IRE_ZONEONLY; 187211042SErik.Nordmark@Sun.COM if (scopeid != 0) { 187311042SErik.Nordmark@Sun.COM ill = ill_lookup_on_ifindex(scopeid, B_TRUE, ipst); 187411042SErik.Nordmark@Sun.COM if (ill == NULL) 187511042SErik.Nordmark@Sun.COM return (IPVL_BAD); 187611042SErik.Nordmark@Sun.COM match_flags |= MATCH_IRE_ILL; 187711042SErik.Nordmark@Sun.COM } 187811042SErik.Nordmark@Sun.COM 187911042SErik.Nordmark@Sun.COM src_ire = ire_ftable_lookup_v6(v6src, NULL, NULL, 0, 188011042SErik.Nordmark@Sun.COM ill, zoneid, NULL, match_flags, 0, ipst, NULL); 188111042SErik.Nordmark@Sun.COM if (ill != NULL) 188211042SErik.Nordmark@Sun.COM ill_refrele(ill); 188311042SErik.Nordmark@Sun.COM 188411042SErik.Nordmark@Sun.COM /* 188511042SErik.Nordmark@Sun.COM * If an address other than in6addr_any is requested, 188611042SErik.Nordmark@Sun.COM * we verify that it is a valid address for bind 188711042SErik.Nordmark@Sun.COM * Note: Following code is in if-else-if form for 188811042SErik.Nordmark@Sun.COM * readability compared to a condition check. 188911042SErik.Nordmark@Sun.COM */ 189011042SErik.Nordmark@Sun.COM if (src_ire != NULL && (src_ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK))) { 189111042SErik.Nordmark@Sun.COM /* 189211042SErik.Nordmark@Sun.COM * (2) Bind to address of local UP interface 189311042SErik.Nordmark@Sun.COM */ 189411042SErik.Nordmark@Sun.COM ire_refrele(src_ire); 189511042SErik.Nordmark@Sun.COM return (IPVL_UNICAST_UP); 189611042SErik.Nordmark@Sun.COM } else if (IN6_IS_ADDR_MULTICAST(v6src)) { 189711042SErik.Nordmark@Sun.COM /* (4) bind to multicast address. */ 189811042SErik.Nordmark@Sun.COM if (src_ire != NULL) 189911042SErik.Nordmark@Sun.COM ire_refrele(src_ire); 190011042SErik.Nordmark@Sun.COM 190111042SErik.Nordmark@Sun.COM /* 190211042SErik.Nordmark@Sun.COM * Note: caller should take IPV6_MULTICAST_IF 190311042SErik.Nordmark@Sun.COM * into account when selecting a real source address. 190411042SErik.Nordmark@Sun.COM */ 190511042SErik.Nordmark@Sun.COM if (allow_mcbc) 190611042SErik.Nordmark@Sun.COM return (IPVL_MCAST); 190711042SErik.Nordmark@Sun.COM else 190811042SErik.Nordmark@Sun.COM return (IPVL_BAD); 190911042SErik.Nordmark@Sun.COM } else { 191011042SErik.Nordmark@Sun.COM ipif_t *ipif; 191111042SErik.Nordmark@Sun.COM 191211042SErik.Nordmark@Sun.COM /* 191311042SErik.Nordmark@Sun.COM * (3) Bind to address of local DOWN interface? 191411042SErik.Nordmark@Sun.COM * (ipif_lookup_addr() looks up all interfaces 191511042SErik.Nordmark@Sun.COM * but we do not get here for UP interfaces 191611042SErik.Nordmark@Sun.COM * - case (2) above) 191711042SErik.Nordmark@Sun.COM */ 191811042SErik.Nordmark@Sun.COM if (src_ire != NULL) 191911042SErik.Nordmark@Sun.COM ire_refrele(src_ire); 192011042SErik.Nordmark@Sun.COM 192111042SErik.Nordmark@Sun.COM ipif = ipif_lookup_addr_v6(v6src, NULL, zoneid, ipst); 192211042SErik.Nordmark@Sun.COM if (ipif == NULL) 192311042SErik.Nordmark@Sun.COM return (IPVL_BAD); 192411042SErik.Nordmark@Sun.COM 192511042SErik.Nordmark@Sun.COM /* Not a useful source? */ 192611042SErik.Nordmark@Sun.COM if (ipif->ipif_flags & (IPIF_NOLOCAL | IPIF_ANYCAST)) { 192711042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 192811042SErik.Nordmark@Sun.COM return (IPVL_BAD); 192911042SErik.Nordmark@Sun.COM } 193011042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 193111042SErik.Nordmark@Sun.COM return (IPVL_UNICAST_DOWN); 193211042SErik.Nordmark@Sun.COM } 193311042SErik.Nordmark@Sun.COM } 193411042SErik.Nordmark@Sun.COM 19358348SEric.Yu@Sun.COM /* 193611042SErik.Nordmark@Sun.COM * Verify that both the source and destination addresses are valid. If 193711042SErik.Nordmark@Sun.COM * IPDF_VERIFY_DST is not set, then the destination address may be unreachable, 193811042SErik.Nordmark@Sun.COM * i.e. have no route to it. Protocols like TCP want to verify destination 193911042SErik.Nordmark@Sun.COM * reachability, while tunnels do not. 194011042SErik.Nordmark@Sun.COM * 194111042SErik.Nordmark@Sun.COM * Determine the route, the interface, and (optionally) the source address 194211042SErik.Nordmark@Sun.COM * to use to reach a given destination. 194311042SErik.Nordmark@Sun.COM * Note that we allow connect to broadcast and multicast addresses when 194411042SErik.Nordmark@Sun.COM * IPDF_ALLOW_MCBC is set. 194511042SErik.Nordmark@Sun.COM * first_hop and dst_addr are normally the same, but if source routing 194611042SErik.Nordmark@Sun.COM * they will differ; in that case the first_hop is what we'll use for the 194711042SErik.Nordmark@Sun.COM * routing lookup but the dce and label checks will be done on dst_addr, 194811042SErik.Nordmark@Sun.COM * 194911042SErik.Nordmark@Sun.COM * If uinfo is set, then we fill in the best available information 195011042SErik.Nordmark@Sun.COM * we have for the destination. This is based on (in priority order) any 195111042SErik.Nordmark@Sun.COM * metrics and path MTU stored in a dce_t, route metrics, and finally the 195211042SErik.Nordmark@Sun.COM * ill_mtu. 195311042SErik.Nordmark@Sun.COM * 195411042SErik.Nordmark@Sun.COM * Tsol note: If we have a source route then dst_addr != firsthop. But we 195511042SErik.Nordmark@Sun.COM * always do the label check on dst_addr. 195611042SErik.Nordmark@Sun.COM * 195711042SErik.Nordmark@Sun.COM * Assumes that the caller has set ixa_scopeid for link-local communication. 19588348SEric.Yu@Sun.COM */ 195911042SErik.Nordmark@Sun.COM int 196011042SErik.Nordmark@Sun.COM ip_set_destination_v6(in6_addr_t *src_addrp, const in6_addr_t *dst_addr, 196111042SErik.Nordmark@Sun.COM const in6_addr_t *firsthop, ip_xmit_attr_t *ixa, iulp_t *uinfo, 196211042SErik.Nordmark@Sun.COM uint32_t flags, uint_t mac_mode) 19630Sstevel@tonic-gate { 196411042SErik.Nordmark@Sun.COM ire_t *ire; 19650Sstevel@tonic-gate int error = 0; 196611042SErik.Nordmark@Sun.COM in6_addr_t setsrc; /* RTF_SETSRC */ 196711042SErik.Nordmark@Sun.COM zoneid_t zoneid = ixa->ixa_zoneid; /* Honors SO_ALLZONES */ 196811042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst; 196911042SErik.Nordmark@Sun.COM dce_t *dce; 197011042SErik.Nordmark@Sun.COM uint_t pmtu; 197111042SErik.Nordmark@Sun.COM uint_t ifindex; 197211042SErik.Nordmark@Sun.COM uint_t generation; 197311042SErik.Nordmark@Sun.COM nce_t *nce; 197411042SErik.Nordmark@Sun.COM ill_t *ill = NULL; 197511042SErik.Nordmark@Sun.COM boolean_t multirt = B_FALSE; 197611042SErik.Nordmark@Sun.COM 197711042SErik.Nordmark@Sun.COM ASSERT(!IN6_IS_ADDR_V4MAPPED(dst_addr)); 197811042SErik.Nordmark@Sun.COM 197911042SErik.Nordmark@Sun.COM ASSERT(!(ixa->ixa_flags & IXAF_IS_IPV4)); 19800Sstevel@tonic-gate 19810Sstevel@tonic-gate /* 198211042SErik.Nordmark@Sun.COM * We never send to zero; the ULPs map it to the loopback address. 198311042SErik.Nordmark@Sun.COM * We can't allow it since we use zero to mean unitialized in some 198411042SErik.Nordmark@Sun.COM * places. 198511042SErik.Nordmark@Sun.COM */ 198611042SErik.Nordmark@Sun.COM ASSERT(!IN6_IS_ADDR_UNSPECIFIED(dst_addr)); 198711042SErik.Nordmark@Sun.COM 198811042SErik.Nordmark@Sun.COM if (is_system_labeled()) { 198911042SErik.Nordmark@Sun.COM ts_label_t *tsl = NULL; 199011042SErik.Nordmark@Sun.COM 199111042SErik.Nordmark@Sun.COM error = tsol_check_dest(ixa->ixa_tsl, dst_addr, IPV6_VERSION, 199211042SErik.Nordmark@Sun.COM mac_mode, (flags & IPDF_ZONE_IS_GLOBAL) != 0, &tsl); 199311042SErik.Nordmark@Sun.COM if (error != 0) 199411042SErik.Nordmark@Sun.COM return (error); 199511042SErik.Nordmark@Sun.COM if (tsl != NULL) { 199611042SErik.Nordmark@Sun.COM /* Update the label */ 199711042SErik.Nordmark@Sun.COM ip_xmit_attr_replace_tsl(ixa, tsl); 199811042SErik.Nordmark@Sun.COM } 199911042SErik.Nordmark@Sun.COM } 200011042SErik.Nordmark@Sun.COM 200111042SErik.Nordmark@Sun.COM setsrc = ipv6_all_zeros; 200211042SErik.Nordmark@Sun.COM /* 200311042SErik.Nordmark@Sun.COM * Select a route; For IPMP interfaces, we would only select 200411042SErik.Nordmark@Sun.COM * a "hidden" route (i.e., going through a specific under_ill) 200511042SErik.Nordmark@Sun.COM * if ixa_ifindex has been specified. 20060Sstevel@tonic-gate */ 200711042SErik.Nordmark@Sun.COM ire = ip_select_route_v6(firsthop, ixa, &generation, &setsrc, &error, 200811042SErik.Nordmark@Sun.COM &multirt); 200911042SErik.Nordmark@Sun.COM ASSERT(ire != NULL); /* IRE_NOROUTE if none found */ 201011042SErik.Nordmark@Sun.COM if (error != 0) 201111042SErik.Nordmark@Sun.COM goto bad_addr; 201211042SErik.Nordmark@Sun.COM 201311042SErik.Nordmark@Sun.COM /* 201411042SErik.Nordmark@Sun.COM * ire can't be a broadcast or multicast unless IPDF_ALLOW_MCBC is set. 201511042SErik.Nordmark@Sun.COM * If IPDF_VERIFY_DST is set, the destination must be reachable. 201611042SErik.Nordmark@Sun.COM * Otherwise the destination needn't be reachable. 201711042SErik.Nordmark@Sun.COM * 201811042SErik.Nordmark@Sun.COM * If we match on a reject or black hole, then we've got a 201911042SErik.Nordmark@Sun.COM * local failure. May as well fail out the connect() attempt, 202011042SErik.Nordmark@Sun.COM * since it's never going to succeed. 202111042SErik.Nordmark@Sun.COM */ 202211042SErik.Nordmark@Sun.COM if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) { 20230Sstevel@tonic-gate /* 202411042SErik.Nordmark@Sun.COM * If we're verifying destination reachability, we always want 202511042SErik.Nordmark@Sun.COM * to complain here. 202611042SErik.Nordmark@Sun.COM * 202711042SErik.Nordmark@Sun.COM * If we're not verifying destination reachability but the 202811042SErik.Nordmark@Sun.COM * destination has a route, we still want to fail on the 202911042SErik.Nordmark@Sun.COM * temporary address and broadcast address tests. 203011042SErik.Nordmark@Sun.COM * 203111042SErik.Nordmark@Sun.COM * In both cases do we let the code continue so some reasonable 203211042SErik.Nordmark@Sun.COM * information is returned to the caller. That enables the 203311042SErik.Nordmark@Sun.COM * caller to use (and even cache) the IRE. conn_ip_ouput will 203411042SErik.Nordmark@Sun.COM * use the generation mismatch path to check for the unreachable 203511042SErik.Nordmark@Sun.COM * case thereby avoiding any specific check in the main path. 20360Sstevel@tonic-gate */ 203711042SErik.Nordmark@Sun.COM ASSERT(generation == IRE_GENERATION_VERIFY); 203811042SErik.Nordmark@Sun.COM if (flags & IPDF_VERIFY_DST) { 20390Sstevel@tonic-gate /* 204011042SErik.Nordmark@Sun.COM * Set errno but continue to set up ixa_ire to be 204111042SErik.Nordmark@Sun.COM * the RTF_REJECT|RTF_BLACKHOLE IRE. 204211042SErik.Nordmark@Sun.COM * That allows callers to use ip_output to get an 204311042SErik.Nordmark@Sun.COM * ICMP error back. 20440Sstevel@tonic-gate */ 204511042SErik.Nordmark@Sun.COM if (!(ire->ire_type & IRE_HOST)) 204611042SErik.Nordmark@Sun.COM error = ENETUNREACH; 204711042SErik.Nordmark@Sun.COM else 204811042SErik.Nordmark@Sun.COM error = EHOSTUNREACH; 204911042SErik.Nordmark@Sun.COM } 205011042SErik.Nordmark@Sun.COM } 205111042SErik.Nordmark@Sun.COM 205211042SErik.Nordmark@Sun.COM if ((ire->ire_type & (IRE_BROADCAST|IRE_MULTICAST)) && 205311042SErik.Nordmark@Sun.COM !(flags & IPDF_ALLOW_MCBC)) { 205411042SErik.Nordmark@Sun.COM ire_refrele(ire); 205511042SErik.Nordmark@Sun.COM ire = ire_reject(ipst, B_FALSE); 205611042SErik.Nordmark@Sun.COM generation = IRE_GENERATION_VERIFY; 205711042SErik.Nordmark@Sun.COM error = ENETUNREACH; 205811042SErik.Nordmark@Sun.COM } 205911042SErik.Nordmark@Sun.COM 206011042SErik.Nordmark@Sun.COM /* Cache things */ 206111042SErik.Nordmark@Sun.COM if (ixa->ixa_ire != NULL) 206211042SErik.Nordmark@Sun.COM ire_refrele_notr(ixa->ixa_ire); 206311042SErik.Nordmark@Sun.COM #ifdef DEBUG 206411042SErik.Nordmark@Sun.COM ire_refhold_notr(ire); 206511042SErik.Nordmark@Sun.COM ire_refrele(ire); 206611042SErik.Nordmark@Sun.COM #endif 206711042SErik.Nordmark@Sun.COM ixa->ixa_ire = ire; 206811042SErik.Nordmark@Sun.COM ixa->ixa_ire_generation = generation; 206911042SErik.Nordmark@Sun.COM 207011042SErik.Nordmark@Sun.COM /* 207111042SErik.Nordmark@Sun.COM * For multicast with multirt we have a flag passed back from 207211042SErik.Nordmark@Sun.COM * ire_lookup_multi_ill_v6 since we don't have an IRE for each 207311042SErik.Nordmark@Sun.COM * possible multicast address. 207411042SErik.Nordmark@Sun.COM * We also need a flag for multicast since we can't check 207511042SErik.Nordmark@Sun.COM * whether RTF_MULTIRT is set in ixa_ire for multicast. 207611042SErik.Nordmark@Sun.COM */ 207711042SErik.Nordmark@Sun.COM if (multirt) { 207811042SErik.Nordmark@Sun.COM ixa->ixa_postfragfn = ip_postfrag_multirt_v6; 207911042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_MULTIRT_MULTICAST; 208011042SErik.Nordmark@Sun.COM } else { 208111042SErik.Nordmark@Sun.COM ixa->ixa_postfragfn = ire->ire_postfragfn; 208211042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_MULTIRT_MULTICAST; 208311042SErik.Nordmark@Sun.COM } 208411042SErik.Nordmark@Sun.COM if (!(ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE))) { 208511042SErik.Nordmark@Sun.COM /* Get an nce to cache. */ 208611042SErik.Nordmark@Sun.COM nce = ire_to_nce(ire, NULL, firsthop); 208711042SErik.Nordmark@Sun.COM if (nce == NULL) { 208811042SErik.Nordmark@Sun.COM /* Allocation failure? */ 208911042SErik.Nordmark@Sun.COM ixa->ixa_ire_generation = IRE_GENERATION_VERIFY; 20900Sstevel@tonic-gate } else { 209111042SErik.Nordmark@Sun.COM if (ixa->ixa_nce != NULL) 209211042SErik.Nordmark@Sun.COM nce_refrele(ixa->ixa_nce); 209311042SErik.Nordmark@Sun.COM ixa->ixa_nce = nce; 20940Sstevel@tonic-gate } 209511042SErik.Nordmark@Sun.COM } 209611042SErik.Nordmark@Sun.COM 209711042SErik.Nordmark@Sun.COM /* 209811042SErik.Nordmark@Sun.COM * If the source address is a loopback address, the 209911042SErik.Nordmark@Sun.COM * destination had best be local or multicast. 210011042SErik.Nordmark@Sun.COM * If we are sending to an IRE_LOCAL using a loopback source then 210111042SErik.Nordmark@Sun.COM * it had better be the same zoneid. 210211042SErik.Nordmark@Sun.COM */ 210311042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_LOOPBACK(src_addrp)) { 210411042SErik.Nordmark@Sun.COM if ((ire->ire_type & IRE_LOCAL) && ire->ire_zoneid != zoneid) { 210511042SErik.Nordmark@Sun.COM ire = NULL; /* Stored in ixa_ire */ 210611042SErik.Nordmark@Sun.COM error = EADDRNOTAVAIL; 210711042SErik.Nordmark@Sun.COM goto bad_addr; 210811042SErik.Nordmark@Sun.COM } 210911042SErik.Nordmark@Sun.COM if (!(ire->ire_type & (IRE_LOOPBACK|IRE_LOCAL|IRE_MULTICAST))) { 211011042SErik.Nordmark@Sun.COM ire = NULL; /* Stored in ixa_ire */ 211111042SErik.Nordmark@Sun.COM error = EADDRNOTAVAIL; 21120Sstevel@tonic-gate goto bad_addr; 21130Sstevel@tonic-gate } 21140Sstevel@tonic-gate } 21150Sstevel@tonic-gate 21160Sstevel@tonic-gate /* 211711042SErik.Nordmark@Sun.COM * Does the caller want us to pick a source address? 21180Sstevel@tonic-gate */ 211911042SErik.Nordmark@Sun.COM if (flags & IPDF_SELECT_SRC) { 212011042SErik.Nordmark@Sun.COM in6_addr_t src_addr; 212111042SErik.Nordmark@Sun.COM 2122*11131SErik.Nordmark@Sun.COM /* 2123*11131SErik.Nordmark@Sun.COM * We use use ire_nexthop_ill to avoid the under ipmp 2124*11131SErik.Nordmark@Sun.COM * interface for source address selection. Note that for ipmp 2125*11131SErik.Nordmark@Sun.COM * probe packets, ixa_ifindex would have been specified, and 2126*11131SErik.Nordmark@Sun.COM * the ip_select_route() invocation would have picked an ire 2127*11131SErik.Nordmark@Sun.COM * will ire_ill pointing at an under interface. 2128*11131SErik.Nordmark@Sun.COM */ 2129*11131SErik.Nordmark@Sun.COM ill = ire_nexthop_ill(ire); 2130*11131SErik.Nordmark@Sun.COM 213111042SErik.Nordmark@Sun.COM /* If unreachable we have no ill but need some source */ 213211042SErik.Nordmark@Sun.COM if (ill == NULL) { 213311042SErik.Nordmark@Sun.COM src_addr = ipv6_loopback; 213411042SErik.Nordmark@Sun.COM /* Make sure we look for a better source address */ 213511042SErik.Nordmark@Sun.COM generation = SRC_GENERATION_VERIFY; 213611042SErik.Nordmark@Sun.COM } else { 213711042SErik.Nordmark@Sun.COM error = ip_select_source_v6(ill, &setsrc, dst_addr, 213811042SErik.Nordmark@Sun.COM zoneid, ipst, B_FALSE, ixa->ixa_src_preferences, 213911042SErik.Nordmark@Sun.COM &src_addr, &generation, NULL); 214011042SErik.Nordmark@Sun.COM if (error != 0) { 214111042SErik.Nordmark@Sun.COM ire = NULL; /* Stored in ixa_ire */ 21420Sstevel@tonic-gate goto bad_addr; 21430Sstevel@tonic-gate } 21440Sstevel@tonic-gate } 214511042SErik.Nordmark@Sun.COM 21460Sstevel@tonic-gate /* 214711042SErik.Nordmark@Sun.COM * We allow the source address to to down. 214811042SErik.Nordmark@Sun.COM * However, we check that we don't use the loopback address 214911042SErik.Nordmark@Sun.COM * as a source when sending out on the wire. 21500Sstevel@tonic-gate */ 215111042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_LOOPBACK(&src_addr) && 215211042SErik.Nordmark@Sun.COM !(ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK|IRE_MULTICAST)) && 215311042SErik.Nordmark@Sun.COM !(ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE))) { 215411042SErik.Nordmark@Sun.COM ire = NULL; /* Stored in ixa_ire */ 215511042SErik.Nordmark@Sun.COM error = EADDRNOTAVAIL; 21568348SEric.Yu@Sun.COM goto bad_addr; 21578348SEric.Yu@Sun.COM } 215811042SErik.Nordmark@Sun.COM 215911042SErik.Nordmark@Sun.COM *src_addrp = src_addr; 216011042SErik.Nordmark@Sun.COM ixa->ixa_src_generation = generation; 21610Sstevel@tonic-gate } 21620Sstevel@tonic-gate 21630Sstevel@tonic-gate /* 216411042SErik.Nordmark@Sun.COM * Make sure we don't leave an unreachable ixa_nce in place 216511042SErik.Nordmark@Sun.COM * since ip_select_route is used when we unplumb i.e., remove 216611042SErik.Nordmark@Sun.COM * references on ixa_ire, ixa_nce, and ixa_dce. 21670Sstevel@tonic-gate */ 216811042SErik.Nordmark@Sun.COM nce = ixa->ixa_nce; 216911042SErik.Nordmark@Sun.COM if (nce != NULL && nce->nce_is_condemned) { 217011042SErik.Nordmark@Sun.COM nce_refrele(nce); 217111042SErik.Nordmark@Sun.COM ixa->ixa_nce = NULL; 217211042SErik.Nordmark@Sun.COM ixa->ixa_ire_generation = IRE_GENERATION_VERIFY; 217311042SErik.Nordmark@Sun.COM } 217411042SErik.Nordmark@Sun.COM 217511042SErik.Nordmark@Sun.COM 217611042SErik.Nordmark@Sun.COM ifindex = 0; 217711042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_LINKSCOPE(dst_addr)) { 217811042SErik.Nordmark@Sun.COM /* If we are creating a DCE we'd better have an ifindex */ 217911042SErik.Nordmark@Sun.COM if (ill != NULL) 218011042SErik.Nordmark@Sun.COM ifindex = ill->ill_phyint->phyint_ifindex; 218111042SErik.Nordmark@Sun.COM else 218211042SErik.Nordmark@Sun.COM flags &= ~IPDF_UNIQUE_DCE; 218311042SErik.Nordmark@Sun.COM } 218411042SErik.Nordmark@Sun.COM 218511042SErik.Nordmark@Sun.COM if (flags & IPDF_UNIQUE_DCE) { 218611042SErik.Nordmark@Sun.COM /* Fallback to the default dce if allocation fails */ 218711042SErik.Nordmark@Sun.COM dce = dce_lookup_and_add_v6(dst_addr, ifindex, ipst); 218811042SErik.Nordmark@Sun.COM if (dce != NULL) { 218911042SErik.Nordmark@Sun.COM generation = dce->dce_generation; 219011042SErik.Nordmark@Sun.COM } else { 219111042SErik.Nordmark@Sun.COM dce = dce_lookup_v6(dst_addr, ifindex, ipst, 219211042SErik.Nordmark@Sun.COM &generation); 21935388Sja97890 } 219411042SErik.Nordmark@Sun.COM } else { 219511042SErik.Nordmark@Sun.COM dce = dce_lookup_v6(dst_addr, ifindex, ipst, &generation); 21960Sstevel@tonic-gate } 219711042SErik.Nordmark@Sun.COM ASSERT(dce != NULL); 219811042SErik.Nordmark@Sun.COM if (ixa->ixa_dce != NULL) 219911042SErik.Nordmark@Sun.COM dce_refrele_notr(ixa->ixa_dce); 220011042SErik.Nordmark@Sun.COM #ifdef DEBUG 220111042SErik.Nordmark@Sun.COM dce_refhold_notr(dce); 220211042SErik.Nordmark@Sun.COM dce_refrele(dce); 220311042SErik.Nordmark@Sun.COM #endif 220411042SErik.Nordmark@Sun.COM ixa->ixa_dce = dce; 220511042SErik.Nordmark@Sun.COM ixa->ixa_dce_generation = generation; 22060Sstevel@tonic-gate 22070Sstevel@tonic-gate /* 220811042SErik.Nordmark@Sun.COM * Note that IPv6 multicast supports PMTU discovery unlike IPv4 220911042SErik.Nordmark@Sun.COM * multicast. But pmtu discovery is only enabled for connected 221011042SErik.Nordmark@Sun.COM * sockets in general. 22110Sstevel@tonic-gate */ 22120Sstevel@tonic-gate 22130Sstevel@tonic-gate /* 221411042SErik.Nordmark@Sun.COM * Set initial value for fragmentation limit. Either conn_ip_output 221511042SErik.Nordmark@Sun.COM * or ULP might updates it when there are routing changes. 221611042SErik.Nordmark@Sun.COM * Handles a NULL ixa_ire->ire_ill or a NULL ixa_nce for RTF_REJECT. 22170Sstevel@tonic-gate */ 221811042SErik.Nordmark@Sun.COM pmtu = ip_get_pmtu(ixa); 221911042SErik.Nordmark@Sun.COM ixa->ixa_fragsize = pmtu; 222011042SErik.Nordmark@Sun.COM /* Make sure ixa_fragsize and ixa_pmtu remain identical */ 222111042SErik.Nordmark@Sun.COM if (ixa->ixa_flags & IXAF_VERIFY_PMTU) 222211042SErik.Nordmark@Sun.COM ixa->ixa_pmtu = pmtu; 22238348SEric.Yu@Sun.COM 22248348SEric.Yu@Sun.COM /* 222511042SErik.Nordmark@Sun.COM * Extract information useful for some transports. 222611042SErik.Nordmark@Sun.COM * First we look for DCE metrics. Then we take what we have in 222711042SErik.Nordmark@Sun.COM * the metrics in the route, where the offlink is used if we have 222811042SErik.Nordmark@Sun.COM * one. 22298348SEric.Yu@Sun.COM */ 223011042SErik.Nordmark@Sun.COM if (uinfo != NULL) { 223111042SErik.Nordmark@Sun.COM bzero(uinfo, sizeof (*uinfo)); 223211042SErik.Nordmark@Sun.COM 223311042SErik.Nordmark@Sun.COM if (dce->dce_flags & DCEF_UINFO) 223411042SErik.Nordmark@Sun.COM *uinfo = dce->dce_uinfo; 223511042SErik.Nordmark@Sun.COM 223611042SErik.Nordmark@Sun.COM rts_merge_metrics(uinfo, &ire->ire_metrics); 223711042SErik.Nordmark@Sun.COM 223811042SErik.Nordmark@Sun.COM /* Allow ire_metrics to decrease the path MTU from above */ 223911042SErik.Nordmark@Sun.COM if (uinfo->iulp_mtu == 0 || uinfo->iulp_mtu > pmtu) 224011042SErik.Nordmark@Sun.COM uinfo->iulp_mtu = pmtu; 224111042SErik.Nordmark@Sun.COM 224211042SErik.Nordmark@Sun.COM uinfo->iulp_localnet = (ire->ire_type & IRE_ONLINK) != 0; 224311042SErik.Nordmark@Sun.COM uinfo->iulp_loopback = (ire->ire_type & IRE_LOOPBACK) != 0; 224411042SErik.Nordmark@Sun.COM uinfo->iulp_local = (ire->ire_type & IRE_LOCAL) != 0; 22458348SEric.Yu@Sun.COM } 22468348SEric.Yu@Sun.COM 224711042SErik.Nordmark@Sun.COM if (ill != NULL) 224811042SErik.Nordmark@Sun.COM ill_refrele(ill); 224911042SErik.Nordmark@Sun.COM 225011042SErik.Nordmark@Sun.COM return (error); 22518348SEric.Yu@Sun.COM 22528348SEric.Yu@Sun.COM bad_addr: 225311042SErik.Nordmark@Sun.COM if (ire != NULL) 225411042SErik.Nordmark@Sun.COM ire_refrele(ire); 225511042SErik.Nordmark@Sun.COM 225611042SErik.Nordmark@Sun.COM if (ill != NULL) 225711042SErik.Nordmark@Sun.COM ill_refrele(ill); 225811042SErik.Nordmark@Sun.COM 225911042SErik.Nordmark@Sun.COM /* 226011042SErik.Nordmark@Sun.COM * Make sure we don't leave an unreachable ixa_nce in place 226111042SErik.Nordmark@Sun.COM * since ip_select_route is used when we unplumb i.e., remove 226211042SErik.Nordmark@Sun.COM * references on ixa_ire, ixa_nce, and ixa_dce. 226311042SErik.Nordmark@Sun.COM */ 226411042SErik.Nordmark@Sun.COM nce = ixa->ixa_nce; 226511042SErik.Nordmark@Sun.COM if (nce != NULL && nce->nce_is_condemned) { 226611042SErik.Nordmark@Sun.COM nce_refrele(nce); 226711042SErik.Nordmark@Sun.COM ixa->ixa_nce = NULL; 226811042SErik.Nordmark@Sun.COM ixa->ixa_ire_generation = IRE_GENERATION_VERIFY; 22690Sstevel@tonic-gate } 227011042SErik.Nordmark@Sun.COM 227111042SErik.Nordmark@Sun.COM return (error); 22720Sstevel@tonic-gate } 22730Sstevel@tonic-gate 22740Sstevel@tonic-gate /* 22750Sstevel@tonic-gate * Handle protocols with which IP is less intimate. There 22760Sstevel@tonic-gate * can be more than one stream bound to a particular 22770Sstevel@tonic-gate * protocol. When this is the case, normally each one gets a copy 22780Sstevel@tonic-gate * of any incoming packets. 22790Sstevel@tonic-gate * 22800Sstevel@tonic-gate * Zones notes: 228111042SErik.Nordmark@Sun.COM * Packets will be distributed to conns in all zones. This is really only 22820Sstevel@tonic-gate * useful for ICMPv6 as only applications in the global zone can create raw 22830Sstevel@tonic-gate * sockets for other protocols. 22840Sstevel@tonic-gate */ 228511042SErik.Nordmark@Sun.COM void 228611042SErik.Nordmark@Sun.COM ip_fanout_proto_v6(mblk_t *mp, ip6_t *ip6h, ip_recv_attr_t *ira) 22870Sstevel@tonic-gate { 228811042SErik.Nordmark@Sun.COM mblk_t *mp1; 228911042SErik.Nordmark@Sun.COM in6_addr_t laddr = ip6h->ip6_dst; 229011042SErik.Nordmark@Sun.COM conn_t *connp, *first_connp, *next_connp; 229111042SErik.Nordmark@Sun.COM connf_t *connfp; 229211042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; 229311042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst; 229411042SErik.Nordmark@Sun.COM 229511042SErik.Nordmark@Sun.COM connfp = &ipst->ips_ipcl_proto_fanout_v6[ira->ira_protocol]; 22960Sstevel@tonic-gate mutex_enter(&connfp->connf_lock); 22970Sstevel@tonic-gate connp = connfp->connf_head; 22980Sstevel@tonic-gate for (connp = connfp->connf_head; connp != NULL; 22994459Skcpoon connp = connp->conn_next) { 230011042SErik.Nordmark@Sun.COM /* Note: IPCL_PROTO_MATCH_V6 includes conn_wantpacket */ 230111042SErik.Nordmark@Sun.COM if (IPCL_PROTO_MATCH_V6(connp, ira, ip6h) && 230211042SErik.Nordmark@Sun.COM (!(ira->ira_flags & IRAF_SYSTEM_LABELED) || 230311042SErik.Nordmark@Sun.COM tsol_receive_local(mp, &laddr, IPV6_VERSION, ira, connp))) 23040Sstevel@tonic-gate break; 23050Sstevel@tonic-gate } 23060Sstevel@tonic-gate 23078348SEric.Yu@Sun.COM if (connp == NULL) { 23080Sstevel@tonic-gate /* 23090Sstevel@tonic-gate * No one bound to this port. Is 23100Sstevel@tonic-gate * there a client that wants all 23110Sstevel@tonic-gate * unclaimed datagrams? 23120Sstevel@tonic-gate */ 23130Sstevel@tonic-gate mutex_exit(&connfp->connf_lock); 231411042SErik.Nordmark@Sun.COM ip_fanout_send_icmp_v6(mp, ICMP6_PARAM_PROB, 231511042SErik.Nordmark@Sun.COM ICMP6_PARAMPROB_NEXTHEADER, ira); 23160Sstevel@tonic-gate return; 23170Sstevel@tonic-gate } 23180Sstevel@tonic-gate 231911042SErik.Nordmark@Sun.COM ASSERT(IPCL_IS_NONSTR(connp) || connp->conn_rq != NULL); 23208348SEric.Yu@Sun.COM 23210Sstevel@tonic-gate CONN_INC_REF(connp); 23220Sstevel@tonic-gate first_connp = connp; 23230Sstevel@tonic-gate 23240Sstevel@tonic-gate /* 23250Sstevel@tonic-gate * XXX: Fix the multiple protocol listeners case. We should not 232611042SErik.Nordmark@Sun.COM * be walking the conn->conn_next list here. 23270Sstevel@tonic-gate */ 232810616SSebastien.Roy@Sun.COM connp = connp->conn_next; 23290Sstevel@tonic-gate for (;;) { 23300Sstevel@tonic-gate while (connp != NULL) { 233111042SErik.Nordmark@Sun.COM /* Note: IPCL_PROTO_MATCH_V6 includes conn_wantpacket */ 233211042SErik.Nordmark@Sun.COM if (IPCL_PROTO_MATCH_V6(connp, ira, ip6h) && 233311042SErik.Nordmark@Sun.COM (!(ira->ira_flags & IRAF_SYSTEM_LABELED) || 233411042SErik.Nordmark@Sun.COM tsol_receive_local(mp, &laddr, IPV6_VERSION, 233511042SErik.Nordmark@Sun.COM ira, connp))) 23360Sstevel@tonic-gate break; 23370Sstevel@tonic-gate connp = connp->conn_next; 23380Sstevel@tonic-gate } 23390Sstevel@tonic-gate 234011042SErik.Nordmark@Sun.COM if (connp == NULL) { 234111042SErik.Nordmark@Sun.COM /* No more interested clients */ 23420Sstevel@tonic-gate connp = first_connp; 23430Sstevel@tonic-gate break; 23440Sstevel@tonic-gate } 234511042SErik.Nordmark@Sun.COM if (((mp1 = dupmsg(mp)) == NULL) && 234611042SErik.Nordmark@Sun.COM ((mp1 = copymsg(mp)) == NULL)) { 234711042SErik.Nordmark@Sun.COM /* Memory allocation failed */ 234811042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 234911042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards", mp, ill); 235011042SErik.Nordmark@Sun.COM connp = first_connp; 235111042SErik.Nordmark@Sun.COM break; 235211042SErik.Nordmark@Sun.COM } 235311042SErik.Nordmark@Sun.COM 23540Sstevel@tonic-gate CONN_INC_REF(connp); 23550Sstevel@tonic-gate mutex_exit(&connfp->connf_lock); 235611042SErik.Nordmark@Sun.COM 235711042SErik.Nordmark@Sun.COM ip_fanout_proto_conn(connp, mp1, NULL, (ip6_t *)mp1->b_rptr, 235811042SErik.Nordmark@Sun.COM ira); 235911042SErik.Nordmark@Sun.COM 23600Sstevel@tonic-gate mutex_enter(&connfp->connf_lock); 23610Sstevel@tonic-gate /* Follow the next pointer before releasing the conn. */ 23620Sstevel@tonic-gate next_connp = connp->conn_next; 23630Sstevel@tonic-gate CONN_DEC_REF(connp); 23640Sstevel@tonic-gate connp = next_connp; 23650Sstevel@tonic-gate } 23660Sstevel@tonic-gate 23670Sstevel@tonic-gate /* Last one. Send it upstream. */ 23680Sstevel@tonic-gate mutex_exit(&connfp->connf_lock); 23690Sstevel@tonic-gate 237011042SErik.Nordmark@Sun.COM ip_fanout_proto_conn(connp, mp, NULL, ip6h, ira); 237111042SErik.Nordmark@Sun.COM 23720Sstevel@tonic-gate CONN_DEC_REF(connp); 23730Sstevel@tonic-gate } 23740Sstevel@tonic-gate 23750Sstevel@tonic-gate /* 237611042SErik.Nordmark@Sun.COM * Called when it is conceptually a ULP that would sent the packet 237711042SErik.Nordmark@Sun.COM * e.g., port unreachable and nexthdr unknown. Check that the packet 237811042SErik.Nordmark@Sun.COM * would have passed the IPsec global policy before sending the error. 237911042SErik.Nordmark@Sun.COM * 238011042SErik.Nordmark@Sun.COM * Send an ICMP error after patching up the packet appropriately. 238111042SErik.Nordmark@Sun.COM * Uses ip_drop_input and bumps the appropriate MIB. 238211042SErik.Nordmark@Sun.COM * For ICMP6_PARAMPROB_NEXTHEADER we determine the offset to use. 23830Sstevel@tonic-gate */ 238411042SErik.Nordmark@Sun.COM void 238511042SErik.Nordmark@Sun.COM ip_fanout_send_icmp_v6(mblk_t *mp, uint_t icmp_type, uint8_t icmp_code, 238611042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira) 23870Sstevel@tonic-gate { 238811042SErik.Nordmark@Sun.COM ip6_t *ip6h; 238911042SErik.Nordmark@Sun.COM boolean_t secure; 239011042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; 239111042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst; 239211042SErik.Nordmark@Sun.COM netstack_t *ns = ipst->ips_netstack; 239311042SErik.Nordmark@Sun.COM ipsec_stack_t *ipss = ns->netstack_ipsec; 239411042SErik.Nordmark@Sun.COM 239511042SErik.Nordmark@Sun.COM secure = ira->ira_flags & IRAF_IPSEC_SECURE; 239611042SErik.Nordmark@Sun.COM 23970Sstevel@tonic-gate /* 23980Sstevel@tonic-gate * We are generating an icmp error for some inbound packet. 23990Sstevel@tonic-gate * Called from all ip_fanout_(udp, tcp, proto) functions. 24000Sstevel@tonic-gate * Before we generate an error, check with global policy 24010Sstevel@tonic-gate * to see whether this is allowed to enter the system. As 24020Sstevel@tonic-gate * there is no "conn", we are checking with global policy. 24030Sstevel@tonic-gate */ 24040Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 24053448Sdh155122 if (secure || ipss->ipsec_inbound_v6_policy_present) { 240611042SErik.Nordmark@Sun.COM mp = ipsec_check_global_policy(mp, NULL, NULL, ip6h, ira, ns); 240711042SErik.Nordmark@Sun.COM if (mp == NULL) 240811042SErik.Nordmark@Sun.COM return; 24090Sstevel@tonic-gate } 24100Sstevel@tonic-gate 241111042SErik.Nordmark@Sun.COM /* We never send errors for protocols that we do implement */ 241211042SErik.Nordmark@Sun.COM if (ira->ira_protocol == IPPROTO_ICMPV6) { 241311042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 241411042SErik.Nordmark@Sun.COM ip_drop_input("ip_fanout_send_icmp_v6", mp, ill); 241511042SErik.Nordmark@Sun.COM freemsg(mp); 24160Sstevel@tonic-gate return; 24170Sstevel@tonic-gate } 24180Sstevel@tonic-gate 241911042SErik.Nordmark@Sun.COM switch (icmp_type) { 242011042SErik.Nordmark@Sun.COM case ICMP6_DST_UNREACH: 242111042SErik.Nordmark@Sun.COM ASSERT(icmp_code == ICMP6_DST_UNREACH_NOPORT); 242211042SErik.Nordmark@Sun.COM 242311042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, udpIfStatsNoPorts); 242411042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsNoPorts", mp, ill); 242511042SErik.Nordmark@Sun.COM 242611042SErik.Nordmark@Sun.COM icmp_unreachable_v6(mp, icmp_code, B_FALSE, ira); 242711042SErik.Nordmark@Sun.COM break; 242811042SErik.Nordmark@Sun.COM case ICMP6_PARAM_PROB: 242911042SErik.Nordmark@Sun.COM ASSERT(icmp_code == ICMP6_PARAMPROB_NEXTHEADER); 243011042SErik.Nordmark@Sun.COM 243111042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInUnknownProtos); 243211042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInUnknownProtos", mp, ill); 243311042SErik.Nordmark@Sun.COM 243411042SErik.Nordmark@Sun.COM /* Let the system determine the offset for this one */ 243511042SErik.Nordmark@Sun.COM icmp_param_problem_nexthdr_v6(mp, B_FALSE, ira); 243611042SErik.Nordmark@Sun.COM break; 243711042SErik.Nordmark@Sun.COM default: 243811042SErik.Nordmark@Sun.COM #ifdef DEBUG 243911042SErik.Nordmark@Sun.COM panic("ip_fanout_send_icmp_v6: wrong type"); 244011042SErik.Nordmark@Sun.COM /*NOTREACHED*/ 244111042SErik.Nordmark@Sun.COM #else 244211042SErik.Nordmark@Sun.COM freemsg(mp); 244311042SErik.Nordmark@Sun.COM break; 244411042SErik.Nordmark@Sun.COM #endif 24450Sstevel@tonic-gate } 24460Sstevel@tonic-gate } 24470Sstevel@tonic-gate 24480Sstevel@tonic-gate /* 244911042SErik.Nordmark@Sun.COM * Fanout for UDP packets that are multicast or ICMP errors. 245011042SErik.Nordmark@Sun.COM * (Unicast fanout is handled in ip_input_v6.) 245111042SErik.Nordmark@Sun.COM * 245211042SErik.Nordmark@Sun.COM * If SO_REUSEADDR is set all multicast packets 245311042SErik.Nordmark@Sun.COM * will be delivered to all conns bound to the same port. 245411042SErik.Nordmark@Sun.COM * 24550Sstevel@tonic-gate * Fanout for UDP packets. 24560Sstevel@tonic-gate * The caller puts <fport, lport> in the ports parameter. 24570Sstevel@tonic-gate * ire_type must be IRE_BROADCAST for multicast and broadcast packets. 24580Sstevel@tonic-gate * 24590Sstevel@tonic-gate * If SO_REUSEADDR is set all multicast and broadcast packets 246011042SErik.Nordmark@Sun.COM * will be delivered to all conns bound to the same port. 24610Sstevel@tonic-gate * 24620Sstevel@tonic-gate * Zones notes: 246311042SErik.Nordmark@Sun.COM * Earlier in ip_input on a system with multiple shared-IP zones we 246411042SErik.Nordmark@Sun.COM * duplicate the multicast and broadcast packets and send them up 246511042SErik.Nordmark@Sun.COM * with each explicit zoneid that exists on that ill. 246611042SErik.Nordmark@Sun.COM * This means that here we can match the zoneid with SO_ALLZONES being special. 24670Sstevel@tonic-gate */ 246811042SErik.Nordmark@Sun.COM void 246911042SErik.Nordmark@Sun.COM ip_fanout_udp_multi_v6(mblk_t *mp, ip6_t *ip6h, uint16_t lport, uint16_t fport, 247011042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira) 24710Sstevel@tonic-gate { 247211042SErik.Nordmark@Sun.COM in6_addr_t laddr; 24730Sstevel@tonic-gate conn_t *connp; 24740Sstevel@tonic-gate connf_t *connfp; 247511042SErik.Nordmark@Sun.COM in6_addr_t faddr; 247611042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; 247711042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst; 247811042SErik.Nordmark@Sun.COM 247911042SErik.Nordmark@Sun.COM ASSERT(ira->ira_flags & (IRAF_MULTIBROADCAST|IRAF_ICMP_ERROR)); 248011042SErik.Nordmark@Sun.COM 248111042SErik.Nordmark@Sun.COM laddr = ip6h->ip6_dst; 248211042SErik.Nordmark@Sun.COM faddr = ip6h->ip6_src; 24831676Sjpk 24840Sstevel@tonic-gate /* Attempt to find a client stream based on destination port. */ 248511042SErik.Nordmark@Sun.COM connfp = &ipst->ips_ipcl_udp_fanout[IPCL_UDP_HASH(lport, ipst)]; 24860Sstevel@tonic-gate mutex_enter(&connfp->connf_lock); 24870Sstevel@tonic-gate connp = connfp->connf_head; 24880Sstevel@tonic-gate while (connp != NULL) { 248911042SErik.Nordmark@Sun.COM if ((IPCL_UDP_MATCH_V6(connp, lport, laddr, fport, faddr)) && 249011042SErik.Nordmark@Sun.COM conn_wantpacket_v6(connp, ira, ip6h) && 249111042SErik.Nordmark@Sun.COM (!(ira->ira_flags & IRAF_SYSTEM_LABELED) || 249211042SErik.Nordmark@Sun.COM tsol_receive_local(mp, &laddr, IPV6_VERSION, ira, connp))) 24930Sstevel@tonic-gate break; 24940Sstevel@tonic-gate connp = connp->conn_next; 24950Sstevel@tonic-gate } 24960Sstevel@tonic-gate 249711042SErik.Nordmark@Sun.COM if (connp == NULL) 24980Sstevel@tonic-gate goto notfound; 24990Sstevel@tonic-gate 25000Sstevel@tonic-gate CONN_INC_REF(connp); 250111042SErik.Nordmark@Sun.COM 250211042SErik.Nordmark@Sun.COM if (connp->conn_reuseaddr) { 250311042SErik.Nordmark@Sun.COM conn_t *first_connp = connp; 250411042SErik.Nordmark@Sun.COM conn_t *next_connp; 250511042SErik.Nordmark@Sun.COM mblk_t *mp1; 250611042SErik.Nordmark@Sun.COM 250711042SErik.Nordmark@Sun.COM connp = connp->conn_next; 250811042SErik.Nordmark@Sun.COM for (;;) { 250911042SErik.Nordmark@Sun.COM while (connp != NULL) { 251011042SErik.Nordmark@Sun.COM if (IPCL_UDP_MATCH_V6(connp, lport, laddr, 251111042SErik.Nordmark@Sun.COM fport, faddr) && 251211042SErik.Nordmark@Sun.COM conn_wantpacket_v6(connp, ira, ip6h) && 251311042SErik.Nordmark@Sun.COM (!(ira->ira_flags & IRAF_SYSTEM_LABELED) || 251411042SErik.Nordmark@Sun.COM tsol_receive_local(mp, &laddr, IPV6_VERSION, 251511042SErik.Nordmark@Sun.COM ira, connp))) 251611042SErik.Nordmark@Sun.COM break; 251711042SErik.Nordmark@Sun.COM connp = connp->conn_next; 251811042SErik.Nordmark@Sun.COM } 251911042SErik.Nordmark@Sun.COM if (connp == NULL) { 252011042SErik.Nordmark@Sun.COM /* No more interested clients */ 252111042SErik.Nordmark@Sun.COM connp = first_connp; 25220Sstevel@tonic-gate break; 252311042SErik.Nordmark@Sun.COM } 252411042SErik.Nordmark@Sun.COM if (((mp1 = dupmsg(mp)) == NULL) && 252511042SErik.Nordmark@Sun.COM ((mp1 = copymsg(mp)) == NULL)) { 252611042SErik.Nordmark@Sun.COM /* Memory allocation failed */ 252711042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 252811042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards", mp, ill); 252911042SErik.Nordmark@Sun.COM connp = first_connp; 253011042SErik.Nordmark@Sun.COM break; 253111042SErik.Nordmark@Sun.COM } 253211042SErik.Nordmark@Sun.COM 253311042SErik.Nordmark@Sun.COM CONN_INC_REF(connp); 253411042SErik.Nordmark@Sun.COM mutex_exit(&connfp->connf_lock); 253511042SErik.Nordmark@Sun.COM 253611042SErik.Nordmark@Sun.COM IP6_STAT(ipst, ip6_udp_fanmb); 253711042SErik.Nordmark@Sun.COM ip_fanout_udp_conn(connp, mp1, NULL, 253811042SErik.Nordmark@Sun.COM (ip6_t *)mp1->b_rptr, ira); 253911042SErik.Nordmark@Sun.COM 254011042SErik.Nordmark@Sun.COM mutex_enter(&connfp->connf_lock); 254111042SErik.Nordmark@Sun.COM /* Follow the next pointer before releasing the conn. */ 254211042SErik.Nordmark@Sun.COM next_connp = connp->conn_next; 254311042SErik.Nordmark@Sun.COM IP6_STAT(ipst, ip6_udp_fanmb); 254411042SErik.Nordmark@Sun.COM CONN_DEC_REF(connp); 254511042SErik.Nordmark@Sun.COM connp = next_connp; 25460Sstevel@tonic-gate } 25470Sstevel@tonic-gate } 25480Sstevel@tonic-gate 25490Sstevel@tonic-gate /* Last one. Send it upstream. */ 25500Sstevel@tonic-gate mutex_exit(&connfp->connf_lock); 25510Sstevel@tonic-gate 25523448Sdh155122 IP6_STAT(ipst, ip6_udp_fanmb); 255311042SErik.Nordmark@Sun.COM ip_fanout_udp_conn(connp, mp, NULL, ip6h, ira); 25540Sstevel@tonic-gate CONN_DEC_REF(connp); 25550Sstevel@tonic-gate return; 25560Sstevel@tonic-gate 25570Sstevel@tonic-gate notfound: 25580Sstevel@tonic-gate mutex_exit(&connfp->connf_lock); 25590Sstevel@tonic-gate /* 25600Sstevel@tonic-gate * No one bound to this port. Is 25610Sstevel@tonic-gate * there a client that wants all 25620Sstevel@tonic-gate * unclaimed datagrams? 25630Sstevel@tonic-gate */ 25643448Sdh155122 if (ipst->ips_ipcl_proto_fanout_v6[IPPROTO_UDP].connf_head != NULL) { 256511042SErik.Nordmark@Sun.COM ASSERT(ira->ira_protocol == IPPROTO_UDP); 256611042SErik.Nordmark@Sun.COM ip_fanout_proto_v6(mp, ip6h, ira); 25670Sstevel@tonic-gate } else { 256811042SErik.Nordmark@Sun.COM ip_fanout_send_icmp_v6(mp, ICMP6_DST_UNREACH, 256911042SErik.Nordmark@Sun.COM ICMP6_DST_UNREACH_NOPORT, ira); 25700Sstevel@tonic-gate } 25710Sstevel@tonic-gate } 25720Sstevel@tonic-gate 25730Sstevel@tonic-gate /* 25740Sstevel@tonic-gate * int ip_find_hdr_v6() 25750Sstevel@tonic-gate * 257611042SErik.Nordmark@Sun.COM * This routine is used by the upper layer protocols, iptun, and IPsec: 25770Sstevel@tonic-gate * - Set extension header pointers to appropriate locations 25780Sstevel@tonic-gate * - Determine IPv6 header length and return it 25790Sstevel@tonic-gate * - Return a pointer to the last nexthdr value 25800Sstevel@tonic-gate * 25810Sstevel@tonic-gate * The caller must initialize ipp_fields. 258211042SErik.Nordmark@Sun.COM * The upper layer protocols normally set label_separate which makes the 258311042SErik.Nordmark@Sun.COM * routine put the TX label in ipp_label_v6. If this is not set then 258411042SErik.Nordmark@Sun.COM * the hop-by-hop options including the label are placed in ipp_hopopts. 25850Sstevel@tonic-gate * 25860Sstevel@tonic-gate * NOTE: If multiple extension headers of the same type are present, 25870Sstevel@tonic-gate * ip_find_hdr_v6() will set the respective extension header pointers 25880Sstevel@tonic-gate * to the first one that it encounters in the IPv6 header. It also 25890Sstevel@tonic-gate * skips fragment headers. This routine deals with malformed packets 25900Sstevel@tonic-gate * of various sorts in which case the returned length is up to the 25910Sstevel@tonic-gate * malformed part. 25920Sstevel@tonic-gate */ 25930Sstevel@tonic-gate int 259411042SErik.Nordmark@Sun.COM ip_find_hdr_v6(mblk_t *mp, ip6_t *ip6h, boolean_t label_separate, ip_pkt_t *ipp, 259511042SErik.Nordmark@Sun.COM uint8_t *nexthdrp) 25960Sstevel@tonic-gate { 25970Sstevel@tonic-gate uint_t length, ehdrlen; 25980Sstevel@tonic-gate uint8_t nexthdr; 25990Sstevel@tonic-gate uint8_t *whereptr, *endptr; 26000Sstevel@tonic-gate ip6_dest_t *tmpdstopts; 26010Sstevel@tonic-gate ip6_rthdr_t *tmprthdr; 26020Sstevel@tonic-gate ip6_hbh_t *tmphopopts; 26030Sstevel@tonic-gate ip6_frag_t *tmpfraghdr; 26040Sstevel@tonic-gate 260511042SErik.Nordmark@Sun.COM ipp->ipp_fields |= IPPF_HOPLIMIT | IPPF_TCLASS | IPPF_ADDR; 260611042SErik.Nordmark@Sun.COM ipp->ipp_hoplimit = ip6h->ip6_hops; 260711042SErik.Nordmark@Sun.COM ipp->ipp_tclass = IPV6_FLOW_TCLASS(ip6h->ip6_flow); 260811042SErik.Nordmark@Sun.COM ipp->ipp_addr = ip6h->ip6_dst; 260911042SErik.Nordmark@Sun.COM 26100Sstevel@tonic-gate length = IPV6_HDR_LEN; 26110Sstevel@tonic-gate whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */ 26120Sstevel@tonic-gate endptr = mp->b_wptr; 26130Sstevel@tonic-gate 26140Sstevel@tonic-gate nexthdr = ip6h->ip6_nxt; 26150Sstevel@tonic-gate while (whereptr < endptr) { 26160Sstevel@tonic-gate /* Is there enough left for len + nexthdr? */ 26170Sstevel@tonic-gate if (whereptr + MIN_EHDR_LEN > endptr) 26180Sstevel@tonic-gate goto done; 26190Sstevel@tonic-gate 26200Sstevel@tonic-gate switch (nexthdr) { 262111042SErik.Nordmark@Sun.COM case IPPROTO_HOPOPTS: { 262211042SErik.Nordmark@Sun.COM /* We check for any CIPSO */ 262311042SErik.Nordmark@Sun.COM uchar_t *secopt; 262411042SErik.Nordmark@Sun.COM boolean_t hbh_needed; 262511042SErik.Nordmark@Sun.COM uchar_t *after_secopt; 262611042SErik.Nordmark@Sun.COM 26270Sstevel@tonic-gate tmphopopts = (ip6_hbh_t *)whereptr; 26280Sstevel@tonic-gate ehdrlen = 8 * (tmphopopts->ip6h_len + 1); 26290Sstevel@tonic-gate if ((uchar_t *)tmphopopts + ehdrlen > endptr) 26300Sstevel@tonic-gate goto done; 26310Sstevel@tonic-gate nexthdr = tmphopopts->ip6h_nxt; 263211042SErik.Nordmark@Sun.COM 263311042SErik.Nordmark@Sun.COM if (!label_separate) { 263411042SErik.Nordmark@Sun.COM secopt = NULL; 263511042SErik.Nordmark@Sun.COM after_secopt = whereptr; 263611042SErik.Nordmark@Sun.COM } else { 263711042SErik.Nordmark@Sun.COM /* 263811042SErik.Nordmark@Sun.COM * We have dropped packets with bad options in 263911042SErik.Nordmark@Sun.COM * ip6_input. No need to check return value 264011042SErik.Nordmark@Sun.COM * here. 264111042SErik.Nordmark@Sun.COM */ 264211042SErik.Nordmark@Sun.COM (void) tsol_find_secopt_v6(whereptr, ehdrlen, 264311042SErik.Nordmark@Sun.COM &secopt, &after_secopt, &hbh_needed); 264411042SErik.Nordmark@Sun.COM } 264511042SErik.Nordmark@Sun.COM if (secopt != NULL && after_secopt - whereptr > 0) { 264611042SErik.Nordmark@Sun.COM ipp->ipp_fields |= IPPF_LABEL_V6; 264711042SErik.Nordmark@Sun.COM ipp->ipp_label_v6 = secopt; 264811042SErik.Nordmark@Sun.COM ipp->ipp_label_len_v6 = after_secopt - whereptr; 264911042SErik.Nordmark@Sun.COM } else { 265011042SErik.Nordmark@Sun.COM ipp->ipp_label_len_v6 = 0; 265111042SErik.Nordmark@Sun.COM after_secopt = whereptr; 265211042SErik.Nordmark@Sun.COM hbh_needed = B_TRUE; 265311042SErik.Nordmark@Sun.COM } 26540Sstevel@tonic-gate /* return only 1st hbh */ 265511042SErik.Nordmark@Sun.COM if (hbh_needed && !(ipp->ipp_fields & IPPF_HOPOPTS)) { 26560Sstevel@tonic-gate ipp->ipp_fields |= IPPF_HOPOPTS; 265711042SErik.Nordmark@Sun.COM ipp->ipp_hopopts = (ip6_hbh_t *)after_secopt; 265811042SErik.Nordmark@Sun.COM ipp->ipp_hopoptslen = ehdrlen - 265911042SErik.Nordmark@Sun.COM ipp->ipp_label_len_v6; 26600Sstevel@tonic-gate } 26610Sstevel@tonic-gate break; 266211042SErik.Nordmark@Sun.COM } 26630Sstevel@tonic-gate case IPPROTO_DSTOPTS: 26640Sstevel@tonic-gate tmpdstopts = (ip6_dest_t *)whereptr; 26650Sstevel@tonic-gate ehdrlen = 8 * (tmpdstopts->ip6d_len + 1); 26660Sstevel@tonic-gate if ((uchar_t *)tmpdstopts + ehdrlen > endptr) 26670Sstevel@tonic-gate goto done; 26680Sstevel@tonic-gate nexthdr = tmpdstopts->ip6d_nxt; 26690Sstevel@tonic-gate /* 26700Sstevel@tonic-gate * ipp_dstopts is set to the destination header after a 26710Sstevel@tonic-gate * routing header. 26720Sstevel@tonic-gate * Assume it is a post-rthdr destination header 26730Sstevel@tonic-gate * and adjust when we find an rthdr. 26740Sstevel@tonic-gate */ 26750Sstevel@tonic-gate if (!(ipp->ipp_fields & IPPF_DSTOPTS)) { 26760Sstevel@tonic-gate ipp->ipp_fields |= IPPF_DSTOPTS; 26770Sstevel@tonic-gate ipp->ipp_dstopts = tmpdstopts; 26780Sstevel@tonic-gate ipp->ipp_dstoptslen = ehdrlen; 26790Sstevel@tonic-gate } 26800Sstevel@tonic-gate break; 26810Sstevel@tonic-gate case IPPROTO_ROUTING: 26820Sstevel@tonic-gate tmprthdr = (ip6_rthdr_t *)whereptr; 26830Sstevel@tonic-gate ehdrlen = 8 * (tmprthdr->ip6r_len + 1); 26840Sstevel@tonic-gate if ((uchar_t *)tmprthdr + ehdrlen > endptr) 26850Sstevel@tonic-gate goto done; 26860Sstevel@tonic-gate nexthdr = tmprthdr->ip6r_nxt; 26870Sstevel@tonic-gate /* return only 1st rthdr */ 26880Sstevel@tonic-gate if (!(ipp->ipp_fields & IPPF_RTHDR)) { 26890Sstevel@tonic-gate ipp->ipp_fields |= IPPF_RTHDR; 26900Sstevel@tonic-gate ipp->ipp_rthdr = tmprthdr; 26910Sstevel@tonic-gate ipp->ipp_rthdrlen = ehdrlen; 26920Sstevel@tonic-gate } 26930Sstevel@tonic-gate /* 26940Sstevel@tonic-gate * Make any destination header we've seen be a 26950Sstevel@tonic-gate * pre-rthdr destination header. 26960Sstevel@tonic-gate */ 26970Sstevel@tonic-gate if (ipp->ipp_fields & IPPF_DSTOPTS) { 26980Sstevel@tonic-gate ipp->ipp_fields &= ~IPPF_DSTOPTS; 269911042SErik.Nordmark@Sun.COM ipp->ipp_fields |= IPPF_RTHDRDSTOPTS; 270011042SErik.Nordmark@Sun.COM ipp->ipp_rthdrdstopts = ipp->ipp_dstopts; 27010Sstevel@tonic-gate ipp->ipp_dstopts = NULL; 270211042SErik.Nordmark@Sun.COM ipp->ipp_rthdrdstoptslen = ipp->ipp_dstoptslen; 27030Sstevel@tonic-gate ipp->ipp_dstoptslen = 0; 27040Sstevel@tonic-gate } 27050Sstevel@tonic-gate break; 27060Sstevel@tonic-gate case IPPROTO_FRAGMENT: 27070Sstevel@tonic-gate tmpfraghdr = (ip6_frag_t *)whereptr; 27080Sstevel@tonic-gate ehdrlen = sizeof (ip6_frag_t); 27090Sstevel@tonic-gate if ((uchar_t *)tmpfraghdr + ehdrlen > endptr) 27100Sstevel@tonic-gate goto done; 27110Sstevel@tonic-gate nexthdr = tmpfraghdr->ip6f_nxt; 27123055Sdanmcd if (!(ipp->ipp_fields & IPPF_FRAGHDR)) { 27133055Sdanmcd ipp->ipp_fields |= IPPF_FRAGHDR; 27143055Sdanmcd ipp->ipp_fraghdr = tmpfraghdr; 27153055Sdanmcd ipp->ipp_fraghdrlen = ehdrlen; 27163055Sdanmcd } 27170Sstevel@tonic-gate break; 27180Sstevel@tonic-gate case IPPROTO_NONE: 27190Sstevel@tonic-gate default: 27200Sstevel@tonic-gate goto done; 27210Sstevel@tonic-gate } 27220Sstevel@tonic-gate length += ehdrlen; 27230Sstevel@tonic-gate whereptr += ehdrlen; 27240Sstevel@tonic-gate } 27250Sstevel@tonic-gate done: 27260Sstevel@tonic-gate if (nexthdrp != NULL) 27270Sstevel@tonic-gate *nexthdrp = nexthdr; 27280Sstevel@tonic-gate return (length); 27290Sstevel@tonic-gate } 27300Sstevel@tonic-gate 27310Sstevel@tonic-gate /* 27320Sstevel@tonic-gate * Try to determine where and what are the IPv6 header length and 27330Sstevel@tonic-gate * pointer to nexthdr value for the upper layer protocol (or an 27340Sstevel@tonic-gate * unknown next hdr). 27350Sstevel@tonic-gate * 27360Sstevel@tonic-gate * Parameters returns a pointer to the nexthdr value; 27370Sstevel@tonic-gate * Must handle malformed packets of various sorts. 27380Sstevel@tonic-gate * Function returns failure for malformed cases. 27390Sstevel@tonic-gate */ 27400Sstevel@tonic-gate boolean_t 27410Sstevel@tonic-gate ip_hdr_length_nexthdr_v6(mblk_t *mp, ip6_t *ip6h, uint16_t *hdr_length_ptr, 27420Sstevel@tonic-gate uint8_t **nexthdrpp) 27430Sstevel@tonic-gate { 27440Sstevel@tonic-gate uint16_t length; 27450Sstevel@tonic-gate uint_t ehdrlen; 27460Sstevel@tonic-gate uint8_t *nexthdrp; 27470Sstevel@tonic-gate uint8_t *whereptr; 27480Sstevel@tonic-gate uint8_t *endptr; 27490Sstevel@tonic-gate ip6_dest_t *desthdr; 27500Sstevel@tonic-gate ip6_rthdr_t *rthdr; 27510Sstevel@tonic-gate ip6_frag_t *fraghdr; 27520Sstevel@tonic-gate 275311042SErik.Nordmark@Sun.COM ASSERT(IPH_HDR_VERSION(ip6h) == IPV6_VERSION); 27540Sstevel@tonic-gate length = IPV6_HDR_LEN; 27550Sstevel@tonic-gate whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */ 27560Sstevel@tonic-gate endptr = mp->b_wptr; 27570Sstevel@tonic-gate 27580Sstevel@tonic-gate nexthdrp = &ip6h->ip6_nxt; 27590Sstevel@tonic-gate while (whereptr < endptr) { 27600Sstevel@tonic-gate /* Is there enough left for len + nexthdr? */ 27610Sstevel@tonic-gate if (whereptr + MIN_EHDR_LEN > endptr) 27620Sstevel@tonic-gate break; 27630Sstevel@tonic-gate 27640Sstevel@tonic-gate switch (*nexthdrp) { 27650Sstevel@tonic-gate case IPPROTO_HOPOPTS: 27660Sstevel@tonic-gate case IPPROTO_DSTOPTS: 27670Sstevel@tonic-gate /* Assumes the headers are identical for hbh and dst */ 27680Sstevel@tonic-gate desthdr = (ip6_dest_t *)whereptr; 27690Sstevel@tonic-gate ehdrlen = 8 * (desthdr->ip6d_len + 1); 27700Sstevel@tonic-gate if ((uchar_t *)desthdr + ehdrlen > endptr) 27710Sstevel@tonic-gate return (B_FALSE); 27720Sstevel@tonic-gate nexthdrp = &desthdr->ip6d_nxt; 27730Sstevel@tonic-gate break; 27740Sstevel@tonic-gate case IPPROTO_ROUTING: 27750Sstevel@tonic-gate rthdr = (ip6_rthdr_t *)whereptr; 27760Sstevel@tonic-gate ehdrlen = 8 * (rthdr->ip6r_len + 1); 27770Sstevel@tonic-gate if ((uchar_t *)rthdr + ehdrlen > endptr) 27780Sstevel@tonic-gate return (B_FALSE); 27790Sstevel@tonic-gate nexthdrp = &rthdr->ip6r_nxt; 27800Sstevel@tonic-gate break; 27810Sstevel@tonic-gate case IPPROTO_FRAGMENT: 27820Sstevel@tonic-gate fraghdr = (ip6_frag_t *)whereptr; 27830Sstevel@tonic-gate ehdrlen = sizeof (ip6_frag_t); 27840Sstevel@tonic-gate if ((uchar_t *)&fraghdr[1] > endptr) 27850Sstevel@tonic-gate return (B_FALSE); 27860Sstevel@tonic-gate nexthdrp = &fraghdr->ip6f_nxt; 27870Sstevel@tonic-gate break; 27880Sstevel@tonic-gate case IPPROTO_NONE: 27890Sstevel@tonic-gate /* No next header means we're finished */ 27900Sstevel@tonic-gate default: 27910Sstevel@tonic-gate *hdr_length_ptr = length; 27920Sstevel@tonic-gate *nexthdrpp = nexthdrp; 27930Sstevel@tonic-gate return (B_TRUE); 27940Sstevel@tonic-gate } 27950Sstevel@tonic-gate length += ehdrlen; 27960Sstevel@tonic-gate whereptr += ehdrlen; 27970Sstevel@tonic-gate *hdr_length_ptr = length; 27980Sstevel@tonic-gate *nexthdrpp = nexthdrp; 27990Sstevel@tonic-gate } 28000Sstevel@tonic-gate switch (*nexthdrp) { 28010Sstevel@tonic-gate case IPPROTO_HOPOPTS: 28020Sstevel@tonic-gate case IPPROTO_DSTOPTS: 28030Sstevel@tonic-gate case IPPROTO_ROUTING: 28040Sstevel@tonic-gate case IPPROTO_FRAGMENT: 28050Sstevel@tonic-gate /* 28060Sstevel@tonic-gate * If any know extension headers are still to be processed, 28070Sstevel@tonic-gate * the packet's malformed (or at least all the IP header(s) are 28080Sstevel@tonic-gate * not in the same mblk - and that should never happen. 28090Sstevel@tonic-gate */ 28100Sstevel@tonic-gate return (B_FALSE); 28110Sstevel@tonic-gate 28120Sstevel@tonic-gate default: 28130Sstevel@tonic-gate /* 28140Sstevel@tonic-gate * If we get here, we know that all of the IP headers were in 28150Sstevel@tonic-gate * the same mblk, even if the ULP header is in the next mblk. 28160Sstevel@tonic-gate */ 28170Sstevel@tonic-gate *hdr_length_ptr = length; 28180Sstevel@tonic-gate *nexthdrpp = nexthdrp; 28190Sstevel@tonic-gate return (B_TRUE); 28200Sstevel@tonic-gate } 28210Sstevel@tonic-gate } 28220Sstevel@tonic-gate 28230Sstevel@tonic-gate /* 28240Sstevel@tonic-gate * Return the length of the IPv6 related headers (including extension headers) 28250Sstevel@tonic-gate * Returns a length even if the packet is malformed. 28260Sstevel@tonic-gate */ 28270Sstevel@tonic-gate int 28280Sstevel@tonic-gate ip_hdr_length_v6(mblk_t *mp, ip6_t *ip6h) 28290Sstevel@tonic-gate { 28300Sstevel@tonic-gate uint16_t hdr_len; 28310Sstevel@tonic-gate uint8_t *nexthdrp; 28320Sstevel@tonic-gate 28330Sstevel@tonic-gate (void) ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_len, &nexthdrp); 28340Sstevel@tonic-gate return (hdr_len); 28350Sstevel@tonic-gate } 28360Sstevel@tonic-gate 28370Sstevel@tonic-gate /* 28380Sstevel@tonic-gate * Parse and process any hop-by-hop or destination options. 28390Sstevel@tonic-gate * 28400Sstevel@tonic-gate * Assumes that q is an ill read queue so that ICMP errors for link-local 28410Sstevel@tonic-gate * destinations are sent out the correct interface. 28420Sstevel@tonic-gate * 28430Sstevel@tonic-gate * Returns -1 if there was an error and mp has been consumed. 28440Sstevel@tonic-gate * Returns 0 if no special action is needed. 28450Sstevel@tonic-gate * Returns 1 if the packet contained a router alert option for this node 28460Sstevel@tonic-gate * which is verified to be "interesting/known" for our implementation. 28470Sstevel@tonic-gate * 28480Sstevel@tonic-gate * XXX Note: In future as more hbh or dest options are defined, 28490Sstevel@tonic-gate * it may be better to have different routines for hbh and dest 28500Sstevel@tonic-gate * options as opt_type fields other than IP6OPT_PAD1 and IP6OPT_PADN 28510Sstevel@tonic-gate * may have same value in different namespaces. Or is it same namespace ?? 28520Sstevel@tonic-gate * Current code checks for each opt_type (other than pads) if it is in 28530Sstevel@tonic-gate * the expected nexthdr (hbh or dest) 28540Sstevel@tonic-gate */ 285511042SErik.Nordmark@Sun.COM int 285611042SErik.Nordmark@Sun.COM ip_process_options_v6(mblk_t *mp, ip6_t *ip6h, 285711042SErik.Nordmark@Sun.COM uint8_t *optptr, uint_t optlen, uint8_t hdr_type, ip_recv_attr_t *ira) 28580Sstevel@tonic-gate { 28590Sstevel@tonic-gate uint8_t opt_type; 28600Sstevel@tonic-gate uint_t optused; 28610Sstevel@tonic-gate int ret = 0; 28621676Sjpk const char *errtype; 286311042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; 286411042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst; 28650Sstevel@tonic-gate 28660Sstevel@tonic-gate while (optlen != 0) { 28670Sstevel@tonic-gate opt_type = *optptr; 28680Sstevel@tonic-gate if (opt_type == IP6OPT_PAD1) { 28690Sstevel@tonic-gate optused = 1; 28700Sstevel@tonic-gate } else { 28710Sstevel@tonic-gate if (optlen < 2) 28720Sstevel@tonic-gate goto bad_opt; 28731676Sjpk errtype = "malformed"; 28741676Sjpk if (opt_type == ip6opt_ls) { 28751676Sjpk optused = 2 + optptr[1]; 28761676Sjpk if (optused > optlen) 28771676Sjpk goto bad_opt; 28781676Sjpk } else switch (opt_type) { 28790Sstevel@tonic-gate case IP6OPT_PADN: 28800Sstevel@tonic-gate /* 28810Sstevel@tonic-gate * Note:We don't verify that (N-2) pad octets 28820Sstevel@tonic-gate * are zero as required by spec. Adhere to 28830Sstevel@tonic-gate * "be liberal in what you accept..." part of 28840Sstevel@tonic-gate * implementation philosophy (RFC791,RFC1122) 28850Sstevel@tonic-gate */ 28860Sstevel@tonic-gate optused = 2 + optptr[1]; 28870Sstevel@tonic-gate if (optused > optlen) 28880Sstevel@tonic-gate goto bad_opt; 28890Sstevel@tonic-gate break; 28900Sstevel@tonic-gate 28910Sstevel@tonic-gate case IP6OPT_JUMBO: 28920Sstevel@tonic-gate if (hdr_type != IPPROTO_HOPOPTS) 28930Sstevel@tonic-gate goto opt_error; 28940Sstevel@tonic-gate goto opt_error; /* XXX Not implemented! */ 28950Sstevel@tonic-gate 28960Sstevel@tonic-gate case IP6OPT_ROUTER_ALERT: { 28970Sstevel@tonic-gate struct ip6_opt_router *or; 28980Sstevel@tonic-gate 28990Sstevel@tonic-gate if (hdr_type != IPPROTO_HOPOPTS) 29000Sstevel@tonic-gate goto opt_error; 29010Sstevel@tonic-gate optused = 2 + optptr[1]; 29020Sstevel@tonic-gate if (optused > optlen) 29030Sstevel@tonic-gate goto bad_opt; 29040Sstevel@tonic-gate or = (struct ip6_opt_router *)optptr; 29050Sstevel@tonic-gate /* Check total length and alignment */ 29060Sstevel@tonic-gate if (optused != sizeof (*or) || 29070Sstevel@tonic-gate ((uintptr_t)or->ip6or_value & 0x1) != 0) 29080Sstevel@tonic-gate goto opt_error; 29090Sstevel@tonic-gate /* Check value */ 29100Sstevel@tonic-gate switch (*((uint16_t *)or->ip6or_value)) { 29110Sstevel@tonic-gate case IP6_ALERT_MLD: 29120Sstevel@tonic-gate case IP6_ALERT_RSVP: 29130Sstevel@tonic-gate ret = 1; 29140Sstevel@tonic-gate } 29150Sstevel@tonic-gate break; 29160Sstevel@tonic-gate } 29170Sstevel@tonic-gate case IP6OPT_HOME_ADDRESS: { 29180Sstevel@tonic-gate /* 29190Sstevel@tonic-gate * Minimal support for the home address option 29200Sstevel@tonic-gate * (which is required by all IPv6 nodes). 29210Sstevel@tonic-gate * Implement by just swapping the home address 29220Sstevel@tonic-gate * and source address. 29230Sstevel@tonic-gate * XXX Note: this has IPsec implications since 29240Sstevel@tonic-gate * AH needs to take this into account. 29250Sstevel@tonic-gate * Also, when IPsec is used we need to ensure 29260Sstevel@tonic-gate * that this is only processed once 29270Sstevel@tonic-gate * in the received packet (to avoid swapping 29280Sstevel@tonic-gate * back and forth). 29290Sstevel@tonic-gate * NOTE:This option processing is considered 29300Sstevel@tonic-gate * to be unsafe and prone to a denial of 29310Sstevel@tonic-gate * service attack. 29320Sstevel@tonic-gate * The current processing is not safe even with 29330Sstevel@tonic-gate * IPsec secured IP packets. Since the home 29340Sstevel@tonic-gate * address option processing requirement still 29350Sstevel@tonic-gate * is in the IETF draft and in the process of 29360Sstevel@tonic-gate * being redefined for its usage, it has been 29370Sstevel@tonic-gate * decided to turn off the option by default. 29380Sstevel@tonic-gate * If this section of code needs to be executed, 29390Sstevel@tonic-gate * ndd variable ip6_ignore_home_address_opt 29400Sstevel@tonic-gate * should be set to 0 at the user's own risk. 29410Sstevel@tonic-gate */ 29420Sstevel@tonic-gate struct ip6_opt_home_address *oh; 29430Sstevel@tonic-gate in6_addr_t tmp; 29440Sstevel@tonic-gate 29453448Sdh155122 if (ipst->ips_ipv6_ignore_home_address_opt) 29460Sstevel@tonic-gate goto opt_error; 29470Sstevel@tonic-gate 29480Sstevel@tonic-gate if (hdr_type != IPPROTO_DSTOPTS) 29490Sstevel@tonic-gate goto opt_error; 29500Sstevel@tonic-gate optused = 2 + optptr[1]; 29510Sstevel@tonic-gate if (optused > optlen) 29520Sstevel@tonic-gate goto bad_opt; 29530Sstevel@tonic-gate 29540Sstevel@tonic-gate /* 29550Sstevel@tonic-gate * We did this dest. opt the first time 29560Sstevel@tonic-gate * around (i.e. before AH processing). 29570Sstevel@tonic-gate * If we've done AH... stop now. 29580Sstevel@tonic-gate */ 295911042SErik.Nordmark@Sun.COM if ((ira->ira_flags & IRAF_IPSEC_SECURE) && 296011042SErik.Nordmark@Sun.COM ira->ira_ipsec_ah_sa != NULL) 296111042SErik.Nordmark@Sun.COM break; 29620Sstevel@tonic-gate 29630Sstevel@tonic-gate oh = (struct ip6_opt_home_address *)optptr; 29640Sstevel@tonic-gate /* Check total length and alignment */ 29650Sstevel@tonic-gate if (optused < sizeof (*oh) || 29660Sstevel@tonic-gate ((uintptr_t)oh->ip6oh_addr & 0x7) != 0) 29670Sstevel@tonic-gate goto opt_error; 29680Sstevel@tonic-gate /* Swap ip6_src and the home address */ 29690Sstevel@tonic-gate tmp = ip6h->ip6_src; 29700Sstevel@tonic-gate /* XXX Note: only 8 byte alignment option */ 29710Sstevel@tonic-gate ip6h->ip6_src = *(in6_addr_t *)oh->ip6oh_addr; 29720Sstevel@tonic-gate *(in6_addr_t *)oh->ip6oh_addr = tmp; 29730Sstevel@tonic-gate break; 29740Sstevel@tonic-gate } 29750Sstevel@tonic-gate 29760Sstevel@tonic-gate case IP6OPT_TUNNEL_LIMIT: 29770Sstevel@tonic-gate if (hdr_type != IPPROTO_DSTOPTS) { 29780Sstevel@tonic-gate goto opt_error; 29790Sstevel@tonic-gate } 29800Sstevel@tonic-gate optused = 2 + optptr[1]; 29810Sstevel@tonic-gate if (optused > optlen) { 29820Sstevel@tonic-gate goto bad_opt; 29830Sstevel@tonic-gate } 29840Sstevel@tonic-gate if (optused != 3) { 29850Sstevel@tonic-gate goto opt_error; 29860Sstevel@tonic-gate } 29870Sstevel@tonic-gate break; 29880Sstevel@tonic-gate 29890Sstevel@tonic-gate default: 29901676Sjpk errtype = "unknown"; 29911676Sjpk /* FALLTHROUGH */ 29920Sstevel@tonic-gate opt_error: 29932733Snordmark /* Determine which zone should send error */ 29940Sstevel@tonic-gate switch (IP6OPT_TYPE(opt_type)) { 29950Sstevel@tonic-gate case IP6OPT_TYPE_SKIP: 29960Sstevel@tonic-gate optused = 2 + optptr[1]; 29970Sstevel@tonic-gate if (optused > optlen) 29980Sstevel@tonic-gate goto bad_opt; 29991676Sjpk ip1dbg(("ip_process_options_v6: %s " 30001676Sjpk "opt 0x%x skipped\n", 30011676Sjpk errtype, opt_type)); 30020Sstevel@tonic-gate break; 30030Sstevel@tonic-gate case IP6OPT_TYPE_DISCARD: 30041676Sjpk ip1dbg(("ip_process_options_v6: %s " 30051676Sjpk "opt 0x%x; packet dropped\n", 30061676Sjpk errtype, opt_type)); 300711042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, 300811042SErik.Nordmark@Sun.COM ipIfStatsInHdrErrors); 300911042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInHdrErrors", 301011042SErik.Nordmark@Sun.COM mp, ill); 301111042SErik.Nordmark@Sun.COM freemsg(mp); 30120Sstevel@tonic-gate return (-1); 30130Sstevel@tonic-gate case IP6OPT_TYPE_ICMP: 301411042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, 301511042SErik.Nordmark@Sun.COM ipIfStatsInHdrErrors); 301611042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInHdrErrors", 301711042SErik.Nordmark@Sun.COM mp, ill); 301811042SErik.Nordmark@Sun.COM icmp_param_problem_v6(mp, 30190Sstevel@tonic-gate ICMP6_PARAMPROB_OPTION, 30200Sstevel@tonic-gate (uint32_t)(optptr - 30210Sstevel@tonic-gate (uint8_t *)ip6h), 302211042SErik.Nordmark@Sun.COM B_FALSE, ira); 30230Sstevel@tonic-gate return (-1); 30240Sstevel@tonic-gate case IP6OPT_TYPE_FORCEICMP: 302511042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, 302611042SErik.Nordmark@Sun.COM ipIfStatsInHdrErrors); 302711042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInHdrErrors", 302811042SErik.Nordmark@Sun.COM mp, ill); 302911042SErik.Nordmark@Sun.COM icmp_param_problem_v6(mp, 30300Sstevel@tonic-gate ICMP6_PARAMPROB_OPTION, 30310Sstevel@tonic-gate (uint32_t)(optptr - 30320Sstevel@tonic-gate (uint8_t *)ip6h), 303311042SErik.Nordmark@Sun.COM B_TRUE, ira); 30340Sstevel@tonic-gate return (-1); 30351676Sjpk default: 30361676Sjpk ASSERT(0); 30370Sstevel@tonic-gate } 30380Sstevel@tonic-gate } 30390Sstevel@tonic-gate } 30400Sstevel@tonic-gate optlen -= optused; 30410Sstevel@tonic-gate optptr += optused; 30420Sstevel@tonic-gate } 30430Sstevel@tonic-gate return (ret); 30440Sstevel@tonic-gate 30450Sstevel@tonic-gate bad_opt: 30462733Snordmark /* Determine which zone should send error */ 304711042SErik.Nordmark@Sun.COM ip_drop_input("ICMP_PARAM_PROBLEM", mp, ill); 304811042SErik.Nordmark@Sun.COM icmp_param_problem_v6(mp, ICMP6_PARAMPROB_OPTION, 304911042SErik.Nordmark@Sun.COM (uint32_t)(optptr - (uint8_t *)ip6h), 305011042SErik.Nordmark@Sun.COM B_FALSE, ira); 30510Sstevel@tonic-gate return (-1); 30520Sstevel@tonic-gate } 30530Sstevel@tonic-gate 30540Sstevel@tonic-gate /* 30550Sstevel@tonic-gate * Process a routing header that is not yet empty. 305610031SBrian.Utterback@Sun.COM * Because of RFC 5095, we now reject all route headers. 30570Sstevel@tonic-gate */ 305811042SErik.Nordmark@Sun.COM void 305911042SErik.Nordmark@Sun.COM ip_process_rthdr(mblk_t *mp, ip6_t *ip6h, ip6_rthdr_t *rth, 306011042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira) 30610Sstevel@tonic-gate { 306211042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; 30633448Sdh155122 ip_stack_t *ipst = ill->ill_ipst; 30640Sstevel@tonic-gate 30650Sstevel@tonic-gate ASSERT(rth->ip6r_segleft != 0); 30660Sstevel@tonic-gate 30673448Sdh155122 if (!ipst->ips_ipv6_forward_src_routed) { 30680Sstevel@tonic-gate /* XXX Check for source routed out same interface? */ 30693284Sapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsForwProhibits); 30703284Sapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInAddrErrors); 307111042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInAddrErrors", mp, ill); 30720Sstevel@tonic-gate freemsg(mp); 30730Sstevel@tonic-gate return; 30740Sstevel@tonic-gate } 307511042SErik.Nordmark@Sun.COM 307611042SErik.Nordmark@Sun.COM ip_drop_input("ICMP_PARAM_PROBLEM", mp, ill); 307711042SErik.Nordmark@Sun.COM icmp_param_problem_v6(mp, ICMP6_PARAMPROB_HEADER, 307811042SErik.Nordmark@Sun.COM (uint32_t)((uchar_t *)&rth->ip6r_type - (uchar_t *)ip6h), 307911042SErik.Nordmark@Sun.COM B_FALSE, ira); 30800Sstevel@tonic-gate } 30810Sstevel@tonic-gate 30820Sstevel@tonic-gate /* 30830Sstevel@tonic-gate * Read side put procedure for IPv6 module. 30840Sstevel@tonic-gate */ 30852958Sdr146992 void 30860Sstevel@tonic-gate ip_rput_v6(queue_t *q, mblk_t *mp) 30870Sstevel@tonic-gate { 30880Sstevel@tonic-gate ill_t *ill; 30890Sstevel@tonic-gate 30900Sstevel@tonic-gate ill = (ill_t *)q->q_ptr; 309111042SErik.Nordmark@Sun.COM if (ill->ill_state_flags & (ILL_CONDEMNED | ILL_LL_SUBNET_PENDING)) { 30920Sstevel@tonic-gate union DL_primitives *dl; 30930Sstevel@tonic-gate 30940Sstevel@tonic-gate dl = (union DL_primitives *)mp->b_rptr; 30950Sstevel@tonic-gate /* 30960Sstevel@tonic-gate * Things are opening or closing - only accept DLPI 30970Sstevel@tonic-gate * ack messages. If the stream is closing and ip_wsrv 30980Sstevel@tonic-gate * has completed, ip_close is out of the qwait, but has 30990Sstevel@tonic-gate * not yet completed qprocsoff. Don't proceed any further 31000Sstevel@tonic-gate * because the ill has been cleaned up and things hanging 31010Sstevel@tonic-gate * off the ill have been freed. 31020Sstevel@tonic-gate */ 31030Sstevel@tonic-gate if ((mp->b_datap->db_type != M_PCPROTO) || 31040Sstevel@tonic-gate (dl->dl_primitive == DL_UNITDATA_IND)) { 3105741Smasputra inet_freemsg(mp); 31060Sstevel@tonic-gate return; 31070Sstevel@tonic-gate } 31080Sstevel@tonic-gate } 310911042SErik.Nordmark@Sun.COM if (DB_TYPE(mp) == M_DATA) { 311011042SErik.Nordmark@Sun.COM struct mac_header_info_s mhi; 311111042SErik.Nordmark@Sun.COM 311211042SErik.Nordmark@Sun.COM ip_mdata_to_mhi(ill, mp, &mhi); 311311042SErik.Nordmark@Sun.COM ip_input_v6(ill, NULL, mp, &mhi); 311411042SErik.Nordmark@Sun.COM } else { 311511042SErik.Nordmark@Sun.COM ip_rput_notdata(ill, mp); 31162546Scarlsonj } 31170Sstevel@tonic-gate } 31180Sstevel@tonic-gate 31190Sstevel@tonic-gate /* 31200Sstevel@tonic-gate * Walk through the IPv6 packet in mp and see if there's an AH header 31210Sstevel@tonic-gate * in it. See if the AH header needs to get done before other headers in 31220Sstevel@tonic-gate * the packet. (Worker function for ipsec_early_ah_v6().) 31230Sstevel@tonic-gate */ 31240Sstevel@tonic-gate #define IPSEC_HDR_DONT_PROCESS 0 31250Sstevel@tonic-gate #define IPSEC_HDR_PROCESS 1 31268626Ssommerfeld@sun.com #define IPSEC_MEMORY_ERROR 2 /* or malformed packet */ 31270Sstevel@tonic-gate static int 31280Sstevel@tonic-gate ipsec_needs_processing_v6(mblk_t *mp, uint8_t *nexthdr) 31290Sstevel@tonic-gate { 31300Sstevel@tonic-gate uint_t length; 31310Sstevel@tonic-gate uint_t ehdrlen; 31320Sstevel@tonic-gate uint8_t *whereptr; 31330Sstevel@tonic-gate uint8_t *endptr; 31340Sstevel@tonic-gate uint8_t *nexthdrp; 31350Sstevel@tonic-gate ip6_dest_t *desthdr; 31360Sstevel@tonic-gate ip6_rthdr_t *rthdr; 31370Sstevel@tonic-gate ip6_t *ip6h; 31380Sstevel@tonic-gate 31390Sstevel@tonic-gate /* 31400Sstevel@tonic-gate * For now just pullup everything. In general, the less pullups, 31410Sstevel@tonic-gate * the better, but there's so much squirrelling through anyway, 31420Sstevel@tonic-gate * it's just easier this way. 31430Sstevel@tonic-gate */ 31440Sstevel@tonic-gate if (!pullupmsg(mp, -1)) { 31450Sstevel@tonic-gate return (IPSEC_MEMORY_ERROR); 31460Sstevel@tonic-gate } 31470Sstevel@tonic-gate 31480Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 31490Sstevel@tonic-gate length = IPV6_HDR_LEN; 31500Sstevel@tonic-gate whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */ 31510Sstevel@tonic-gate endptr = mp->b_wptr; 31520Sstevel@tonic-gate 31530Sstevel@tonic-gate /* 31540Sstevel@tonic-gate * We can't just use the argument nexthdr in the place 31550Sstevel@tonic-gate * of nexthdrp becaue we don't dereference nexthdrp 31560Sstevel@tonic-gate * till we confirm whether it is a valid address. 31570Sstevel@tonic-gate */ 31580Sstevel@tonic-gate nexthdrp = &ip6h->ip6_nxt; 31590Sstevel@tonic-gate while (whereptr < endptr) { 31600Sstevel@tonic-gate /* Is there enough left for len + nexthdr? */ 31610Sstevel@tonic-gate if (whereptr + MIN_EHDR_LEN > endptr) 31620Sstevel@tonic-gate return (IPSEC_MEMORY_ERROR); 31630Sstevel@tonic-gate 31640Sstevel@tonic-gate switch (*nexthdrp) { 31650Sstevel@tonic-gate case IPPROTO_HOPOPTS: 31660Sstevel@tonic-gate case IPPROTO_DSTOPTS: 31670Sstevel@tonic-gate /* Assumes the headers are identical for hbh and dst */ 31680Sstevel@tonic-gate desthdr = (ip6_dest_t *)whereptr; 31690Sstevel@tonic-gate ehdrlen = 8 * (desthdr->ip6d_len + 1); 31700Sstevel@tonic-gate if ((uchar_t *)desthdr + ehdrlen > endptr) 31710Sstevel@tonic-gate return (IPSEC_MEMORY_ERROR); 31720Sstevel@tonic-gate /* 31734823Sseb * Return DONT_PROCESS because the destination 31744823Sseb * options header may be for each hop in a 31754823Sseb * routing-header, and we only want AH if we're 31764823Sseb * finished with routing headers. 31770Sstevel@tonic-gate */ 31780Sstevel@tonic-gate if (*nexthdrp == IPPROTO_DSTOPTS) 31790Sstevel@tonic-gate return (IPSEC_HDR_DONT_PROCESS); 31800Sstevel@tonic-gate nexthdrp = &desthdr->ip6d_nxt; 31810Sstevel@tonic-gate break; 31820Sstevel@tonic-gate case IPPROTO_ROUTING: 31830Sstevel@tonic-gate rthdr = (ip6_rthdr_t *)whereptr; 31840Sstevel@tonic-gate 31850Sstevel@tonic-gate /* 31860Sstevel@tonic-gate * If there's more hops left on the routing header, 31870Sstevel@tonic-gate * return now with DON'T PROCESS. 31880Sstevel@tonic-gate */ 31890Sstevel@tonic-gate if (rthdr->ip6r_segleft > 0) 31900Sstevel@tonic-gate return (IPSEC_HDR_DONT_PROCESS); 31910Sstevel@tonic-gate 31920Sstevel@tonic-gate ehdrlen = 8 * (rthdr->ip6r_len + 1); 31930Sstevel@tonic-gate if ((uchar_t *)rthdr + ehdrlen > endptr) 31940Sstevel@tonic-gate return (IPSEC_MEMORY_ERROR); 31950Sstevel@tonic-gate nexthdrp = &rthdr->ip6r_nxt; 31960Sstevel@tonic-gate break; 31970Sstevel@tonic-gate case IPPROTO_FRAGMENT: 31980Sstevel@tonic-gate /* Wait for reassembly */ 31990Sstevel@tonic-gate return (IPSEC_HDR_DONT_PROCESS); 32000Sstevel@tonic-gate case IPPROTO_AH: 32010Sstevel@tonic-gate *nexthdr = IPPROTO_AH; 32020Sstevel@tonic-gate return (IPSEC_HDR_PROCESS); 32030Sstevel@tonic-gate case IPPROTO_NONE: 32040Sstevel@tonic-gate /* No next header means we're finished */ 32050Sstevel@tonic-gate default: 32060Sstevel@tonic-gate return (IPSEC_HDR_DONT_PROCESS); 32070Sstevel@tonic-gate } 32080Sstevel@tonic-gate length += ehdrlen; 32090Sstevel@tonic-gate whereptr += ehdrlen; 32100Sstevel@tonic-gate } 32118626Ssommerfeld@sun.com /* 32128626Ssommerfeld@sun.com * Malformed/truncated packet. 32138626Ssommerfeld@sun.com */ 32148626Ssommerfeld@sun.com return (IPSEC_MEMORY_ERROR); 32150Sstevel@tonic-gate } 32160Sstevel@tonic-gate 32170Sstevel@tonic-gate /* 321811042SErik.Nordmark@Sun.COM * Path for AH if options are present. 321911042SErik.Nordmark@Sun.COM * Returns NULL if the mblk was consumed. 32200Sstevel@tonic-gate * 32210Sstevel@tonic-gate * Sometimes AH needs to be done before other IPv6 headers for security 32220Sstevel@tonic-gate * reasons. This function (and its ipsec_needs_processing_v6() above) 32230Sstevel@tonic-gate * indicates if that is so, and fans out to the appropriate IPsec protocol 32240Sstevel@tonic-gate * for the datagram passed in. 32250Sstevel@tonic-gate */ 322611042SErik.Nordmark@Sun.COM mblk_t * 322711042SErik.Nordmark@Sun.COM ipsec_early_ah_v6(mblk_t *mp, ip_recv_attr_t *ira) 32280Sstevel@tonic-gate { 32290Sstevel@tonic-gate uint8_t nexthdr; 32300Sstevel@tonic-gate ah_t *ah; 323111042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; 32323448Sdh155122 ip_stack_t *ipst = ill->ill_ipst; 323311042SErik.Nordmark@Sun.COM ipsec_stack_t *ipss = ipst->ips_netstack->netstack_ipsec; 323411042SErik.Nordmark@Sun.COM 323511042SErik.Nordmark@Sun.COM switch (ipsec_needs_processing_v6(mp, &nexthdr)) { 32360Sstevel@tonic-gate case IPSEC_MEMORY_ERROR: 32373284Sapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 323811042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards", mp, ill); 323911042SErik.Nordmark@Sun.COM freemsg(mp); 324011042SErik.Nordmark@Sun.COM return (NULL); 32410Sstevel@tonic-gate case IPSEC_HDR_DONT_PROCESS: 324211042SErik.Nordmark@Sun.COM return (mp); 32430Sstevel@tonic-gate } 32440Sstevel@tonic-gate 32450Sstevel@tonic-gate /* Default means send it to AH! */ 32460Sstevel@tonic-gate ASSERT(nexthdr == IPPROTO_AH); 32470Sstevel@tonic-gate 32483448Sdh155122 if (!ipsec_loaded(ipss)) { 324911042SErik.Nordmark@Sun.COM ip_proto_not_sup(mp, ira); 325011042SErik.Nordmark@Sun.COM return (NULL); 32510Sstevel@tonic-gate } 32520Sstevel@tonic-gate 325311042SErik.Nordmark@Sun.COM mp = ipsec_inbound_ah_sa(mp, ira, &ah); 325411042SErik.Nordmark@Sun.COM if (mp == NULL) 325511042SErik.Nordmark@Sun.COM return (NULL); 325611042SErik.Nordmark@Sun.COM ASSERT(ah != NULL); 325711042SErik.Nordmark@Sun.COM ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE); 325811042SErik.Nordmark@Sun.COM ASSERT(ira->ira_ipsec_ah_sa != NULL); 325911042SErik.Nordmark@Sun.COM ASSERT(ira->ira_ipsec_ah_sa->ipsa_input_func != NULL); 326011042SErik.Nordmark@Sun.COM mp = ira->ira_ipsec_ah_sa->ipsa_input_func(mp, ah, ira); 326111042SErik.Nordmark@Sun.COM 326211042SErik.Nordmark@Sun.COM if (mp == NULL) { 326311042SErik.Nordmark@Sun.COM /* 326411042SErik.Nordmark@Sun.COM * Either it failed or is pending. In the former case 326511042SErik.Nordmark@Sun.COM * ipIfStatsInDiscards was increased. 326611042SErik.Nordmark@Sun.COM */ 326711042SErik.Nordmark@Sun.COM return (NULL); 32680Sstevel@tonic-gate } 326911042SErik.Nordmark@Sun.COM 327011042SErik.Nordmark@Sun.COM /* we're done with IPsec processing, send it up */ 327111042SErik.Nordmark@Sun.COM ip_input_post_ipsec(mp, ira); 327211042SErik.Nordmark@Sun.COM return (NULL); 32730Sstevel@tonic-gate } 32740Sstevel@tonic-gate 32750Sstevel@tonic-gate /* 32760Sstevel@tonic-gate * Reassemble fragment. 32770Sstevel@tonic-gate * When it returns a completed message the first mblk will only contain 327811042SErik.Nordmark@Sun.COM * the headers prior to the fragment header, with the nexthdr value updated 327911042SErik.Nordmark@Sun.COM * to be the header after the fragment header. 32800Sstevel@tonic-gate */ 328111042SErik.Nordmark@Sun.COM mblk_t * 328211042SErik.Nordmark@Sun.COM ip_input_fragment_v6(mblk_t *mp, ip6_t *ip6h, 328311042SErik.Nordmark@Sun.COM ip6_frag_t *fraghdr, uint_t remlen, ip_recv_attr_t *ira) 32840Sstevel@tonic-gate { 32850Sstevel@tonic-gate uint32_t ident = ntohl(fraghdr->ip6f_ident); 32860Sstevel@tonic-gate uint16_t offset; 32870Sstevel@tonic-gate boolean_t more_frags; 32880Sstevel@tonic-gate uint8_t nexthdr = fraghdr->ip6f_nxt; 32890Sstevel@tonic-gate in6_addr_t *v6dst_ptr; 32900Sstevel@tonic-gate in6_addr_t *v6src_ptr; 32910Sstevel@tonic-gate uint_t end; 32920Sstevel@tonic-gate uint_t hdr_length; 32930Sstevel@tonic-gate size_t count; 32940Sstevel@tonic-gate ipf_t *ipf; 32950Sstevel@tonic-gate ipf_t **ipfp; 32960Sstevel@tonic-gate ipfb_t *ipfb; 32970Sstevel@tonic-gate mblk_t *mp1; 32980Sstevel@tonic-gate uint8_t ecn_info = 0; 32990Sstevel@tonic-gate size_t msg_len; 33000Sstevel@tonic-gate mblk_t *tail_mp; 33010Sstevel@tonic-gate mblk_t *t_mp; 33020Sstevel@tonic-gate boolean_t pruned = B_FALSE; 3303741Smasputra uint32_t sum_val; 3304741Smasputra uint16_t sum_flags; 330511042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; 33063448Sdh155122 ip_stack_t *ipst = ill->ill_ipst; 330711042SErik.Nordmark@Sun.COM uint_t prev_nexthdr_offset; 330811042SErik.Nordmark@Sun.COM uint8_t prev_nexthdr; 330911042SErik.Nordmark@Sun.COM uint8_t *ptr; 331011042SErik.Nordmark@Sun.COM uint32_t packet_size; 3311741Smasputra 3312741Smasputra /* 3313741Smasputra * We utilize hardware computed checksum info only for UDP since 3314741Smasputra * IP fragmentation is a normal occurence for the protocol. In 3315741Smasputra * addition, checksum offload support for IP fragments carrying 3316741Smasputra * UDP payload is commonly implemented across network adapters. 3317741Smasputra */ 331811042SErik.Nordmark@Sun.COM ASSERT(ira->ira_rill != NULL); 331911042SErik.Nordmark@Sun.COM if (nexthdr == IPPROTO_UDP && dohwcksum && 332011042SErik.Nordmark@Sun.COM ILL_HCKSUM_CAPABLE(ira->ira_rill) && 3321741Smasputra (DB_CKSUMFLAGS(mp) & (HCK_FULLCKSUM | HCK_PARTIALCKSUM))) { 3322741Smasputra mblk_t *mp1 = mp->b_cont; 3323741Smasputra int32_t len; 3324741Smasputra 3325741Smasputra /* Record checksum information from the packet */ 3326741Smasputra sum_val = (uint32_t)DB_CKSUM16(mp); 3327741Smasputra sum_flags = DB_CKSUMFLAGS(mp); 3328741Smasputra 3329741Smasputra /* fragmented payload offset from beginning of mblk */ 3330741Smasputra offset = (uint16_t)((uchar_t *)&fraghdr[1] - mp->b_rptr); 3331741Smasputra 3332741Smasputra if ((sum_flags & HCK_PARTIALCKSUM) && 3333741Smasputra (mp1 == NULL || mp1->b_cont == NULL) && 333411042SErik.Nordmark@Sun.COM offset >= DB_CKSUMSTART(mp) && 333511042SErik.Nordmark@Sun.COM ((len = offset - DB_CKSUMSTART(mp)) & 1) == 0) { 3336741Smasputra uint32_t adj; 3337741Smasputra /* 3338741Smasputra * Partial checksum has been calculated by hardware 3339741Smasputra * and attached to the packet; in addition, any 3340741Smasputra * prepended extraneous data is even byte aligned. 3341741Smasputra * If any such data exists, we adjust the checksum; 3342741Smasputra * this would also handle any postpended data. 3343741Smasputra */ 3344741Smasputra IP_ADJCKSUM_PARTIAL(mp->b_rptr + DB_CKSUMSTART(mp), 3345741Smasputra mp, mp1, len, adj); 3346741Smasputra 3347741Smasputra /* One's complement subtract extraneous checksum */ 3348741Smasputra if (adj >= sum_val) 3349741Smasputra sum_val = ~(adj - sum_val) & 0xFFFF; 3350741Smasputra else 3351741Smasputra sum_val -= adj; 3352741Smasputra } 3353741Smasputra } else { 3354741Smasputra sum_val = 0; 3355741Smasputra sum_flags = 0; 3356741Smasputra } 3357741Smasputra 3358741Smasputra /* Clear hardware checksumming flag */ 3359741Smasputra DB_CKSUMFLAGS(mp) = 0; 33600Sstevel@tonic-gate 33610Sstevel@tonic-gate /* 336211042SErik.Nordmark@Sun.COM * Determine the offset (from the begining of the IP header) 336311042SErik.Nordmark@Sun.COM * of the nexthdr value which has IPPROTO_FRAGMENT. We use 336411042SErik.Nordmark@Sun.COM * this when removing the fragment header from the packet. 336511042SErik.Nordmark@Sun.COM * This packet consists of the IPv6 header, a potential 336611042SErik.Nordmark@Sun.COM * hop-by-hop options header, a potential pre-routing-header 336711042SErik.Nordmark@Sun.COM * destination options header, and a potential routing header. 336811042SErik.Nordmark@Sun.COM */ 336911042SErik.Nordmark@Sun.COM prev_nexthdr_offset = (uint8_t *)&ip6h->ip6_nxt - (uint8_t *)ip6h; 337011042SErik.Nordmark@Sun.COM prev_nexthdr = ip6h->ip6_nxt; 337111042SErik.Nordmark@Sun.COM ptr = (uint8_t *)&ip6h[1]; 337211042SErik.Nordmark@Sun.COM 337311042SErik.Nordmark@Sun.COM if (prev_nexthdr == IPPROTO_HOPOPTS) { 337411042SErik.Nordmark@Sun.COM ip6_hbh_t *hbh_hdr; 337511042SErik.Nordmark@Sun.COM uint_t hdr_len; 337611042SErik.Nordmark@Sun.COM 337711042SErik.Nordmark@Sun.COM hbh_hdr = (ip6_hbh_t *)ptr; 337811042SErik.Nordmark@Sun.COM hdr_len = 8 * (hbh_hdr->ip6h_len + 1); 337911042SErik.Nordmark@Sun.COM prev_nexthdr = hbh_hdr->ip6h_nxt; 338011042SErik.Nordmark@Sun.COM prev_nexthdr_offset = (uint8_t *)&hbh_hdr->ip6h_nxt 338111042SErik.Nordmark@Sun.COM - (uint8_t *)ip6h; 338211042SErik.Nordmark@Sun.COM ptr += hdr_len; 338311042SErik.Nordmark@Sun.COM } 338411042SErik.Nordmark@Sun.COM if (prev_nexthdr == IPPROTO_DSTOPTS) { 338511042SErik.Nordmark@Sun.COM ip6_dest_t *dest_hdr; 338611042SErik.Nordmark@Sun.COM uint_t hdr_len; 338711042SErik.Nordmark@Sun.COM 338811042SErik.Nordmark@Sun.COM dest_hdr = (ip6_dest_t *)ptr; 338911042SErik.Nordmark@Sun.COM hdr_len = 8 * (dest_hdr->ip6d_len + 1); 339011042SErik.Nordmark@Sun.COM prev_nexthdr = dest_hdr->ip6d_nxt; 339111042SErik.Nordmark@Sun.COM prev_nexthdr_offset = (uint8_t *)&dest_hdr->ip6d_nxt 339211042SErik.Nordmark@Sun.COM - (uint8_t *)ip6h; 339311042SErik.Nordmark@Sun.COM ptr += hdr_len; 339411042SErik.Nordmark@Sun.COM } 339511042SErik.Nordmark@Sun.COM if (prev_nexthdr == IPPROTO_ROUTING) { 339611042SErik.Nordmark@Sun.COM ip6_rthdr_t *rthdr; 339711042SErik.Nordmark@Sun.COM uint_t hdr_len; 339811042SErik.Nordmark@Sun.COM 339911042SErik.Nordmark@Sun.COM rthdr = (ip6_rthdr_t *)ptr; 340011042SErik.Nordmark@Sun.COM prev_nexthdr = rthdr->ip6r_nxt; 340111042SErik.Nordmark@Sun.COM prev_nexthdr_offset = (uint8_t *)&rthdr->ip6r_nxt 340211042SErik.Nordmark@Sun.COM - (uint8_t *)ip6h; 340311042SErik.Nordmark@Sun.COM hdr_len = 8 * (rthdr->ip6r_len + 1); 340411042SErik.Nordmark@Sun.COM ptr += hdr_len; 340511042SErik.Nordmark@Sun.COM } 340611042SErik.Nordmark@Sun.COM if (prev_nexthdr != IPPROTO_FRAGMENT) { 340711042SErik.Nordmark@Sun.COM /* Can't handle other headers before the fragment header */ 340811042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors); 340911042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInHdrErrors", mp, ill); 341011042SErik.Nordmark@Sun.COM freemsg(mp); 341111042SErik.Nordmark@Sun.COM return (NULL); 341211042SErik.Nordmark@Sun.COM } 341311042SErik.Nordmark@Sun.COM 341411042SErik.Nordmark@Sun.COM /* 34150Sstevel@tonic-gate * Note: Fragment offset in header is in 8-octet units. 34160Sstevel@tonic-gate * Clearing least significant 3 bits not only extracts 34170Sstevel@tonic-gate * it but also gets it in units of octets. 34180Sstevel@tonic-gate */ 34190Sstevel@tonic-gate offset = ntohs(fraghdr->ip6f_offlg) & ~7; 34200Sstevel@tonic-gate more_frags = (fraghdr->ip6f_offlg & IP6F_MORE_FRAG); 34210Sstevel@tonic-gate 34220Sstevel@tonic-gate /* 34230Sstevel@tonic-gate * Is the more frags flag on and the payload length not a multiple 34240Sstevel@tonic-gate * of eight? 34250Sstevel@tonic-gate */ 34260Sstevel@tonic-gate if (more_frags && (ntohs(ip6h->ip6_plen) & 7)) { 342711042SErik.Nordmark@Sun.COM ip_drop_input("ICMP_PARAM_PROBLEM", mp, ill); 342811042SErik.Nordmark@Sun.COM icmp_param_problem_v6(mp, ICMP6_PARAMPROB_HEADER, 34290Sstevel@tonic-gate (uint32_t)((char *)&ip6h->ip6_plen - 343011042SErik.Nordmark@Sun.COM (char *)ip6h), B_FALSE, ira); 34310Sstevel@tonic-gate return (NULL); 34320Sstevel@tonic-gate } 34330Sstevel@tonic-gate 34340Sstevel@tonic-gate v6src_ptr = &ip6h->ip6_src; 34350Sstevel@tonic-gate v6dst_ptr = &ip6h->ip6_dst; 34360Sstevel@tonic-gate end = remlen; 34370Sstevel@tonic-gate 34380Sstevel@tonic-gate hdr_length = (uint_t)((char *)&fraghdr[1] - (char *)ip6h); 34390Sstevel@tonic-gate end += offset; 34400Sstevel@tonic-gate 34410Sstevel@tonic-gate /* 34420Sstevel@tonic-gate * Would fragment cause reassembled packet to have a payload length 34430Sstevel@tonic-gate * greater than IP_MAXPACKET - the max payload size? 34440Sstevel@tonic-gate */ 34450Sstevel@tonic-gate if (end > IP_MAXPACKET) { 34463284Sapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors); 344711042SErik.Nordmark@Sun.COM ip_drop_input("Reassembled packet too large", mp, ill); 344811042SErik.Nordmark@Sun.COM icmp_param_problem_v6(mp, ICMP6_PARAMPROB_HEADER, 34490Sstevel@tonic-gate (uint32_t)((char *)&fraghdr->ip6f_offlg - 345011042SErik.Nordmark@Sun.COM (char *)ip6h), B_FALSE, ira); 34510Sstevel@tonic-gate return (NULL); 34520Sstevel@tonic-gate } 34530Sstevel@tonic-gate 34540Sstevel@tonic-gate /* 34550Sstevel@tonic-gate * This packet just has one fragment. Reassembly not 34560Sstevel@tonic-gate * needed. 34570Sstevel@tonic-gate */ 34580Sstevel@tonic-gate if (!more_frags && offset == 0) { 34590Sstevel@tonic-gate goto reass_done; 34600Sstevel@tonic-gate } 34610Sstevel@tonic-gate 34620Sstevel@tonic-gate /* 34630Sstevel@tonic-gate * Drop the fragmented as early as possible, if 34640Sstevel@tonic-gate * we don't have resource(s) to re-assemble. 34650Sstevel@tonic-gate */ 34663448Sdh155122 if (ipst->ips_ip_reass_queue_bytes == 0) { 34670Sstevel@tonic-gate freemsg(mp); 34680Sstevel@tonic-gate return (NULL); 34690Sstevel@tonic-gate } 34700Sstevel@tonic-gate 34710Sstevel@tonic-gate /* Record the ECN field info. */ 34720Sstevel@tonic-gate ecn_info = (uint8_t)(ntohl(ip6h->ip6_vcf & htonl(~0xFFCFFFFF)) >> 20); 34730Sstevel@tonic-gate /* 34740Sstevel@tonic-gate * If this is not the first fragment, dump the unfragmentable 34750Sstevel@tonic-gate * portion of the packet. 34760Sstevel@tonic-gate */ 34770Sstevel@tonic-gate if (offset) 34780Sstevel@tonic-gate mp->b_rptr = (uchar_t *)&fraghdr[1]; 34790Sstevel@tonic-gate 34800Sstevel@tonic-gate /* 34810Sstevel@tonic-gate * Fragmentation reassembly. Each ILL has a hash table for 34820Sstevel@tonic-gate * queueing packets undergoing reassembly for all IPIFs 34830Sstevel@tonic-gate * associated with the ILL. The hash is based on the packet 34840Sstevel@tonic-gate * IP ident field. The ILL frag hash table was allocated 34850Sstevel@tonic-gate * as a timer block at the time the ILL was created. Whenever 34860Sstevel@tonic-gate * there is anything on the reassembly queue, the timer will 34870Sstevel@tonic-gate * be running. 34880Sstevel@tonic-gate */ 348911042SErik.Nordmark@Sun.COM /* Handle vnic loopback of fragments */ 349011042SErik.Nordmark@Sun.COM if (mp->b_datap->db_ref > 2) 349111042SErik.Nordmark@Sun.COM msg_len = 0; 349211042SErik.Nordmark@Sun.COM else 349311042SErik.Nordmark@Sun.COM msg_len = MBLKSIZE(mp); 349411042SErik.Nordmark@Sun.COM 34950Sstevel@tonic-gate tail_mp = mp; 34960Sstevel@tonic-gate while (tail_mp->b_cont != NULL) { 34970Sstevel@tonic-gate tail_mp = tail_mp->b_cont; 349811042SErik.Nordmark@Sun.COM if (tail_mp->b_datap->db_ref <= 2) 349911042SErik.Nordmark@Sun.COM msg_len += MBLKSIZE(tail_mp); 35000Sstevel@tonic-gate } 35010Sstevel@tonic-gate /* 35020Sstevel@tonic-gate * If the reassembly list for this ILL will get too big 35030Sstevel@tonic-gate * prune it. 35040Sstevel@tonic-gate */ 35050Sstevel@tonic-gate 35060Sstevel@tonic-gate if ((msg_len + sizeof (*ipf) + ill->ill_frag_count) >= 35073448Sdh155122 ipst->ips_ip_reass_queue_bytes) { 350811042SErik.Nordmark@Sun.COM DTRACE_PROBE3(ip_reass_queue_bytes, uint_t, msg_len, 350911042SErik.Nordmark@Sun.COM uint_t, ill->ill_frag_count, 351011042SErik.Nordmark@Sun.COM uint_t, ipst->ips_ip_reass_queue_bytes); 35113448Sdh155122 ill_frag_prune(ill, 35123448Sdh155122 (ipst->ips_ip_reass_queue_bytes < msg_len) ? 0 : 35133448Sdh155122 (ipst->ips_ip_reass_queue_bytes - msg_len)); 35140Sstevel@tonic-gate pruned = B_TRUE; 35150Sstevel@tonic-gate } 35160Sstevel@tonic-gate 35170Sstevel@tonic-gate ipfb = &ill->ill_frag_hash_tbl[ILL_FRAG_HASH_V6(*v6src_ptr, ident)]; 35180Sstevel@tonic-gate mutex_enter(&ipfb->ipfb_lock); 35190Sstevel@tonic-gate 35200Sstevel@tonic-gate ipfp = &ipfb->ipfb_ipf; 35210Sstevel@tonic-gate /* Try to find an existing fragment queue for this packet. */ 35220Sstevel@tonic-gate for (;;) { 35230Sstevel@tonic-gate ipf = ipfp[0]; 35240Sstevel@tonic-gate if (ipf) { 35250Sstevel@tonic-gate /* 35260Sstevel@tonic-gate * It has to match on ident, source address, and 35270Sstevel@tonic-gate * dest address. 35280Sstevel@tonic-gate */ 35290Sstevel@tonic-gate if (ipf->ipf_ident == ident && 35300Sstevel@tonic-gate IN6_ARE_ADDR_EQUAL(&ipf->ipf_v6src, v6src_ptr) && 35310Sstevel@tonic-gate IN6_ARE_ADDR_EQUAL(&ipf->ipf_v6dst, v6dst_ptr)) { 35320Sstevel@tonic-gate 35330Sstevel@tonic-gate /* 35340Sstevel@tonic-gate * If we have received too many 35350Sstevel@tonic-gate * duplicate fragments for this packet 35360Sstevel@tonic-gate * free it. 35370Sstevel@tonic-gate */ 35380Sstevel@tonic-gate if (ipf->ipf_num_dups > ip_max_frag_dups) { 35390Sstevel@tonic-gate ill_frag_free_pkts(ill, ipfb, ipf, 1); 35400Sstevel@tonic-gate freemsg(mp); 35410Sstevel@tonic-gate mutex_exit(&ipfb->ipfb_lock); 35420Sstevel@tonic-gate return (NULL); 35430Sstevel@tonic-gate } 35440Sstevel@tonic-gate 35450Sstevel@tonic-gate break; 35460Sstevel@tonic-gate } 35470Sstevel@tonic-gate ipfp = &ipf->ipf_hash_next; 35480Sstevel@tonic-gate continue; 35490Sstevel@tonic-gate } 35500Sstevel@tonic-gate 35510Sstevel@tonic-gate 35520Sstevel@tonic-gate /* 35530Sstevel@tonic-gate * If we pruned the list, do we want to store this new 35540Sstevel@tonic-gate * fragment?. We apply an optimization here based on the 35550Sstevel@tonic-gate * fact that most fragments will be received in order. 35560Sstevel@tonic-gate * So if the offset of this incoming fragment is zero, 35570Sstevel@tonic-gate * it is the first fragment of a new packet. We will 35580Sstevel@tonic-gate * keep it. Otherwise drop the fragment, as we have 35590Sstevel@tonic-gate * probably pruned the packet already (since the 35600Sstevel@tonic-gate * packet cannot be found). 35610Sstevel@tonic-gate */ 35620Sstevel@tonic-gate 35630Sstevel@tonic-gate if (pruned && offset != 0) { 35640Sstevel@tonic-gate mutex_exit(&ipfb->ipfb_lock); 35650Sstevel@tonic-gate freemsg(mp); 35660Sstevel@tonic-gate return (NULL); 35670Sstevel@tonic-gate } 35680Sstevel@tonic-gate 35690Sstevel@tonic-gate /* New guy. Allocate a frag message. */ 35700Sstevel@tonic-gate mp1 = allocb(sizeof (*ipf), BPRI_MED); 35710Sstevel@tonic-gate if (!mp1) { 35723284Sapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 357311042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards", mp, ill); 35740Sstevel@tonic-gate freemsg(mp); 35750Sstevel@tonic-gate partial_reass_done: 35760Sstevel@tonic-gate mutex_exit(&ipfb->ipfb_lock); 35770Sstevel@tonic-gate return (NULL); 35780Sstevel@tonic-gate } 35790Sstevel@tonic-gate 35803448Sdh155122 if (ipfb->ipfb_frag_pkts >= MAX_FRAG_PKTS(ipst)) { 35810Sstevel@tonic-gate /* 35820Sstevel@tonic-gate * Too many fragmented packets in this hash bucket. 35830Sstevel@tonic-gate * Free the oldest. 35840Sstevel@tonic-gate */ 35850Sstevel@tonic-gate ill_frag_free_pkts(ill, ipfb, ipfb->ipfb_ipf, 1); 35860Sstevel@tonic-gate } 35870Sstevel@tonic-gate 35880Sstevel@tonic-gate mp1->b_cont = mp; 35890Sstevel@tonic-gate 35900Sstevel@tonic-gate /* Initialize the fragment header. */ 35910Sstevel@tonic-gate ipf = (ipf_t *)mp1->b_rptr; 35920Sstevel@tonic-gate ipf->ipf_mp = mp1; 35930Sstevel@tonic-gate ipf->ipf_ptphn = ipfp; 35940Sstevel@tonic-gate ipfp[0] = ipf; 35950Sstevel@tonic-gate ipf->ipf_hash_next = NULL; 35960Sstevel@tonic-gate ipf->ipf_ident = ident; 35970Sstevel@tonic-gate ipf->ipf_v6src = *v6src_ptr; 35980Sstevel@tonic-gate ipf->ipf_v6dst = *v6dst_ptr; 35990Sstevel@tonic-gate /* Record reassembly start time. */ 36000Sstevel@tonic-gate ipf->ipf_timestamp = gethrestime_sec(); 36010Sstevel@tonic-gate /* Record ipf generation and account for frag header */ 36020Sstevel@tonic-gate ipf->ipf_gen = ill->ill_ipf_gen++; 3603741Smasputra ipf->ipf_count = MBLKSIZE(mp1); 36040Sstevel@tonic-gate ipf->ipf_protocol = nexthdr; 36050Sstevel@tonic-gate ipf->ipf_nf_hdr_len = 0; 36060Sstevel@tonic-gate ipf->ipf_prev_nexthdr_offset = 0; 36070Sstevel@tonic-gate ipf->ipf_last_frag_seen = B_FALSE; 36080Sstevel@tonic-gate ipf->ipf_ecn = ecn_info; 36090Sstevel@tonic-gate ipf->ipf_num_dups = 0; 36100Sstevel@tonic-gate ipfb->ipfb_frag_pkts++; 3611741Smasputra ipf->ipf_checksum = 0; 3612741Smasputra ipf->ipf_checksum_flags = 0; 3613741Smasputra 3614741Smasputra /* Store checksum value in fragment header */ 3615741Smasputra if (sum_flags != 0) { 3616741Smasputra sum_val = (sum_val & 0xFFFF) + (sum_val >> 16); 3617741Smasputra sum_val = (sum_val & 0xFFFF) + (sum_val >> 16); 3618741Smasputra ipf->ipf_checksum = sum_val; 3619741Smasputra ipf->ipf_checksum_flags = sum_flags; 3620741Smasputra } 36210Sstevel@tonic-gate 36220Sstevel@tonic-gate /* 36230Sstevel@tonic-gate * We handle reassembly two ways. In the easy case, 36240Sstevel@tonic-gate * where all the fragments show up in order, we do 36250Sstevel@tonic-gate * minimal bookkeeping, and just clip new pieces on 36260Sstevel@tonic-gate * the end. If we ever see a hole, then we go off 36270Sstevel@tonic-gate * to ip_reassemble which has to mark the pieces and 36280Sstevel@tonic-gate * keep track of the number of holes, etc. Obviously, 36290Sstevel@tonic-gate * the point of having both mechanisms is so we can 36300Sstevel@tonic-gate * handle the easy case as efficiently as possible. 36310Sstevel@tonic-gate */ 36320Sstevel@tonic-gate if (offset == 0) { 36330Sstevel@tonic-gate /* Easy case, in-order reassembly so far. */ 36340Sstevel@tonic-gate /* Update the byte count */ 36350Sstevel@tonic-gate ipf->ipf_count += msg_len; 36360Sstevel@tonic-gate ipf->ipf_tail_mp = tail_mp; 36370Sstevel@tonic-gate /* 36380Sstevel@tonic-gate * Keep track of next expected offset in 36390Sstevel@tonic-gate * ipf_end. 36400Sstevel@tonic-gate */ 36410Sstevel@tonic-gate ipf->ipf_end = end; 36420Sstevel@tonic-gate ipf->ipf_nf_hdr_len = hdr_length; 364311042SErik.Nordmark@Sun.COM ipf->ipf_prev_nexthdr_offset = prev_nexthdr_offset; 36440Sstevel@tonic-gate } else { 36450Sstevel@tonic-gate /* Hard case, hole at the beginning. */ 36460Sstevel@tonic-gate ipf->ipf_tail_mp = NULL; 36470Sstevel@tonic-gate /* 36480Sstevel@tonic-gate * ipf_end == 0 means that we have given up 36490Sstevel@tonic-gate * on easy reassembly. 36500Sstevel@tonic-gate */ 36510Sstevel@tonic-gate ipf->ipf_end = 0; 3652741Smasputra 3653741Smasputra /* Forget checksum offload from now on */ 3654741Smasputra ipf->ipf_checksum_flags = 0; 3655741Smasputra 36560Sstevel@tonic-gate /* 36570Sstevel@tonic-gate * ipf_hole_cnt is set by ip_reassemble. 36580Sstevel@tonic-gate * ipf_count is updated by ip_reassemble. 36590Sstevel@tonic-gate * No need to check for return value here 36600Sstevel@tonic-gate * as we don't expect reassembly to complete or 36610Sstevel@tonic-gate * fail for the first fragment itself. 36620Sstevel@tonic-gate */ 36630Sstevel@tonic-gate (void) ip_reassemble(mp, ipf, offset, more_frags, ill, 36640Sstevel@tonic-gate msg_len); 36650Sstevel@tonic-gate } 36660Sstevel@tonic-gate /* Update per ipfb and ill byte counts */ 36670Sstevel@tonic-gate ipfb->ipfb_count += ipf->ipf_count; 36680Sstevel@tonic-gate ASSERT(ipfb->ipfb_count > 0); /* Wraparound */ 36696759Sgeorges atomic_add_32(&ill->ill_frag_count, ipf->ipf_count); 36700Sstevel@tonic-gate /* If the frag timer wasn't already going, start it. */ 36710Sstevel@tonic-gate mutex_enter(&ill->ill_lock); 36720Sstevel@tonic-gate ill_frag_timer_start(ill); 36730Sstevel@tonic-gate mutex_exit(&ill->ill_lock); 36740Sstevel@tonic-gate goto partial_reass_done; 36750Sstevel@tonic-gate } 36760Sstevel@tonic-gate 36770Sstevel@tonic-gate /* 3678741Smasputra * If the packet's flag has changed (it could be coming up 3679741Smasputra * from an interface different than the previous, therefore 3680741Smasputra * possibly different checksum capability), then forget about 3681741Smasputra * any stored checksum states. Otherwise add the value to 3682741Smasputra * the existing one stored in the fragment header. 3683741Smasputra */ 3684741Smasputra if (sum_flags != 0 && sum_flags == ipf->ipf_checksum_flags) { 3685741Smasputra sum_val += ipf->ipf_checksum; 3686741Smasputra sum_val = (sum_val & 0xFFFF) + (sum_val >> 16); 3687741Smasputra sum_val = (sum_val & 0xFFFF) + (sum_val >> 16); 3688741Smasputra ipf->ipf_checksum = sum_val; 3689741Smasputra } else if (ipf->ipf_checksum_flags != 0) { 3690741Smasputra /* Forget checksum offload from now on */ 3691741Smasputra ipf->ipf_checksum_flags = 0; 3692741Smasputra } 3693741Smasputra 3694741Smasputra /* 36950Sstevel@tonic-gate * We have a new piece of a datagram which is already being 36960Sstevel@tonic-gate * reassembled. Update the ECN info if all IP fragments 36970Sstevel@tonic-gate * are ECN capable. If there is one which is not, clear 36980Sstevel@tonic-gate * all the info. If there is at least one which has CE 36990Sstevel@tonic-gate * code point, IP needs to report that up to transport. 37000Sstevel@tonic-gate */ 37010Sstevel@tonic-gate if (ecn_info != IPH_ECN_NECT && ipf->ipf_ecn != IPH_ECN_NECT) { 37020Sstevel@tonic-gate if (ecn_info == IPH_ECN_CE) 37030Sstevel@tonic-gate ipf->ipf_ecn = IPH_ECN_CE; 37040Sstevel@tonic-gate } else { 37050Sstevel@tonic-gate ipf->ipf_ecn = IPH_ECN_NECT; 37060Sstevel@tonic-gate } 37070Sstevel@tonic-gate 37080Sstevel@tonic-gate if (offset && ipf->ipf_end == offset) { 37090Sstevel@tonic-gate /* The new fragment fits at the end */ 37100Sstevel@tonic-gate ipf->ipf_tail_mp->b_cont = mp; 37110Sstevel@tonic-gate /* Update the byte count */ 37120Sstevel@tonic-gate ipf->ipf_count += msg_len; 37130Sstevel@tonic-gate /* Update per ipfb and ill byte counts */ 37140Sstevel@tonic-gate ipfb->ipfb_count += msg_len; 37150Sstevel@tonic-gate ASSERT(ipfb->ipfb_count > 0); /* Wraparound */ 37166759Sgeorges atomic_add_32(&ill->ill_frag_count, msg_len); 37170Sstevel@tonic-gate if (more_frags) { 37180Sstevel@tonic-gate /* More to come. */ 37190Sstevel@tonic-gate ipf->ipf_end = end; 37200Sstevel@tonic-gate ipf->ipf_tail_mp = tail_mp; 37210Sstevel@tonic-gate goto partial_reass_done; 37220Sstevel@tonic-gate } 37230Sstevel@tonic-gate } else { 37240Sstevel@tonic-gate /* 37250Sstevel@tonic-gate * Go do the hard cases. 37260Sstevel@tonic-gate * Call ip_reassemble(). 37270Sstevel@tonic-gate */ 37280Sstevel@tonic-gate int ret; 37290Sstevel@tonic-gate 37300Sstevel@tonic-gate if (offset == 0) { 37310Sstevel@tonic-gate if (ipf->ipf_prev_nexthdr_offset == 0) { 37320Sstevel@tonic-gate ipf->ipf_nf_hdr_len = hdr_length; 37330Sstevel@tonic-gate ipf->ipf_prev_nexthdr_offset = 373411042SErik.Nordmark@Sun.COM prev_nexthdr_offset; 37350Sstevel@tonic-gate } 37360Sstevel@tonic-gate } 37370Sstevel@tonic-gate /* Save current byte count */ 37380Sstevel@tonic-gate count = ipf->ipf_count; 37390Sstevel@tonic-gate ret = ip_reassemble(mp, ipf, offset, more_frags, ill, msg_len); 37400Sstevel@tonic-gate 37410Sstevel@tonic-gate /* Count of bytes added and subtracted (freeb()ed) */ 37420Sstevel@tonic-gate count = ipf->ipf_count - count; 37430Sstevel@tonic-gate if (count) { 37440Sstevel@tonic-gate /* Update per ipfb and ill byte counts */ 37450Sstevel@tonic-gate ipfb->ipfb_count += count; 37460Sstevel@tonic-gate ASSERT(ipfb->ipfb_count > 0); /* Wraparound */ 37476759Sgeorges atomic_add_32(&ill->ill_frag_count, count); 37480Sstevel@tonic-gate } 37490Sstevel@tonic-gate if (ret == IP_REASS_PARTIAL) { 37500Sstevel@tonic-gate goto partial_reass_done; 37510Sstevel@tonic-gate } else if (ret == IP_REASS_FAILED) { 37520Sstevel@tonic-gate /* Reassembly failed. Free up all resources */ 37530Sstevel@tonic-gate ill_frag_free_pkts(ill, ipfb, ipf, 1); 37540Sstevel@tonic-gate for (t_mp = mp; t_mp != NULL; t_mp = t_mp->b_cont) { 37550Sstevel@tonic-gate IP_REASS_SET_START(t_mp, 0); 37560Sstevel@tonic-gate IP_REASS_SET_END(t_mp, 0); 37570Sstevel@tonic-gate } 37580Sstevel@tonic-gate freemsg(mp); 37590Sstevel@tonic-gate goto partial_reass_done; 37600Sstevel@tonic-gate } 37610Sstevel@tonic-gate 37620Sstevel@tonic-gate /* We will reach here iff 'ret' is IP_REASS_COMPLETE */ 37630Sstevel@tonic-gate } 37640Sstevel@tonic-gate /* 37650Sstevel@tonic-gate * We have completed reassembly. Unhook the frag header from 37660Sstevel@tonic-gate * the reassembly list. 37670Sstevel@tonic-gate * 37680Sstevel@tonic-gate * Grab the unfragmentable header length next header value out 37690Sstevel@tonic-gate * of the first fragment 37700Sstevel@tonic-gate */ 37710Sstevel@tonic-gate ASSERT(ipf->ipf_nf_hdr_len != 0); 37720Sstevel@tonic-gate hdr_length = ipf->ipf_nf_hdr_len; 37730Sstevel@tonic-gate 37740Sstevel@tonic-gate /* 37750Sstevel@tonic-gate * Before we free the frag header, record the ECN info 37760Sstevel@tonic-gate * to report back to the transport. 37770Sstevel@tonic-gate */ 37780Sstevel@tonic-gate ecn_info = ipf->ipf_ecn; 37790Sstevel@tonic-gate 37800Sstevel@tonic-gate /* 37810Sstevel@tonic-gate * Store the nextheader field in the header preceding the fragment 37820Sstevel@tonic-gate * header 37830Sstevel@tonic-gate */ 37840Sstevel@tonic-gate nexthdr = ipf->ipf_protocol; 378511042SErik.Nordmark@Sun.COM prev_nexthdr_offset = ipf->ipf_prev_nexthdr_offset; 37860Sstevel@tonic-gate ipfp = ipf->ipf_ptphn; 3787741Smasputra 3788741Smasputra /* We need to supply these to caller */ 3789741Smasputra if ((sum_flags = ipf->ipf_checksum_flags) != 0) 3790741Smasputra sum_val = ipf->ipf_checksum; 3791741Smasputra else 3792741Smasputra sum_val = 0; 3793741Smasputra 37940Sstevel@tonic-gate mp1 = ipf->ipf_mp; 37950Sstevel@tonic-gate count = ipf->ipf_count; 37960Sstevel@tonic-gate ipf = ipf->ipf_hash_next; 37970Sstevel@tonic-gate if (ipf) 37980Sstevel@tonic-gate ipf->ipf_ptphn = ipfp; 37990Sstevel@tonic-gate ipfp[0] = ipf; 38006759Sgeorges atomic_add_32(&ill->ill_frag_count, -count); 38010Sstevel@tonic-gate ASSERT(ipfb->ipfb_count >= count); 38020Sstevel@tonic-gate ipfb->ipfb_count -= count; 38030Sstevel@tonic-gate ipfb->ipfb_frag_pkts--; 38040Sstevel@tonic-gate mutex_exit(&ipfb->ipfb_lock); 38050Sstevel@tonic-gate /* Ditch the frag header. */ 38060Sstevel@tonic-gate mp = mp1->b_cont; 38070Sstevel@tonic-gate freeb(mp1); 38080Sstevel@tonic-gate 38090Sstevel@tonic-gate /* 38100Sstevel@tonic-gate * Make sure the packet is good by doing some sanity 38110Sstevel@tonic-gate * check. If bad we can silentely drop the packet. 38120Sstevel@tonic-gate */ 38130Sstevel@tonic-gate reass_done: 38140Sstevel@tonic-gate if (hdr_length < sizeof (ip6_frag_t)) { 38153284Sapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors); 381611042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInHdrErrors", mp, ill); 381711042SErik.Nordmark@Sun.COM ip1dbg(("ip_input_fragment_v6: bad packet\n")); 38180Sstevel@tonic-gate freemsg(mp); 38190Sstevel@tonic-gate return (NULL); 38200Sstevel@tonic-gate } 38210Sstevel@tonic-gate 38220Sstevel@tonic-gate /* 38230Sstevel@tonic-gate * Remove the fragment header from the initial header by 38240Sstevel@tonic-gate * splitting the mblk into the non-fragmentable header and 38250Sstevel@tonic-gate * everthing after the fragment extension header. This has the 38260Sstevel@tonic-gate * side effect of putting all the headers that need destination 38270Sstevel@tonic-gate * processing into the b_cont block-- on return this fact is 38280Sstevel@tonic-gate * used in order to avoid having to look at the extensions 38290Sstevel@tonic-gate * already processed. 38300Sstevel@tonic-gate * 38310Sstevel@tonic-gate * Note that this code assumes that the unfragmentable portion 38320Sstevel@tonic-gate * of the header is in the first mblk and increments 38330Sstevel@tonic-gate * the read pointer past it. If this assumption is broken 38340Sstevel@tonic-gate * this code fails badly. 38350Sstevel@tonic-gate */ 38360Sstevel@tonic-gate if (mp->b_rptr + hdr_length != mp->b_wptr) { 38370Sstevel@tonic-gate mblk_t *nmp; 38380Sstevel@tonic-gate 38390Sstevel@tonic-gate if (!(nmp = dupb(mp))) { 384011042SErik.Nordmark@Sun.COM ip1dbg(("ip_input_fragment_v6: dupb failed\n")); 38413284Sapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 384211042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards", mp, ill); 38430Sstevel@tonic-gate freemsg(mp); 38440Sstevel@tonic-gate return (NULL); 38450Sstevel@tonic-gate } 38460Sstevel@tonic-gate nmp->b_cont = mp->b_cont; 38470Sstevel@tonic-gate mp->b_cont = nmp; 38480Sstevel@tonic-gate nmp->b_rptr += hdr_length; 38490Sstevel@tonic-gate } 38500Sstevel@tonic-gate mp->b_wptr = mp->b_rptr + hdr_length - sizeof (ip6_frag_t); 38510Sstevel@tonic-gate 38520Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 385311042SErik.Nordmark@Sun.COM ((char *)ip6h)[prev_nexthdr_offset] = nexthdr; 38540Sstevel@tonic-gate 38550Sstevel@tonic-gate /* Restore original IP length in header. */ 385611042SErik.Nordmark@Sun.COM packet_size = msgdsize(mp); 385711042SErik.Nordmark@Sun.COM ip6h->ip6_plen = htons((uint16_t)(packet_size - IPV6_HDR_LEN)); 38580Sstevel@tonic-gate /* Record the ECN info. */ 38590Sstevel@tonic-gate ip6h->ip6_vcf &= htonl(0xFFCFFFFF); 38600Sstevel@tonic-gate ip6h->ip6_vcf |= htonl(ecn_info << 20); 38610Sstevel@tonic-gate 386211042SErik.Nordmark@Sun.COM /* Update the receive attributes */ 386311042SErik.Nordmark@Sun.COM ira->ira_pktlen = packet_size; 386411042SErik.Nordmark@Sun.COM ira->ira_ip_hdr_length = hdr_length - sizeof (ip6_frag_t); 386511042SErik.Nordmark@Sun.COM ira->ira_protocol = nexthdr; 386611042SErik.Nordmark@Sun.COM 386711042SErik.Nordmark@Sun.COM /* Reassembly is successful; set checksum information in packet */ 386811042SErik.Nordmark@Sun.COM DB_CKSUM16(mp) = (uint16_t)sum_val; 386911042SErik.Nordmark@Sun.COM DB_CKSUMFLAGS(mp) = sum_flags; 387011042SErik.Nordmark@Sun.COM DB_CKSUMSTART(mp) = ira->ira_ip_hdr_length; 3871741Smasputra 38720Sstevel@tonic-gate return (mp); 38730Sstevel@tonic-gate } 38740Sstevel@tonic-gate 38750Sstevel@tonic-gate /* 38768847Sdanmcd@sun.com * Given an mblk and a ptr, find the destination address in an IPv6 routing 38778847Sdanmcd@sun.com * header. 38788847Sdanmcd@sun.com */ 38798847Sdanmcd@sun.com static in6_addr_t 388011042SErik.Nordmark@Sun.COM pluck_out_dst(const mblk_t *mp, uint8_t *whereptr, in6_addr_t oldrv) 38818847Sdanmcd@sun.com { 38828847Sdanmcd@sun.com ip6_rthdr0_t *rt0; 38838847Sdanmcd@sun.com int segleft, numaddr; 38848847Sdanmcd@sun.com in6_addr_t *ap, rv = oldrv; 38858847Sdanmcd@sun.com 38868847Sdanmcd@sun.com rt0 = (ip6_rthdr0_t *)whereptr; 38878847Sdanmcd@sun.com if (rt0->ip6r0_type != 0 && rt0->ip6r0_type != 2) { 38888847Sdanmcd@sun.com DTRACE_PROBE2(pluck_out_dst_unknown_type, mblk_t *, mp, 38898847Sdanmcd@sun.com uint8_t *, whereptr); 38908847Sdanmcd@sun.com return (rv); 38918847Sdanmcd@sun.com } 38928847Sdanmcd@sun.com segleft = rt0->ip6r0_segleft; 38938847Sdanmcd@sun.com numaddr = rt0->ip6r0_len / 2; 38948847Sdanmcd@sun.com 38958847Sdanmcd@sun.com if ((rt0->ip6r0_len & 0x1) || 389611042SErik.Nordmark@Sun.COM (mp != NULL && whereptr + (rt0->ip6r0_len + 1) * 8 > mp->b_wptr) || 38978847Sdanmcd@sun.com (segleft > rt0->ip6r0_len / 2)) { 38988847Sdanmcd@sun.com /* 38998847Sdanmcd@sun.com * Corrupt packet. Either the routing header length is odd 39008847Sdanmcd@sun.com * (can't happen) or mismatched compared to the packet, or the 39018847Sdanmcd@sun.com * number of addresses is. Return what we can. This will 39028847Sdanmcd@sun.com * only be a problem on forwarded packets that get squeezed 39038847Sdanmcd@sun.com * through an outbound tunnel enforcing IPsec Tunnel Mode. 39048847Sdanmcd@sun.com */ 39058847Sdanmcd@sun.com DTRACE_PROBE2(pluck_out_dst_badpkt, mblk_t *, mp, uint8_t *, 39068847Sdanmcd@sun.com whereptr); 39078847Sdanmcd@sun.com return (rv); 39088847Sdanmcd@sun.com } 39098847Sdanmcd@sun.com 39108847Sdanmcd@sun.com if (segleft != 0) { 39118847Sdanmcd@sun.com ap = (in6_addr_t *)((char *)rt0 + sizeof (*rt0)); 39128847Sdanmcd@sun.com rv = ap[numaddr - 1]; 39138847Sdanmcd@sun.com } 39148847Sdanmcd@sun.com 39158847Sdanmcd@sun.com return (rv); 39168847Sdanmcd@sun.com } 39178847Sdanmcd@sun.com 39188847Sdanmcd@sun.com /* 39190Sstevel@tonic-gate * Walk through the options to see if there is a routing header. 39200Sstevel@tonic-gate * If present get the destination which is the last address of 39210Sstevel@tonic-gate * the option. 392211042SErik.Nordmark@Sun.COM * mp needs to be provided in cases when the extension headers might span 392311042SErik.Nordmark@Sun.COM * b_cont; mp is never modified by this function. 39240Sstevel@tonic-gate */ 39250Sstevel@tonic-gate in6_addr_t 392611042SErik.Nordmark@Sun.COM ip_get_dst_v6(ip6_t *ip6h, const mblk_t *mp, boolean_t *is_fragment) 39270Sstevel@tonic-gate { 392811042SErik.Nordmark@Sun.COM const mblk_t *current_mp = mp; 39290Sstevel@tonic-gate uint8_t nexthdr; 39300Sstevel@tonic-gate uint8_t *whereptr; 39310Sstevel@tonic-gate int ehdrlen; 39328847Sdanmcd@sun.com in6_addr_t rv; 39338705Sdanmcd@sun.com 39348705Sdanmcd@sun.com whereptr = (uint8_t *)ip6h; 39358705Sdanmcd@sun.com ehdrlen = sizeof (ip6_t); 39368705Sdanmcd@sun.com 39378847Sdanmcd@sun.com /* We assume at least the IPv6 base header is within one mblk. */ 393811042SErik.Nordmark@Sun.COM ASSERT(mp == NULL || 393911042SErik.Nordmark@Sun.COM (mp->b_rptr <= whereptr && mp->b_wptr >= whereptr + ehdrlen)); 39408847Sdanmcd@sun.com 39418847Sdanmcd@sun.com rv = ip6h->ip6_dst; 39428847Sdanmcd@sun.com nexthdr = ip6h->ip6_nxt; 39438847Sdanmcd@sun.com if (is_fragment != NULL) 39448847Sdanmcd@sun.com *is_fragment = B_FALSE; 39458847Sdanmcd@sun.com 39468847Sdanmcd@sun.com /* 39478847Sdanmcd@sun.com * We also assume (thanks to ipsec_tun_outbound()'s pullup) that 39488847Sdanmcd@sun.com * no extension headers will be split across mblks. 39498847Sdanmcd@sun.com */ 39508847Sdanmcd@sun.com 39518847Sdanmcd@sun.com while (nexthdr == IPPROTO_HOPOPTS || nexthdr == IPPROTO_DSTOPTS || 39528847Sdanmcd@sun.com nexthdr == IPPROTO_ROUTING) { 39538847Sdanmcd@sun.com if (nexthdr == IPPROTO_ROUTING) 39548847Sdanmcd@sun.com rv = pluck_out_dst(current_mp, whereptr, rv); 39558847Sdanmcd@sun.com 39568847Sdanmcd@sun.com /* 39578847Sdanmcd@sun.com * All IPv6 extension headers have the next-header in byte 39588847Sdanmcd@sun.com * 0, and the (length - 8) in 8-byte-words. 39598847Sdanmcd@sun.com */ 396011042SErik.Nordmark@Sun.COM while (current_mp != NULL && 396111042SErik.Nordmark@Sun.COM whereptr + ehdrlen >= current_mp->b_wptr) { 39628705Sdanmcd@sun.com ehdrlen -= (current_mp->b_wptr - whereptr); 39638705Sdanmcd@sun.com current_mp = current_mp->b_cont; 39648705Sdanmcd@sun.com if (current_mp == NULL) { 39658705Sdanmcd@sun.com /* Bad packet. Return what we can. */ 39668847Sdanmcd@sun.com DTRACE_PROBE3(ip_get_dst_v6_badpkt, mblk_t *, 39678847Sdanmcd@sun.com mp, mblk_t *, current_mp, ip6_t *, ip6h); 39688705Sdanmcd@sun.com goto done; 39698705Sdanmcd@sun.com } 39708705Sdanmcd@sun.com whereptr = current_mp->b_rptr; 39718705Sdanmcd@sun.com } 39728705Sdanmcd@sun.com whereptr += ehdrlen; 39738705Sdanmcd@sun.com 39748847Sdanmcd@sun.com nexthdr = *whereptr; 397511042SErik.Nordmark@Sun.COM ASSERT(current_mp == NULL || whereptr + 1 < current_mp->b_wptr); 39768847Sdanmcd@sun.com ehdrlen = (*(whereptr + 1) + 1) * 8; 39770Sstevel@tonic-gate } 39780Sstevel@tonic-gate 39790Sstevel@tonic-gate done: 39808847Sdanmcd@sun.com if (nexthdr == IPPROTO_FRAGMENT && is_fragment != NULL) 39818847Sdanmcd@sun.com *is_fragment = B_TRUE; 39820Sstevel@tonic-gate return (rv); 39830Sstevel@tonic-gate } 39840Sstevel@tonic-gate 39850Sstevel@tonic-gate /* 39860Sstevel@tonic-gate * ip_source_routed_v6: 398711042SErik.Nordmark@Sun.COM * This function is called by redirect code (called from ip_input_v6) to 39880Sstevel@tonic-gate * know whether this packet is source routed through this node i.e 39890Sstevel@tonic-gate * whether this node (router) is part of the journey. This 39900Sstevel@tonic-gate * function is called under two cases : 39910Sstevel@tonic-gate * 39920Sstevel@tonic-gate * case 1 : Routing header was processed by this node and 39930Sstevel@tonic-gate * ip_process_rthdr replaced ip6_dst with the next hop 39948348SEric.Yu@Sun.COM * and we are forwarding the packet to the next hop. 39950Sstevel@tonic-gate * 39960Sstevel@tonic-gate * case 2 : Routing header was not processed by this node and we 39970Sstevel@tonic-gate * are just forwarding the packet. 39980Sstevel@tonic-gate * 39990Sstevel@tonic-gate * For case (1) we don't want to send redirects. For case(2) we 40000Sstevel@tonic-gate * want to send redirects. 40010Sstevel@tonic-gate */ 40020Sstevel@tonic-gate static boolean_t 40033448Sdh155122 ip_source_routed_v6(ip6_t *ip6h, mblk_t *mp, ip_stack_t *ipst) 40040Sstevel@tonic-gate { 40050Sstevel@tonic-gate uint8_t nexthdr; 40060Sstevel@tonic-gate in6_addr_t *addrptr; 40070Sstevel@tonic-gate ip6_rthdr0_t *rthdr; 40080Sstevel@tonic-gate uint8_t numaddr; 40090Sstevel@tonic-gate ip6_hbh_t *hbhhdr; 40100Sstevel@tonic-gate uint_t ehdrlen; 40110Sstevel@tonic-gate uint8_t *byteptr; 40120Sstevel@tonic-gate 40130Sstevel@tonic-gate ip2dbg(("ip_source_routed_v6\n")); 40140Sstevel@tonic-gate nexthdr = ip6h->ip6_nxt; 40150Sstevel@tonic-gate ehdrlen = IPV6_HDR_LEN; 40160Sstevel@tonic-gate 40170Sstevel@tonic-gate /* if a routing hdr is preceeded by HOPOPT or DSTOPT */ 40180Sstevel@tonic-gate while (nexthdr == IPPROTO_HOPOPTS || 40190Sstevel@tonic-gate nexthdr == IPPROTO_DSTOPTS) { 40200Sstevel@tonic-gate byteptr = (uint8_t *)ip6h + ehdrlen; 40210Sstevel@tonic-gate /* 40220Sstevel@tonic-gate * Check if we have already processed 40230Sstevel@tonic-gate * packets or we are just a forwarding 40240Sstevel@tonic-gate * router which only pulled up msgs up 40250Sstevel@tonic-gate * to IPV6HDR and one HBH ext header 40260Sstevel@tonic-gate */ 40270Sstevel@tonic-gate if (byteptr + MIN_EHDR_LEN > mp->b_wptr) { 40280Sstevel@tonic-gate ip2dbg(("ip_source_routed_v6: Extension" 40290Sstevel@tonic-gate " headers not processed\n")); 40300Sstevel@tonic-gate return (B_FALSE); 40310Sstevel@tonic-gate } 40320Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)byteptr; 40330Sstevel@tonic-gate nexthdr = hbhhdr->ip6h_nxt; 40340Sstevel@tonic-gate ehdrlen = ehdrlen + 8 * (hbhhdr->ip6h_len + 1); 40350Sstevel@tonic-gate } 40360Sstevel@tonic-gate switch (nexthdr) { 40370Sstevel@tonic-gate case IPPROTO_ROUTING: 40380Sstevel@tonic-gate byteptr = (uint8_t *)ip6h + ehdrlen; 40390Sstevel@tonic-gate /* 40400Sstevel@tonic-gate * If for some reason, we haven't pulled up 40410Sstevel@tonic-gate * the routing hdr data mblk, then we must 40420Sstevel@tonic-gate * not have processed it at all. So for sure 40430Sstevel@tonic-gate * we are not part of the source routed journey. 40440Sstevel@tonic-gate */ 40450Sstevel@tonic-gate if (byteptr + MIN_EHDR_LEN > mp->b_wptr) { 40460Sstevel@tonic-gate ip2dbg(("ip_source_routed_v6: Routing" 40470Sstevel@tonic-gate " header not processed\n")); 40480Sstevel@tonic-gate return (B_FALSE); 40490Sstevel@tonic-gate } 40500Sstevel@tonic-gate rthdr = (ip6_rthdr0_t *)byteptr; 40510Sstevel@tonic-gate /* 40520Sstevel@tonic-gate * Either we are an intermediate router or the 40530Sstevel@tonic-gate * last hop before destination and we have 40540Sstevel@tonic-gate * already processed the routing header. 40550Sstevel@tonic-gate * If segment_left is greater than or equal to zero, 40560Sstevel@tonic-gate * then we must be the (numaddr - segleft) entry 40570Sstevel@tonic-gate * of the routing header. Although ip6r0_segleft 40580Sstevel@tonic-gate * is a unit8_t variable, we still check for zero 40590Sstevel@tonic-gate * or greater value, if in case the data type 40600Sstevel@tonic-gate * is changed someday in future. 40610Sstevel@tonic-gate */ 40620Sstevel@tonic-gate if (rthdr->ip6r0_segleft > 0 || 40630Sstevel@tonic-gate rthdr->ip6r0_segleft == 0) { 40640Sstevel@tonic-gate numaddr = rthdr->ip6r0_len / 2; 40650Sstevel@tonic-gate addrptr = (in6_addr_t *)((char *)rthdr + 40660Sstevel@tonic-gate sizeof (*rthdr)); 40670Sstevel@tonic-gate addrptr += (numaddr - (rthdr->ip6r0_segleft + 1)); 40680Sstevel@tonic-gate if (addrptr != NULL) { 406911042SErik.Nordmark@Sun.COM if (ip_type_v6(addrptr, ipst) == IRE_LOCAL) 40700Sstevel@tonic-gate return (B_TRUE); 407111042SErik.Nordmark@Sun.COM ip1dbg(("ip_source_routed_v6: Not local\n")); 40720Sstevel@tonic-gate } 40730Sstevel@tonic-gate } 40740Sstevel@tonic-gate /* FALLTHRU */ 40750Sstevel@tonic-gate default: 40760Sstevel@tonic-gate ip2dbg(("ip_source_routed_v6: Not source routed here\n")); 40770Sstevel@tonic-gate return (B_FALSE); 40780Sstevel@tonic-gate } 40790Sstevel@tonic-gate } 40800Sstevel@tonic-gate 40810Sstevel@tonic-gate /* 40820Sstevel@tonic-gate * IPv6 fragmentation. Essentially the same as IPv4 fragmentation. 40830Sstevel@tonic-gate * We have not optimized this in terms of number of mblks 40840Sstevel@tonic-gate * allocated. For instance, for each fragment sent we always allocate a 40850Sstevel@tonic-gate * mblk to hold the IPv6 header and fragment header. 40860Sstevel@tonic-gate * 408711042SErik.Nordmark@Sun.COM * Assumes that all the extension headers are contained in the first mblk 408811042SErik.Nordmark@Sun.COM * and that the fragment header has has already been added by calling 408911042SErik.Nordmark@Sun.COM * ip_fraghdr_add_v6. 40900Sstevel@tonic-gate */ 409111042SErik.Nordmark@Sun.COM int 409211042SErik.Nordmark@Sun.COM ip_fragment_v6(mblk_t *mp, nce_t *nce, iaflags_t ixaflags, uint_t pkt_len, 409311042SErik.Nordmark@Sun.COM uint32_t max_frag, uint32_t xmit_hint, zoneid_t szone, zoneid_t nolzid, 409411042SErik.Nordmark@Sun.COM pfirepostfrag_t postfragfn, uintptr_t *ixa_cookie) 40950Sstevel@tonic-gate { 40960Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)mp->b_rptr; 40970Sstevel@tonic-gate ip6_t *fip6h; 40980Sstevel@tonic-gate mblk_t *hmp; 40990Sstevel@tonic-gate mblk_t *hmp0; 41000Sstevel@tonic-gate mblk_t *dmp; 41010Sstevel@tonic-gate ip6_frag_t *fraghdr; 41020Sstevel@tonic-gate size_t unfragmentable_len; 41030Sstevel@tonic-gate size_t mlen; 41040Sstevel@tonic-gate size_t max_chunk; 41050Sstevel@tonic-gate uint16_t off_flags; 41060Sstevel@tonic-gate uint16_t offset = 0; 410711042SErik.Nordmark@Sun.COM ill_t *ill = nce->nce_ill; 410811042SErik.Nordmark@Sun.COM uint8_t nexthdr; 410911042SErik.Nordmark@Sun.COM uint8_t *ptr; 411011042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst; 411111042SErik.Nordmark@Sun.COM uint_t priority = mp->b_band; 411211042SErik.Nordmark@Sun.COM int error = 0; 411311042SErik.Nordmark@Sun.COM 411411042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragReqds); 411511042SErik.Nordmark@Sun.COM if (max_frag == 0) { 411611042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails); 411711042SErik.Nordmark@Sun.COM ip_drop_output("FragFails: zero max_frag", mp, ill); 411811042SErik.Nordmark@Sun.COM freemsg(mp); 411911042SErik.Nordmark@Sun.COM return (EINVAL); 412011042SErik.Nordmark@Sun.COM } 412111042SErik.Nordmark@Sun.COM 412211042SErik.Nordmark@Sun.COM /* 412311042SErik.Nordmark@Sun.COM * Caller should have added fraghdr_t to pkt_len, and also 412411042SErik.Nordmark@Sun.COM * updated ip6_plen. 412511042SErik.Nordmark@Sun.COM */ 412611042SErik.Nordmark@Sun.COM ASSERT(ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN == pkt_len); 412711042SErik.Nordmark@Sun.COM ASSERT(msgdsize(mp) == pkt_len); 412811042SErik.Nordmark@Sun.COM 412911042SErik.Nordmark@Sun.COM /* 413011042SErik.Nordmark@Sun.COM * Determine the length of the unfragmentable portion of this 413111042SErik.Nordmark@Sun.COM * datagram. This consists of the IPv6 header, a potential 413211042SErik.Nordmark@Sun.COM * hop-by-hop options header, a potential pre-routing-header 413311042SErik.Nordmark@Sun.COM * destination options header, and a potential routing header. 413411042SErik.Nordmark@Sun.COM */ 413511042SErik.Nordmark@Sun.COM nexthdr = ip6h->ip6_nxt; 413611042SErik.Nordmark@Sun.COM ptr = (uint8_t *)&ip6h[1]; 413711042SErik.Nordmark@Sun.COM 413811042SErik.Nordmark@Sun.COM if (nexthdr == IPPROTO_HOPOPTS) { 413911042SErik.Nordmark@Sun.COM ip6_hbh_t *hbh_hdr; 414011042SErik.Nordmark@Sun.COM uint_t hdr_len; 414111042SErik.Nordmark@Sun.COM 414211042SErik.Nordmark@Sun.COM hbh_hdr = (ip6_hbh_t *)ptr; 414311042SErik.Nordmark@Sun.COM hdr_len = 8 * (hbh_hdr->ip6h_len + 1); 414411042SErik.Nordmark@Sun.COM nexthdr = hbh_hdr->ip6h_nxt; 414511042SErik.Nordmark@Sun.COM ptr += hdr_len; 414611042SErik.Nordmark@Sun.COM } 414711042SErik.Nordmark@Sun.COM if (nexthdr == IPPROTO_DSTOPTS) { 414811042SErik.Nordmark@Sun.COM ip6_dest_t *dest_hdr; 414911042SErik.Nordmark@Sun.COM uint_t hdr_len; 415011042SErik.Nordmark@Sun.COM 415111042SErik.Nordmark@Sun.COM dest_hdr = (ip6_dest_t *)ptr; 415211042SErik.Nordmark@Sun.COM if (dest_hdr->ip6d_nxt == IPPROTO_ROUTING) { 415311042SErik.Nordmark@Sun.COM hdr_len = 8 * (dest_hdr->ip6d_len + 1); 415411042SErik.Nordmark@Sun.COM nexthdr = dest_hdr->ip6d_nxt; 415511042SErik.Nordmark@Sun.COM ptr += hdr_len; 415611042SErik.Nordmark@Sun.COM } 415711042SErik.Nordmark@Sun.COM } 415811042SErik.Nordmark@Sun.COM if (nexthdr == IPPROTO_ROUTING) { 415911042SErik.Nordmark@Sun.COM ip6_rthdr_t *rthdr; 416011042SErik.Nordmark@Sun.COM uint_t hdr_len; 416111042SErik.Nordmark@Sun.COM 416211042SErik.Nordmark@Sun.COM rthdr = (ip6_rthdr_t *)ptr; 416311042SErik.Nordmark@Sun.COM nexthdr = rthdr->ip6r_nxt; 416411042SErik.Nordmark@Sun.COM hdr_len = 8 * (rthdr->ip6r_len + 1); 416511042SErik.Nordmark@Sun.COM ptr += hdr_len; 416611042SErik.Nordmark@Sun.COM } 416711042SErik.Nordmark@Sun.COM if (nexthdr != IPPROTO_FRAGMENT) { 416811042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails); 416911042SErik.Nordmark@Sun.COM ip_drop_output("FragFails: bad nexthdr", mp, ill); 417011042SErik.Nordmark@Sun.COM freemsg(mp); 417111042SErik.Nordmark@Sun.COM return (EINVAL); 417211042SErik.Nordmark@Sun.COM } 417311042SErik.Nordmark@Sun.COM unfragmentable_len = (uint_t)(ptr - (uint8_t *)ip6h); 417411042SErik.Nordmark@Sun.COM unfragmentable_len += sizeof (ip6_frag_t); 417511042SErik.Nordmark@Sun.COM 417611042SErik.Nordmark@Sun.COM max_chunk = (max_frag - unfragmentable_len) & ~7; 417711042SErik.Nordmark@Sun.COM 417811042SErik.Nordmark@Sun.COM /* 417911042SErik.Nordmark@Sun.COM * Allocate an mblk with enough room for the link-layer 418011042SErik.Nordmark@Sun.COM * header and the unfragmentable part of the datagram, which includes 418111042SErik.Nordmark@Sun.COM * the fragment header. This (or a copy) will be used as the 418211042SErik.Nordmark@Sun.COM * first mblk for each fragment we send. 418311042SErik.Nordmark@Sun.COM */ 418411042SErik.Nordmark@Sun.COM hmp = allocb_tmpl(unfragmentable_len + ipst->ips_ip_wroff_extra, mp); 418511042SErik.Nordmark@Sun.COM if (hmp == NULL) { 418611042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails); 418711042SErik.Nordmark@Sun.COM ip_drop_output("FragFails: no hmp", mp, ill); 418811042SErik.Nordmark@Sun.COM freemsg(mp); 418911042SErik.Nordmark@Sun.COM return (ENOBUFS); 419011042SErik.Nordmark@Sun.COM } 419111042SErik.Nordmark@Sun.COM hmp->b_rptr += ipst->ips_ip_wroff_extra; 419211042SErik.Nordmark@Sun.COM hmp->b_wptr = hmp->b_rptr + unfragmentable_len; 419311042SErik.Nordmark@Sun.COM 419411042SErik.Nordmark@Sun.COM fip6h = (ip6_t *)hmp->b_rptr; 419511042SErik.Nordmark@Sun.COM bcopy(ip6h, fip6h, unfragmentable_len); 419611042SErik.Nordmark@Sun.COM 419711042SErik.Nordmark@Sun.COM /* 419811042SErik.Nordmark@Sun.COM * pkt_len is set to the total length of the fragmentable data in this 419911042SErik.Nordmark@Sun.COM * datagram. For each fragment sent, we will decrement pkt_len 420011042SErik.Nordmark@Sun.COM * by the amount of fragmentable data sent in that fragment 420111042SErik.Nordmark@Sun.COM * until len reaches zero. 420211042SErik.Nordmark@Sun.COM */ 420311042SErik.Nordmark@Sun.COM pkt_len -= unfragmentable_len; 420411042SErik.Nordmark@Sun.COM 420511042SErik.Nordmark@Sun.COM /* 420611042SErik.Nordmark@Sun.COM * Move read ptr past unfragmentable portion, we don't want this part 420711042SErik.Nordmark@Sun.COM * of the data in our fragments. 420811042SErik.Nordmark@Sun.COM */ 420911042SErik.Nordmark@Sun.COM mp->b_rptr += unfragmentable_len; 421011042SErik.Nordmark@Sun.COM if (mp->b_rptr == mp->b_wptr) { 421111042SErik.Nordmark@Sun.COM mblk_t *mp1 = mp->b_cont; 421211042SErik.Nordmark@Sun.COM freeb(mp); 421311042SErik.Nordmark@Sun.COM mp = mp1; 421411042SErik.Nordmark@Sun.COM } 421511042SErik.Nordmark@Sun.COM 421611042SErik.Nordmark@Sun.COM while (pkt_len != 0) { 421711042SErik.Nordmark@Sun.COM mlen = MIN(pkt_len, max_chunk); 421811042SErik.Nordmark@Sun.COM pkt_len -= mlen; 421911042SErik.Nordmark@Sun.COM if (pkt_len != 0) { 422011042SErik.Nordmark@Sun.COM /* Not last */ 422111042SErik.Nordmark@Sun.COM hmp0 = copyb(hmp); 422211042SErik.Nordmark@Sun.COM if (hmp0 == NULL) { 422311042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, 422411042SErik.Nordmark@Sun.COM ipIfStatsOutFragFails); 422511042SErik.Nordmark@Sun.COM ip_drop_output("FragFails: copyb failed", 422611042SErik.Nordmark@Sun.COM mp, ill); 422711042SErik.Nordmark@Sun.COM freeb(hmp); 422811042SErik.Nordmark@Sun.COM freemsg(mp); 422911042SErik.Nordmark@Sun.COM ip1dbg(("ip_fragment_v6: copyb failed\n")); 423011042SErik.Nordmark@Sun.COM return (ENOBUFS); 423111042SErik.Nordmark@Sun.COM } 423211042SErik.Nordmark@Sun.COM off_flags = IP6F_MORE_FRAG; 423311042SErik.Nordmark@Sun.COM } else { 423411042SErik.Nordmark@Sun.COM /* Last fragment */ 423511042SErik.Nordmark@Sun.COM hmp0 = hmp; 423611042SErik.Nordmark@Sun.COM hmp = NULL; 423711042SErik.Nordmark@Sun.COM off_flags = 0; 423811042SErik.Nordmark@Sun.COM } 423911042SErik.Nordmark@Sun.COM fip6h = (ip6_t *)(hmp0->b_rptr); 424011042SErik.Nordmark@Sun.COM fraghdr = (ip6_frag_t *)(hmp0->b_rptr + unfragmentable_len - 424111042SErik.Nordmark@Sun.COM sizeof (ip6_frag_t)); 424211042SErik.Nordmark@Sun.COM 424311042SErik.Nordmark@Sun.COM fip6h->ip6_plen = htons((uint16_t)(mlen + 424411042SErik.Nordmark@Sun.COM unfragmentable_len - IPV6_HDR_LEN)); 424511042SErik.Nordmark@Sun.COM /* 424611042SErik.Nordmark@Sun.COM * Note: Optimization alert. 424711042SErik.Nordmark@Sun.COM * In IPv6 (and IPv4) protocol header, Fragment Offset 424811042SErik.Nordmark@Sun.COM * ("offset") is 13 bits wide and in 8-octet units. 424911042SErik.Nordmark@Sun.COM * In IPv6 protocol header (unlike IPv4) in a 16 bit field, 425011042SErik.Nordmark@Sun.COM * it occupies the most significant 13 bits. 425111042SErik.Nordmark@Sun.COM * (least significant 13 bits in IPv4). 425211042SErik.Nordmark@Sun.COM * We do not do any shifts here. Not shifting is same effect 425311042SErik.Nordmark@Sun.COM * as taking offset value in octet units, dividing by 8 and 425411042SErik.Nordmark@Sun.COM * then shifting 3 bits left to line it up in place in proper 425511042SErik.Nordmark@Sun.COM * place protocol header. 425611042SErik.Nordmark@Sun.COM */ 425711042SErik.Nordmark@Sun.COM fraghdr->ip6f_offlg = htons(offset) | off_flags; 425811042SErik.Nordmark@Sun.COM 425911042SErik.Nordmark@Sun.COM if (!(dmp = ip_carve_mp(&mp, mlen))) { 426011042SErik.Nordmark@Sun.COM /* mp has already been freed by ip_carve_mp() */ 426111042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails); 426211042SErik.Nordmark@Sun.COM ip_drop_output("FragFails: could not carve mp", 426311042SErik.Nordmark@Sun.COM hmp0, ill); 426411042SErik.Nordmark@Sun.COM if (hmp != NULL) 426511042SErik.Nordmark@Sun.COM freeb(hmp); 426611042SErik.Nordmark@Sun.COM freeb(hmp0); 426711042SErik.Nordmark@Sun.COM ip1dbg(("ip_carve_mp: failed\n")); 426811042SErik.Nordmark@Sun.COM return (ENOBUFS); 426911042SErik.Nordmark@Sun.COM } 427011042SErik.Nordmark@Sun.COM hmp0->b_cont = dmp; 427111042SErik.Nordmark@Sun.COM /* Get the priority marking, if any */ 427211042SErik.Nordmark@Sun.COM hmp0->b_band = priority; 427311042SErik.Nordmark@Sun.COM 427411042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragCreates); 427511042SErik.Nordmark@Sun.COM 427611042SErik.Nordmark@Sun.COM error = postfragfn(hmp0, nce, ixaflags, 427711042SErik.Nordmark@Sun.COM mlen + unfragmentable_len, xmit_hint, szone, nolzid, 427811042SErik.Nordmark@Sun.COM ixa_cookie); 427911042SErik.Nordmark@Sun.COM if (error != 0 && error != EWOULDBLOCK && hmp != NULL) { 428011042SErik.Nordmark@Sun.COM /* No point in sending the other fragments */ 428111042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails); 428211042SErik.Nordmark@Sun.COM ip_drop_output("FragFails: postfragfn failed", 428311042SErik.Nordmark@Sun.COM hmp, ill); 428411042SErik.Nordmark@Sun.COM freeb(hmp); 428511042SErik.Nordmark@Sun.COM freemsg(mp); 428611042SErik.Nordmark@Sun.COM return (error); 428711042SErik.Nordmark@Sun.COM } 428811042SErik.Nordmark@Sun.COM /* No need to redo state machine in loop */ 428911042SErik.Nordmark@Sun.COM ixaflags &= ~IXAF_REACH_CONF; 429011042SErik.Nordmark@Sun.COM 429111042SErik.Nordmark@Sun.COM offset += mlen; 429211042SErik.Nordmark@Sun.COM } 429311042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragOKs); 429411042SErik.Nordmark@Sun.COM return (error); 429511042SErik.Nordmark@Sun.COM } 429611042SErik.Nordmark@Sun.COM 429711042SErik.Nordmark@Sun.COM /* 429811042SErik.Nordmark@Sun.COM * Add a fragment header to an IPv6 packet. 429911042SErik.Nordmark@Sun.COM * Assumes that all the extension headers are contained in the first mblk. 430011042SErik.Nordmark@Sun.COM * 430111042SErik.Nordmark@Sun.COM * The fragment header is inserted after an hop-by-hop options header 430211042SErik.Nordmark@Sun.COM * and after [an optional destinations header followed by] a routing header. 430311042SErik.Nordmark@Sun.COM */ 430411042SErik.Nordmark@Sun.COM mblk_t * 430511042SErik.Nordmark@Sun.COM ip_fraghdr_add_v6(mblk_t *mp, uint32_t ident, ip_xmit_attr_t *ixa) 430611042SErik.Nordmark@Sun.COM { 430711042SErik.Nordmark@Sun.COM ip6_t *ip6h = (ip6_t *)mp->b_rptr; 430811042SErik.Nordmark@Sun.COM ip6_t *fip6h; 430911042SErik.Nordmark@Sun.COM mblk_t *hmp; 431011042SErik.Nordmark@Sun.COM ip6_frag_t *fraghdr; 431111042SErik.Nordmark@Sun.COM size_t unfragmentable_len; 43120Sstevel@tonic-gate uint8_t nexthdr; 43130Sstevel@tonic-gate uint_t prev_nexthdr_offset; 43140Sstevel@tonic-gate uint8_t *ptr; 431511042SErik.Nordmark@Sun.COM uint_t priority = mp->b_band; 431611042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst; 43173284Sapersson 43180Sstevel@tonic-gate /* 43190Sstevel@tonic-gate * Determine the length of the unfragmentable portion of this 43200Sstevel@tonic-gate * datagram. This consists of the IPv6 header, a potential 43210Sstevel@tonic-gate * hop-by-hop options header, a potential pre-routing-header 43220Sstevel@tonic-gate * destination options header, and a potential routing header. 43230Sstevel@tonic-gate */ 43240Sstevel@tonic-gate nexthdr = ip6h->ip6_nxt; 43250Sstevel@tonic-gate prev_nexthdr_offset = (uint8_t *)&ip6h->ip6_nxt - (uint8_t *)ip6h; 43260Sstevel@tonic-gate ptr = (uint8_t *)&ip6h[1]; 43270Sstevel@tonic-gate 43280Sstevel@tonic-gate if (nexthdr == IPPROTO_HOPOPTS) { 43290Sstevel@tonic-gate ip6_hbh_t *hbh_hdr; 43300Sstevel@tonic-gate uint_t hdr_len; 43310Sstevel@tonic-gate 43320Sstevel@tonic-gate hbh_hdr = (ip6_hbh_t *)ptr; 43330Sstevel@tonic-gate hdr_len = 8 * (hbh_hdr->ip6h_len + 1); 43340Sstevel@tonic-gate nexthdr = hbh_hdr->ip6h_nxt; 43350Sstevel@tonic-gate prev_nexthdr_offset = (uint8_t *)&hbh_hdr->ip6h_nxt 43360Sstevel@tonic-gate - (uint8_t *)ip6h; 43370Sstevel@tonic-gate ptr += hdr_len; 43380Sstevel@tonic-gate } 43390Sstevel@tonic-gate if (nexthdr == IPPROTO_DSTOPTS) { 43400Sstevel@tonic-gate ip6_dest_t *dest_hdr; 43410Sstevel@tonic-gate uint_t hdr_len; 43420Sstevel@tonic-gate 43430Sstevel@tonic-gate dest_hdr = (ip6_dest_t *)ptr; 43440Sstevel@tonic-gate if (dest_hdr->ip6d_nxt == IPPROTO_ROUTING) { 43450Sstevel@tonic-gate hdr_len = 8 * (dest_hdr->ip6d_len + 1); 43460Sstevel@tonic-gate nexthdr = dest_hdr->ip6d_nxt; 43470Sstevel@tonic-gate prev_nexthdr_offset = (uint8_t *)&dest_hdr->ip6d_nxt 43480Sstevel@tonic-gate - (uint8_t *)ip6h; 43490Sstevel@tonic-gate ptr += hdr_len; 43500Sstevel@tonic-gate } 43510Sstevel@tonic-gate } 43520Sstevel@tonic-gate if (nexthdr == IPPROTO_ROUTING) { 43530Sstevel@tonic-gate ip6_rthdr_t *rthdr; 43540Sstevel@tonic-gate uint_t hdr_len; 43550Sstevel@tonic-gate 43560Sstevel@tonic-gate rthdr = (ip6_rthdr_t *)ptr; 43570Sstevel@tonic-gate nexthdr = rthdr->ip6r_nxt; 43580Sstevel@tonic-gate prev_nexthdr_offset = (uint8_t *)&rthdr->ip6r_nxt 43590Sstevel@tonic-gate - (uint8_t *)ip6h; 43600Sstevel@tonic-gate hdr_len = 8 * (rthdr->ip6r_len + 1); 43610Sstevel@tonic-gate ptr += hdr_len; 43620Sstevel@tonic-gate } 43630Sstevel@tonic-gate unfragmentable_len = (uint_t)(ptr - (uint8_t *)ip6h); 43640Sstevel@tonic-gate 43650Sstevel@tonic-gate /* 43660Sstevel@tonic-gate * Allocate an mblk with enough room for the link-layer 43670Sstevel@tonic-gate * header, the unfragmentable part of the datagram, and the 436811042SErik.Nordmark@Sun.COM * fragment header. 43690Sstevel@tonic-gate */ 43708778SErik.Nordmark@Sun.COM hmp = allocb_tmpl(unfragmentable_len + sizeof (ip6_frag_t) + 43718778SErik.Nordmark@Sun.COM ipst->ips_ip_wroff_extra, mp); 43720Sstevel@tonic-gate if (hmp == NULL) { 437311042SErik.Nordmark@Sun.COM ill_t *ill = ixa->ixa_nce->nce_ill; 437411042SErik.Nordmark@Sun.COM 437511042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards); 437611042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards: allocb failure", mp, ill); 43770Sstevel@tonic-gate freemsg(mp); 437811042SErik.Nordmark@Sun.COM return (NULL); 43790Sstevel@tonic-gate } 43803448Sdh155122 hmp->b_rptr += ipst->ips_ip_wroff_extra; 43810Sstevel@tonic-gate hmp->b_wptr = hmp->b_rptr + unfragmentable_len + sizeof (ip6_frag_t); 43820Sstevel@tonic-gate 43830Sstevel@tonic-gate fip6h = (ip6_t *)hmp->b_rptr; 43840Sstevel@tonic-gate fraghdr = (ip6_frag_t *)(hmp->b_rptr + unfragmentable_len); 43850Sstevel@tonic-gate 43860Sstevel@tonic-gate bcopy(ip6h, fip6h, unfragmentable_len); 438711042SErik.Nordmark@Sun.COM fip6h->ip6_plen = htons(ntohs(fip6h->ip6_plen) + sizeof (ip6_frag_t)); 43880Sstevel@tonic-gate hmp->b_rptr[prev_nexthdr_offset] = IPPROTO_FRAGMENT; 43890Sstevel@tonic-gate 43900Sstevel@tonic-gate fraghdr->ip6f_nxt = nexthdr; 43910Sstevel@tonic-gate fraghdr->ip6f_reserved = 0; 4392741Smasputra fraghdr->ip6f_offlg = 0; 43930Sstevel@tonic-gate fraghdr->ip6f_ident = htonl(ident); 43940Sstevel@tonic-gate 439511042SErik.Nordmark@Sun.COM /* Get the priority marking, if any */ 439611042SErik.Nordmark@Sun.COM hmp->b_band = priority; 43970Sstevel@tonic-gate 43980Sstevel@tonic-gate /* 43990Sstevel@tonic-gate * Move read ptr past unfragmentable portion, we don't want this part 44000Sstevel@tonic-gate * of the data in our fragments. 44010Sstevel@tonic-gate */ 44020Sstevel@tonic-gate mp->b_rptr += unfragmentable_len; 440311042SErik.Nordmark@Sun.COM hmp->b_cont = mp; 440411042SErik.Nordmark@Sun.COM return (hmp); 44050Sstevel@tonic-gate } 44060Sstevel@tonic-gate 44070Sstevel@tonic-gate /* 44080Sstevel@tonic-gate * Determine if the ill and multicast aspects of that packets 44090Sstevel@tonic-gate * "matches" the conn. 44100Sstevel@tonic-gate */ 44110Sstevel@tonic-gate boolean_t 441211042SErik.Nordmark@Sun.COM conn_wantpacket_v6(conn_t *connp, ip_recv_attr_t *ira, ip6_t *ip6h) 44130Sstevel@tonic-gate { 441411042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_rill; 441511042SErik.Nordmark@Sun.COM zoneid_t zoneid = ira->ira_zoneid; 441611042SErik.Nordmark@Sun.COM uint_t in_ifindex; 441711042SErik.Nordmark@Sun.COM in6_addr_t *v6dst_ptr = &ip6h->ip6_dst; 441811042SErik.Nordmark@Sun.COM in6_addr_t *v6src_ptr = &ip6h->ip6_src; 44190Sstevel@tonic-gate 44200Sstevel@tonic-gate /* 442111042SErik.Nordmark@Sun.COM * conn_incoming_ifindex is set by IPV6_BOUND_IF and as link-local 442211042SErik.Nordmark@Sun.COM * scopeid. This is used to limit 442311042SErik.Nordmark@Sun.COM * unicast and multicast reception to conn_incoming_ifindex. 44240Sstevel@tonic-gate * conn_wantpacket_v6 is called both for unicast and 442511042SErik.Nordmark@Sun.COM * multicast packets. 44268485SPeter.Memishian@Sun.COM */ 442711042SErik.Nordmark@Sun.COM in_ifindex = connp->conn_incoming_ifindex; 442811042SErik.Nordmark@Sun.COM 442911042SErik.Nordmark@Sun.COM /* mpathd can bind to the under IPMP interface, which we allow */ 443011042SErik.Nordmark@Sun.COM if (in_ifindex != 0 && in_ifindex != ill->ill_phyint->phyint_ifindex) { 443111042SErik.Nordmark@Sun.COM if (!IS_UNDER_IPMP(ill)) 443211042SErik.Nordmark@Sun.COM return (B_FALSE); 443311042SErik.Nordmark@Sun.COM 443411042SErik.Nordmark@Sun.COM if (in_ifindex != ipmp_ill_get_ipmp_ifindex(ill)) 443511042SErik.Nordmark@Sun.COM return (B_FALSE); 44360Sstevel@tonic-gate } 44370Sstevel@tonic-gate 443811042SErik.Nordmark@Sun.COM if (!IPCL_ZONE_MATCH(connp, zoneid)) 443911042SErik.Nordmark@Sun.COM return (B_FALSE); 444011042SErik.Nordmark@Sun.COM 444111042SErik.Nordmark@Sun.COM if (!(ira->ira_flags & IRAF_MULTICAST)) 444211042SErik.Nordmark@Sun.COM return (B_TRUE); 444311042SErik.Nordmark@Sun.COM 44440Sstevel@tonic-gate if (connp->conn_multi_router) 44450Sstevel@tonic-gate return (B_TRUE); 44460Sstevel@tonic-gate 444711042SErik.Nordmark@Sun.COM if (ira->ira_protocol == IPPROTO_RSVP) 444811042SErik.Nordmark@Sun.COM return (B_TRUE); 444911042SErik.Nordmark@Sun.COM 445011042SErik.Nordmark@Sun.COM return (conn_hasmembers_ill_withsrc_v6(connp, v6dst_ptr, v6src_ptr, 445111042SErik.Nordmark@Sun.COM ira->ira_ill)); 44520Sstevel@tonic-gate } 44530Sstevel@tonic-gate 44540Sstevel@tonic-gate /* 44550Sstevel@tonic-gate * pr_addr_dbg function provides the needed buffer space to call 44560Sstevel@tonic-gate * inet_ntop() function's 3rd argument. This function should be 44570Sstevel@tonic-gate * used by any kernel routine which wants to save INET6_ADDRSTRLEN 44580Sstevel@tonic-gate * stack buffer space in it's own stack frame. This function uses 44590Sstevel@tonic-gate * a buffer from it's own stack and prints the information. 44600Sstevel@tonic-gate * Example: pr_addr_dbg("func: no route for %s\n ", AF_INET, addr) 44610Sstevel@tonic-gate * 44620Sstevel@tonic-gate * Note: This function can call inet_ntop() once. 44630Sstevel@tonic-gate */ 44640Sstevel@tonic-gate void 44650Sstevel@tonic-gate pr_addr_dbg(char *fmt1, int af, const void *addr) 44660Sstevel@tonic-gate { 44670Sstevel@tonic-gate char buf[INET6_ADDRSTRLEN]; 44680Sstevel@tonic-gate 44690Sstevel@tonic-gate if (fmt1 == NULL) { 44700Sstevel@tonic-gate ip0dbg(("pr_addr_dbg: Wrong arguments\n")); 44710Sstevel@tonic-gate return; 44720Sstevel@tonic-gate } 44730Sstevel@tonic-gate 44740Sstevel@tonic-gate /* 44750Sstevel@tonic-gate * This does not compare debug level and just prints 44760Sstevel@tonic-gate * out. Thus it is the responsibility of the caller 44770Sstevel@tonic-gate * to check the appropriate debug-level before calling 44780Sstevel@tonic-gate * this function. 44790Sstevel@tonic-gate */ 44800Sstevel@tonic-gate if (ip_debug > 0) { 44810Sstevel@tonic-gate printf(fmt1, inet_ntop(af, addr, buf, sizeof (buf))); 44820Sstevel@tonic-gate } 44830Sstevel@tonic-gate 44840Sstevel@tonic-gate 44850Sstevel@tonic-gate } 44860Sstevel@tonic-gate 44870Sstevel@tonic-gate 44880Sstevel@tonic-gate /* 448911042SErik.Nordmark@Sun.COM * Return the length in bytes of the IPv6 headers (base header 449011042SErik.Nordmark@Sun.COM * extension headers) that will be needed based on the 449111042SErik.Nordmark@Sun.COM * ip_pkt_t structure passed by the caller. 44920Sstevel@tonic-gate * 44930Sstevel@tonic-gate * The returned length does not include the length of the upper level 44940Sstevel@tonic-gate * protocol (ULP) header. 44950Sstevel@tonic-gate */ 44960Sstevel@tonic-gate int 449711042SErik.Nordmark@Sun.COM ip_total_hdrs_len_v6(const ip_pkt_t *ipp) 44980Sstevel@tonic-gate { 44990Sstevel@tonic-gate int len; 45000Sstevel@tonic-gate 45010Sstevel@tonic-gate len = IPV6_HDR_LEN; 450211042SErik.Nordmark@Sun.COM 450311042SErik.Nordmark@Sun.COM /* 450411042SErik.Nordmark@Sun.COM * If there's a security label here, then we ignore any hop-by-hop 450511042SErik.Nordmark@Sun.COM * options the user may try to set. 450611042SErik.Nordmark@Sun.COM */ 450711042SErik.Nordmark@Sun.COM if (ipp->ipp_fields & IPPF_LABEL_V6) { 450811042SErik.Nordmark@Sun.COM uint_t hopoptslen; 450911042SErik.Nordmark@Sun.COM /* 451011042SErik.Nordmark@Sun.COM * Note that ipp_label_len_v6 is just the option - not 451111042SErik.Nordmark@Sun.COM * the hopopts extension header. It also needs to be padded 451211042SErik.Nordmark@Sun.COM * to a multiple of 8 bytes. 451311042SErik.Nordmark@Sun.COM */ 451411042SErik.Nordmark@Sun.COM ASSERT(ipp->ipp_label_len_v6 != 0); 451511042SErik.Nordmark@Sun.COM hopoptslen = ipp->ipp_label_len_v6 + sizeof (ip6_hbh_t); 451611042SErik.Nordmark@Sun.COM hopoptslen = (hopoptslen + 7)/8 * 8; 451711042SErik.Nordmark@Sun.COM len += hopoptslen; 451811042SErik.Nordmark@Sun.COM } else if (ipp->ipp_fields & IPPF_HOPOPTS) { 45190Sstevel@tonic-gate ASSERT(ipp->ipp_hopoptslen != 0); 45200Sstevel@tonic-gate len += ipp->ipp_hopoptslen; 45210Sstevel@tonic-gate } 452211042SErik.Nordmark@Sun.COM 452311042SErik.Nordmark@Sun.COM /* 452411042SErik.Nordmark@Sun.COM * En-route destination options 452511042SErik.Nordmark@Sun.COM * Only do them if there's a routing header as well 452611042SErik.Nordmark@Sun.COM */ 452711042SErik.Nordmark@Sun.COM if ((ipp->ipp_fields & (IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) == 452811042SErik.Nordmark@Sun.COM (IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) { 452911042SErik.Nordmark@Sun.COM ASSERT(ipp->ipp_rthdrdstoptslen != 0); 453011042SErik.Nordmark@Sun.COM len += ipp->ipp_rthdrdstoptslen; 453111042SErik.Nordmark@Sun.COM } 45320Sstevel@tonic-gate if (ipp->ipp_fields & IPPF_RTHDR) { 45330Sstevel@tonic-gate ASSERT(ipp->ipp_rthdrlen != 0); 45340Sstevel@tonic-gate len += ipp->ipp_rthdrlen; 45350Sstevel@tonic-gate } 45360Sstevel@tonic-gate if (ipp->ipp_fields & IPPF_DSTOPTS) { 45370Sstevel@tonic-gate ASSERT(ipp->ipp_dstoptslen != 0); 45380Sstevel@tonic-gate len += ipp->ipp_dstoptslen; 45390Sstevel@tonic-gate } 45400Sstevel@tonic-gate return (len); 45410Sstevel@tonic-gate } 45420Sstevel@tonic-gate 45430Sstevel@tonic-gate /* 45440Sstevel@tonic-gate * All-purpose routine to build a header chain of an IPv6 header 454511042SErik.Nordmark@Sun.COM * followed by any required extension headers and a proto header. 45460Sstevel@tonic-gate * 454711042SErik.Nordmark@Sun.COM * The caller has to set the source and destination address as well as 454811042SErik.Nordmark@Sun.COM * ip6_plen. The caller has to massage any routing header and compensate 454911042SErik.Nordmark@Sun.COM * for the ULP pseudo-header checksum due to the source route. 45500Sstevel@tonic-gate * 455111042SErik.Nordmark@Sun.COM * The extension headers will all be fully filled in. 45520Sstevel@tonic-gate */ 45530Sstevel@tonic-gate void 455411042SErik.Nordmark@Sun.COM ip_build_hdrs_v6(uchar_t *buf, uint_t buf_len, const ip_pkt_t *ipp, 455511042SErik.Nordmark@Sun.COM uint8_t protocol, uint32_t flowinfo) 45560Sstevel@tonic-gate { 45570Sstevel@tonic-gate uint8_t *nxthdr_ptr; 45580Sstevel@tonic-gate uint8_t *cp; 455911042SErik.Nordmark@Sun.COM ip6_t *ip6h = (ip6_t *)buf; 456011042SErik.Nordmark@Sun.COM 456111042SErik.Nordmark@Sun.COM /* Initialize IPv6 header */ 456211042SErik.Nordmark@Sun.COM ip6h->ip6_vcf = 456311042SErik.Nordmark@Sun.COM (IPV6_DEFAULT_VERS_AND_FLOW & IPV6_VERS_AND_FLOW_MASK) | 456411042SErik.Nordmark@Sun.COM (flowinfo & ~IPV6_VERS_AND_FLOW_MASK); 456511042SErik.Nordmark@Sun.COM 456611042SErik.Nordmark@Sun.COM if (ipp->ipp_fields & IPPF_TCLASS) { 456711042SErik.Nordmark@Sun.COM /* Overrides the class part of flowinfo */ 456811042SErik.Nordmark@Sun.COM ip6h->ip6_vcf = IPV6_TCLASS_FLOW(ip6h->ip6_vcf, 456911042SErik.Nordmark@Sun.COM ipp->ipp_tclass); 45700Sstevel@tonic-gate } 457111042SErik.Nordmark@Sun.COM 457211042SErik.Nordmark@Sun.COM if (ipp->ipp_fields & IPPF_HOPLIMIT) 457311042SErik.Nordmark@Sun.COM ip6h->ip6_hops = ipp->ipp_hoplimit; 457411042SErik.Nordmark@Sun.COM else 457511042SErik.Nordmark@Sun.COM ip6h->ip6_hops = ipp->ipp_unicast_hops; 457611042SErik.Nordmark@Sun.COM 457711042SErik.Nordmark@Sun.COM if ((ipp->ipp_fields & IPPF_ADDR) && 457811042SErik.Nordmark@Sun.COM !IN6_IS_ADDR_V4MAPPED(&ipp->ipp_addr)) 45790Sstevel@tonic-gate ip6h->ip6_src = ipp->ipp_addr; 45800Sstevel@tonic-gate 45810Sstevel@tonic-gate nxthdr_ptr = (uint8_t *)&ip6h->ip6_nxt; 45820Sstevel@tonic-gate cp = (uint8_t *)&ip6h[1]; 45830Sstevel@tonic-gate /* 45840Sstevel@tonic-gate * Here's where we have to start stringing together 45850Sstevel@tonic-gate * any extension headers in the right order: 45860Sstevel@tonic-gate * Hop-by-hop, destination, routing, and final destination opts. 45870Sstevel@tonic-gate */ 458811042SErik.Nordmark@Sun.COM /* 458911042SErik.Nordmark@Sun.COM * If there's a security label here, then we ignore any hop-by-hop 459011042SErik.Nordmark@Sun.COM * options the user may try to set. 459111042SErik.Nordmark@Sun.COM */ 459211042SErik.Nordmark@Sun.COM if (ipp->ipp_fields & IPPF_LABEL_V6) { 459311042SErik.Nordmark@Sun.COM /* 459411042SErik.Nordmark@Sun.COM * Hop-by-hop options with the label. 459511042SErik.Nordmark@Sun.COM * Note that ipp_label_v6 is just the option - not 459611042SErik.Nordmark@Sun.COM * the hopopts extension header. It also needs to be padded 459711042SErik.Nordmark@Sun.COM * to a multiple of 8 bytes. 459811042SErik.Nordmark@Sun.COM */ 459911042SErik.Nordmark@Sun.COM ip6_hbh_t *hbh = (ip6_hbh_t *)cp; 460011042SErik.Nordmark@Sun.COM uint_t hopoptslen; 460111042SErik.Nordmark@Sun.COM uint_t padlen; 460211042SErik.Nordmark@Sun.COM 460311042SErik.Nordmark@Sun.COM padlen = ipp->ipp_label_len_v6 + sizeof (ip6_hbh_t); 460411042SErik.Nordmark@Sun.COM hopoptslen = (padlen + 7)/8 * 8; 460511042SErik.Nordmark@Sun.COM padlen = hopoptslen - padlen; 460611042SErik.Nordmark@Sun.COM 460711042SErik.Nordmark@Sun.COM *nxthdr_ptr = IPPROTO_HOPOPTS; 460811042SErik.Nordmark@Sun.COM nxthdr_ptr = &hbh->ip6h_nxt; 460911042SErik.Nordmark@Sun.COM hbh->ip6h_len = hopoptslen/8 - 1; 461011042SErik.Nordmark@Sun.COM cp += sizeof (ip6_hbh_t); 461111042SErik.Nordmark@Sun.COM bcopy(ipp->ipp_label_v6, cp, ipp->ipp_label_len_v6); 461211042SErik.Nordmark@Sun.COM cp += ipp->ipp_label_len_v6; 461311042SErik.Nordmark@Sun.COM 461411042SErik.Nordmark@Sun.COM ASSERT(padlen <= 7); 461511042SErik.Nordmark@Sun.COM switch (padlen) { 461611042SErik.Nordmark@Sun.COM case 0: 461711042SErik.Nordmark@Sun.COM break; 461811042SErik.Nordmark@Sun.COM case 1: 461911042SErik.Nordmark@Sun.COM cp[0] = IP6OPT_PAD1; 462011042SErik.Nordmark@Sun.COM break; 462111042SErik.Nordmark@Sun.COM default: 462211042SErik.Nordmark@Sun.COM cp[0] = IP6OPT_PADN; 462311042SErik.Nordmark@Sun.COM cp[1] = padlen - 2; 462411042SErik.Nordmark@Sun.COM bzero(&cp[2], padlen - 2); 462511042SErik.Nordmark@Sun.COM break; 462611042SErik.Nordmark@Sun.COM } 462711042SErik.Nordmark@Sun.COM cp += padlen; 462811042SErik.Nordmark@Sun.COM } else if (ipp->ipp_fields & IPPF_HOPOPTS) { 46290Sstevel@tonic-gate /* Hop-by-hop options */ 46300Sstevel@tonic-gate ip6_hbh_t *hbh = (ip6_hbh_t *)cp; 46310Sstevel@tonic-gate 46320Sstevel@tonic-gate *nxthdr_ptr = IPPROTO_HOPOPTS; 46330Sstevel@tonic-gate nxthdr_ptr = &hbh->ip6h_nxt; 46340Sstevel@tonic-gate 46350Sstevel@tonic-gate bcopy(ipp->ipp_hopopts, cp, ipp->ipp_hopoptslen); 46360Sstevel@tonic-gate cp += ipp->ipp_hopoptslen; 46370Sstevel@tonic-gate } 46380Sstevel@tonic-gate /* 46390Sstevel@tonic-gate * En-route destination options 46400Sstevel@tonic-gate * Only do them if there's a routing header as well 46410Sstevel@tonic-gate */ 464211042SErik.Nordmark@Sun.COM if ((ipp->ipp_fields & (IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) == 464311042SErik.Nordmark@Sun.COM (IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) { 46440Sstevel@tonic-gate ip6_dest_t *dst = (ip6_dest_t *)cp; 46450Sstevel@tonic-gate 46460Sstevel@tonic-gate *nxthdr_ptr = IPPROTO_DSTOPTS; 46470Sstevel@tonic-gate nxthdr_ptr = &dst->ip6d_nxt; 46480Sstevel@tonic-gate 464911042SErik.Nordmark@Sun.COM bcopy(ipp->ipp_rthdrdstopts, cp, ipp->ipp_rthdrdstoptslen); 465011042SErik.Nordmark@Sun.COM cp += ipp->ipp_rthdrdstoptslen; 46510Sstevel@tonic-gate } 46520Sstevel@tonic-gate /* 46530Sstevel@tonic-gate * Routing header next 46540Sstevel@tonic-gate */ 46550Sstevel@tonic-gate if (ipp->ipp_fields & IPPF_RTHDR) { 46560Sstevel@tonic-gate ip6_rthdr_t *rt = (ip6_rthdr_t *)cp; 46570Sstevel@tonic-gate 46580Sstevel@tonic-gate *nxthdr_ptr = IPPROTO_ROUTING; 46590Sstevel@tonic-gate nxthdr_ptr = &rt->ip6r_nxt; 46600Sstevel@tonic-gate 46610Sstevel@tonic-gate bcopy(ipp->ipp_rthdr, cp, ipp->ipp_rthdrlen); 46620Sstevel@tonic-gate cp += ipp->ipp_rthdrlen; 46630Sstevel@tonic-gate } 46640Sstevel@tonic-gate /* 46650Sstevel@tonic-gate * Do ultimate destination options 46660Sstevel@tonic-gate */ 46670Sstevel@tonic-gate if (ipp->ipp_fields & IPPF_DSTOPTS) { 46680Sstevel@tonic-gate ip6_dest_t *dest = (ip6_dest_t *)cp; 46690Sstevel@tonic-gate 46700Sstevel@tonic-gate *nxthdr_ptr = IPPROTO_DSTOPTS; 46710Sstevel@tonic-gate nxthdr_ptr = &dest->ip6d_nxt; 46720Sstevel@tonic-gate 46730Sstevel@tonic-gate bcopy(ipp->ipp_dstopts, cp, ipp->ipp_dstoptslen); 46740Sstevel@tonic-gate cp += ipp->ipp_dstoptslen; 46750Sstevel@tonic-gate } 46760Sstevel@tonic-gate /* 46770Sstevel@tonic-gate * Now set the last header pointer to the proto passed in 46780Sstevel@tonic-gate */ 46790Sstevel@tonic-gate *nxthdr_ptr = protocol; 468011042SErik.Nordmark@Sun.COM ASSERT((int)(cp - buf) == buf_len); 46810Sstevel@tonic-gate } 46820Sstevel@tonic-gate 46830Sstevel@tonic-gate /* 46840Sstevel@tonic-gate * Return a pointer to the routing header extension header 46850Sstevel@tonic-gate * in the IPv6 header(s) chain passed in. 46860Sstevel@tonic-gate * If none found, return NULL 46870Sstevel@tonic-gate * Assumes that all extension headers are in same mblk as the v6 header 46880Sstevel@tonic-gate */ 46890Sstevel@tonic-gate ip6_rthdr_t * 46900Sstevel@tonic-gate ip_find_rthdr_v6(ip6_t *ip6h, uint8_t *endptr) 46910Sstevel@tonic-gate { 46920Sstevel@tonic-gate ip6_dest_t *desthdr; 46930Sstevel@tonic-gate ip6_frag_t *fraghdr; 46940Sstevel@tonic-gate uint_t hdrlen; 46950Sstevel@tonic-gate uint8_t nexthdr; 46960Sstevel@tonic-gate uint8_t *ptr = (uint8_t *)&ip6h[1]; 46970Sstevel@tonic-gate 46980Sstevel@tonic-gate if (ip6h->ip6_nxt == IPPROTO_ROUTING) 46990Sstevel@tonic-gate return ((ip6_rthdr_t *)ptr); 47000Sstevel@tonic-gate 47010Sstevel@tonic-gate /* 47020Sstevel@tonic-gate * The routing header will precede all extension headers 47030Sstevel@tonic-gate * other than the hop-by-hop and destination options 47040Sstevel@tonic-gate * extension headers, so if we see anything other than those, 47050Sstevel@tonic-gate * we're done and didn't find it. 47060Sstevel@tonic-gate * We could see a destination options header alone but no 47070Sstevel@tonic-gate * routing header, in which case we'll return NULL as soon as 47080Sstevel@tonic-gate * we see anything after that. 47090Sstevel@tonic-gate * Hop-by-hop and destination option headers are identical, 47100Sstevel@tonic-gate * so we can use either one we want as a template. 47110Sstevel@tonic-gate */ 47120Sstevel@tonic-gate nexthdr = ip6h->ip6_nxt; 47130Sstevel@tonic-gate while (ptr < endptr) { 47140Sstevel@tonic-gate /* Is there enough left for len + nexthdr? */ 47150Sstevel@tonic-gate if (ptr + MIN_EHDR_LEN > endptr) 47160Sstevel@tonic-gate return (NULL); 47170Sstevel@tonic-gate 47180Sstevel@tonic-gate switch (nexthdr) { 47190Sstevel@tonic-gate case IPPROTO_HOPOPTS: 47200Sstevel@tonic-gate case IPPROTO_DSTOPTS: 47210Sstevel@tonic-gate /* Assumes the headers are identical for hbh and dst */ 47220Sstevel@tonic-gate desthdr = (ip6_dest_t *)ptr; 47230Sstevel@tonic-gate hdrlen = 8 * (desthdr->ip6d_len + 1); 47240Sstevel@tonic-gate nexthdr = desthdr->ip6d_nxt; 47250Sstevel@tonic-gate break; 47260Sstevel@tonic-gate 47270Sstevel@tonic-gate case IPPROTO_ROUTING: 47280Sstevel@tonic-gate return ((ip6_rthdr_t *)ptr); 47290Sstevel@tonic-gate 47300Sstevel@tonic-gate case IPPROTO_FRAGMENT: 47310Sstevel@tonic-gate fraghdr = (ip6_frag_t *)ptr; 47320Sstevel@tonic-gate hdrlen = sizeof (ip6_frag_t); 47330Sstevel@tonic-gate nexthdr = fraghdr->ip6f_nxt; 47340Sstevel@tonic-gate break; 47350Sstevel@tonic-gate 47360Sstevel@tonic-gate default: 47370Sstevel@tonic-gate return (NULL); 47380Sstevel@tonic-gate } 47390Sstevel@tonic-gate ptr += hdrlen; 47400Sstevel@tonic-gate } 47410Sstevel@tonic-gate return (NULL); 47420Sstevel@tonic-gate } 47430Sstevel@tonic-gate 47440Sstevel@tonic-gate /* 47450Sstevel@tonic-gate * Called for source-routed packets originating on this node. 47460Sstevel@tonic-gate * Manipulates the original routing header by moving every entry up 47470Sstevel@tonic-gate * one slot, placing the first entry in the v6 header's v6_dst field, 47480Sstevel@tonic-gate * and placing the ultimate destination in the routing header's last 47490Sstevel@tonic-gate * slot. 47500Sstevel@tonic-gate * 47510Sstevel@tonic-gate * Returns the checksum diference between the ultimate destination 47520Sstevel@tonic-gate * (last hop in the routing header when the packet is sent) and 47530Sstevel@tonic-gate * the first hop (ip6_dst when the packet is sent) 47540Sstevel@tonic-gate */ 47553448Sdh155122 /* ARGSUSED2 */ 47560Sstevel@tonic-gate uint32_t 47573448Sdh155122 ip_massage_options_v6(ip6_t *ip6h, ip6_rthdr_t *rth, netstack_t *ns) 47580Sstevel@tonic-gate { 47590Sstevel@tonic-gate uint_t numaddr; 47600Sstevel@tonic-gate uint_t i; 47610Sstevel@tonic-gate in6_addr_t *addrptr; 47620Sstevel@tonic-gate in6_addr_t tmp; 47630Sstevel@tonic-gate ip6_rthdr0_t *rthdr = (ip6_rthdr0_t *)rth; 47640Sstevel@tonic-gate uint32_t cksm; 47650Sstevel@tonic-gate uint32_t addrsum = 0; 47660Sstevel@tonic-gate uint16_t *ptr; 47670Sstevel@tonic-gate 47680Sstevel@tonic-gate /* 47690Sstevel@tonic-gate * Perform any processing needed for source routing. 47700Sstevel@tonic-gate * We know that all extension headers will be in the same mblk 47710Sstevel@tonic-gate * as the IPv6 header. 47720Sstevel@tonic-gate */ 47730Sstevel@tonic-gate 47740Sstevel@tonic-gate /* 47750Sstevel@tonic-gate * If no segments left in header, or the header length field is zero, 47760Sstevel@tonic-gate * don't move hop addresses around; 47770Sstevel@tonic-gate * Checksum difference is zero. 47780Sstevel@tonic-gate */ 47790Sstevel@tonic-gate if ((rthdr->ip6r0_segleft == 0) || (rthdr->ip6r0_len == 0)) 47800Sstevel@tonic-gate return (0); 47810Sstevel@tonic-gate 47820Sstevel@tonic-gate ptr = (uint16_t *)&ip6h->ip6_dst; 47830Sstevel@tonic-gate cksm = 0; 47840Sstevel@tonic-gate for (i = 0; i < (sizeof (in6_addr_t) / sizeof (uint16_t)); i++) { 47850Sstevel@tonic-gate cksm += ptr[i]; 47860Sstevel@tonic-gate } 47870Sstevel@tonic-gate cksm = (cksm & 0xFFFF) + (cksm >> 16); 47880Sstevel@tonic-gate 47890Sstevel@tonic-gate /* 47900Sstevel@tonic-gate * Here's where the fun begins - we have to 47910Sstevel@tonic-gate * move all addresses up one spot, take the 47920Sstevel@tonic-gate * first hop and make it our first ip6_dst, 47930Sstevel@tonic-gate * and place the ultimate destination in the 47940Sstevel@tonic-gate * newly-opened last slot. 47950Sstevel@tonic-gate */ 47960Sstevel@tonic-gate addrptr = (in6_addr_t *)((char *)rthdr + sizeof (*rthdr)); 47970Sstevel@tonic-gate numaddr = rthdr->ip6r0_len / 2; 47980Sstevel@tonic-gate tmp = *addrptr; 47990Sstevel@tonic-gate for (i = 0; i < (numaddr - 1); addrptr++, i++) { 48000Sstevel@tonic-gate *addrptr = addrptr[1]; 48010Sstevel@tonic-gate } 48020Sstevel@tonic-gate *addrptr = ip6h->ip6_dst; 48030Sstevel@tonic-gate ip6h->ip6_dst = tmp; 48040Sstevel@tonic-gate 48050Sstevel@tonic-gate /* 48060Sstevel@tonic-gate * From the checksummed ultimate destination subtract the checksummed 48070Sstevel@tonic-gate * current ip6_dst (the first hop address). Return that number. 48080Sstevel@tonic-gate * (In the v4 case, the second part of this is done in each routine 48090Sstevel@tonic-gate * that calls ip_massage_options(). We do it all in this one place 48100Sstevel@tonic-gate * for v6). 48110Sstevel@tonic-gate */ 48120Sstevel@tonic-gate ptr = (uint16_t *)&ip6h->ip6_dst; 48130Sstevel@tonic-gate for (i = 0; i < (sizeof (in6_addr_t) / sizeof (uint16_t)); i++) { 48140Sstevel@tonic-gate addrsum += ptr[i]; 48150Sstevel@tonic-gate } 48160Sstevel@tonic-gate cksm -= ((addrsum >> 16) + (addrsum & 0xFFFF)); 48170Sstevel@tonic-gate if ((int)cksm < 0) 48180Sstevel@tonic-gate cksm--; 48190Sstevel@tonic-gate cksm = (cksm & 0xFFFF) + (cksm >> 16); 48200Sstevel@tonic-gate 48210Sstevel@tonic-gate return (cksm); 48220Sstevel@tonic-gate } 48230Sstevel@tonic-gate 48240Sstevel@tonic-gate void 48253448Sdh155122 *ip6_kstat_init(netstackid_t stackid, ip6_stat_t *ip6_statisticsp) 48260Sstevel@tonic-gate { 48273448Sdh155122 kstat_t *ksp; 48283448Sdh155122 48293448Sdh155122 ip6_stat_t template = { 48303448Sdh155122 { "ip6_udp_fannorm", KSTAT_DATA_UINT64 }, 48313448Sdh155122 { "ip6_udp_fanmb", KSTAT_DATA_UINT64 }, 483211042SErik.Nordmark@Sun.COM { "ip6_recv_pullup", KSTAT_DATA_UINT64 }, 483311042SErik.Nordmark@Sun.COM { "ip6_db_ref", KSTAT_DATA_UINT64 }, 483411042SErik.Nordmark@Sun.COM { "ip6_notaligned", KSTAT_DATA_UINT64 }, 483511042SErik.Nordmark@Sun.COM { "ip6_multimblk", KSTAT_DATA_UINT64 }, 483611042SErik.Nordmark@Sun.COM { "ipsec_proto_ahesp", KSTAT_DATA_UINT64 }, 48373448Sdh155122 { "ip6_out_sw_cksum", KSTAT_DATA_UINT64 }, 483811042SErik.Nordmark@Sun.COM { "ip6_out_sw_cksum_bytes", KSTAT_DATA_UINT64 }, 48393448Sdh155122 { "ip6_in_sw_cksum", KSTAT_DATA_UINT64 }, 48403448Sdh155122 { "ip6_tcp_in_full_hw_cksum_err", KSTAT_DATA_UINT64 }, 48413448Sdh155122 { "ip6_tcp_in_part_hw_cksum_err", KSTAT_DATA_UINT64 }, 48423448Sdh155122 { "ip6_tcp_in_sw_cksum_err", KSTAT_DATA_UINT64 }, 48433448Sdh155122 { "ip6_udp_in_full_hw_cksum_err", KSTAT_DATA_UINT64 }, 48443448Sdh155122 { "ip6_udp_in_part_hw_cksum_err", KSTAT_DATA_UINT64 }, 48453448Sdh155122 { "ip6_udp_in_sw_cksum_err", KSTAT_DATA_UINT64 }, 48463448Sdh155122 }; 48473448Sdh155122 ksp = kstat_create_netstack("ip", 0, "ip6stat", "net", 48483448Sdh155122 KSTAT_TYPE_NAMED, sizeof (template) / sizeof (kstat_named_t), 48493448Sdh155122 KSTAT_FLAG_VIRTUAL, stackid); 48503448Sdh155122 48513448Sdh155122 if (ksp == NULL) 48523448Sdh155122 return (NULL); 48533448Sdh155122 48543448Sdh155122 bcopy(&template, ip6_statisticsp, sizeof (template)); 48553448Sdh155122 ksp->ks_data = (void *)ip6_statisticsp; 48563448Sdh155122 ksp->ks_private = (void *)(uintptr_t)stackid; 48573448Sdh155122 48583448Sdh155122 kstat_install(ksp); 48593448Sdh155122 return (ksp); 48603448Sdh155122 } 48613448Sdh155122 48623448Sdh155122 void 48633448Sdh155122 ip6_kstat_fini(netstackid_t stackid, kstat_t *ksp) 48643448Sdh155122 { 48653448Sdh155122 if (ksp != NULL) { 48663448Sdh155122 ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private); 48673448Sdh155122 kstat_delete_netstack(ksp, stackid); 48680Sstevel@tonic-gate } 48690Sstevel@tonic-gate } 48700Sstevel@tonic-gate 48710Sstevel@tonic-gate /* 48720Sstevel@tonic-gate * The following two functions set and get the value for the 48730Sstevel@tonic-gate * IPV6_SRC_PREFERENCES socket option. 48740Sstevel@tonic-gate */ 48750Sstevel@tonic-gate int 487611042SErik.Nordmark@Sun.COM ip6_set_src_preferences(ip_xmit_attr_t *ixa, uint32_t prefs) 48770Sstevel@tonic-gate { 48780Sstevel@tonic-gate /* 48790Sstevel@tonic-gate * We only support preferences that are covered by 48800Sstevel@tonic-gate * IPV6_PREFER_SRC_MASK. 48810Sstevel@tonic-gate */ 48820Sstevel@tonic-gate if (prefs & ~IPV6_PREFER_SRC_MASK) 48830Sstevel@tonic-gate return (EINVAL); 48840Sstevel@tonic-gate 48850Sstevel@tonic-gate /* 48860Sstevel@tonic-gate * Look for conflicting preferences or default preferences. If 48870Sstevel@tonic-gate * both bits of a related pair are clear, the application wants the 48880Sstevel@tonic-gate * system's default value for that pair. Both bits in a pair can't 48890Sstevel@tonic-gate * be set. 48900Sstevel@tonic-gate */ 48910Sstevel@tonic-gate if ((prefs & IPV6_PREFER_SRC_MIPMASK) == 0) { 48920Sstevel@tonic-gate prefs |= IPV6_PREFER_SRC_MIPDEFAULT; 48930Sstevel@tonic-gate } else if ((prefs & IPV6_PREFER_SRC_MIPMASK) == 48940Sstevel@tonic-gate IPV6_PREFER_SRC_MIPMASK) { 48950Sstevel@tonic-gate return (EINVAL); 48960Sstevel@tonic-gate } 48970Sstevel@tonic-gate if ((prefs & IPV6_PREFER_SRC_TMPMASK) == 0) { 48980Sstevel@tonic-gate prefs |= IPV6_PREFER_SRC_TMPDEFAULT; 48990Sstevel@tonic-gate } else if ((prefs & IPV6_PREFER_SRC_TMPMASK) == 49000Sstevel@tonic-gate IPV6_PREFER_SRC_TMPMASK) { 49010Sstevel@tonic-gate return (EINVAL); 49020Sstevel@tonic-gate } 49030Sstevel@tonic-gate if ((prefs & IPV6_PREFER_SRC_CGAMASK) == 0) { 49040Sstevel@tonic-gate prefs |= IPV6_PREFER_SRC_CGADEFAULT; 49050Sstevel@tonic-gate } else if ((prefs & IPV6_PREFER_SRC_CGAMASK) == 49060Sstevel@tonic-gate IPV6_PREFER_SRC_CGAMASK) { 49070Sstevel@tonic-gate return (EINVAL); 49080Sstevel@tonic-gate } 49090Sstevel@tonic-gate 491011042SErik.Nordmark@Sun.COM ixa->ixa_src_preferences = prefs; 49110Sstevel@tonic-gate return (0); 49120Sstevel@tonic-gate } 49130Sstevel@tonic-gate 49140Sstevel@tonic-gate size_t 491511042SErik.Nordmark@Sun.COM ip6_get_src_preferences(ip_xmit_attr_t *ixa, uint32_t *val) 49160Sstevel@tonic-gate { 491711042SErik.Nordmark@Sun.COM *val = ixa->ixa_src_preferences; 491811042SErik.Nordmark@Sun.COM return (sizeof (ixa->ixa_src_preferences)); 49190Sstevel@tonic-gate } 49200Sstevel@tonic-gate 49210Sstevel@tonic-gate /* 49220Sstevel@tonic-gate * Get the size of the IP options (including the IP headers size) 49230Sstevel@tonic-gate * without including the AH header's size. If till_ah is B_FALSE, 49240Sstevel@tonic-gate * and if AH header is present, dest options beyond AH header will 49250Sstevel@tonic-gate * also be included in the returned size. 49260Sstevel@tonic-gate */ 49270Sstevel@tonic-gate int 49280Sstevel@tonic-gate ipsec_ah_get_hdr_size_v6(mblk_t *mp, boolean_t till_ah) 49290Sstevel@tonic-gate { 49300Sstevel@tonic-gate ip6_t *ip6h; 49310Sstevel@tonic-gate uint8_t nexthdr; 49320Sstevel@tonic-gate uint8_t *whereptr; 49330Sstevel@tonic-gate ip6_hbh_t *hbhhdr; 49340Sstevel@tonic-gate ip6_dest_t *dsthdr; 49350Sstevel@tonic-gate ip6_rthdr_t *rthdr; 49360Sstevel@tonic-gate int ehdrlen; 49370Sstevel@tonic-gate int size; 49380Sstevel@tonic-gate ah_t *ah; 49390Sstevel@tonic-gate 49400Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 49410Sstevel@tonic-gate size = IPV6_HDR_LEN; 49420Sstevel@tonic-gate nexthdr = ip6h->ip6_nxt; 49430Sstevel@tonic-gate whereptr = (uint8_t *)&ip6h[1]; 49440Sstevel@tonic-gate for (;;) { 49450Sstevel@tonic-gate /* Assume IP has already stripped it */ 494611042SErik.Nordmark@Sun.COM ASSERT(nexthdr != IPPROTO_FRAGMENT); 49470Sstevel@tonic-gate switch (nexthdr) { 49480Sstevel@tonic-gate case IPPROTO_HOPOPTS: 49490Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)whereptr; 49500Sstevel@tonic-gate nexthdr = hbhhdr->ip6h_nxt; 49510Sstevel@tonic-gate ehdrlen = 8 * (hbhhdr->ip6h_len + 1); 49520Sstevel@tonic-gate break; 49530Sstevel@tonic-gate case IPPROTO_DSTOPTS: 49540Sstevel@tonic-gate dsthdr = (ip6_dest_t *)whereptr; 49550Sstevel@tonic-gate nexthdr = dsthdr->ip6d_nxt; 49560Sstevel@tonic-gate ehdrlen = 8 * (dsthdr->ip6d_len + 1); 49570Sstevel@tonic-gate break; 49580Sstevel@tonic-gate case IPPROTO_ROUTING: 49590Sstevel@tonic-gate rthdr = (ip6_rthdr_t *)whereptr; 49600Sstevel@tonic-gate nexthdr = rthdr->ip6r_nxt; 49610Sstevel@tonic-gate ehdrlen = 8 * (rthdr->ip6r_len + 1); 49620Sstevel@tonic-gate break; 49630Sstevel@tonic-gate default : 49640Sstevel@tonic-gate if (till_ah) { 49650Sstevel@tonic-gate ASSERT(nexthdr == IPPROTO_AH); 49660Sstevel@tonic-gate return (size); 49670Sstevel@tonic-gate } 49680Sstevel@tonic-gate /* 49690Sstevel@tonic-gate * If we don't have a AH header to traverse, 49700Sstevel@tonic-gate * return now. This happens normally for 49710Sstevel@tonic-gate * outbound datagrams where we have not inserted 49720Sstevel@tonic-gate * the AH header. 49730Sstevel@tonic-gate */ 49740Sstevel@tonic-gate if (nexthdr != IPPROTO_AH) { 49750Sstevel@tonic-gate return (size); 49760Sstevel@tonic-gate } 49770Sstevel@tonic-gate 49780Sstevel@tonic-gate /* 49790Sstevel@tonic-gate * We don't include the AH header's size 49800Sstevel@tonic-gate * to be symmetrical with other cases where 49810Sstevel@tonic-gate * we either don't have a AH header (outbound) 49820Sstevel@tonic-gate * or peek into the AH header yet (inbound and 49830Sstevel@tonic-gate * not pulled up yet). 49840Sstevel@tonic-gate */ 49850Sstevel@tonic-gate ah = (ah_t *)whereptr; 49860Sstevel@tonic-gate nexthdr = ah->ah_nexthdr; 49870Sstevel@tonic-gate ehdrlen = (ah->ah_length << 2) + 8; 49880Sstevel@tonic-gate 49890Sstevel@tonic-gate if (nexthdr == IPPROTO_DSTOPTS) { 49900Sstevel@tonic-gate if (whereptr + ehdrlen >= mp->b_wptr) { 49910Sstevel@tonic-gate /* 49920Sstevel@tonic-gate * The destination options header 49930Sstevel@tonic-gate * is not part of the first mblk. 49940Sstevel@tonic-gate */ 49950Sstevel@tonic-gate whereptr = mp->b_cont->b_rptr; 49960Sstevel@tonic-gate } else { 49970Sstevel@tonic-gate whereptr += ehdrlen; 49980Sstevel@tonic-gate } 49990Sstevel@tonic-gate 50000Sstevel@tonic-gate dsthdr = (ip6_dest_t *)whereptr; 50010Sstevel@tonic-gate ehdrlen = 8 * (dsthdr->ip6d_len + 1); 50020Sstevel@tonic-gate size += ehdrlen; 50030Sstevel@tonic-gate } 50040Sstevel@tonic-gate return (size); 50050Sstevel@tonic-gate } 50060Sstevel@tonic-gate whereptr += ehdrlen; 50070Sstevel@tonic-gate size += ehdrlen; 50080Sstevel@tonic-gate } 50090Sstevel@tonic-gate } 50108485SPeter.Memishian@Sun.COM 50118485SPeter.Memishian@Sun.COM /* 50128485SPeter.Memishian@Sun.COM * Utility routine that checks if `v6srcp' is a valid address on underlying 50138485SPeter.Memishian@Sun.COM * interface `ill'. If `ipifp' is non-NULL, it's set to a held ipif 50148485SPeter.Memishian@Sun.COM * associated with `v6srcp' on success. NOTE: if this is not called from 50158485SPeter.Memishian@Sun.COM * inside the IPSQ (ill_g_lock is not held), `ill' may be removed from the 50168485SPeter.Memishian@Sun.COM * group during or after this lookup. 50178485SPeter.Memishian@Sun.COM */ 501811042SErik.Nordmark@Sun.COM boolean_t 50198485SPeter.Memishian@Sun.COM ipif_lookup_testaddr_v6(ill_t *ill, const in6_addr_t *v6srcp, ipif_t **ipifp) 50208485SPeter.Memishian@Sun.COM { 50218485SPeter.Memishian@Sun.COM ipif_t *ipif; 50228485SPeter.Memishian@Sun.COM 502311042SErik.Nordmark@Sun.COM 50248485SPeter.Memishian@Sun.COM ipif = ipif_lookup_addr_exact_v6(v6srcp, ill, ill->ill_ipst); 50258485SPeter.Memishian@Sun.COM if (ipif != NULL) { 50268485SPeter.Memishian@Sun.COM if (ipifp != NULL) 50278485SPeter.Memishian@Sun.COM *ipifp = ipif; 50288485SPeter.Memishian@Sun.COM else 50298485SPeter.Memishian@Sun.COM ipif_refrele(ipif); 50308485SPeter.Memishian@Sun.COM return (B_TRUE); 50318485SPeter.Memishian@Sun.COM } 50328485SPeter.Memishian@Sun.COM 50338485SPeter.Memishian@Sun.COM if (ip_debug > 2) { 50348485SPeter.Memishian@Sun.COM pr_addr_dbg("ipif_lookup_testaddr_v6: cannot find ipif for " 50358485SPeter.Memishian@Sun.COM "src %s\n", AF_INET6, v6srcp); 50368485SPeter.Memishian@Sun.COM } 50378485SPeter.Memishian@Sun.COM return (B_FALSE); 50388485SPeter.Memishian@Sun.COM } 5039