17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 595579064Sja97890 * Common Development and Distribution License (the "License"). 695579064Sja97890 * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 2253287767SSowmini Varadhan * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. 237c478bd9Sstevel@tonic-gate * Copyright (c) 1990 Mentat Inc. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #include <sys/types.h> 277c478bd9Sstevel@tonic-gate #include <sys/stream.h> 287c478bd9Sstevel@tonic-gate #include <sys/dlpi.h> 297c478bd9Sstevel@tonic-gate #include <sys/stropts.h> 307c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 317c478bd9Sstevel@tonic-gate #include <sys/strsun.h> 327c478bd9Sstevel@tonic-gate #include <sys/strlog.h> 337c478bd9Sstevel@tonic-gate #include <sys/strsubr.h> 347c478bd9Sstevel@tonic-gate #define _SUN_TPI_VERSION 2 357c478bd9Sstevel@tonic-gate #include <sys/tihdr.h> 367c478bd9Sstevel@tonic-gate #include <sys/ddi.h> 377c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 387c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 397c478bd9Sstevel@tonic-gate #include <sys/debug.h> 40381a2a9aSdr146992 #include <sys/sdt.h> 417c478bd9Sstevel@tonic-gate #include <sys/kobj.h> 427c478bd9Sstevel@tonic-gate #include <sys/zone.h> 43381a2a9aSdr146992 #include <sys/neti.h> 44381a2a9aSdr146992 #include <sys/hook.h> 457c478bd9Sstevel@tonic-gate 467c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 477c478bd9Sstevel@tonic-gate #include <sys/systm.h> 487c478bd9Sstevel@tonic-gate #include <sys/param.h> 497c478bd9Sstevel@tonic-gate #include <sys/socket.h> 507c478bd9Sstevel@tonic-gate #include <sys/vtrace.h> 517c478bd9Sstevel@tonic-gate #include <sys/isa_defs.h> 527c478bd9Sstevel@tonic-gate #include <sys/atomic.h> 537c478bd9Sstevel@tonic-gate #include <sys/policy.h> 54bd670b35SErik Nordmark #include <sys/mac.h> 557c478bd9Sstevel@tonic-gate #include <net/if.h> 56ff550d0eSmasputra #include <net/if_types.h> 577c478bd9Sstevel@tonic-gate #include <net/route.h> 587c478bd9Sstevel@tonic-gate #include <net/if_dl.h> 597c478bd9Sstevel@tonic-gate #include <sys/sockio.h> 607c478bd9Sstevel@tonic-gate #include <netinet/in.h> 617c478bd9Sstevel@tonic-gate #include <netinet/ip6.h> 627c478bd9Sstevel@tonic-gate #include <netinet/icmp6.h> 637c478bd9Sstevel@tonic-gate #include <netinet/sctp.h> 647c478bd9Sstevel@tonic-gate 657c478bd9Sstevel@tonic-gate #include <inet/common.h> 667c478bd9Sstevel@tonic-gate #include <inet/mi.h> 67fc80c0dfSnordmark #include <inet/optcom.h> 687c478bd9Sstevel@tonic-gate #include <inet/mib2.h> 697c478bd9Sstevel@tonic-gate #include <inet/nd.h> 707c478bd9Sstevel@tonic-gate #include <inet/arp.h> 717c478bd9Sstevel@tonic-gate 727c478bd9Sstevel@tonic-gate #include <inet/ip.h> 73ff550d0eSmasputra #include <inet/ip_impl.h> 747c478bd9Sstevel@tonic-gate #include <inet/ip6.h> 757c478bd9Sstevel@tonic-gate #include <inet/ip6_asp.h> 767c478bd9Sstevel@tonic-gate #include <inet/tcp.h> 77ff550d0eSmasputra #include <inet/tcp_impl.h> 78ff550d0eSmasputra #include <inet/udp_impl.h> 797c478bd9Sstevel@tonic-gate #include <inet/ipp_common.h> 807c478bd9Sstevel@tonic-gate 817c478bd9Sstevel@tonic-gate #include <inet/ip_multi.h> 827c478bd9Sstevel@tonic-gate #include <inet/ip_if.h> 837c478bd9Sstevel@tonic-gate #include <inet/ip_ire.h> 847c478bd9Sstevel@tonic-gate #include <inet/ip_rts.h> 857c478bd9Sstevel@tonic-gate #include <inet/ip_ndp.h> 867c478bd9Sstevel@tonic-gate #include <net/pfkeyv2.h> 877c478bd9Sstevel@tonic-gate #include <inet/sadb.h> 887c478bd9Sstevel@tonic-gate #include <inet/ipsec_impl.h> 892b24ab6bSSebastien Roy #include <inet/iptun/iptun_impl.h> 907c478bd9Sstevel@tonic-gate #include <inet/sctp_ip.h> 917c478bd9Sstevel@tonic-gate #include <sys/pattr.h> 927c478bd9Sstevel@tonic-gate #include <inet/ipclassifier.h> 937c478bd9Sstevel@tonic-gate #include <inet/ipsecah.h> 94fc80c0dfSnordmark #include <inet/rawip_impl.h> 95fc80c0dfSnordmark #include <inet/rts_impl.h> 96da14cebeSEric Cheng #include <sys/squeue_impl.h> 977c478bd9Sstevel@tonic-gate #include <sys/squeue.h> 987c478bd9Sstevel@tonic-gate 9945916cd2Sjpk #include <sys/tsol/label.h> 10045916cd2Sjpk #include <sys/tsol/tnet.h> 10145916cd2Sjpk 10269bb4bb4Scarlsonj /* Temporary; for CR 6451644 work-around */ 10369bb4bb4Scarlsonj #include <sys/ethernet.h> 10469bb4bb4Scarlsonj 1057c478bd9Sstevel@tonic-gate /* 1067c478bd9Sstevel@tonic-gate * Naming conventions: 1077c478bd9Sstevel@tonic-gate * These rules should be judiciously applied 1087c478bd9Sstevel@tonic-gate * if there is a need to identify something as IPv6 versus IPv4 1097c478bd9Sstevel@tonic-gate * IPv6 funcions will end with _v6 in the ip module. 1107c478bd9Sstevel@tonic-gate * IPv6 funcions will end with _ipv6 in the transport modules. 1117c478bd9Sstevel@tonic-gate * IPv6 macros: 1127c478bd9Sstevel@tonic-gate * Some macros end with _V6; e.g. ILL_FRAG_HASH_V6 1137c478bd9Sstevel@tonic-gate * Some macros start with V6_; e.g. V6_OR_V4_INADDR_ANY 1147c478bd9Sstevel@tonic-gate * And then there are ..V4_PART_OF_V6. 1157c478bd9Sstevel@tonic-gate * The intent is that macros in the ip module end with _V6. 1167c478bd9Sstevel@tonic-gate * IPv6 global variables will start with ipv6_ 1177c478bd9Sstevel@tonic-gate * IPv6 structures will start with ipv6 1187c478bd9Sstevel@tonic-gate * IPv6 defined constants should start with IPV6_ 1197c478bd9Sstevel@tonic-gate * (but then there are NDP_DEFAULT_VERS_PRI_AND_FLOW, etc) 1207c478bd9Sstevel@tonic-gate */ 1217c478bd9Sstevel@tonic-gate 1227c478bd9Sstevel@tonic-gate /* 12345916cd2Sjpk * ip6opt_ls is used to enable IPv6 (via /etc/system on TX systems). 12445916cd2Sjpk * We need to do this because we didn't obtain the IP6OPT_LS (0x0a) 12545916cd2Sjpk * from IANA. This mechanism will remain in effect until an official 12645916cd2Sjpk * number is obtained. 12745916cd2Sjpk */ 12845916cd2Sjpk uchar_t ip6opt_ls; 12945916cd2Sjpk 1307c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_all_ones = 1317c478bd9Sstevel@tonic-gate { 0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU }; 1327c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_all_zeros = { 0, 0, 0, 0 }; 1337c478bd9Sstevel@tonic-gate 1347c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN 1357c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_unspecified_group = { 0xff000000U, 0, 0, 0 }; 1367c478bd9Sstevel@tonic-gate #else /* _BIG_ENDIAN */ 1377c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_unspecified_group = { 0x000000ffU, 0, 0, 0 }; 1387c478bd9Sstevel@tonic-gate #endif /* _BIG_ENDIAN */ 1397c478bd9Sstevel@tonic-gate 1407c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN 1417c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_loopback = { 0, 0, 0, 0x00000001U }; 1427c478bd9Sstevel@tonic-gate #else /* _BIG_ENDIAN */ 1437c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_loopback = { 0, 0, 0, 0x01000000U }; 1447c478bd9Sstevel@tonic-gate #endif /* _BIG_ENDIAN */ 1457c478bd9Sstevel@tonic-gate 1467c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN 1477c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_all_hosts_mcast = { 0xff020000U, 0, 0, 0x00000001U }; 1487c478bd9Sstevel@tonic-gate #else /* _BIG_ENDIAN */ 1497c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_all_hosts_mcast = { 0x000002ffU, 0, 0, 0x01000000U }; 1507c478bd9Sstevel@tonic-gate #endif /* _BIG_ENDIAN */ 1517c478bd9Sstevel@tonic-gate 1527c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN 1537c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_all_rtrs_mcast = { 0xff020000U, 0, 0, 0x00000002U }; 1547c478bd9Sstevel@tonic-gate #else /* _BIG_ENDIAN */ 1557c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_all_rtrs_mcast = { 0x000002ffU, 0, 0, 0x02000000U }; 1567c478bd9Sstevel@tonic-gate #endif /* _BIG_ENDIAN */ 1577c478bd9Sstevel@tonic-gate 1587c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN 1597c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_all_v2rtrs_mcast = { 0xff020000U, 0, 0, 0x00000016U }; 1607c478bd9Sstevel@tonic-gate #else /* _BIG_ENDIAN */ 1617c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_all_v2rtrs_mcast = { 0x000002ffU, 0, 0, 0x16000000U }; 1627c478bd9Sstevel@tonic-gate #endif /* _BIG_ENDIAN */ 1637c478bd9Sstevel@tonic-gate 1647c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN 1657c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_solicited_node_mcast = 1667c478bd9Sstevel@tonic-gate { 0xff020000U, 0, 0x00000001U, 0xff000000U }; 1677c478bd9Sstevel@tonic-gate #else /* _BIG_ENDIAN */ 1687c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_solicited_node_mcast = 1697c478bd9Sstevel@tonic-gate { 0x000002ffU, 0, 0x01000000U, 0x000000ffU }; 1707c478bd9Sstevel@tonic-gate #endif /* _BIG_ENDIAN */ 1717c478bd9Sstevel@tonic-gate 172bd670b35SErik Nordmark static boolean_t icmp_inbound_verify_v6(mblk_t *, icmp6_t *, ip_recv_attr_t *); 173bd670b35SErik Nordmark static void icmp_inbound_too_big_v6(icmp6_t *, ip_recv_attr_t *); 174bd670b35SErik Nordmark static void icmp_pkt_v6(mblk_t *, void *, size_t, const in6_addr_t *, 175bd670b35SErik Nordmark ip_recv_attr_t *); 176bd670b35SErik Nordmark static void icmp_redirect_v6(mblk_t *, ip6_t *, nd_redirect_t *, 177bd670b35SErik Nordmark ip_recv_attr_t *); 178bd670b35SErik Nordmark static void icmp_send_redirect_v6(mblk_t *, in6_addr_t *, 179bd670b35SErik Nordmark in6_addr_t *, ip_recv_attr_t *); 180bd670b35SErik Nordmark static void icmp_send_reply_v6(mblk_t *, ip6_t *, icmp6_t *, 181bd670b35SErik Nordmark ip_recv_attr_t *); 182f4b3ec61Sdh155122 static boolean_t ip_source_routed_v6(ip6_t *, mblk_t *, ip_stack_t *); 1837c478bd9Sstevel@tonic-gate 1847c478bd9Sstevel@tonic-gate /* 185bd670b35SErik Nordmark * icmp_inbound_v6 deals with ICMP messages that are handled by IP. 186bd670b35SErik Nordmark * If the ICMP message is consumed by IP, i.e., it should not be delivered 187bd670b35SErik Nordmark * to any IPPROTO_ICMP raw sockets, then it returns NULL. 188bd670b35SErik Nordmark * Likewise, if the ICMP error is misformed (too short, etc), then it 189bd670b35SErik Nordmark * returns NULL. The caller uses this to determine whether or not to send 190bd670b35SErik Nordmark * to raw sockets. 1917c478bd9Sstevel@tonic-gate * 1927c478bd9Sstevel@tonic-gate * All error messages are passed to the matching transport stream. 1937c478bd9Sstevel@tonic-gate * 194bd670b35SErik Nordmark * See comment for icmp_inbound_v4() on how IPsec is handled. 1957c478bd9Sstevel@tonic-gate */ 196bd670b35SErik Nordmark mblk_t * 197bd670b35SErik Nordmark icmp_inbound_v6(mblk_t *mp, ip_recv_attr_t *ira) 1987c478bd9Sstevel@tonic-gate { 1997c478bd9Sstevel@tonic-gate icmp6_t *icmp6; 200bd670b35SErik Nordmark ip6_t *ip6h; /* Outer header */ 201bd670b35SErik Nordmark int ip_hdr_length; /* Outer header length */ 2027c478bd9Sstevel@tonic-gate boolean_t interested; 203bd670b35SErik Nordmark ill_t *ill = ira->ira_ill; 204f4b3ec61Sdh155122 ip_stack_t *ipst = ill->ill_ipst; 205bd670b35SErik Nordmark mblk_t *mp_ret = NULL; 2067c478bd9Sstevel@tonic-gate 2077c478bd9Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 2087c478bd9Sstevel@tonic-gate 2097c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInMsgs); 2107c478bd9Sstevel@tonic-gate 21101685f97SSowmini Varadhan /* Check for Martian packets */ 21201685f97SSowmini Varadhan if (IN6_IS_ADDR_MULTICAST(&ip6h->ip6_src)) { 21301685f97SSowmini Varadhan BUMP_MIB(ill->ill_ip_mib, ipIfStatsInAddrErrors); 21401685f97SSowmini Varadhan ip_drop_input("ipIfStatsInAddrErrors: mcast src", mp, ill); 21501685f97SSowmini Varadhan freemsg(mp); 21601685f97SSowmini Varadhan return (NULL); 21701685f97SSowmini Varadhan } 21801685f97SSowmini Varadhan 219bd670b35SErik Nordmark /* Make sure ira_l2src is set for ndp_input */ 220bd670b35SErik Nordmark if (!(ira->ira_flags & IRAF_L2SRC_SET)) 221bd670b35SErik Nordmark ip_setl2src(mp, ira, ira->ira_rill); 2227c478bd9Sstevel@tonic-gate 223bd670b35SErik Nordmark ip_hdr_length = ira->ira_ip_hdr_length; 224bd670b35SErik Nordmark if ((mp->b_wptr - mp->b_rptr) < (ip_hdr_length + ICMP6_MINLEN)) { 225bd670b35SErik Nordmark if (ira->ira_pktlen < (ip_hdr_length + ICMP6_MINLEN)) { 226bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsInTruncatedPkts); 227bd670b35SErik Nordmark ip_drop_input("ipIfStatsInTruncatedPkts", mp, ill); 228bd670b35SErik Nordmark freemsg(mp); 229bd670b35SErik Nordmark return (NULL); 230bd670b35SErik Nordmark } 231bd670b35SErik Nordmark ip6h = ip_pullup(mp, ip_hdr_length + ICMP6_MINLEN, ira); 232bd670b35SErik Nordmark if (ip6h == NULL) { 23345916cd2Sjpk BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors); 234bd670b35SErik Nordmark freemsg(mp); 235bd670b35SErik Nordmark return (NULL); 23645916cd2Sjpk } 23745916cd2Sjpk } 23845916cd2Sjpk 239bd670b35SErik Nordmark icmp6 = (icmp6_t *)(&mp->b_rptr[ip_hdr_length]); 240bd670b35SErik Nordmark DTRACE_PROBE2(icmp__inbound__v6, ip6_t *, ip6h, icmp6_t *, icmp6); 2417c478bd9Sstevel@tonic-gate ip2dbg(("icmp_inbound_v6: type %d code %d\n", icmp6->icmp6_type, 2427c478bd9Sstevel@tonic-gate icmp6->icmp6_code)); 2437c478bd9Sstevel@tonic-gate 2447c478bd9Sstevel@tonic-gate /* 245bd670b35SErik Nordmark * We will set "interested" to "true" if we should pass a copy to 246bd670b35SErik Nordmark * the transport i.e., if it is an error message. 2477c478bd9Sstevel@tonic-gate */ 248bd670b35SErik Nordmark interested = !(icmp6->icmp6_type & ICMP6_INFOMSG_MASK); 2497c478bd9Sstevel@tonic-gate 2507c478bd9Sstevel@tonic-gate switch (icmp6->icmp6_type) { 2517c478bd9Sstevel@tonic-gate case ICMP6_DST_UNREACH: 2527c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInDestUnreachs); 2537c478bd9Sstevel@tonic-gate if (icmp6->icmp6_code == ICMP6_DST_UNREACH_ADMIN) 2547c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInAdminProhibs); 2557c478bd9Sstevel@tonic-gate break; 2567c478bd9Sstevel@tonic-gate 2577c478bd9Sstevel@tonic-gate case ICMP6_TIME_EXCEEDED: 2587c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInTimeExcds); 2597c478bd9Sstevel@tonic-gate break; 2607c478bd9Sstevel@tonic-gate 2617c478bd9Sstevel@tonic-gate case ICMP6_PARAM_PROB: 2627c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInParmProblems); 2637c478bd9Sstevel@tonic-gate break; 2647c478bd9Sstevel@tonic-gate 2657c478bd9Sstevel@tonic-gate case ICMP6_PACKET_TOO_BIG: 266bd670b35SErik Nordmark BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInPktTooBigs); 267bd670b35SErik Nordmark break; 268bd670b35SErik Nordmark 2697c478bd9Sstevel@tonic-gate case ICMP6_ECHO_REQUEST: 2707c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInEchos); 2717c478bd9Sstevel@tonic-gate if (IN6_IS_ADDR_MULTICAST(&ip6h->ip6_dst) && 272f4b3ec61Sdh155122 !ipst->ips_ipv6_resp_echo_mcast) 2737c478bd9Sstevel@tonic-gate break; 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate /* 2767c478bd9Sstevel@tonic-gate * We must have exclusive use of the mblk to convert it to 2777c478bd9Sstevel@tonic-gate * a response. 2787c478bd9Sstevel@tonic-gate * If not, we copy it. 2797c478bd9Sstevel@tonic-gate */ 2807c478bd9Sstevel@tonic-gate if (mp->b_datap->db_ref > 1) { 2817c478bd9Sstevel@tonic-gate mblk_t *mp1; 2827c478bd9Sstevel@tonic-gate 2837c478bd9Sstevel@tonic-gate mp1 = copymsg(mp); 2847c478bd9Sstevel@tonic-gate if (mp1 == NULL) { 285bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 286bd670b35SErik Nordmark ip_drop_input("ipIfStatsInDiscards - copymsg", 287bd670b35SErik Nordmark mp, ill); 288bd670b35SErik Nordmark freemsg(mp); 289bd670b35SErik Nordmark return (NULL); 2907c478bd9Sstevel@tonic-gate } 291bd670b35SErik Nordmark freemsg(mp); 2927c478bd9Sstevel@tonic-gate mp = mp1; 2937c478bd9Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 294bd670b35SErik Nordmark icmp6 = (icmp6_t *)(&mp->b_rptr[ip_hdr_length]); 2957c478bd9Sstevel@tonic-gate } 2967c478bd9Sstevel@tonic-gate 2977c478bd9Sstevel@tonic-gate icmp6->icmp6_type = ICMP6_ECHO_REPLY; 298bd670b35SErik Nordmark icmp_send_reply_v6(mp, ip6h, icmp6, ira); 299bd670b35SErik Nordmark return (NULL); 3007c478bd9Sstevel@tonic-gate 3017c478bd9Sstevel@tonic-gate case ICMP6_ECHO_REPLY: 3027c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInEchoReplies); 3037c478bd9Sstevel@tonic-gate break; 3047c478bd9Sstevel@tonic-gate 3057c478bd9Sstevel@tonic-gate case ND_ROUTER_SOLICIT: 3067c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInRouterSolicits); 3077c478bd9Sstevel@tonic-gate break; 3087c478bd9Sstevel@tonic-gate 3097c478bd9Sstevel@tonic-gate case ND_ROUTER_ADVERT: 3107c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInRouterAdvertisements); 3117c478bd9Sstevel@tonic-gate break; 3127c478bd9Sstevel@tonic-gate 3137c478bd9Sstevel@tonic-gate case ND_NEIGHBOR_SOLICIT: 3147c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInNeighborSolicits); 315bd670b35SErik Nordmark ndp_input(mp, ira); 316bd670b35SErik Nordmark return (NULL); 3177c478bd9Sstevel@tonic-gate 3187c478bd9Sstevel@tonic-gate case ND_NEIGHBOR_ADVERT: 3197c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, 3207c478bd9Sstevel@tonic-gate ipv6IfIcmpInNeighborAdvertisements); 321bd670b35SErik Nordmark ndp_input(mp, ira); 322bd670b35SErik Nordmark return (NULL); 3237c478bd9Sstevel@tonic-gate 324bd670b35SErik Nordmark case ND_REDIRECT: 3257c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInRedirects); 3267c478bd9Sstevel@tonic-gate 327f4b3ec61Sdh155122 if (ipst->ips_ipv6_ignore_redirect) 3287c478bd9Sstevel@tonic-gate break; 3297c478bd9Sstevel@tonic-gate 330bd670b35SErik Nordmark /* We now allow a RAW socket to receive this. */ 331bd670b35SErik Nordmark interested = B_TRUE; 3327c478bd9Sstevel@tonic-gate break; 3337c478bd9Sstevel@tonic-gate 3347c478bd9Sstevel@tonic-gate /* 3357c478bd9Sstevel@tonic-gate * The next three icmp messages will be handled by MLD. 3367c478bd9Sstevel@tonic-gate * Pass all valid MLD packets up to any process(es) 337bd670b35SErik Nordmark * listening on a raw ICMP socket. 3387c478bd9Sstevel@tonic-gate */ 3397c478bd9Sstevel@tonic-gate case MLD_LISTENER_QUERY: 3407c478bd9Sstevel@tonic-gate case MLD_LISTENER_REPORT: 3417c478bd9Sstevel@tonic-gate case MLD_LISTENER_REDUCTION: 342bd670b35SErik Nordmark mp = mld_input(mp, ira); 343bd670b35SErik Nordmark return (mp); 3447c478bd9Sstevel@tonic-gate default: 3457c478bd9Sstevel@tonic-gate break; 3467c478bd9Sstevel@tonic-gate } 347bd670b35SErik Nordmark /* 348bd670b35SErik Nordmark * See if there is an ICMP client to avoid an extra copymsg/freemsg 349bd670b35SErik Nordmark * if there isn't one. 350bd670b35SErik Nordmark */ 351bd670b35SErik Nordmark if (ipst->ips_ipcl_proto_fanout_v6[IPPROTO_ICMPV6].connf_head != NULL) { 352bd670b35SErik Nordmark /* If there is an ICMP client and we want one too, copy it. */ 353bd670b35SErik Nordmark 354bd670b35SErik Nordmark if (!interested) { 355bd670b35SErik Nordmark /* Caller will deliver to RAW sockets */ 356bd670b35SErik Nordmark return (mp); 3577c478bd9Sstevel@tonic-gate } 358bd670b35SErik Nordmark mp_ret = copymsg(mp); 359bd670b35SErik Nordmark if (mp_ret == NULL) { 360bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 361bd670b35SErik Nordmark ip_drop_input("ipIfStatsInDiscards - copymsg", mp, ill); 362bd670b35SErik Nordmark } 363bd670b35SErik Nordmark } else if (!interested) { 364bd670b35SErik Nordmark /* Neither we nor raw sockets are interested. Drop packet now */ 365bd670b35SErik Nordmark freemsg(mp); 366bd670b35SErik Nordmark return (NULL); 3677c478bd9Sstevel@tonic-gate } 3687c478bd9Sstevel@tonic-gate 3697c478bd9Sstevel@tonic-gate /* 370bd670b35SErik Nordmark * ICMP error or redirect packet. Make sure we have enough of 371bd670b35SErik Nordmark * the header and that db_ref == 1 since we might end up modifying 372bd670b35SErik Nordmark * the packet. 3737c478bd9Sstevel@tonic-gate */ 374bd670b35SErik Nordmark if (mp->b_cont != NULL) { 375bd670b35SErik Nordmark if (ip_pullup(mp, -1, ira) == NULL) { 376bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 377bd670b35SErik Nordmark ip_drop_input("ipIfStatsInDiscards - ip_pullup", 378bd670b35SErik Nordmark mp, ill); 379bd670b35SErik Nordmark freemsg(mp); 380bd670b35SErik Nordmark return (mp_ret); 381bd670b35SErik Nordmark } 382bd670b35SErik Nordmark } 3837c478bd9Sstevel@tonic-gate 3847c478bd9Sstevel@tonic-gate if (mp->b_datap->db_ref > 1) { 3857c478bd9Sstevel@tonic-gate mblk_t *mp1; 3867c478bd9Sstevel@tonic-gate 3877c478bd9Sstevel@tonic-gate mp1 = copymsg(mp); 3887c478bd9Sstevel@tonic-gate if (mp1 == NULL) { 3893173664eSapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 390bd670b35SErik Nordmark ip_drop_input("ipIfStatsInDiscards - copymsg", mp, ill); 391bd670b35SErik Nordmark freemsg(mp); 392bd670b35SErik Nordmark return (mp_ret); 3937c478bd9Sstevel@tonic-gate } 394bd670b35SErik Nordmark freemsg(mp); 3957c478bd9Sstevel@tonic-gate mp = mp1; 3967c478bd9Sstevel@tonic-gate } 3977c478bd9Sstevel@tonic-gate 3987c478bd9Sstevel@tonic-gate /* 399bd670b35SErik Nordmark * In case mp has changed, verify the message before any further 400bd670b35SErik Nordmark * processes. 401bd670b35SErik Nordmark */ 402bd670b35SErik Nordmark ip6h = (ip6_t *)mp->b_rptr; 403bd670b35SErik Nordmark icmp6 = (icmp6_t *)(&mp->b_rptr[ip_hdr_length]); 404bd670b35SErik Nordmark if (!icmp_inbound_verify_v6(mp, icmp6, ira)) { 405bd670b35SErik Nordmark freemsg(mp); 406bd670b35SErik Nordmark return (mp_ret); 407bd670b35SErik Nordmark } 408bd670b35SErik Nordmark 409bd670b35SErik Nordmark switch (icmp6->icmp6_type) { 410bd670b35SErik Nordmark case ND_REDIRECT: 411bd670b35SErik Nordmark icmp_redirect_v6(mp, ip6h, (nd_redirect_t *)icmp6, ira); 412bd670b35SErik Nordmark break; 413bd670b35SErik Nordmark case ICMP6_PACKET_TOO_BIG: 414bd670b35SErik Nordmark /* Update DCE and adjust MTU is icmp header if needed */ 415bd670b35SErik Nordmark icmp_inbound_too_big_v6(icmp6, ira); 416bd670b35SErik Nordmark /* FALLTHRU */ 417bd670b35SErik Nordmark default: 418bd670b35SErik Nordmark icmp_inbound_error_fanout_v6(mp, icmp6, ira); 419bd670b35SErik Nordmark break; 420bd670b35SErik Nordmark } 421bd670b35SErik Nordmark 422bd670b35SErik Nordmark return (mp_ret); 423bd670b35SErik Nordmark } 424bd670b35SErik Nordmark 425bd670b35SErik Nordmark /* 426bd670b35SErik Nordmark * Send an ICMP echo reply. 427bd670b35SErik Nordmark * The caller has already updated the payload part of the packet. 428bd670b35SErik Nordmark * We handle the ICMP checksum, IP source address selection and feed 429bd670b35SErik Nordmark * the packet into ip_output_simple. 430bd670b35SErik Nordmark */ 431bd670b35SErik Nordmark static void 432bd670b35SErik Nordmark icmp_send_reply_v6(mblk_t *mp, ip6_t *ip6h, icmp6_t *icmp6, 433bd670b35SErik Nordmark ip_recv_attr_t *ira) 434bd670b35SErik Nordmark { 435bd670b35SErik Nordmark uint_t ip_hdr_length = ira->ira_ip_hdr_length; 436bd670b35SErik Nordmark ill_t *ill = ira->ira_ill; 437bd670b35SErik Nordmark ip_stack_t *ipst = ill->ill_ipst; 438bd670b35SErik Nordmark ip_xmit_attr_t ixas; 439bd670b35SErik Nordmark in6_addr_t origsrc; 440bd670b35SErik Nordmark 441bd670b35SErik Nordmark /* 442bd670b35SErik Nordmark * Remove any extension headers (do not reverse a source route) 443bd670b35SErik Nordmark * and clear the flow id (keep traffic class for now). 444bd670b35SErik Nordmark */ 445bd670b35SErik Nordmark if (ip_hdr_length != IPV6_HDR_LEN) { 446bd670b35SErik Nordmark int i; 447bd670b35SErik Nordmark 448bd670b35SErik Nordmark for (i = 0; i < IPV6_HDR_LEN; i++) { 449bd670b35SErik Nordmark mp->b_rptr[ip_hdr_length - i - 1] = 450bd670b35SErik Nordmark mp->b_rptr[IPV6_HDR_LEN - i - 1]; 451bd670b35SErik Nordmark } 452bd670b35SErik Nordmark mp->b_rptr += (ip_hdr_length - IPV6_HDR_LEN); 453bd670b35SErik Nordmark ip6h = (ip6_t *)mp->b_rptr; 454bd670b35SErik Nordmark ip6h->ip6_nxt = IPPROTO_ICMPV6; 455bd670b35SErik Nordmark i = ntohs(ip6h->ip6_plen); 456bd670b35SErik Nordmark i -= (ip_hdr_length - IPV6_HDR_LEN); 457bd670b35SErik Nordmark ip6h->ip6_plen = htons(i); 458bd670b35SErik Nordmark ip_hdr_length = IPV6_HDR_LEN; 459bd670b35SErik Nordmark ASSERT(ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN == msgdsize(mp)); 460bd670b35SErik Nordmark } 461bd670b35SErik Nordmark ip6h->ip6_vcf &= ~IPV6_FLOWINFO_FLOWLABEL; 462bd670b35SErik Nordmark 463bd670b35SErik Nordmark /* Reverse the source and destination addresses. */ 464bd670b35SErik Nordmark origsrc = ip6h->ip6_src; 465bd670b35SErik Nordmark ip6h->ip6_src = ip6h->ip6_dst; 466bd670b35SErik Nordmark ip6h->ip6_dst = origsrc; 467bd670b35SErik Nordmark 468bd670b35SErik Nordmark /* set the hop limit */ 469bd670b35SErik Nordmark ip6h->ip6_hops = ipst->ips_ipv6_def_hops; 470bd670b35SErik Nordmark 471bd670b35SErik Nordmark /* 472bd670b35SErik Nordmark * Prepare for checksum by putting icmp length in the icmp 473bd670b35SErik Nordmark * checksum field. The checksum is calculated in ip_output 474bd670b35SErik Nordmark */ 475bd670b35SErik Nordmark icmp6->icmp6_cksum = ip6h->ip6_plen; 476bd670b35SErik Nordmark 477bd670b35SErik Nordmark bzero(&ixas, sizeof (ixas)); 478bd670b35SErik Nordmark ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6; 479bd670b35SErik Nordmark ixas.ixa_zoneid = ira->ira_zoneid; 480bd670b35SErik Nordmark ixas.ixa_cred = kcred; 481bd670b35SErik Nordmark ixas.ixa_cpid = NOPID; 482bd670b35SErik Nordmark ixas.ixa_tsl = ira->ira_tsl; /* Behave as a multi-level responder */ 483bd670b35SErik Nordmark ixas.ixa_ifindex = 0; 484bd670b35SErik Nordmark ixas.ixa_ipst = ipst; 485bd670b35SErik Nordmark ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; 486bd670b35SErik Nordmark 487bd670b35SErik Nordmark if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) { 488bd670b35SErik Nordmark /* 489bd670b35SErik Nordmark * This packet should go out the same way as it 490bd670b35SErik Nordmark * came in i.e in clear, independent of the IPsec 491bd670b35SErik Nordmark * policy for transmitting packets. 492bd670b35SErik Nordmark */ 493bd670b35SErik Nordmark ixas.ixa_flags |= IXAF_NO_IPSEC; 494bd670b35SErik Nordmark } else { 495bd670b35SErik Nordmark if (!ipsec_in_to_out(ira, &ixas, mp, NULL, ip6h)) { 496bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 497bd670b35SErik Nordmark /* Note: mp already consumed and ip_drop_packet done */ 498bd670b35SErik Nordmark return; 499bd670b35SErik Nordmark } 500bd670b35SErik Nordmark } 501bd670b35SErik Nordmark 502bd670b35SErik Nordmark /* Was the destination (now source) link-local? Send out same group */ 503bd670b35SErik Nordmark if (IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_src)) { 504bd670b35SErik Nordmark ixas.ixa_flags |= IXAF_SCOPEID_SET; 505bd670b35SErik Nordmark if (IS_UNDER_IPMP(ill)) 506bd670b35SErik Nordmark ixas.ixa_scopeid = ill_get_upper_ifindex(ill); 507bd670b35SErik Nordmark else 508bd670b35SErik Nordmark ixas.ixa_scopeid = ill->ill_phyint->phyint_ifindex; 509bd670b35SErik Nordmark } 510bd670b35SErik Nordmark 511bd670b35SErik Nordmark if (ira->ira_flags & IRAF_MULTIBROADCAST) { 512bd670b35SErik Nordmark /* 513bd670b35SErik Nordmark * Not one or our addresses (IRE_LOCALs), thus we let 514bd670b35SErik Nordmark * ip_output_simple pick the source. 515bd670b35SErik Nordmark */ 516bd670b35SErik Nordmark ip6h->ip6_src = ipv6_all_zeros; 517bd670b35SErik Nordmark ixas.ixa_flags |= IXAF_SET_SOURCE; 518bd670b35SErik Nordmark } 519bd670b35SErik Nordmark 520bd670b35SErik Nordmark /* Should we send using dce_pmtu? */ 521bd670b35SErik Nordmark if (ipst->ips_ipv6_icmp_return_pmtu) 522bd670b35SErik Nordmark ixas.ixa_flags |= IXAF_PMTU_DISCOVERY; 523bd670b35SErik Nordmark 524bd670b35SErik Nordmark (void) ip_output_simple(mp, &ixas); 525bd670b35SErik Nordmark ixa_cleanup(&ixas); 526bd670b35SErik Nordmark 527bd670b35SErik Nordmark } 528bd670b35SErik Nordmark 529bd670b35SErik Nordmark /* 530bd670b35SErik Nordmark * Verify the ICMP messages for either for ICMP error or redirect packet. 531bd670b35SErik Nordmark * The caller should have fully pulled up the message. If it's a redirect 532bd670b35SErik Nordmark * packet, only basic checks on IP header will be done; otherwise, verify 533bd670b35SErik Nordmark * the packet by looking at the included ULP header. 534bd670b35SErik Nordmark * 535bd670b35SErik Nordmark * Called before icmp_inbound_error_fanout_v6 is called. 536bd670b35SErik Nordmark */ 537bd670b35SErik Nordmark static boolean_t 538bd670b35SErik Nordmark icmp_inbound_verify_v6(mblk_t *mp, icmp6_t *icmp6, ip_recv_attr_t *ira) 539bd670b35SErik Nordmark { 540bd670b35SErik Nordmark ill_t *ill = ira->ira_ill; 541bd670b35SErik Nordmark uint16_t hdr_length; 542bd670b35SErik Nordmark uint8_t *nexthdrp; 543bd670b35SErik Nordmark uint8_t nexthdr; 544bd670b35SErik Nordmark ip_stack_t *ipst = ill->ill_ipst; 545bd670b35SErik Nordmark conn_t *connp; 546bd670b35SErik Nordmark ip6_t *ip6h; /* Inner header */ 547bd670b35SErik Nordmark 548bd670b35SErik Nordmark ip6h = (ip6_t *)&icmp6[1]; 549bd670b35SErik Nordmark if ((uchar_t *)ip6h + IPV6_HDR_LEN > mp->b_wptr) 550bd670b35SErik Nordmark goto truncated; 551bd670b35SErik Nordmark 552bd670b35SErik Nordmark if (icmp6->icmp6_type == ND_REDIRECT) { 553bd670b35SErik Nordmark hdr_length = sizeof (nd_redirect_t); 554bd670b35SErik Nordmark } else { 555bd670b35SErik Nordmark if ((IPH_HDR_VERSION(ip6h) != IPV6_VERSION)) 556bd670b35SErik Nordmark goto discard_pkt; 557bd670b35SErik Nordmark hdr_length = IPV6_HDR_LEN; 558bd670b35SErik Nordmark } 559bd670b35SErik Nordmark 560bd670b35SErik Nordmark if ((uchar_t *)ip6h + hdr_length > mp->b_wptr) 561bd670b35SErik Nordmark goto truncated; 562bd670b35SErik Nordmark 563bd670b35SErik Nordmark /* 564bd670b35SErik Nordmark * Stop here for ICMP_REDIRECT. 565bd670b35SErik Nordmark */ 566bd670b35SErik Nordmark if (icmp6->icmp6_type == ND_REDIRECT) 567bd670b35SErik Nordmark return (B_TRUE); 568bd670b35SErik Nordmark 569bd670b35SErik Nordmark /* 570bd670b35SErik Nordmark * ICMP errors only. 571bd670b35SErik Nordmark */ 572bd670b35SErik Nordmark if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp)) 573bd670b35SErik Nordmark goto discard_pkt; 574bd670b35SErik Nordmark nexthdr = *nexthdrp; 575bd670b35SErik Nordmark 576bd670b35SErik Nordmark /* Try to pass the ICMP message to clients who need it */ 577bd670b35SErik Nordmark switch (nexthdr) { 578bd670b35SErik Nordmark case IPPROTO_UDP: 579bd670b35SErik Nordmark /* 580bd670b35SErik Nordmark * Verify we have at least ICMP_MIN_TP_HDR_LEN bytes of 581bd670b35SErik Nordmark * transport header. 582bd670b35SErik Nordmark */ 583bd670b35SErik Nordmark if ((uchar_t *)ip6h + hdr_length + ICMP_MIN_TP_HDR_LEN > 584bd670b35SErik Nordmark mp->b_wptr) 585bd670b35SErik Nordmark goto truncated; 586bd670b35SErik Nordmark break; 587bd670b35SErik Nordmark case IPPROTO_TCP: { 588bd670b35SErik Nordmark tcpha_t *tcpha; 589bd670b35SErik Nordmark 590bd670b35SErik Nordmark /* 591bd670b35SErik Nordmark * Verify we have at least ICMP_MIN_TP_HDR_LEN bytes of 592bd670b35SErik Nordmark * transport header. 593bd670b35SErik Nordmark */ 594bd670b35SErik Nordmark if ((uchar_t *)ip6h + hdr_length + ICMP_MIN_TP_HDR_LEN > 595bd670b35SErik Nordmark mp->b_wptr) 596bd670b35SErik Nordmark goto truncated; 597bd670b35SErik Nordmark 598bd670b35SErik Nordmark tcpha = (tcpha_t *)((uchar_t *)ip6h + hdr_length); 599bd670b35SErik Nordmark /* 600bd670b35SErik Nordmark * With IPMP we need to match across group, which we do 601bd670b35SErik Nordmark * since we have the upper ill from ira_ill. 602bd670b35SErik Nordmark */ 603bd670b35SErik Nordmark connp = ipcl_tcp_lookup_reversed_ipv6(ip6h, tcpha, TCPS_LISTEN, 604bd670b35SErik Nordmark ill->ill_phyint->phyint_ifindex, ipst); 605bd670b35SErik Nordmark if (connp == NULL) 606bd670b35SErik Nordmark goto discard_pkt; 607bd670b35SErik Nordmark 608bd670b35SErik Nordmark if ((connp->conn_verifyicmp != NULL) && 609bd670b35SErik Nordmark !connp->conn_verifyicmp(connp, tcpha, NULL, icmp6, ira)) { 610bd670b35SErik Nordmark CONN_DEC_REF(connp); 611bd670b35SErik Nordmark goto discard_pkt; 612bd670b35SErik Nordmark } 613bd670b35SErik Nordmark CONN_DEC_REF(connp); 614bd670b35SErik Nordmark break; 615bd670b35SErik Nordmark } 616bd670b35SErik Nordmark case IPPROTO_SCTP: 617bd670b35SErik Nordmark /* 618bd670b35SErik Nordmark * Verify we have at least ICMP_MIN_TP_HDR_LEN bytes of 619bd670b35SErik Nordmark * transport header. 620bd670b35SErik Nordmark */ 621bd670b35SErik Nordmark if ((uchar_t *)ip6h + hdr_length + ICMP_MIN_TP_HDR_LEN > 622bd670b35SErik Nordmark mp->b_wptr) 623bd670b35SErik Nordmark goto truncated; 624bd670b35SErik Nordmark break; 625bd670b35SErik Nordmark case IPPROTO_ESP: 626bd670b35SErik Nordmark case IPPROTO_AH: 627bd670b35SErik Nordmark break; 628bd670b35SErik Nordmark case IPPROTO_ENCAP: 629bd670b35SErik Nordmark case IPPROTO_IPV6: { 630bd670b35SErik Nordmark /* Look for self-encapsulated packets that caused an error */ 631bd670b35SErik Nordmark ip6_t *in_ip6h; 632bd670b35SErik Nordmark 633bd670b35SErik Nordmark in_ip6h = (ip6_t *)((uint8_t *)ip6h + hdr_length); 634bd670b35SErik Nordmark if ((uint8_t *)in_ip6h + (nexthdr == IPPROTO_ENCAP ? 635bd670b35SErik Nordmark sizeof (ipha_t) : sizeof (ip6_t)) > mp->b_wptr) 636bd670b35SErik Nordmark goto truncated; 637bd670b35SErik Nordmark break; 638bd670b35SErik Nordmark } 639bd670b35SErik Nordmark default: 640bd670b35SErik Nordmark break; 641bd670b35SErik Nordmark } 642bd670b35SErik Nordmark 643bd670b35SErik Nordmark return (B_TRUE); 644bd670b35SErik Nordmark 645bd670b35SErik Nordmark discard_pkt: 646bd670b35SErik Nordmark /* Bogus ICMP error. */ 647bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 648bd670b35SErik Nordmark return (B_FALSE); 649bd670b35SErik Nordmark 650bd670b35SErik Nordmark truncated: 651bd670b35SErik Nordmark /* We pulled up everthing already. Must be truncated */ 652bd670b35SErik Nordmark BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors); 653bd670b35SErik Nordmark return (B_FALSE); 654bd670b35SErik Nordmark } 655bd670b35SErik Nordmark 656bd670b35SErik Nordmark /* 657bd670b35SErik Nordmark * Process received IPv6 ICMP Packet too big. 658bd670b35SErik Nordmark * The caller is responsible for validating the packet before passing it in 659bd670b35SErik Nordmark * and also to fanout the ICMP error to any matching transport conns. Assumes 660bd670b35SErik Nordmark * the message has been fully pulled up. 661bd670b35SErik Nordmark * 662bd670b35SErik Nordmark * Before getting here, the caller has called icmp_inbound_verify_v6() 663bd670b35SErik Nordmark * that should have verified with ULP to prevent undoing the changes we're 664bd670b35SErik Nordmark * going to make to DCE. For example, TCP might have verified that the packet 665bd670b35SErik Nordmark * which generated error is in the send window. 666bd670b35SErik Nordmark * 667bd670b35SErik Nordmark * In some cases modified this MTU in the ICMP header packet; the caller 668bd670b35SErik Nordmark * should pass to the matching ULP after this returns. 669bd670b35SErik Nordmark */ 670bd670b35SErik Nordmark static void 671bd670b35SErik Nordmark icmp_inbound_too_big_v6(icmp6_t *icmp6, ip_recv_attr_t *ira) 672bd670b35SErik Nordmark { 673bd670b35SErik Nordmark uint32_t mtu; 674bd670b35SErik Nordmark dce_t *dce; 675bd670b35SErik Nordmark ill_t *ill = ira->ira_ill; /* Upper ill if IPMP */ 676bd670b35SErik Nordmark ip_stack_t *ipst = ill->ill_ipst; 677bd670b35SErik Nordmark int old_max_frag; 678bd670b35SErik Nordmark in6_addr_t final_dst; 679bd670b35SErik Nordmark ip6_t *ip6h; /* Inner IP header */ 680bd670b35SErik Nordmark 681bd670b35SErik Nordmark /* Caller has already pulled up everything. */ 682bd670b35SErik Nordmark ip6h = (ip6_t *)&icmp6[1]; 683bd670b35SErik Nordmark final_dst = ip_get_dst_v6(ip6h, NULL, NULL); 684bd670b35SErik Nordmark 685bd670b35SErik Nordmark /* 686bd670b35SErik Nordmark * For link local destinations matching simply on address is not 6877c478bd9Sstevel@tonic-gate * sufficient. Same link local addresses for different ILL's is 6887c478bd9Sstevel@tonic-gate * possible. 6897c478bd9Sstevel@tonic-gate */ 690bd670b35SErik Nordmark if (IN6_IS_ADDR_LINKSCOPE(&final_dst)) { 691bd670b35SErik Nordmark dce = dce_lookup_and_add_v6(&final_dst, 692bd670b35SErik Nordmark ill->ill_phyint->phyint_ifindex, ipst); 693bd670b35SErik Nordmark } else { 694bd670b35SErik Nordmark dce = dce_lookup_and_add_v6(&final_dst, 0, ipst); 695bd670b35SErik Nordmark } 696bd670b35SErik Nordmark if (dce == NULL) { 697bd670b35SErik Nordmark /* Couldn't add a unique one - ENOMEM */ 6987c478bd9Sstevel@tonic-gate if (ip_debug > 2) { 6997c478bd9Sstevel@tonic-gate /* ip1dbg */ 7007c478bd9Sstevel@tonic-gate pr_addr_dbg("icmp_inbound_too_big_v6:" 701bd670b35SErik Nordmark "no dce for dst %s\n", AF_INET6, 702bd670b35SErik Nordmark &final_dst); 7037c478bd9Sstevel@tonic-gate } 7047c478bd9Sstevel@tonic-gate return; 7057c478bd9Sstevel@tonic-gate } 7067c478bd9Sstevel@tonic-gate 7077c478bd9Sstevel@tonic-gate mtu = ntohl(icmp6->icmp6_mtu); 708bd670b35SErik Nordmark 709bd670b35SErik Nordmark mutex_enter(&dce->dce_lock); 710bd670b35SErik Nordmark if (dce->dce_flags & DCEF_PMTU) 711bd670b35SErik Nordmark old_max_frag = dce->dce_pmtu; 712*1eee170aSErik Nordmark else if (IN6_IS_ADDR_MULTICAST(&final_dst)) 713*1eee170aSErik Nordmark old_max_frag = ill->ill_mc_mtu; 714bd670b35SErik Nordmark else 715bd670b35SErik Nordmark old_max_frag = ill->ill_mtu; 716bd670b35SErik Nordmark 7177c478bd9Sstevel@tonic-gate if (mtu < IPV6_MIN_MTU) { 7187c478bd9Sstevel@tonic-gate ip1dbg(("Received mtu less than IPv6 " 7197c478bd9Sstevel@tonic-gate "min mtu %d: %d\n", IPV6_MIN_MTU, mtu)); 7207c478bd9Sstevel@tonic-gate mtu = IPV6_MIN_MTU; 7217c478bd9Sstevel@tonic-gate /* 7227c478bd9Sstevel@tonic-gate * If an mtu less than IPv6 min mtu is received, 7237c478bd9Sstevel@tonic-gate * we must include a fragment header in 7247c478bd9Sstevel@tonic-gate * subsequent packets. 7257c478bd9Sstevel@tonic-gate */ 726bd670b35SErik Nordmark dce->dce_flags |= DCEF_TOO_SMALL_PMTU; 727bd670b35SErik Nordmark } else { 728bd670b35SErik Nordmark dce->dce_flags &= ~DCEF_TOO_SMALL_PMTU; 7297c478bd9Sstevel@tonic-gate } 7307c478bd9Sstevel@tonic-gate ip1dbg(("Received mtu from router: %d\n", mtu)); 731bd670b35SErik Nordmark dce->dce_pmtu = MIN(old_max_frag, mtu); 732bd670b35SErik Nordmark 733bd670b35SErik Nordmark /* Prepare to send the new max frag size for the ULP. */ 734bd670b35SErik Nordmark if (dce->dce_flags & DCEF_TOO_SMALL_PMTU) { 7357c478bd9Sstevel@tonic-gate /* 7367c478bd9Sstevel@tonic-gate * If we need a fragment header in every packet 7377c478bd9Sstevel@tonic-gate * (above case or multirouting), make sure the 7387c478bd9Sstevel@tonic-gate * ULP takes it into account when computing the 7397c478bd9Sstevel@tonic-gate * payload size. 7407c478bd9Sstevel@tonic-gate */ 741bd670b35SErik Nordmark icmp6->icmp6_mtu = htonl(dce->dce_pmtu - sizeof (ip6_frag_t)); 7427c478bd9Sstevel@tonic-gate } else { 743bd670b35SErik Nordmark icmp6->icmp6_mtu = htonl(dce->dce_pmtu); 7447c478bd9Sstevel@tonic-gate } 745bd670b35SErik Nordmark /* We now have a PMTU for sure */ 746bd670b35SErik Nordmark dce->dce_flags |= DCEF_PMTU; 747d3d50737SRafael Vanoni dce->dce_last_change_time = TICK_TO_SEC(ddi_get_lbolt64()); 748bd670b35SErik Nordmark mutex_exit(&dce->dce_lock); 7497c478bd9Sstevel@tonic-gate /* 750bd670b35SErik Nordmark * After dropping the lock the new value is visible to everyone. 751bd670b35SErik Nordmark * Then we bump the generation number so any cached values reinspect 752bd670b35SErik Nordmark * the dce_t. 7537c478bd9Sstevel@tonic-gate */ 754bd670b35SErik Nordmark dce_increment_generation(dce); 755bd670b35SErik Nordmark dce_refrele(dce); 7562b24ab6bSSebastien Roy } 7572b24ab6bSSebastien Roy 7582b24ab6bSSebastien Roy /* 7597c478bd9Sstevel@tonic-gate * Fanout received ICMPv6 error packets to the transports. 7607c478bd9Sstevel@tonic-gate * Assumes the IPv6 plus ICMPv6 headers have been pulled up but nothing else. 761bd670b35SErik Nordmark * 762bd670b35SErik Nordmark * The caller must have called icmp_inbound_verify_v6. 7637c478bd9Sstevel@tonic-gate */ 7647c478bd9Sstevel@tonic-gate void 765bd670b35SErik Nordmark icmp_inbound_error_fanout_v6(mblk_t *mp, icmp6_t *icmp6, ip_recv_attr_t *ira) 7667c478bd9Sstevel@tonic-gate { 7677c478bd9Sstevel@tonic-gate uint16_t *up; /* Pointer to ports in ULP header */ 7687c478bd9Sstevel@tonic-gate uint32_t ports; /* reversed ports for fanout */ 7697c478bd9Sstevel@tonic-gate ip6_t rip6h; /* With reversed addresses */ 770bd670b35SErik Nordmark ip6_t *ip6h; /* Inner IP header */ 771bd670b35SErik Nordmark uint16_t hdr_length; /* Inner IP header length */ 7727c478bd9Sstevel@tonic-gate uint8_t *nexthdrp; 7737c478bd9Sstevel@tonic-gate uint8_t nexthdr; 7747c478bd9Sstevel@tonic-gate tcpha_t *tcpha; 7757c478bd9Sstevel@tonic-gate conn_t *connp; 776bd670b35SErik Nordmark ill_t *ill = ira->ira_ill; /* Upper in the case of IPMP */ 777f4b3ec61Sdh155122 ip_stack_t *ipst = ill->ill_ipst; 778bd670b35SErik Nordmark ipsec_stack_t *ipss = ipst->ips_netstack->netstack_ipsec; 7797c478bd9Sstevel@tonic-gate 780bd670b35SErik Nordmark /* Caller has already pulled up everything. */ 781bd670b35SErik Nordmark ip6h = (ip6_t *)&icmp6[1]; 782bd670b35SErik Nordmark ASSERT(mp->b_cont == NULL); 783bd670b35SErik Nordmark ASSERT((uchar_t *)&ip6h[1] <= mp->b_wptr); 7847c478bd9Sstevel@tonic-gate 7857c478bd9Sstevel@tonic-gate if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp)) 7867c478bd9Sstevel@tonic-gate goto drop_pkt; 7877c478bd9Sstevel@tonic-gate nexthdr = *nexthdrp; 788bd670b35SErik Nordmark ira->ira_protocol = nexthdr; 7897c478bd9Sstevel@tonic-gate 7902b24ab6bSSebastien Roy /* 7912b24ab6bSSebastien Roy * We need a separate IP header with the source and destination 7922b24ab6bSSebastien Roy * addresses reversed to do fanout/classification because the ip6h in 7932b24ab6bSSebastien Roy * the ICMPv6 error is in the form we sent it out. 7942b24ab6bSSebastien Roy */ 7952b24ab6bSSebastien Roy rip6h.ip6_src = ip6h->ip6_dst; 7962b24ab6bSSebastien Roy rip6h.ip6_dst = ip6h->ip6_src; 7972b24ab6bSSebastien Roy rip6h.ip6_nxt = nexthdr; 7982b24ab6bSSebastien Roy 7997c478bd9Sstevel@tonic-gate /* Try to pass the ICMP message to clients who need it */ 8007c478bd9Sstevel@tonic-gate switch (nexthdr) { 8017c478bd9Sstevel@tonic-gate case IPPROTO_UDP: { 8022b24ab6bSSebastien Roy /* Attempt to find a client stream based on port. */ 8037c478bd9Sstevel@tonic-gate up = (uint16_t *)((uchar_t *)ip6h + hdr_length); 8047c478bd9Sstevel@tonic-gate 805bd670b35SErik Nordmark /* Note that we send error to all matches. */ 806bd670b35SErik Nordmark ira->ira_flags |= IRAF_ICMP_ERROR; 807bd670b35SErik Nordmark ip_fanout_udp_multi_v6(mp, &rip6h, up[0], up[1], ira); 808bd670b35SErik Nordmark ira->ira_flags &= ~IRAF_ICMP_ERROR; 8097c478bd9Sstevel@tonic-gate return; 8107c478bd9Sstevel@tonic-gate } 8117c478bd9Sstevel@tonic-gate case IPPROTO_TCP: { 8127c478bd9Sstevel@tonic-gate /* 8137c478bd9Sstevel@tonic-gate * Attempt to find a client stream based on port. 8147c478bd9Sstevel@tonic-gate * Note that we do a reverse lookup since the header is 8157c478bd9Sstevel@tonic-gate * in the form we sent it out. 8167c478bd9Sstevel@tonic-gate */ 817bd670b35SErik Nordmark tcpha = (tcpha_t *)((uchar_t *)ip6h + hdr_length); 818bd670b35SErik Nordmark /* 819bd670b35SErik Nordmark * With IPMP we need to match across group, which we do 820bd670b35SErik Nordmark * since we have the upper ill from ira_ill. 821bd670b35SErik Nordmark */ 8227c478bd9Sstevel@tonic-gate connp = ipcl_tcp_lookup_reversed_ipv6(ip6h, tcpha, 823f4b3ec61Sdh155122 TCPS_LISTEN, ill->ill_phyint->phyint_ifindex, ipst); 8247c478bd9Sstevel@tonic-gate if (connp == NULL) { 8257c478bd9Sstevel@tonic-gate goto drop_pkt; 8267c478bd9Sstevel@tonic-gate } 8277c478bd9Sstevel@tonic-gate 828bd670b35SErik Nordmark if (CONN_INBOUND_POLICY_PRESENT_V6(connp, ipss) || 829bd670b35SErik Nordmark (ira->ira_flags & IRAF_IPSEC_SECURE)) { 830bd670b35SErik Nordmark mp = ipsec_check_inbound_policy(mp, connp, 831bd670b35SErik Nordmark NULL, ip6h, ira); 832bd670b35SErik Nordmark if (mp == NULL) { 833bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 834bd670b35SErik Nordmark /* Note that mp is NULL */ 835bd670b35SErik Nordmark ip_drop_input("ipIfStatsInDiscards", mp, ill); 836bd670b35SErik Nordmark CONN_DEC_REF(connp); 837bd670b35SErik Nordmark return; 838bd670b35SErik Nordmark } 839bd670b35SErik Nordmark } 840bd670b35SErik Nordmark 841bd670b35SErik Nordmark ira->ira_flags |= IRAF_ICMP_ERROR; 842bd670b35SErik Nordmark if (IPCL_IS_TCP(connp)) { 843bd670b35SErik Nordmark SQUEUE_ENTER_ONE(connp->conn_sqp, mp, 844bd670b35SErik Nordmark connp->conn_recvicmp, connp, ira, SQ_FILL, 845bd670b35SErik Nordmark SQTAG_TCP6_INPUT_ICMP_ERR); 846bd670b35SErik Nordmark } else { 847bd670b35SErik Nordmark /* Not TCP; must be SOCK_RAW, IPPROTO_TCP */ 848bd670b35SErik Nordmark ill_t *rill = ira->ira_rill; 849bd670b35SErik Nordmark 850bd670b35SErik Nordmark ira->ira_ill = ira->ira_rill = NULL; 851bd670b35SErik Nordmark (connp->conn_recv)(connp, mp, NULL, ira); 852bd670b35SErik Nordmark CONN_DEC_REF(connp); 853bd670b35SErik Nordmark ira->ira_ill = ill; 854bd670b35SErik Nordmark ira->ira_rill = rill; 855bd670b35SErik Nordmark } 856bd670b35SErik Nordmark ira->ira_flags &= ~IRAF_ICMP_ERROR; 8577c478bd9Sstevel@tonic-gate return; 8587c478bd9Sstevel@tonic-gate 8597c478bd9Sstevel@tonic-gate } 8607c478bd9Sstevel@tonic-gate case IPPROTO_SCTP: 8617c478bd9Sstevel@tonic-gate up = (uint16_t *)((uchar_t *)ip6h + hdr_length); 862bd670b35SErik Nordmark /* Find a SCTP client stream for this packet. */ 8637c478bd9Sstevel@tonic-gate ((uint16_t *)&ports)[0] = up[1]; 8647c478bd9Sstevel@tonic-gate ((uint16_t *)&ports)[1] = up[0]; 865bd670b35SErik Nordmark 866bd670b35SErik Nordmark ira->ira_flags |= IRAF_ICMP_ERROR; 867bd670b35SErik Nordmark ip_fanout_sctp(mp, NULL, &rip6h, ports, ira); 868bd670b35SErik Nordmark ira->ira_flags &= ~IRAF_ICMP_ERROR; 8697c478bd9Sstevel@tonic-gate return; 870bd670b35SErik Nordmark 8717c478bd9Sstevel@tonic-gate case IPPROTO_ESP: 872bd670b35SErik Nordmark case IPPROTO_AH: 873f4b3ec61Sdh155122 if (!ipsec_loaded(ipss)) { 874bd670b35SErik Nordmark ip_proto_not_sup(mp, ira); 8757c478bd9Sstevel@tonic-gate return; 8767c478bd9Sstevel@tonic-gate } 8777c478bd9Sstevel@tonic-gate 8787c478bd9Sstevel@tonic-gate if (nexthdr == IPPROTO_ESP) 879bd670b35SErik Nordmark mp = ipsecesp_icmp_error(mp, ira); 8807c478bd9Sstevel@tonic-gate else 881bd670b35SErik Nordmark mp = ipsecah_icmp_error(mp, ira); 882bd670b35SErik Nordmark if (mp == NULL) 8837c478bd9Sstevel@tonic-gate return; 8847c478bd9Sstevel@tonic-gate 885bd670b35SErik Nordmark /* Just in case ipsec didn't preserve the NULL b_cont */ 886bd670b35SErik Nordmark if (mp->b_cont != NULL) { 887bd670b35SErik Nordmark if (!pullupmsg(mp, -1)) 8887c478bd9Sstevel@tonic-gate goto drop_pkt; 88948de1bd2Skcpoon } 8907c478bd9Sstevel@tonic-gate 8917c478bd9Sstevel@tonic-gate /* 892bd670b35SErik Nordmark * If succesful, the mp has been modified to not include 893bd670b35SErik Nordmark * the ESP/AH header so we can fanout to the ULP's icmp 894bd670b35SErik Nordmark * error handler. 8957c478bd9Sstevel@tonic-gate */ 896bd670b35SErik Nordmark if (mp->b_wptr - mp->b_rptr < IPV6_HDR_LEN) 897bd670b35SErik Nordmark goto drop_pkt; 898bd670b35SErik Nordmark 899bd670b35SErik Nordmark ip6h = (ip6_t *)mp->b_rptr; 900bd670b35SErik Nordmark /* Don't call hdr_length_v6() unless you have to. */ 901bd670b35SErik Nordmark if (ip6h->ip6_nxt != IPPROTO_ICMPV6) 902bd670b35SErik Nordmark hdr_length = ip_hdr_length_v6(mp, ip6h); 903bd670b35SErik Nordmark else 904bd670b35SErik Nordmark hdr_length = IPV6_HDR_LEN; 905bd670b35SErik Nordmark 906bd670b35SErik Nordmark /* Verify the modified message before any further processes. */ 907bd670b35SErik Nordmark icmp6 = (icmp6_t *)(&mp->b_rptr[hdr_length]); 908bd670b35SErik Nordmark if (!icmp_inbound_verify_v6(mp, icmp6, ira)) { 909bd670b35SErik Nordmark freemsg(mp); 910bd670b35SErik Nordmark return; 9112b24ab6bSSebastien Roy } 912bd670b35SErik Nordmark 913bd670b35SErik Nordmark icmp_inbound_error_fanout_v6(mp, icmp6, ira); 914bd670b35SErik Nordmark return; 915bd670b35SErik Nordmark 916bd670b35SErik Nordmark case IPPROTO_IPV6: { 917bd670b35SErik Nordmark /* Look for self-encapsulated packets that caused an error */ 918bd670b35SErik Nordmark ip6_t *in_ip6h; 919bd670b35SErik Nordmark 920bd670b35SErik Nordmark in_ip6h = (ip6_t *)((uint8_t *)ip6h + hdr_length); 921bd670b35SErik Nordmark 922bd670b35SErik Nordmark if (IN6_ARE_ADDR_EQUAL(&in_ip6h->ip6_src, &ip6h->ip6_src) && 923bd670b35SErik Nordmark IN6_ARE_ADDR_EQUAL(&in_ip6h->ip6_dst, &ip6h->ip6_dst)) { 9247c478bd9Sstevel@tonic-gate /* 9257c478bd9Sstevel@tonic-gate * Self-encapsulated case. As in the ipv4 case, 9267c478bd9Sstevel@tonic-gate * we need to strip the 2nd IP header. Since mp 9277c478bd9Sstevel@tonic-gate * is already pulled-up, we can simply bcopy 9287c478bd9Sstevel@tonic-gate * the 3rd header + data over the 2nd header. 9297c478bd9Sstevel@tonic-gate */ 9307c478bd9Sstevel@tonic-gate uint16_t unused_len; 9317c478bd9Sstevel@tonic-gate 9327c478bd9Sstevel@tonic-gate /* 9337c478bd9Sstevel@tonic-gate * Make sure we don't do recursion more than once. 9347c478bd9Sstevel@tonic-gate */ 935bd670b35SErik Nordmark if (!ip_hdr_length_nexthdr_v6(mp, in_ip6h, 9367c478bd9Sstevel@tonic-gate &unused_len, &nexthdrp) || 9377c478bd9Sstevel@tonic-gate *nexthdrp == IPPROTO_IPV6) { 9387c478bd9Sstevel@tonic-gate goto drop_pkt; 9397c478bd9Sstevel@tonic-gate } 9407c478bd9Sstevel@tonic-gate 9417c478bd9Sstevel@tonic-gate /* 9427c478bd9Sstevel@tonic-gate * Copy the 3rd header + remaining data on top 9437c478bd9Sstevel@tonic-gate * of the 2nd header. 9447c478bd9Sstevel@tonic-gate */ 945bd670b35SErik Nordmark bcopy(in_ip6h, ip6h, mp->b_wptr - (uchar_t *)in_ip6h); 9467c478bd9Sstevel@tonic-gate 9477c478bd9Sstevel@tonic-gate /* 9487c478bd9Sstevel@tonic-gate * Subtract length of the 2nd header. 9497c478bd9Sstevel@tonic-gate */ 9507c478bd9Sstevel@tonic-gate mp->b_wptr -= hdr_length; 9517c478bd9Sstevel@tonic-gate 952bd670b35SErik Nordmark ip6h = (ip6_t *)mp->b_rptr; 953bd670b35SErik Nordmark /* Don't call hdr_length_v6() unless you have to. */ 954bd670b35SErik Nordmark if (ip6h->ip6_nxt != IPPROTO_ICMPV6) 955bd670b35SErik Nordmark hdr_length = ip_hdr_length_v6(mp, ip6h); 956bd670b35SErik Nordmark else 957bd670b35SErik Nordmark hdr_length = IPV6_HDR_LEN; 958bd670b35SErik Nordmark 959bd670b35SErik Nordmark /* 960bd670b35SErik Nordmark * Verify the modified message before any further 961bd670b35SErik Nordmark * processes. 962bd670b35SErik Nordmark */ 963bd670b35SErik Nordmark icmp6 = (icmp6_t *)(&mp->b_rptr[hdr_length]); 964bd670b35SErik Nordmark if (!icmp_inbound_verify_v6(mp, icmp6, ira)) { 965bd670b35SErik Nordmark freemsg(mp); 966bd670b35SErik Nordmark return; 967bd670b35SErik Nordmark } 968bd670b35SErik Nordmark 9697c478bd9Sstevel@tonic-gate /* 9707c478bd9Sstevel@tonic-gate * Now recurse, and see what I _really_ should be 9717c478bd9Sstevel@tonic-gate * doing here. 9727c478bd9Sstevel@tonic-gate */ 973bd670b35SErik Nordmark icmp_inbound_error_fanout_v6(mp, icmp6, ira); 9747c478bd9Sstevel@tonic-gate return; 9757c478bd9Sstevel@tonic-gate } 976bd670b35SErik Nordmark /* FALLTHRU */ 977bd670b35SErik Nordmark } 978bd670b35SErik Nordmark case IPPROTO_ENCAP: 979bd670b35SErik Nordmark if ((connp = ipcl_iptun_classify_v6(&rip6h.ip6_src, 980bd670b35SErik Nordmark &rip6h.ip6_dst, ipst)) != NULL) { 981bd670b35SErik Nordmark ira->ira_flags |= IRAF_ICMP_ERROR; 982bd670b35SErik Nordmark connp->conn_recvicmp(connp, mp, NULL, ira); 983bd670b35SErik Nordmark CONN_DEC_REF(connp); 984bd670b35SErik Nordmark ira->ira_flags &= ~IRAF_ICMP_ERROR; 9852b24ab6bSSebastien Roy return; 986bd670b35SErik Nordmark } 9872b24ab6bSSebastien Roy /* 988bd670b35SErik Nordmark * No IP tunnel is interested, fallthrough and see 989bd670b35SErik Nordmark * if a raw socket will want it. 9902b24ab6bSSebastien Roy */ 9917c478bd9Sstevel@tonic-gate /* FALLTHRU */ 9927c478bd9Sstevel@tonic-gate default: 993bd670b35SErik Nordmark ira->ira_flags |= IRAF_ICMP_ERROR; 994bd670b35SErik Nordmark ASSERT(ira->ira_protocol == nexthdr); 995bd670b35SErik Nordmark ip_fanout_proto_v6(mp, &rip6h, ira); 996bd670b35SErik Nordmark ira->ira_flags &= ~IRAF_ICMP_ERROR; 9977c478bd9Sstevel@tonic-gate return; 9987c478bd9Sstevel@tonic-gate } 9997c478bd9Sstevel@tonic-gate /* NOTREACHED */ 10007c478bd9Sstevel@tonic-gate drop_pkt: 10017c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors); 10027c478bd9Sstevel@tonic-gate ip1dbg(("icmp_inbound_error_fanout_v6: drop pkt\n")); 1003bd670b35SErik Nordmark freemsg(mp); 10047c478bd9Sstevel@tonic-gate } 10057c478bd9Sstevel@tonic-gate 10067c478bd9Sstevel@tonic-gate /* 10077c478bd9Sstevel@tonic-gate * Process received IPv6 ICMP Redirect messages. 1008bd670b35SErik Nordmark * Assumes the caller has verified that the headers are in the pulled up mblk. 1009bd670b35SErik Nordmark * Consumes mp. 10107c478bd9Sstevel@tonic-gate */ 10117c478bd9Sstevel@tonic-gate /* ARGSUSED */ 10127c478bd9Sstevel@tonic-gate static void 1013bd670b35SErik Nordmark icmp_redirect_v6(mblk_t *mp, ip6_t *ip6h, nd_redirect_t *rd, 1014bd670b35SErik Nordmark ip_recv_attr_t *ira) 10157c478bd9Sstevel@tonic-gate { 1016bd670b35SErik Nordmark ire_t *ire, *nire; 1017bd670b35SErik Nordmark ire_t *prev_ire = NULL; 10187c478bd9Sstevel@tonic-gate ire_t *redir_ire; 10197c478bd9Sstevel@tonic-gate in6_addr_t *src, *dst, *gateway; 10207c478bd9Sstevel@tonic-gate nd_opt_hdr_t *opt; 10217c478bd9Sstevel@tonic-gate nce_t *nce; 1022bd670b35SErik Nordmark int ncec_flags = 0; 10237c478bd9Sstevel@tonic-gate int err = 0; 10247c478bd9Sstevel@tonic-gate boolean_t redirect_to_router = B_FALSE; 10257c478bd9Sstevel@tonic-gate int len; 10266bdb8e66Sdd193516 int optlen; 1027bd670b35SErik Nordmark ill_t *ill = ira->ira_rill; 1028bd670b35SErik Nordmark ill_t *rill = ira->ira_rill; 1029f4b3ec61Sdh155122 ip_stack_t *ipst = ill->ill_ipst; 10307c478bd9Sstevel@tonic-gate 1031bd670b35SErik Nordmark /* 1032bd670b35SErik Nordmark * Since ira_ill is where the IRE_LOCAL was hosted we use ira_rill 1033bd670b35SErik Nordmark * and make it be the IPMP upper so avoid being confused by a packet 1034bd670b35SErik Nordmark * addressed to a unicast address on a different ill. 1035bd670b35SErik Nordmark */ 1036bd670b35SErik Nordmark if (IS_UNDER_IPMP(rill)) { 1037bd670b35SErik Nordmark rill = ipmp_ill_hold_ipmp_ill(rill); 1038bd670b35SErik Nordmark if (rill == NULL) { 1039bd670b35SErik Nordmark BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects); 1040bd670b35SErik Nordmark ip_drop_input("ipv6IfIcmpInBadRedirects - IPMP ill", 1041bd670b35SErik Nordmark mp, ill); 1042bd670b35SErik Nordmark freemsg(mp); 1043bd670b35SErik Nordmark return; 1044bd670b35SErik Nordmark } 1045bd670b35SErik Nordmark ASSERT(rill != ira->ira_rill); 1046bd670b35SErik Nordmark } 10477c478bd9Sstevel@tonic-gate 1048bd670b35SErik Nordmark len = mp->b_wptr - (uchar_t *)rd; 10497c478bd9Sstevel@tonic-gate src = &ip6h->ip6_src; 10507c478bd9Sstevel@tonic-gate dst = &rd->nd_rd_dst; 10517c478bd9Sstevel@tonic-gate gateway = &rd->nd_rd_target; 10526bdb8e66Sdd193516 10536bdb8e66Sdd193516 /* Verify if it is a valid redirect */ 10546bdb8e66Sdd193516 if (!IN6_IS_ADDR_LINKLOCAL(src) || 10556bdb8e66Sdd193516 (ip6h->ip6_hops != IPV6_MAX_HOPS) || 10566bdb8e66Sdd193516 (rd->nd_rd_code != 0) || 10576bdb8e66Sdd193516 (len < sizeof (nd_redirect_t)) || 10586bdb8e66Sdd193516 (IN6_IS_ADDR_V4MAPPED(dst)) || 10596bdb8e66Sdd193516 (IN6_IS_ADDR_MULTICAST(dst))) { 10606bdb8e66Sdd193516 BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects); 1061bd670b35SErik Nordmark ip_drop_input("ipv6IfIcmpInBadRedirects - addr/len", mp, ill); 1062bd670b35SErik Nordmark goto fail_redirect; 10636bdb8e66Sdd193516 } 10646bdb8e66Sdd193516 10656bdb8e66Sdd193516 if (!(IN6_IS_ADDR_LINKLOCAL(gateway) || 10666bdb8e66Sdd193516 IN6_ARE_ADDR_EQUAL(gateway, dst))) { 10676bdb8e66Sdd193516 BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects); 1068bd670b35SErik Nordmark ip_drop_input("ipv6IfIcmpInBadRedirects - bad gateway", 1069bd670b35SErik Nordmark mp, ill); 1070bd670b35SErik Nordmark goto fail_redirect; 10716bdb8e66Sdd193516 } 10726bdb8e66Sdd193516 1073bd670b35SErik Nordmark optlen = len - sizeof (nd_redirect_t); 1074bd670b35SErik Nordmark if (optlen != 0) { 1075bd670b35SErik Nordmark if (!ndp_verify_optlen((nd_opt_hdr_t *)&rd[1], optlen)) { 10766bdb8e66Sdd193516 BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects); 1077bd670b35SErik Nordmark ip_drop_input("ipv6IfIcmpInBadRedirects - options", 1078bd670b35SErik Nordmark mp, ill); 1079bd670b35SErik Nordmark goto fail_redirect; 10806bdb8e66Sdd193516 } 10816bdb8e66Sdd193516 } 10826bdb8e66Sdd193516 10837c478bd9Sstevel@tonic-gate if (!IN6_ARE_ADDR_EQUAL(gateway, dst)) { 10847c478bd9Sstevel@tonic-gate redirect_to_router = B_TRUE; 1085bd670b35SErik Nordmark ncec_flags |= NCE_F_ISROUTER; 1086bd670b35SErik Nordmark } else { 1087bd670b35SErik Nordmark gateway = dst; /* Add nce for dst */ 10887c478bd9Sstevel@tonic-gate } 10896bdb8e66Sdd193516 10906bdb8e66Sdd193516 10916bdb8e66Sdd193516 /* 10926bdb8e66Sdd193516 * Verify that the IP source address of the redirect is 10936bdb8e66Sdd193516 * the same as the current first-hop router for the specified 10946bdb8e66Sdd193516 * ICMP destination address. 10956bdb8e66Sdd193516 * Also, Make sure we had a route for the dest in question and 10966bdb8e66Sdd193516 * that route was pointing to the old gateway (the source of the 10976bdb8e66Sdd193516 * redirect packet.) 10986b7506c7SErik Nordmark * We do longest match and then compare ire_gateway_addr_v6 below. 10996bdb8e66Sdd193516 */ 11006b7506c7SErik Nordmark prev_ire = ire_ftable_lookup_v6(dst, 0, 0, 0, rill, 11016b7506c7SErik Nordmark ALL_ZONES, NULL, MATCH_IRE_ILL, 0, ipst, NULL); 11026bdb8e66Sdd193516 11037c478bd9Sstevel@tonic-gate /* 11047c478bd9Sstevel@tonic-gate * Check that 11057c478bd9Sstevel@tonic-gate * the redirect was not from ourselves 11067c478bd9Sstevel@tonic-gate * old gateway is still directly reachable 11077c478bd9Sstevel@tonic-gate */ 11087c478bd9Sstevel@tonic-gate if (prev_ire == NULL || 1109bd670b35SErik Nordmark (prev_ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK)) || 11106b7506c7SErik Nordmark (prev_ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) || 11116b7506c7SErik Nordmark !IN6_ARE_ADDR_EQUAL(src, &prev_ire->ire_gateway_addr_v6)) { 11127c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects); 1113bd670b35SErik Nordmark ip_drop_input("ipv6IfIcmpInBadRedirects - ire", mp, ill); 11147c478bd9Sstevel@tonic-gate goto fail_redirect; 11157c478bd9Sstevel@tonic-gate } 11167c478bd9Sstevel@tonic-gate 1117bd670b35SErik Nordmark ASSERT(prev_ire->ire_ill != NULL); 1118bd670b35SErik Nordmark if (prev_ire->ire_ill->ill_flags & ILLF_NONUD) 1119bd670b35SErik Nordmark ncec_flags |= NCE_F_NONUD; 11207c478bd9Sstevel@tonic-gate 11217c478bd9Sstevel@tonic-gate opt = (nd_opt_hdr_t *)&rd[1]; 11226bdb8e66Sdd193516 opt = ndp_get_option(opt, optlen, ND_OPT_TARGET_LINKADDR); 11237c478bd9Sstevel@tonic-gate if (opt != NULL) { 1124bd670b35SErik Nordmark err = nce_lookup_then_add_v6(rill, 11257c478bd9Sstevel@tonic-gate (uchar_t *)&opt[1], /* Link layer address */ 1126bd670b35SErik Nordmark rill->ill_phys_addr_length, 1127bd670b35SErik Nordmark gateway, ncec_flags, ND_STALE, &nce); 11287c478bd9Sstevel@tonic-gate switch (err) { 11297c478bd9Sstevel@tonic-gate case 0: 1130bd670b35SErik Nordmark nce_refrele(nce); 11317c478bd9Sstevel@tonic-gate break; 11327c478bd9Sstevel@tonic-gate case EEXIST: 11337c478bd9Sstevel@tonic-gate /* 11347c478bd9Sstevel@tonic-gate * Check to see if link layer address has changed and 1135bd670b35SErik Nordmark * process the ncec_state accordingly. 11367c478bd9Sstevel@tonic-gate */ 1137bd670b35SErik Nordmark nce_process(nce->nce_common, 1138bd670b35SErik Nordmark (uchar_t *)&opt[1], 0, B_FALSE); 1139bd670b35SErik Nordmark nce_refrele(nce); 11407c478bd9Sstevel@tonic-gate break; 11417c478bd9Sstevel@tonic-gate default: 11427c478bd9Sstevel@tonic-gate ip1dbg(("icmp_redirect_v6: NCE create failed %d\n", 11437c478bd9Sstevel@tonic-gate err)); 11447c478bd9Sstevel@tonic-gate goto fail_redirect; 11457c478bd9Sstevel@tonic-gate } 11467c478bd9Sstevel@tonic-gate } 11477c478bd9Sstevel@tonic-gate if (redirect_to_router) { 11487c478bd9Sstevel@tonic-gate ASSERT(IN6_IS_ADDR_LINKLOCAL(gateway)); 11497c478bd9Sstevel@tonic-gate 11507c478bd9Sstevel@tonic-gate /* 11517c478bd9Sstevel@tonic-gate * Create a Route Association. This will allow us to remember 11527c478bd9Sstevel@tonic-gate * a router told us to use the particular gateway. 11537c478bd9Sstevel@tonic-gate */ 11547c478bd9Sstevel@tonic-gate ire = ire_create_v6( 11557c478bd9Sstevel@tonic-gate dst, 11567c478bd9Sstevel@tonic-gate &ipv6_all_ones, /* mask */ 11577c478bd9Sstevel@tonic-gate gateway, /* gateway addr */ 11586bdb8e66Sdd193516 IRE_HOST, 1159bd670b35SErik Nordmark prev_ire->ire_ill, 1160bd670b35SErik Nordmark ALL_ZONES, 11617c478bd9Sstevel@tonic-gate (RTF_DYNAMIC | RTF_GATEWAY | RTF_HOST), 1162f4b3ec61Sdh155122 NULL, 1163f4b3ec61Sdh155122 ipst); 11647c478bd9Sstevel@tonic-gate } else { 1165bd670b35SErik Nordmark ipif_t *ipif; 1166bd670b35SErik Nordmark in6_addr_t gw; 11676bdb8e66Sdd193516 11687c478bd9Sstevel@tonic-gate /* 11696bdb8e66Sdd193516 * Just create an on link entry, i.e. interface route. 1170bd670b35SErik Nordmark * The gateway field is our link-local on the ill. 11717c478bd9Sstevel@tonic-gate */ 1172bd670b35SErik Nordmark mutex_enter(&rill->ill_lock); 1173bd670b35SErik Nordmark for (ipif = rill->ill_ipif; ipif != NULL; 1174bd670b35SErik Nordmark ipif = ipif->ipif_next) { 1175bd670b35SErik Nordmark if (!(ipif->ipif_state_flags & IPIF_CONDEMNED) && 1176bd670b35SErik Nordmark IN6_IS_ADDR_LINKLOCAL(&ipif->ipif_v6lcl_addr)) 1177bd670b35SErik Nordmark break; 1178bd670b35SErik Nordmark } 1179bd670b35SErik Nordmark if (ipif == NULL) { 1180bd670b35SErik Nordmark /* We have no link-local address! */ 1181bd670b35SErik Nordmark mutex_exit(&rill->ill_lock); 1182bd670b35SErik Nordmark goto fail_redirect; 1183bd670b35SErik Nordmark } 1184bd670b35SErik Nordmark gw = ipif->ipif_v6lcl_addr; 1185bd670b35SErik Nordmark mutex_exit(&rill->ill_lock); 1186bd670b35SErik Nordmark 11877c478bd9Sstevel@tonic-gate ire = ire_create_v6( 11887c478bd9Sstevel@tonic-gate dst, /* gateway == dst */ 11897c478bd9Sstevel@tonic-gate &ipv6_all_ones, /* mask */ 1190bd670b35SErik Nordmark &gw, /* gateway addr */ 1191bd670b35SErik Nordmark rill->ill_net_type, /* IF_[NO]RESOLVER */ 1192bd670b35SErik Nordmark prev_ire->ire_ill, 1193bd670b35SErik Nordmark ALL_ZONES, 11946bdb8e66Sdd193516 (RTF_DYNAMIC | RTF_HOST), 1195f4b3ec61Sdh155122 NULL, 1196f4b3ec61Sdh155122 ipst); 11977c478bd9Sstevel@tonic-gate } 11986bdb8e66Sdd193516 11997c478bd9Sstevel@tonic-gate if (ire == NULL) 12007c478bd9Sstevel@tonic-gate goto fail_redirect; 12017c478bd9Sstevel@tonic-gate 1202bd670b35SErik Nordmark nire = ire_add(ire); 1203bd670b35SErik Nordmark /* Check if it was a duplicate entry */ 1204bd670b35SErik Nordmark if (nire != NULL && nire != ire) { 1205bd670b35SErik Nordmark ASSERT(nire->ire_identical_ref > 1); 1206bd670b35SErik Nordmark ire_delete(nire); 1207bd670b35SErik Nordmark ire_refrele(nire); 1208bd670b35SErik Nordmark nire = NULL; 1209bd670b35SErik Nordmark } 1210bd670b35SErik Nordmark ire = nire; 1211bd670b35SErik Nordmark if (ire != NULL) { 1212bd670b35SErik Nordmark ire_refrele(ire); /* Held in ire_add */ 12137c478bd9Sstevel@tonic-gate 12147c478bd9Sstevel@tonic-gate /* tell routing sockets that we received a redirect */ 12157c478bd9Sstevel@tonic-gate ip_rts_change_v6(RTM_REDIRECT, 12167c478bd9Sstevel@tonic-gate &rd->nd_rd_dst, 12177c478bd9Sstevel@tonic-gate &rd->nd_rd_target, 1218bd670b35SErik Nordmark &ipv6_all_ones, 0, src, 12197c478bd9Sstevel@tonic-gate (RTF_DYNAMIC | RTF_GATEWAY | RTF_HOST), 0, 1220f4b3ec61Sdh155122 (RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_AUTHOR), ipst); 12217c478bd9Sstevel@tonic-gate 12227c478bd9Sstevel@tonic-gate /* 12236bdb8e66Sdd193516 * Delete any existing IRE_HOST type ires for this destination. 12247c478bd9Sstevel@tonic-gate * This together with the added IRE has the effect of 12257c478bd9Sstevel@tonic-gate * modifying an existing redirect. 12267c478bd9Sstevel@tonic-gate */ 12276bdb8e66Sdd193516 redir_ire = ire_ftable_lookup_v6(dst, 0, src, IRE_HOST, 1228bd670b35SErik Nordmark prev_ire->ire_ill, ALL_ZONES, NULL, 1229bd670b35SErik Nordmark (MATCH_IRE_GW | MATCH_IRE_TYPE | MATCH_IRE_ILL), 0, ipst, 1230bd670b35SErik Nordmark NULL); 12317c478bd9Sstevel@tonic-gate 12327c478bd9Sstevel@tonic-gate if (redir_ire != NULL) { 12336bdb8e66Sdd193516 if (redir_ire->ire_flags & RTF_DYNAMIC) 12347c478bd9Sstevel@tonic-gate ire_delete(redir_ire); 12357c478bd9Sstevel@tonic-gate ire_refrele(redir_ire); 12367c478bd9Sstevel@tonic-gate } 12377c478bd9Sstevel@tonic-gate } 12387c478bd9Sstevel@tonic-gate 12397c478bd9Sstevel@tonic-gate ire_refrele(prev_ire); 12407c478bd9Sstevel@tonic-gate prev_ire = NULL; 12417c478bd9Sstevel@tonic-gate 12427c478bd9Sstevel@tonic-gate fail_redirect: 12437c478bd9Sstevel@tonic-gate if (prev_ire != NULL) 12447c478bd9Sstevel@tonic-gate ire_refrele(prev_ire); 12457c478bd9Sstevel@tonic-gate freemsg(mp); 1246bd670b35SErik Nordmark if (rill != ira->ira_rill) 1247bd670b35SErik Nordmark ill_refrele(rill); 12487c478bd9Sstevel@tonic-gate } 12497c478bd9Sstevel@tonic-gate 12507c478bd9Sstevel@tonic-gate /* 12517c478bd9Sstevel@tonic-gate * Build and ship an IPv6 ICMP message using the packet data in mp, 12527c478bd9Sstevel@tonic-gate * and the ICMP header pointed to by "stuff". (May be called as 12537c478bd9Sstevel@tonic-gate * writer.) 12547c478bd9Sstevel@tonic-gate * Note: assumes that icmp_pkt_err_ok_v6 has been called to 12557c478bd9Sstevel@tonic-gate * verify that an icmp error packet can be sent. 12567c478bd9Sstevel@tonic-gate * 12577c478bd9Sstevel@tonic-gate * If v6src_ptr is set use it as a source. Otherwise select a reasonable 12587c478bd9Sstevel@tonic-gate * source address (see above function). 12597c478bd9Sstevel@tonic-gate */ 12607c478bd9Sstevel@tonic-gate static void 1261bd670b35SErik Nordmark icmp_pkt_v6(mblk_t *mp, void *stuff, size_t len, 1262bd670b35SErik Nordmark const in6_addr_t *v6src_ptr, ip_recv_attr_t *ira) 12637c478bd9Sstevel@tonic-gate { 12647c478bd9Sstevel@tonic-gate ip6_t *ip6h; 12657c478bd9Sstevel@tonic-gate in6_addr_t v6dst; 12667c478bd9Sstevel@tonic-gate size_t len_needed; 12677c478bd9Sstevel@tonic-gate size_t msg_len; 12687c478bd9Sstevel@tonic-gate mblk_t *mp1; 12697c478bd9Sstevel@tonic-gate icmp6_t *icmp6; 12707c478bd9Sstevel@tonic-gate in6_addr_t v6src; 1271bd670b35SErik Nordmark ill_t *ill = ira->ira_ill; 1272bd670b35SErik Nordmark ip_stack_t *ipst = ill->ill_ipst; 1273bd670b35SErik Nordmark ip_xmit_attr_t ixas; 12747c478bd9Sstevel@tonic-gate 12757c478bd9Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 12767c478bd9Sstevel@tonic-gate 1277bd670b35SErik Nordmark bzero(&ixas, sizeof (ixas)); 1278bd670b35SErik Nordmark ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6; 1279bd670b35SErik Nordmark ixas.ixa_zoneid = ira->ira_zoneid; 1280bd670b35SErik Nordmark ixas.ixa_ifindex = 0; 1281bd670b35SErik Nordmark ixas.ixa_ipst = ipst; 1282bd670b35SErik Nordmark ixas.ixa_cred = kcred; 1283bd670b35SErik Nordmark ixas.ixa_cpid = NOPID; 1284bd670b35SErik Nordmark ixas.ixa_tsl = ira->ira_tsl; /* Behave as a multi-level responder */ 1285bd670b35SErik Nordmark ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; 12867c478bd9Sstevel@tonic-gate 12877c478bd9Sstevel@tonic-gate /* 1288bd670b35SErik Nordmark * If the source of the original packet was link-local, then 1289bd670b35SErik Nordmark * make sure we send on the same ill (group) as we received it on. 12907c478bd9Sstevel@tonic-gate */ 1291bd670b35SErik Nordmark if (IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_src)) { 1292bd670b35SErik Nordmark ixas.ixa_flags |= IXAF_SCOPEID_SET; 1293bd670b35SErik Nordmark if (IS_UNDER_IPMP(ill)) 1294bd670b35SErik Nordmark ixas.ixa_scopeid = ill_get_upper_ifindex(ill); 1295bd670b35SErik Nordmark else 1296bd670b35SErik Nordmark ixas.ixa_scopeid = ill->ill_phyint->phyint_ifindex; 12977c478bd9Sstevel@tonic-gate } 1298bd670b35SErik Nordmark 1299bd670b35SErik Nordmark if (ira->ira_flags & IRAF_IPSEC_SECURE) { 13007c478bd9Sstevel@tonic-gate /* 1301bd670b35SErik Nordmark * Apply IPsec based on how IPsec was applied to 1302bd670b35SErik Nordmark * the packet that had the error. 1303bd670b35SErik Nordmark * 1304bd670b35SErik Nordmark * If it was an outbound packet that caused the ICMP 1305bd670b35SErik Nordmark * error, then the caller will have setup the IRA 1306bd670b35SErik Nordmark * appropriately. 13077c478bd9Sstevel@tonic-gate */ 1308bd670b35SErik Nordmark if (!ipsec_in_to_out(ira, &ixas, mp, NULL, ip6h)) { 1309bd670b35SErik Nordmark BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards); 1310bd670b35SErik Nordmark /* Note: mp already consumed and ip_drop_packet done */ 1311bd670b35SErik Nordmark return; 13127c478bd9Sstevel@tonic-gate } 13137c478bd9Sstevel@tonic-gate } else { 13147c478bd9Sstevel@tonic-gate /* 13157c478bd9Sstevel@tonic-gate * This is in clear. The icmp message we are building 1316bd670b35SErik Nordmark * here should go out in clear, independent of our policy. 13177c478bd9Sstevel@tonic-gate */ 1318bd670b35SErik Nordmark ixas.ixa_flags |= IXAF_NO_IPSEC; 13197c478bd9Sstevel@tonic-gate } 13207c478bd9Sstevel@tonic-gate 13217c478bd9Sstevel@tonic-gate /* 1322bd670b35SErik Nordmark * If the caller specified the source we use that. 1323bd670b35SErik Nordmark * Otherwise, if the packet was for one of our unicast addresses, make 1324bd670b35SErik Nordmark * sure we respond with that as the source. Otherwise 1325bd670b35SErik Nordmark * have ip_output_simple pick the source address. 13267c478bd9Sstevel@tonic-gate */ 13277c478bd9Sstevel@tonic-gate if (v6src_ptr != NULL) { 13287c478bd9Sstevel@tonic-gate v6src = *v6src_ptr; 13297c478bd9Sstevel@tonic-gate } else { 1330bd670b35SErik Nordmark ire_t *ire; 1331bd670b35SErik Nordmark uint_t match_flags = MATCH_IRE_TYPE | MATCH_IRE_ZONEONLY; 1332bd670b35SErik Nordmark 1333bd670b35SErik Nordmark if (IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src) || 1334bd670b35SErik Nordmark IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_dst)) 1335bd670b35SErik Nordmark match_flags |= MATCH_IRE_ILL; 1336bd670b35SErik Nordmark 1337bd670b35SErik Nordmark ire = ire_ftable_lookup_v6(&ip6h->ip6_dst, 0, 0, 1338bd670b35SErik Nordmark (IRE_LOCAL|IRE_LOOPBACK), ill, ira->ira_zoneid, NULL, 1339bd670b35SErik Nordmark match_flags, 0, ipst, NULL); 1340bd670b35SErik Nordmark if (ire != NULL) { 1341bd670b35SErik Nordmark v6src = ip6h->ip6_dst; 1342bd670b35SErik Nordmark ire_refrele(ire); 1343bd670b35SErik Nordmark } else { 1344bd670b35SErik Nordmark v6src = ipv6_all_zeros; 1345bd670b35SErik Nordmark ixas.ixa_flags |= IXAF_SET_SOURCE; 13467c478bd9Sstevel@tonic-gate } 13477c478bd9Sstevel@tonic-gate } 13487c478bd9Sstevel@tonic-gate v6dst = ip6h->ip6_src; 1349f4b3ec61Sdh155122 len_needed = ipst->ips_ipv6_icmp_return - IPV6_HDR_LEN - len; 13507c478bd9Sstevel@tonic-gate msg_len = msgdsize(mp); 13517c478bd9Sstevel@tonic-gate if (msg_len > len_needed) { 13527c478bd9Sstevel@tonic-gate if (!adjmsg(mp, len_needed - msg_len)) { 13537c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutErrors); 1354bd670b35SErik Nordmark freemsg(mp); 13557c478bd9Sstevel@tonic-gate return; 13567c478bd9Sstevel@tonic-gate } 13577c478bd9Sstevel@tonic-gate msg_len = len_needed; 13587c478bd9Sstevel@tonic-gate } 1359bd670b35SErik Nordmark mp1 = allocb(IPV6_HDR_LEN + len, BPRI_MED); 13607c478bd9Sstevel@tonic-gate if (mp1 == NULL) { 13617c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutErrors); 1362bd670b35SErik Nordmark freemsg(mp); 13637c478bd9Sstevel@tonic-gate return; 13647c478bd9Sstevel@tonic-gate } 13657c478bd9Sstevel@tonic-gate mp1->b_cont = mp; 13667c478bd9Sstevel@tonic-gate mp = mp1; 13677c478bd9Sstevel@tonic-gate 13687c478bd9Sstevel@tonic-gate /* 1369bd670b35SErik Nordmark * Set IXAF_TRUSTED_ICMP so we can let the ICMP messages this 13707c478bd9Sstevel@tonic-gate * node generates be accepted in peace by all on-host destinations. 13717c478bd9Sstevel@tonic-gate * If we do NOT assume that all on-host destinations trust 1372bd670b35SErik Nordmark * self-generated ICMP messages, then rework here, ip6.c, and spd.c. 1373bd670b35SErik Nordmark * (Look for IXAF_TRUSTED_ICMP). 13747c478bd9Sstevel@tonic-gate */ 1375bd670b35SErik Nordmark ixas.ixa_flags |= IXAF_TRUSTED_ICMP; 13767c478bd9Sstevel@tonic-gate 13777c478bd9Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 13787c478bd9Sstevel@tonic-gate mp1->b_wptr = (uchar_t *)ip6h + (IPV6_HDR_LEN + len); 13797c478bd9Sstevel@tonic-gate 13807c478bd9Sstevel@tonic-gate ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW; 13817c478bd9Sstevel@tonic-gate ip6h->ip6_nxt = IPPROTO_ICMPV6; 1382f4b3ec61Sdh155122 ip6h->ip6_hops = ipst->ips_ipv6_def_hops; 13837c478bd9Sstevel@tonic-gate ip6h->ip6_dst = v6dst; 13847c478bd9Sstevel@tonic-gate ip6h->ip6_src = v6src; 13857c478bd9Sstevel@tonic-gate msg_len += IPV6_HDR_LEN + len; 13867c478bd9Sstevel@tonic-gate if (msg_len > IP_MAXPACKET + IPV6_HDR_LEN) { 13877c478bd9Sstevel@tonic-gate (void) adjmsg(mp, IP_MAXPACKET + IPV6_HDR_LEN - msg_len); 13887c478bd9Sstevel@tonic-gate msg_len = IP_MAXPACKET + IPV6_HDR_LEN; 13897c478bd9Sstevel@tonic-gate } 13907c478bd9Sstevel@tonic-gate ip6h->ip6_plen = htons((uint16_t)(msgdsize(mp) - IPV6_HDR_LEN)); 13917c478bd9Sstevel@tonic-gate icmp6 = (icmp6_t *)&ip6h[1]; 13927c478bd9Sstevel@tonic-gate bcopy(stuff, (char *)icmp6, len); 13937c478bd9Sstevel@tonic-gate /* 13947c478bd9Sstevel@tonic-gate * Prepare for checksum by putting icmp length in the icmp 1395bd670b35SErik Nordmark * checksum field. The checksum is calculated in ip_output_wire_v6. 13967c478bd9Sstevel@tonic-gate */ 13977c478bd9Sstevel@tonic-gate icmp6->icmp6_cksum = ip6h->ip6_plen; 13987c478bd9Sstevel@tonic-gate if (icmp6->icmp6_type == ND_REDIRECT) { 13997c478bd9Sstevel@tonic-gate ip6h->ip6_hops = IPV6_MAX_HOPS; 14007c478bd9Sstevel@tonic-gate } 1401bd670b35SErik Nordmark 1402bd670b35SErik Nordmark (void) ip_output_simple(mp, &ixas); 1403bd670b35SErik Nordmark ixa_cleanup(&ixas); 14047c478bd9Sstevel@tonic-gate } 14057c478bd9Sstevel@tonic-gate 14067c478bd9Sstevel@tonic-gate /* 14077c478bd9Sstevel@tonic-gate * Update the output mib when ICMPv6 packets are sent. 14087c478bd9Sstevel@tonic-gate */ 1409bd670b35SErik Nordmark void 14107c478bd9Sstevel@tonic-gate icmp_update_out_mib_v6(ill_t *ill, icmp6_t *icmp6) 14117c478bd9Sstevel@tonic-gate { 14127c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutMsgs); 14137c478bd9Sstevel@tonic-gate 14147c478bd9Sstevel@tonic-gate switch (icmp6->icmp6_type) { 14157c478bd9Sstevel@tonic-gate case ICMP6_DST_UNREACH: 14167c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutDestUnreachs); 14177c478bd9Sstevel@tonic-gate if (icmp6->icmp6_code == ICMP6_DST_UNREACH_ADMIN) 14187c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutAdminProhibs); 14197c478bd9Sstevel@tonic-gate break; 14207c478bd9Sstevel@tonic-gate 14217c478bd9Sstevel@tonic-gate case ICMP6_TIME_EXCEEDED: 14227c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutTimeExcds); 14237c478bd9Sstevel@tonic-gate break; 14247c478bd9Sstevel@tonic-gate 14257c478bd9Sstevel@tonic-gate case ICMP6_PARAM_PROB: 14267c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutParmProblems); 14277c478bd9Sstevel@tonic-gate break; 14287c478bd9Sstevel@tonic-gate 14297c478bd9Sstevel@tonic-gate case ICMP6_PACKET_TOO_BIG: 14307c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutPktTooBigs); 14317c478bd9Sstevel@tonic-gate break; 14327c478bd9Sstevel@tonic-gate 14337c478bd9Sstevel@tonic-gate case ICMP6_ECHO_REQUEST: 14347c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutEchos); 14357c478bd9Sstevel@tonic-gate break; 14367c478bd9Sstevel@tonic-gate 14377c478bd9Sstevel@tonic-gate case ICMP6_ECHO_REPLY: 14387c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutEchoReplies); 14397c478bd9Sstevel@tonic-gate break; 14407c478bd9Sstevel@tonic-gate 14417c478bd9Sstevel@tonic-gate case ND_ROUTER_SOLICIT: 14427c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutRouterSolicits); 14437c478bd9Sstevel@tonic-gate break; 14447c478bd9Sstevel@tonic-gate 14457c478bd9Sstevel@tonic-gate case ND_ROUTER_ADVERT: 14467c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutRouterAdvertisements); 14477c478bd9Sstevel@tonic-gate break; 14487c478bd9Sstevel@tonic-gate 14497c478bd9Sstevel@tonic-gate case ND_NEIGHBOR_SOLICIT: 14507c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutNeighborSolicits); 14517c478bd9Sstevel@tonic-gate break; 14527c478bd9Sstevel@tonic-gate 14537c478bd9Sstevel@tonic-gate case ND_NEIGHBOR_ADVERT: 14547c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, 14557c478bd9Sstevel@tonic-gate ipv6IfIcmpOutNeighborAdvertisements); 14567c478bd9Sstevel@tonic-gate break; 14577c478bd9Sstevel@tonic-gate 14587c478bd9Sstevel@tonic-gate case ND_REDIRECT: 14597c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutRedirects); 14607c478bd9Sstevel@tonic-gate break; 14617c478bd9Sstevel@tonic-gate 14627c478bd9Sstevel@tonic-gate case MLD_LISTENER_QUERY: 14637c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutGroupMembQueries); 14647c478bd9Sstevel@tonic-gate break; 14657c478bd9Sstevel@tonic-gate 14667c478bd9Sstevel@tonic-gate case MLD_LISTENER_REPORT: 14677c478bd9Sstevel@tonic-gate case MLD_V2_LISTENER_REPORT: 14687c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutGroupMembResponses); 14697c478bd9Sstevel@tonic-gate break; 14707c478bd9Sstevel@tonic-gate 14717c478bd9Sstevel@tonic-gate case MLD_LISTENER_REDUCTION: 14727c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutGroupMembReductions); 14737c478bd9Sstevel@tonic-gate break; 14747c478bd9Sstevel@tonic-gate } 14757c478bd9Sstevel@tonic-gate } 14767c478bd9Sstevel@tonic-gate 14777c478bd9Sstevel@tonic-gate /* 14787c478bd9Sstevel@tonic-gate * Check if it is ok to send an ICMPv6 error packet in 14797c478bd9Sstevel@tonic-gate * response to the IP packet in mp. 14807c478bd9Sstevel@tonic-gate * Free the message and return null if no 14817c478bd9Sstevel@tonic-gate * ICMP error packet should be sent. 14827c478bd9Sstevel@tonic-gate */ 14837c478bd9Sstevel@tonic-gate static mblk_t * 1484bd670b35SErik Nordmark icmp_pkt_err_ok_v6(mblk_t *mp, boolean_t mcast_ok, ip_recv_attr_t *ira) 14857c478bd9Sstevel@tonic-gate { 1486bd670b35SErik Nordmark ill_t *ill = ira->ira_ill; 1487bd670b35SErik Nordmark ip_stack_t *ipst = ill->ill_ipst; 1488bd670b35SErik Nordmark boolean_t llbcast; 14897c478bd9Sstevel@tonic-gate ip6_t *ip6h; 14907c478bd9Sstevel@tonic-gate 14917c478bd9Sstevel@tonic-gate if (!mp) 14927c478bd9Sstevel@tonic-gate return (NULL); 14937c478bd9Sstevel@tonic-gate 1494bd670b35SErik Nordmark /* We view multicast and broadcast as the same.. */ 1495bd670b35SErik Nordmark llbcast = (ira->ira_flags & 1496bd670b35SErik Nordmark (IRAF_L2DST_MULTICAST|IRAF_L2DST_BROADCAST)) != 0; 14977c478bd9Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 14987c478bd9Sstevel@tonic-gate 14997c478bd9Sstevel@tonic-gate /* Check if source address uniquely identifies the host */ 15007c478bd9Sstevel@tonic-gate 15017c478bd9Sstevel@tonic-gate if (IN6_IS_ADDR_MULTICAST(&ip6h->ip6_src) || 15027c478bd9Sstevel@tonic-gate IN6_IS_ADDR_V4MAPPED(&ip6h->ip6_src) || 15037c478bd9Sstevel@tonic-gate IN6_IS_ADDR_UNSPECIFIED(&ip6h->ip6_src)) { 15047c478bd9Sstevel@tonic-gate freemsg(mp); 15057c478bd9Sstevel@tonic-gate return (NULL); 15067c478bd9Sstevel@tonic-gate } 15077c478bd9Sstevel@tonic-gate 15087c478bd9Sstevel@tonic-gate if (ip6h->ip6_nxt == IPPROTO_ICMPV6) { 15097c478bd9Sstevel@tonic-gate size_t len_needed = IPV6_HDR_LEN + ICMP6_MINLEN; 15107c478bd9Sstevel@tonic-gate icmp6_t *icmp6; 15117c478bd9Sstevel@tonic-gate 15127c478bd9Sstevel@tonic-gate if (mp->b_wptr - mp->b_rptr < len_needed) { 15137c478bd9Sstevel@tonic-gate if (!pullupmsg(mp, len_needed)) { 15147c478bd9Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, 15157c478bd9Sstevel@tonic-gate ipv6IfIcmpInErrors); 15167c478bd9Sstevel@tonic-gate freemsg(mp); 15177c478bd9Sstevel@tonic-gate return (NULL); 15187c478bd9Sstevel@tonic-gate } 15197c478bd9Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 15207c478bd9Sstevel@tonic-gate } 15217c478bd9Sstevel@tonic-gate icmp6 = (icmp6_t *)&ip6h[1]; 15227c478bd9Sstevel@tonic-gate /* Explicitly do not generate errors in response to redirects */ 15237c478bd9Sstevel@tonic-gate if (ICMP6_IS_ERROR(icmp6->icmp6_type) || 15247c478bd9Sstevel@tonic-gate icmp6->icmp6_type == ND_REDIRECT) { 15257c478bd9Sstevel@tonic-gate freemsg(mp); 15267c478bd9Sstevel@tonic-gate return (NULL); 15277c478bd9Sstevel@tonic-gate } 15287c478bd9Sstevel@tonic-gate } 15297c478bd9Sstevel@tonic-gate /* 15307c478bd9Sstevel@tonic-gate * Check that the destination is not multicast and that the packet 15317c478bd9Sstevel@tonic-gate * was not sent on link layer broadcast or multicast. (Exception 15327c478bd9Sstevel@tonic-gate * is Packet too big message as per the draft - when mcast_ok is set.) 15337c478bd9Sstevel@tonic-gate */ 15347c478bd9Sstevel@tonic-gate if (!mcast_ok && 15357c478bd9Sstevel@tonic-gate (llbcast || IN6_IS_ADDR_MULTICAST(&ip6h->ip6_dst))) { 15367c478bd9Sstevel@tonic-gate freemsg(mp); 15377c478bd9Sstevel@tonic-gate return (NULL); 15387c478bd9Sstevel@tonic-gate } 1539bd670b35SErik Nordmark /* 1540bd670b35SErik Nordmark * If this is a labeled system, then check to see if we're allowed to 1541bd670b35SErik Nordmark * send a response to this particular sender. If not, then just drop. 1542bd670b35SErik Nordmark */ 1543bd670b35SErik Nordmark if (is_system_labeled() && !tsol_can_reply_error(mp, ira)) { 1544bd670b35SErik Nordmark BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutErrors); 1545bd670b35SErik Nordmark freemsg(mp); 1546bd670b35SErik Nordmark return (NULL); 1547bd670b35SErik Nordmark } 1548bd670b35SErik Nordmark 1549f4b3ec61Sdh155122 if (icmp_err_rate_limit(ipst)) { 15507c478bd9Sstevel@tonic-gate /* 15517c478bd9Sstevel@tonic-gate * Only send ICMP error packets every so often. 15527c478bd9Sstevel@tonic-gate * This should be done on a per port/source basis, 15537c478bd9Sstevel@tonic-gate * but for now this will suffice. 15547c478bd9Sstevel@tonic-gate */ 15557c478bd9Sstevel@tonic-gate freemsg(mp); 15567c478bd9Sstevel@tonic-gate return (NULL); 15577c478bd9Sstevel@tonic-gate } 15587c478bd9Sstevel@tonic-gate return (mp); 15597c478bd9Sstevel@tonic-gate } 15607c478bd9Sstevel@tonic-gate 15617c478bd9Sstevel@tonic-gate /* 1562bd670b35SErik Nordmark * Called when a packet was sent out the same link that it arrived on. 1563bd670b35SErik Nordmark * Check if it is ok to send a redirect and then send it. 1564bd670b35SErik Nordmark */ 1565bd670b35SErik Nordmark void 1566bd670b35SErik Nordmark ip_send_potential_redirect_v6(mblk_t *mp, ip6_t *ip6h, ire_t *ire, 1567bd670b35SErik Nordmark ip_recv_attr_t *ira) 1568bd670b35SErik Nordmark { 1569bd670b35SErik Nordmark ill_t *ill = ira->ira_ill; 1570bd670b35SErik Nordmark ip_stack_t *ipst = ill->ill_ipst; 1571bd670b35SErik Nordmark in6_addr_t *v6targ; 1572bd670b35SErik Nordmark ire_t *src_ire_v6 = NULL; 1573bd670b35SErik Nordmark mblk_t *mp1; 1574bd670b35SErik Nordmark ire_t *nhop_ire = NULL; 1575bd670b35SErik Nordmark 1576bd670b35SErik Nordmark /* 1577bd670b35SErik Nordmark * Don't send a redirect when forwarding a source 1578bd670b35SErik Nordmark * routed packet. 1579bd670b35SErik Nordmark */ 1580bd670b35SErik Nordmark if (ip_source_routed_v6(ip6h, mp, ipst)) 1581bd670b35SErik Nordmark return; 1582bd670b35SErik Nordmark 1583bd670b35SErik Nordmark if (ire->ire_type & IRE_ONLINK) { 1584bd670b35SErik Nordmark /* Target is directly connected */ 1585bd670b35SErik Nordmark v6targ = &ip6h->ip6_dst; 1586bd670b35SErik Nordmark } else { 1587bd670b35SErik Nordmark /* Determine the most specific IRE used to send the packets */ 1588bd670b35SErik Nordmark nhop_ire = ire_nexthop(ire); 1589bd670b35SErik Nordmark if (nhop_ire == NULL) 1590bd670b35SErik Nordmark return; 1591bd670b35SErik Nordmark 1592bd670b35SErik Nordmark /* 1593bd670b35SErik Nordmark * We won't send redirects to a router 1594bd670b35SErik Nordmark * that doesn't have a link local 1595bd670b35SErik Nordmark * address, but will forward. 1596bd670b35SErik Nordmark */ 1597bd670b35SErik Nordmark if (!IN6_IS_ADDR_LINKLOCAL(&nhop_ire->ire_addr_v6)) { 1598bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsInAddrErrors); 1599bd670b35SErik Nordmark ip_drop_input("ipIfStatsInAddrErrors", mp, ill); 1600bd670b35SErik Nordmark ire_refrele(nhop_ire); 1601bd670b35SErik Nordmark return; 1602bd670b35SErik Nordmark } 1603bd670b35SErik Nordmark v6targ = &nhop_ire->ire_addr_v6; 1604bd670b35SErik Nordmark } 1605bd670b35SErik Nordmark src_ire_v6 = ire_ftable_lookup_v6(&ip6h->ip6_src, 1606bd670b35SErik Nordmark NULL, NULL, IRE_INTERFACE, ire->ire_ill, ALL_ZONES, NULL, 1607bd670b35SErik Nordmark MATCH_IRE_ILL | MATCH_IRE_TYPE, 0, ipst, NULL); 1608bd670b35SErik Nordmark 1609bd670b35SErik Nordmark if (src_ire_v6 == NULL) { 1610bd670b35SErik Nordmark if (nhop_ire != NULL) 1611bd670b35SErik Nordmark ire_refrele(nhop_ire); 1612bd670b35SErik Nordmark return; 1613bd670b35SErik Nordmark } 1614bd670b35SErik Nordmark 1615bd670b35SErik Nordmark /* 1616bd670b35SErik Nordmark * The source is directly connected. 1617bd670b35SErik Nordmark */ 1618bd670b35SErik Nordmark mp1 = copymsg(mp); 1619bd670b35SErik Nordmark if (mp1 != NULL) 1620bd670b35SErik Nordmark icmp_send_redirect_v6(mp1, v6targ, &ip6h->ip6_dst, ira); 1621bd670b35SErik Nordmark 1622bd670b35SErik Nordmark if (nhop_ire != NULL) 1623bd670b35SErik Nordmark ire_refrele(nhop_ire); 1624bd670b35SErik Nordmark ire_refrele(src_ire_v6); 1625bd670b35SErik Nordmark } 1626bd670b35SErik Nordmark 1627bd670b35SErik Nordmark /* 16287c478bd9Sstevel@tonic-gate * Generate an ICMPv6 redirect message. 16297c478bd9Sstevel@tonic-gate * Include target link layer address option if it exits. 16307c478bd9Sstevel@tonic-gate * Always include redirect header. 16317c478bd9Sstevel@tonic-gate */ 16327c478bd9Sstevel@tonic-gate static void 1633bd670b35SErik Nordmark icmp_send_redirect_v6(mblk_t *mp, in6_addr_t *targetp, in6_addr_t *dest, 1634bd670b35SErik Nordmark ip_recv_attr_t *ira) 16357c478bd9Sstevel@tonic-gate { 16367c478bd9Sstevel@tonic-gate nd_redirect_t *rd; 16377c478bd9Sstevel@tonic-gate nd_opt_rd_hdr_t *rdh; 16387c478bd9Sstevel@tonic-gate uchar_t *buf; 1639bd670b35SErik Nordmark ncec_t *ncec = NULL; 16407c478bd9Sstevel@tonic-gate nd_opt_hdr_t *opt; 16417c478bd9Sstevel@tonic-gate int len; 16427c478bd9Sstevel@tonic-gate int ll_opt_len = 0; 16437c478bd9Sstevel@tonic-gate int max_redir_hdr_data_len; 16447c478bd9Sstevel@tonic-gate int pkt_len; 16457c478bd9Sstevel@tonic-gate in6_addr_t *srcp; 1646bd670b35SErik Nordmark ill_t *ill; 1647bd670b35SErik Nordmark boolean_t need_refrele; 1648bd670b35SErik Nordmark ip_stack_t *ipst = ira->ira_ill->ill_ipst; 16497c478bd9Sstevel@tonic-gate 1650bd670b35SErik Nordmark mp = icmp_pkt_err_ok_v6(mp, B_FALSE, ira); 16517c478bd9Sstevel@tonic-gate if (mp == NULL) 16527c478bd9Sstevel@tonic-gate return; 1653bd670b35SErik Nordmark 1654bd670b35SErik Nordmark if (IS_UNDER_IPMP(ira->ira_ill)) { 1655bd670b35SErik Nordmark ill = ipmp_ill_hold_ipmp_ill(ira->ira_ill); 1656bd670b35SErik Nordmark if (ill == NULL) { 1657bd670b35SErik Nordmark ill = ira->ira_ill; 1658bd670b35SErik Nordmark BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects); 1659bd670b35SErik Nordmark ip_drop_output("no IPMP ill for sending redirect", 1660bd670b35SErik Nordmark mp, ill); 1661bd670b35SErik Nordmark freemsg(mp); 1662bd670b35SErik Nordmark return; 1663bd670b35SErik Nordmark } 1664bd670b35SErik Nordmark need_refrele = B_TRUE; 1665bd670b35SErik Nordmark } else { 1666bd670b35SErik Nordmark ill = ira->ira_ill; 1667bd670b35SErik Nordmark need_refrele = B_FALSE; 1668bd670b35SErik Nordmark } 1669bd670b35SErik Nordmark 1670bd670b35SErik Nordmark ncec = ncec_lookup_illgrp_v6(ill, targetp); 1671bd670b35SErik Nordmark if (ncec != NULL && ncec->ncec_state != ND_INCOMPLETE && 1672bd670b35SErik Nordmark ncec->ncec_lladdr != NULL) { 16737c478bd9Sstevel@tonic-gate ll_opt_len = (sizeof (nd_opt_hdr_t) + 16747c478bd9Sstevel@tonic-gate ill->ill_phys_addr_length + 7)/8 * 8; 16757c478bd9Sstevel@tonic-gate } 16767c478bd9Sstevel@tonic-gate len = sizeof (nd_redirect_t) + sizeof (nd_opt_rd_hdr_t) + ll_opt_len; 16777c478bd9Sstevel@tonic-gate ASSERT(len % 4 == 0); 16787c478bd9Sstevel@tonic-gate buf = kmem_alloc(len, KM_NOSLEEP); 16797c478bd9Sstevel@tonic-gate if (buf == NULL) { 1680bd670b35SErik Nordmark if (ncec != NULL) 1681bd670b35SErik Nordmark ncec_refrele(ncec); 1682bd670b35SErik Nordmark if (need_refrele) 1683bd670b35SErik Nordmark ill_refrele(ill); 16847c478bd9Sstevel@tonic-gate freemsg(mp); 16857c478bd9Sstevel@tonic-gate return; 16867c478bd9Sstevel@tonic-gate } 16877c478bd9Sstevel@tonic-gate 16887c478bd9Sstevel@tonic-gate rd = (nd_redirect_t *)buf; 16897c478bd9Sstevel@tonic-gate rd->nd_rd_type = (uint8_t)ND_REDIRECT; 16907c478bd9Sstevel@tonic-gate rd->nd_rd_code = 0; 16917c478bd9Sstevel@tonic-gate rd->nd_rd_reserved = 0; 16927c478bd9Sstevel@tonic-gate rd->nd_rd_target = *targetp; 16937c478bd9Sstevel@tonic-gate rd->nd_rd_dst = *dest; 16947c478bd9Sstevel@tonic-gate 16957c478bd9Sstevel@tonic-gate opt = (nd_opt_hdr_t *)(buf + sizeof (nd_redirect_t)); 1696bd670b35SErik Nordmark if (ncec != NULL && ll_opt_len != 0) { 16977c478bd9Sstevel@tonic-gate opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; 16987c478bd9Sstevel@tonic-gate opt->nd_opt_len = ll_opt_len/8; 1699bd670b35SErik Nordmark bcopy((char *)ncec->ncec_lladdr, &opt[1], 17007c478bd9Sstevel@tonic-gate ill->ill_phys_addr_length); 17017c478bd9Sstevel@tonic-gate } 1702bd670b35SErik Nordmark if (ncec != NULL) 1703bd670b35SErik Nordmark ncec_refrele(ncec); 17047c478bd9Sstevel@tonic-gate rdh = (nd_opt_rd_hdr_t *)(buf + sizeof (nd_redirect_t) + ll_opt_len); 17057c478bd9Sstevel@tonic-gate rdh->nd_opt_rh_type = (uint8_t)ND_OPT_REDIRECTED_HEADER; 17067c478bd9Sstevel@tonic-gate /* max_redir_hdr_data_len and nd_opt_rh_len must be multiple of 8 */ 1707f4b3ec61Sdh155122 max_redir_hdr_data_len = 1708f4b3ec61Sdh155122 (ipst->ips_ipv6_icmp_return - IPV6_HDR_LEN - len)/8*8; 17097c478bd9Sstevel@tonic-gate pkt_len = msgdsize(mp); 17107c478bd9Sstevel@tonic-gate /* Make sure mp is 8 byte aligned */ 17117c478bd9Sstevel@tonic-gate if (pkt_len > max_redir_hdr_data_len) { 17127c478bd9Sstevel@tonic-gate rdh->nd_opt_rh_len = (max_redir_hdr_data_len + 17137c478bd9Sstevel@tonic-gate sizeof (nd_opt_rd_hdr_t))/8; 17147c478bd9Sstevel@tonic-gate (void) adjmsg(mp, max_redir_hdr_data_len - pkt_len); 17157c478bd9Sstevel@tonic-gate } else { 17167c478bd9Sstevel@tonic-gate rdh->nd_opt_rh_len = (pkt_len + sizeof (nd_opt_rd_hdr_t))/8; 17177c478bd9Sstevel@tonic-gate (void) adjmsg(mp, -(pkt_len % 8)); 17187c478bd9Sstevel@tonic-gate } 17197c478bd9Sstevel@tonic-gate rdh->nd_opt_rh_reserved1 = 0; 17207c478bd9Sstevel@tonic-gate rdh->nd_opt_rh_reserved2 = 0; 1721bd670b35SErik Nordmark /* ipif_v6lcl_addr contains the link-local source address */ 1722bd670b35SErik Nordmark srcp = &ill->ill_ipif->ipif_v6lcl_addr; 17237c478bd9Sstevel@tonic-gate 17245597b60aSnordmark /* Redirects sent by router, and router is global zone */ 1725bd670b35SErik Nordmark ASSERT(ira->ira_zoneid == ALL_ZONES); 1726bd670b35SErik Nordmark ira->ira_zoneid = GLOBAL_ZONEID; 1727bd670b35SErik Nordmark icmp_pkt_v6(mp, buf, len, srcp, ira); 17287c478bd9Sstevel@tonic-gate kmem_free(buf, len); 1729bd670b35SErik Nordmark if (need_refrele) 1730bd670b35SErik Nordmark ill_refrele(ill); 17317c478bd9Sstevel@tonic-gate } 17327c478bd9Sstevel@tonic-gate 17337c478bd9Sstevel@tonic-gate 17347c478bd9Sstevel@tonic-gate /* Generate an ICMP time exceeded message. (May be called as writer.) */ 17357c478bd9Sstevel@tonic-gate void 1736bd670b35SErik Nordmark icmp_time_exceeded_v6(mblk_t *mp, uint8_t code, boolean_t mcast_ok, 1737bd670b35SErik Nordmark ip_recv_attr_t *ira) 17387c478bd9Sstevel@tonic-gate { 17397c478bd9Sstevel@tonic-gate icmp6_t icmp6; 17407c478bd9Sstevel@tonic-gate 1741bd670b35SErik Nordmark mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira); 1742bd670b35SErik Nordmark if (mp == NULL) 17437c478bd9Sstevel@tonic-gate return; 1744bd670b35SErik Nordmark 17457c478bd9Sstevel@tonic-gate bzero(&icmp6, sizeof (icmp6_t)); 17467c478bd9Sstevel@tonic-gate icmp6.icmp6_type = ICMP6_TIME_EXCEEDED; 17477c478bd9Sstevel@tonic-gate icmp6.icmp6_code = code; 1748bd670b35SErik Nordmark icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira); 17497c478bd9Sstevel@tonic-gate } 17507c478bd9Sstevel@tonic-gate 17517c478bd9Sstevel@tonic-gate /* 17527c478bd9Sstevel@tonic-gate * Generate an ICMP unreachable message. 1753bd670b35SErik Nordmark * When called from ip_output side a minimal ip_recv_attr_t needs to be 1754bd670b35SErik Nordmark * constructed by the caller. 17557c478bd9Sstevel@tonic-gate */ 17567c478bd9Sstevel@tonic-gate void 1757bd670b35SErik Nordmark icmp_unreachable_v6(mblk_t *mp, uint8_t code, boolean_t mcast_ok, 1758bd670b35SErik Nordmark ip_recv_attr_t *ira) 17597c478bd9Sstevel@tonic-gate { 17607c478bd9Sstevel@tonic-gate icmp6_t icmp6; 17617c478bd9Sstevel@tonic-gate 1762bd670b35SErik Nordmark mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira); 1763bd670b35SErik Nordmark if (mp == NULL) 17647c478bd9Sstevel@tonic-gate return; 1765bd670b35SErik Nordmark 17667c478bd9Sstevel@tonic-gate bzero(&icmp6, sizeof (icmp6_t)); 17677c478bd9Sstevel@tonic-gate icmp6.icmp6_type = ICMP6_DST_UNREACH; 17687c478bd9Sstevel@tonic-gate icmp6.icmp6_code = code; 1769bd670b35SErik Nordmark icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira); 17707c478bd9Sstevel@tonic-gate } 17717c478bd9Sstevel@tonic-gate 17727c478bd9Sstevel@tonic-gate /* 17737c478bd9Sstevel@tonic-gate * Generate an ICMP pkt too big message. 1774bd670b35SErik Nordmark * When called from ip_output side a minimal ip_recv_attr_t needs to be 1775bd670b35SErik Nordmark * constructed by the caller. 17767c478bd9Sstevel@tonic-gate */ 1777bd670b35SErik Nordmark void 1778bd670b35SErik Nordmark icmp_pkt2big_v6(mblk_t *mp, uint32_t mtu, boolean_t mcast_ok, 1779bd670b35SErik Nordmark ip_recv_attr_t *ira) 17807c478bd9Sstevel@tonic-gate { 17817c478bd9Sstevel@tonic-gate icmp6_t icmp6; 17827c478bd9Sstevel@tonic-gate 1783bd670b35SErik Nordmark mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira); 1784bd670b35SErik Nordmark if (mp == NULL) 17857c478bd9Sstevel@tonic-gate return; 1786bd670b35SErik Nordmark 17877c478bd9Sstevel@tonic-gate bzero(&icmp6, sizeof (icmp6_t)); 17887c478bd9Sstevel@tonic-gate icmp6.icmp6_type = ICMP6_PACKET_TOO_BIG; 17897c478bd9Sstevel@tonic-gate icmp6.icmp6_code = 0; 17907c478bd9Sstevel@tonic-gate icmp6.icmp6_mtu = htonl(mtu); 17917c478bd9Sstevel@tonic-gate 1792bd670b35SErik Nordmark icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira); 17937c478bd9Sstevel@tonic-gate } 17947c478bd9Sstevel@tonic-gate 17957c478bd9Sstevel@tonic-gate /* 17967c478bd9Sstevel@tonic-gate * Generate an ICMP parameter problem message. (May be called as writer.) 17977c478bd9Sstevel@tonic-gate * 'offset' is the offset from the beginning of the packet in error. 1798bd670b35SErik Nordmark * When called from ip_output side a minimal ip_recv_attr_t needs to be 1799bd670b35SErik Nordmark * constructed by the caller. 18007c478bd9Sstevel@tonic-gate */ 18017c478bd9Sstevel@tonic-gate static void 1802bd670b35SErik Nordmark icmp_param_problem_v6(mblk_t *mp, uint8_t code, uint32_t offset, 1803bd670b35SErik Nordmark boolean_t mcast_ok, ip_recv_attr_t *ira) 18047c478bd9Sstevel@tonic-gate { 18057c478bd9Sstevel@tonic-gate icmp6_t icmp6; 18067c478bd9Sstevel@tonic-gate 1807bd670b35SErik Nordmark mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira); 1808bd670b35SErik Nordmark if (mp == NULL) 18097c478bd9Sstevel@tonic-gate return; 1810bd670b35SErik Nordmark 18117c478bd9Sstevel@tonic-gate bzero((char *)&icmp6, sizeof (icmp6_t)); 18127c478bd9Sstevel@tonic-gate icmp6.icmp6_type = ICMP6_PARAM_PROB; 18137c478bd9Sstevel@tonic-gate icmp6.icmp6_code = code; 18147c478bd9Sstevel@tonic-gate icmp6.icmp6_pptr = htonl(offset); 1815bd670b35SErik Nordmark icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira); 18167c478bd9Sstevel@tonic-gate } 18177c478bd9Sstevel@tonic-gate 1818bd670b35SErik Nordmark void 1819bd670b35SErik Nordmark icmp_param_problem_nexthdr_v6(mblk_t *mp, boolean_t mcast_ok, 1820bd670b35SErik Nordmark ip_recv_attr_t *ira) 18217c478bd9Sstevel@tonic-gate { 1822bd670b35SErik Nordmark ip6_t *ip6h = (ip6_t *)mp->b_rptr; 1823bd670b35SErik Nordmark uint16_t hdr_length; 1824bd670b35SErik Nordmark uint8_t *nexthdrp; 1825bd670b35SErik Nordmark uint32_t offset; 1826bd670b35SErik Nordmark ill_t *ill = ira->ira_ill; 1827de8c4a14SErik Nordmark 1828bd670b35SErik Nordmark /* Determine the offset of the bad nexthdr value */ 1829bd670b35SErik Nordmark if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp)) { 1830bd670b35SErik Nordmark /* Malformed packet */ 1831bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 1832bd670b35SErik Nordmark ip_drop_input("ipIfStatsInDiscards", mp, ill); 1833bd670b35SErik Nordmark freemsg(mp); 1834bd670b35SErik Nordmark return; 1835de8c4a14SErik Nordmark } 18367c478bd9Sstevel@tonic-gate 1837bd670b35SErik Nordmark offset = nexthdrp - mp->b_rptr; 1838bd670b35SErik Nordmark icmp_param_problem_v6(mp, ICMP6_PARAMPROB_NEXTHEADER, offset, 1839bd670b35SErik Nordmark mcast_ok, ira); 18407c478bd9Sstevel@tonic-gate } 18417c478bd9Sstevel@tonic-gate 184245916cd2Sjpk /* 1843bd670b35SErik Nordmark * Verify whether or not the IP address is a valid local address. 1844bd670b35SErik Nordmark * Could be a unicast, including one for a down interface. 1845bd670b35SErik Nordmark * If allow_mcbc then a multicast or broadcast address is also 1846bd670b35SErik Nordmark * acceptable. 184745916cd2Sjpk * 18487c478bd9Sstevel@tonic-gate * In the case of a multicast address, however, the 18497c478bd9Sstevel@tonic-gate * upper protocol is expected to reset the src address 1850bd670b35SErik Nordmark * to zero when we return IPVL_MCAST so that 18517c478bd9Sstevel@tonic-gate * no packets are emitted with multicast address as 18527c478bd9Sstevel@tonic-gate * source address. 18537c478bd9Sstevel@tonic-gate * The addresses valid for bind are: 18547c478bd9Sstevel@tonic-gate * (1) - in6addr_any 18557c478bd9Sstevel@tonic-gate * (2) - IP address of an UP interface 18567c478bd9Sstevel@tonic-gate * (3) - IP address of a DOWN interface 18577c478bd9Sstevel@tonic-gate * (4) - a multicast address. In this case 18587c478bd9Sstevel@tonic-gate * the conn will only receive packets destined to 18597c478bd9Sstevel@tonic-gate * the specified multicast address. Note: the 18607c478bd9Sstevel@tonic-gate * application still has to issue an 18617c478bd9Sstevel@tonic-gate * IPV6_JOIN_GROUP socket option. 18627c478bd9Sstevel@tonic-gate * 18637c478bd9Sstevel@tonic-gate * In all the above cases, the bound address must be valid in the current zone. 18647c478bd9Sstevel@tonic-gate * When the address is loopback or multicast, there might be many matching IREs 18657c478bd9Sstevel@tonic-gate * so bind has to look up based on the zone. 18667c478bd9Sstevel@tonic-gate */ 1867bd670b35SErik Nordmark ip_laddr_t 1868bd670b35SErik Nordmark ip_laddr_verify_v6(const in6_addr_t *v6src, zoneid_t zoneid, 1869bd670b35SErik Nordmark ip_stack_t *ipst, boolean_t allow_mcbc, uint_t scopeid) 18707c478bd9Sstevel@tonic-gate { 1871bd670b35SErik Nordmark ire_t *src_ire; 1872bd670b35SErik Nordmark uint_t match_flags; 1873bd670b35SErik Nordmark ill_t *ill = NULL; 18747c478bd9Sstevel@tonic-gate 1875bd670b35SErik Nordmark ASSERT(!IN6_IS_ADDR_V4MAPPED(v6src)); 1876bd670b35SErik Nordmark ASSERT(!IN6_IS_ADDR_UNSPECIFIED(v6src)); 18770f1702c5SYu Xiangning 1878bd670b35SErik Nordmark match_flags = MATCH_IRE_ZONEONLY; 1879bd670b35SErik Nordmark if (scopeid != 0) { 1880bd670b35SErik Nordmark ill = ill_lookup_on_ifindex(scopeid, B_TRUE, ipst); 1881bd670b35SErik Nordmark if (ill == NULL) 1882bd670b35SErik Nordmark return (IPVL_BAD); 1883bd670b35SErik Nordmark match_flags |= MATCH_IRE_ILL; 1884bd670b35SErik Nordmark } 18857c478bd9Sstevel@tonic-gate 1886bd670b35SErik Nordmark src_ire = ire_ftable_lookup_v6(v6src, NULL, NULL, 0, 1887bd670b35SErik Nordmark ill, zoneid, NULL, match_flags, 0, ipst, NULL); 1888bd670b35SErik Nordmark if (ill != NULL) 1889bd670b35SErik Nordmark ill_refrele(ill); 18907c478bd9Sstevel@tonic-gate 18917c478bd9Sstevel@tonic-gate /* 18927c478bd9Sstevel@tonic-gate * If an address other than in6addr_any is requested, 18937c478bd9Sstevel@tonic-gate * we verify that it is a valid address for bind 18947c478bd9Sstevel@tonic-gate * Note: Following code is in if-else-if form for 18957c478bd9Sstevel@tonic-gate * readability compared to a condition check. 18967c478bd9Sstevel@tonic-gate */ 1897bd670b35SErik Nordmark if (src_ire != NULL && (src_ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK))) { 18987c478bd9Sstevel@tonic-gate /* 18997c478bd9Sstevel@tonic-gate * (2) Bind to address of local UP interface 19007c478bd9Sstevel@tonic-gate */ 1901bd670b35SErik Nordmark ire_refrele(src_ire); 1902bd670b35SErik Nordmark return (IPVL_UNICAST_UP); 19037c478bd9Sstevel@tonic-gate } else if (IN6_IS_ADDR_MULTICAST(v6src)) { 1904bd670b35SErik Nordmark /* (4) bind to multicast address. */ 19057c478bd9Sstevel@tonic-gate if (src_ire != NULL) 19067c478bd9Sstevel@tonic-gate ire_refrele(src_ire); 19077c478bd9Sstevel@tonic-gate 19087c478bd9Sstevel@tonic-gate /* 1909bd670b35SErik Nordmark * Note: caller should take IPV6_MULTICAST_IF 1910bd670b35SErik Nordmark * into account when selecting a real source address. 19117c478bd9Sstevel@tonic-gate */ 1912bd670b35SErik Nordmark if (allow_mcbc) 1913bd670b35SErik Nordmark return (IPVL_MCAST); 1914bd670b35SErik Nordmark else 1915bd670b35SErik Nordmark return (IPVL_BAD); 19167c478bd9Sstevel@tonic-gate } else { 19177c478bd9Sstevel@tonic-gate ipif_t *ipif; 19187c478bd9Sstevel@tonic-gate 19197c478bd9Sstevel@tonic-gate /* 1920bd670b35SErik Nordmark * (3) Bind to address of local DOWN interface? 1921bd670b35SErik Nordmark * (ipif_lookup_addr() looks up all interfaces 1922bd670b35SErik Nordmark * but we do not get here for UP interfaces 1923bd670b35SErik Nordmark * - case (2) above) 19247c478bd9Sstevel@tonic-gate */ 1925bd670b35SErik Nordmark if (src_ire != NULL) 1926bd670b35SErik Nordmark ire_refrele(src_ire); 1927bd670b35SErik Nordmark 1928bd670b35SErik Nordmark ipif = ipif_lookup_addr_v6(v6src, NULL, zoneid, ipst); 1929bd670b35SErik Nordmark if (ipif == NULL) 1930bd670b35SErik Nordmark return (IPVL_BAD); 1931bd670b35SErik Nordmark 1932bd670b35SErik Nordmark /* Not a useful source? */ 1933bd670b35SErik Nordmark if (ipif->ipif_flags & (IPIF_NOLOCAL | IPIF_ANYCAST)) { 19347c478bd9Sstevel@tonic-gate ipif_refrele(ipif); 1935bd670b35SErik Nordmark return (IPVL_BAD); 19367c478bd9Sstevel@tonic-gate } 19377c478bd9Sstevel@tonic-gate ipif_refrele(ipif); 1938bd670b35SErik Nordmark return (IPVL_UNICAST_DOWN); 1939bd670b35SErik Nordmark } 1940bd670b35SErik Nordmark } 1941bd670b35SErik Nordmark 19427c478bd9Sstevel@tonic-gate /* 1943bd670b35SErik Nordmark * Verify that both the source and destination addresses are valid. If 1944bd670b35SErik Nordmark * IPDF_VERIFY_DST is not set, then the destination address may be unreachable, 1945bd670b35SErik Nordmark * i.e. have no route to it. Protocols like TCP want to verify destination 1946bd670b35SErik Nordmark * reachability, while tunnels do not. 19477c478bd9Sstevel@tonic-gate * 1948bd670b35SErik Nordmark * Determine the route, the interface, and (optionally) the source address 1949bd670b35SErik Nordmark * to use to reach a given destination. 1950bd670b35SErik Nordmark * Note that we allow connect to broadcast and multicast addresses when 1951bd670b35SErik Nordmark * IPDF_ALLOW_MCBC is set. 1952bd670b35SErik Nordmark * first_hop and dst_addr are normally the same, but if source routing 1953bd670b35SErik Nordmark * they will differ; in that case the first_hop is what we'll use for the 1954bd670b35SErik Nordmark * routing lookup but the dce and label checks will be done on dst_addr, 1955bd670b35SErik Nordmark * 1956bd670b35SErik Nordmark * If uinfo is set, then we fill in the best available information 1957bd670b35SErik Nordmark * we have for the destination. This is based on (in priority order) any 1958bd670b35SErik Nordmark * metrics and path MTU stored in a dce_t, route metrics, and finally the 1959*1eee170aSErik Nordmark * ill_mtu/ill_mc_mtu. 1960bd670b35SErik Nordmark * 1961bd670b35SErik Nordmark * Tsol note: If we have a source route then dst_addr != firsthop. But we 1962bd670b35SErik Nordmark * always do the label check on dst_addr. 1963bd670b35SErik Nordmark * 1964bd670b35SErik Nordmark * Assumes that the caller has set ixa_scopeid for link-local communication. 19657c478bd9Sstevel@tonic-gate */ 1966bd670b35SErik Nordmark int 1967bd670b35SErik Nordmark ip_set_destination_v6(in6_addr_t *src_addrp, const in6_addr_t *dst_addr, 1968bd670b35SErik Nordmark const in6_addr_t *firsthop, ip_xmit_attr_t *ixa, iulp_t *uinfo, 1969bd670b35SErik Nordmark uint32_t flags, uint_t mac_mode) 1970bd670b35SErik Nordmark { 1971bd670b35SErik Nordmark ire_t *ire; 1972bd670b35SErik Nordmark int error = 0; 1973bd670b35SErik Nordmark in6_addr_t setsrc; /* RTF_SETSRC */ 1974bd670b35SErik Nordmark zoneid_t zoneid = ixa->ixa_zoneid; /* Honors SO_ALLZONES */ 1975bd670b35SErik Nordmark ip_stack_t *ipst = ixa->ixa_ipst; 1976bd670b35SErik Nordmark dce_t *dce; 1977bd670b35SErik Nordmark uint_t pmtu; 1978bd670b35SErik Nordmark uint_t ifindex; 1979bd670b35SErik Nordmark uint_t generation; 1980bd670b35SErik Nordmark nce_t *nce; 1981bd670b35SErik Nordmark ill_t *ill = NULL; 1982bd670b35SErik Nordmark boolean_t multirt = B_FALSE; 1983bd670b35SErik Nordmark 1984bd670b35SErik Nordmark ASSERT(!IN6_IS_ADDR_V4MAPPED(dst_addr)); 1985bd670b35SErik Nordmark 1986bd670b35SErik Nordmark ASSERT(!(ixa->ixa_flags & IXAF_IS_IPV4)); 1987bd670b35SErik Nordmark 1988bd670b35SErik Nordmark /* 1989bd670b35SErik Nordmark * We never send to zero; the ULPs map it to the loopback address. 1990bd670b35SErik Nordmark * We can't allow it since we use zero to mean unitialized in some 1991bd670b35SErik Nordmark * places. 1992bd670b35SErik Nordmark */ 1993bd670b35SErik Nordmark ASSERT(!IN6_IS_ADDR_UNSPECIFIED(dst_addr)); 1994bd670b35SErik Nordmark 1995bd670b35SErik Nordmark if (is_system_labeled()) { 1996bd670b35SErik Nordmark ts_label_t *tsl = NULL; 1997bd670b35SErik Nordmark 1998bd670b35SErik Nordmark error = tsol_check_dest(ixa->ixa_tsl, dst_addr, IPV6_VERSION, 1999bd670b35SErik Nordmark mac_mode, (flags & IPDF_ZONE_IS_GLOBAL) != 0, &tsl); 2000bd670b35SErik Nordmark if (error != 0) 2001bd670b35SErik Nordmark return (error); 2002bd670b35SErik Nordmark if (tsl != NULL) { 2003bd670b35SErik Nordmark /* Update the label */ 2004bd670b35SErik Nordmark ip_xmit_attr_replace_tsl(ixa, tsl); 20057c478bd9Sstevel@tonic-gate } 20067c478bd9Sstevel@tonic-gate } 2007bd670b35SErik Nordmark 2008bd670b35SErik Nordmark setsrc = ipv6_all_zeros; 2009bd670b35SErik Nordmark /* 2010bd670b35SErik Nordmark * Select a route; For IPMP interfaces, we would only select 2011bd670b35SErik Nordmark * a "hidden" route (i.e., going through a specific under_ill) 2012bd670b35SErik Nordmark * if ixa_ifindex has been specified. 2013bd670b35SErik Nordmark */ 201444b099c4SSowmini Varadhan ire = ip_select_route_v6(firsthop, *src_addrp, ixa, &generation, 201544b099c4SSowmini Varadhan &setsrc, &error, &multirt); 2016bd670b35SErik Nordmark ASSERT(ire != NULL); /* IRE_NOROUTE if none found */ 2017bd670b35SErik Nordmark if (error != 0) 20187c478bd9Sstevel@tonic-gate goto bad_addr; 20197c478bd9Sstevel@tonic-gate 20207c478bd9Sstevel@tonic-gate /* 2021bd670b35SErik Nordmark * ire can't be a broadcast or multicast unless IPDF_ALLOW_MCBC is set. 2022bd670b35SErik Nordmark * If IPDF_VERIFY_DST is set, the destination must be reachable. 2023bd670b35SErik Nordmark * Otherwise the destination needn't be reachable. 2024bd670b35SErik Nordmark * 2025bd670b35SErik Nordmark * If we match on a reject or black hole, then we've got a 2026bd670b35SErik Nordmark * local failure. May as well fail out the connect() attempt, 2027bd670b35SErik Nordmark * since it's never going to succeed. 20287c478bd9Sstevel@tonic-gate */ 2029bd670b35SErik Nordmark if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) { 20309a09d68dSja97890 /* 2031bd670b35SErik Nordmark * If we're verifying destination reachability, we always want 2032bd670b35SErik Nordmark * to complain here. 2033bd670b35SErik Nordmark * 2034bd670b35SErik Nordmark * If we're not verifying destination reachability but the 2035bd670b35SErik Nordmark * destination has a route, we still want to fail on the 2036bd670b35SErik Nordmark * temporary address and broadcast address tests. 2037bd670b35SErik Nordmark * 2038bd670b35SErik Nordmark * In both cases do we let the code continue so some reasonable 2039bd670b35SErik Nordmark * information is returned to the caller. That enables the 2040bd670b35SErik Nordmark * caller to use (and even cache) the IRE. conn_ip_ouput will 2041bd670b35SErik Nordmark * use the generation mismatch path to check for the unreachable 2042bd670b35SErik Nordmark * case thereby avoiding any specific check in the main path. 20439a09d68dSja97890 */ 2044bd670b35SErik Nordmark ASSERT(generation == IRE_GENERATION_VERIFY); 2045bd670b35SErik Nordmark if (flags & IPDF_VERIFY_DST) { 20467c478bd9Sstevel@tonic-gate /* 2047bd670b35SErik Nordmark * Set errno but continue to set up ixa_ire to be 2048bd670b35SErik Nordmark * the RTF_REJECT|RTF_BLACKHOLE IRE. 2049bd670b35SErik Nordmark * That allows callers to use ip_output to get an 2050bd670b35SErik Nordmark * ICMP error back. 20517c478bd9Sstevel@tonic-gate */ 2052bd670b35SErik Nordmark if (!(ire->ire_type & IRE_HOST)) 20537c478bd9Sstevel@tonic-gate error = ENETUNREACH; 20547c478bd9Sstevel@tonic-gate else 20557c478bd9Sstevel@tonic-gate error = EHOSTUNREACH; 20567c478bd9Sstevel@tonic-gate } 20577c478bd9Sstevel@tonic-gate } 2058bd670b35SErik Nordmark 2059bd670b35SErik Nordmark if ((ire->ire_type & (IRE_BROADCAST|IRE_MULTICAST)) && 2060bd670b35SErik Nordmark !(flags & IPDF_ALLOW_MCBC)) { 2061bd670b35SErik Nordmark ire_refrele(ire); 2062bd670b35SErik Nordmark ire = ire_reject(ipst, B_FALSE); 2063bd670b35SErik Nordmark generation = IRE_GENERATION_VERIFY; 2064bd670b35SErik Nordmark error = ENETUNREACH; 2065bd670b35SErik Nordmark } 2066bd670b35SErik Nordmark 2067bd670b35SErik Nordmark /* Cache things */ 2068bd670b35SErik Nordmark if (ixa->ixa_ire != NULL) 2069bd670b35SErik Nordmark ire_refrele_notr(ixa->ixa_ire); 2070bd670b35SErik Nordmark #ifdef DEBUG 2071bd670b35SErik Nordmark ire_refhold_notr(ire); 2072bd670b35SErik Nordmark ire_refrele(ire); 2073bd670b35SErik Nordmark #endif 2074bd670b35SErik Nordmark ixa->ixa_ire = ire; 2075bd670b35SErik Nordmark ixa->ixa_ire_generation = generation; 2076bd670b35SErik Nordmark 2077bd670b35SErik Nordmark /* 207853287767SSowmini Varadhan * Ensure that ixa_dce is always set any time that ixa_ire is set, 207953287767SSowmini Varadhan * since some callers will send a packet to conn_ip_output() even if 208053287767SSowmini Varadhan * there's an error. 208153287767SSowmini Varadhan */ 208253287767SSowmini Varadhan ifindex = 0; 208353287767SSowmini Varadhan if (IN6_IS_ADDR_LINKSCOPE(dst_addr)) { 208453287767SSowmini Varadhan /* If we are creating a DCE we'd better have an ifindex */ 208553287767SSowmini Varadhan if (ill != NULL) 208653287767SSowmini Varadhan ifindex = ill->ill_phyint->phyint_ifindex; 208753287767SSowmini Varadhan else 208853287767SSowmini Varadhan flags &= ~IPDF_UNIQUE_DCE; 208953287767SSowmini Varadhan } 209053287767SSowmini Varadhan 209153287767SSowmini Varadhan if (flags & IPDF_UNIQUE_DCE) { 209253287767SSowmini Varadhan /* Fallback to the default dce if allocation fails */ 209353287767SSowmini Varadhan dce = dce_lookup_and_add_v6(dst_addr, ifindex, ipst); 209453287767SSowmini Varadhan if (dce != NULL) { 209553287767SSowmini Varadhan generation = dce->dce_generation; 209653287767SSowmini Varadhan } else { 209753287767SSowmini Varadhan dce = dce_lookup_v6(dst_addr, ifindex, ipst, 209853287767SSowmini Varadhan &generation); 209953287767SSowmini Varadhan } 210053287767SSowmini Varadhan } else { 210153287767SSowmini Varadhan dce = dce_lookup_v6(dst_addr, ifindex, ipst, &generation); 210253287767SSowmini Varadhan } 210353287767SSowmini Varadhan ASSERT(dce != NULL); 210453287767SSowmini Varadhan if (ixa->ixa_dce != NULL) 210553287767SSowmini Varadhan dce_refrele_notr(ixa->ixa_dce); 210653287767SSowmini Varadhan #ifdef DEBUG 210753287767SSowmini Varadhan dce_refhold_notr(dce); 210853287767SSowmini Varadhan dce_refrele(dce); 210953287767SSowmini Varadhan #endif 211053287767SSowmini Varadhan ixa->ixa_dce = dce; 211153287767SSowmini Varadhan ixa->ixa_dce_generation = generation; 211253287767SSowmini Varadhan 211353287767SSowmini Varadhan 211453287767SSowmini Varadhan /* 2115bd670b35SErik Nordmark * For multicast with multirt we have a flag passed back from 2116bd670b35SErik Nordmark * ire_lookup_multi_ill_v6 since we don't have an IRE for each 2117bd670b35SErik Nordmark * possible multicast address. 2118bd670b35SErik Nordmark * We also need a flag for multicast since we can't check 2119bd670b35SErik Nordmark * whether RTF_MULTIRT is set in ixa_ire for multicast. 2120bd670b35SErik Nordmark */ 2121bd670b35SErik Nordmark if (multirt) { 2122bd670b35SErik Nordmark ixa->ixa_postfragfn = ip_postfrag_multirt_v6; 2123bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_MULTIRT_MULTICAST; 21247c478bd9Sstevel@tonic-gate } else { 2125bd670b35SErik Nordmark ixa->ixa_postfragfn = ire->ire_postfragfn; 2126bd670b35SErik Nordmark ixa->ixa_flags &= ~IXAF_MULTIRT_MULTICAST; 21277c478bd9Sstevel@tonic-gate } 2128bd670b35SErik Nordmark if (!(ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE))) { 2129bd670b35SErik Nordmark /* Get an nce to cache. */ 2130bd670b35SErik Nordmark nce = ire_to_nce(ire, NULL, firsthop); 2131bd670b35SErik Nordmark if (nce == NULL) { 2132bd670b35SErik Nordmark /* Allocation failure? */ 2133bd670b35SErik Nordmark ixa->ixa_ire_generation = IRE_GENERATION_VERIFY; 21347c478bd9Sstevel@tonic-gate } else { 2135bd670b35SErik Nordmark if (ixa->ixa_nce != NULL) 2136bd670b35SErik Nordmark nce_refrele(ixa->ixa_nce); 2137bd670b35SErik Nordmark ixa->ixa_nce = nce; 21387c478bd9Sstevel@tonic-gate } 21397c478bd9Sstevel@tonic-gate } 21407c478bd9Sstevel@tonic-gate 21417c478bd9Sstevel@tonic-gate /* 21427c478bd9Sstevel@tonic-gate * If the source address is a loopback address, the 21437c478bd9Sstevel@tonic-gate * destination had best be local or multicast. 2144bd670b35SErik Nordmark * If we are sending to an IRE_LOCAL using a loopback source then 2145bd670b35SErik Nordmark * it had better be the same zoneid. 21467c478bd9Sstevel@tonic-gate */ 2147bd670b35SErik Nordmark if (IN6_IS_ADDR_LOOPBACK(src_addrp)) { 2148bd670b35SErik Nordmark if ((ire->ire_type & IRE_LOCAL) && ire->ire_zoneid != zoneid) { 2149bd670b35SErik Nordmark ire = NULL; /* Stored in ixa_ire */ 2150bd670b35SErik Nordmark error = EADDRNOTAVAIL; 21517c478bd9Sstevel@tonic-gate goto bad_addr; 21527c478bd9Sstevel@tonic-gate } 2153bd670b35SErik Nordmark if (!(ire->ire_type & (IRE_LOOPBACK|IRE_LOCAL|IRE_MULTICAST))) { 2154bd670b35SErik Nordmark ire = NULL; /* Stored in ixa_ire */ 2155bd670b35SErik Nordmark error = EADDRNOTAVAIL; 21567c478bd9Sstevel@tonic-gate goto bad_addr; 21577c478bd9Sstevel@tonic-gate } 21587c478bd9Sstevel@tonic-gate } 21597c478bd9Sstevel@tonic-gate 21607c478bd9Sstevel@tonic-gate /* 2161bd670b35SErik Nordmark * Does the caller want us to pick a source address? 21627c478bd9Sstevel@tonic-gate */ 2163bd670b35SErik Nordmark if (flags & IPDF_SELECT_SRC) { 2164bd670b35SErik Nordmark in6_addr_t src_addr; 21657c478bd9Sstevel@tonic-gate 2166188e1664SErik Nordmark /* 2167188e1664SErik Nordmark * We use use ire_nexthop_ill to avoid the under ipmp 2168188e1664SErik Nordmark * interface for source address selection. Note that for ipmp 2169188e1664SErik Nordmark * probe packets, ixa_ifindex would have been specified, and 2170188e1664SErik Nordmark * the ip_select_route() invocation would have picked an ire 2171188e1664SErik Nordmark * will ire_ill pointing at an under interface. 2172188e1664SErik Nordmark */ 2173188e1664SErik Nordmark ill = ire_nexthop_ill(ire); 2174188e1664SErik Nordmark 2175bd670b35SErik Nordmark /* If unreachable we have no ill but need some source */ 2176bd670b35SErik Nordmark if (ill == NULL) { 2177bd670b35SErik Nordmark src_addr = ipv6_loopback; 2178bd670b35SErik Nordmark /* Make sure we look for a better source address */ 2179bd670b35SErik Nordmark generation = SRC_GENERATION_VERIFY; 21800f1702c5SYu Xiangning } else { 2181bd670b35SErik Nordmark error = ip_select_source_v6(ill, &setsrc, dst_addr, 2182bd670b35SErik Nordmark zoneid, ipst, B_FALSE, ixa->ixa_src_preferences, 2183bd670b35SErik Nordmark &src_addr, &generation, NULL); 2184bd670b35SErik Nordmark if (error != 0) { 2185bd670b35SErik Nordmark ire = NULL; /* Stored in ixa_ire */ 21860f1702c5SYu Xiangning goto bad_addr; 21870f1702c5SYu Xiangning } 2188bd670b35SErik Nordmark } 21890f1702c5SYu Xiangning 2190bd670b35SErik Nordmark /* 2191bd670b35SErik Nordmark * We allow the source address to to down. 2192bd670b35SErik Nordmark * However, we check that we don't use the loopback address 2193bd670b35SErik Nordmark * as a source when sending out on the wire. 2194bd670b35SErik Nordmark */ 2195bd670b35SErik Nordmark if (IN6_IS_ADDR_LOOPBACK(&src_addr) && 2196bd670b35SErik Nordmark !(ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK|IRE_MULTICAST)) && 2197bd670b35SErik Nordmark !(ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE))) { 2198bd670b35SErik Nordmark ire = NULL; /* Stored in ixa_ire */ 2199bd670b35SErik Nordmark error = EADDRNOTAVAIL; 22000f1702c5SYu Xiangning goto bad_addr; 22010f1702c5SYu Xiangning } 22020f1702c5SYu Xiangning 2203bd670b35SErik Nordmark *src_addrp = src_addr; 2204bd670b35SErik Nordmark ixa->ixa_src_generation = generation; 22050f1702c5SYu Xiangning } 22060f1702c5SYu Xiangning 22070f1702c5SYu Xiangning /* 2208bd670b35SErik Nordmark * Make sure we don't leave an unreachable ixa_nce in place 2209bd670b35SErik Nordmark * since ip_select_route is used when we unplumb i.e., remove 2210bd670b35SErik Nordmark * references on ixa_ire, ixa_nce, and ixa_dce. 22117c478bd9Sstevel@tonic-gate */ 2212bd670b35SErik Nordmark nce = ixa->ixa_nce; 2213bd670b35SErik Nordmark if (nce != NULL && nce->nce_is_condemned) { 2214bd670b35SErik Nordmark nce_refrele(nce); 2215bd670b35SErik Nordmark ixa->ixa_nce = NULL; 2216bd670b35SErik Nordmark ixa->ixa_ire_generation = IRE_GENERATION_VERIFY; 22177c478bd9Sstevel@tonic-gate } 22187c478bd9Sstevel@tonic-gate 2219bd670b35SErik Nordmark /* 2220bd670b35SErik Nordmark * Note that IPv6 multicast supports PMTU discovery unlike IPv4 2221bd670b35SErik Nordmark * multicast. But pmtu discovery is only enabled for connected 2222bd670b35SErik Nordmark * sockets in general. 2223bd670b35SErik Nordmark */ 2224bd670b35SErik Nordmark 2225bd670b35SErik Nordmark /* 2226bd670b35SErik Nordmark * Set initial value for fragmentation limit. Either conn_ip_output 2227bd670b35SErik Nordmark * or ULP might updates it when there are routing changes. 2228bd670b35SErik Nordmark * Handles a NULL ixa_ire->ire_ill or a NULL ixa_nce for RTF_REJECT. 2229bd670b35SErik Nordmark */ 2230bd670b35SErik Nordmark pmtu = ip_get_pmtu(ixa); 2231bd670b35SErik Nordmark ixa->ixa_fragsize = pmtu; 2232bd670b35SErik Nordmark /* Make sure ixa_fragsize and ixa_pmtu remain identical */ 2233bd670b35SErik Nordmark if (ixa->ixa_flags & IXAF_VERIFY_PMTU) 2234bd670b35SErik Nordmark ixa->ixa_pmtu = pmtu; 2235bd670b35SErik Nordmark 2236bd670b35SErik Nordmark /* 2237bd670b35SErik Nordmark * Extract information useful for some transports. 2238bd670b35SErik Nordmark * First we look for DCE metrics. Then we take what we have in 2239bd670b35SErik Nordmark * the metrics in the route, where the offlink is used if we have 2240bd670b35SErik Nordmark * one. 2241bd670b35SErik Nordmark */ 2242bd670b35SErik Nordmark if (uinfo != NULL) { 2243bd670b35SErik Nordmark bzero(uinfo, sizeof (*uinfo)); 2244bd670b35SErik Nordmark 2245bd670b35SErik Nordmark if (dce->dce_flags & DCEF_UINFO) 2246bd670b35SErik Nordmark *uinfo = dce->dce_uinfo; 2247bd670b35SErik Nordmark 2248bd670b35SErik Nordmark rts_merge_metrics(uinfo, &ire->ire_metrics); 2249bd670b35SErik Nordmark 2250bd670b35SErik Nordmark /* Allow ire_metrics to decrease the path MTU from above */ 2251bd670b35SErik Nordmark if (uinfo->iulp_mtu == 0 || uinfo->iulp_mtu > pmtu) 2252bd670b35SErik Nordmark uinfo->iulp_mtu = pmtu; 2253bd670b35SErik Nordmark 2254bd670b35SErik Nordmark uinfo->iulp_localnet = (ire->ire_type & IRE_ONLINK) != 0; 2255bd670b35SErik Nordmark uinfo->iulp_loopback = (ire->ire_type & IRE_LOOPBACK) != 0; 2256bd670b35SErik Nordmark uinfo->iulp_local = (ire->ire_type & IRE_LOCAL) != 0; 2257bd670b35SErik Nordmark } 2258bd670b35SErik Nordmark 2259bd670b35SErik Nordmark if (ill != NULL) 2260bd670b35SErik Nordmark ill_refrele(ill); 2261bd670b35SErik Nordmark 2262bd670b35SErik Nordmark return (error); 2263bd670b35SErik Nordmark 2264bd670b35SErik Nordmark bad_addr: 2265bd670b35SErik Nordmark if (ire != NULL) 2266bd670b35SErik Nordmark ire_refrele(ire); 2267bd670b35SErik Nordmark 2268bd670b35SErik Nordmark if (ill != NULL) 2269bd670b35SErik Nordmark ill_refrele(ill); 2270bd670b35SErik Nordmark 2271bd670b35SErik Nordmark /* 2272bd670b35SErik Nordmark * Make sure we don't leave an unreachable ixa_nce in place 2273bd670b35SErik Nordmark * since ip_select_route is used when we unplumb i.e., remove 2274bd670b35SErik Nordmark * references on ixa_ire, ixa_nce, and ixa_dce. 2275bd670b35SErik Nordmark */ 2276bd670b35SErik Nordmark nce = ixa->ixa_nce; 2277bd670b35SErik Nordmark if (nce != NULL && nce->nce_is_condemned) { 2278bd670b35SErik Nordmark nce_refrele(nce); 2279bd670b35SErik Nordmark ixa->ixa_nce = NULL; 2280bd670b35SErik Nordmark ixa->ixa_ire_generation = IRE_GENERATION_VERIFY; 2281bd670b35SErik Nordmark } 2282bd670b35SErik Nordmark 2283bd670b35SErik Nordmark return (error); 22847c478bd9Sstevel@tonic-gate } 22857c478bd9Sstevel@tonic-gate 22867c478bd9Sstevel@tonic-gate /* 22877c478bd9Sstevel@tonic-gate * Handle protocols with which IP is less intimate. There 22887c478bd9Sstevel@tonic-gate * can be more than one stream bound to a particular 22897c478bd9Sstevel@tonic-gate * protocol. When this is the case, normally each one gets a copy 22907c478bd9Sstevel@tonic-gate * of any incoming packets. 22917c478bd9Sstevel@tonic-gate * 22927c478bd9Sstevel@tonic-gate * Zones notes: 2293bd670b35SErik Nordmark * Packets will be distributed to conns in all zones. This is really only 22947c478bd9Sstevel@tonic-gate * useful for ICMPv6 as only applications in the global zone can create raw 22957c478bd9Sstevel@tonic-gate * sockets for other protocols. 22967c478bd9Sstevel@tonic-gate */ 2297bd670b35SErik Nordmark void 2298bd670b35SErik Nordmark ip_fanout_proto_v6(mblk_t *mp, ip6_t *ip6h, ip_recv_attr_t *ira) 22997c478bd9Sstevel@tonic-gate { 2300bd670b35SErik Nordmark mblk_t *mp1; 2301bd670b35SErik Nordmark in6_addr_t laddr = ip6h->ip6_dst; 23027c478bd9Sstevel@tonic-gate conn_t *connp, *first_connp, *next_connp; 23037c478bd9Sstevel@tonic-gate connf_t *connfp; 2304bd670b35SErik Nordmark ill_t *ill = ira->ira_ill; 2305bd670b35SErik Nordmark ip_stack_t *ipst = ill->ill_ipst; 23067c478bd9Sstevel@tonic-gate 2307bd670b35SErik Nordmark connfp = &ipst->ips_ipcl_proto_fanout_v6[ira->ira_protocol]; 23087c478bd9Sstevel@tonic-gate mutex_enter(&connfp->connf_lock); 23097c478bd9Sstevel@tonic-gate connp = connfp->connf_head; 23107c478bd9Sstevel@tonic-gate for (connp = connfp->connf_head; connp != NULL; 23117c478bd9Sstevel@tonic-gate connp = connp->conn_next) { 2312bd670b35SErik Nordmark /* Note: IPCL_PROTO_MATCH_V6 includes conn_wantpacket */ 2313bd670b35SErik Nordmark if (IPCL_PROTO_MATCH_V6(connp, ira, ip6h) && 2314bd670b35SErik Nordmark (!(ira->ira_flags & IRAF_SYSTEM_LABELED) || 2315bd670b35SErik Nordmark tsol_receive_local(mp, &laddr, IPV6_VERSION, ira, connp))) 23167c478bd9Sstevel@tonic-gate break; 23177c478bd9Sstevel@tonic-gate } 23187c478bd9Sstevel@tonic-gate 23190f1702c5SYu Xiangning if (connp == NULL) { 23207c478bd9Sstevel@tonic-gate /* 23217c478bd9Sstevel@tonic-gate * No one bound to this port. Is 23227c478bd9Sstevel@tonic-gate * there a client that wants all 23237c478bd9Sstevel@tonic-gate * unclaimed datagrams? 23247c478bd9Sstevel@tonic-gate */ 23257c478bd9Sstevel@tonic-gate mutex_exit(&connfp->connf_lock); 2326bd670b35SErik Nordmark ip_fanout_send_icmp_v6(mp, ICMP6_PARAM_PROB, 2327bd670b35SErik Nordmark ICMP6_PARAMPROB_NEXTHEADER, ira); 23287c478bd9Sstevel@tonic-gate return; 23297c478bd9Sstevel@tonic-gate } 23307c478bd9Sstevel@tonic-gate 2331bd670b35SErik Nordmark ASSERT(IPCL_IS_NONSTR(connp) || connp->conn_rq != NULL); 23320f1702c5SYu Xiangning 23337c478bd9Sstevel@tonic-gate CONN_INC_REF(connp); 23347c478bd9Sstevel@tonic-gate first_connp = connp; 23357c478bd9Sstevel@tonic-gate 23367c478bd9Sstevel@tonic-gate /* 23377c478bd9Sstevel@tonic-gate * XXX: Fix the multiple protocol listeners case. We should not 2338bd670b35SErik Nordmark * be walking the conn->conn_next list here. 23397c478bd9Sstevel@tonic-gate */ 23407c478bd9Sstevel@tonic-gate connp = connp->conn_next; 23417c478bd9Sstevel@tonic-gate for (;;) { 23427c478bd9Sstevel@tonic-gate while (connp != NULL) { 2343bd670b35SErik Nordmark /* Note: IPCL_PROTO_MATCH_V6 includes conn_wantpacket */ 2344bd670b35SErik Nordmark if (IPCL_PROTO_MATCH_V6(connp, ira, ip6h) && 2345bd670b35SErik Nordmark (!(ira->ira_flags & IRAF_SYSTEM_LABELED) || 2346bd670b35SErik Nordmark tsol_receive_local(mp, &laddr, IPV6_VERSION, 2347bd670b35SErik Nordmark ira, connp))) 23487c478bd9Sstevel@tonic-gate break; 23497c478bd9Sstevel@tonic-gate connp = connp->conn_next; 23507c478bd9Sstevel@tonic-gate } 23517c478bd9Sstevel@tonic-gate 2352bd670b35SErik Nordmark if (connp == NULL) { 2353bd670b35SErik Nordmark /* No more interested clients */ 23547c478bd9Sstevel@tonic-gate connp = first_connp; 23557c478bd9Sstevel@tonic-gate break; 23567c478bd9Sstevel@tonic-gate } 2357bd670b35SErik Nordmark if (((mp1 = dupmsg(mp)) == NULL) && 2358bd670b35SErik Nordmark ((mp1 = copymsg(mp)) == NULL)) { 2359bd670b35SErik Nordmark /* Memory allocation failed */ 2360bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 2361bd670b35SErik Nordmark ip_drop_input("ipIfStatsInDiscards", mp, ill); 2362bd670b35SErik Nordmark connp = first_connp; 2363bd670b35SErik Nordmark break; 2364bd670b35SErik Nordmark } 2365bd670b35SErik Nordmark 23667c478bd9Sstevel@tonic-gate CONN_INC_REF(connp); 23677c478bd9Sstevel@tonic-gate mutex_exit(&connfp->connf_lock); 23687c478bd9Sstevel@tonic-gate 2369bd670b35SErik Nordmark ip_fanout_proto_conn(connp, mp1, NULL, (ip6_t *)mp1->b_rptr, 2370bd670b35SErik Nordmark ira); 23712b24ab6bSSebastien Roy 23727c478bd9Sstevel@tonic-gate mutex_enter(&connfp->connf_lock); 23737c478bd9Sstevel@tonic-gate /* Follow the next pointer before releasing the conn. */ 23747c478bd9Sstevel@tonic-gate next_connp = connp->conn_next; 23757c478bd9Sstevel@tonic-gate CONN_DEC_REF(connp); 23767c478bd9Sstevel@tonic-gate connp = next_connp; 23777c478bd9Sstevel@tonic-gate } 23787c478bd9Sstevel@tonic-gate 23797c478bd9Sstevel@tonic-gate /* Last one. Send it upstream. */ 23807c478bd9Sstevel@tonic-gate mutex_exit(&connfp->connf_lock); 23817c478bd9Sstevel@tonic-gate 2382bd670b35SErik Nordmark ip_fanout_proto_conn(connp, mp, NULL, ip6h, ira); 23837c478bd9Sstevel@tonic-gate 23847c478bd9Sstevel@tonic-gate CONN_DEC_REF(connp); 23857c478bd9Sstevel@tonic-gate } 23867c478bd9Sstevel@tonic-gate 23877c478bd9Sstevel@tonic-gate /* 2388bd670b35SErik Nordmark * Called when it is conceptually a ULP that would sent the packet 2389bd670b35SErik Nordmark * e.g., port unreachable and nexthdr unknown. Check that the packet 2390bd670b35SErik Nordmark * would have passed the IPsec global policy before sending the error. 2391bd670b35SErik Nordmark * 2392bd670b35SErik Nordmark * Send an ICMP error after patching up the packet appropriately. 2393bd670b35SErik Nordmark * Uses ip_drop_input and bumps the appropriate MIB. 2394bd670b35SErik Nordmark * For ICMP6_PARAMPROB_NEXTHEADER we determine the offset to use. 23957c478bd9Sstevel@tonic-gate */ 2396bd670b35SErik Nordmark void 2397bd670b35SErik Nordmark ip_fanout_send_icmp_v6(mblk_t *mp, uint_t icmp_type, uint8_t icmp_code, 2398bd670b35SErik Nordmark ip_recv_attr_t *ira) 23997c478bd9Sstevel@tonic-gate { 24007c478bd9Sstevel@tonic-gate ip6_t *ip6h; 24017c478bd9Sstevel@tonic-gate boolean_t secure; 2402bd670b35SErik Nordmark ill_t *ill = ira->ira_ill; 2403bd670b35SErik Nordmark ip_stack_t *ipst = ill->ill_ipst; 2404bd670b35SErik Nordmark netstack_t *ns = ipst->ips_netstack; 2405bd670b35SErik Nordmark ipsec_stack_t *ipss = ns->netstack_ipsec; 24067c478bd9Sstevel@tonic-gate 2407bd670b35SErik Nordmark secure = ira->ira_flags & IRAF_IPSEC_SECURE; 2408bd670b35SErik Nordmark 24097c478bd9Sstevel@tonic-gate /* 24107c478bd9Sstevel@tonic-gate * We are generating an icmp error for some inbound packet. 24117c478bd9Sstevel@tonic-gate * Called from all ip_fanout_(udp, tcp, proto) functions. 24127c478bd9Sstevel@tonic-gate * Before we generate an error, check with global policy 24137c478bd9Sstevel@tonic-gate * to see whether this is allowed to enter the system. As 24147c478bd9Sstevel@tonic-gate * there is no "conn", we are checking with global policy. 24157c478bd9Sstevel@tonic-gate */ 24167c478bd9Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 2417f4b3ec61Sdh155122 if (secure || ipss->ipsec_inbound_v6_policy_present) { 2418bd670b35SErik Nordmark mp = ipsec_check_global_policy(mp, NULL, NULL, ip6h, ira, ns); 2419bd670b35SErik Nordmark if (mp == NULL) 2420bd670b35SErik Nordmark return; 24217c478bd9Sstevel@tonic-gate } 24227c478bd9Sstevel@tonic-gate 2423bd670b35SErik Nordmark /* We never send errors for protocols that we do implement */ 2424bd670b35SErik Nordmark if (ira->ira_protocol == IPPROTO_ICMPV6) { 2425bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 2426bd670b35SErik Nordmark ip_drop_input("ip_fanout_send_icmp_v6", mp, ill); 2427bd670b35SErik Nordmark freemsg(mp); 2428bd670b35SErik Nordmark return; 2429bd670b35SErik Nordmark } 24307c478bd9Sstevel@tonic-gate 24317c478bd9Sstevel@tonic-gate switch (icmp_type) { 24327c478bd9Sstevel@tonic-gate case ICMP6_DST_UNREACH: 2433bd670b35SErik Nordmark ASSERT(icmp_code == ICMP6_DST_UNREACH_NOPORT); 2434bd670b35SErik Nordmark 2435bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, udpIfStatsNoPorts); 2436bd670b35SErik Nordmark ip_drop_input("ipIfStatsNoPorts", mp, ill); 2437bd670b35SErik Nordmark 2438bd670b35SErik Nordmark icmp_unreachable_v6(mp, icmp_code, B_FALSE, ira); 24397c478bd9Sstevel@tonic-gate break; 24407c478bd9Sstevel@tonic-gate case ICMP6_PARAM_PROB: 2441bd670b35SErik Nordmark ASSERT(icmp_code == ICMP6_PARAMPROB_NEXTHEADER); 2442bd670b35SErik Nordmark 2443bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsInUnknownProtos); 2444bd670b35SErik Nordmark ip_drop_input("ipIfStatsInUnknownProtos", mp, ill); 2445bd670b35SErik Nordmark 2446bd670b35SErik Nordmark /* Let the system determine the offset for this one */ 2447bd670b35SErik Nordmark icmp_param_problem_nexthdr_v6(mp, B_FALSE, ira); 24487c478bd9Sstevel@tonic-gate break; 24497c478bd9Sstevel@tonic-gate default: 24507c478bd9Sstevel@tonic-gate #ifdef DEBUG 24517c478bd9Sstevel@tonic-gate panic("ip_fanout_send_icmp_v6: wrong type"); 24527c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 24537c478bd9Sstevel@tonic-gate #else 2454bd670b35SErik Nordmark freemsg(mp); 24557c478bd9Sstevel@tonic-gate break; 24567c478bd9Sstevel@tonic-gate #endif 24577c478bd9Sstevel@tonic-gate } 24587c478bd9Sstevel@tonic-gate } 24597c478bd9Sstevel@tonic-gate 24607c478bd9Sstevel@tonic-gate /* 2461bd670b35SErik Nordmark * Fanout for UDP packets that are multicast or ICMP errors. 2462bd670b35SErik Nordmark * (Unicast fanout is handled in ip_input_v6.) 2463bd670b35SErik Nordmark * 2464bd670b35SErik Nordmark * If SO_REUSEADDR is set all multicast packets 2465bd670b35SErik Nordmark * will be delivered to all conns bound to the same port. 2466bd670b35SErik Nordmark * 24677c478bd9Sstevel@tonic-gate * Fanout for UDP packets. 24687c478bd9Sstevel@tonic-gate * The caller puts <fport, lport> in the ports parameter. 24697c478bd9Sstevel@tonic-gate * ire_type must be IRE_BROADCAST for multicast and broadcast packets. 24707c478bd9Sstevel@tonic-gate * 24717c478bd9Sstevel@tonic-gate * If SO_REUSEADDR is set all multicast and broadcast packets 2472bd670b35SErik Nordmark * will be delivered to all conns bound to the same port. 24737c478bd9Sstevel@tonic-gate * 24747c478bd9Sstevel@tonic-gate * Zones notes: 2475bd670b35SErik Nordmark * Earlier in ip_input on a system with multiple shared-IP zones we 2476bd670b35SErik Nordmark * duplicate the multicast and broadcast packets and send them up 2477bd670b35SErik Nordmark * with each explicit zoneid that exists on that ill. 2478bd670b35SErik Nordmark * This means that here we can match the zoneid with SO_ALLZONES being special. 24797c478bd9Sstevel@tonic-gate */ 2480bd670b35SErik Nordmark void 2481bd670b35SErik Nordmark ip_fanout_udp_multi_v6(mblk_t *mp, ip6_t *ip6h, uint16_t lport, uint16_t fport, 2482bd670b35SErik Nordmark ip_recv_attr_t *ira) 24837c478bd9Sstevel@tonic-gate { 2484bd670b35SErik Nordmark in6_addr_t laddr; 24857c478bd9Sstevel@tonic-gate conn_t *connp; 24867c478bd9Sstevel@tonic-gate connf_t *connfp; 2487bd670b35SErik Nordmark in6_addr_t faddr; 2488bd670b35SErik Nordmark ill_t *ill = ira->ira_ill; 2489bd670b35SErik Nordmark ip_stack_t *ipst = ill->ill_ipst; 24907c478bd9Sstevel@tonic-gate 2491bd670b35SErik Nordmark ASSERT(ira->ira_flags & (IRAF_MULTIBROADCAST|IRAF_ICMP_ERROR)); 24927c478bd9Sstevel@tonic-gate 2493bd670b35SErik Nordmark laddr = ip6h->ip6_dst; 2494bd670b35SErik Nordmark faddr = ip6h->ip6_src; 249545916cd2Sjpk 24967c478bd9Sstevel@tonic-gate /* Attempt to find a client stream based on destination port. */ 2497bd670b35SErik Nordmark connfp = &ipst->ips_ipcl_udp_fanout[IPCL_UDP_HASH(lport, ipst)]; 24987c478bd9Sstevel@tonic-gate mutex_enter(&connfp->connf_lock); 24997c478bd9Sstevel@tonic-gate connp = connfp->connf_head; 25007c478bd9Sstevel@tonic-gate while (connp != NULL) { 2501bd670b35SErik Nordmark if ((IPCL_UDP_MATCH_V6(connp, lport, laddr, fport, faddr)) && 2502bd670b35SErik Nordmark conn_wantpacket_v6(connp, ira, ip6h) && 2503bd670b35SErik Nordmark (!(ira->ira_flags & IRAF_SYSTEM_LABELED) || 2504bd670b35SErik Nordmark tsol_receive_local(mp, &laddr, IPV6_VERSION, ira, connp))) 25057c478bd9Sstevel@tonic-gate break; 25067c478bd9Sstevel@tonic-gate connp = connp->conn_next; 25077c478bd9Sstevel@tonic-gate } 25087c478bd9Sstevel@tonic-gate 2509bd670b35SErik Nordmark if (connp == NULL) 25107c478bd9Sstevel@tonic-gate goto notfound; 25117c478bd9Sstevel@tonic-gate 25127c478bd9Sstevel@tonic-gate CONN_INC_REF(connp); 2513bd670b35SErik Nordmark 2514bd670b35SErik Nordmark if (connp->conn_reuseaddr) { 2515bd670b35SErik Nordmark conn_t *first_connp = connp; 2516bd670b35SErik Nordmark conn_t *next_connp; 2517bd670b35SErik Nordmark mblk_t *mp1; 2518bd670b35SErik Nordmark 25197c478bd9Sstevel@tonic-gate connp = connp->conn_next; 25207c478bd9Sstevel@tonic-gate for (;;) { 25217c478bd9Sstevel@tonic-gate while (connp != NULL) { 2522bd670b35SErik Nordmark if (IPCL_UDP_MATCH_V6(connp, lport, laddr, 2523bd670b35SErik Nordmark fport, faddr) && 2524bd670b35SErik Nordmark conn_wantpacket_v6(connp, ira, ip6h) && 2525bd670b35SErik Nordmark (!(ira->ira_flags & IRAF_SYSTEM_LABELED) || 2526bd670b35SErik Nordmark tsol_receive_local(mp, &laddr, IPV6_VERSION, 2527bd670b35SErik Nordmark ira, connp))) 25287c478bd9Sstevel@tonic-gate break; 25297c478bd9Sstevel@tonic-gate connp = connp->conn_next; 25307c478bd9Sstevel@tonic-gate } 2531bd670b35SErik Nordmark if (connp == NULL) { 2532bd670b35SErik Nordmark /* No more interested clients */ 2533bd670b35SErik Nordmark connp = first_connp; 25347c478bd9Sstevel@tonic-gate break; 25357c478bd9Sstevel@tonic-gate } 2536bd670b35SErik Nordmark if (((mp1 = dupmsg(mp)) == NULL) && 2537bd670b35SErik Nordmark ((mp1 = copymsg(mp)) == NULL)) { 2538bd670b35SErik Nordmark /* Memory allocation failed */ 2539bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 2540bd670b35SErik Nordmark ip_drop_input("ipIfStatsInDiscards", mp, ill); 2541bd670b35SErik Nordmark connp = first_connp; 2542bd670b35SErik Nordmark break; 2543bd670b35SErik Nordmark } 2544bd670b35SErik Nordmark 25457c478bd9Sstevel@tonic-gate CONN_INC_REF(connp); 25467c478bd9Sstevel@tonic-gate mutex_exit(&connfp->connf_lock); 25477c478bd9Sstevel@tonic-gate 2548bd670b35SErik Nordmark IP6_STAT(ipst, ip6_udp_fanmb); 2549bd670b35SErik Nordmark ip_fanout_udp_conn(connp, mp1, NULL, 2550bd670b35SErik Nordmark (ip6_t *)mp1->b_rptr, ira); 2551ff550d0eSmasputra 25527c478bd9Sstevel@tonic-gate mutex_enter(&connfp->connf_lock); 25537c478bd9Sstevel@tonic-gate /* Follow the next pointer before releasing the conn. */ 2554bd670b35SErik Nordmark next_connp = connp->conn_next; 2555f4b3ec61Sdh155122 IP6_STAT(ipst, ip6_udp_fanmb); 25567c478bd9Sstevel@tonic-gate CONN_DEC_REF(connp); 2557bd670b35SErik Nordmark connp = next_connp; 2558bd670b35SErik Nordmark } 25597c478bd9Sstevel@tonic-gate } 25607c478bd9Sstevel@tonic-gate 25617c478bd9Sstevel@tonic-gate /* Last one. Send it upstream. */ 25627c478bd9Sstevel@tonic-gate mutex_exit(&connfp->connf_lock); 25637c478bd9Sstevel@tonic-gate 2564f4b3ec61Sdh155122 IP6_STAT(ipst, ip6_udp_fanmb); 2565bd670b35SErik Nordmark ip_fanout_udp_conn(connp, mp, NULL, ip6h, ira); 25667c478bd9Sstevel@tonic-gate CONN_DEC_REF(connp); 25677c478bd9Sstevel@tonic-gate return; 25687c478bd9Sstevel@tonic-gate 25697c478bd9Sstevel@tonic-gate notfound: 25707c478bd9Sstevel@tonic-gate mutex_exit(&connfp->connf_lock); 25717c478bd9Sstevel@tonic-gate /* 25727c478bd9Sstevel@tonic-gate * No one bound to this port. Is 25737c478bd9Sstevel@tonic-gate * there a client that wants all 25747c478bd9Sstevel@tonic-gate * unclaimed datagrams? 25757c478bd9Sstevel@tonic-gate */ 2576f4b3ec61Sdh155122 if (ipst->ips_ipcl_proto_fanout_v6[IPPROTO_UDP].connf_head != NULL) { 2577bd670b35SErik Nordmark ASSERT(ira->ira_protocol == IPPROTO_UDP); 2578bd670b35SErik Nordmark ip_fanout_proto_v6(mp, ip6h, ira); 25797c478bd9Sstevel@tonic-gate } else { 2580bd670b35SErik Nordmark ip_fanout_send_icmp_v6(mp, ICMP6_DST_UNREACH, 2581bd670b35SErik Nordmark ICMP6_DST_UNREACH_NOPORT, ira); 25827c478bd9Sstevel@tonic-gate } 25837c478bd9Sstevel@tonic-gate } 25847c478bd9Sstevel@tonic-gate 25857c478bd9Sstevel@tonic-gate /* 25867c478bd9Sstevel@tonic-gate * int ip_find_hdr_v6() 25877c478bd9Sstevel@tonic-gate * 2588bd670b35SErik Nordmark * This routine is used by the upper layer protocols, iptun, and IPsec: 25897c478bd9Sstevel@tonic-gate * - Set extension header pointers to appropriate locations 25907c478bd9Sstevel@tonic-gate * - Determine IPv6 header length and return it 25917c478bd9Sstevel@tonic-gate * - Return a pointer to the last nexthdr value 25927c478bd9Sstevel@tonic-gate * 25937c478bd9Sstevel@tonic-gate * The caller must initialize ipp_fields. 2594bd670b35SErik Nordmark * The upper layer protocols normally set label_separate which makes the 2595bd670b35SErik Nordmark * routine put the TX label in ipp_label_v6. If this is not set then 2596bd670b35SErik Nordmark * the hop-by-hop options including the label are placed in ipp_hopopts. 25977c478bd9Sstevel@tonic-gate * 25987c478bd9Sstevel@tonic-gate * NOTE: If multiple extension headers of the same type are present, 25997c478bd9Sstevel@tonic-gate * ip_find_hdr_v6() will set the respective extension header pointers 26007c478bd9Sstevel@tonic-gate * to the first one that it encounters in the IPv6 header. It also 26017c478bd9Sstevel@tonic-gate * skips fragment headers. This routine deals with malformed packets 26027c478bd9Sstevel@tonic-gate * of various sorts in which case the returned length is up to the 26037c478bd9Sstevel@tonic-gate * malformed part. 26047c478bd9Sstevel@tonic-gate */ 26057c478bd9Sstevel@tonic-gate int 2606bd670b35SErik Nordmark ip_find_hdr_v6(mblk_t *mp, ip6_t *ip6h, boolean_t label_separate, ip_pkt_t *ipp, 2607bd670b35SErik Nordmark uint8_t *nexthdrp) 26087c478bd9Sstevel@tonic-gate { 26097c478bd9Sstevel@tonic-gate uint_t length, ehdrlen; 26107c478bd9Sstevel@tonic-gate uint8_t nexthdr; 26117c478bd9Sstevel@tonic-gate uint8_t *whereptr, *endptr; 26127c478bd9Sstevel@tonic-gate ip6_dest_t *tmpdstopts; 26137c478bd9Sstevel@tonic-gate ip6_rthdr_t *tmprthdr; 26147c478bd9Sstevel@tonic-gate ip6_hbh_t *tmphopopts; 26157c478bd9Sstevel@tonic-gate ip6_frag_t *tmpfraghdr; 26167c478bd9Sstevel@tonic-gate 2617bd670b35SErik Nordmark ipp->ipp_fields |= IPPF_HOPLIMIT | IPPF_TCLASS | IPPF_ADDR; 2618bd670b35SErik Nordmark ipp->ipp_hoplimit = ip6h->ip6_hops; 2619bd670b35SErik Nordmark ipp->ipp_tclass = IPV6_FLOW_TCLASS(ip6h->ip6_flow); 2620bd670b35SErik Nordmark ipp->ipp_addr = ip6h->ip6_dst; 2621bd670b35SErik Nordmark 26227c478bd9Sstevel@tonic-gate length = IPV6_HDR_LEN; 26237c478bd9Sstevel@tonic-gate whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */ 26247c478bd9Sstevel@tonic-gate endptr = mp->b_wptr; 26257c478bd9Sstevel@tonic-gate 26267c478bd9Sstevel@tonic-gate nexthdr = ip6h->ip6_nxt; 26277c478bd9Sstevel@tonic-gate while (whereptr < endptr) { 26287c478bd9Sstevel@tonic-gate /* Is there enough left for len + nexthdr? */ 26297c478bd9Sstevel@tonic-gate if (whereptr + MIN_EHDR_LEN > endptr) 26307c478bd9Sstevel@tonic-gate goto done; 26317c478bd9Sstevel@tonic-gate 26327c478bd9Sstevel@tonic-gate switch (nexthdr) { 2633bd670b35SErik Nordmark case IPPROTO_HOPOPTS: { 2634bd670b35SErik Nordmark /* We check for any CIPSO */ 2635bd670b35SErik Nordmark uchar_t *secopt; 2636bd670b35SErik Nordmark boolean_t hbh_needed; 2637bd670b35SErik Nordmark uchar_t *after_secopt; 2638bd670b35SErik Nordmark 26397c478bd9Sstevel@tonic-gate tmphopopts = (ip6_hbh_t *)whereptr; 26407c478bd9Sstevel@tonic-gate ehdrlen = 8 * (tmphopopts->ip6h_len + 1); 26417c478bd9Sstevel@tonic-gate if ((uchar_t *)tmphopopts + ehdrlen > endptr) 26427c478bd9Sstevel@tonic-gate goto done; 26437c478bd9Sstevel@tonic-gate nexthdr = tmphopopts->ip6h_nxt; 2644bd670b35SErik Nordmark 2645bd670b35SErik Nordmark if (!label_separate) { 2646bd670b35SErik Nordmark secopt = NULL; 2647bd670b35SErik Nordmark after_secopt = whereptr; 2648bd670b35SErik Nordmark } else { 2649bd670b35SErik Nordmark /* 2650bd670b35SErik Nordmark * We have dropped packets with bad options in 2651bd670b35SErik Nordmark * ip6_input. No need to check return value 2652bd670b35SErik Nordmark * here. 2653bd670b35SErik Nordmark */ 2654bd670b35SErik Nordmark (void) tsol_find_secopt_v6(whereptr, ehdrlen, 2655bd670b35SErik Nordmark &secopt, &after_secopt, &hbh_needed); 2656bd670b35SErik Nordmark } 2657bd670b35SErik Nordmark if (secopt != NULL && after_secopt - whereptr > 0) { 2658bd670b35SErik Nordmark ipp->ipp_fields |= IPPF_LABEL_V6; 2659bd670b35SErik Nordmark ipp->ipp_label_v6 = secopt; 2660bd670b35SErik Nordmark ipp->ipp_label_len_v6 = after_secopt - whereptr; 2661bd670b35SErik Nordmark } else { 2662bd670b35SErik Nordmark ipp->ipp_label_len_v6 = 0; 2663bd670b35SErik Nordmark after_secopt = whereptr; 2664bd670b35SErik Nordmark hbh_needed = B_TRUE; 2665bd670b35SErik Nordmark } 26667c478bd9Sstevel@tonic-gate /* return only 1st hbh */ 2667bd670b35SErik Nordmark if (hbh_needed && !(ipp->ipp_fields & IPPF_HOPOPTS)) { 26687c478bd9Sstevel@tonic-gate ipp->ipp_fields |= IPPF_HOPOPTS; 2669bd670b35SErik Nordmark ipp->ipp_hopopts = (ip6_hbh_t *)after_secopt; 2670bd670b35SErik Nordmark ipp->ipp_hopoptslen = ehdrlen - 2671bd670b35SErik Nordmark ipp->ipp_label_len_v6; 26727c478bd9Sstevel@tonic-gate } 26737c478bd9Sstevel@tonic-gate break; 2674bd670b35SErik Nordmark } 26757c478bd9Sstevel@tonic-gate case IPPROTO_DSTOPTS: 26767c478bd9Sstevel@tonic-gate tmpdstopts = (ip6_dest_t *)whereptr; 26777c478bd9Sstevel@tonic-gate ehdrlen = 8 * (tmpdstopts->ip6d_len + 1); 26787c478bd9Sstevel@tonic-gate if ((uchar_t *)tmpdstopts + ehdrlen > endptr) 26797c478bd9Sstevel@tonic-gate goto done; 26807c478bd9Sstevel@tonic-gate nexthdr = tmpdstopts->ip6d_nxt; 26817c478bd9Sstevel@tonic-gate /* 26827c478bd9Sstevel@tonic-gate * ipp_dstopts is set to the destination header after a 26837c478bd9Sstevel@tonic-gate * routing header. 26847c478bd9Sstevel@tonic-gate * Assume it is a post-rthdr destination header 26857c478bd9Sstevel@tonic-gate * and adjust when we find an rthdr. 26867c478bd9Sstevel@tonic-gate */ 26877c478bd9Sstevel@tonic-gate if (!(ipp->ipp_fields & IPPF_DSTOPTS)) { 26887c478bd9Sstevel@tonic-gate ipp->ipp_fields |= IPPF_DSTOPTS; 26897c478bd9Sstevel@tonic-gate ipp->ipp_dstopts = tmpdstopts; 26907c478bd9Sstevel@tonic-gate ipp->ipp_dstoptslen = ehdrlen; 26917c478bd9Sstevel@tonic-gate } 26927c478bd9Sstevel@tonic-gate break; 26937c478bd9Sstevel@tonic-gate case IPPROTO_ROUTING: 26947c478bd9Sstevel@tonic-gate tmprthdr = (ip6_rthdr_t *)whereptr; 26957c478bd9Sstevel@tonic-gate ehdrlen = 8 * (tmprthdr->ip6r_len + 1); 26967c478bd9Sstevel@tonic-gate if ((uchar_t *)tmprthdr + ehdrlen > endptr) 26977c478bd9Sstevel@tonic-gate goto done; 26987c478bd9Sstevel@tonic-gate nexthdr = tmprthdr->ip6r_nxt; 26997c478bd9Sstevel@tonic-gate /* return only 1st rthdr */ 27007c478bd9Sstevel@tonic-gate if (!(ipp->ipp_fields & IPPF_RTHDR)) { 27017c478bd9Sstevel@tonic-gate ipp->ipp_fields |= IPPF_RTHDR; 27027c478bd9Sstevel@tonic-gate ipp->ipp_rthdr = tmprthdr; 27037c478bd9Sstevel@tonic-gate ipp->ipp_rthdrlen = ehdrlen; 27047c478bd9Sstevel@tonic-gate } 27057c478bd9Sstevel@tonic-gate /* 27067c478bd9Sstevel@tonic-gate * Make any destination header we've seen be a 27077c478bd9Sstevel@tonic-gate * pre-rthdr destination header. 27087c478bd9Sstevel@tonic-gate */ 27097c478bd9Sstevel@tonic-gate if (ipp->ipp_fields & IPPF_DSTOPTS) { 27107c478bd9Sstevel@tonic-gate ipp->ipp_fields &= ~IPPF_DSTOPTS; 2711bd670b35SErik Nordmark ipp->ipp_fields |= IPPF_RTHDRDSTOPTS; 2712bd670b35SErik Nordmark ipp->ipp_rthdrdstopts = ipp->ipp_dstopts; 27137c478bd9Sstevel@tonic-gate ipp->ipp_dstopts = NULL; 2714bd670b35SErik Nordmark ipp->ipp_rthdrdstoptslen = ipp->ipp_dstoptslen; 27157c478bd9Sstevel@tonic-gate ipp->ipp_dstoptslen = 0; 27167c478bd9Sstevel@tonic-gate } 27177c478bd9Sstevel@tonic-gate break; 27187c478bd9Sstevel@tonic-gate case IPPROTO_FRAGMENT: 27197c478bd9Sstevel@tonic-gate tmpfraghdr = (ip6_frag_t *)whereptr; 27207c478bd9Sstevel@tonic-gate ehdrlen = sizeof (ip6_frag_t); 27217c478bd9Sstevel@tonic-gate if ((uchar_t *)tmpfraghdr + ehdrlen > endptr) 27227c478bd9Sstevel@tonic-gate goto done; 27237c478bd9Sstevel@tonic-gate nexthdr = tmpfraghdr->ip6f_nxt; 27248810c16bSdanmcd if (!(ipp->ipp_fields & IPPF_FRAGHDR)) { 27258810c16bSdanmcd ipp->ipp_fields |= IPPF_FRAGHDR; 27268810c16bSdanmcd ipp->ipp_fraghdr = tmpfraghdr; 27278810c16bSdanmcd ipp->ipp_fraghdrlen = ehdrlen; 27288810c16bSdanmcd } 27297c478bd9Sstevel@tonic-gate break; 27307c478bd9Sstevel@tonic-gate case IPPROTO_NONE: 27317c478bd9Sstevel@tonic-gate default: 27327c478bd9Sstevel@tonic-gate goto done; 27337c478bd9Sstevel@tonic-gate } 27347c478bd9Sstevel@tonic-gate length += ehdrlen; 27357c478bd9Sstevel@tonic-gate whereptr += ehdrlen; 27367c478bd9Sstevel@tonic-gate } 27377c478bd9Sstevel@tonic-gate done: 27387c478bd9Sstevel@tonic-gate if (nexthdrp != NULL) 27397c478bd9Sstevel@tonic-gate *nexthdrp = nexthdr; 27407c478bd9Sstevel@tonic-gate return (length); 27417c478bd9Sstevel@tonic-gate } 27427c478bd9Sstevel@tonic-gate 27437c478bd9Sstevel@tonic-gate /* 27447c478bd9Sstevel@tonic-gate * Try to determine where and what are the IPv6 header length and 27457c478bd9Sstevel@tonic-gate * pointer to nexthdr value for the upper layer protocol (or an 27467c478bd9Sstevel@tonic-gate * unknown next hdr). 27477c478bd9Sstevel@tonic-gate * 27487c478bd9Sstevel@tonic-gate * Parameters returns a pointer to the nexthdr value; 27497c478bd9Sstevel@tonic-gate * Must handle malformed packets of various sorts. 27507c478bd9Sstevel@tonic-gate * Function returns failure for malformed cases. 27517c478bd9Sstevel@tonic-gate */ 27527c478bd9Sstevel@tonic-gate boolean_t 27537c478bd9Sstevel@tonic-gate ip_hdr_length_nexthdr_v6(mblk_t *mp, ip6_t *ip6h, uint16_t *hdr_length_ptr, 27547c478bd9Sstevel@tonic-gate uint8_t **nexthdrpp) 27557c478bd9Sstevel@tonic-gate { 27567c478bd9Sstevel@tonic-gate uint16_t length; 27577c478bd9Sstevel@tonic-gate uint_t ehdrlen; 27587c478bd9Sstevel@tonic-gate uint8_t *nexthdrp; 27597c478bd9Sstevel@tonic-gate uint8_t *whereptr; 27607c478bd9Sstevel@tonic-gate uint8_t *endptr; 27617c478bd9Sstevel@tonic-gate ip6_dest_t *desthdr; 27627c478bd9Sstevel@tonic-gate ip6_rthdr_t *rthdr; 27637c478bd9Sstevel@tonic-gate ip6_frag_t *fraghdr; 27647c478bd9Sstevel@tonic-gate 2765bd670b35SErik Nordmark ASSERT(IPH_HDR_VERSION(ip6h) == IPV6_VERSION); 27667c478bd9Sstevel@tonic-gate length = IPV6_HDR_LEN; 27677c478bd9Sstevel@tonic-gate whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */ 27687c478bd9Sstevel@tonic-gate endptr = mp->b_wptr; 27697c478bd9Sstevel@tonic-gate 27707c478bd9Sstevel@tonic-gate nexthdrp = &ip6h->ip6_nxt; 27717c478bd9Sstevel@tonic-gate while (whereptr < endptr) { 27727c478bd9Sstevel@tonic-gate /* Is there enough left for len + nexthdr? */ 27737c478bd9Sstevel@tonic-gate if (whereptr + MIN_EHDR_LEN > endptr) 27747c478bd9Sstevel@tonic-gate break; 27757c478bd9Sstevel@tonic-gate 27767c478bd9Sstevel@tonic-gate switch (*nexthdrp) { 27777c478bd9Sstevel@tonic-gate case IPPROTO_HOPOPTS: 27787c478bd9Sstevel@tonic-gate case IPPROTO_DSTOPTS: 27797c478bd9Sstevel@tonic-gate /* Assumes the headers are identical for hbh and dst */ 27807c478bd9Sstevel@tonic-gate desthdr = (ip6_dest_t *)whereptr; 27817c478bd9Sstevel@tonic-gate ehdrlen = 8 * (desthdr->ip6d_len + 1); 27827c478bd9Sstevel@tonic-gate if ((uchar_t *)desthdr + ehdrlen > endptr) 27837c478bd9Sstevel@tonic-gate return (B_FALSE); 27847c478bd9Sstevel@tonic-gate nexthdrp = &desthdr->ip6d_nxt; 27857c478bd9Sstevel@tonic-gate break; 27867c478bd9Sstevel@tonic-gate case IPPROTO_ROUTING: 27877c478bd9Sstevel@tonic-gate rthdr = (ip6_rthdr_t *)whereptr; 27887c478bd9Sstevel@tonic-gate ehdrlen = 8 * (rthdr->ip6r_len + 1); 27897c478bd9Sstevel@tonic-gate if ((uchar_t *)rthdr + ehdrlen > endptr) 27907c478bd9Sstevel@tonic-gate return (B_FALSE); 27917c478bd9Sstevel@tonic-gate nexthdrp = &rthdr->ip6r_nxt; 27927c478bd9Sstevel@tonic-gate break; 27937c478bd9Sstevel@tonic-gate case IPPROTO_FRAGMENT: 27947c478bd9Sstevel@tonic-gate fraghdr = (ip6_frag_t *)whereptr; 27957c478bd9Sstevel@tonic-gate ehdrlen = sizeof (ip6_frag_t); 27967c478bd9Sstevel@tonic-gate if ((uchar_t *)&fraghdr[1] > endptr) 27977c478bd9Sstevel@tonic-gate return (B_FALSE); 27987c478bd9Sstevel@tonic-gate nexthdrp = &fraghdr->ip6f_nxt; 27997c478bd9Sstevel@tonic-gate break; 28007c478bd9Sstevel@tonic-gate case IPPROTO_NONE: 28017c478bd9Sstevel@tonic-gate /* No next header means we're finished */ 28027c478bd9Sstevel@tonic-gate default: 28037c478bd9Sstevel@tonic-gate *hdr_length_ptr = length; 28047c478bd9Sstevel@tonic-gate *nexthdrpp = nexthdrp; 28057c478bd9Sstevel@tonic-gate return (B_TRUE); 28067c478bd9Sstevel@tonic-gate } 28077c478bd9Sstevel@tonic-gate length += ehdrlen; 28087c478bd9Sstevel@tonic-gate whereptr += ehdrlen; 28097c478bd9Sstevel@tonic-gate *hdr_length_ptr = length; 28107c478bd9Sstevel@tonic-gate *nexthdrpp = nexthdrp; 28117c478bd9Sstevel@tonic-gate } 28127c478bd9Sstevel@tonic-gate switch (*nexthdrp) { 28137c478bd9Sstevel@tonic-gate case IPPROTO_HOPOPTS: 28147c478bd9Sstevel@tonic-gate case IPPROTO_DSTOPTS: 28157c478bd9Sstevel@tonic-gate case IPPROTO_ROUTING: 28167c478bd9Sstevel@tonic-gate case IPPROTO_FRAGMENT: 28177c478bd9Sstevel@tonic-gate /* 28187c478bd9Sstevel@tonic-gate * If any know extension headers are still to be processed, 28197c478bd9Sstevel@tonic-gate * the packet's malformed (or at least all the IP header(s) are 28207c478bd9Sstevel@tonic-gate * not in the same mblk - and that should never happen. 28217c478bd9Sstevel@tonic-gate */ 28227c478bd9Sstevel@tonic-gate return (B_FALSE); 28237c478bd9Sstevel@tonic-gate 28247c478bd9Sstevel@tonic-gate default: 28257c478bd9Sstevel@tonic-gate /* 28267c478bd9Sstevel@tonic-gate * If we get here, we know that all of the IP headers were in 28277c478bd9Sstevel@tonic-gate * the same mblk, even if the ULP header is in the next mblk. 28287c478bd9Sstevel@tonic-gate */ 28297c478bd9Sstevel@tonic-gate *hdr_length_ptr = length; 28307c478bd9Sstevel@tonic-gate *nexthdrpp = nexthdrp; 28317c478bd9Sstevel@tonic-gate return (B_TRUE); 28327c478bd9Sstevel@tonic-gate } 28337c478bd9Sstevel@tonic-gate } 28347c478bd9Sstevel@tonic-gate 28357c478bd9Sstevel@tonic-gate /* 28367c478bd9Sstevel@tonic-gate * Return the length of the IPv6 related headers (including extension headers) 28377c478bd9Sstevel@tonic-gate * Returns a length even if the packet is malformed. 28387c478bd9Sstevel@tonic-gate */ 28397c478bd9Sstevel@tonic-gate int 28407c478bd9Sstevel@tonic-gate ip_hdr_length_v6(mblk_t *mp, ip6_t *ip6h) 28417c478bd9Sstevel@tonic-gate { 28427c478bd9Sstevel@tonic-gate uint16_t hdr_len; 28437c478bd9Sstevel@tonic-gate uint8_t *nexthdrp; 28447c478bd9Sstevel@tonic-gate 28457c478bd9Sstevel@tonic-gate (void) ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_len, &nexthdrp); 28467c478bd9Sstevel@tonic-gate return (hdr_len); 28477c478bd9Sstevel@tonic-gate } 28487c478bd9Sstevel@tonic-gate 28497c478bd9Sstevel@tonic-gate /* 28507c478bd9Sstevel@tonic-gate * Parse and process any hop-by-hop or destination options. 28517c478bd9Sstevel@tonic-gate * 28527c478bd9Sstevel@tonic-gate * Assumes that q is an ill read queue so that ICMP errors for link-local 28537c478bd9Sstevel@tonic-gate * destinations are sent out the correct interface. 28547c478bd9Sstevel@tonic-gate * 28557c478bd9Sstevel@tonic-gate * Returns -1 if there was an error and mp has been consumed. 28567c478bd9Sstevel@tonic-gate * Returns 0 if no special action is needed. 28577c478bd9Sstevel@tonic-gate * Returns 1 if the packet contained a router alert option for this node 28587c478bd9Sstevel@tonic-gate * which is verified to be "interesting/known" for our implementation. 28597c478bd9Sstevel@tonic-gate * 28607c478bd9Sstevel@tonic-gate * XXX Note: In future as more hbh or dest options are defined, 28617c478bd9Sstevel@tonic-gate * it may be better to have different routines for hbh and dest 28627c478bd9Sstevel@tonic-gate * options as opt_type fields other than IP6OPT_PAD1 and IP6OPT_PADN 28637c478bd9Sstevel@tonic-gate * may have same value in different namespaces. Or is it same namespace ?? 28647c478bd9Sstevel@tonic-gate * Current code checks for each opt_type (other than pads) if it is in 28657c478bd9Sstevel@tonic-gate * the expected nexthdr (hbh or dest) 28667c478bd9Sstevel@tonic-gate */ 2867bd670b35SErik Nordmark int 2868bd670b35SErik Nordmark ip_process_options_v6(mblk_t *mp, ip6_t *ip6h, 2869bd670b35SErik Nordmark uint8_t *optptr, uint_t optlen, uint8_t hdr_type, ip_recv_attr_t *ira) 28707c478bd9Sstevel@tonic-gate { 28717c478bd9Sstevel@tonic-gate uint8_t opt_type; 28727c478bd9Sstevel@tonic-gate uint_t optused; 28737c478bd9Sstevel@tonic-gate int ret = 0; 287445916cd2Sjpk const char *errtype; 2875bd670b35SErik Nordmark ill_t *ill = ira->ira_ill; 2876bd670b35SErik Nordmark ip_stack_t *ipst = ill->ill_ipst; 28777c478bd9Sstevel@tonic-gate 28787c478bd9Sstevel@tonic-gate while (optlen != 0) { 28797c478bd9Sstevel@tonic-gate opt_type = *optptr; 28807c478bd9Sstevel@tonic-gate if (opt_type == IP6OPT_PAD1) { 28817c478bd9Sstevel@tonic-gate optused = 1; 28827c478bd9Sstevel@tonic-gate } else { 28837c478bd9Sstevel@tonic-gate if (optlen < 2) 28847c478bd9Sstevel@tonic-gate goto bad_opt; 288545916cd2Sjpk errtype = "malformed"; 288645916cd2Sjpk if (opt_type == ip6opt_ls) { 288745916cd2Sjpk optused = 2 + optptr[1]; 288845916cd2Sjpk if (optused > optlen) 288945916cd2Sjpk goto bad_opt; 289045916cd2Sjpk } else switch (opt_type) { 28917c478bd9Sstevel@tonic-gate case IP6OPT_PADN: 28927c478bd9Sstevel@tonic-gate /* 28937c478bd9Sstevel@tonic-gate * Note:We don't verify that (N-2) pad octets 28947c478bd9Sstevel@tonic-gate * are zero as required by spec. Adhere to 28957c478bd9Sstevel@tonic-gate * "be liberal in what you accept..." part of 28967c478bd9Sstevel@tonic-gate * implementation philosophy (RFC791,RFC1122) 28977c478bd9Sstevel@tonic-gate */ 28987c478bd9Sstevel@tonic-gate optused = 2 + optptr[1]; 28997c478bd9Sstevel@tonic-gate if (optused > optlen) 29007c478bd9Sstevel@tonic-gate goto bad_opt; 29017c478bd9Sstevel@tonic-gate break; 29027c478bd9Sstevel@tonic-gate 29037c478bd9Sstevel@tonic-gate case IP6OPT_JUMBO: 29047c478bd9Sstevel@tonic-gate if (hdr_type != IPPROTO_HOPOPTS) 29057c478bd9Sstevel@tonic-gate goto opt_error; 29067c478bd9Sstevel@tonic-gate goto opt_error; /* XXX Not implemented! */ 29077c478bd9Sstevel@tonic-gate 29087c478bd9Sstevel@tonic-gate case IP6OPT_ROUTER_ALERT: { 29097c478bd9Sstevel@tonic-gate struct ip6_opt_router *or; 29107c478bd9Sstevel@tonic-gate 29117c478bd9Sstevel@tonic-gate if (hdr_type != IPPROTO_HOPOPTS) 29127c478bd9Sstevel@tonic-gate goto opt_error; 29137c478bd9Sstevel@tonic-gate optused = 2 + optptr[1]; 29147c478bd9Sstevel@tonic-gate if (optused > optlen) 29157c478bd9Sstevel@tonic-gate goto bad_opt; 29167c478bd9Sstevel@tonic-gate or = (struct ip6_opt_router *)optptr; 29177c478bd9Sstevel@tonic-gate /* Check total length and alignment */ 29187c478bd9Sstevel@tonic-gate if (optused != sizeof (*or) || 29197c478bd9Sstevel@tonic-gate ((uintptr_t)or->ip6or_value & 0x1) != 0) 29207c478bd9Sstevel@tonic-gate goto opt_error; 29217c478bd9Sstevel@tonic-gate /* Check value */ 29227c478bd9Sstevel@tonic-gate switch (*((uint16_t *)or->ip6or_value)) { 29237c478bd9Sstevel@tonic-gate case IP6_ALERT_MLD: 29247c478bd9Sstevel@tonic-gate case IP6_ALERT_RSVP: 29257c478bd9Sstevel@tonic-gate ret = 1; 29267c478bd9Sstevel@tonic-gate } 29277c478bd9Sstevel@tonic-gate break; 29287c478bd9Sstevel@tonic-gate } 29297c478bd9Sstevel@tonic-gate case IP6OPT_HOME_ADDRESS: { 29307c478bd9Sstevel@tonic-gate /* 29317c478bd9Sstevel@tonic-gate * Minimal support for the home address option 29327c478bd9Sstevel@tonic-gate * (which is required by all IPv6 nodes). 29337c478bd9Sstevel@tonic-gate * Implement by just swapping the home address 29347c478bd9Sstevel@tonic-gate * and source address. 29357c478bd9Sstevel@tonic-gate * XXX Note: this has IPsec implications since 29367c478bd9Sstevel@tonic-gate * AH needs to take this into account. 29377c478bd9Sstevel@tonic-gate * Also, when IPsec is used we need to ensure 29387c478bd9Sstevel@tonic-gate * that this is only processed once 29397c478bd9Sstevel@tonic-gate * in the received packet (to avoid swapping 29407c478bd9Sstevel@tonic-gate * back and forth). 29417c478bd9Sstevel@tonic-gate * NOTE:This option processing is considered 29427c478bd9Sstevel@tonic-gate * to be unsafe and prone to a denial of 29437c478bd9Sstevel@tonic-gate * service attack. 29447c478bd9Sstevel@tonic-gate * The current processing is not safe even with 29457c478bd9Sstevel@tonic-gate * IPsec secured IP packets. Since the home 29467c478bd9Sstevel@tonic-gate * address option processing requirement still 29477c478bd9Sstevel@tonic-gate * is in the IETF draft and in the process of 29487c478bd9Sstevel@tonic-gate * being redefined for its usage, it has been 29497c478bd9Sstevel@tonic-gate * decided to turn off the option by default. 29507c478bd9Sstevel@tonic-gate * If this section of code needs to be executed, 29517c478bd9Sstevel@tonic-gate * ndd variable ip6_ignore_home_address_opt 29527c478bd9Sstevel@tonic-gate * should be set to 0 at the user's own risk. 29537c478bd9Sstevel@tonic-gate */ 29547c478bd9Sstevel@tonic-gate struct ip6_opt_home_address *oh; 29557c478bd9Sstevel@tonic-gate in6_addr_t tmp; 29567c478bd9Sstevel@tonic-gate 2957f4b3ec61Sdh155122 if (ipst->ips_ipv6_ignore_home_address_opt) 29587c478bd9Sstevel@tonic-gate goto opt_error; 29597c478bd9Sstevel@tonic-gate 29607c478bd9Sstevel@tonic-gate if (hdr_type != IPPROTO_DSTOPTS) 29617c478bd9Sstevel@tonic-gate goto opt_error; 29627c478bd9Sstevel@tonic-gate optused = 2 + optptr[1]; 29637c478bd9Sstevel@tonic-gate if (optused > optlen) 29647c478bd9Sstevel@tonic-gate goto bad_opt; 29657c478bd9Sstevel@tonic-gate 29667c478bd9Sstevel@tonic-gate /* 29677c478bd9Sstevel@tonic-gate * We did this dest. opt the first time 29687c478bd9Sstevel@tonic-gate * around (i.e. before AH processing). 29697c478bd9Sstevel@tonic-gate * If we've done AH... stop now. 29707c478bd9Sstevel@tonic-gate */ 2971bd670b35SErik Nordmark if ((ira->ira_flags & IRAF_IPSEC_SECURE) && 2972bd670b35SErik Nordmark ira->ira_ipsec_ah_sa != NULL) 29737c478bd9Sstevel@tonic-gate break; 29747c478bd9Sstevel@tonic-gate 29757c478bd9Sstevel@tonic-gate oh = (struct ip6_opt_home_address *)optptr; 29767c478bd9Sstevel@tonic-gate /* Check total length and alignment */ 29777c478bd9Sstevel@tonic-gate if (optused < sizeof (*oh) || 29787c478bd9Sstevel@tonic-gate ((uintptr_t)oh->ip6oh_addr & 0x7) != 0) 29797c478bd9Sstevel@tonic-gate goto opt_error; 29807c478bd9Sstevel@tonic-gate /* Swap ip6_src and the home address */ 29817c478bd9Sstevel@tonic-gate tmp = ip6h->ip6_src; 29827c478bd9Sstevel@tonic-gate /* XXX Note: only 8 byte alignment option */ 29837c478bd9Sstevel@tonic-gate ip6h->ip6_src = *(in6_addr_t *)oh->ip6oh_addr; 29847c478bd9Sstevel@tonic-gate *(in6_addr_t *)oh->ip6oh_addr = tmp; 29857c478bd9Sstevel@tonic-gate break; 29867c478bd9Sstevel@tonic-gate } 29877c478bd9Sstevel@tonic-gate 29887c478bd9Sstevel@tonic-gate case IP6OPT_TUNNEL_LIMIT: 29897c478bd9Sstevel@tonic-gate if (hdr_type != IPPROTO_DSTOPTS) { 29907c478bd9Sstevel@tonic-gate goto opt_error; 29917c478bd9Sstevel@tonic-gate } 29927c478bd9Sstevel@tonic-gate optused = 2 + optptr[1]; 29937c478bd9Sstevel@tonic-gate if (optused > optlen) { 29947c478bd9Sstevel@tonic-gate goto bad_opt; 29957c478bd9Sstevel@tonic-gate } 29967c478bd9Sstevel@tonic-gate if (optused != 3) { 29977c478bd9Sstevel@tonic-gate goto opt_error; 29987c478bd9Sstevel@tonic-gate } 29997c478bd9Sstevel@tonic-gate break; 30007c478bd9Sstevel@tonic-gate 30017c478bd9Sstevel@tonic-gate default: 300245916cd2Sjpk errtype = "unknown"; 300345916cd2Sjpk /* FALLTHROUGH */ 30047c478bd9Sstevel@tonic-gate opt_error: 30055597b60aSnordmark /* Determine which zone should send error */ 30067c478bd9Sstevel@tonic-gate switch (IP6OPT_TYPE(opt_type)) { 30077c478bd9Sstevel@tonic-gate case IP6OPT_TYPE_SKIP: 30087c478bd9Sstevel@tonic-gate optused = 2 + optptr[1]; 30097c478bd9Sstevel@tonic-gate if (optused > optlen) 30107c478bd9Sstevel@tonic-gate goto bad_opt; 301145916cd2Sjpk ip1dbg(("ip_process_options_v6: %s " 301245916cd2Sjpk "opt 0x%x skipped\n", 301345916cd2Sjpk errtype, opt_type)); 30147c478bd9Sstevel@tonic-gate break; 30157c478bd9Sstevel@tonic-gate case IP6OPT_TYPE_DISCARD: 301645916cd2Sjpk ip1dbg(("ip_process_options_v6: %s " 301745916cd2Sjpk "opt 0x%x; packet dropped\n", 301845916cd2Sjpk errtype, opt_type)); 3019bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, 3020bd670b35SErik Nordmark ipIfStatsInHdrErrors); 3021bd670b35SErik Nordmark ip_drop_input("ipIfStatsInHdrErrors", 3022bd670b35SErik Nordmark mp, ill); 3023bd670b35SErik Nordmark freemsg(mp); 30247c478bd9Sstevel@tonic-gate return (-1); 30257c478bd9Sstevel@tonic-gate case IP6OPT_TYPE_ICMP: 3026bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, 3027bd670b35SErik Nordmark ipIfStatsInHdrErrors); 3028bd670b35SErik Nordmark ip_drop_input("ipIfStatsInHdrErrors", 3029bd670b35SErik Nordmark mp, ill); 3030bd670b35SErik Nordmark icmp_param_problem_v6(mp, 30317c478bd9Sstevel@tonic-gate ICMP6_PARAMPROB_OPTION, 30327c478bd9Sstevel@tonic-gate (uint32_t)(optptr - 30337c478bd9Sstevel@tonic-gate (uint8_t *)ip6h), 3034bd670b35SErik Nordmark B_FALSE, ira); 30357c478bd9Sstevel@tonic-gate return (-1); 30367c478bd9Sstevel@tonic-gate case IP6OPT_TYPE_FORCEICMP: 3037bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, 3038bd670b35SErik Nordmark ipIfStatsInHdrErrors); 3039bd670b35SErik Nordmark ip_drop_input("ipIfStatsInHdrErrors", 3040bd670b35SErik Nordmark mp, ill); 3041bd670b35SErik Nordmark icmp_param_problem_v6(mp, 30427c478bd9Sstevel@tonic-gate ICMP6_PARAMPROB_OPTION, 30437c478bd9Sstevel@tonic-gate (uint32_t)(optptr - 30447c478bd9Sstevel@tonic-gate (uint8_t *)ip6h), 3045bd670b35SErik Nordmark B_TRUE, ira); 30467c478bd9Sstevel@tonic-gate return (-1); 304745916cd2Sjpk default: 304845916cd2Sjpk ASSERT(0); 30497c478bd9Sstevel@tonic-gate } 30507c478bd9Sstevel@tonic-gate } 30517c478bd9Sstevel@tonic-gate } 30527c478bd9Sstevel@tonic-gate optlen -= optused; 30537c478bd9Sstevel@tonic-gate optptr += optused; 30547c478bd9Sstevel@tonic-gate } 30557c478bd9Sstevel@tonic-gate return (ret); 30567c478bd9Sstevel@tonic-gate 30577c478bd9Sstevel@tonic-gate bad_opt: 30585597b60aSnordmark /* Determine which zone should send error */ 3059bd670b35SErik Nordmark ip_drop_input("ICMP_PARAM_PROBLEM", mp, ill); 3060bd670b35SErik Nordmark icmp_param_problem_v6(mp, ICMP6_PARAMPROB_OPTION, 30617c478bd9Sstevel@tonic-gate (uint32_t)(optptr - (uint8_t *)ip6h), 3062bd670b35SErik Nordmark B_FALSE, ira); 30637c478bd9Sstevel@tonic-gate return (-1); 30647c478bd9Sstevel@tonic-gate } 30657c478bd9Sstevel@tonic-gate 30667c478bd9Sstevel@tonic-gate /* 30677c478bd9Sstevel@tonic-gate * Process a routing header that is not yet empty. 3068f0376ac1SBrian Utterback * Because of RFC 5095, we now reject all route headers. 30697c478bd9Sstevel@tonic-gate */ 3070bd670b35SErik Nordmark void 3071bd670b35SErik Nordmark ip_process_rthdr(mblk_t *mp, ip6_t *ip6h, ip6_rthdr_t *rth, 3072bd670b35SErik Nordmark ip_recv_attr_t *ira) 30737c478bd9Sstevel@tonic-gate { 3074bd670b35SErik Nordmark ill_t *ill = ira->ira_ill; 3075f4b3ec61Sdh155122 ip_stack_t *ipst = ill->ill_ipst; 30767c478bd9Sstevel@tonic-gate 30777c478bd9Sstevel@tonic-gate ASSERT(rth->ip6r_segleft != 0); 30787c478bd9Sstevel@tonic-gate 3079f4b3ec61Sdh155122 if (!ipst->ips_ipv6_forward_src_routed) { 30807c478bd9Sstevel@tonic-gate /* XXX Check for source routed out same interface? */ 30813173664eSapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsForwProhibits); 30823173664eSapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInAddrErrors); 3083bd670b35SErik Nordmark ip_drop_input("ipIfStatsInAddrErrors", mp, ill); 30847c478bd9Sstevel@tonic-gate freemsg(mp); 30857c478bd9Sstevel@tonic-gate return; 30867c478bd9Sstevel@tonic-gate } 3087bd670b35SErik Nordmark 3088bd670b35SErik Nordmark ip_drop_input("ICMP_PARAM_PROBLEM", mp, ill); 3089bd670b35SErik Nordmark icmp_param_problem_v6(mp, ICMP6_PARAMPROB_HEADER, 3090bd670b35SErik Nordmark (uint32_t)((uchar_t *)&rth->ip6r_type - (uchar_t *)ip6h), 3091bd670b35SErik Nordmark B_FALSE, ira); 30927c478bd9Sstevel@tonic-gate } 30937c478bd9Sstevel@tonic-gate 30947c478bd9Sstevel@tonic-gate /* 30957c478bd9Sstevel@tonic-gate * Read side put procedure for IPv6 module. 30967c478bd9Sstevel@tonic-gate */ 3097381a2a9aSdr146992 void 30987c478bd9Sstevel@tonic-gate ip_rput_v6(queue_t *q, mblk_t *mp) 30997c478bd9Sstevel@tonic-gate { 31007c478bd9Sstevel@tonic-gate ill_t *ill; 31017c478bd9Sstevel@tonic-gate 31027c478bd9Sstevel@tonic-gate ill = (ill_t *)q->q_ptr; 3103bd670b35SErik Nordmark if (ill->ill_state_flags & (ILL_CONDEMNED | ILL_LL_SUBNET_PENDING)) { 31047c478bd9Sstevel@tonic-gate union DL_primitives *dl; 31057c478bd9Sstevel@tonic-gate 31067c478bd9Sstevel@tonic-gate dl = (union DL_primitives *)mp->b_rptr; 31077c478bd9Sstevel@tonic-gate /* 31087c478bd9Sstevel@tonic-gate * Things are opening or closing - only accept DLPI 31097c478bd9Sstevel@tonic-gate * ack messages. If the stream is closing and ip_wsrv 31107c478bd9Sstevel@tonic-gate * has completed, ip_close is out of the qwait, but has 31117c478bd9Sstevel@tonic-gate * not yet completed qprocsoff. Don't proceed any further 31127c478bd9Sstevel@tonic-gate * because the ill has been cleaned up and things hanging 31137c478bd9Sstevel@tonic-gate * off the ill have been freed. 31147c478bd9Sstevel@tonic-gate */ 31157c478bd9Sstevel@tonic-gate if ((mp->b_datap->db_type != M_PCPROTO) || 31167c478bd9Sstevel@tonic-gate (dl->dl_primitive == DL_UNITDATA_IND)) { 3117ff550d0eSmasputra inet_freemsg(mp); 31187c478bd9Sstevel@tonic-gate return; 31197c478bd9Sstevel@tonic-gate } 31207c478bd9Sstevel@tonic-gate } 3121bd670b35SErik Nordmark if (DB_TYPE(mp) == M_DATA) { 3122bd670b35SErik Nordmark struct mac_header_info_s mhi; 31237c478bd9Sstevel@tonic-gate 3124bd670b35SErik Nordmark ip_mdata_to_mhi(ill, mp, &mhi); 3125bd670b35SErik Nordmark ip_input_v6(ill, NULL, mp, &mhi); 31267c478bd9Sstevel@tonic-gate } else { 3127bd670b35SErik Nordmark ip_rput_notdata(ill, mp); 31287c478bd9Sstevel@tonic-gate } 31297c478bd9Sstevel@tonic-gate } 31307c478bd9Sstevel@tonic-gate 31317c478bd9Sstevel@tonic-gate /* 31327c478bd9Sstevel@tonic-gate * Walk through the IPv6 packet in mp and see if there's an AH header 31337c478bd9Sstevel@tonic-gate * in it. See if the AH header needs to get done before other headers in 31347c478bd9Sstevel@tonic-gate * the packet. (Worker function for ipsec_early_ah_v6().) 31357c478bd9Sstevel@tonic-gate */ 31367c478bd9Sstevel@tonic-gate #define IPSEC_HDR_DONT_PROCESS 0 31377c478bd9Sstevel@tonic-gate #define IPSEC_HDR_PROCESS 1 31385002eb58SBill Sommerfeld #define IPSEC_MEMORY_ERROR 2 /* or malformed packet */ 31397c478bd9Sstevel@tonic-gate static int 31407c478bd9Sstevel@tonic-gate ipsec_needs_processing_v6(mblk_t *mp, uint8_t *nexthdr) 31417c478bd9Sstevel@tonic-gate { 31427c478bd9Sstevel@tonic-gate uint_t length; 31437c478bd9Sstevel@tonic-gate uint_t ehdrlen; 31447c478bd9Sstevel@tonic-gate uint8_t *whereptr; 31457c478bd9Sstevel@tonic-gate uint8_t *endptr; 31467c478bd9Sstevel@tonic-gate uint8_t *nexthdrp; 31477c478bd9Sstevel@tonic-gate ip6_dest_t *desthdr; 31487c478bd9Sstevel@tonic-gate ip6_rthdr_t *rthdr; 31497c478bd9Sstevel@tonic-gate ip6_t *ip6h; 31507c478bd9Sstevel@tonic-gate 31517c478bd9Sstevel@tonic-gate /* 31527c478bd9Sstevel@tonic-gate * For now just pullup everything. In general, the less pullups, 31537c478bd9Sstevel@tonic-gate * the better, but there's so much squirrelling through anyway, 31547c478bd9Sstevel@tonic-gate * it's just easier this way. 31557c478bd9Sstevel@tonic-gate */ 31567c478bd9Sstevel@tonic-gate if (!pullupmsg(mp, -1)) { 31577c478bd9Sstevel@tonic-gate return (IPSEC_MEMORY_ERROR); 31587c478bd9Sstevel@tonic-gate } 31597c478bd9Sstevel@tonic-gate 31607c478bd9Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 31617c478bd9Sstevel@tonic-gate length = IPV6_HDR_LEN; 31627c478bd9Sstevel@tonic-gate whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */ 31637c478bd9Sstevel@tonic-gate endptr = mp->b_wptr; 31647c478bd9Sstevel@tonic-gate 31657c478bd9Sstevel@tonic-gate /* 31667c478bd9Sstevel@tonic-gate * We can't just use the argument nexthdr in the place 31677c478bd9Sstevel@tonic-gate * of nexthdrp becaue we don't dereference nexthdrp 31687c478bd9Sstevel@tonic-gate * till we confirm whether it is a valid address. 31697c478bd9Sstevel@tonic-gate */ 31707c478bd9Sstevel@tonic-gate nexthdrp = &ip6h->ip6_nxt; 31717c478bd9Sstevel@tonic-gate while (whereptr < endptr) { 31727c478bd9Sstevel@tonic-gate /* Is there enough left for len + nexthdr? */ 31737c478bd9Sstevel@tonic-gate if (whereptr + MIN_EHDR_LEN > endptr) 31747c478bd9Sstevel@tonic-gate return (IPSEC_MEMORY_ERROR); 31757c478bd9Sstevel@tonic-gate 31767c478bd9Sstevel@tonic-gate switch (*nexthdrp) { 31777c478bd9Sstevel@tonic-gate case IPPROTO_HOPOPTS: 31787c478bd9Sstevel@tonic-gate case IPPROTO_DSTOPTS: 31797c478bd9Sstevel@tonic-gate /* Assumes the headers are identical for hbh and dst */ 31807c478bd9Sstevel@tonic-gate desthdr = (ip6_dest_t *)whereptr; 31817c478bd9Sstevel@tonic-gate ehdrlen = 8 * (desthdr->ip6d_len + 1); 31827c478bd9Sstevel@tonic-gate if ((uchar_t *)desthdr + ehdrlen > endptr) 31837c478bd9Sstevel@tonic-gate return (IPSEC_MEMORY_ERROR); 31847c478bd9Sstevel@tonic-gate /* 31855c0b7edeSseb * Return DONT_PROCESS because the destination 31865c0b7edeSseb * options header may be for each hop in a 31875c0b7edeSseb * routing-header, and we only want AH if we're 31885c0b7edeSseb * finished with routing headers. 31897c478bd9Sstevel@tonic-gate */ 31907c478bd9Sstevel@tonic-gate if (*nexthdrp == IPPROTO_DSTOPTS) 31917c478bd9Sstevel@tonic-gate return (IPSEC_HDR_DONT_PROCESS); 31927c478bd9Sstevel@tonic-gate nexthdrp = &desthdr->ip6d_nxt; 31937c478bd9Sstevel@tonic-gate break; 31947c478bd9Sstevel@tonic-gate case IPPROTO_ROUTING: 31957c478bd9Sstevel@tonic-gate rthdr = (ip6_rthdr_t *)whereptr; 31967c478bd9Sstevel@tonic-gate 31977c478bd9Sstevel@tonic-gate /* 31987c478bd9Sstevel@tonic-gate * If there's more hops left on the routing header, 31997c478bd9Sstevel@tonic-gate * return now with DON'T PROCESS. 32007c478bd9Sstevel@tonic-gate */ 32017c478bd9Sstevel@tonic-gate if (rthdr->ip6r_segleft > 0) 32027c478bd9Sstevel@tonic-gate return (IPSEC_HDR_DONT_PROCESS); 32037c478bd9Sstevel@tonic-gate 32047c478bd9Sstevel@tonic-gate ehdrlen = 8 * (rthdr->ip6r_len + 1); 32057c478bd9Sstevel@tonic-gate if ((uchar_t *)rthdr + ehdrlen > endptr) 32067c478bd9Sstevel@tonic-gate return (IPSEC_MEMORY_ERROR); 32077c478bd9Sstevel@tonic-gate nexthdrp = &rthdr->ip6r_nxt; 32087c478bd9Sstevel@tonic-gate break; 32097c478bd9Sstevel@tonic-gate case IPPROTO_FRAGMENT: 32107c478bd9Sstevel@tonic-gate /* Wait for reassembly */ 32117c478bd9Sstevel@tonic-gate return (IPSEC_HDR_DONT_PROCESS); 32127c478bd9Sstevel@tonic-gate case IPPROTO_AH: 32137c478bd9Sstevel@tonic-gate *nexthdr = IPPROTO_AH; 32147c478bd9Sstevel@tonic-gate return (IPSEC_HDR_PROCESS); 32157c478bd9Sstevel@tonic-gate case IPPROTO_NONE: 32167c478bd9Sstevel@tonic-gate /* No next header means we're finished */ 32177c478bd9Sstevel@tonic-gate default: 32187c478bd9Sstevel@tonic-gate return (IPSEC_HDR_DONT_PROCESS); 32197c478bd9Sstevel@tonic-gate } 32207c478bd9Sstevel@tonic-gate length += ehdrlen; 32217c478bd9Sstevel@tonic-gate whereptr += ehdrlen; 32227c478bd9Sstevel@tonic-gate } 32235002eb58SBill Sommerfeld /* 32245002eb58SBill Sommerfeld * Malformed/truncated packet. 32255002eb58SBill Sommerfeld */ 32265002eb58SBill Sommerfeld return (IPSEC_MEMORY_ERROR); 32277c478bd9Sstevel@tonic-gate } 32287c478bd9Sstevel@tonic-gate 32297c478bd9Sstevel@tonic-gate /* 3230bd670b35SErik Nordmark * Path for AH if options are present. 3231bd670b35SErik Nordmark * Returns NULL if the mblk was consumed. 32327c478bd9Sstevel@tonic-gate * 32337c478bd9Sstevel@tonic-gate * Sometimes AH needs to be done before other IPv6 headers for security 32347c478bd9Sstevel@tonic-gate * reasons. This function (and its ipsec_needs_processing_v6() above) 32357c478bd9Sstevel@tonic-gate * indicates if that is so, and fans out to the appropriate IPsec protocol 32367c478bd9Sstevel@tonic-gate * for the datagram passed in. 32377c478bd9Sstevel@tonic-gate */ 3238bd670b35SErik Nordmark mblk_t * 3239bd670b35SErik Nordmark ipsec_early_ah_v6(mblk_t *mp, ip_recv_attr_t *ira) 32407c478bd9Sstevel@tonic-gate { 32417c478bd9Sstevel@tonic-gate uint8_t nexthdr; 32427c478bd9Sstevel@tonic-gate ah_t *ah; 3243bd670b35SErik Nordmark ill_t *ill = ira->ira_ill; 3244f4b3ec61Sdh155122 ip_stack_t *ipst = ill->ill_ipst; 3245bd670b35SErik Nordmark ipsec_stack_t *ipss = ipst->ips_netstack->netstack_ipsec; 32467c478bd9Sstevel@tonic-gate 3247bd670b35SErik Nordmark switch (ipsec_needs_processing_v6(mp, &nexthdr)) { 32487c478bd9Sstevel@tonic-gate case IPSEC_MEMORY_ERROR: 32493173664eSapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 3250bd670b35SErik Nordmark ip_drop_input("ipIfStatsInDiscards", mp, ill); 3251bd670b35SErik Nordmark freemsg(mp); 3252bd670b35SErik Nordmark return (NULL); 32537c478bd9Sstevel@tonic-gate case IPSEC_HDR_DONT_PROCESS: 3254bd670b35SErik Nordmark return (mp); 32557c478bd9Sstevel@tonic-gate } 32567c478bd9Sstevel@tonic-gate 32577c478bd9Sstevel@tonic-gate /* Default means send it to AH! */ 32587c478bd9Sstevel@tonic-gate ASSERT(nexthdr == IPPROTO_AH); 32597c478bd9Sstevel@tonic-gate 3260f4b3ec61Sdh155122 if (!ipsec_loaded(ipss)) { 3261bd670b35SErik Nordmark ip_proto_not_sup(mp, ira); 3262bd670b35SErik Nordmark return (NULL); 32637c478bd9Sstevel@tonic-gate } 32647c478bd9Sstevel@tonic-gate 3265bd670b35SErik Nordmark mp = ipsec_inbound_ah_sa(mp, ira, &ah); 3266bd670b35SErik Nordmark if (mp == NULL) 3267bd670b35SErik Nordmark return (NULL); 3268bd670b35SErik Nordmark ASSERT(ah != NULL); 3269bd670b35SErik Nordmark ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE); 3270bd670b35SErik Nordmark ASSERT(ira->ira_ipsec_ah_sa != NULL); 3271bd670b35SErik Nordmark ASSERT(ira->ira_ipsec_ah_sa->ipsa_input_func != NULL); 3272bd670b35SErik Nordmark mp = ira->ira_ipsec_ah_sa->ipsa_input_func(mp, ah, ira); 32737c478bd9Sstevel@tonic-gate 3274bd670b35SErik Nordmark if (mp == NULL) { 3275bd670b35SErik Nordmark /* 3276bd670b35SErik Nordmark * Either it failed or is pending. In the former case 3277bd670b35SErik Nordmark * ipIfStatsInDiscards was increased. 3278bd670b35SErik Nordmark */ 3279bd670b35SErik Nordmark return (NULL); 3280bd670b35SErik Nordmark } 3281bd670b35SErik Nordmark 32827c478bd9Sstevel@tonic-gate /* we're done with IPsec processing, send it up */ 3283bd670b35SErik Nordmark ip_input_post_ipsec(mp, ira); 3284bd670b35SErik Nordmark return (NULL); 32857c478bd9Sstevel@tonic-gate } 32867c478bd9Sstevel@tonic-gate 32877c478bd9Sstevel@tonic-gate /* 32887c478bd9Sstevel@tonic-gate * Reassemble fragment. 32897c478bd9Sstevel@tonic-gate * When it returns a completed message the first mblk will only contain 3290bd670b35SErik Nordmark * the headers prior to the fragment header, with the nexthdr value updated 3291bd670b35SErik Nordmark * to be the header after the fragment header. 32927c478bd9Sstevel@tonic-gate */ 3293bd670b35SErik Nordmark mblk_t * 3294bd670b35SErik Nordmark ip_input_fragment_v6(mblk_t *mp, ip6_t *ip6h, 3295bd670b35SErik Nordmark ip6_frag_t *fraghdr, uint_t remlen, ip_recv_attr_t *ira) 32967c478bd9Sstevel@tonic-gate { 32977c478bd9Sstevel@tonic-gate uint32_t ident = ntohl(fraghdr->ip6f_ident); 32987c478bd9Sstevel@tonic-gate uint16_t offset; 32997c478bd9Sstevel@tonic-gate boolean_t more_frags; 33007c478bd9Sstevel@tonic-gate uint8_t nexthdr = fraghdr->ip6f_nxt; 33017c478bd9Sstevel@tonic-gate in6_addr_t *v6dst_ptr; 33027c478bd9Sstevel@tonic-gate in6_addr_t *v6src_ptr; 33037c478bd9Sstevel@tonic-gate uint_t end; 33047c478bd9Sstevel@tonic-gate uint_t hdr_length; 33057c478bd9Sstevel@tonic-gate size_t count; 33067c478bd9Sstevel@tonic-gate ipf_t *ipf; 33077c478bd9Sstevel@tonic-gate ipf_t **ipfp; 33087c478bd9Sstevel@tonic-gate ipfb_t *ipfb; 33097c478bd9Sstevel@tonic-gate mblk_t *mp1; 33107c478bd9Sstevel@tonic-gate uint8_t ecn_info = 0; 33117c478bd9Sstevel@tonic-gate size_t msg_len; 33127c478bd9Sstevel@tonic-gate mblk_t *tail_mp; 33137c478bd9Sstevel@tonic-gate mblk_t *t_mp; 33147c478bd9Sstevel@tonic-gate boolean_t pruned = B_FALSE; 3315ff550d0eSmasputra uint32_t sum_val; 3316ff550d0eSmasputra uint16_t sum_flags; 3317bd670b35SErik Nordmark ill_t *ill = ira->ira_ill; 3318f4b3ec61Sdh155122 ip_stack_t *ipst = ill->ill_ipst; 3319bd670b35SErik Nordmark uint_t prev_nexthdr_offset; 3320bd670b35SErik Nordmark uint8_t prev_nexthdr; 3321bd670b35SErik Nordmark uint8_t *ptr; 3322bd670b35SErik Nordmark uint32_t packet_size; 3323ff550d0eSmasputra 3324ff550d0eSmasputra /* 3325ff550d0eSmasputra * We utilize hardware computed checksum info only for UDP since 3326ff550d0eSmasputra * IP fragmentation is a normal occurence for the protocol. In 3327ff550d0eSmasputra * addition, checksum offload support for IP fragments carrying 3328ff550d0eSmasputra * UDP payload is commonly implemented across network adapters. 3329ff550d0eSmasputra */ 3330bd670b35SErik Nordmark ASSERT(ira->ira_rill != NULL); 3331bd670b35SErik Nordmark if (nexthdr == IPPROTO_UDP && dohwcksum && 3332bd670b35SErik Nordmark ILL_HCKSUM_CAPABLE(ira->ira_rill) && 3333ff550d0eSmasputra (DB_CKSUMFLAGS(mp) & (HCK_FULLCKSUM | HCK_PARTIALCKSUM))) { 3334ff550d0eSmasputra mblk_t *mp1 = mp->b_cont; 3335ff550d0eSmasputra int32_t len; 3336ff550d0eSmasputra 3337ff550d0eSmasputra /* Record checksum information from the packet */ 3338ff550d0eSmasputra sum_val = (uint32_t)DB_CKSUM16(mp); 3339ff550d0eSmasputra sum_flags = DB_CKSUMFLAGS(mp); 3340ff550d0eSmasputra 3341ff550d0eSmasputra /* fragmented payload offset from beginning of mblk */ 3342ff550d0eSmasputra offset = (uint16_t)((uchar_t *)&fraghdr[1] - mp->b_rptr); 3343ff550d0eSmasputra 3344ff550d0eSmasputra if ((sum_flags & HCK_PARTIALCKSUM) && 3345ff550d0eSmasputra (mp1 == NULL || mp1->b_cont == NULL) && 3346bd670b35SErik Nordmark offset >= DB_CKSUMSTART(mp) && 3347bd670b35SErik Nordmark ((len = offset - DB_CKSUMSTART(mp)) & 1) == 0) { 3348ff550d0eSmasputra uint32_t adj; 3349ff550d0eSmasputra /* 3350ff550d0eSmasputra * Partial checksum has been calculated by hardware 3351ff550d0eSmasputra * and attached to the packet; in addition, any 3352ff550d0eSmasputra * prepended extraneous data is even byte aligned. 3353ff550d0eSmasputra * If any such data exists, we adjust the checksum; 3354ff550d0eSmasputra * this would also handle any postpended data. 3355ff550d0eSmasputra */ 3356ff550d0eSmasputra IP_ADJCKSUM_PARTIAL(mp->b_rptr + DB_CKSUMSTART(mp), 3357ff550d0eSmasputra mp, mp1, len, adj); 3358ff550d0eSmasputra 3359ff550d0eSmasputra /* One's complement subtract extraneous checksum */ 3360ff550d0eSmasputra if (adj >= sum_val) 3361ff550d0eSmasputra sum_val = ~(adj - sum_val) & 0xFFFF; 3362ff550d0eSmasputra else 3363ff550d0eSmasputra sum_val -= adj; 3364ff550d0eSmasputra } 3365ff550d0eSmasputra } else { 3366ff550d0eSmasputra sum_val = 0; 3367ff550d0eSmasputra sum_flags = 0; 3368ff550d0eSmasputra } 3369ff550d0eSmasputra 3370ff550d0eSmasputra /* Clear hardware checksumming flag */ 3371ff550d0eSmasputra DB_CKSUMFLAGS(mp) = 0; 33727c478bd9Sstevel@tonic-gate 33737c478bd9Sstevel@tonic-gate /* 3374bd670b35SErik Nordmark * Determine the offset (from the begining of the IP header) 3375bd670b35SErik Nordmark * of the nexthdr value which has IPPROTO_FRAGMENT. We use 3376bd670b35SErik Nordmark * this when removing the fragment header from the packet. 3377bd670b35SErik Nordmark * This packet consists of the IPv6 header, a potential 3378bd670b35SErik Nordmark * hop-by-hop options header, a potential pre-routing-header 3379bd670b35SErik Nordmark * destination options header, and a potential routing header. 3380bd670b35SErik Nordmark */ 3381bd670b35SErik Nordmark prev_nexthdr_offset = (uint8_t *)&ip6h->ip6_nxt - (uint8_t *)ip6h; 3382bd670b35SErik Nordmark prev_nexthdr = ip6h->ip6_nxt; 3383bd670b35SErik Nordmark ptr = (uint8_t *)&ip6h[1]; 3384bd670b35SErik Nordmark 3385bd670b35SErik Nordmark if (prev_nexthdr == IPPROTO_HOPOPTS) { 3386bd670b35SErik Nordmark ip6_hbh_t *hbh_hdr; 3387bd670b35SErik Nordmark uint_t hdr_len; 3388bd670b35SErik Nordmark 3389bd670b35SErik Nordmark hbh_hdr = (ip6_hbh_t *)ptr; 3390bd670b35SErik Nordmark hdr_len = 8 * (hbh_hdr->ip6h_len + 1); 3391bd670b35SErik Nordmark prev_nexthdr = hbh_hdr->ip6h_nxt; 3392bd670b35SErik Nordmark prev_nexthdr_offset = (uint8_t *)&hbh_hdr->ip6h_nxt 3393bd670b35SErik Nordmark - (uint8_t *)ip6h; 3394bd670b35SErik Nordmark ptr += hdr_len; 3395bd670b35SErik Nordmark } 3396bd670b35SErik Nordmark if (prev_nexthdr == IPPROTO_DSTOPTS) { 3397bd670b35SErik Nordmark ip6_dest_t *dest_hdr; 3398bd670b35SErik Nordmark uint_t hdr_len; 3399bd670b35SErik Nordmark 3400bd670b35SErik Nordmark dest_hdr = (ip6_dest_t *)ptr; 3401bd670b35SErik Nordmark hdr_len = 8 * (dest_hdr->ip6d_len + 1); 3402bd670b35SErik Nordmark prev_nexthdr = dest_hdr->ip6d_nxt; 3403bd670b35SErik Nordmark prev_nexthdr_offset = (uint8_t *)&dest_hdr->ip6d_nxt 3404bd670b35SErik Nordmark - (uint8_t *)ip6h; 3405bd670b35SErik Nordmark ptr += hdr_len; 3406bd670b35SErik Nordmark } 3407bd670b35SErik Nordmark if (prev_nexthdr == IPPROTO_ROUTING) { 3408bd670b35SErik Nordmark ip6_rthdr_t *rthdr; 3409bd670b35SErik Nordmark uint_t hdr_len; 3410bd670b35SErik Nordmark 3411bd670b35SErik Nordmark rthdr = (ip6_rthdr_t *)ptr; 3412bd670b35SErik Nordmark prev_nexthdr = rthdr->ip6r_nxt; 3413bd670b35SErik Nordmark prev_nexthdr_offset = (uint8_t *)&rthdr->ip6r_nxt 3414bd670b35SErik Nordmark - (uint8_t *)ip6h; 3415bd670b35SErik Nordmark hdr_len = 8 * (rthdr->ip6r_len + 1); 3416bd670b35SErik Nordmark ptr += hdr_len; 3417bd670b35SErik Nordmark } 3418bd670b35SErik Nordmark if (prev_nexthdr != IPPROTO_FRAGMENT) { 3419bd670b35SErik Nordmark /* Can't handle other headers before the fragment header */ 3420bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors); 3421bd670b35SErik Nordmark ip_drop_input("ipIfStatsInHdrErrors", mp, ill); 3422bd670b35SErik Nordmark freemsg(mp); 3423bd670b35SErik Nordmark return (NULL); 3424bd670b35SErik Nordmark } 3425bd670b35SErik Nordmark 3426bd670b35SErik Nordmark /* 34277c478bd9Sstevel@tonic-gate * Note: Fragment offset in header is in 8-octet units. 34287c478bd9Sstevel@tonic-gate * Clearing least significant 3 bits not only extracts 34297c478bd9Sstevel@tonic-gate * it but also gets it in units of octets. 34307c478bd9Sstevel@tonic-gate */ 34317c478bd9Sstevel@tonic-gate offset = ntohs(fraghdr->ip6f_offlg) & ~7; 34327c478bd9Sstevel@tonic-gate more_frags = (fraghdr->ip6f_offlg & IP6F_MORE_FRAG); 34337c478bd9Sstevel@tonic-gate 34347c478bd9Sstevel@tonic-gate /* 34357c478bd9Sstevel@tonic-gate * Is the more frags flag on and the payload length not a multiple 34367c478bd9Sstevel@tonic-gate * of eight? 34377c478bd9Sstevel@tonic-gate */ 34387c478bd9Sstevel@tonic-gate if (more_frags && (ntohs(ip6h->ip6_plen) & 7)) { 3439bd670b35SErik Nordmark ip_drop_input("ICMP_PARAM_PROBLEM", mp, ill); 3440bd670b35SErik Nordmark icmp_param_problem_v6(mp, ICMP6_PARAMPROB_HEADER, 34417c478bd9Sstevel@tonic-gate (uint32_t)((char *)&ip6h->ip6_plen - 3442bd670b35SErik Nordmark (char *)ip6h), B_FALSE, ira); 34437c478bd9Sstevel@tonic-gate return (NULL); 34447c478bd9Sstevel@tonic-gate } 34457c478bd9Sstevel@tonic-gate 34467c478bd9Sstevel@tonic-gate v6src_ptr = &ip6h->ip6_src; 34477c478bd9Sstevel@tonic-gate v6dst_ptr = &ip6h->ip6_dst; 34487c478bd9Sstevel@tonic-gate end = remlen; 34497c478bd9Sstevel@tonic-gate 34507c478bd9Sstevel@tonic-gate hdr_length = (uint_t)((char *)&fraghdr[1] - (char *)ip6h); 34517c478bd9Sstevel@tonic-gate end += offset; 34527c478bd9Sstevel@tonic-gate 34537c478bd9Sstevel@tonic-gate /* 34547c478bd9Sstevel@tonic-gate * Would fragment cause reassembled packet to have a payload length 34557c478bd9Sstevel@tonic-gate * greater than IP_MAXPACKET - the max payload size? 34567c478bd9Sstevel@tonic-gate */ 34577c478bd9Sstevel@tonic-gate if (end > IP_MAXPACKET) { 34583173664eSapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors); 3459bd670b35SErik Nordmark ip_drop_input("Reassembled packet too large", mp, ill); 3460bd670b35SErik Nordmark icmp_param_problem_v6(mp, ICMP6_PARAMPROB_HEADER, 34617c478bd9Sstevel@tonic-gate (uint32_t)((char *)&fraghdr->ip6f_offlg - 3462bd670b35SErik Nordmark (char *)ip6h), B_FALSE, ira); 34637c478bd9Sstevel@tonic-gate return (NULL); 34647c478bd9Sstevel@tonic-gate } 34657c478bd9Sstevel@tonic-gate 34667c478bd9Sstevel@tonic-gate /* 34677c478bd9Sstevel@tonic-gate * This packet just has one fragment. Reassembly not 34687c478bd9Sstevel@tonic-gate * needed. 34697c478bd9Sstevel@tonic-gate */ 34707c478bd9Sstevel@tonic-gate if (!more_frags && offset == 0) { 34717c478bd9Sstevel@tonic-gate goto reass_done; 34727c478bd9Sstevel@tonic-gate } 34737c478bd9Sstevel@tonic-gate 34747c478bd9Sstevel@tonic-gate /* 34757c478bd9Sstevel@tonic-gate * Drop the fragmented as early as possible, if 34767c478bd9Sstevel@tonic-gate * we don't have resource(s) to re-assemble. 34777c478bd9Sstevel@tonic-gate */ 3478f4b3ec61Sdh155122 if (ipst->ips_ip_reass_queue_bytes == 0) { 34797c478bd9Sstevel@tonic-gate freemsg(mp); 34807c478bd9Sstevel@tonic-gate return (NULL); 34817c478bd9Sstevel@tonic-gate } 34827c478bd9Sstevel@tonic-gate 34837c478bd9Sstevel@tonic-gate /* Record the ECN field info. */ 34847c478bd9Sstevel@tonic-gate ecn_info = (uint8_t)(ntohl(ip6h->ip6_vcf & htonl(~0xFFCFFFFF)) >> 20); 34857c478bd9Sstevel@tonic-gate /* 34867c478bd9Sstevel@tonic-gate * If this is not the first fragment, dump the unfragmentable 34877c478bd9Sstevel@tonic-gate * portion of the packet. 34887c478bd9Sstevel@tonic-gate */ 34897c478bd9Sstevel@tonic-gate if (offset) 34907c478bd9Sstevel@tonic-gate mp->b_rptr = (uchar_t *)&fraghdr[1]; 34917c478bd9Sstevel@tonic-gate 34927c478bd9Sstevel@tonic-gate /* 34937c478bd9Sstevel@tonic-gate * Fragmentation reassembly. Each ILL has a hash table for 34947c478bd9Sstevel@tonic-gate * queueing packets undergoing reassembly for all IPIFs 34957c478bd9Sstevel@tonic-gate * associated with the ILL. The hash is based on the packet 34967c478bd9Sstevel@tonic-gate * IP ident field. The ILL frag hash table was allocated 34977c478bd9Sstevel@tonic-gate * as a timer block at the time the ILL was created. Whenever 34987c478bd9Sstevel@tonic-gate * there is anything on the reassembly queue, the timer will 34997c478bd9Sstevel@tonic-gate * be running. 35007c478bd9Sstevel@tonic-gate */ 3501bd670b35SErik Nordmark /* Handle vnic loopback of fragments */ 3502bd670b35SErik Nordmark if (mp->b_datap->db_ref > 2) 3503bd670b35SErik Nordmark msg_len = 0; 3504bd670b35SErik Nordmark else 3505ff550d0eSmasputra msg_len = MBLKSIZE(mp); 3506bd670b35SErik Nordmark 35077c478bd9Sstevel@tonic-gate tail_mp = mp; 35087c478bd9Sstevel@tonic-gate while (tail_mp->b_cont != NULL) { 35097c478bd9Sstevel@tonic-gate tail_mp = tail_mp->b_cont; 3510bd670b35SErik Nordmark if (tail_mp->b_datap->db_ref <= 2) 3511ff550d0eSmasputra msg_len += MBLKSIZE(tail_mp); 35127c478bd9Sstevel@tonic-gate } 35137c478bd9Sstevel@tonic-gate /* 35147c478bd9Sstevel@tonic-gate * If the reassembly list for this ILL will get too big 35157c478bd9Sstevel@tonic-gate * prune it. 35167c478bd9Sstevel@tonic-gate */ 35177c478bd9Sstevel@tonic-gate 35187c478bd9Sstevel@tonic-gate if ((msg_len + sizeof (*ipf) + ill->ill_frag_count) >= 3519f4b3ec61Sdh155122 ipst->ips_ip_reass_queue_bytes) { 3520bd670b35SErik Nordmark DTRACE_PROBE3(ip_reass_queue_bytes, uint_t, msg_len, 3521bd670b35SErik Nordmark uint_t, ill->ill_frag_count, 3522bd670b35SErik Nordmark uint_t, ipst->ips_ip_reass_queue_bytes); 3523f4b3ec61Sdh155122 ill_frag_prune(ill, 3524f4b3ec61Sdh155122 (ipst->ips_ip_reass_queue_bytes < msg_len) ? 0 : 3525f4b3ec61Sdh155122 (ipst->ips_ip_reass_queue_bytes - msg_len)); 35267c478bd9Sstevel@tonic-gate pruned = B_TRUE; 35277c478bd9Sstevel@tonic-gate } 35287c478bd9Sstevel@tonic-gate 35297c478bd9Sstevel@tonic-gate ipfb = &ill->ill_frag_hash_tbl[ILL_FRAG_HASH_V6(*v6src_ptr, ident)]; 35307c478bd9Sstevel@tonic-gate mutex_enter(&ipfb->ipfb_lock); 35317c478bd9Sstevel@tonic-gate 35327c478bd9Sstevel@tonic-gate ipfp = &ipfb->ipfb_ipf; 35337c478bd9Sstevel@tonic-gate /* Try to find an existing fragment queue for this packet. */ 35347c478bd9Sstevel@tonic-gate for (;;) { 35357c478bd9Sstevel@tonic-gate ipf = ipfp[0]; 35367c478bd9Sstevel@tonic-gate if (ipf) { 35377c478bd9Sstevel@tonic-gate /* 35387c478bd9Sstevel@tonic-gate * It has to match on ident, source address, and 35397c478bd9Sstevel@tonic-gate * dest address. 35407c478bd9Sstevel@tonic-gate */ 35417c478bd9Sstevel@tonic-gate if (ipf->ipf_ident == ident && 35427c478bd9Sstevel@tonic-gate IN6_ARE_ADDR_EQUAL(&ipf->ipf_v6src, v6src_ptr) && 35437c478bd9Sstevel@tonic-gate IN6_ARE_ADDR_EQUAL(&ipf->ipf_v6dst, v6dst_ptr)) { 35447c478bd9Sstevel@tonic-gate 35457c478bd9Sstevel@tonic-gate /* 35467c478bd9Sstevel@tonic-gate * If we have received too many 35477c478bd9Sstevel@tonic-gate * duplicate fragments for this packet 35487c478bd9Sstevel@tonic-gate * free it. 35497c478bd9Sstevel@tonic-gate */ 35507c478bd9Sstevel@tonic-gate if (ipf->ipf_num_dups > ip_max_frag_dups) { 35517c478bd9Sstevel@tonic-gate ill_frag_free_pkts(ill, ipfb, ipf, 1); 35527c478bd9Sstevel@tonic-gate freemsg(mp); 35537c478bd9Sstevel@tonic-gate mutex_exit(&ipfb->ipfb_lock); 35547c478bd9Sstevel@tonic-gate return (NULL); 35557c478bd9Sstevel@tonic-gate } 35567c478bd9Sstevel@tonic-gate 35577c478bd9Sstevel@tonic-gate break; 35587c478bd9Sstevel@tonic-gate } 35597c478bd9Sstevel@tonic-gate ipfp = &ipf->ipf_hash_next; 35607c478bd9Sstevel@tonic-gate continue; 35617c478bd9Sstevel@tonic-gate } 35627c478bd9Sstevel@tonic-gate 35637c478bd9Sstevel@tonic-gate 35647c478bd9Sstevel@tonic-gate /* 35657c478bd9Sstevel@tonic-gate * If we pruned the list, do we want to store this new 35667c478bd9Sstevel@tonic-gate * fragment?. We apply an optimization here based on the 35677c478bd9Sstevel@tonic-gate * fact that most fragments will be received in order. 35687c478bd9Sstevel@tonic-gate * So if the offset of this incoming fragment is zero, 35697c478bd9Sstevel@tonic-gate * it is the first fragment of a new packet. We will 35707c478bd9Sstevel@tonic-gate * keep it. Otherwise drop the fragment, as we have 35717c478bd9Sstevel@tonic-gate * probably pruned the packet already (since the 35727c478bd9Sstevel@tonic-gate * packet cannot be found). 35737c478bd9Sstevel@tonic-gate */ 35747c478bd9Sstevel@tonic-gate 35757c478bd9Sstevel@tonic-gate if (pruned && offset != 0) { 35767c478bd9Sstevel@tonic-gate mutex_exit(&ipfb->ipfb_lock); 35777c478bd9Sstevel@tonic-gate freemsg(mp); 35787c478bd9Sstevel@tonic-gate return (NULL); 35797c478bd9Sstevel@tonic-gate } 35807c478bd9Sstevel@tonic-gate 35817c478bd9Sstevel@tonic-gate /* New guy. Allocate a frag message. */ 35827c478bd9Sstevel@tonic-gate mp1 = allocb(sizeof (*ipf), BPRI_MED); 35837c478bd9Sstevel@tonic-gate if (!mp1) { 35843173664eSapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 3585bd670b35SErik Nordmark ip_drop_input("ipIfStatsInDiscards", mp, ill); 35867c478bd9Sstevel@tonic-gate freemsg(mp); 35877c478bd9Sstevel@tonic-gate partial_reass_done: 35887c478bd9Sstevel@tonic-gate mutex_exit(&ipfb->ipfb_lock); 35897c478bd9Sstevel@tonic-gate return (NULL); 35907c478bd9Sstevel@tonic-gate } 35917c478bd9Sstevel@tonic-gate 3592f4b3ec61Sdh155122 if (ipfb->ipfb_frag_pkts >= MAX_FRAG_PKTS(ipst)) { 35937c478bd9Sstevel@tonic-gate /* 35947c478bd9Sstevel@tonic-gate * Too many fragmented packets in this hash bucket. 35957c478bd9Sstevel@tonic-gate * Free the oldest. 35967c478bd9Sstevel@tonic-gate */ 35977c478bd9Sstevel@tonic-gate ill_frag_free_pkts(ill, ipfb, ipfb->ipfb_ipf, 1); 35987c478bd9Sstevel@tonic-gate } 35997c478bd9Sstevel@tonic-gate 36007c478bd9Sstevel@tonic-gate mp1->b_cont = mp; 36017c478bd9Sstevel@tonic-gate 36027c478bd9Sstevel@tonic-gate /* Initialize the fragment header. */ 36037c478bd9Sstevel@tonic-gate ipf = (ipf_t *)mp1->b_rptr; 36047c478bd9Sstevel@tonic-gate ipf->ipf_mp = mp1; 36057c478bd9Sstevel@tonic-gate ipf->ipf_ptphn = ipfp; 36067c478bd9Sstevel@tonic-gate ipfp[0] = ipf; 36077c478bd9Sstevel@tonic-gate ipf->ipf_hash_next = NULL; 36087c478bd9Sstevel@tonic-gate ipf->ipf_ident = ident; 36097c478bd9Sstevel@tonic-gate ipf->ipf_v6src = *v6src_ptr; 36107c478bd9Sstevel@tonic-gate ipf->ipf_v6dst = *v6dst_ptr; 36117c478bd9Sstevel@tonic-gate /* Record reassembly start time. */ 36127c478bd9Sstevel@tonic-gate ipf->ipf_timestamp = gethrestime_sec(); 36137c478bd9Sstevel@tonic-gate /* Record ipf generation and account for frag header */ 36147c478bd9Sstevel@tonic-gate ipf->ipf_gen = ill->ill_ipf_gen++; 3615ff550d0eSmasputra ipf->ipf_count = MBLKSIZE(mp1); 36167c478bd9Sstevel@tonic-gate ipf->ipf_protocol = nexthdr; 36177c478bd9Sstevel@tonic-gate ipf->ipf_nf_hdr_len = 0; 36187c478bd9Sstevel@tonic-gate ipf->ipf_prev_nexthdr_offset = 0; 36197c478bd9Sstevel@tonic-gate ipf->ipf_last_frag_seen = B_FALSE; 36207c478bd9Sstevel@tonic-gate ipf->ipf_ecn = ecn_info; 36217c478bd9Sstevel@tonic-gate ipf->ipf_num_dups = 0; 36227c478bd9Sstevel@tonic-gate ipfb->ipfb_frag_pkts++; 3623ff550d0eSmasputra ipf->ipf_checksum = 0; 3624ff550d0eSmasputra ipf->ipf_checksum_flags = 0; 3625ff550d0eSmasputra 3626ff550d0eSmasputra /* Store checksum value in fragment header */ 3627ff550d0eSmasputra if (sum_flags != 0) { 3628ff550d0eSmasputra sum_val = (sum_val & 0xFFFF) + (sum_val >> 16); 3629ff550d0eSmasputra sum_val = (sum_val & 0xFFFF) + (sum_val >> 16); 3630ff550d0eSmasputra ipf->ipf_checksum = sum_val; 3631ff550d0eSmasputra ipf->ipf_checksum_flags = sum_flags; 3632ff550d0eSmasputra } 36337c478bd9Sstevel@tonic-gate 36347c478bd9Sstevel@tonic-gate /* 36357c478bd9Sstevel@tonic-gate * We handle reassembly two ways. In the easy case, 36367c478bd9Sstevel@tonic-gate * where all the fragments show up in order, we do 36377c478bd9Sstevel@tonic-gate * minimal bookkeeping, and just clip new pieces on 36387c478bd9Sstevel@tonic-gate * the end. If we ever see a hole, then we go off 36397c478bd9Sstevel@tonic-gate * to ip_reassemble which has to mark the pieces and 36407c478bd9Sstevel@tonic-gate * keep track of the number of holes, etc. Obviously, 36417c478bd9Sstevel@tonic-gate * the point of having both mechanisms is so we can 36427c478bd9Sstevel@tonic-gate * handle the easy case as efficiently as possible. 36437c478bd9Sstevel@tonic-gate */ 36447c478bd9Sstevel@tonic-gate if (offset == 0) { 36457c478bd9Sstevel@tonic-gate /* Easy case, in-order reassembly so far. */ 36467c478bd9Sstevel@tonic-gate /* Update the byte count */ 36477c478bd9Sstevel@tonic-gate ipf->ipf_count += msg_len; 36487c478bd9Sstevel@tonic-gate ipf->ipf_tail_mp = tail_mp; 36497c478bd9Sstevel@tonic-gate /* 36507c478bd9Sstevel@tonic-gate * Keep track of next expected offset in 36517c478bd9Sstevel@tonic-gate * ipf_end. 36527c478bd9Sstevel@tonic-gate */ 36537c478bd9Sstevel@tonic-gate ipf->ipf_end = end; 36547c478bd9Sstevel@tonic-gate ipf->ipf_nf_hdr_len = hdr_length; 3655bd670b35SErik Nordmark ipf->ipf_prev_nexthdr_offset = prev_nexthdr_offset; 36567c478bd9Sstevel@tonic-gate } else { 36577c478bd9Sstevel@tonic-gate /* Hard case, hole at the beginning. */ 36587c478bd9Sstevel@tonic-gate ipf->ipf_tail_mp = NULL; 36597c478bd9Sstevel@tonic-gate /* 36607c478bd9Sstevel@tonic-gate * ipf_end == 0 means that we have given up 36617c478bd9Sstevel@tonic-gate * on easy reassembly. 36627c478bd9Sstevel@tonic-gate */ 36637c478bd9Sstevel@tonic-gate ipf->ipf_end = 0; 3664ff550d0eSmasputra 3665ff550d0eSmasputra /* Forget checksum offload from now on */ 3666ff550d0eSmasputra ipf->ipf_checksum_flags = 0; 3667ff550d0eSmasputra 36687c478bd9Sstevel@tonic-gate /* 36697c478bd9Sstevel@tonic-gate * ipf_hole_cnt is set by ip_reassemble. 36707c478bd9Sstevel@tonic-gate * ipf_count is updated by ip_reassemble. 36717c478bd9Sstevel@tonic-gate * No need to check for return value here 36727c478bd9Sstevel@tonic-gate * as we don't expect reassembly to complete or 36737c478bd9Sstevel@tonic-gate * fail for the first fragment itself. 36747c478bd9Sstevel@tonic-gate */ 36757c478bd9Sstevel@tonic-gate (void) ip_reassemble(mp, ipf, offset, more_frags, ill, 36767c478bd9Sstevel@tonic-gate msg_len); 36777c478bd9Sstevel@tonic-gate } 36787c478bd9Sstevel@tonic-gate /* Update per ipfb and ill byte counts */ 36797c478bd9Sstevel@tonic-gate ipfb->ipfb_count += ipf->ipf_count; 36807c478bd9Sstevel@tonic-gate ASSERT(ipfb->ipfb_count > 0); /* Wraparound */ 36810a5d959fSgeorges atomic_add_32(&ill->ill_frag_count, ipf->ipf_count); 36827c478bd9Sstevel@tonic-gate /* If the frag timer wasn't already going, start it. */ 36837c478bd9Sstevel@tonic-gate mutex_enter(&ill->ill_lock); 36847c478bd9Sstevel@tonic-gate ill_frag_timer_start(ill); 36857c478bd9Sstevel@tonic-gate mutex_exit(&ill->ill_lock); 36867c478bd9Sstevel@tonic-gate goto partial_reass_done; 36877c478bd9Sstevel@tonic-gate } 36887c478bd9Sstevel@tonic-gate 36897c478bd9Sstevel@tonic-gate /* 3690ff550d0eSmasputra * If the packet's flag has changed (it could be coming up 3691ff550d0eSmasputra * from an interface different than the previous, therefore 3692ff550d0eSmasputra * possibly different checksum capability), then forget about 3693ff550d0eSmasputra * any stored checksum states. Otherwise add the value to 3694ff550d0eSmasputra * the existing one stored in the fragment header. 3695ff550d0eSmasputra */ 3696ff550d0eSmasputra if (sum_flags != 0 && sum_flags == ipf->ipf_checksum_flags) { 3697ff550d0eSmasputra sum_val += ipf->ipf_checksum; 3698ff550d0eSmasputra sum_val = (sum_val & 0xFFFF) + (sum_val >> 16); 3699ff550d0eSmasputra sum_val = (sum_val & 0xFFFF) + (sum_val >> 16); 3700ff550d0eSmasputra ipf->ipf_checksum = sum_val; 3701ff550d0eSmasputra } else if (ipf->ipf_checksum_flags != 0) { 3702ff550d0eSmasputra /* Forget checksum offload from now on */ 3703ff550d0eSmasputra ipf->ipf_checksum_flags = 0; 3704ff550d0eSmasputra } 3705ff550d0eSmasputra 3706ff550d0eSmasputra /* 37077c478bd9Sstevel@tonic-gate * We have a new piece of a datagram which is already being 37087c478bd9Sstevel@tonic-gate * reassembled. Update the ECN info if all IP fragments 37097c478bd9Sstevel@tonic-gate * are ECN capable. If there is one which is not, clear 37107c478bd9Sstevel@tonic-gate * all the info. If there is at least one which has CE 37117c478bd9Sstevel@tonic-gate * code point, IP needs to report that up to transport. 37127c478bd9Sstevel@tonic-gate */ 37137c478bd9Sstevel@tonic-gate if (ecn_info != IPH_ECN_NECT && ipf->ipf_ecn != IPH_ECN_NECT) { 37147c478bd9Sstevel@tonic-gate if (ecn_info == IPH_ECN_CE) 37157c478bd9Sstevel@tonic-gate ipf->ipf_ecn = IPH_ECN_CE; 37167c478bd9Sstevel@tonic-gate } else { 37177c478bd9Sstevel@tonic-gate ipf->ipf_ecn = IPH_ECN_NECT; 37187c478bd9Sstevel@tonic-gate } 37197c478bd9Sstevel@tonic-gate 37207c478bd9Sstevel@tonic-gate if (offset && ipf->ipf_end == offset) { 37217c478bd9Sstevel@tonic-gate /* The new fragment fits at the end */ 37227c478bd9Sstevel@tonic-gate ipf->ipf_tail_mp->b_cont = mp; 37237c478bd9Sstevel@tonic-gate /* Update the byte count */ 37247c478bd9Sstevel@tonic-gate ipf->ipf_count += msg_len; 37257c478bd9Sstevel@tonic-gate /* Update per ipfb and ill byte counts */ 37267c478bd9Sstevel@tonic-gate ipfb->ipfb_count += msg_len; 37277c478bd9Sstevel@tonic-gate ASSERT(ipfb->ipfb_count > 0); /* Wraparound */ 37280a5d959fSgeorges atomic_add_32(&ill->ill_frag_count, msg_len); 37297c478bd9Sstevel@tonic-gate if (more_frags) { 37307c478bd9Sstevel@tonic-gate /* More to come. */ 37317c478bd9Sstevel@tonic-gate ipf->ipf_end = end; 37327c478bd9Sstevel@tonic-gate ipf->ipf_tail_mp = tail_mp; 37337c478bd9Sstevel@tonic-gate goto partial_reass_done; 37347c478bd9Sstevel@tonic-gate } 37357c478bd9Sstevel@tonic-gate } else { 37367c478bd9Sstevel@tonic-gate /* 37377c478bd9Sstevel@tonic-gate * Go do the hard cases. 37387c478bd9Sstevel@tonic-gate * Call ip_reassemble(). 37397c478bd9Sstevel@tonic-gate */ 37407c478bd9Sstevel@tonic-gate int ret; 37417c478bd9Sstevel@tonic-gate 37427c478bd9Sstevel@tonic-gate if (offset == 0) { 37437c478bd9Sstevel@tonic-gate if (ipf->ipf_prev_nexthdr_offset == 0) { 37447c478bd9Sstevel@tonic-gate ipf->ipf_nf_hdr_len = hdr_length; 37457c478bd9Sstevel@tonic-gate ipf->ipf_prev_nexthdr_offset = 3746bd670b35SErik Nordmark prev_nexthdr_offset; 37477c478bd9Sstevel@tonic-gate } 37487c478bd9Sstevel@tonic-gate } 37497c478bd9Sstevel@tonic-gate /* Save current byte count */ 37507c478bd9Sstevel@tonic-gate count = ipf->ipf_count; 37517c478bd9Sstevel@tonic-gate ret = ip_reassemble(mp, ipf, offset, more_frags, ill, msg_len); 37527c478bd9Sstevel@tonic-gate 37537c478bd9Sstevel@tonic-gate /* Count of bytes added and subtracted (freeb()ed) */ 37547c478bd9Sstevel@tonic-gate count = ipf->ipf_count - count; 37557c478bd9Sstevel@tonic-gate if (count) { 37567c478bd9Sstevel@tonic-gate /* Update per ipfb and ill byte counts */ 37577c478bd9Sstevel@tonic-gate ipfb->ipfb_count += count; 37587c478bd9Sstevel@tonic-gate ASSERT(ipfb->ipfb_count > 0); /* Wraparound */ 37590a5d959fSgeorges atomic_add_32(&ill->ill_frag_count, count); 37607c478bd9Sstevel@tonic-gate } 37617c478bd9Sstevel@tonic-gate if (ret == IP_REASS_PARTIAL) { 37627c478bd9Sstevel@tonic-gate goto partial_reass_done; 37637c478bd9Sstevel@tonic-gate } else if (ret == IP_REASS_FAILED) { 37647c478bd9Sstevel@tonic-gate /* Reassembly failed. Free up all resources */ 37657c478bd9Sstevel@tonic-gate ill_frag_free_pkts(ill, ipfb, ipf, 1); 37667c478bd9Sstevel@tonic-gate for (t_mp = mp; t_mp != NULL; t_mp = t_mp->b_cont) { 37677c478bd9Sstevel@tonic-gate IP_REASS_SET_START(t_mp, 0); 37687c478bd9Sstevel@tonic-gate IP_REASS_SET_END(t_mp, 0); 37697c478bd9Sstevel@tonic-gate } 37707c478bd9Sstevel@tonic-gate freemsg(mp); 37717c478bd9Sstevel@tonic-gate goto partial_reass_done; 37727c478bd9Sstevel@tonic-gate } 37737c478bd9Sstevel@tonic-gate 37747c478bd9Sstevel@tonic-gate /* We will reach here iff 'ret' is IP_REASS_COMPLETE */ 37757c478bd9Sstevel@tonic-gate } 37767c478bd9Sstevel@tonic-gate /* 37777c478bd9Sstevel@tonic-gate * We have completed reassembly. Unhook the frag header from 37787c478bd9Sstevel@tonic-gate * the reassembly list. 37797c478bd9Sstevel@tonic-gate * 37807c478bd9Sstevel@tonic-gate * Grab the unfragmentable header length next header value out 37817c478bd9Sstevel@tonic-gate * of the first fragment 37827c478bd9Sstevel@tonic-gate */ 37837c478bd9Sstevel@tonic-gate ASSERT(ipf->ipf_nf_hdr_len != 0); 37847c478bd9Sstevel@tonic-gate hdr_length = ipf->ipf_nf_hdr_len; 37857c478bd9Sstevel@tonic-gate 37867c478bd9Sstevel@tonic-gate /* 37877c478bd9Sstevel@tonic-gate * Before we free the frag header, record the ECN info 37887c478bd9Sstevel@tonic-gate * to report back to the transport. 37897c478bd9Sstevel@tonic-gate */ 37907c478bd9Sstevel@tonic-gate ecn_info = ipf->ipf_ecn; 37917c478bd9Sstevel@tonic-gate 37927c478bd9Sstevel@tonic-gate /* 37937c478bd9Sstevel@tonic-gate * Store the nextheader field in the header preceding the fragment 37947c478bd9Sstevel@tonic-gate * header 37957c478bd9Sstevel@tonic-gate */ 37967c478bd9Sstevel@tonic-gate nexthdr = ipf->ipf_protocol; 3797bd670b35SErik Nordmark prev_nexthdr_offset = ipf->ipf_prev_nexthdr_offset; 37987c478bd9Sstevel@tonic-gate ipfp = ipf->ipf_ptphn; 3799ff550d0eSmasputra 3800ff550d0eSmasputra /* We need to supply these to caller */ 3801ff550d0eSmasputra if ((sum_flags = ipf->ipf_checksum_flags) != 0) 3802ff550d0eSmasputra sum_val = ipf->ipf_checksum; 3803ff550d0eSmasputra else 3804ff550d0eSmasputra sum_val = 0; 3805ff550d0eSmasputra 38067c478bd9Sstevel@tonic-gate mp1 = ipf->ipf_mp; 38077c478bd9Sstevel@tonic-gate count = ipf->ipf_count; 38087c478bd9Sstevel@tonic-gate ipf = ipf->ipf_hash_next; 38097c478bd9Sstevel@tonic-gate if (ipf) 38107c478bd9Sstevel@tonic-gate ipf->ipf_ptphn = ipfp; 38117c478bd9Sstevel@tonic-gate ipfp[0] = ipf; 38120a5d959fSgeorges atomic_add_32(&ill->ill_frag_count, -count); 38137c478bd9Sstevel@tonic-gate ASSERT(ipfb->ipfb_count >= count); 38147c478bd9Sstevel@tonic-gate ipfb->ipfb_count -= count; 38157c478bd9Sstevel@tonic-gate ipfb->ipfb_frag_pkts--; 38167c478bd9Sstevel@tonic-gate mutex_exit(&ipfb->ipfb_lock); 38177c478bd9Sstevel@tonic-gate /* Ditch the frag header. */ 38187c478bd9Sstevel@tonic-gate mp = mp1->b_cont; 38197c478bd9Sstevel@tonic-gate freeb(mp1); 38207c478bd9Sstevel@tonic-gate 38217c478bd9Sstevel@tonic-gate /* 38227c478bd9Sstevel@tonic-gate * Make sure the packet is good by doing some sanity 38237c478bd9Sstevel@tonic-gate * check. If bad we can silentely drop the packet. 38247c478bd9Sstevel@tonic-gate */ 38257c478bd9Sstevel@tonic-gate reass_done: 38267c478bd9Sstevel@tonic-gate if (hdr_length < sizeof (ip6_frag_t)) { 38273173664eSapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors); 3828bd670b35SErik Nordmark ip_drop_input("ipIfStatsInHdrErrors", mp, ill); 3829bd670b35SErik Nordmark ip1dbg(("ip_input_fragment_v6: bad packet\n")); 38307c478bd9Sstevel@tonic-gate freemsg(mp); 38317c478bd9Sstevel@tonic-gate return (NULL); 38327c478bd9Sstevel@tonic-gate } 38337c478bd9Sstevel@tonic-gate 38347c478bd9Sstevel@tonic-gate /* 38357c478bd9Sstevel@tonic-gate * Remove the fragment header from the initial header by 38367c478bd9Sstevel@tonic-gate * splitting the mblk into the non-fragmentable header and 38377c478bd9Sstevel@tonic-gate * everthing after the fragment extension header. This has the 38387c478bd9Sstevel@tonic-gate * side effect of putting all the headers that need destination 38397c478bd9Sstevel@tonic-gate * processing into the b_cont block-- on return this fact is 38407c478bd9Sstevel@tonic-gate * used in order to avoid having to look at the extensions 38417c478bd9Sstevel@tonic-gate * already processed. 38427c478bd9Sstevel@tonic-gate * 38437c478bd9Sstevel@tonic-gate * Note that this code assumes that the unfragmentable portion 38447c478bd9Sstevel@tonic-gate * of the header is in the first mblk and increments 38457c478bd9Sstevel@tonic-gate * the read pointer past it. If this assumption is broken 38467c478bd9Sstevel@tonic-gate * this code fails badly. 38477c478bd9Sstevel@tonic-gate */ 38487c478bd9Sstevel@tonic-gate if (mp->b_rptr + hdr_length != mp->b_wptr) { 38497c478bd9Sstevel@tonic-gate mblk_t *nmp; 38507c478bd9Sstevel@tonic-gate 38517c478bd9Sstevel@tonic-gate if (!(nmp = dupb(mp))) { 3852bd670b35SErik Nordmark ip1dbg(("ip_input_fragment_v6: dupb failed\n")); 38533173664eSapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 3854bd670b35SErik Nordmark ip_drop_input("ipIfStatsInDiscards", mp, ill); 38557c478bd9Sstevel@tonic-gate freemsg(mp); 38567c478bd9Sstevel@tonic-gate return (NULL); 38577c478bd9Sstevel@tonic-gate } 38587c478bd9Sstevel@tonic-gate nmp->b_cont = mp->b_cont; 38597c478bd9Sstevel@tonic-gate mp->b_cont = nmp; 38607c478bd9Sstevel@tonic-gate nmp->b_rptr += hdr_length; 38617c478bd9Sstevel@tonic-gate } 38627c478bd9Sstevel@tonic-gate mp->b_wptr = mp->b_rptr + hdr_length - sizeof (ip6_frag_t); 38637c478bd9Sstevel@tonic-gate 38647c478bd9Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 3865bd670b35SErik Nordmark ((char *)ip6h)[prev_nexthdr_offset] = nexthdr; 38667c478bd9Sstevel@tonic-gate 38677c478bd9Sstevel@tonic-gate /* Restore original IP length in header. */ 3868bd670b35SErik Nordmark packet_size = msgdsize(mp); 3869bd670b35SErik Nordmark ip6h->ip6_plen = htons((uint16_t)(packet_size - IPV6_HDR_LEN)); 38707c478bd9Sstevel@tonic-gate /* Record the ECN info. */ 38717c478bd9Sstevel@tonic-gate ip6h->ip6_vcf &= htonl(0xFFCFFFFF); 38727c478bd9Sstevel@tonic-gate ip6h->ip6_vcf |= htonl(ecn_info << 20); 38737c478bd9Sstevel@tonic-gate 3874bd670b35SErik Nordmark /* Update the receive attributes */ 3875bd670b35SErik Nordmark ira->ira_pktlen = packet_size; 3876bd670b35SErik Nordmark ira->ira_ip_hdr_length = hdr_length - sizeof (ip6_frag_t); 3877bd670b35SErik Nordmark ira->ira_protocol = nexthdr; 3878bd670b35SErik Nordmark 3879bd670b35SErik Nordmark /* Reassembly is successful; set checksum information in packet */ 3880bd670b35SErik Nordmark DB_CKSUM16(mp) = (uint16_t)sum_val; 3881bd670b35SErik Nordmark DB_CKSUMFLAGS(mp) = sum_flags; 3882bd670b35SErik Nordmark DB_CKSUMSTART(mp) = ira->ira_ip_hdr_length; 3883ff550d0eSmasputra 38847c478bd9Sstevel@tonic-gate return (mp); 38857c478bd9Sstevel@tonic-gate } 38867c478bd9Sstevel@tonic-gate 38877c478bd9Sstevel@tonic-gate /* 388805d90a32SDan McDonald * Given an mblk and a ptr, find the destination address in an IPv6 routing 388905d90a32SDan McDonald * header. 389005d90a32SDan McDonald */ 389105d90a32SDan McDonald static in6_addr_t 3892bd670b35SErik Nordmark pluck_out_dst(const mblk_t *mp, uint8_t *whereptr, in6_addr_t oldrv) 389305d90a32SDan McDonald { 389405d90a32SDan McDonald ip6_rthdr0_t *rt0; 389505d90a32SDan McDonald int segleft, numaddr; 389605d90a32SDan McDonald in6_addr_t *ap, rv = oldrv; 389705d90a32SDan McDonald 389805d90a32SDan McDonald rt0 = (ip6_rthdr0_t *)whereptr; 389905d90a32SDan McDonald if (rt0->ip6r0_type != 0 && rt0->ip6r0_type != 2) { 390005d90a32SDan McDonald DTRACE_PROBE2(pluck_out_dst_unknown_type, mblk_t *, mp, 390105d90a32SDan McDonald uint8_t *, whereptr); 390205d90a32SDan McDonald return (rv); 390305d90a32SDan McDonald } 390405d90a32SDan McDonald segleft = rt0->ip6r0_segleft; 390505d90a32SDan McDonald numaddr = rt0->ip6r0_len / 2; 390605d90a32SDan McDonald 390705d90a32SDan McDonald if ((rt0->ip6r0_len & 0x1) || 3908bd670b35SErik Nordmark (mp != NULL && whereptr + (rt0->ip6r0_len + 1) * 8 > mp->b_wptr) || 390905d90a32SDan McDonald (segleft > rt0->ip6r0_len / 2)) { 391005d90a32SDan McDonald /* 391105d90a32SDan McDonald * Corrupt packet. Either the routing header length is odd 391205d90a32SDan McDonald * (can't happen) or mismatched compared to the packet, or the 391305d90a32SDan McDonald * number of addresses is. Return what we can. This will 391405d90a32SDan McDonald * only be a problem on forwarded packets that get squeezed 391505d90a32SDan McDonald * through an outbound tunnel enforcing IPsec Tunnel Mode. 391605d90a32SDan McDonald */ 391705d90a32SDan McDonald DTRACE_PROBE2(pluck_out_dst_badpkt, mblk_t *, mp, uint8_t *, 391805d90a32SDan McDonald whereptr); 391905d90a32SDan McDonald return (rv); 392005d90a32SDan McDonald } 392105d90a32SDan McDonald 392205d90a32SDan McDonald if (segleft != 0) { 392305d90a32SDan McDonald ap = (in6_addr_t *)((char *)rt0 + sizeof (*rt0)); 392405d90a32SDan McDonald rv = ap[numaddr - 1]; 392505d90a32SDan McDonald } 392605d90a32SDan McDonald 392705d90a32SDan McDonald return (rv); 392805d90a32SDan McDonald } 392905d90a32SDan McDonald 393005d90a32SDan McDonald /* 39317c478bd9Sstevel@tonic-gate * Walk through the options to see if there is a routing header. 39327c478bd9Sstevel@tonic-gate * If present get the destination which is the last address of 39337c478bd9Sstevel@tonic-gate * the option. 3934bd670b35SErik Nordmark * mp needs to be provided in cases when the extension headers might span 3935bd670b35SErik Nordmark * b_cont; mp is never modified by this function. 39367c478bd9Sstevel@tonic-gate */ 39377c478bd9Sstevel@tonic-gate in6_addr_t 3938bd670b35SErik Nordmark ip_get_dst_v6(ip6_t *ip6h, const mblk_t *mp, boolean_t *is_fragment) 39397c478bd9Sstevel@tonic-gate { 3940bd670b35SErik Nordmark const mblk_t *current_mp = mp; 39417c478bd9Sstevel@tonic-gate uint8_t nexthdr; 39427c478bd9Sstevel@tonic-gate uint8_t *whereptr; 39437c478bd9Sstevel@tonic-gate int ehdrlen; 394405d90a32SDan McDonald in6_addr_t rv; 39453062294fSDan McDonald 39463062294fSDan McDonald whereptr = (uint8_t *)ip6h; 39473062294fSDan McDonald ehdrlen = sizeof (ip6_t); 39483062294fSDan McDonald 394905d90a32SDan McDonald /* We assume at least the IPv6 base header is within one mblk. */ 3950bd670b35SErik Nordmark ASSERT(mp == NULL || 3951bd670b35SErik Nordmark (mp->b_rptr <= whereptr && mp->b_wptr >= whereptr + ehdrlen)); 395205d90a32SDan McDonald 395305d90a32SDan McDonald rv = ip6h->ip6_dst; 395405d90a32SDan McDonald nexthdr = ip6h->ip6_nxt; 395505d90a32SDan McDonald if (is_fragment != NULL) 395605d90a32SDan McDonald *is_fragment = B_FALSE; 395705d90a32SDan McDonald 395805d90a32SDan McDonald /* 395905d90a32SDan McDonald * We also assume (thanks to ipsec_tun_outbound()'s pullup) that 396005d90a32SDan McDonald * no extension headers will be split across mblks. 396105d90a32SDan McDonald */ 396205d90a32SDan McDonald 396305d90a32SDan McDonald while (nexthdr == IPPROTO_HOPOPTS || nexthdr == IPPROTO_DSTOPTS || 396405d90a32SDan McDonald nexthdr == IPPROTO_ROUTING) { 396505d90a32SDan McDonald if (nexthdr == IPPROTO_ROUTING) 396605d90a32SDan McDonald rv = pluck_out_dst(current_mp, whereptr, rv); 396705d90a32SDan McDonald 396805d90a32SDan McDonald /* 396905d90a32SDan McDonald * All IPv6 extension headers have the next-header in byte 397005d90a32SDan McDonald * 0, and the (length - 8) in 8-byte-words. 397105d90a32SDan McDonald */ 3972bd670b35SErik Nordmark while (current_mp != NULL && 3973bd670b35SErik Nordmark whereptr + ehdrlen >= current_mp->b_wptr) { 39743062294fSDan McDonald ehdrlen -= (current_mp->b_wptr - whereptr); 39753062294fSDan McDonald current_mp = current_mp->b_cont; 39763062294fSDan McDonald if (current_mp == NULL) { 39773062294fSDan McDonald /* Bad packet. Return what we can. */ 397805d90a32SDan McDonald DTRACE_PROBE3(ip_get_dst_v6_badpkt, mblk_t *, 397905d90a32SDan McDonald mp, mblk_t *, current_mp, ip6_t *, ip6h); 39803062294fSDan McDonald goto done; 39813062294fSDan McDonald } 39823062294fSDan McDonald whereptr = current_mp->b_rptr; 39833062294fSDan McDonald } 39843062294fSDan McDonald whereptr += ehdrlen; 39853062294fSDan McDonald 398605d90a32SDan McDonald nexthdr = *whereptr; 3987bd670b35SErik Nordmark ASSERT(current_mp == NULL || whereptr + 1 < current_mp->b_wptr); 398805d90a32SDan McDonald ehdrlen = (*(whereptr + 1) + 1) * 8; 39897c478bd9Sstevel@tonic-gate } 39907c478bd9Sstevel@tonic-gate 39917c478bd9Sstevel@tonic-gate done: 399205d90a32SDan McDonald if (nexthdr == IPPROTO_FRAGMENT && is_fragment != NULL) 399305d90a32SDan McDonald *is_fragment = B_TRUE; 39947c478bd9Sstevel@tonic-gate return (rv); 39957c478bd9Sstevel@tonic-gate } 39967c478bd9Sstevel@tonic-gate 39977c478bd9Sstevel@tonic-gate /* 39987c478bd9Sstevel@tonic-gate * ip_source_routed_v6: 3999bd670b35SErik Nordmark * This function is called by redirect code (called from ip_input_v6) to 40007c478bd9Sstevel@tonic-gate * know whether this packet is source routed through this node i.e 40017c478bd9Sstevel@tonic-gate * whether this node (router) is part of the journey. This 40027c478bd9Sstevel@tonic-gate * function is called under two cases : 40037c478bd9Sstevel@tonic-gate * 40047c478bd9Sstevel@tonic-gate * case 1 : Routing header was processed by this node and 40057c478bd9Sstevel@tonic-gate * ip_process_rthdr replaced ip6_dst with the next hop 40067c478bd9Sstevel@tonic-gate * and we are forwarding the packet to the next hop. 40077c478bd9Sstevel@tonic-gate * 40087c478bd9Sstevel@tonic-gate * case 2 : Routing header was not processed by this node and we 40097c478bd9Sstevel@tonic-gate * are just forwarding the packet. 40107c478bd9Sstevel@tonic-gate * 40117c478bd9Sstevel@tonic-gate * For case (1) we don't want to send redirects. For case(2) we 40127c478bd9Sstevel@tonic-gate * want to send redirects. 40137c478bd9Sstevel@tonic-gate */ 40147c478bd9Sstevel@tonic-gate static boolean_t 4015f4b3ec61Sdh155122 ip_source_routed_v6(ip6_t *ip6h, mblk_t *mp, ip_stack_t *ipst) 40167c478bd9Sstevel@tonic-gate { 40177c478bd9Sstevel@tonic-gate uint8_t nexthdr; 40187c478bd9Sstevel@tonic-gate in6_addr_t *addrptr; 40197c478bd9Sstevel@tonic-gate ip6_rthdr0_t *rthdr; 40207c478bd9Sstevel@tonic-gate uint8_t numaddr; 40217c478bd9Sstevel@tonic-gate ip6_hbh_t *hbhhdr; 40227c478bd9Sstevel@tonic-gate uint_t ehdrlen; 40237c478bd9Sstevel@tonic-gate uint8_t *byteptr; 40247c478bd9Sstevel@tonic-gate 40257c478bd9Sstevel@tonic-gate ip2dbg(("ip_source_routed_v6\n")); 40267c478bd9Sstevel@tonic-gate nexthdr = ip6h->ip6_nxt; 40277c478bd9Sstevel@tonic-gate ehdrlen = IPV6_HDR_LEN; 40287c478bd9Sstevel@tonic-gate 40297c478bd9Sstevel@tonic-gate /* if a routing hdr is preceeded by HOPOPT or DSTOPT */ 40307c478bd9Sstevel@tonic-gate while (nexthdr == IPPROTO_HOPOPTS || 40317c478bd9Sstevel@tonic-gate nexthdr == IPPROTO_DSTOPTS) { 40327c478bd9Sstevel@tonic-gate byteptr = (uint8_t *)ip6h + ehdrlen; 40337c478bd9Sstevel@tonic-gate /* 40347c478bd9Sstevel@tonic-gate * Check if we have already processed 40357c478bd9Sstevel@tonic-gate * packets or we are just a forwarding 40367c478bd9Sstevel@tonic-gate * router which only pulled up msgs up 40377c478bd9Sstevel@tonic-gate * to IPV6HDR and one HBH ext header 40387c478bd9Sstevel@tonic-gate */ 40397c478bd9Sstevel@tonic-gate if (byteptr + MIN_EHDR_LEN > mp->b_wptr) { 40407c478bd9Sstevel@tonic-gate ip2dbg(("ip_source_routed_v6: Extension" 40417c478bd9Sstevel@tonic-gate " headers not processed\n")); 40427c478bd9Sstevel@tonic-gate return (B_FALSE); 40437c478bd9Sstevel@tonic-gate } 40447c478bd9Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)byteptr; 40457c478bd9Sstevel@tonic-gate nexthdr = hbhhdr->ip6h_nxt; 40467c478bd9Sstevel@tonic-gate ehdrlen = ehdrlen + 8 * (hbhhdr->ip6h_len + 1); 40477c478bd9Sstevel@tonic-gate } 40487c478bd9Sstevel@tonic-gate switch (nexthdr) { 40497c478bd9Sstevel@tonic-gate case IPPROTO_ROUTING: 40507c478bd9Sstevel@tonic-gate byteptr = (uint8_t *)ip6h + ehdrlen; 40517c478bd9Sstevel@tonic-gate /* 40527c478bd9Sstevel@tonic-gate * If for some reason, we haven't pulled up 40537c478bd9Sstevel@tonic-gate * the routing hdr data mblk, then we must 40547c478bd9Sstevel@tonic-gate * not have processed it at all. So for sure 40557c478bd9Sstevel@tonic-gate * we are not part of the source routed journey. 40567c478bd9Sstevel@tonic-gate */ 40577c478bd9Sstevel@tonic-gate if (byteptr + MIN_EHDR_LEN > mp->b_wptr) { 40587c478bd9Sstevel@tonic-gate ip2dbg(("ip_source_routed_v6: Routing" 40597c478bd9Sstevel@tonic-gate " header not processed\n")); 40607c478bd9Sstevel@tonic-gate return (B_FALSE); 40617c478bd9Sstevel@tonic-gate } 40627c478bd9Sstevel@tonic-gate rthdr = (ip6_rthdr0_t *)byteptr; 40637c478bd9Sstevel@tonic-gate /* 40647c478bd9Sstevel@tonic-gate * Either we are an intermediate router or the 40657c478bd9Sstevel@tonic-gate * last hop before destination and we have 40667c478bd9Sstevel@tonic-gate * already processed the routing header. 40677c478bd9Sstevel@tonic-gate * If segment_left is greater than or equal to zero, 40687c478bd9Sstevel@tonic-gate * then we must be the (numaddr - segleft) entry 40697c478bd9Sstevel@tonic-gate * of the routing header. Although ip6r0_segleft 40707c478bd9Sstevel@tonic-gate * is a unit8_t variable, we still check for zero 40717c478bd9Sstevel@tonic-gate * or greater value, if in case the data type 40727c478bd9Sstevel@tonic-gate * is changed someday in future. 40737c478bd9Sstevel@tonic-gate */ 40747c478bd9Sstevel@tonic-gate if (rthdr->ip6r0_segleft > 0 || 40757c478bd9Sstevel@tonic-gate rthdr->ip6r0_segleft == 0) { 40767c478bd9Sstevel@tonic-gate numaddr = rthdr->ip6r0_len / 2; 40777c478bd9Sstevel@tonic-gate addrptr = (in6_addr_t *)((char *)rthdr + 40787c478bd9Sstevel@tonic-gate sizeof (*rthdr)); 40797c478bd9Sstevel@tonic-gate addrptr += (numaddr - (rthdr->ip6r0_segleft + 1)); 40807c478bd9Sstevel@tonic-gate if (addrptr != NULL) { 4081bd670b35SErik Nordmark if (ip_type_v6(addrptr, ipst) == IRE_LOCAL) 40827c478bd9Sstevel@tonic-gate return (B_TRUE); 4083bd670b35SErik Nordmark ip1dbg(("ip_source_routed_v6: Not local\n")); 40847c478bd9Sstevel@tonic-gate } 40857c478bd9Sstevel@tonic-gate } 40867c478bd9Sstevel@tonic-gate /* FALLTHRU */ 40877c478bd9Sstevel@tonic-gate default: 40887c478bd9Sstevel@tonic-gate ip2dbg(("ip_source_routed_v6: Not source routed here\n")); 40897c478bd9Sstevel@tonic-gate return (B_FALSE); 40907c478bd9Sstevel@tonic-gate } 40917c478bd9Sstevel@tonic-gate } 40927c478bd9Sstevel@tonic-gate 40937c478bd9Sstevel@tonic-gate /* 40947c478bd9Sstevel@tonic-gate * IPv6 fragmentation. Essentially the same as IPv4 fragmentation. 40957c478bd9Sstevel@tonic-gate * We have not optimized this in terms of number of mblks 40967c478bd9Sstevel@tonic-gate * allocated. For instance, for each fragment sent we always allocate a 40977c478bd9Sstevel@tonic-gate * mblk to hold the IPv6 header and fragment header. 40987c478bd9Sstevel@tonic-gate * 4099bd670b35SErik Nordmark * Assumes that all the extension headers are contained in the first mblk 4100bd670b35SErik Nordmark * and that the fragment header has has already been added by calling 4101bd670b35SErik Nordmark * ip_fraghdr_add_v6. 41027c478bd9Sstevel@tonic-gate */ 4103bd670b35SErik Nordmark int 4104bd670b35SErik Nordmark ip_fragment_v6(mblk_t *mp, nce_t *nce, iaflags_t ixaflags, uint_t pkt_len, 4105bd670b35SErik Nordmark uint32_t max_frag, uint32_t xmit_hint, zoneid_t szone, zoneid_t nolzid, 4106bd670b35SErik Nordmark pfirepostfrag_t postfragfn, uintptr_t *ixa_cookie) 41077c478bd9Sstevel@tonic-gate { 41087c478bd9Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)mp->b_rptr; 41097c478bd9Sstevel@tonic-gate ip6_t *fip6h; 41107c478bd9Sstevel@tonic-gate mblk_t *hmp; 41117c478bd9Sstevel@tonic-gate mblk_t *hmp0; 41127c478bd9Sstevel@tonic-gate mblk_t *dmp; 41137c478bd9Sstevel@tonic-gate ip6_frag_t *fraghdr; 41147c478bd9Sstevel@tonic-gate size_t unfragmentable_len; 41157c478bd9Sstevel@tonic-gate size_t mlen; 41167c478bd9Sstevel@tonic-gate size_t max_chunk; 41177c478bd9Sstevel@tonic-gate uint16_t off_flags; 41187c478bd9Sstevel@tonic-gate uint16_t offset = 0; 4119bd670b35SErik Nordmark ill_t *ill = nce->nce_ill; 4120bd670b35SErik Nordmark uint8_t nexthdr; 4121bd670b35SErik Nordmark uint8_t *ptr; 4122bd670b35SErik Nordmark ip_stack_t *ipst = ill->ill_ipst; 4123bd670b35SErik Nordmark uint_t priority = mp->b_band; 4124bd670b35SErik Nordmark int error = 0; 4125bd670b35SErik Nordmark 4126bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragReqds); 4127bd670b35SErik Nordmark if (max_frag == 0) { 4128bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails); 4129bd670b35SErik Nordmark ip_drop_output("FragFails: zero max_frag", mp, ill); 4130bd670b35SErik Nordmark freemsg(mp); 4131bd670b35SErik Nordmark return (EINVAL); 4132bd670b35SErik Nordmark } 4133bd670b35SErik Nordmark 4134bd670b35SErik Nordmark /* 4135bd670b35SErik Nordmark * Caller should have added fraghdr_t to pkt_len, and also 4136bd670b35SErik Nordmark * updated ip6_plen. 4137bd670b35SErik Nordmark */ 4138bd670b35SErik Nordmark ASSERT(ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN == pkt_len); 4139bd670b35SErik Nordmark ASSERT(msgdsize(mp) == pkt_len); 4140bd670b35SErik Nordmark 4141bd670b35SErik Nordmark /* 4142bd670b35SErik Nordmark * Determine the length of the unfragmentable portion of this 4143bd670b35SErik Nordmark * datagram. This consists of the IPv6 header, a potential 4144bd670b35SErik Nordmark * hop-by-hop options header, a potential pre-routing-header 4145bd670b35SErik Nordmark * destination options header, and a potential routing header. 4146bd670b35SErik Nordmark */ 4147bd670b35SErik Nordmark nexthdr = ip6h->ip6_nxt; 4148bd670b35SErik Nordmark ptr = (uint8_t *)&ip6h[1]; 4149bd670b35SErik Nordmark 4150bd670b35SErik Nordmark if (nexthdr == IPPROTO_HOPOPTS) { 4151bd670b35SErik Nordmark ip6_hbh_t *hbh_hdr; 4152bd670b35SErik Nordmark uint_t hdr_len; 4153bd670b35SErik Nordmark 4154bd670b35SErik Nordmark hbh_hdr = (ip6_hbh_t *)ptr; 4155bd670b35SErik Nordmark hdr_len = 8 * (hbh_hdr->ip6h_len + 1); 4156bd670b35SErik Nordmark nexthdr = hbh_hdr->ip6h_nxt; 4157bd670b35SErik Nordmark ptr += hdr_len; 4158bd670b35SErik Nordmark } 4159bd670b35SErik Nordmark if (nexthdr == IPPROTO_DSTOPTS) { 4160bd670b35SErik Nordmark ip6_dest_t *dest_hdr; 4161bd670b35SErik Nordmark uint_t hdr_len; 4162bd670b35SErik Nordmark 4163bd670b35SErik Nordmark dest_hdr = (ip6_dest_t *)ptr; 4164bd670b35SErik Nordmark if (dest_hdr->ip6d_nxt == IPPROTO_ROUTING) { 4165bd670b35SErik Nordmark hdr_len = 8 * (dest_hdr->ip6d_len + 1); 4166bd670b35SErik Nordmark nexthdr = dest_hdr->ip6d_nxt; 4167bd670b35SErik Nordmark ptr += hdr_len; 4168bd670b35SErik Nordmark } 4169bd670b35SErik Nordmark } 4170bd670b35SErik Nordmark if (nexthdr == IPPROTO_ROUTING) { 4171bd670b35SErik Nordmark ip6_rthdr_t *rthdr; 4172bd670b35SErik Nordmark uint_t hdr_len; 4173bd670b35SErik Nordmark 4174bd670b35SErik Nordmark rthdr = (ip6_rthdr_t *)ptr; 4175bd670b35SErik Nordmark nexthdr = rthdr->ip6r_nxt; 4176bd670b35SErik Nordmark hdr_len = 8 * (rthdr->ip6r_len + 1); 4177bd670b35SErik Nordmark ptr += hdr_len; 4178bd670b35SErik Nordmark } 4179bd670b35SErik Nordmark if (nexthdr != IPPROTO_FRAGMENT) { 4180bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails); 4181bd670b35SErik Nordmark ip_drop_output("FragFails: bad nexthdr", mp, ill); 4182bd670b35SErik Nordmark freemsg(mp); 4183bd670b35SErik Nordmark return (EINVAL); 4184bd670b35SErik Nordmark } 4185bd670b35SErik Nordmark unfragmentable_len = (uint_t)(ptr - (uint8_t *)ip6h); 4186bd670b35SErik Nordmark unfragmentable_len += sizeof (ip6_frag_t); 4187bd670b35SErik Nordmark 4188bd670b35SErik Nordmark max_chunk = (max_frag - unfragmentable_len) & ~7; 4189bd670b35SErik Nordmark 4190bd670b35SErik Nordmark /* 4191bd670b35SErik Nordmark * Allocate an mblk with enough room for the link-layer 4192bd670b35SErik Nordmark * header and the unfragmentable part of the datagram, which includes 4193bd670b35SErik Nordmark * the fragment header. This (or a copy) will be used as the 4194bd670b35SErik Nordmark * first mblk for each fragment we send. 4195bd670b35SErik Nordmark */ 4196bd670b35SErik Nordmark hmp = allocb_tmpl(unfragmentable_len + ipst->ips_ip_wroff_extra, mp); 4197bd670b35SErik Nordmark if (hmp == NULL) { 4198bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails); 4199bd670b35SErik Nordmark ip_drop_output("FragFails: no hmp", mp, ill); 4200bd670b35SErik Nordmark freemsg(mp); 4201bd670b35SErik Nordmark return (ENOBUFS); 4202bd670b35SErik Nordmark } 4203bd670b35SErik Nordmark hmp->b_rptr += ipst->ips_ip_wroff_extra; 4204bd670b35SErik Nordmark hmp->b_wptr = hmp->b_rptr + unfragmentable_len; 4205bd670b35SErik Nordmark 4206bd670b35SErik Nordmark fip6h = (ip6_t *)hmp->b_rptr; 4207bd670b35SErik Nordmark bcopy(ip6h, fip6h, unfragmentable_len); 4208bd670b35SErik Nordmark 4209bd670b35SErik Nordmark /* 4210bd670b35SErik Nordmark * pkt_len is set to the total length of the fragmentable data in this 4211bd670b35SErik Nordmark * datagram. For each fragment sent, we will decrement pkt_len 4212bd670b35SErik Nordmark * by the amount of fragmentable data sent in that fragment 4213bd670b35SErik Nordmark * until len reaches zero. 4214bd670b35SErik Nordmark */ 4215bd670b35SErik Nordmark pkt_len -= unfragmentable_len; 4216bd670b35SErik Nordmark 4217bd670b35SErik Nordmark /* 4218bd670b35SErik Nordmark * Move read ptr past unfragmentable portion, we don't want this part 4219bd670b35SErik Nordmark * of the data in our fragments. 4220bd670b35SErik Nordmark */ 4221bd670b35SErik Nordmark mp->b_rptr += unfragmentable_len; 4222bd670b35SErik Nordmark if (mp->b_rptr == mp->b_wptr) { 4223bd670b35SErik Nordmark mblk_t *mp1 = mp->b_cont; 4224bd670b35SErik Nordmark freeb(mp); 4225bd670b35SErik Nordmark mp = mp1; 4226bd670b35SErik Nordmark } 4227bd670b35SErik Nordmark 4228bd670b35SErik Nordmark while (pkt_len != 0) { 4229bd670b35SErik Nordmark mlen = MIN(pkt_len, max_chunk); 4230bd670b35SErik Nordmark pkt_len -= mlen; 4231bd670b35SErik Nordmark if (pkt_len != 0) { 4232bd670b35SErik Nordmark /* Not last */ 4233bd670b35SErik Nordmark hmp0 = copyb(hmp); 4234bd670b35SErik Nordmark if (hmp0 == NULL) { 4235bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, 4236bd670b35SErik Nordmark ipIfStatsOutFragFails); 4237bd670b35SErik Nordmark ip_drop_output("FragFails: copyb failed", 4238bd670b35SErik Nordmark mp, ill); 4239bd670b35SErik Nordmark freeb(hmp); 4240bd670b35SErik Nordmark freemsg(mp); 4241bd670b35SErik Nordmark ip1dbg(("ip_fragment_v6: copyb failed\n")); 4242bd670b35SErik Nordmark return (ENOBUFS); 4243bd670b35SErik Nordmark } 4244bd670b35SErik Nordmark off_flags = IP6F_MORE_FRAG; 4245bd670b35SErik Nordmark } else { 4246bd670b35SErik Nordmark /* Last fragment */ 4247bd670b35SErik Nordmark hmp0 = hmp; 4248bd670b35SErik Nordmark hmp = NULL; 4249bd670b35SErik Nordmark off_flags = 0; 4250bd670b35SErik Nordmark } 4251bd670b35SErik Nordmark fip6h = (ip6_t *)(hmp0->b_rptr); 4252bd670b35SErik Nordmark fraghdr = (ip6_frag_t *)(hmp0->b_rptr + unfragmentable_len - 4253bd670b35SErik Nordmark sizeof (ip6_frag_t)); 4254bd670b35SErik Nordmark 4255bd670b35SErik Nordmark fip6h->ip6_plen = htons((uint16_t)(mlen + 4256bd670b35SErik Nordmark unfragmentable_len - IPV6_HDR_LEN)); 4257bd670b35SErik Nordmark /* 4258bd670b35SErik Nordmark * Note: Optimization alert. 4259bd670b35SErik Nordmark * In IPv6 (and IPv4) protocol header, Fragment Offset 4260bd670b35SErik Nordmark * ("offset") is 13 bits wide and in 8-octet units. 4261bd670b35SErik Nordmark * In IPv6 protocol header (unlike IPv4) in a 16 bit field, 4262bd670b35SErik Nordmark * it occupies the most significant 13 bits. 4263bd670b35SErik Nordmark * (least significant 13 bits in IPv4). 4264bd670b35SErik Nordmark * We do not do any shifts here. Not shifting is same effect 4265bd670b35SErik Nordmark * as taking offset value in octet units, dividing by 8 and 4266bd670b35SErik Nordmark * then shifting 3 bits left to line it up in place in proper 4267bd670b35SErik Nordmark * place protocol header. 4268bd670b35SErik Nordmark */ 4269bd670b35SErik Nordmark fraghdr->ip6f_offlg = htons(offset) | off_flags; 4270bd670b35SErik Nordmark 4271bd670b35SErik Nordmark if (!(dmp = ip_carve_mp(&mp, mlen))) { 4272bd670b35SErik Nordmark /* mp has already been freed by ip_carve_mp() */ 4273bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails); 4274bd670b35SErik Nordmark ip_drop_output("FragFails: could not carve mp", 4275bd670b35SErik Nordmark hmp0, ill); 4276bd670b35SErik Nordmark if (hmp != NULL) 4277bd670b35SErik Nordmark freeb(hmp); 4278bd670b35SErik Nordmark freeb(hmp0); 4279bd670b35SErik Nordmark ip1dbg(("ip_carve_mp: failed\n")); 4280bd670b35SErik Nordmark return (ENOBUFS); 4281bd670b35SErik Nordmark } 4282bd670b35SErik Nordmark hmp0->b_cont = dmp; 4283bd670b35SErik Nordmark /* Get the priority marking, if any */ 4284bd670b35SErik Nordmark hmp0->b_band = priority; 4285bd670b35SErik Nordmark 4286bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragCreates); 4287bd670b35SErik Nordmark 4288bd670b35SErik Nordmark error = postfragfn(hmp0, nce, ixaflags, 4289bd670b35SErik Nordmark mlen + unfragmentable_len, xmit_hint, szone, nolzid, 4290bd670b35SErik Nordmark ixa_cookie); 4291bd670b35SErik Nordmark if (error != 0 && error != EWOULDBLOCK && hmp != NULL) { 4292bd670b35SErik Nordmark /* No point in sending the other fragments */ 4293bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails); 4294bd670b35SErik Nordmark ip_drop_output("FragFails: postfragfn failed", 4295bd670b35SErik Nordmark hmp, ill); 4296bd670b35SErik Nordmark freeb(hmp); 4297bd670b35SErik Nordmark freemsg(mp); 4298bd670b35SErik Nordmark return (error); 4299bd670b35SErik Nordmark } 4300bd670b35SErik Nordmark /* No need to redo state machine in loop */ 4301bd670b35SErik Nordmark ixaflags &= ~IXAF_REACH_CONF; 4302bd670b35SErik Nordmark 4303bd670b35SErik Nordmark offset += mlen; 4304bd670b35SErik Nordmark } 4305bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragOKs); 4306bd670b35SErik Nordmark return (error); 4307bd670b35SErik Nordmark } 4308bd670b35SErik Nordmark 4309bd670b35SErik Nordmark /* 4310bd670b35SErik Nordmark * Add a fragment header to an IPv6 packet. 4311bd670b35SErik Nordmark * Assumes that all the extension headers are contained in the first mblk. 4312bd670b35SErik Nordmark * 4313bd670b35SErik Nordmark * The fragment header is inserted after an hop-by-hop options header 4314bd670b35SErik Nordmark * and after [an optional destinations header followed by] a routing header. 4315bd670b35SErik Nordmark */ 4316bd670b35SErik Nordmark mblk_t * 4317bd670b35SErik Nordmark ip_fraghdr_add_v6(mblk_t *mp, uint32_t ident, ip_xmit_attr_t *ixa) 4318bd670b35SErik Nordmark { 4319bd670b35SErik Nordmark ip6_t *ip6h = (ip6_t *)mp->b_rptr; 4320bd670b35SErik Nordmark ip6_t *fip6h; 4321bd670b35SErik Nordmark mblk_t *hmp; 4322bd670b35SErik Nordmark ip6_frag_t *fraghdr; 4323bd670b35SErik Nordmark size_t unfragmentable_len; 43247c478bd9Sstevel@tonic-gate uint8_t nexthdr; 43257c478bd9Sstevel@tonic-gate uint_t prev_nexthdr_offset; 43267c478bd9Sstevel@tonic-gate uint8_t *ptr; 4327bd670b35SErik Nordmark uint_t priority = mp->b_band; 4328bd670b35SErik Nordmark ip_stack_t *ipst = ixa->ixa_ipst; 43293173664eSapersson 43307c478bd9Sstevel@tonic-gate /* 43317c478bd9Sstevel@tonic-gate * Determine the length of the unfragmentable portion of this 43327c478bd9Sstevel@tonic-gate * datagram. This consists of the IPv6 header, a potential 43337c478bd9Sstevel@tonic-gate * hop-by-hop options header, a potential pre-routing-header 43347c478bd9Sstevel@tonic-gate * destination options header, and a potential routing header. 43357c478bd9Sstevel@tonic-gate */ 43367c478bd9Sstevel@tonic-gate nexthdr = ip6h->ip6_nxt; 43377c478bd9Sstevel@tonic-gate prev_nexthdr_offset = (uint8_t *)&ip6h->ip6_nxt - (uint8_t *)ip6h; 43387c478bd9Sstevel@tonic-gate ptr = (uint8_t *)&ip6h[1]; 43397c478bd9Sstevel@tonic-gate 43407c478bd9Sstevel@tonic-gate if (nexthdr == IPPROTO_HOPOPTS) { 43417c478bd9Sstevel@tonic-gate ip6_hbh_t *hbh_hdr; 43427c478bd9Sstevel@tonic-gate uint_t hdr_len; 43437c478bd9Sstevel@tonic-gate 43447c478bd9Sstevel@tonic-gate hbh_hdr = (ip6_hbh_t *)ptr; 43457c478bd9Sstevel@tonic-gate hdr_len = 8 * (hbh_hdr->ip6h_len + 1); 43467c478bd9Sstevel@tonic-gate nexthdr = hbh_hdr->ip6h_nxt; 43477c478bd9Sstevel@tonic-gate prev_nexthdr_offset = (uint8_t *)&hbh_hdr->ip6h_nxt 43487c478bd9Sstevel@tonic-gate - (uint8_t *)ip6h; 43497c478bd9Sstevel@tonic-gate ptr += hdr_len; 43507c478bd9Sstevel@tonic-gate } 43517c478bd9Sstevel@tonic-gate if (nexthdr == IPPROTO_DSTOPTS) { 43527c478bd9Sstevel@tonic-gate ip6_dest_t *dest_hdr; 43537c478bd9Sstevel@tonic-gate uint_t hdr_len; 43547c478bd9Sstevel@tonic-gate 43557c478bd9Sstevel@tonic-gate dest_hdr = (ip6_dest_t *)ptr; 43567c478bd9Sstevel@tonic-gate if (dest_hdr->ip6d_nxt == IPPROTO_ROUTING) { 43577c478bd9Sstevel@tonic-gate hdr_len = 8 * (dest_hdr->ip6d_len + 1); 43587c478bd9Sstevel@tonic-gate nexthdr = dest_hdr->ip6d_nxt; 43597c478bd9Sstevel@tonic-gate prev_nexthdr_offset = (uint8_t *)&dest_hdr->ip6d_nxt 43607c478bd9Sstevel@tonic-gate - (uint8_t *)ip6h; 43617c478bd9Sstevel@tonic-gate ptr += hdr_len; 43627c478bd9Sstevel@tonic-gate } 43637c478bd9Sstevel@tonic-gate } 43647c478bd9Sstevel@tonic-gate if (nexthdr == IPPROTO_ROUTING) { 43657c478bd9Sstevel@tonic-gate ip6_rthdr_t *rthdr; 43667c478bd9Sstevel@tonic-gate uint_t hdr_len; 43677c478bd9Sstevel@tonic-gate 43687c478bd9Sstevel@tonic-gate rthdr = (ip6_rthdr_t *)ptr; 43697c478bd9Sstevel@tonic-gate nexthdr = rthdr->ip6r_nxt; 43707c478bd9Sstevel@tonic-gate prev_nexthdr_offset = (uint8_t *)&rthdr->ip6r_nxt 43717c478bd9Sstevel@tonic-gate - (uint8_t *)ip6h; 43727c478bd9Sstevel@tonic-gate hdr_len = 8 * (rthdr->ip6r_len + 1); 43737c478bd9Sstevel@tonic-gate ptr += hdr_len; 43747c478bd9Sstevel@tonic-gate } 43757c478bd9Sstevel@tonic-gate unfragmentable_len = (uint_t)(ptr - (uint8_t *)ip6h); 43767c478bd9Sstevel@tonic-gate 43777c478bd9Sstevel@tonic-gate /* 43787c478bd9Sstevel@tonic-gate * Allocate an mblk with enough room for the link-layer 43797c478bd9Sstevel@tonic-gate * header, the unfragmentable part of the datagram, and the 4380bd670b35SErik Nordmark * fragment header. 43817c478bd9Sstevel@tonic-gate */ 4382de8c4a14SErik Nordmark hmp = allocb_tmpl(unfragmentable_len + sizeof (ip6_frag_t) + 4383de8c4a14SErik Nordmark ipst->ips_ip_wroff_extra, mp); 43847c478bd9Sstevel@tonic-gate if (hmp == NULL) { 4385bd670b35SErik Nordmark ill_t *ill = ixa->ixa_nce->nce_ill; 4386bd670b35SErik Nordmark 4387bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards); 4388bd670b35SErik Nordmark ip_drop_output("ipIfStatsOutDiscards: allocb failure", mp, ill); 43897c478bd9Sstevel@tonic-gate freemsg(mp); 4390bd670b35SErik Nordmark return (NULL); 43917c478bd9Sstevel@tonic-gate } 4392f4b3ec61Sdh155122 hmp->b_rptr += ipst->ips_ip_wroff_extra; 43937c478bd9Sstevel@tonic-gate hmp->b_wptr = hmp->b_rptr + unfragmentable_len + sizeof (ip6_frag_t); 43947c478bd9Sstevel@tonic-gate 43957c478bd9Sstevel@tonic-gate fip6h = (ip6_t *)hmp->b_rptr; 43967c478bd9Sstevel@tonic-gate fraghdr = (ip6_frag_t *)(hmp->b_rptr + unfragmentable_len); 43977c478bd9Sstevel@tonic-gate 43987c478bd9Sstevel@tonic-gate bcopy(ip6h, fip6h, unfragmentable_len); 4399bd670b35SErik Nordmark fip6h->ip6_plen = htons(ntohs(fip6h->ip6_plen) + sizeof (ip6_frag_t)); 44007c478bd9Sstevel@tonic-gate hmp->b_rptr[prev_nexthdr_offset] = IPPROTO_FRAGMENT; 44017c478bd9Sstevel@tonic-gate 44027c478bd9Sstevel@tonic-gate fraghdr->ip6f_nxt = nexthdr; 44037c478bd9Sstevel@tonic-gate fraghdr->ip6f_reserved = 0; 4404ff550d0eSmasputra fraghdr->ip6f_offlg = 0; 44057c478bd9Sstevel@tonic-gate fraghdr->ip6f_ident = htonl(ident); 44067c478bd9Sstevel@tonic-gate 4407bd670b35SErik Nordmark /* Get the priority marking, if any */ 4408bd670b35SErik Nordmark hmp->b_band = priority; 44097c478bd9Sstevel@tonic-gate 44107c478bd9Sstevel@tonic-gate /* 44117c478bd9Sstevel@tonic-gate * Move read ptr past unfragmentable portion, we don't want this part 44127c478bd9Sstevel@tonic-gate * of the data in our fragments. 44137c478bd9Sstevel@tonic-gate */ 44147c478bd9Sstevel@tonic-gate mp->b_rptr += unfragmentable_len; 4415bd670b35SErik Nordmark hmp->b_cont = mp; 4416bd670b35SErik Nordmark return (hmp); 44177c478bd9Sstevel@tonic-gate } 44187c478bd9Sstevel@tonic-gate 44197c478bd9Sstevel@tonic-gate /* 44207c478bd9Sstevel@tonic-gate * Determine if the ill and multicast aspects of that packets 44217c478bd9Sstevel@tonic-gate * "matches" the conn. 44227c478bd9Sstevel@tonic-gate */ 44237c478bd9Sstevel@tonic-gate boolean_t 4424bd670b35SErik Nordmark conn_wantpacket_v6(conn_t *connp, ip_recv_attr_t *ira, ip6_t *ip6h) 44257c478bd9Sstevel@tonic-gate { 4426bd670b35SErik Nordmark ill_t *ill = ira->ira_rill; 4427bd670b35SErik Nordmark zoneid_t zoneid = ira->ira_zoneid; 4428bd670b35SErik Nordmark uint_t in_ifindex; 44297c478bd9Sstevel@tonic-gate in6_addr_t *v6dst_ptr = &ip6h->ip6_dst; 44307c478bd9Sstevel@tonic-gate in6_addr_t *v6src_ptr = &ip6h->ip6_src; 44317c478bd9Sstevel@tonic-gate 44327c478bd9Sstevel@tonic-gate /* 4433bd670b35SErik Nordmark * conn_incoming_ifindex is set by IPV6_BOUND_IF and as link-local 4434bd670b35SErik Nordmark * scopeid. This is used to limit 4435bd670b35SErik Nordmark * unicast and multicast reception to conn_incoming_ifindex. 44367c478bd9Sstevel@tonic-gate * conn_wantpacket_v6 is called both for unicast and 4437bd670b35SErik Nordmark * multicast packets. 44387c478bd9Sstevel@tonic-gate */ 4439bd670b35SErik Nordmark in_ifindex = connp->conn_incoming_ifindex; 4440bd670b35SErik Nordmark 4441bd670b35SErik Nordmark /* mpathd can bind to the under IPMP interface, which we allow */ 4442bd670b35SErik Nordmark if (in_ifindex != 0 && in_ifindex != ill->ill_phyint->phyint_ifindex) { 4443bd670b35SErik Nordmark if (!IS_UNDER_IPMP(ill)) 44447c478bd9Sstevel@tonic-gate return (B_FALSE); 4445bd670b35SErik Nordmark 4446bd670b35SErik Nordmark if (in_ifindex != ipmp_ill_get_ipmp_ifindex(ill)) 4447e11c3f44Smeem return (B_FALSE); 4448e11c3f44Smeem } 4449bd670b35SErik Nordmark 4450bd670b35SErik Nordmark if (!IPCL_ZONE_MATCH(connp, zoneid)) 4451bd670b35SErik Nordmark return (B_FALSE); 4452bd670b35SErik Nordmark 4453bd670b35SErik Nordmark if (!(ira->ira_flags & IRAF_MULTICAST)) 4454bd670b35SErik Nordmark return (B_TRUE); 44557c478bd9Sstevel@tonic-gate 44567c478bd9Sstevel@tonic-gate if (connp->conn_multi_router) 44577c478bd9Sstevel@tonic-gate return (B_TRUE); 44587c478bd9Sstevel@tonic-gate 4459bd670b35SErik Nordmark if (ira->ira_protocol == IPPROTO_RSVP) 4460bd670b35SErik Nordmark return (B_TRUE); 44617c478bd9Sstevel@tonic-gate 4462bd670b35SErik Nordmark return (conn_hasmembers_ill_withsrc_v6(connp, v6dst_ptr, v6src_ptr, 4463bd670b35SErik Nordmark ira->ira_ill)); 44647c478bd9Sstevel@tonic-gate } 44657c478bd9Sstevel@tonic-gate 44667c478bd9Sstevel@tonic-gate /* 44677c478bd9Sstevel@tonic-gate * pr_addr_dbg function provides the needed buffer space to call 44687c478bd9Sstevel@tonic-gate * inet_ntop() function's 3rd argument. This function should be 44697c478bd9Sstevel@tonic-gate * used by any kernel routine which wants to save INET6_ADDRSTRLEN 44707c478bd9Sstevel@tonic-gate * stack buffer space in it's own stack frame. This function uses 44717c478bd9Sstevel@tonic-gate * a buffer from it's own stack and prints the information. 44727c478bd9Sstevel@tonic-gate * Example: pr_addr_dbg("func: no route for %s\n ", AF_INET, addr) 44737c478bd9Sstevel@tonic-gate * 44747c478bd9Sstevel@tonic-gate * Note: This function can call inet_ntop() once. 44757c478bd9Sstevel@tonic-gate */ 44767c478bd9Sstevel@tonic-gate void 44777c478bd9Sstevel@tonic-gate pr_addr_dbg(char *fmt1, int af, const void *addr) 44787c478bd9Sstevel@tonic-gate { 44797c478bd9Sstevel@tonic-gate char buf[INET6_ADDRSTRLEN]; 44807c478bd9Sstevel@tonic-gate 44817c478bd9Sstevel@tonic-gate if (fmt1 == NULL) { 44827c478bd9Sstevel@tonic-gate ip0dbg(("pr_addr_dbg: Wrong arguments\n")); 44837c478bd9Sstevel@tonic-gate return; 44847c478bd9Sstevel@tonic-gate } 44857c478bd9Sstevel@tonic-gate 44867c478bd9Sstevel@tonic-gate /* 44877c478bd9Sstevel@tonic-gate * This does not compare debug level and just prints 44887c478bd9Sstevel@tonic-gate * out. Thus it is the responsibility of the caller 44897c478bd9Sstevel@tonic-gate * to check the appropriate debug-level before calling 44907c478bd9Sstevel@tonic-gate * this function. 44917c478bd9Sstevel@tonic-gate */ 44927c478bd9Sstevel@tonic-gate if (ip_debug > 0) { 44937c478bd9Sstevel@tonic-gate printf(fmt1, inet_ntop(af, addr, buf, sizeof (buf))); 44947c478bd9Sstevel@tonic-gate } 44957c478bd9Sstevel@tonic-gate 44967c478bd9Sstevel@tonic-gate 44977c478bd9Sstevel@tonic-gate } 44987c478bd9Sstevel@tonic-gate 44997c478bd9Sstevel@tonic-gate 45007c478bd9Sstevel@tonic-gate /* 4501bd670b35SErik Nordmark * Return the length in bytes of the IPv6 headers (base header 4502bd670b35SErik Nordmark * extension headers) that will be needed based on the 4503bd670b35SErik Nordmark * ip_pkt_t structure passed by the caller. 45047c478bd9Sstevel@tonic-gate * 45057c478bd9Sstevel@tonic-gate * The returned length does not include the length of the upper level 45067c478bd9Sstevel@tonic-gate * protocol (ULP) header. 45077c478bd9Sstevel@tonic-gate */ 45087c478bd9Sstevel@tonic-gate int 4509bd670b35SErik Nordmark ip_total_hdrs_len_v6(const ip_pkt_t *ipp) 45107c478bd9Sstevel@tonic-gate { 45117c478bd9Sstevel@tonic-gate int len; 45127c478bd9Sstevel@tonic-gate 45137c478bd9Sstevel@tonic-gate len = IPV6_HDR_LEN; 4514bd670b35SErik Nordmark 4515bd670b35SErik Nordmark /* 4516bd670b35SErik Nordmark * If there's a security label here, then we ignore any hop-by-hop 4517bd670b35SErik Nordmark * options the user may try to set. 4518bd670b35SErik Nordmark */ 4519bd670b35SErik Nordmark if (ipp->ipp_fields & IPPF_LABEL_V6) { 4520bd670b35SErik Nordmark uint_t hopoptslen; 4521bd670b35SErik Nordmark /* 4522bd670b35SErik Nordmark * Note that ipp_label_len_v6 is just the option - not 4523bd670b35SErik Nordmark * the hopopts extension header. It also needs to be padded 4524bd670b35SErik Nordmark * to a multiple of 8 bytes. 4525bd670b35SErik Nordmark */ 4526bd670b35SErik Nordmark ASSERT(ipp->ipp_label_len_v6 != 0); 4527bd670b35SErik Nordmark hopoptslen = ipp->ipp_label_len_v6 + sizeof (ip6_hbh_t); 4528bd670b35SErik Nordmark hopoptslen = (hopoptslen + 7)/8 * 8; 4529bd670b35SErik Nordmark len += hopoptslen; 4530bd670b35SErik Nordmark } else if (ipp->ipp_fields & IPPF_HOPOPTS) { 45317c478bd9Sstevel@tonic-gate ASSERT(ipp->ipp_hopoptslen != 0); 45327c478bd9Sstevel@tonic-gate len += ipp->ipp_hopoptslen; 45337c478bd9Sstevel@tonic-gate } 4534bd670b35SErik Nordmark 45357c478bd9Sstevel@tonic-gate /* 45367c478bd9Sstevel@tonic-gate * En-route destination options 45377c478bd9Sstevel@tonic-gate * Only do them if there's a routing header as well 45387c478bd9Sstevel@tonic-gate */ 4539bd670b35SErik Nordmark if ((ipp->ipp_fields & (IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) == 4540bd670b35SErik Nordmark (IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) { 4541bd670b35SErik Nordmark ASSERT(ipp->ipp_rthdrdstoptslen != 0); 4542bd670b35SErik Nordmark len += ipp->ipp_rthdrdstoptslen; 4543bd670b35SErik Nordmark } 4544bd670b35SErik Nordmark if (ipp->ipp_fields & IPPF_RTHDR) { 4545bd670b35SErik Nordmark ASSERT(ipp->ipp_rthdrlen != 0); 4546bd670b35SErik Nordmark len += ipp->ipp_rthdrlen; 45477c478bd9Sstevel@tonic-gate } 45487c478bd9Sstevel@tonic-gate if (ipp->ipp_fields & IPPF_DSTOPTS) { 45497c478bd9Sstevel@tonic-gate ASSERT(ipp->ipp_dstoptslen != 0); 45507c478bd9Sstevel@tonic-gate len += ipp->ipp_dstoptslen; 45517c478bd9Sstevel@tonic-gate } 45527c478bd9Sstevel@tonic-gate return (len); 45537c478bd9Sstevel@tonic-gate } 45547c478bd9Sstevel@tonic-gate 45557c478bd9Sstevel@tonic-gate /* 45567c478bd9Sstevel@tonic-gate * All-purpose routine to build a header chain of an IPv6 header 4557bd670b35SErik Nordmark * followed by any required extension headers and a proto header. 45587c478bd9Sstevel@tonic-gate * 4559bd670b35SErik Nordmark * The caller has to set the source and destination address as well as 4560bd670b35SErik Nordmark * ip6_plen. The caller has to massage any routing header and compensate 4561bd670b35SErik Nordmark * for the ULP pseudo-header checksum due to the source route. 45627c478bd9Sstevel@tonic-gate * 4563bd670b35SErik Nordmark * The extension headers will all be fully filled in. 45647c478bd9Sstevel@tonic-gate */ 45657c478bd9Sstevel@tonic-gate void 4566bd670b35SErik Nordmark ip_build_hdrs_v6(uchar_t *buf, uint_t buf_len, const ip_pkt_t *ipp, 4567bd670b35SErik Nordmark uint8_t protocol, uint32_t flowinfo) 45687c478bd9Sstevel@tonic-gate { 45697c478bd9Sstevel@tonic-gate uint8_t *nxthdr_ptr; 45707c478bd9Sstevel@tonic-gate uint8_t *cp; 4571bd670b35SErik Nordmark ip6_t *ip6h = (ip6_t *)buf; 45727c478bd9Sstevel@tonic-gate 45737c478bd9Sstevel@tonic-gate /* Initialize IPv6 header */ 4574bd670b35SErik Nordmark ip6h->ip6_vcf = 4575bd670b35SErik Nordmark (IPV6_DEFAULT_VERS_AND_FLOW & IPV6_VERS_AND_FLOW_MASK) | 4576bd670b35SErik Nordmark (flowinfo & ~IPV6_VERS_AND_FLOW_MASK); 4577bd670b35SErik Nordmark 45787c478bd9Sstevel@tonic-gate if (ipp->ipp_fields & IPPF_TCLASS) { 4579bd670b35SErik Nordmark /* Overrides the class part of flowinfo */ 4580bd670b35SErik Nordmark ip6h->ip6_vcf = IPV6_TCLASS_FLOW(ip6h->ip6_vcf, 4581bd670b35SErik Nordmark ipp->ipp_tclass); 45827c478bd9Sstevel@tonic-gate } 4583bd670b35SErik Nordmark 4584bd670b35SErik Nordmark if (ipp->ipp_fields & IPPF_HOPLIMIT) 4585bd670b35SErik Nordmark ip6h->ip6_hops = ipp->ipp_hoplimit; 4586bd670b35SErik Nordmark else 4587bd670b35SErik Nordmark ip6h->ip6_hops = ipp->ipp_unicast_hops; 4588bd670b35SErik Nordmark 4589bd670b35SErik Nordmark if ((ipp->ipp_fields & IPPF_ADDR) && 4590bd670b35SErik Nordmark !IN6_IS_ADDR_V4MAPPED(&ipp->ipp_addr)) 45917c478bd9Sstevel@tonic-gate ip6h->ip6_src = ipp->ipp_addr; 45927c478bd9Sstevel@tonic-gate 45937c478bd9Sstevel@tonic-gate nxthdr_ptr = (uint8_t *)&ip6h->ip6_nxt; 45947c478bd9Sstevel@tonic-gate cp = (uint8_t *)&ip6h[1]; 45957c478bd9Sstevel@tonic-gate /* 45967c478bd9Sstevel@tonic-gate * Here's where we have to start stringing together 45977c478bd9Sstevel@tonic-gate * any extension headers in the right order: 45987c478bd9Sstevel@tonic-gate * Hop-by-hop, destination, routing, and final destination opts. 45997c478bd9Sstevel@tonic-gate */ 4600bd670b35SErik Nordmark /* 4601bd670b35SErik Nordmark * If there's a security label here, then we ignore any hop-by-hop 4602bd670b35SErik Nordmark * options the user may try to set. 4603bd670b35SErik Nordmark */ 4604bd670b35SErik Nordmark if (ipp->ipp_fields & IPPF_LABEL_V6) { 4605bd670b35SErik Nordmark /* 4606bd670b35SErik Nordmark * Hop-by-hop options with the label. 4607bd670b35SErik Nordmark * Note that ipp_label_v6 is just the option - not 4608bd670b35SErik Nordmark * the hopopts extension header. It also needs to be padded 4609bd670b35SErik Nordmark * to a multiple of 8 bytes. 4610bd670b35SErik Nordmark */ 4611bd670b35SErik Nordmark ip6_hbh_t *hbh = (ip6_hbh_t *)cp; 4612bd670b35SErik Nordmark uint_t hopoptslen; 4613bd670b35SErik Nordmark uint_t padlen; 4614bd670b35SErik Nordmark 4615bd670b35SErik Nordmark padlen = ipp->ipp_label_len_v6 + sizeof (ip6_hbh_t); 4616bd670b35SErik Nordmark hopoptslen = (padlen + 7)/8 * 8; 4617bd670b35SErik Nordmark padlen = hopoptslen - padlen; 4618bd670b35SErik Nordmark 4619bd670b35SErik Nordmark *nxthdr_ptr = IPPROTO_HOPOPTS; 4620bd670b35SErik Nordmark nxthdr_ptr = &hbh->ip6h_nxt; 4621bd670b35SErik Nordmark hbh->ip6h_len = hopoptslen/8 - 1; 4622bd670b35SErik Nordmark cp += sizeof (ip6_hbh_t); 4623bd670b35SErik Nordmark bcopy(ipp->ipp_label_v6, cp, ipp->ipp_label_len_v6); 4624bd670b35SErik Nordmark cp += ipp->ipp_label_len_v6; 4625bd670b35SErik Nordmark 4626bd670b35SErik Nordmark ASSERT(padlen <= 7); 4627bd670b35SErik Nordmark switch (padlen) { 4628bd670b35SErik Nordmark case 0: 4629bd670b35SErik Nordmark break; 4630bd670b35SErik Nordmark case 1: 4631bd670b35SErik Nordmark cp[0] = IP6OPT_PAD1; 4632bd670b35SErik Nordmark break; 4633bd670b35SErik Nordmark default: 4634bd670b35SErik Nordmark cp[0] = IP6OPT_PADN; 4635bd670b35SErik Nordmark cp[1] = padlen - 2; 4636bd670b35SErik Nordmark bzero(&cp[2], padlen - 2); 4637bd670b35SErik Nordmark break; 4638bd670b35SErik Nordmark } 4639bd670b35SErik Nordmark cp += padlen; 4640bd670b35SErik Nordmark } else if (ipp->ipp_fields & IPPF_HOPOPTS) { 46417c478bd9Sstevel@tonic-gate /* Hop-by-hop options */ 46427c478bd9Sstevel@tonic-gate ip6_hbh_t *hbh = (ip6_hbh_t *)cp; 46437c478bd9Sstevel@tonic-gate 46447c478bd9Sstevel@tonic-gate *nxthdr_ptr = IPPROTO_HOPOPTS; 46457c478bd9Sstevel@tonic-gate nxthdr_ptr = &hbh->ip6h_nxt; 46467c478bd9Sstevel@tonic-gate 46477c478bd9Sstevel@tonic-gate bcopy(ipp->ipp_hopopts, cp, ipp->ipp_hopoptslen); 46487c478bd9Sstevel@tonic-gate cp += ipp->ipp_hopoptslen; 46497c478bd9Sstevel@tonic-gate } 46507c478bd9Sstevel@tonic-gate /* 46517c478bd9Sstevel@tonic-gate * En-route destination options 46527c478bd9Sstevel@tonic-gate * Only do them if there's a routing header as well 46537c478bd9Sstevel@tonic-gate */ 4654bd670b35SErik Nordmark if ((ipp->ipp_fields & (IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) == 4655bd670b35SErik Nordmark (IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) { 46567c478bd9Sstevel@tonic-gate ip6_dest_t *dst = (ip6_dest_t *)cp; 46577c478bd9Sstevel@tonic-gate 46587c478bd9Sstevel@tonic-gate *nxthdr_ptr = IPPROTO_DSTOPTS; 46597c478bd9Sstevel@tonic-gate nxthdr_ptr = &dst->ip6d_nxt; 46607c478bd9Sstevel@tonic-gate 4661bd670b35SErik Nordmark bcopy(ipp->ipp_rthdrdstopts, cp, ipp->ipp_rthdrdstoptslen); 4662bd670b35SErik Nordmark cp += ipp->ipp_rthdrdstoptslen; 46637c478bd9Sstevel@tonic-gate } 46647c478bd9Sstevel@tonic-gate /* 46657c478bd9Sstevel@tonic-gate * Routing header next 46667c478bd9Sstevel@tonic-gate */ 46677c478bd9Sstevel@tonic-gate if (ipp->ipp_fields & IPPF_RTHDR) { 46687c478bd9Sstevel@tonic-gate ip6_rthdr_t *rt = (ip6_rthdr_t *)cp; 46697c478bd9Sstevel@tonic-gate 46707c478bd9Sstevel@tonic-gate *nxthdr_ptr = IPPROTO_ROUTING; 46717c478bd9Sstevel@tonic-gate nxthdr_ptr = &rt->ip6r_nxt; 46727c478bd9Sstevel@tonic-gate 46737c478bd9Sstevel@tonic-gate bcopy(ipp->ipp_rthdr, cp, ipp->ipp_rthdrlen); 46747c478bd9Sstevel@tonic-gate cp += ipp->ipp_rthdrlen; 46757c478bd9Sstevel@tonic-gate } 46767c478bd9Sstevel@tonic-gate /* 46777c478bd9Sstevel@tonic-gate * Do ultimate destination options 46787c478bd9Sstevel@tonic-gate */ 46797c478bd9Sstevel@tonic-gate if (ipp->ipp_fields & IPPF_DSTOPTS) { 46807c478bd9Sstevel@tonic-gate ip6_dest_t *dest = (ip6_dest_t *)cp; 46817c478bd9Sstevel@tonic-gate 46827c478bd9Sstevel@tonic-gate *nxthdr_ptr = IPPROTO_DSTOPTS; 46837c478bd9Sstevel@tonic-gate nxthdr_ptr = &dest->ip6d_nxt; 46847c478bd9Sstevel@tonic-gate 46857c478bd9Sstevel@tonic-gate bcopy(ipp->ipp_dstopts, cp, ipp->ipp_dstoptslen); 46867c478bd9Sstevel@tonic-gate cp += ipp->ipp_dstoptslen; 46877c478bd9Sstevel@tonic-gate } 46887c478bd9Sstevel@tonic-gate /* 46897c478bd9Sstevel@tonic-gate * Now set the last header pointer to the proto passed in 46907c478bd9Sstevel@tonic-gate */ 46917c478bd9Sstevel@tonic-gate *nxthdr_ptr = protocol; 4692bd670b35SErik Nordmark ASSERT((int)(cp - buf) == buf_len); 46937c478bd9Sstevel@tonic-gate } 46947c478bd9Sstevel@tonic-gate 46957c478bd9Sstevel@tonic-gate /* 46967c478bd9Sstevel@tonic-gate * Return a pointer to the routing header extension header 46977c478bd9Sstevel@tonic-gate * in the IPv6 header(s) chain passed in. 46987c478bd9Sstevel@tonic-gate * If none found, return NULL 46997c478bd9Sstevel@tonic-gate * Assumes that all extension headers are in same mblk as the v6 header 47007c478bd9Sstevel@tonic-gate */ 47017c478bd9Sstevel@tonic-gate ip6_rthdr_t * 47027c478bd9Sstevel@tonic-gate ip_find_rthdr_v6(ip6_t *ip6h, uint8_t *endptr) 47037c478bd9Sstevel@tonic-gate { 47047c478bd9Sstevel@tonic-gate ip6_dest_t *desthdr; 47057c478bd9Sstevel@tonic-gate ip6_frag_t *fraghdr; 47067c478bd9Sstevel@tonic-gate uint_t hdrlen; 47077c478bd9Sstevel@tonic-gate uint8_t nexthdr; 47087c478bd9Sstevel@tonic-gate uint8_t *ptr = (uint8_t *)&ip6h[1]; 47097c478bd9Sstevel@tonic-gate 47107c478bd9Sstevel@tonic-gate if (ip6h->ip6_nxt == IPPROTO_ROUTING) 47117c478bd9Sstevel@tonic-gate return ((ip6_rthdr_t *)ptr); 47127c478bd9Sstevel@tonic-gate 47137c478bd9Sstevel@tonic-gate /* 47147c478bd9Sstevel@tonic-gate * The routing header will precede all extension headers 47157c478bd9Sstevel@tonic-gate * other than the hop-by-hop and destination options 47167c478bd9Sstevel@tonic-gate * extension headers, so if we see anything other than those, 47177c478bd9Sstevel@tonic-gate * we're done and didn't find it. 47187c478bd9Sstevel@tonic-gate * We could see a destination options header alone but no 47197c478bd9Sstevel@tonic-gate * routing header, in which case we'll return NULL as soon as 47207c478bd9Sstevel@tonic-gate * we see anything after that. 47217c478bd9Sstevel@tonic-gate * Hop-by-hop and destination option headers are identical, 47227c478bd9Sstevel@tonic-gate * so we can use either one we want as a template. 47237c478bd9Sstevel@tonic-gate */ 47247c478bd9Sstevel@tonic-gate nexthdr = ip6h->ip6_nxt; 47257c478bd9Sstevel@tonic-gate while (ptr < endptr) { 47267c478bd9Sstevel@tonic-gate /* Is there enough left for len + nexthdr? */ 47277c478bd9Sstevel@tonic-gate if (ptr + MIN_EHDR_LEN > endptr) 47287c478bd9Sstevel@tonic-gate return (NULL); 47297c478bd9Sstevel@tonic-gate 47307c478bd9Sstevel@tonic-gate switch (nexthdr) { 47317c478bd9Sstevel@tonic-gate case IPPROTO_HOPOPTS: 47327c478bd9Sstevel@tonic-gate case IPPROTO_DSTOPTS: 47337c478bd9Sstevel@tonic-gate /* Assumes the headers are identical for hbh and dst */ 47347c478bd9Sstevel@tonic-gate desthdr = (ip6_dest_t *)ptr; 47357c478bd9Sstevel@tonic-gate hdrlen = 8 * (desthdr->ip6d_len + 1); 47367c478bd9Sstevel@tonic-gate nexthdr = desthdr->ip6d_nxt; 47377c478bd9Sstevel@tonic-gate break; 47387c478bd9Sstevel@tonic-gate 47397c478bd9Sstevel@tonic-gate case IPPROTO_ROUTING: 47407c478bd9Sstevel@tonic-gate return ((ip6_rthdr_t *)ptr); 47417c478bd9Sstevel@tonic-gate 47427c478bd9Sstevel@tonic-gate case IPPROTO_FRAGMENT: 47437c478bd9Sstevel@tonic-gate fraghdr = (ip6_frag_t *)ptr; 47447c478bd9Sstevel@tonic-gate hdrlen = sizeof (ip6_frag_t); 47457c478bd9Sstevel@tonic-gate nexthdr = fraghdr->ip6f_nxt; 47467c478bd9Sstevel@tonic-gate break; 47477c478bd9Sstevel@tonic-gate 47487c478bd9Sstevel@tonic-gate default: 47497c478bd9Sstevel@tonic-gate return (NULL); 47507c478bd9Sstevel@tonic-gate } 47517c478bd9Sstevel@tonic-gate ptr += hdrlen; 47527c478bd9Sstevel@tonic-gate } 47537c478bd9Sstevel@tonic-gate return (NULL); 47547c478bd9Sstevel@tonic-gate } 47557c478bd9Sstevel@tonic-gate 47567c478bd9Sstevel@tonic-gate /* 47577c478bd9Sstevel@tonic-gate * Called for source-routed packets originating on this node. 47587c478bd9Sstevel@tonic-gate * Manipulates the original routing header by moving every entry up 47597c478bd9Sstevel@tonic-gate * one slot, placing the first entry in the v6 header's v6_dst field, 47607c478bd9Sstevel@tonic-gate * and placing the ultimate destination in the routing header's last 47617c478bd9Sstevel@tonic-gate * slot. 47627c478bd9Sstevel@tonic-gate * 47637c478bd9Sstevel@tonic-gate * Returns the checksum diference between the ultimate destination 47647c478bd9Sstevel@tonic-gate * (last hop in the routing header when the packet is sent) and 47657c478bd9Sstevel@tonic-gate * the first hop (ip6_dst when the packet is sent) 47667c478bd9Sstevel@tonic-gate */ 4767f4b3ec61Sdh155122 /* ARGSUSED2 */ 47687c478bd9Sstevel@tonic-gate uint32_t 4769f4b3ec61Sdh155122 ip_massage_options_v6(ip6_t *ip6h, ip6_rthdr_t *rth, netstack_t *ns) 47707c478bd9Sstevel@tonic-gate { 47717c478bd9Sstevel@tonic-gate uint_t numaddr; 47727c478bd9Sstevel@tonic-gate uint_t i; 47737c478bd9Sstevel@tonic-gate in6_addr_t *addrptr; 47747c478bd9Sstevel@tonic-gate in6_addr_t tmp; 47757c478bd9Sstevel@tonic-gate ip6_rthdr0_t *rthdr = (ip6_rthdr0_t *)rth; 47767c478bd9Sstevel@tonic-gate uint32_t cksm; 47777c478bd9Sstevel@tonic-gate uint32_t addrsum = 0; 47787c478bd9Sstevel@tonic-gate uint16_t *ptr; 47797c478bd9Sstevel@tonic-gate 47807c478bd9Sstevel@tonic-gate /* 47817c478bd9Sstevel@tonic-gate * Perform any processing needed for source routing. 47827c478bd9Sstevel@tonic-gate * We know that all extension headers will be in the same mblk 47837c478bd9Sstevel@tonic-gate * as the IPv6 header. 47847c478bd9Sstevel@tonic-gate */ 47857c478bd9Sstevel@tonic-gate 47867c478bd9Sstevel@tonic-gate /* 47877c478bd9Sstevel@tonic-gate * If no segments left in header, or the header length field is zero, 47887c478bd9Sstevel@tonic-gate * don't move hop addresses around; 47897c478bd9Sstevel@tonic-gate * Checksum difference is zero. 47907c478bd9Sstevel@tonic-gate */ 47917c478bd9Sstevel@tonic-gate if ((rthdr->ip6r0_segleft == 0) || (rthdr->ip6r0_len == 0)) 47927c478bd9Sstevel@tonic-gate return (0); 47937c478bd9Sstevel@tonic-gate 47947c478bd9Sstevel@tonic-gate ptr = (uint16_t *)&ip6h->ip6_dst; 47957c478bd9Sstevel@tonic-gate cksm = 0; 47967c478bd9Sstevel@tonic-gate for (i = 0; i < (sizeof (in6_addr_t) / sizeof (uint16_t)); i++) { 47977c478bd9Sstevel@tonic-gate cksm += ptr[i]; 47987c478bd9Sstevel@tonic-gate } 47997c478bd9Sstevel@tonic-gate cksm = (cksm & 0xFFFF) + (cksm >> 16); 48007c478bd9Sstevel@tonic-gate 48017c478bd9Sstevel@tonic-gate /* 48027c478bd9Sstevel@tonic-gate * Here's where the fun begins - we have to 48037c478bd9Sstevel@tonic-gate * move all addresses up one spot, take the 48047c478bd9Sstevel@tonic-gate * first hop and make it our first ip6_dst, 48057c478bd9Sstevel@tonic-gate * and place the ultimate destination in the 48067c478bd9Sstevel@tonic-gate * newly-opened last slot. 48077c478bd9Sstevel@tonic-gate */ 48087c478bd9Sstevel@tonic-gate addrptr = (in6_addr_t *)((char *)rthdr + sizeof (*rthdr)); 48097c478bd9Sstevel@tonic-gate numaddr = rthdr->ip6r0_len / 2; 48107c478bd9Sstevel@tonic-gate tmp = *addrptr; 48117c478bd9Sstevel@tonic-gate for (i = 0; i < (numaddr - 1); addrptr++, i++) { 48127c478bd9Sstevel@tonic-gate *addrptr = addrptr[1]; 48137c478bd9Sstevel@tonic-gate } 48147c478bd9Sstevel@tonic-gate *addrptr = ip6h->ip6_dst; 48157c478bd9Sstevel@tonic-gate ip6h->ip6_dst = tmp; 48167c478bd9Sstevel@tonic-gate 48177c478bd9Sstevel@tonic-gate /* 48187c478bd9Sstevel@tonic-gate * From the checksummed ultimate destination subtract the checksummed 48197c478bd9Sstevel@tonic-gate * current ip6_dst (the first hop address). Return that number. 48207c478bd9Sstevel@tonic-gate * (In the v4 case, the second part of this is done in each routine 48217c478bd9Sstevel@tonic-gate * that calls ip_massage_options(). We do it all in this one place 48227c478bd9Sstevel@tonic-gate * for v6). 48237c478bd9Sstevel@tonic-gate */ 48247c478bd9Sstevel@tonic-gate ptr = (uint16_t *)&ip6h->ip6_dst; 48257c478bd9Sstevel@tonic-gate for (i = 0; i < (sizeof (in6_addr_t) / sizeof (uint16_t)); i++) { 48267c478bd9Sstevel@tonic-gate addrsum += ptr[i]; 48277c478bd9Sstevel@tonic-gate } 48287c478bd9Sstevel@tonic-gate cksm -= ((addrsum >> 16) + (addrsum & 0xFFFF)); 48297c478bd9Sstevel@tonic-gate if ((int)cksm < 0) 48307c478bd9Sstevel@tonic-gate cksm--; 48317c478bd9Sstevel@tonic-gate cksm = (cksm & 0xFFFF) + (cksm >> 16); 48327c478bd9Sstevel@tonic-gate 48337c478bd9Sstevel@tonic-gate return (cksm); 48347c478bd9Sstevel@tonic-gate } 48357c478bd9Sstevel@tonic-gate 48367c478bd9Sstevel@tonic-gate void 4837f4b3ec61Sdh155122 *ip6_kstat_init(netstackid_t stackid, ip6_stat_t *ip6_statisticsp) 48387c478bd9Sstevel@tonic-gate { 4839f4b3ec61Sdh155122 kstat_t *ksp; 4840f4b3ec61Sdh155122 4841f4b3ec61Sdh155122 ip6_stat_t template = { 4842f4b3ec61Sdh155122 { "ip6_udp_fannorm", KSTAT_DATA_UINT64 }, 4843f4b3ec61Sdh155122 { "ip6_udp_fanmb", KSTAT_DATA_UINT64 }, 4844bd670b35SErik Nordmark { "ip6_recv_pullup", KSTAT_DATA_UINT64 }, 4845bd670b35SErik Nordmark { "ip6_db_ref", KSTAT_DATA_UINT64 }, 4846bd670b35SErik Nordmark { "ip6_notaligned", KSTAT_DATA_UINT64 }, 4847bd670b35SErik Nordmark { "ip6_multimblk", KSTAT_DATA_UINT64 }, 4848bd670b35SErik Nordmark { "ipsec_proto_ahesp", KSTAT_DATA_UINT64 }, 4849f4b3ec61Sdh155122 { "ip6_out_sw_cksum", KSTAT_DATA_UINT64 }, 4850bd670b35SErik Nordmark { "ip6_out_sw_cksum_bytes", KSTAT_DATA_UINT64 }, 4851f4b3ec61Sdh155122 { "ip6_in_sw_cksum", KSTAT_DATA_UINT64 }, 4852f4b3ec61Sdh155122 { "ip6_tcp_in_full_hw_cksum_err", KSTAT_DATA_UINT64 }, 4853f4b3ec61Sdh155122 { "ip6_tcp_in_part_hw_cksum_err", KSTAT_DATA_UINT64 }, 4854f4b3ec61Sdh155122 { "ip6_tcp_in_sw_cksum_err", KSTAT_DATA_UINT64 }, 4855f4b3ec61Sdh155122 { "ip6_udp_in_full_hw_cksum_err", KSTAT_DATA_UINT64 }, 4856f4b3ec61Sdh155122 { "ip6_udp_in_part_hw_cksum_err", KSTAT_DATA_UINT64 }, 4857f4b3ec61Sdh155122 { "ip6_udp_in_sw_cksum_err", KSTAT_DATA_UINT64 }, 4858f4b3ec61Sdh155122 }; 4859f4b3ec61Sdh155122 ksp = kstat_create_netstack("ip", 0, "ip6stat", "net", 4860f4b3ec61Sdh155122 KSTAT_TYPE_NAMED, sizeof (template) / sizeof (kstat_named_t), 4861f4b3ec61Sdh155122 KSTAT_FLAG_VIRTUAL, stackid); 4862f4b3ec61Sdh155122 4863f4b3ec61Sdh155122 if (ksp == NULL) 4864f4b3ec61Sdh155122 return (NULL); 4865f4b3ec61Sdh155122 4866f4b3ec61Sdh155122 bcopy(&template, ip6_statisticsp, sizeof (template)); 4867f4b3ec61Sdh155122 ksp->ks_data = (void *)ip6_statisticsp; 4868f4b3ec61Sdh155122 ksp->ks_private = (void *)(uintptr_t)stackid; 4869f4b3ec61Sdh155122 4870f4b3ec61Sdh155122 kstat_install(ksp); 4871f4b3ec61Sdh155122 return (ksp); 4872f4b3ec61Sdh155122 } 4873f4b3ec61Sdh155122 4874f4b3ec61Sdh155122 void 4875f4b3ec61Sdh155122 ip6_kstat_fini(netstackid_t stackid, kstat_t *ksp) 4876f4b3ec61Sdh155122 { 4877f4b3ec61Sdh155122 if (ksp != NULL) { 4878f4b3ec61Sdh155122 ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private); 4879f4b3ec61Sdh155122 kstat_delete_netstack(ksp, stackid); 48807c478bd9Sstevel@tonic-gate } 48817c478bd9Sstevel@tonic-gate } 48827c478bd9Sstevel@tonic-gate 48837c478bd9Sstevel@tonic-gate /* 48847c478bd9Sstevel@tonic-gate * The following two functions set and get the value for the 48857c478bd9Sstevel@tonic-gate * IPV6_SRC_PREFERENCES socket option. 48867c478bd9Sstevel@tonic-gate */ 48877c478bd9Sstevel@tonic-gate int 4888bd670b35SErik Nordmark ip6_set_src_preferences(ip_xmit_attr_t *ixa, uint32_t prefs) 48897c478bd9Sstevel@tonic-gate { 48907c478bd9Sstevel@tonic-gate /* 48917c478bd9Sstevel@tonic-gate * We only support preferences that are covered by 48927c478bd9Sstevel@tonic-gate * IPV6_PREFER_SRC_MASK. 48937c478bd9Sstevel@tonic-gate */ 48947c478bd9Sstevel@tonic-gate if (prefs & ~IPV6_PREFER_SRC_MASK) 48957c478bd9Sstevel@tonic-gate return (EINVAL); 48967c478bd9Sstevel@tonic-gate 48977c478bd9Sstevel@tonic-gate /* 48987c478bd9Sstevel@tonic-gate * Look for conflicting preferences or default preferences. If 48997c478bd9Sstevel@tonic-gate * both bits of a related pair are clear, the application wants the 49007c478bd9Sstevel@tonic-gate * system's default value for that pair. Both bits in a pair can't 49017c478bd9Sstevel@tonic-gate * be set. 49027c478bd9Sstevel@tonic-gate */ 49037c478bd9Sstevel@tonic-gate if ((prefs & IPV6_PREFER_SRC_MIPMASK) == 0) { 49047c478bd9Sstevel@tonic-gate prefs |= IPV6_PREFER_SRC_MIPDEFAULT; 49057c478bd9Sstevel@tonic-gate } else if ((prefs & IPV6_PREFER_SRC_MIPMASK) == 49067c478bd9Sstevel@tonic-gate IPV6_PREFER_SRC_MIPMASK) { 49077c478bd9Sstevel@tonic-gate return (EINVAL); 49087c478bd9Sstevel@tonic-gate } 49097c478bd9Sstevel@tonic-gate if ((prefs & IPV6_PREFER_SRC_TMPMASK) == 0) { 49107c478bd9Sstevel@tonic-gate prefs |= IPV6_PREFER_SRC_TMPDEFAULT; 49117c478bd9Sstevel@tonic-gate } else if ((prefs & IPV6_PREFER_SRC_TMPMASK) == 49127c478bd9Sstevel@tonic-gate IPV6_PREFER_SRC_TMPMASK) { 49137c478bd9Sstevel@tonic-gate return (EINVAL); 49147c478bd9Sstevel@tonic-gate } 49157c478bd9Sstevel@tonic-gate if ((prefs & IPV6_PREFER_SRC_CGAMASK) == 0) { 49167c478bd9Sstevel@tonic-gate prefs |= IPV6_PREFER_SRC_CGADEFAULT; 49177c478bd9Sstevel@tonic-gate } else if ((prefs & IPV6_PREFER_SRC_CGAMASK) == 49187c478bd9Sstevel@tonic-gate IPV6_PREFER_SRC_CGAMASK) { 49197c478bd9Sstevel@tonic-gate return (EINVAL); 49207c478bd9Sstevel@tonic-gate } 49217c478bd9Sstevel@tonic-gate 4922bd670b35SErik Nordmark ixa->ixa_src_preferences = prefs; 49237c478bd9Sstevel@tonic-gate return (0); 49247c478bd9Sstevel@tonic-gate } 49257c478bd9Sstevel@tonic-gate 49267c478bd9Sstevel@tonic-gate size_t 4927bd670b35SErik Nordmark ip6_get_src_preferences(ip_xmit_attr_t *ixa, uint32_t *val) 49287c478bd9Sstevel@tonic-gate { 4929bd670b35SErik Nordmark *val = ixa->ixa_src_preferences; 4930bd670b35SErik Nordmark return (sizeof (ixa->ixa_src_preferences)); 49317c478bd9Sstevel@tonic-gate } 49327c478bd9Sstevel@tonic-gate 49337c478bd9Sstevel@tonic-gate /* 49347c478bd9Sstevel@tonic-gate * Get the size of the IP options (including the IP headers size) 49357c478bd9Sstevel@tonic-gate * without including the AH header's size. If till_ah is B_FALSE, 49367c478bd9Sstevel@tonic-gate * and if AH header is present, dest options beyond AH header will 49377c478bd9Sstevel@tonic-gate * also be included in the returned size. 49387c478bd9Sstevel@tonic-gate */ 49397c478bd9Sstevel@tonic-gate int 49407c478bd9Sstevel@tonic-gate ipsec_ah_get_hdr_size_v6(mblk_t *mp, boolean_t till_ah) 49417c478bd9Sstevel@tonic-gate { 49427c478bd9Sstevel@tonic-gate ip6_t *ip6h; 49437c478bd9Sstevel@tonic-gate uint8_t nexthdr; 49447c478bd9Sstevel@tonic-gate uint8_t *whereptr; 49457c478bd9Sstevel@tonic-gate ip6_hbh_t *hbhhdr; 49467c478bd9Sstevel@tonic-gate ip6_dest_t *dsthdr; 49477c478bd9Sstevel@tonic-gate ip6_rthdr_t *rthdr; 49487c478bd9Sstevel@tonic-gate int ehdrlen; 49497c478bd9Sstevel@tonic-gate int size; 49507c478bd9Sstevel@tonic-gate ah_t *ah; 49517c478bd9Sstevel@tonic-gate 49527c478bd9Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 49537c478bd9Sstevel@tonic-gate size = IPV6_HDR_LEN; 49547c478bd9Sstevel@tonic-gate nexthdr = ip6h->ip6_nxt; 49557c478bd9Sstevel@tonic-gate whereptr = (uint8_t *)&ip6h[1]; 49567c478bd9Sstevel@tonic-gate for (;;) { 49577c478bd9Sstevel@tonic-gate /* Assume IP has already stripped it */ 4958bd670b35SErik Nordmark ASSERT(nexthdr != IPPROTO_FRAGMENT); 49597c478bd9Sstevel@tonic-gate switch (nexthdr) { 49607c478bd9Sstevel@tonic-gate case IPPROTO_HOPOPTS: 49617c478bd9Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)whereptr; 49627c478bd9Sstevel@tonic-gate nexthdr = hbhhdr->ip6h_nxt; 49637c478bd9Sstevel@tonic-gate ehdrlen = 8 * (hbhhdr->ip6h_len + 1); 49647c478bd9Sstevel@tonic-gate break; 49657c478bd9Sstevel@tonic-gate case IPPROTO_DSTOPTS: 49667c478bd9Sstevel@tonic-gate dsthdr = (ip6_dest_t *)whereptr; 49677c478bd9Sstevel@tonic-gate nexthdr = dsthdr->ip6d_nxt; 49687c478bd9Sstevel@tonic-gate ehdrlen = 8 * (dsthdr->ip6d_len + 1); 49697c478bd9Sstevel@tonic-gate break; 49707c478bd9Sstevel@tonic-gate case IPPROTO_ROUTING: 49717c478bd9Sstevel@tonic-gate rthdr = (ip6_rthdr_t *)whereptr; 49727c478bd9Sstevel@tonic-gate nexthdr = rthdr->ip6r_nxt; 49737c478bd9Sstevel@tonic-gate ehdrlen = 8 * (rthdr->ip6r_len + 1); 49747c478bd9Sstevel@tonic-gate break; 49757c478bd9Sstevel@tonic-gate default : 49767c478bd9Sstevel@tonic-gate if (till_ah) { 49777c478bd9Sstevel@tonic-gate ASSERT(nexthdr == IPPROTO_AH); 49787c478bd9Sstevel@tonic-gate return (size); 49797c478bd9Sstevel@tonic-gate } 49807c478bd9Sstevel@tonic-gate /* 49817c478bd9Sstevel@tonic-gate * If we don't have a AH header to traverse, 49827c478bd9Sstevel@tonic-gate * return now. This happens normally for 49837c478bd9Sstevel@tonic-gate * outbound datagrams where we have not inserted 49847c478bd9Sstevel@tonic-gate * the AH header. 49857c478bd9Sstevel@tonic-gate */ 49867c478bd9Sstevel@tonic-gate if (nexthdr != IPPROTO_AH) { 49877c478bd9Sstevel@tonic-gate return (size); 49887c478bd9Sstevel@tonic-gate } 49897c478bd9Sstevel@tonic-gate 49907c478bd9Sstevel@tonic-gate /* 49917c478bd9Sstevel@tonic-gate * We don't include the AH header's size 49927c478bd9Sstevel@tonic-gate * to be symmetrical with other cases where 49937c478bd9Sstevel@tonic-gate * we either don't have a AH header (outbound) 49947c478bd9Sstevel@tonic-gate * or peek into the AH header yet (inbound and 49957c478bd9Sstevel@tonic-gate * not pulled up yet). 49967c478bd9Sstevel@tonic-gate */ 49977c478bd9Sstevel@tonic-gate ah = (ah_t *)whereptr; 49987c478bd9Sstevel@tonic-gate nexthdr = ah->ah_nexthdr; 49997c478bd9Sstevel@tonic-gate ehdrlen = (ah->ah_length << 2) + 8; 50007c478bd9Sstevel@tonic-gate 50017c478bd9Sstevel@tonic-gate if (nexthdr == IPPROTO_DSTOPTS) { 50027c478bd9Sstevel@tonic-gate if (whereptr + ehdrlen >= mp->b_wptr) { 50037c478bd9Sstevel@tonic-gate /* 50047c478bd9Sstevel@tonic-gate * The destination options header 50057c478bd9Sstevel@tonic-gate * is not part of the first mblk. 50067c478bd9Sstevel@tonic-gate */ 50077c478bd9Sstevel@tonic-gate whereptr = mp->b_cont->b_rptr; 50087c478bd9Sstevel@tonic-gate } else { 50097c478bd9Sstevel@tonic-gate whereptr += ehdrlen; 50107c478bd9Sstevel@tonic-gate } 50117c478bd9Sstevel@tonic-gate 50127c478bd9Sstevel@tonic-gate dsthdr = (ip6_dest_t *)whereptr; 50137c478bd9Sstevel@tonic-gate ehdrlen = 8 * (dsthdr->ip6d_len + 1); 50147c478bd9Sstevel@tonic-gate size += ehdrlen; 50157c478bd9Sstevel@tonic-gate } 50167c478bd9Sstevel@tonic-gate return (size); 50177c478bd9Sstevel@tonic-gate } 50187c478bd9Sstevel@tonic-gate whereptr += ehdrlen; 50197c478bd9Sstevel@tonic-gate size += ehdrlen; 50207c478bd9Sstevel@tonic-gate } 50217c478bd9Sstevel@tonic-gate } 5022e11c3f44Smeem 5023e11c3f44Smeem /* 5024e11c3f44Smeem * Utility routine that checks if `v6srcp' is a valid address on underlying 5025e11c3f44Smeem * interface `ill'. If `ipifp' is non-NULL, it's set to a held ipif 5026e11c3f44Smeem * associated with `v6srcp' on success. NOTE: if this is not called from 5027e11c3f44Smeem * inside the IPSQ (ill_g_lock is not held), `ill' may be removed from the 5028e11c3f44Smeem * group during or after this lookup. 5029e11c3f44Smeem */ 5030bd670b35SErik Nordmark boolean_t 5031e11c3f44Smeem ipif_lookup_testaddr_v6(ill_t *ill, const in6_addr_t *v6srcp, ipif_t **ipifp) 5032e11c3f44Smeem { 5033e11c3f44Smeem ipif_t *ipif; 5034e11c3f44Smeem 5035bd670b35SErik Nordmark 5036e11c3f44Smeem ipif = ipif_lookup_addr_exact_v6(v6srcp, ill, ill->ill_ipst); 5037e11c3f44Smeem if (ipif != NULL) { 5038e11c3f44Smeem if (ipifp != NULL) 5039e11c3f44Smeem *ipifp = ipif; 5040e11c3f44Smeem else 5041e11c3f44Smeem ipif_refrele(ipif); 5042e11c3f44Smeem return (B_TRUE); 5043e11c3f44Smeem } 5044e11c3f44Smeem 5045e11c3f44Smeem if (ip_debug > 2) { 5046e11c3f44Smeem pr_addr_dbg("ipif_lookup_testaddr_v6: cannot find ipif for " 5047e11c3f44Smeem "src %s\n", AF_INET6, v6srcp); 5048e11c3f44Smeem } 5049e11c3f44Smeem return (B_FALSE); 5050e11c3f44Smeem } 5051