10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
51473Sja97890 * Common Development and Distribution License (the "License").
61473Sja97890 * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
2212328SSowmini.Varadhan@Sun.COM * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate * Copyright (c) 1990 Mentat Inc.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate #include <sys/types.h>
270Sstevel@tonic-gate #include <sys/stream.h>
280Sstevel@tonic-gate #include <sys/dlpi.h>
290Sstevel@tonic-gate #include <sys/stropts.h>
300Sstevel@tonic-gate #include <sys/sysmacros.h>
310Sstevel@tonic-gate #include <sys/strsun.h>
320Sstevel@tonic-gate #include <sys/strlog.h>
330Sstevel@tonic-gate #include <sys/strsubr.h>
340Sstevel@tonic-gate #define _SUN_TPI_VERSION 2
350Sstevel@tonic-gate #include <sys/tihdr.h>
360Sstevel@tonic-gate #include <sys/ddi.h>
370Sstevel@tonic-gate #include <sys/sunddi.h>
380Sstevel@tonic-gate #include <sys/cmn_err.h>
390Sstevel@tonic-gate #include <sys/debug.h>
402958Sdr146992 #include <sys/sdt.h>
410Sstevel@tonic-gate #include <sys/kobj.h>
420Sstevel@tonic-gate #include <sys/zone.h>
432958Sdr146992 #include <sys/neti.h>
442958Sdr146992 #include <sys/hook.h>
450Sstevel@tonic-gate
460Sstevel@tonic-gate #include <sys/kmem.h>
470Sstevel@tonic-gate #include <sys/systm.h>
480Sstevel@tonic-gate #include <sys/param.h>
490Sstevel@tonic-gate #include <sys/socket.h>
500Sstevel@tonic-gate #include <sys/vtrace.h>
510Sstevel@tonic-gate #include <sys/isa_defs.h>
520Sstevel@tonic-gate #include <sys/atomic.h>
530Sstevel@tonic-gate #include <sys/policy.h>
5411042SErik.Nordmark@Sun.COM #include <sys/mac.h>
550Sstevel@tonic-gate #include <net/if.h>
56741Smasputra #include <net/if_types.h>
570Sstevel@tonic-gate #include <net/route.h>
580Sstevel@tonic-gate #include <net/if_dl.h>
590Sstevel@tonic-gate #include <sys/sockio.h>
600Sstevel@tonic-gate #include <netinet/in.h>
610Sstevel@tonic-gate #include <netinet/ip6.h>
620Sstevel@tonic-gate #include <netinet/icmp6.h>
630Sstevel@tonic-gate #include <netinet/sctp.h>
640Sstevel@tonic-gate
650Sstevel@tonic-gate #include <inet/common.h>
660Sstevel@tonic-gate #include <inet/mi.h>
675240Snordmark #include <inet/optcom.h>
680Sstevel@tonic-gate #include <inet/mib2.h>
690Sstevel@tonic-gate #include <inet/nd.h>
700Sstevel@tonic-gate #include <inet/arp.h>
710Sstevel@tonic-gate
720Sstevel@tonic-gate #include <inet/ip.h>
73741Smasputra #include <inet/ip_impl.h>
740Sstevel@tonic-gate #include <inet/ip6.h>
750Sstevel@tonic-gate #include <inet/ip6_asp.h>
760Sstevel@tonic-gate #include <inet/tcp.h>
77741Smasputra #include <inet/tcp_impl.h>
78741Smasputra #include <inet/udp_impl.h>
790Sstevel@tonic-gate #include <inet/ipp_common.h>
800Sstevel@tonic-gate
810Sstevel@tonic-gate #include <inet/ip_multi.h>
820Sstevel@tonic-gate #include <inet/ip_if.h>
830Sstevel@tonic-gate #include <inet/ip_ire.h>
840Sstevel@tonic-gate #include <inet/ip_rts.h>
850Sstevel@tonic-gate #include <inet/ip_ndp.h>
860Sstevel@tonic-gate #include <net/pfkeyv2.h>
870Sstevel@tonic-gate #include <inet/sadb.h>
880Sstevel@tonic-gate #include <inet/ipsec_impl.h>
8910616SSebastien.Roy@Sun.COM #include <inet/iptun/iptun_impl.h>
900Sstevel@tonic-gate #include <inet/sctp_ip.h>
910Sstevel@tonic-gate #include <sys/pattr.h>
920Sstevel@tonic-gate #include <inet/ipclassifier.h>
930Sstevel@tonic-gate #include <inet/ipsecah.h>
945240Snordmark #include <inet/rawip_impl.h>
955240Snordmark #include <inet/rts_impl.h>
968275SEric Cheng #include <sys/squeue_impl.h>
970Sstevel@tonic-gate #include <sys/squeue.h>
980Sstevel@tonic-gate
991676Sjpk #include <sys/tsol/label.h>
1001676Sjpk #include <sys/tsol/tnet.h>
1011676Sjpk
1022546Scarlsonj /* Temporary; for CR 6451644 work-around */
1032546Scarlsonj #include <sys/ethernet.h>
1042546Scarlsonj
1050Sstevel@tonic-gate /*
1060Sstevel@tonic-gate * Naming conventions:
1070Sstevel@tonic-gate * These rules should be judiciously applied
1080Sstevel@tonic-gate * if there is a need to identify something as IPv6 versus IPv4
1090Sstevel@tonic-gate * IPv6 funcions will end with _v6 in the ip module.
1100Sstevel@tonic-gate * IPv6 funcions will end with _ipv6 in the transport modules.
1110Sstevel@tonic-gate * IPv6 macros:
1120Sstevel@tonic-gate * Some macros end with _V6; e.g. ILL_FRAG_HASH_V6
1130Sstevel@tonic-gate * Some macros start with V6_; e.g. V6_OR_V4_INADDR_ANY
1140Sstevel@tonic-gate * And then there are ..V4_PART_OF_V6.
1150Sstevel@tonic-gate * The intent is that macros in the ip module end with _V6.
1160Sstevel@tonic-gate * IPv6 global variables will start with ipv6_
1170Sstevel@tonic-gate * IPv6 structures will start with ipv6
1180Sstevel@tonic-gate * IPv6 defined constants should start with IPV6_
1190Sstevel@tonic-gate * (but then there are NDP_DEFAULT_VERS_PRI_AND_FLOW, etc)
1200Sstevel@tonic-gate */
1210Sstevel@tonic-gate
1220Sstevel@tonic-gate /*
1231676Sjpk * ip6opt_ls is used to enable IPv6 (via /etc/system on TX systems).
1241676Sjpk * We need to do this because we didn't obtain the IP6OPT_LS (0x0a)
1251676Sjpk * from IANA. This mechanism will remain in effect until an official
1261676Sjpk * number is obtained.
1271676Sjpk */
1281676Sjpk uchar_t ip6opt_ls;
1291676Sjpk
1300Sstevel@tonic-gate const in6_addr_t ipv6_all_ones =
1310Sstevel@tonic-gate { 0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU };
1320Sstevel@tonic-gate const in6_addr_t ipv6_all_zeros = { 0, 0, 0, 0 };
1330Sstevel@tonic-gate
1340Sstevel@tonic-gate #ifdef _BIG_ENDIAN
1350Sstevel@tonic-gate const in6_addr_t ipv6_unspecified_group = { 0xff000000U, 0, 0, 0 };
1360Sstevel@tonic-gate #else /* _BIG_ENDIAN */
1370Sstevel@tonic-gate const in6_addr_t ipv6_unspecified_group = { 0x000000ffU, 0, 0, 0 };
1380Sstevel@tonic-gate #endif /* _BIG_ENDIAN */
1390Sstevel@tonic-gate
1400Sstevel@tonic-gate #ifdef _BIG_ENDIAN
1410Sstevel@tonic-gate const in6_addr_t ipv6_loopback = { 0, 0, 0, 0x00000001U };
1420Sstevel@tonic-gate #else /* _BIG_ENDIAN */
1430Sstevel@tonic-gate const in6_addr_t ipv6_loopback = { 0, 0, 0, 0x01000000U };
1440Sstevel@tonic-gate #endif /* _BIG_ENDIAN */
1450Sstevel@tonic-gate
1460Sstevel@tonic-gate #ifdef _BIG_ENDIAN
1470Sstevel@tonic-gate const in6_addr_t ipv6_all_hosts_mcast = { 0xff020000U, 0, 0, 0x00000001U };
1480Sstevel@tonic-gate #else /* _BIG_ENDIAN */
1490Sstevel@tonic-gate const in6_addr_t ipv6_all_hosts_mcast = { 0x000002ffU, 0, 0, 0x01000000U };
1500Sstevel@tonic-gate #endif /* _BIG_ENDIAN */
1510Sstevel@tonic-gate
1520Sstevel@tonic-gate #ifdef _BIG_ENDIAN
1530Sstevel@tonic-gate const in6_addr_t ipv6_all_rtrs_mcast = { 0xff020000U, 0, 0, 0x00000002U };
1540Sstevel@tonic-gate #else /* _BIG_ENDIAN */
1550Sstevel@tonic-gate const in6_addr_t ipv6_all_rtrs_mcast = { 0x000002ffU, 0, 0, 0x02000000U };
1560Sstevel@tonic-gate #endif /* _BIG_ENDIAN */
1570Sstevel@tonic-gate
1580Sstevel@tonic-gate #ifdef _BIG_ENDIAN
1590Sstevel@tonic-gate const in6_addr_t ipv6_all_v2rtrs_mcast = { 0xff020000U, 0, 0, 0x00000016U };
1600Sstevel@tonic-gate #else /* _BIG_ENDIAN */
1610Sstevel@tonic-gate const in6_addr_t ipv6_all_v2rtrs_mcast = { 0x000002ffU, 0, 0, 0x16000000U };
1620Sstevel@tonic-gate #endif /* _BIG_ENDIAN */
1630Sstevel@tonic-gate
1640Sstevel@tonic-gate #ifdef _BIG_ENDIAN
1650Sstevel@tonic-gate const in6_addr_t ipv6_solicited_node_mcast =
1660Sstevel@tonic-gate { 0xff020000U, 0, 0x00000001U, 0xff000000U };
1670Sstevel@tonic-gate #else /* _BIG_ENDIAN */
1680Sstevel@tonic-gate const in6_addr_t ipv6_solicited_node_mcast =
1690Sstevel@tonic-gate { 0x000002ffU, 0, 0x01000000U, 0x000000ffU };
1700Sstevel@tonic-gate #endif /* _BIG_ENDIAN */
1710Sstevel@tonic-gate
17211042SErik.Nordmark@Sun.COM static boolean_t icmp_inbound_verify_v6(mblk_t *, icmp6_t *, ip_recv_attr_t *);
17311042SErik.Nordmark@Sun.COM static void icmp_inbound_too_big_v6(icmp6_t *, ip_recv_attr_t *);
17411042SErik.Nordmark@Sun.COM static void icmp_pkt_v6(mblk_t *, void *, size_t, const in6_addr_t *,
17511042SErik.Nordmark@Sun.COM ip_recv_attr_t *);
17611042SErik.Nordmark@Sun.COM static void icmp_redirect_v6(mblk_t *, ip6_t *, nd_redirect_t *,
17711042SErik.Nordmark@Sun.COM ip_recv_attr_t *);
17811042SErik.Nordmark@Sun.COM static void icmp_send_redirect_v6(mblk_t *, in6_addr_t *,
17911042SErik.Nordmark@Sun.COM in6_addr_t *, ip_recv_attr_t *);
18011042SErik.Nordmark@Sun.COM static void icmp_send_reply_v6(mblk_t *, ip6_t *, icmp6_t *,
18111042SErik.Nordmark@Sun.COM ip_recv_attr_t *);
1823448Sdh155122 static boolean_t ip_source_routed_v6(ip6_t *, mblk_t *, ip_stack_t *);
1830Sstevel@tonic-gate
1840Sstevel@tonic-gate /*
18511042SErik.Nordmark@Sun.COM * icmp_inbound_v6 deals with ICMP messages that are handled by IP.
18611042SErik.Nordmark@Sun.COM * If the ICMP message is consumed by IP, i.e., it should not be delivered
18711042SErik.Nordmark@Sun.COM * to any IPPROTO_ICMP raw sockets, then it returns NULL.
18811042SErik.Nordmark@Sun.COM * Likewise, if the ICMP error is misformed (too short, etc), then it
18911042SErik.Nordmark@Sun.COM * returns NULL. The caller uses this to determine whether or not to send
19011042SErik.Nordmark@Sun.COM * to raw sockets.
1910Sstevel@tonic-gate *
1920Sstevel@tonic-gate * All error messages are passed to the matching transport stream.
1930Sstevel@tonic-gate *
19411042SErik.Nordmark@Sun.COM * See comment for icmp_inbound_v4() on how IPsec is handled.
1950Sstevel@tonic-gate */
19611042SErik.Nordmark@Sun.COM mblk_t *
icmp_inbound_v6(mblk_t * mp,ip_recv_attr_t * ira)19711042SErik.Nordmark@Sun.COM icmp_inbound_v6(mblk_t *mp, ip_recv_attr_t *ira)
1980Sstevel@tonic-gate {
1990Sstevel@tonic-gate icmp6_t *icmp6;
20011042SErik.Nordmark@Sun.COM ip6_t *ip6h; /* Outer header */
20111042SErik.Nordmark@Sun.COM int ip_hdr_length; /* Outer header length */
2020Sstevel@tonic-gate boolean_t interested;
20311042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill;
2043448Sdh155122 ip_stack_t *ipst = ill->ill_ipst;
20511042SErik.Nordmark@Sun.COM mblk_t *mp_ret = NULL;
2060Sstevel@tonic-gate
2070Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr;
2080Sstevel@tonic-gate
2090Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInMsgs);
2100Sstevel@tonic-gate
21112038SSowmini.Varadhan@Sun.COM /* Check for Martian packets */
21212038SSowmini.Varadhan@Sun.COM if (IN6_IS_ADDR_MULTICAST(&ip6h->ip6_src)) {
21312038SSowmini.Varadhan@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInAddrErrors);
21412038SSowmini.Varadhan@Sun.COM ip_drop_input("ipIfStatsInAddrErrors: mcast src", mp, ill);
21512038SSowmini.Varadhan@Sun.COM freemsg(mp);
21612038SSowmini.Varadhan@Sun.COM return (NULL);
21712038SSowmini.Varadhan@Sun.COM }
21812038SSowmini.Varadhan@Sun.COM
21911042SErik.Nordmark@Sun.COM /* Make sure ira_l2src is set for ndp_input */
22011042SErik.Nordmark@Sun.COM if (!(ira->ira_flags & IRAF_L2SRC_SET))
22111042SErik.Nordmark@Sun.COM ip_setl2src(mp, ira, ira->ira_rill);
22211042SErik.Nordmark@Sun.COM
22311042SErik.Nordmark@Sun.COM ip_hdr_length = ira->ira_ip_hdr_length;
22411042SErik.Nordmark@Sun.COM if ((mp->b_wptr - mp->b_rptr) < (ip_hdr_length + ICMP6_MINLEN)) {
22511042SErik.Nordmark@Sun.COM if (ira->ira_pktlen < (ip_hdr_length + ICMP6_MINLEN)) {
22611042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInTruncatedPkts);
22711042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInTruncatedPkts", mp, ill);
22811042SErik.Nordmark@Sun.COM freemsg(mp);
22911042SErik.Nordmark@Sun.COM return (NULL);
2300Sstevel@tonic-gate }
23111042SErik.Nordmark@Sun.COM ip6h = ip_pullup(mp, ip_hdr_length + ICMP6_MINLEN, ira);
23211042SErik.Nordmark@Sun.COM if (ip6h == NULL) {
23311042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
23411042SErik.Nordmark@Sun.COM freemsg(mp);
23511042SErik.Nordmark@Sun.COM return (NULL);
23611042SErik.Nordmark@Sun.COM }
2370Sstevel@tonic-gate }
23811042SErik.Nordmark@Sun.COM
23911042SErik.Nordmark@Sun.COM icmp6 = (icmp6_t *)(&mp->b_rptr[ip_hdr_length]);
24011042SErik.Nordmark@Sun.COM DTRACE_PROBE2(icmp__inbound__v6, ip6_t *, ip6h, icmp6_t *, icmp6);
24111042SErik.Nordmark@Sun.COM ip2dbg(("icmp_inbound_v6: type %d code %d\n", icmp6->icmp6_type,
24211042SErik.Nordmark@Sun.COM icmp6->icmp6_code));
2430Sstevel@tonic-gate
2441676Sjpk /*
24511042SErik.Nordmark@Sun.COM * We will set "interested" to "true" if we should pass a copy to
24611042SErik.Nordmark@Sun.COM * the transport i.e., if it is an error message.
2471676Sjpk */
2480Sstevel@tonic-gate interested = !(icmp6->icmp6_type & ICMP6_INFOMSG_MASK);
2490Sstevel@tonic-gate
2500Sstevel@tonic-gate switch (icmp6->icmp6_type) {
2510Sstevel@tonic-gate case ICMP6_DST_UNREACH:
2520Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInDestUnreachs);
2530Sstevel@tonic-gate if (icmp6->icmp6_code == ICMP6_DST_UNREACH_ADMIN)
2540Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInAdminProhibs);
2550Sstevel@tonic-gate break;
2560Sstevel@tonic-gate
2570Sstevel@tonic-gate case ICMP6_TIME_EXCEEDED:
2580Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInTimeExcds);
2590Sstevel@tonic-gate break;
2600Sstevel@tonic-gate
2610Sstevel@tonic-gate case ICMP6_PARAM_PROB:
2620Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInParmProblems);
2630Sstevel@tonic-gate break;
2640Sstevel@tonic-gate
2650Sstevel@tonic-gate case ICMP6_PACKET_TOO_BIG:
26611042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInPktTooBigs);
26711042SErik.Nordmark@Sun.COM break;
26811042SErik.Nordmark@Sun.COM
2690Sstevel@tonic-gate case ICMP6_ECHO_REQUEST:
2700Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInEchos);
2710Sstevel@tonic-gate if (IN6_IS_ADDR_MULTICAST(&ip6h->ip6_dst) &&
2723448Sdh155122 !ipst->ips_ipv6_resp_echo_mcast)
2730Sstevel@tonic-gate break;
2740Sstevel@tonic-gate
2750Sstevel@tonic-gate /*
2760Sstevel@tonic-gate * We must have exclusive use of the mblk to convert it to
2770Sstevel@tonic-gate * a response.
2780Sstevel@tonic-gate * If not, we copy it.
2790Sstevel@tonic-gate */
2800Sstevel@tonic-gate if (mp->b_datap->db_ref > 1) {
2810Sstevel@tonic-gate mblk_t *mp1;
2820Sstevel@tonic-gate
2830Sstevel@tonic-gate mp1 = copymsg(mp);
28411042SErik.Nordmark@Sun.COM if (mp1 == NULL) {
28511042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
28611042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards - copymsg",
28711042SErik.Nordmark@Sun.COM mp, ill);
28811042SErik.Nordmark@Sun.COM freemsg(mp);
28911042SErik.Nordmark@Sun.COM return (NULL);
29011042SErik.Nordmark@Sun.COM }
2910Sstevel@tonic-gate freemsg(mp);
2920Sstevel@tonic-gate mp = mp1;
2930Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr;
29411042SErik.Nordmark@Sun.COM icmp6 = (icmp6_t *)(&mp->b_rptr[ip_hdr_length]);
2950Sstevel@tonic-gate }
2960Sstevel@tonic-gate
2970Sstevel@tonic-gate icmp6->icmp6_type = ICMP6_ECHO_REPLY;
29811042SErik.Nordmark@Sun.COM icmp_send_reply_v6(mp, ip6h, icmp6, ira);
29911042SErik.Nordmark@Sun.COM return (NULL);
3000Sstevel@tonic-gate
3010Sstevel@tonic-gate case ICMP6_ECHO_REPLY:
3020Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInEchoReplies);
3030Sstevel@tonic-gate break;
3040Sstevel@tonic-gate
3050Sstevel@tonic-gate case ND_ROUTER_SOLICIT:
3060Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInRouterSolicits);
3070Sstevel@tonic-gate break;
3080Sstevel@tonic-gate
3090Sstevel@tonic-gate case ND_ROUTER_ADVERT:
3100Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInRouterAdvertisements);
3110Sstevel@tonic-gate break;
3120Sstevel@tonic-gate
3130Sstevel@tonic-gate case ND_NEIGHBOR_SOLICIT:
3140Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInNeighborSolicits);
31511042SErik.Nordmark@Sun.COM ndp_input(mp, ira);
31611042SErik.Nordmark@Sun.COM return (NULL);
3170Sstevel@tonic-gate
3180Sstevel@tonic-gate case ND_NEIGHBOR_ADVERT:
3190Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib,
3200Sstevel@tonic-gate ipv6IfIcmpInNeighborAdvertisements);
32111042SErik.Nordmark@Sun.COM ndp_input(mp, ira);
32211042SErik.Nordmark@Sun.COM return (NULL);
32311042SErik.Nordmark@Sun.COM
32411042SErik.Nordmark@Sun.COM case ND_REDIRECT:
3250Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInRedirects);
3260Sstevel@tonic-gate
3273448Sdh155122 if (ipst->ips_ipv6_ignore_redirect)
3280Sstevel@tonic-gate break;
3290Sstevel@tonic-gate
33011042SErik.Nordmark@Sun.COM /* We now allow a RAW socket to receive this. */
33111042SErik.Nordmark@Sun.COM interested = B_TRUE;
33211042SErik.Nordmark@Sun.COM break;
3330Sstevel@tonic-gate
3340Sstevel@tonic-gate /*
3350Sstevel@tonic-gate * The next three icmp messages will be handled by MLD.
3360Sstevel@tonic-gate * Pass all valid MLD packets up to any process(es)
33711042SErik.Nordmark@Sun.COM * listening on a raw ICMP socket.
3380Sstevel@tonic-gate */
3390Sstevel@tonic-gate case MLD_LISTENER_QUERY:
3400Sstevel@tonic-gate case MLD_LISTENER_REPORT:
3410Sstevel@tonic-gate case MLD_LISTENER_REDUCTION:
34211042SErik.Nordmark@Sun.COM mp = mld_input(mp, ira);
34311042SErik.Nordmark@Sun.COM return (mp);
3440Sstevel@tonic-gate default:
3450Sstevel@tonic-gate break;
3460Sstevel@tonic-gate }
34711042SErik.Nordmark@Sun.COM /*
34811042SErik.Nordmark@Sun.COM * See if there is an ICMP client to avoid an extra copymsg/freemsg
34911042SErik.Nordmark@Sun.COM * if there isn't one.
35011042SErik.Nordmark@Sun.COM */
35111042SErik.Nordmark@Sun.COM if (ipst->ips_ipcl_proto_fanout_v6[IPPROTO_ICMPV6].connf_head != NULL) {
35211042SErik.Nordmark@Sun.COM /* If there is an ICMP client and we want one too, copy it. */
35311042SErik.Nordmark@Sun.COM
35411042SErik.Nordmark@Sun.COM if (!interested) {
35511042SErik.Nordmark@Sun.COM /* Caller will deliver to RAW sockets */
35611042SErik.Nordmark@Sun.COM return (mp);
35711042SErik.Nordmark@Sun.COM }
35811042SErik.Nordmark@Sun.COM mp_ret = copymsg(mp);
35911042SErik.Nordmark@Sun.COM if (mp_ret == NULL) {
36011042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
36111042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards - copymsg", mp, ill);
36211042SErik.Nordmark@Sun.COM }
36311042SErik.Nordmark@Sun.COM } else if (!interested) {
36411042SErik.Nordmark@Sun.COM /* Neither we nor raw sockets are interested. Drop packet now */
36511042SErik.Nordmark@Sun.COM freemsg(mp);
36611042SErik.Nordmark@Sun.COM return (NULL);
3670Sstevel@tonic-gate }
36811042SErik.Nordmark@Sun.COM
3690Sstevel@tonic-gate /*
37011042SErik.Nordmark@Sun.COM * ICMP error or redirect packet. Make sure we have enough of
37111042SErik.Nordmark@Sun.COM * the header and that db_ref == 1 since we might end up modifying
37211042SErik.Nordmark@Sun.COM * the packet.
3730Sstevel@tonic-gate */
37411042SErik.Nordmark@Sun.COM if (mp->b_cont != NULL) {
37511042SErik.Nordmark@Sun.COM if (ip_pullup(mp, -1, ira) == NULL) {
37611042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
37711042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards - ip_pullup",
37811042SErik.Nordmark@Sun.COM mp, ill);
37911042SErik.Nordmark@Sun.COM freemsg(mp);
38011042SErik.Nordmark@Sun.COM return (mp_ret);
38111042SErik.Nordmark@Sun.COM }
38211042SErik.Nordmark@Sun.COM }
38311042SErik.Nordmark@Sun.COM
3840Sstevel@tonic-gate if (mp->b_datap->db_ref > 1) {
3850Sstevel@tonic-gate mblk_t *mp1;
3860Sstevel@tonic-gate
3870Sstevel@tonic-gate mp1 = copymsg(mp);
3880Sstevel@tonic-gate if (mp1 == NULL) {
3893284Sapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
39011042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards - copymsg", mp, ill);
39111042SErik.Nordmark@Sun.COM freemsg(mp);
39211042SErik.Nordmark@Sun.COM return (mp_ret);
3930Sstevel@tonic-gate }
39411042SErik.Nordmark@Sun.COM freemsg(mp);
3950Sstevel@tonic-gate mp = mp1;
3960Sstevel@tonic-gate }
3970Sstevel@tonic-gate
3980Sstevel@tonic-gate /*
39911042SErik.Nordmark@Sun.COM * In case mp has changed, verify the message before any further
40011042SErik.Nordmark@Sun.COM * processes.
40111042SErik.Nordmark@Sun.COM */
40211042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr;
40311042SErik.Nordmark@Sun.COM icmp6 = (icmp6_t *)(&mp->b_rptr[ip_hdr_length]);
40411042SErik.Nordmark@Sun.COM if (!icmp_inbound_verify_v6(mp, icmp6, ira)) {
40511042SErik.Nordmark@Sun.COM freemsg(mp);
40611042SErik.Nordmark@Sun.COM return (mp_ret);
40711042SErik.Nordmark@Sun.COM }
40811042SErik.Nordmark@Sun.COM
40911042SErik.Nordmark@Sun.COM switch (icmp6->icmp6_type) {
41011042SErik.Nordmark@Sun.COM case ND_REDIRECT:
41111042SErik.Nordmark@Sun.COM icmp_redirect_v6(mp, ip6h, (nd_redirect_t *)icmp6, ira);
41211042SErik.Nordmark@Sun.COM break;
41311042SErik.Nordmark@Sun.COM case ICMP6_PACKET_TOO_BIG:
41411042SErik.Nordmark@Sun.COM /* Update DCE and adjust MTU is icmp header if needed */
41511042SErik.Nordmark@Sun.COM icmp_inbound_too_big_v6(icmp6, ira);
41611042SErik.Nordmark@Sun.COM /* FALLTHRU */
41711042SErik.Nordmark@Sun.COM default:
41811042SErik.Nordmark@Sun.COM icmp_inbound_error_fanout_v6(mp, icmp6, ira);
41911042SErik.Nordmark@Sun.COM break;
42011042SErik.Nordmark@Sun.COM }
42111042SErik.Nordmark@Sun.COM
42211042SErik.Nordmark@Sun.COM return (mp_ret);
42311042SErik.Nordmark@Sun.COM }
42411042SErik.Nordmark@Sun.COM
42511042SErik.Nordmark@Sun.COM /*
42611042SErik.Nordmark@Sun.COM * Send an ICMP echo reply.
42711042SErik.Nordmark@Sun.COM * The caller has already updated the payload part of the packet.
42811042SErik.Nordmark@Sun.COM * We handle the ICMP checksum, IP source address selection and feed
42911042SErik.Nordmark@Sun.COM * the packet into ip_output_simple.
43011042SErik.Nordmark@Sun.COM */
43111042SErik.Nordmark@Sun.COM static void
icmp_send_reply_v6(mblk_t * mp,ip6_t * ip6h,icmp6_t * icmp6,ip_recv_attr_t * ira)43211042SErik.Nordmark@Sun.COM icmp_send_reply_v6(mblk_t *mp, ip6_t *ip6h, icmp6_t *icmp6,
43311042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira)
43411042SErik.Nordmark@Sun.COM {
43511042SErik.Nordmark@Sun.COM uint_t ip_hdr_length = ira->ira_ip_hdr_length;
43611042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill;
43711042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst;
43811042SErik.Nordmark@Sun.COM ip_xmit_attr_t ixas;
43911042SErik.Nordmark@Sun.COM in6_addr_t origsrc;
44011042SErik.Nordmark@Sun.COM
44111042SErik.Nordmark@Sun.COM /*
44211042SErik.Nordmark@Sun.COM * Remove any extension headers (do not reverse a source route)
44311042SErik.Nordmark@Sun.COM * and clear the flow id (keep traffic class for now).
44411042SErik.Nordmark@Sun.COM */
44511042SErik.Nordmark@Sun.COM if (ip_hdr_length != IPV6_HDR_LEN) {
44611042SErik.Nordmark@Sun.COM int i;
44711042SErik.Nordmark@Sun.COM
44811042SErik.Nordmark@Sun.COM for (i = 0; i < IPV6_HDR_LEN; i++) {
44911042SErik.Nordmark@Sun.COM mp->b_rptr[ip_hdr_length - i - 1] =
45011042SErik.Nordmark@Sun.COM mp->b_rptr[IPV6_HDR_LEN - i - 1];
45111042SErik.Nordmark@Sun.COM }
45211042SErik.Nordmark@Sun.COM mp->b_rptr += (ip_hdr_length - IPV6_HDR_LEN);
45311042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr;
45411042SErik.Nordmark@Sun.COM ip6h->ip6_nxt = IPPROTO_ICMPV6;
45511042SErik.Nordmark@Sun.COM i = ntohs(ip6h->ip6_plen);
45611042SErik.Nordmark@Sun.COM i -= (ip_hdr_length - IPV6_HDR_LEN);
45711042SErik.Nordmark@Sun.COM ip6h->ip6_plen = htons(i);
45811042SErik.Nordmark@Sun.COM ip_hdr_length = IPV6_HDR_LEN;
45911042SErik.Nordmark@Sun.COM ASSERT(ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN == msgdsize(mp));
46011042SErik.Nordmark@Sun.COM }
46111042SErik.Nordmark@Sun.COM ip6h->ip6_vcf &= ~IPV6_FLOWINFO_FLOWLABEL;
46211042SErik.Nordmark@Sun.COM
46311042SErik.Nordmark@Sun.COM /* Reverse the source and destination addresses. */
46411042SErik.Nordmark@Sun.COM origsrc = ip6h->ip6_src;
46511042SErik.Nordmark@Sun.COM ip6h->ip6_src = ip6h->ip6_dst;
46611042SErik.Nordmark@Sun.COM ip6h->ip6_dst = origsrc;
46711042SErik.Nordmark@Sun.COM
46811042SErik.Nordmark@Sun.COM /* set the hop limit */
46911042SErik.Nordmark@Sun.COM ip6h->ip6_hops = ipst->ips_ipv6_def_hops;
47011042SErik.Nordmark@Sun.COM
47111042SErik.Nordmark@Sun.COM /*
47211042SErik.Nordmark@Sun.COM * Prepare for checksum by putting icmp length in the icmp
47311042SErik.Nordmark@Sun.COM * checksum field. The checksum is calculated in ip_output
47411042SErik.Nordmark@Sun.COM */
47511042SErik.Nordmark@Sun.COM icmp6->icmp6_cksum = ip6h->ip6_plen;
47611042SErik.Nordmark@Sun.COM
47711042SErik.Nordmark@Sun.COM bzero(&ixas, sizeof (ixas));
47811042SErik.Nordmark@Sun.COM ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6;
47911042SErik.Nordmark@Sun.COM ixas.ixa_zoneid = ira->ira_zoneid;
48011042SErik.Nordmark@Sun.COM ixas.ixa_cred = kcred;
48111042SErik.Nordmark@Sun.COM ixas.ixa_cpid = NOPID;
48211042SErik.Nordmark@Sun.COM ixas.ixa_tsl = ira->ira_tsl; /* Behave as a multi-level responder */
48311042SErik.Nordmark@Sun.COM ixas.ixa_ifindex = 0;
48411042SErik.Nordmark@Sun.COM ixas.ixa_ipst = ipst;
48511042SErik.Nordmark@Sun.COM ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
48611042SErik.Nordmark@Sun.COM
48711042SErik.Nordmark@Sun.COM if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) {
48811042SErik.Nordmark@Sun.COM /*
48911042SErik.Nordmark@Sun.COM * This packet should go out the same way as it
49011042SErik.Nordmark@Sun.COM * came in i.e in clear, independent of the IPsec
49111042SErik.Nordmark@Sun.COM * policy for transmitting packets.
49211042SErik.Nordmark@Sun.COM */
49311042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_NO_IPSEC;
49411042SErik.Nordmark@Sun.COM } else {
49511042SErik.Nordmark@Sun.COM if (!ipsec_in_to_out(ira, &ixas, mp, NULL, ip6h)) {
49611042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
49711042SErik.Nordmark@Sun.COM /* Note: mp already consumed and ip_drop_packet done */
49811042SErik.Nordmark@Sun.COM return;
49911042SErik.Nordmark@Sun.COM }
50011042SErik.Nordmark@Sun.COM }
50111042SErik.Nordmark@Sun.COM
50211042SErik.Nordmark@Sun.COM /* Was the destination (now source) link-local? Send out same group */
50311042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_src)) {
50411042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_SCOPEID_SET;
50511042SErik.Nordmark@Sun.COM if (IS_UNDER_IPMP(ill))
50611042SErik.Nordmark@Sun.COM ixas.ixa_scopeid = ill_get_upper_ifindex(ill);
50711042SErik.Nordmark@Sun.COM else
50811042SErik.Nordmark@Sun.COM ixas.ixa_scopeid = ill->ill_phyint->phyint_ifindex;
50911042SErik.Nordmark@Sun.COM }
51011042SErik.Nordmark@Sun.COM
51111042SErik.Nordmark@Sun.COM if (ira->ira_flags & IRAF_MULTIBROADCAST) {
51211042SErik.Nordmark@Sun.COM /*
51311042SErik.Nordmark@Sun.COM * Not one or our addresses (IRE_LOCALs), thus we let
51411042SErik.Nordmark@Sun.COM * ip_output_simple pick the source.
51511042SErik.Nordmark@Sun.COM */
51611042SErik.Nordmark@Sun.COM ip6h->ip6_src = ipv6_all_zeros;
51711042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_SET_SOURCE;
51811042SErik.Nordmark@Sun.COM }
51911042SErik.Nordmark@Sun.COM
52011042SErik.Nordmark@Sun.COM /* Should we send using dce_pmtu? */
52111042SErik.Nordmark@Sun.COM if (ipst->ips_ipv6_icmp_return_pmtu)
52211042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_PMTU_DISCOVERY;
52311042SErik.Nordmark@Sun.COM
52411042SErik.Nordmark@Sun.COM (void) ip_output_simple(mp, &ixas);
52511042SErik.Nordmark@Sun.COM ixa_cleanup(&ixas);
52611042SErik.Nordmark@Sun.COM
52711042SErik.Nordmark@Sun.COM }
52811042SErik.Nordmark@Sun.COM
52911042SErik.Nordmark@Sun.COM /*
53011042SErik.Nordmark@Sun.COM * Verify the ICMP messages for either for ICMP error or redirect packet.
53111042SErik.Nordmark@Sun.COM * The caller should have fully pulled up the message. If it's a redirect
53211042SErik.Nordmark@Sun.COM * packet, only basic checks on IP header will be done; otherwise, verify
53311042SErik.Nordmark@Sun.COM * the packet by looking at the included ULP header.
53411042SErik.Nordmark@Sun.COM *
53511042SErik.Nordmark@Sun.COM * Called before icmp_inbound_error_fanout_v6 is called.
53611042SErik.Nordmark@Sun.COM */
53711042SErik.Nordmark@Sun.COM static boolean_t
icmp_inbound_verify_v6(mblk_t * mp,icmp6_t * icmp6,ip_recv_attr_t * ira)53811042SErik.Nordmark@Sun.COM icmp_inbound_verify_v6(mblk_t *mp, icmp6_t *icmp6, ip_recv_attr_t *ira)
53911042SErik.Nordmark@Sun.COM {
54011042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill;
54111042SErik.Nordmark@Sun.COM uint16_t hdr_length;
54211042SErik.Nordmark@Sun.COM uint8_t *nexthdrp;
54311042SErik.Nordmark@Sun.COM uint8_t nexthdr;
54411042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst;
54511042SErik.Nordmark@Sun.COM conn_t *connp;
54611042SErik.Nordmark@Sun.COM ip6_t *ip6h; /* Inner header */
54711042SErik.Nordmark@Sun.COM
54811042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)&icmp6[1];
54911042SErik.Nordmark@Sun.COM if ((uchar_t *)ip6h + IPV6_HDR_LEN > mp->b_wptr)
55011042SErik.Nordmark@Sun.COM goto truncated;
55111042SErik.Nordmark@Sun.COM
55211042SErik.Nordmark@Sun.COM if (icmp6->icmp6_type == ND_REDIRECT) {
55311042SErik.Nordmark@Sun.COM hdr_length = sizeof (nd_redirect_t);
55411042SErik.Nordmark@Sun.COM } else {
55511042SErik.Nordmark@Sun.COM if ((IPH_HDR_VERSION(ip6h) != IPV6_VERSION))
55611042SErik.Nordmark@Sun.COM goto discard_pkt;
55711042SErik.Nordmark@Sun.COM hdr_length = IPV6_HDR_LEN;
55811042SErik.Nordmark@Sun.COM }
55911042SErik.Nordmark@Sun.COM
56011042SErik.Nordmark@Sun.COM if ((uchar_t *)ip6h + hdr_length > mp->b_wptr)
56111042SErik.Nordmark@Sun.COM goto truncated;
56211042SErik.Nordmark@Sun.COM
56311042SErik.Nordmark@Sun.COM /*
56411042SErik.Nordmark@Sun.COM * Stop here for ICMP_REDIRECT.
56511042SErik.Nordmark@Sun.COM */
56611042SErik.Nordmark@Sun.COM if (icmp6->icmp6_type == ND_REDIRECT)
56711042SErik.Nordmark@Sun.COM return (B_TRUE);
56811042SErik.Nordmark@Sun.COM
56911042SErik.Nordmark@Sun.COM /*
57011042SErik.Nordmark@Sun.COM * ICMP errors only.
57111042SErik.Nordmark@Sun.COM */
57211042SErik.Nordmark@Sun.COM if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp))
57311042SErik.Nordmark@Sun.COM goto discard_pkt;
57411042SErik.Nordmark@Sun.COM nexthdr = *nexthdrp;
57511042SErik.Nordmark@Sun.COM
57611042SErik.Nordmark@Sun.COM /* Try to pass the ICMP message to clients who need it */
57711042SErik.Nordmark@Sun.COM switch (nexthdr) {
57811042SErik.Nordmark@Sun.COM case IPPROTO_UDP:
57911042SErik.Nordmark@Sun.COM /*
58011042SErik.Nordmark@Sun.COM * Verify we have at least ICMP_MIN_TP_HDR_LEN bytes of
58111042SErik.Nordmark@Sun.COM * transport header.
58211042SErik.Nordmark@Sun.COM */
58311042SErik.Nordmark@Sun.COM if ((uchar_t *)ip6h + hdr_length + ICMP_MIN_TP_HDR_LEN >
58411042SErik.Nordmark@Sun.COM mp->b_wptr)
58511042SErik.Nordmark@Sun.COM goto truncated;
58611042SErik.Nordmark@Sun.COM break;
58711042SErik.Nordmark@Sun.COM case IPPROTO_TCP: {
58811042SErik.Nordmark@Sun.COM tcpha_t *tcpha;
58911042SErik.Nordmark@Sun.COM
59011042SErik.Nordmark@Sun.COM /*
59111042SErik.Nordmark@Sun.COM * Verify we have at least ICMP_MIN_TP_HDR_LEN bytes of
59211042SErik.Nordmark@Sun.COM * transport header.
59311042SErik.Nordmark@Sun.COM */
59411042SErik.Nordmark@Sun.COM if ((uchar_t *)ip6h + hdr_length + ICMP_MIN_TP_HDR_LEN >
59511042SErik.Nordmark@Sun.COM mp->b_wptr)
59611042SErik.Nordmark@Sun.COM goto truncated;
59711042SErik.Nordmark@Sun.COM
59811042SErik.Nordmark@Sun.COM tcpha = (tcpha_t *)((uchar_t *)ip6h + hdr_length);
59911042SErik.Nordmark@Sun.COM /*
60011042SErik.Nordmark@Sun.COM * With IPMP we need to match across group, which we do
60111042SErik.Nordmark@Sun.COM * since we have the upper ill from ira_ill.
60211042SErik.Nordmark@Sun.COM */
60311042SErik.Nordmark@Sun.COM connp = ipcl_tcp_lookup_reversed_ipv6(ip6h, tcpha, TCPS_LISTEN,
60411042SErik.Nordmark@Sun.COM ill->ill_phyint->phyint_ifindex, ipst);
60511042SErik.Nordmark@Sun.COM if (connp == NULL)
60611042SErik.Nordmark@Sun.COM goto discard_pkt;
60711042SErik.Nordmark@Sun.COM
60811042SErik.Nordmark@Sun.COM if ((connp->conn_verifyicmp != NULL) &&
60911042SErik.Nordmark@Sun.COM !connp->conn_verifyicmp(connp, tcpha, NULL, icmp6, ira)) {
61011042SErik.Nordmark@Sun.COM CONN_DEC_REF(connp);
61111042SErik.Nordmark@Sun.COM goto discard_pkt;
61211042SErik.Nordmark@Sun.COM }
61311042SErik.Nordmark@Sun.COM CONN_DEC_REF(connp);
61411042SErik.Nordmark@Sun.COM break;
61511042SErik.Nordmark@Sun.COM }
61611042SErik.Nordmark@Sun.COM case IPPROTO_SCTP:
61711042SErik.Nordmark@Sun.COM /*
61811042SErik.Nordmark@Sun.COM * Verify we have at least ICMP_MIN_TP_HDR_LEN bytes of
61911042SErik.Nordmark@Sun.COM * transport header.
62011042SErik.Nordmark@Sun.COM */
62111042SErik.Nordmark@Sun.COM if ((uchar_t *)ip6h + hdr_length + ICMP_MIN_TP_HDR_LEN >
62211042SErik.Nordmark@Sun.COM mp->b_wptr)
62311042SErik.Nordmark@Sun.COM goto truncated;
62411042SErik.Nordmark@Sun.COM break;
62511042SErik.Nordmark@Sun.COM case IPPROTO_ESP:
62611042SErik.Nordmark@Sun.COM case IPPROTO_AH:
62711042SErik.Nordmark@Sun.COM break;
62811042SErik.Nordmark@Sun.COM case IPPROTO_ENCAP:
62911042SErik.Nordmark@Sun.COM case IPPROTO_IPV6: {
63011042SErik.Nordmark@Sun.COM /* Look for self-encapsulated packets that caused an error */
63111042SErik.Nordmark@Sun.COM ip6_t *in_ip6h;
63211042SErik.Nordmark@Sun.COM
63311042SErik.Nordmark@Sun.COM in_ip6h = (ip6_t *)((uint8_t *)ip6h + hdr_length);
63411042SErik.Nordmark@Sun.COM if ((uint8_t *)in_ip6h + (nexthdr == IPPROTO_ENCAP ?
63511042SErik.Nordmark@Sun.COM sizeof (ipha_t) : sizeof (ip6_t)) > mp->b_wptr)
63611042SErik.Nordmark@Sun.COM goto truncated;
63711042SErik.Nordmark@Sun.COM break;
63811042SErik.Nordmark@Sun.COM }
63911042SErik.Nordmark@Sun.COM default:
64011042SErik.Nordmark@Sun.COM break;
64111042SErik.Nordmark@Sun.COM }
64211042SErik.Nordmark@Sun.COM
64311042SErik.Nordmark@Sun.COM return (B_TRUE);
64411042SErik.Nordmark@Sun.COM
64511042SErik.Nordmark@Sun.COM discard_pkt:
64611042SErik.Nordmark@Sun.COM /* Bogus ICMP error. */
64711042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
64811042SErik.Nordmark@Sun.COM return (B_FALSE);
64911042SErik.Nordmark@Sun.COM
65011042SErik.Nordmark@Sun.COM truncated:
65111042SErik.Nordmark@Sun.COM /* We pulled up everthing already. Must be truncated */
65211042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
65311042SErik.Nordmark@Sun.COM return (B_FALSE);
65411042SErik.Nordmark@Sun.COM }
65511042SErik.Nordmark@Sun.COM
65611042SErik.Nordmark@Sun.COM /*
65711042SErik.Nordmark@Sun.COM * Process received IPv6 ICMP Packet too big.
65811042SErik.Nordmark@Sun.COM * The caller is responsible for validating the packet before passing it in
65911042SErik.Nordmark@Sun.COM * and also to fanout the ICMP error to any matching transport conns. Assumes
66011042SErik.Nordmark@Sun.COM * the message has been fully pulled up.
66111042SErik.Nordmark@Sun.COM *
66211042SErik.Nordmark@Sun.COM * Before getting here, the caller has called icmp_inbound_verify_v6()
66311042SErik.Nordmark@Sun.COM * that should have verified with ULP to prevent undoing the changes we're
66411042SErik.Nordmark@Sun.COM * going to make to DCE. For example, TCP might have verified that the packet
66511042SErik.Nordmark@Sun.COM * which generated error is in the send window.
66611042SErik.Nordmark@Sun.COM *
66711042SErik.Nordmark@Sun.COM * In some cases modified this MTU in the ICMP header packet; the caller
66811042SErik.Nordmark@Sun.COM * should pass to the matching ULP after this returns.
66911042SErik.Nordmark@Sun.COM */
67011042SErik.Nordmark@Sun.COM static void
icmp_inbound_too_big_v6(icmp6_t * icmp6,ip_recv_attr_t * ira)67111042SErik.Nordmark@Sun.COM icmp_inbound_too_big_v6(icmp6_t *icmp6, ip_recv_attr_t *ira)
67211042SErik.Nordmark@Sun.COM {
67311042SErik.Nordmark@Sun.COM uint32_t mtu;
67411042SErik.Nordmark@Sun.COM dce_t *dce;
67511042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; /* Upper ill if IPMP */
67611042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst;
67711042SErik.Nordmark@Sun.COM int old_max_frag;
67811042SErik.Nordmark@Sun.COM in6_addr_t final_dst;
67911042SErik.Nordmark@Sun.COM ip6_t *ip6h; /* Inner IP header */
68011042SErik.Nordmark@Sun.COM
68111042SErik.Nordmark@Sun.COM /* Caller has already pulled up everything. */
68211042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)&icmp6[1];
68311042SErik.Nordmark@Sun.COM final_dst = ip_get_dst_v6(ip6h, NULL, NULL);
68411042SErik.Nordmark@Sun.COM
68511042SErik.Nordmark@Sun.COM /*
68611042SErik.Nordmark@Sun.COM * For link local destinations matching simply on address is not
6870Sstevel@tonic-gate * sufficient. Same link local addresses for different ILL's is
6880Sstevel@tonic-gate * possible.
6890Sstevel@tonic-gate */
69011042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_LINKSCOPE(&final_dst)) {
69111042SErik.Nordmark@Sun.COM dce = dce_lookup_and_add_v6(&final_dst,
69211042SErik.Nordmark@Sun.COM ill->ill_phyint->phyint_ifindex, ipst);
69311042SErik.Nordmark@Sun.COM } else {
69411042SErik.Nordmark@Sun.COM dce = dce_lookup_and_add_v6(&final_dst, 0, ipst);
69511042SErik.Nordmark@Sun.COM }
69611042SErik.Nordmark@Sun.COM if (dce == NULL) {
69711042SErik.Nordmark@Sun.COM /* Couldn't add a unique one - ENOMEM */
69811042SErik.Nordmark@Sun.COM if (ip_debug > 2) {
69911042SErik.Nordmark@Sun.COM /* ip1dbg */
70011042SErik.Nordmark@Sun.COM pr_addr_dbg("icmp_inbound_too_big_v6:"
70111042SErik.Nordmark@Sun.COM "no dce for dst %s\n", AF_INET6,
70211042SErik.Nordmark@Sun.COM &final_dst);
7030Sstevel@tonic-gate }
70411042SErik.Nordmark@Sun.COM return;
70511042SErik.Nordmark@Sun.COM }
70611042SErik.Nordmark@Sun.COM
70711042SErik.Nordmark@Sun.COM mtu = ntohl(icmp6->icmp6_mtu);
70811042SErik.Nordmark@Sun.COM
70911042SErik.Nordmark@Sun.COM mutex_enter(&dce->dce_lock);
71011042SErik.Nordmark@Sun.COM if (dce->dce_flags & DCEF_PMTU)
71111042SErik.Nordmark@Sun.COM old_max_frag = dce->dce_pmtu;
712*13123SErik.Nordmark@Sun.COM else if (IN6_IS_ADDR_MULTICAST(&final_dst))
713*13123SErik.Nordmark@Sun.COM old_max_frag = ill->ill_mc_mtu;
71411042SErik.Nordmark@Sun.COM else
71511042SErik.Nordmark@Sun.COM old_max_frag = ill->ill_mtu;
71611042SErik.Nordmark@Sun.COM
71711042SErik.Nordmark@Sun.COM if (mtu < IPV6_MIN_MTU) {
71811042SErik.Nordmark@Sun.COM ip1dbg(("Received mtu less than IPv6 "
71911042SErik.Nordmark@Sun.COM "min mtu %d: %d\n", IPV6_MIN_MTU, mtu));
72011042SErik.Nordmark@Sun.COM mtu = IPV6_MIN_MTU;
7210Sstevel@tonic-gate /*
72211042SErik.Nordmark@Sun.COM * If an mtu less than IPv6 min mtu is received,
72311042SErik.Nordmark@Sun.COM * we must include a fragment header in
72411042SErik.Nordmark@Sun.COM * subsequent packets.
7250Sstevel@tonic-gate */
72611042SErik.Nordmark@Sun.COM dce->dce_flags |= DCEF_TOO_SMALL_PMTU;
72711042SErik.Nordmark@Sun.COM } else {
72811042SErik.Nordmark@Sun.COM dce->dce_flags &= ~DCEF_TOO_SMALL_PMTU;
7290Sstevel@tonic-gate }
73011042SErik.Nordmark@Sun.COM ip1dbg(("Received mtu from router: %d\n", mtu));
73111042SErik.Nordmark@Sun.COM dce->dce_pmtu = MIN(old_max_frag, mtu);
73211042SErik.Nordmark@Sun.COM
73311042SErik.Nordmark@Sun.COM /* Prepare to send the new max frag size for the ULP. */
73411042SErik.Nordmark@Sun.COM if (dce->dce_flags & DCEF_TOO_SMALL_PMTU) {
73511042SErik.Nordmark@Sun.COM /*
73611042SErik.Nordmark@Sun.COM * If we need a fragment header in every packet
73711042SErik.Nordmark@Sun.COM * (above case or multirouting), make sure the
73811042SErik.Nordmark@Sun.COM * ULP takes it into account when computing the
73911042SErik.Nordmark@Sun.COM * payload size.
74011042SErik.Nordmark@Sun.COM */
74111042SErik.Nordmark@Sun.COM icmp6->icmp6_mtu = htonl(dce->dce_pmtu - sizeof (ip6_frag_t));
74211042SErik.Nordmark@Sun.COM } else {
74311042SErik.Nordmark@Sun.COM icmp6->icmp6_mtu = htonl(dce->dce_pmtu);
74411042SErik.Nordmark@Sun.COM }
74511042SErik.Nordmark@Sun.COM /* We now have a PMTU for sure */
74611042SErik.Nordmark@Sun.COM dce->dce_flags |= DCEF_PMTU;
74711066Srafael.vanoni@sun.com dce->dce_last_change_time = TICK_TO_SEC(ddi_get_lbolt64());
74811042SErik.Nordmark@Sun.COM mutex_exit(&dce->dce_lock);
74911042SErik.Nordmark@Sun.COM /*
75011042SErik.Nordmark@Sun.COM * After dropping the lock the new value is visible to everyone.
75111042SErik.Nordmark@Sun.COM * Then we bump the generation number so any cached values reinspect
75211042SErik.Nordmark@Sun.COM * the dce_t.
75311042SErik.Nordmark@Sun.COM */
75411042SErik.Nordmark@Sun.COM dce_increment_generation(dce);
75511042SErik.Nordmark@Sun.COM dce_refrele(dce);
75610616SSebastien.Roy@Sun.COM }
75710616SSebastien.Roy@Sun.COM
75810616SSebastien.Roy@Sun.COM /*
7590Sstevel@tonic-gate * Fanout received ICMPv6 error packets to the transports.
7600Sstevel@tonic-gate * Assumes the IPv6 plus ICMPv6 headers have been pulled up but nothing else.
76111042SErik.Nordmark@Sun.COM *
76211042SErik.Nordmark@Sun.COM * The caller must have called icmp_inbound_verify_v6.
7630Sstevel@tonic-gate */
7640Sstevel@tonic-gate void
icmp_inbound_error_fanout_v6(mblk_t * mp,icmp6_t * icmp6,ip_recv_attr_t * ira)76511042SErik.Nordmark@Sun.COM icmp_inbound_error_fanout_v6(mblk_t *mp, icmp6_t *icmp6, ip_recv_attr_t *ira)
7660Sstevel@tonic-gate {
76711042SErik.Nordmark@Sun.COM uint16_t *up; /* Pointer to ports in ULP header */
76811042SErik.Nordmark@Sun.COM uint32_t ports; /* reversed ports for fanout */
76911042SErik.Nordmark@Sun.COM ip6_t rip6h; /* With reversed addresses */
77011042SErik.Nordmark@Sun.COM ip6_t *ip6h; /* Inner IP header */
77111042SErik.Nordmark@Sun.COM uint16_t hdr_length; /* Inner IP header length */
7720Sstevel@tonic-gate uint8_t *nexthdrp;
7730Sstevel@tonic-gate uint8_t nexthdr;
77411042SErik.Nordmark@Sun.COM tcpha_t *tcpha;
77511042SErik.Nordmark@Sun.COM conn_t *connp;
77611042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; /* Upper in the case of IPMP */
7773448Sdh155122 ip_stack_t *ipst = ill->ill_ipst;
77811042SErik.Nordmark@Sun.COM ipsec_stack_t *ipss = ipst->ips_netstack->netstack_ipsec;
77911042SErik.Nordmark@Sun.COM
78011042SErik.Nordmark@Sun.COM /* Caller has already pulled up everything. */
78111042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)&icmp6[1];
78211042SErik.Nordmark@Sun.COM ASSERT(mp->b_cont == NULL);
78311042SErik.Nordmark@Sun.COM ASSERT((uchar_t *)&ip6h[1] <= mp->b_wptr);
7840Sstevel@tonic-gate
7850Sstevel@tonic-gate if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp))
7860Sstevel@tonic-gate goto drop_pkt;
7870Sstevel@tonic-gate nexthdr = *nexthdrp;
78811042SErik.Nordmark@Sun.COM ira->ira_protocol = nexthdr;
7890Sstevel@tonic-gate
79010616SSebastien.Roy@Sun.COM /*
79110616SSebastien.Roy@Sun.COM * We need a separate IP header with the source and destination
79210616SSebastien.Roy@Sun.COM * addresses reversed to do fanout/classification because the ip6h in
79310616SSebastien.Roy@Sun.COM * the ICMPv6 error is in the form we sent it out.
79410616SSebastien.Roy@Sun.COM */
79510616SSebastien.Roy@Sun.COM rip6h.ip6_src = ip6h->ip6_dst;
79610616SSebastien.Roy@Sun.COM rip6h.ip6_dst = ip6h->ip6_src;
79710616SSebastien.Roy@Sun.COM rip6h.ip6_nxt = nexthdr;
79810616SSebastien.Roy@Sun.COM
7990Sstevel@tonic-gate /* Try to pass the ICMP message to clients who need it */
8000Sstevel@tonic-gate switch (nexthdr) {
8010Sstevel@tonic-gate case IPPROTO_UDP: {
80210616SSebastien.Roy@Sun.COM /* Attempt to find a client stream based on port. */
8030Sstevel@tonic-gate up = (uint16_t *)((uchar_t *)ip6h + hdr_length);
80411042SErik.Nordmark@Sun.COM
80511042SErik.Nordmark@Sun.COM /* Note that we send error to all matches. */
80611042SErik.Nordmark@Sun.COM ira->ira_flags |= IRAF_ICMP_ERROR;
80711042SErik.Nordmark@Sun.COM ip_fanout_udp_multi_v6(mp, &rip6h, up[0], up[1], ira);
80811042SErik.Nordmark@Sun.COM ira->ira_flags &= ~IRAF_ICMP_ERROR;
8090Sstevel@tonic-gate return;
8100Sstevel@tonic-gate }
8110Sstevel@tonic-gate case IPPROTO_TCP: {
8120Sstevel@tonic-gate /*
8130Sstevel@tonic-gate * Attempt to find a client stream based on port.
8140Sstevel@tonic-gate * Note that we do a reverse lookup since the header is
8150Sstevel@tonic-gate * in the form we sent it out.
81610616SSebastien.Roy@Sun.COM */
81711042SErik.Nordmark@Sun.COM tcpha = (tcpha_t *)((uchar_t *)ip6h + hdr_length);
81811042SErik.Nordmark@Sun.COM /*
81911042SErik.Nordmark@Sun.COM * With IPMP we need to match across group, which we do
82011042SErik.Nordmark@Sun.COM * since we have the upper ill from ira_ill.
82111042SErik.Nordmark@Sun.COM */
8220Sstevel@tonic-gate connp = ipcl_tcp_lookup_reversed_ipv6(ip6h, tcpha,
8233448Sdh155122 TCPS_LISTEN, ill->ill_phyint->phyint_ifindex, ipst);
8240Sstevel@tonic-gate if (connp == NULL) {
8250Sstevel@tonic-gate goto drop_pkt;
8260Sstevel@tonic-gate }
8270Sstevel@tonic-gate
82811042SErik.Nordmark@Sun.COM if (CONN_INBOUND_POLICY_PRESENT_V6(connp, ipss) ||
82911042SErik.Nordmark@Sun.COM (ira->ira_flags & IRAF_IPSEC_SECURE)) {
83011042SErik.Nordmark@Sun.COM mp = ipsec_check_inbound_policy(mp, connp,
83111042SErik.Nordmark@Sun.COM NULL, ip6h, ira);
83211042SErik.Nordmark@Sun.COM if (mp == NULL) {
83311042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
83411042SErik.Nordmark@Sun.COM /* Note that mp is NULL */
83511042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards", mp, ill);
83611042SErik.Nordmark@Sun.COM CONN_DEC_REF(connp);
83711042SErik.Nordmark@Sun.COM return;
83811042SErik.Nordmark@Sun.COM }
83911042SErik.Nordmark@Sun.COM }
84011042SErik.Nordmark@Sun.COM
84111042SErik.Nordmark@Sun.COM ira->ira_flags |= IRAF_ICMP_ERROR;
84211042SErik.Nordmark@Sun.COM if (IPCL_IS_TCP(connp)) {
84311042SErik.Nordmark@Sun.COM SQUEUE_ENTER_ONE(connp->conn_sqp, mp,
84411042SErik.Nordmark@Sun.COM connp->conn_recvicmp, connp, ira, SQ_FILL,
84511042SErik.Nordmark@Sun.COM SQTAG_TCP6_INPUT_ICMP_ERR);
84611042SErik.Nordmark@Sun.COM } else {
84711042SErik.Nordmark@Sun.COM /* Not TCP; must be SOCK_RAW, IPPROTO_TCP */
84811042SErik.Nordmark@Sun.COM ill_t *rill = ira->ira_rill;
84911042SErik.Nordmark@Sun.COM
85011042SErik.Nordmark@Sun.COM ira->ira_ill = ira->ira_rill = NULL;
85111042SErik.Nordmark@Sun.COM (connp->conn_recv)(connp, mp, NULL, ira);
85211042SErik.Nordmark@Sun.COM CONN_DEC_REF(connp);
85311042SErik.Nordmark@Sun.COM ira->ira_ill = ill;
85411042SErik.Nordmark@Sun.COM ira->ira_rill = rill;
85511042SErik.Nordmark@Sun.COM }
85611042SErik.Nordmark@Sun.COM ira->ira_flags &= ~IRAF_ICMP_ERROR;
8570Sstevel@tonic-gate return;
8580Sstevel@tonic-gate
8590Sstevel@tonic-gate }
8600Sstevel@tonic-gate case IPPROTO_SCTP:
8610Sstevel@tonic-gate up = (uint16_t *)((uchar_t *)ip6h + hdr_length);
86211042SErik.Nordmark@Sun.COM /* Find a SCTP client stream for this packet. */
8630Sstevel@tonic-gate ((uint16_t *)&ports)[0] = up[1];
8640Sstevel@tonic-gate ((uint16_t *)&ports)[1] = up[0];
86511042SErik.Nordmark@Sun.COM
86611042SErik.Nordmark@Sun.COM ira->ira_flags |= IRAF_ICMP_ERROR;
86711042SErik.Nordmark@Sun.COM ip_fanout_sctp(mp, NULL, &rip6h, ports, ira);
86811042SErik.Nordmark@Sun.COM ira->ira_flags &= ~IRAF_ICMP_ERROR;
8690Sstevel@tonic-gate return;
87011042SErik.Nordmark@Sun.COM
8710Sstevel@tonic-gate case IPPROTO_ESP:
87211042SErik.Nordmark@Sun.COM case IPPROTO_AH:
8733448Sdh155122 if (!ipsec_loaded(ipss)) {
87411042SErik.Nordmark@Sun.COM ip_proto_not_sup(mp, ira);
8750Sstevel@tonic-gate return;
8760Sstevel@tonic-gate }
8770Sstevel@tonic-gate
8780Sstevel@tonic-gate if (nexthdr == IPPROTO_ESP)
87911042SErik.Nordmark@Sun.COM mp = ipsecesp_icmp_error(mp, ira);
8800Sstevel@tonic-gate else
88111042SErik.Nordmark@Sun.COM mp = ipsecah_icmp_error(mp, ira);
88211042SErik.Nordmark@Sun.COM if (mp == NULL)
8830Sstevel@tonic-gate return;
8840Sstevel@tonic-gate
88511042SErik.Nordmark@Sun.COM /* Just in case ipsec didn't preserve the NULL b_cont */
88611042SErik.Nordmark@Sun.COM if (mp->b_cont != NULL) {
88711042SErik.Nordmark@Sun.COM if (!pullupmsg(mp, -1))
88811042SErik.Nordmark@Sun.COM goto drop_pkt;
8894459Skcpoon }
8900Sstevel@tonic-gate
89111042SErik.Nordmark@Sun.COM /*
89211042SErik.Nordmark@Sun.COM * If succesful, the mp has been modified to not include
89311042SErik.Nordmark@Sun.COM * the ESP/AH header so we can fanout to the ULP's icmp
89411042SErik.Nordmark@Sun.COM * error handler.
89511042SErik.Nordmark@Sun.COM */
89611042SErik.Nordmark@Sun.COM if (mp->b_wptr - mp->b_rptr < IPV6_HDR_LEN)
89711042SErik.Nordmark@Sun.COM goto drop_pkt;
89811042SErik.Nordmark@Sun.COM
89911042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr;
90011042SErik.Nordmark@Sun.COM /* Don't call hdr_length_v6() unless you have to. */
90111042SErik.Nordmark@Sun.COM if (ip6h->ip6_nxt != IPPROTO_ICMPV6)
90211042SErik.Nordmark@Sun.COM hdr_length = ip_hdr_length_v6(mp, ip6h);
90311042SErik.Nordmark@Sun.COM else
90411042SErik.Nordmark@Sun.COM hdr_length = IPV6_HDR_LEN;
90511042SErik.Nordmark@Sun.COM
90611042SErik.Nordmark@Sun.COM /* Verify the modified message before any further processes. */
90711042SErik.Nordmark@Sun.COM icmp6 = (icmp6_t *)(&mp->b_rptr[hdr_length]);
90811042SErik.Nordmark@Sun.COM if (!icmp_inbound_verify_v6(mp, icmp6, ira)) {
90911042SErik.Nordmark@Sun.COM freemsg(mp);
91011042SErik.Nordmark@Sun.COM return;
91111042SErik.Nordmark@Sun.COM }
91211042SErik.Nordmark@Sun.COM
91311042SErik.Nordmark@Sun.COM icmp_inbound_error_fanout_v6(mp, icmp6, ira);
91411042SErik.Nordmark@Sun.COM return;
91511042SErik.Nordmark@Sun.COM
91611042SErik.Nordmark@Sun.COM case IPPROTO_IPV6: {
91711042SErik.Nordmark@Sun.COM /* Look for self-encapsulated packets that caused an error */
91811042SErik.Nordmark@Sun.COM ip6_t *in_ip6h;
91911042SErik.Nordmark@Sun.COM
92011042SErik.Nordmark@Sun.COM in_ip6h = (ip6_t *)((uint8_t *)ip6h + hdr_length);
92111042SErik.Nordmark@Sun.COM
92211042SErik.Nordmark@Sun.COM if (IN6_ARE_ADDR_EQUAL(&in_ip6h->ip6_src, &ip6h->ip6_src) &&
92311042SErik.Nordmark@Sun.COM IN6_ARE_ADDR_EQUAL(&in_ip6h->ip6_dst, &ip6h->ip6_dst)) {
9240Sstevel@tonic-gate /*
9250Sstevel@tonic-gate * Self-encapsulated case. As in the ipv4 case,
9260Sstevel@tonic-gate * we need to strip the 2nd IP header. Since mp
9270Sstevel@tonic-gate * is already pulled-up, we can simply bcopy
9280Sstevel@tonic-gate * the 3rd header + data over the 2nd header.
9290Sstevel@tonic-gate */
9300Sstevel@tonic-gate uint16_t unused_len;
9310Sstevel@tonic-gate
9320Sstevel@tonic-gate /*
9330Sstevel@tonic-gate * Make sure we don't do recursion more than once.
9340Sstevel@tonic-gate */
93511042SErik.Nordmark@Sun.COM if (!ip_hdr_length_nexthdr_v6(mp, in_ip6h,
9360Sstevel@tonic-gate &unused_len, &nexthdrp) ||
9370Sstevel@tonic-gate *nexthdrp == IPPROTO_IPV6) {
9380Sstevel@tonic-gate goto drop_pkt;
9390Sstevel@tonic-gate }
9400Sstevel@tonic-gate
9410Sstevel@tonic-gate /*
9420Sstevel@tonic-gate * Copy the 3rd header + remaining data on top
9430Sstevel@tonic-gate * of the 2nd header.
9440Sstevel@tonic-gate */
94511042SErik.Nordmark@Sun.COM bcopy(in_ip6h, ip6h, mp->b_wptr - (uchar_t *)in_ip6h);
9460Sstevel@tonic-gate
9470Sstevel@tonic-gate /*
9480Sstevel@tonic-gate * Subtract length of the 2nd header.
9490Sstevel@tonic-gate */
9500Sstevel@tonic-gate mp->b_wptr -= hdr_length;
9510Sstevel@tonic-gate
95211042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr;
95311042SErik.Nordmark@Sun.COM /* Don't call hdr_length_v6() unless you have to. */
95411042SErik.Nordmark@Sun.COM if (ip6h->ip6_nxt != IPPROTO_ICMPV6)
95511042SErik.Nordmark@Sun.COM hdr_length = ip_hdr_length_v6(mp, ip6h);
95611042SErik.Nordmark@Sun.COM else
95711042SErik.Nordmark@Sun.COM hdr_length = IPV6_HDR_LEN;
95811042SErik.Nordmark@Sun.COM
95911042SErik.Nordmark@Sun.COM /*
96011042SErik.Nordmark@Sun.COM * Verify the modified message before any further
96111042SErik.Nordmark@Sun.COM * processes.
96211042SErik.Nordmark@Sun.COM */
96311042SErik.Nordmark@Sun.COM icmp6 = (icmp6_t *)(&mp->b_rptr[hdr_length]);
96411042SErik.Nordmark@Sun.COM if (!icmp_inbound_verify_v6(mp, icmp6, ira)) {
96511042SErik.Nordmark@Sun.COM freemsg(mp);
96611042SErik.Nordmark@Sun.COM return;
96711042SErik.Nordmark@Sun.COM }
96811042SErik.Nordmark@Sun.COM
9690Sstevel@tonic-gate /*
9700Sstevel@tonic-gate * Now recurse, and see what I _really_ should be
9710Sstevel@tonic-gate * doing here.
9720Sstevel@tonic-gate */
97311042SErik.Nordmark@Sun.COM icmp_inbound_error_fanout_v6(mp, icmp6, ira);
9740Sstevel@tonic-gate return;
9750Sstevel@tonic-gate }
97611042SErik.Nordmark@Sun.COM /* FALLTHRU */
97711042SErik.Nordmark@Sun.COM }
97811042SErik.Nordmark@Sun.COM case IPPROTO_ENCAP:
97911042SErik.Nordmark@Sun.COM if ((connp = ipcl_iptun_classify_v6(&rip6h.ip6_src,
98011042SErik.Nordmark@Sun.COM &rip6h.ip6_dst, ipst)) != NULL) {
98111042SErik.Nordmark@Sun.COM ira->ira_flags |= IRAF_ICMP_ERROR;
98211042SErik.Nordmark@Sun.COM connp->conn_recvicmp(connp, mp, NULL, ira);
98311042SErik.Nordmark@Sun.COM CONN_DEC_REF(connp);
98411042SErik.Nordmark@Sun.COM ira->ira_flags &= ~IRAF_ICMP_ERROR;
98510616SSebastien.Roy@Sun.COM return;
98611042SErik.Nordmark@Sun.COM }
98710616SSebastien.Roy@Sun.COM /*
98811042SErik.Nordmark@Sun.COM * No IP tunnel is interested, fallthrough and see
98911042SErik.Nordmark@Sun.COM * if a raw socket will want it.
99010616SSebastien.Roy@Sun.COM */
9910Sstevel@tonic-gate /* FALLTHRU */
9920Sstevel@tonic-gate default:
99311042SErik.Nordmark@Sun.COM ira->ira_flags |= IRAF_ICMP_ERROR;
99411042SErik.Nordmark@Sun.COM ASSERT(ira->ira_protocol == nexthdr);
99511042SErik.Nordmark@Sun.COM ip_fanout_proto_v6(mp, &rip6h, ira);
99611042SErik.Nordmark@Sun.COM ira->ira_flags &= ~IRAF_ICMP_ERROR;
9970Sstevel@tonic-gate return;
9980Sstevel@tonic-gate }
9990Sstevel@tonic-gate /* NOTREACHED */
10000Sstevel@tonic-gate drop_pkt:
10010Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
10020Sstevel@tonic-gate ip1dbg(("icmp_inbound_error_fanout_v6: drop pkt\n"));
100311042SErik.Nordmark@Sun.COM freemsg(mp);
10040Sstevel@tonic-gate }
10050Sstevel@tonic-gate
10060Sstevel@tonic-gate /*
10070Sstevel@tonic-gate * Process received IPv6 ICMP Redirect messages.
100811042SErik.Nordmark@Sun.COM * Assumes the caller has verified that the headers are in the pulled up mblk.
100911042SErik.Nordmark@Sun.COM * Consumes mp.
10100Sstevel@tonic-gate */
10110Sstevel@tonic-gate /* ARGSUSED */
10120Sstevel@tonic-gate static void
icmp_redirect_v6(mblk_t * mp,ip6_t * ip6h,nd_redirect_t * rd,ip_recv_attr_t * ira)101311042SErik.Nordmark@Sun.COM icmp_redirect_v6(mblk_t *mp, ip6_t *ip6h, nd_redirect_t *rd,
101411042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira)
10150Sstevel@tonic-gate {
101611042SErik.Nordmark@Sun.COM ire_t *ire, *nire;
101711042SErik.Nordmark@Sun.COM ire_t *prev_ire = NULL;
10180Sstevel@tonic-gate ire_t *redir_ire;
10190Sstevel@tonic-gate in6_addr_t *src, *dst, *gateway;
10200Sstevel@tonic-gate nd_opt_hdr_t *opt;
10210Sstevel@tonic-gate nce_t *nce;
102211042SErik.Nordmark@Sun.COM int ncec_flags = 0;
10230Sstevel@tonic-gate int err = 0;
10240Sstevel@tonic-gate boolean_t redirect_to_router = B_FALSE;
10250Sstevel@tonic-gate int len;
10263004Sdd193516 int optlen;
102711042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_rill;
102811042SErik.Nordmark@Sun.COM ill_t *rill = ira->ira_rill;
10293448Sdh155122 ip_stack_t *ipst = ill->ill_ipst;
10300Sstevel@tonic-gate
103111042SErik.Nordmark@Sun.COM /*
103211042SErik.Nordmark@Sun.COM * Since ira_ill is where the IRE_LOCAL was hosted we use ira_rill
103311042SErik.Nordmark@Sun.COM * and make it be the IPMP upper so avoid being confused by a packet
103411042SErik.Nordmark@Sun.COM * addressed to a unicast address on a different ill.
103511042SErik.Nordmark@Sun.COM */
103611042SErik.Nordmark@Sun.COM if (IS_UNDER_IPMP(rill)) {
103711042SErik.Nordmark@Sun.COM rill = ipmp_ill_hold_ipmp_ill(rill);
103811042SErik.Nordmark@Sun.COM if (rill == NULL) {
103911042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects);
104011042SErik.Nordmark@Sun.COM ip_drop_input("ipv6IfIcmpInBadRedirects - IPMP ill",
104111042SErik.Nordmark@Sun.COM mp, ill);
104211042SErik.Nordmark@Sun.COM freemsg(mp);
104311042SErik.Nordmark@Sun.COM return;
104411042SErik.Nordmark@Sun.COM }
104511042SErik.Nordmark@Sun.COM ASSERT(rill != ira->ira_rill);
104611042SErik.Nordmark@Sun.COM }
104711042SErik.Nordmark@Sun.COM
104811042SErik.Nordmark@Sun.COM len = mp->b_wptr - (uchar_t *)rd;
10490Sstevel@tonic-gate src = &ip6h->ip6_src;
10500Sstevel@tonic-gate dst = &rd->nd_rd_dst;
10510Sstevel@tonic-gate gateway = &rd->nd_rd_target;
10523004Sdd193516
10533004Sdd193516 /* Verify if it is a valid redirect */
10543004Sdd193516 if (!IN6_IS_ADDR_LINKLOCAL(src) ||
10553004Sdd193516 (ip6h->ip6_hops != IPV6_MAX_HOPS) ||
10563004Sdd193516 (rd->nd_rd_code != 0) ||
10573004Sdd193516 (len < sizeof (nd_redirect_t)) ||
10583004Sdd193516 (IN6_IS_ADDR_V4MAPPED(dst)) ||
10593004Sdd193516 (IN6_IS_ADDR_MULTICAST(dst))) {
10603004Sdd193516 BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects);
106111042SErik.Nordmark@Sun.COM ip_drop_input("ipv6IfIcmpInBadRedirects - addr/len", mp, ill);
106211042SErik.Nordmark@Sun.COM goto fail_redirect;
10633004Sdd193516 }
10643004Sdd193516
10653004Sdd193516 if (!(IN6_IS_ADDR_LINKLOCAL(gateway) ||
10663004Sdd193516 IN6_ARE_ADDR_EQUAL(gateway, dst))) {
10673004Sdd193516 BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects);
106811042SErik.Nordmark@Sun.COM ip_drop_input("ipv6IfIcmpInBadRedirects - bad gateway",
106911042SErik.Nordmark@Sun.COM mp, ill);
107011042SErik.Nordmark@Sun.COM goto fail_redirect;
10713004Sdd193516 }
10723004Sdd193516
107311042SErik.Nordmark@Sun.COM optlen = len - sizeof (nd_redirect_t);
107411042SErik.Nordmark@Sun.COM if (optlen != 0) {
107511042SErik.Nordmark@Sun.COM if (!ndp_verify_optlen((nd_opt_hdr_t *)&rd[1], optlen)) {
10763004Sdd193516 BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects);
107711042SErik.Nordmark@Sun.COM ip_drop_input("ipv6IfIcmpInBadRedirects - options",
107811042SErik.Nordmark@Sun.COM mp, ill);
107911042SErik.Nordmark@Sun.COM goto fail_redirect;
10803004Sdd193516 }
10813004Sdd193516 }
10823004Sdd193516
10830Sstevel@tonic-gate if (!IN6_ARE_ADDR_EQUAL(gateway, dst)) {
10840Sstevel@tonic-gate redirect_to_router = B_TRUE;
108511042SErik.Nordmark@Sun.COM ncec_flags |= NCE_F_ISROUTER;
108611042SErik.Nordmark@Sun.COM } else {
108711042SErik.Nordmark@Sun.COM gateway = dst; /* Add nce for dst */
10880Sstevel@tonic-gate }
10893004Sdd193516
10903004Sdd193516
10913004Sdd193516 /*
10923004Sdd193516 * Verify that the IP source address of the redirect is
10933004Sdd193516 * the same as the current first-hop router for the specified
10943004Sdd193516 * ICMP destination address.
10953004Sdd193516 * Also, Make sure we had a route for the dest in question and
10963004Sdd193516 * that route was pointing to the old gateway (the source of the
10973004Sdd193516 * redirect packet.)
109811362SErik.Nordmark@Sun.COM * We do longest match and then compare ire_gateway_addr_v6 below.
10993004Sdd193516 */
110011362SErik.Nordmark@Sun.COM prev_ire = ire_ftable_lookup_v6(dst, 0, 0, 0, rill,
110111362SErik.Nordmark@Sun.COM ALL_ZONES, NULL, MATCH_IRE_ILL, 0, ipst, NULL);
11023004Sdd193516
11030Sstevel@tonic-gate /*
11040Sstevel@tonic-gate * Check that
11050Sstevel@tonic-gate * the redirect was not from ourselves
11060Sstevel@tonic-gate * old gateway is still directly reachable
11070Sstevel@tonic-gate */
11080Sstevel@tonic-gate if (prev_ire == NULL ||
110911042SErik.Nordmark@Sun.COM (prev_ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK)) ||
111011362SErik.Nordmark@Sun.COM (prev_ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) ||
111111362SErik.Nordmark@Sun.COM !IN6_ARE_ADDR_EQUAL(src, &prev_ire->ire_gateway_addr_v6)) {
11120Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects);
111311042SErik.Nordmark@Sun.COM ip_drop_input("ipv6IfIcmpInBadRedirects - ire", mp, ill);
11140Sstevel@tonic-gate goto fail_redirect;
11150Sstevel@tonic-gate }
111611042SErik.Nordmark@Sun.COM
111711042SErik.Nordmark@Sun.COM ASSERT(prev_ire->ire_ill != NULL);
111811042SErik.Nordmark@Sun.COM if (prev_ire->ire_ill->ill_flags & ILLF_NONUD)
111911042SErik.Nordmark@Sun.COM ncec_flags |= NCE_F_NONUD;
112011042SErik.Nordmark@Sun.COM
11210Sstevel@tonic-gate opt = (nd_opt_hdr_t *)&rd[1];
11223004Sdd193516 opt = ndp_get_option(opt, optlen, ND_OPT_TARGET_LINKADDR);
11230Sstevel@tonic-gate if (opt != NULL) {
112411042SErik.Nordmark@Sun.COM err = nce_lookup_then_add_v6(rill,
11250Sstevel@tonic-gate (uchar_t *)&opt[1], /* Link layer address */
112611042SErik.Nordmark@Sun.COM rill->ill_phys_addr_length,
112711042SErik.Nordmark@Sun.COM gateway, ncec_flags, ND_STALE, &nce);
11280Sstevel@tonic-gate switch (err) {
11290Sstevel@tonic-gate case 0:
113011042SErik.Nordmark@Sun.COM nce_refrele(nce);
11310Sstevel@tonic-gate break;
11320Sstevel@tonic-gate case EEXIST:
11330Sstevel@tonic-gate /*
11340Sstevel@tonic-gate * Check to see if link layer address has changed and
113511042SErik.Nordmark@Sun.COM * process the ncec_state accordingly.
11360Sstevel@tonic-gate */
113711042SErik.Nordmark@Sun.COM nce_process(nce->nce_common,
113811042SErik.Nordmark@Sun.COM (uchar_t *)&opt[1], 0, B_FALSE);
113911042SErik.Nordmark@Sun.COM nce_refrele(nce);
11400Sstevel@tonic-gate break;
11410Sstevel@tonic-gate default:
11420Sstevel@tonic-gate ip1dbg(("icmp_redirect_v6: NCE create failed %d\n",
11430Sstevel@tonic-gate err));
11440Sstevel@tonic-gate goto fail_redirect;
11450Sstevel@tonic-gate }
11460Sstevel@tonic-gate }
11470Sstevel@tonic-gate if (redirect_to_router) {
11480Sstevel@tonic-gate ASSERT(IN6_IS_ADDR_LINKLOCAL(gateway));
11490Sstevel@tonic-gate
11500Sstevel@tonic-gate /*
11510Sstevel@tonic-gate * Create a Route Association. This will allow us to remember
11520Sstevel@tonic-gate * a router told us to use the particular gateway.
11530Sstevel@tonic-gate */
11540Sstevel@tonic-gate ire = ire_create_v6(
11550Sstevel@tonic-gate dst,
11560Sstevel@tonic-gate &ipv6_all_ones, /* mask */
11570Sstevel@tonic-gate gateway, /* gateway addr */
11583004Sdd193516 IRE_HOST,
115911042SErik.Nordmark@Sun.COM prev_ire->ire_ill,
116011042SErik.Nordmark@Sun.COM ALL_ZONES,
11610Sstevel@tonic-gate (RTF_DYNAMIC | RTF_GATEWAY | RTF_HOST),
11623448Sdh155122 NULL,
11633448Sdh155122 ipst);
11640Sstevel@tonic-gate } else {
116511042SErik.Nordmark@Sun.COM ipif_t *ipif;
116611042SErik.Nordmark@Sun.COM in6_addr_t gw;
11673004Sdd193516
11683004Sdd193516 /*
11693004Sdd193516 * Just create an on link entry, i.e. interface route.
117011042SErik.Nordmark@Sun.COM * The gateway field is our link-local on the ill.
11710Sstevel@tonic-gate */
117211042SErik.Nordmark@Sun.COM mutex_enter(&rill->ill_lock);
117311042SErik.Nordmark@Sun.COM for (ipif = rill->ill_ipif; ipif != NULL;
117411042SErik.Nordmark@Sun.COM ipif = ipif->ipif_next) {
117511042SErik.Nordmark@Sun.COM if (!(ipif->ipif_state_flags & IPIF_CONDEMNED) &&
117611042SErik.Nordmark@Sun.COM IN6_IS_ADDR_LINKLOCAL(&ipif->ipif_v6lcl_addr))
117711042SErik.Nordmark@Sun.COM break;
117811042SErik.Nordmark@Sun.COM }
117911042SErik.Nordmark@Sun.COM if (ipif == NULL) {
118011042SErik.Nordmark@Sun.COM /* We have no link-local address! */
118111042SErik.Nordmark@Sun.COM mutex_exit(&rill->ill_lock);
118211042SErik.Nordmark@Sun.COM goto fail_redirect;
118311042SErik.Nordmark@Sun.COM }
118411042SErik.Nordmark@Sun.COM gw = ipif->ipif_v6lcl_addr;
118511042SErik.Nordmark@Sun.COM mutex_exit(&rill->ill_lock);
118611042SErik.Nordmark@Sun.COM
11870Sstevel@tonic-gate ire = ire_create_v6(
11880Sstevel@tonic-gate dst, /* gateway == dst */
11890Sstevel@tonic-gate &ipv6_all_ones, /* mask */
119011042SErik.Nordmark@Sun.COM &gw, /* gateway addr */
119111042SErik.Nordmark@Sun.COM rill->ill_net_type, /* IF_[NO]RESOLVER */
119211042SErik.Nordmark@Sun.COM prev_ire->ire_ill,
119311042SErik.Nordmark@Sun.COM ALL_ZONES,
11943004Sdd193516 (RTF_DYNAMIC | RTF_HOST),
11953448Sdh155122 NULL,
11963448Sdh155122 ipst);
11970Sstevel@tonic-gate }
11983004Sdd193516
11990Sstevel@tonic-gate if (ire == NULL)
12000Sstevel@tonic-gate goto fail_redirect;
12010Sstevel@tonic-gate
120211042SErik.Nordmark@Sun.COM nire = ire_add(ire);
120311042SErik.Nordmark@Sun.COM /* Check if it was a duplicate entry */
120411042SErik.Nordmark@Sun.COM if (nire != NULL && nire != ire) {
120511042SErik.Nordmark@Sun.COM ASSERT(nire->ire_identical_ref > 1);
120611042SErik.Nordmark@Sun.COM ire_delete(nire);
120711042SErik.Nordmark@Sun.COM ire_refrele(nire);
120811042SErik.Nordmark@Sun.COM nire = NULL;
120911042SErik.Nordmark@Sun.COM }
121011042SErik.Nordmark@Sun.COM ire = nire;
121111042SErik.Nordmark@Sun.COM if (ire != NULL) {
121211042SErik.Nordmark@Sun.COM ire_refrele(ire); /* Held in ire_add */
12130Sstevel@tonic-gate
12140Sstevel@tonic-gate /* tell routing sockets that we received a redirect */
12150Sstevel@tonic-gate ip_rts_change_v6(RTM_REDIRECT,
12160Sstevel@tonic-gate &rd->nd_rd_dst,
12170Sstevel@tonic-gate &rd->nd_rd_target,
121811042SErik.Nordmark@Sun.COM &ipv6_all_ones, 0, src,
12190Sstevel@tonic-gate (RTF_DYNAMIC | RTF_GATEWAY | RTF_HOST), 0,
12203448Sdh155122 (RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_AUTHOR), ipst);
12210Sstevel@tonic-gate
12220Sstevel@tonic-gate /*
12233004Sdd193516 * Delete any existing IRE_HOST type ires for this destination.
12240Sstevel@tonic-gate * This together with the added IRE has the effect of
12250Sstevel@tonic-gate * modifying an existing redirect.
12260Sstevel@tonic-gate */
12273004Sdd193516 redir_ire = ire_ftable_lookup_v6(dst, 0, src, IRE_HOST,
122811042SErik.Nordmark@Sun.COM prev_ire->ire_ill, ALL_ZONES, NULL,
122911042SErik.Nordmark@Sun.COM (MATCH_IRE_GW | MATCH_IRE_TYPE | MATCH_IRE_ILL), 0, ipst,
123011042SErik.Nordmark@Sun.COM NULL);
12310Sstevel@tonic-gate
12320Sstevel@tonic-gate if (redir_ire != NULL) {
12333004Sdd193516 if (redir_ire->ire_flags & RTF_DYNAMIC)
12343004Sdd193516 ire_delete(redir_ire);
12350Sstevel@tonic-gate ire_refrele(redir_ire);
12360Sstevel@tonic-gate }
12370Sstevel@tonic-gate }
12380Sstevel@tonic-gate
12390Sstevel@tonic-gate ire_refrele(prev_ire);
12400Sstevel@tonic-gate prev_ire = NULL;
12410Sstevel@tonic-gate
12420Sstevel@tonic-gate fail_redirect:
12430Sstevel@tonic-gate if (prev_ire != NULL)
12440Sstevel@tonic-gate ire_refrele(prev_ire);
12450Sstevel@tonic-gate freemsg(mp);
124611042SErik.Nordmark@Sun.COM if (rill != ira->ira_rill)
124711042SErik.Nordmark@Sun.COM ill_refrele(rill);
12480Sstevel@tonic-gate }
12490Sstevel@tonic-gate
12500Sstevel@tonic-gate /*
12510Sstevel@tonic-gate * Build and ship an IPv6 ICMP message using the packet data in mp,
12520Sstevel@tonic-gate * and the ICMP header pointed to by "stuff". (May be called as
12530Sstevel@tonic-gate * writer.)
12540Sstevel@tonic-gate * Note: assumes that icmp_pkt_err_ok_v6 has been called to
12550Sstevel@tonic-gate * verify that an icmp error packet can be sent.
12560Sstevel@tonic-gate *
12570Sstevel@tonic-gate * If v6src_ptr is set use it as a source. Otherwise select a reasonable
12580Sstevel@tonic-gate * source address (see above function).
12590Sstevel@tonic-gate */
12600Sstevel@tonic-gate static void
icmp_pkt_v6(mblk_t * mp,void * stuff,size_t len,const in6_addr_t * v6src_ptr,ip_recv_attr_t * ira)126111042SErik.Nordmark@Sun.COM icmp_pkt_v6(mblk_t *mp, void *stuff, size_t len,
126211042SErik.Nordmark@Sun.COM const in6_addr_t *v6src_ptr, ip_recv_attr_t *ira)
12630Sstevel@tonic-gate {
12640Sstevel@tonic-gate ip6_t *ip6h;
12650Sstevel@tonic-gate in6_addr_t v6dst;
12660Sstevel@tonic-gate size_t len_needed;
12670Sstevel@tonic-gate size_t msg_len;
12680Sstevel@tonic-gate mblk_t *mp1;
12690Sstevel@tonic-gate icmp6_t *icmp6;
12700Sstevel@tonic-gate in6_addr_t v6src;
127111042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill;
127211042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst;
127311042SErik.Nordmark@Sun.COM ip_xmit_attr_t ixas;
127411042SErik.Nordmark@Sun.COM
127511042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr;
127611042SErik.Nordmark@Sun.COM
127711042SErik.Nordmark@Sun.COM bzero(&ixas, sizeof (ixas));
127811042SErik.Nordmark@Sun.COM ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6;
127911042SErik.Nordmark@Sun.COM ixas.ixa_zoneid = ira->ira_zoneid;
128011042SErik.Nordmark@Sun.COM ixas.ixa_ifindex = 0;
128111042SErik.Nordmark@Sun.COM ixas.ixa_ipst = ipst;
128211042SErik.Nordmark@Sun.COM ixas.ixa_cred = kcred;
128311042SErik.Nordmark@Sun.COM ixas.ixa_cpid = NOPID;
128411042SErik.Nordmark@Sun.COM ixas.ixa_tsl = ira->ira_tsl; /* Behave as a multi-level responder */
128511042SErik.Nordmark@Sun.COM ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
128611042SErik.Nordmark@Sun.COM
128711042SErik.Nordmark@Sun.COM /*
128811042SErik.Nordmark@Sun.COM * If the source of the original packet was link-local, then
128911042SErik.Nordmark@Sun.COM * make sure we send on the same ill (group) as we received it on.
129011042SErik.Nordmark@Sun.COM */
129111042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_src)) {
129211042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_SCOPEID_SET;
129311042SErik.Nordmark@Sun.COM if (IS_UNDER_IPMP(ill))
129411042SErik.Nordmark@Sun.COM ixas.ixa_scopeid = ill_get_upper_ifindex(ill);
129511042SErik.Nordmark@Sun.COM else
129611042SErik.Nordmark@Sun.COM ixas.ixa_scopeid = ill->ill_phyint->phyint_ifindex;
12970Sstevel@tonic-gate }
12980Sstevel@tonic-gate
129911042SErik.Nordmark@Sun.COM if (ira->ira_flags & IRAF_IPSEC_SECURE) {
13000Sstevel@tonic-gate /*
130111042SErik.Nordmark@Sun.COM * Apply IPsec based on how IPsec was applied to
130211042SErik.Nordmark@Sun.COM * the packet that had the error.
13030Sstevel@tonic-gate *
130411042SErik.Nordmark@Sun.COM * If it was an outbound packet that caused the ICMP
130511042SErik.Nordmark@Sun.COM * error, then the caller will have setup the IRA
130611042SErik.Nordmark@Sun.COM * appropriately.
13070Sstevel@tonic-gate */
130811042SErik.Nordmark@Sun.COM if (!ipsec_in_to_out(ira, &ixas, mp, NULL, ip6h)) {
130911042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards);
131011042SErik.Nordmark@Sun.COM /* Note: mp already consumed and ip_drop_packet done */
131111042SErik.Nordmark@Sun.COM return;
13120Sstevel@tonic-gate }
13130Sstevel@tonic-gate } else {
13140Sstevel@tonic-gate /*
13150Sstevel@tonic-gate * This is in clear. The icmp message we are building
131611042SErik.Nordmark@Sun.COM * here should go out in clear, independent of our policy.
13170Sstevel@tonic-gate */
131811042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_NO_IPSEC;
13190Sstevel@tonic-gate }
132011042SErik.Nordmark@Sun.COM
132111042SErik.Nordmark@Sun.COM /*
132211042SErik.Nordmark@Sun.COM * If the caller specified the source we use that.
132311042SErik.Nordmark@Sun.COM * Otherwise, if the packet was for one of our unicast addresses, make
132411042SErik.Nordmark@Sun.COM * sure we respond with that as the source. Otherwise
132511042SErik.Nordmark@Sun.COM * have ip_output_simple pick the source address.
132611042SErik.Nordmark@Sun.COM */
13270Sstevel@tonic-gate if (v6src_ptr != NULL) {
13280Sstevel@tonic-gate v6src = *v6src_ptr;
13290Sstevel@tonic-gate } else {
133011042SErik.Nordmark@Sun.COM ire_t *ire;
133111042SErik.Nordmark@Sun.COM uint_t match_flags = MATCH_IRE_TYPE | MATCH_IRE_ZONEONLY;
133211042SErik.Nordmark@Sun.COM
133311042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src) ||
133411042SErik.Nordmark@Sun.COM IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_dst))
133511042SErik.Nordmark@Sun.COM match_flags |= MATCH_IRE_ILL;
133611042SErik.Nordmark@Sun.COM
133711042SErik.Nordmark@Sun.COM ire = ire_ftable_lookup_v6(&ip6h->ip6_dst, 0, 0,
133811042SErik.Nordmark@Sun.COM (IRE_LOCAL|IRE_LOOPBACK), ill, ira->ira_zoneid, NULL,
133911042SErik.Nordmark@Sun.COM match_flags, 0, ipst, NULL);
134011042SErik.Nordmark@Sun.COM if (ire != NULL) {
134111042SErik.Nordmark@Sun.COM v6src = ip6h->ip6_dst;
134211042SErik.Nordmark@Sun.COM ire_refrele(ire);
134311042SErik.Nordmark@Sun.COM } else {
134411042SErik.Nordmark@Sun.COM v6src = ipv6_all_zeros;
134511042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_SET_SOURCE;
13460Sstevel@tonic-gate }
13470Sstevel@tonic-gate }
13480Sstevel@tonic-gate v6dst = ip6h->ip6_src;
13493448Sdh155122 len_needed = ipst->ips_ipv6_icmp_return - IPV6_HDR_LEN - len;
13500Sstevel@tonic-gate msg_len = msgdsize(mp);
13510Sstevel@tonic-gate if (msg_len > len_needed) {
13520Sstevel@tonic-gate if (!adjmsg(mp, len_needed - msg_len)) {
13530Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutErrors);
135411042SErik.Nordmark@Sun.COM freemsg(mp);
13550Sstevel@tonic-gate return;
13560Sstevel@tonic-gate }
13570Sstevel@tonic-gate msg_len = len_needed;
13580Sstevel@tonic-gate }
135911042SErik.Nordmark@Sun.COM mp1 = allocb(IPV6_HDR_LEN + len, BPRI_MED);
13600Sstevel@tonic-gate if (mp1 == NULL) {
13610Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutErrors);
136211042SErik.Nordmark@Sun.COM freemsg(mp);
13630Sstevel@tonic-gate return;
13640Sstevel@tonic-gate }
13650Sstevel@tonic-gate mp1->b_cont = mp;
13660Sstevel@tonic-gate mp = mp1;
13670Sstevel@tonic-gate
13680Sstevel@tonic-gate /*
136911042SErik.Nordmark@Sun.COM * Set IXAF_TRUSTED_ICMP so we can let the ICMP messages this
13700Sstevel@tonic-gate * node generates be accepted in peace by all on-host destinations.
13710Sstevel@tonic-gate * If we do NOT assume that all on-host destinations trust
137211042SErik.Nordmark@Sun.COM * self-generated ICMP messages, then rework here, ip6.c, and spd.c.
137311042SErik.Nordmark@Sun.COM * (Look for IXAF_TRUSTED_ICMP).
13740Sstevel@tonic-gate */
137511042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_TRUSTED_ICMP;
13760Sstevel@tonic-gate
13770Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr;
13780Sstevel@tonic-gate mp1->b_wptr = (uchar_t *)ip6h + (IPV6_HDR_LEN + len);
13790Sstevel@tonic-gate
13800Sstevel@tonic-gate ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
13810Sstevel@tonic-gate ip6h->ip6_nxt = IPPROTO_ICMPV6;
13823448Sdh155122 ip6h->ip6_hops = ipst->ips_ipv6_def_hops;
13830Sstevel@tonic-gate ip6h->ip6_dst = v6dst;
13840Sstevel@tonic-gate ip6h->ip6_src = v6src;
13850Sstevel@tonic-gate msg_len += IPV6_HDR_LEN + len;
13860Sstevel@tonic-gate if (msg_len > IP_MAXPACKET + IPV6_HDR_LEN) {
13870Sstevel@tonic-gate (void) adjmsg(mp, IP_MAXPACKET + IPV6_HDR_LEN - msg_len);
13880Sstevel@tonic-gate msg_len = IP_MAXPACKET + IPV6_HDR_LEN;
13890Sstevel@tonic-gate }
13900Sstevel@tonic-gate ip6h->ip6_plen = htons((uint16_t)(msgdsize(mp) - IPV6_HDR_LEN));
13910Sstevel@tonic-gate icmp6 = (icmp6_t *)&ip6h[1];
13920Sstevel@tonic-gate bcopy(stuff, (char *)icmp6, len);
13930Sstevel@tonic-gate /*
13940Sstevel@tonic-gate * Prepare for checksum by putting icmp length in the icmp
139511042SErik.Nordmark@Sun.COM * checksum field. The checksum is calculated in ip_output_wire_v6.
13960Sstevel@tonic-gate */
13970Sstevel@tonic-gate icmp6->icmp6_cksum = ip6h->ip6_plen;
13980Sstevel@tonic-gate if (icmp6->icmp6_type == ND_REDIRECT) {
13990Sstevel@tonic-gate ip6h->ip6_hops = IPV6_MAX_HOPS;
14000Sstevel@tonic-gate }
140111042SErik.Nordmark@Sun.COM
140211042SErik.Nordmark@Sun.COM (void) ip_output_simple(mp, &ixas);
140311042SErik.Nordmark@Sun.COM ixa_cleanup(&ixas);
14040Sstevel@tonic-gate }
14050Sstevel@tonic-gate
14060Sstevel@tonic-gate /*
14070Sstevel@tonic-gate * Update the output mib when ICMPv6 packets are sent.
14080Sstevel@tonic-gate */
140911042SErik.Nordmark@Sun.COM void
icmp_update_out_mib_v6(ill_t * ill,icmp6_t * icmp6)14100Sstevel@tonic-gate icmp_update_out_mib_v6(ill_t *ill, icmp6_t *icmp6)
14110Sstevel@tonic-gate {
14120Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutMsgs);
14130Sstevel@tonic-gate
14140Sstevel@tonic-gate switch (icmp6->icmp6_type) {
14150Sstevel@tonic-gate case ICMP6_DST_UNREACH:
14160Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutDestUnreachs);
14170Sstevel@tonic-gate if (icmp6->icmp6_code == ICMP6_DST_UNREACH_ADMIN)
14180Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutAdminProhibs);
14190Sstevel@tonic-gate break;
14200Sstevel@tonic-gate
14210Sstevel@tonic-gate case ICMP6_TIME_EXCEEDED:
14220Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutTimeExcds);
14230Sstevel@tonic-gate break;
14240Sstevel@tonic-gate
14250Sstevel@tonic-gate case ICMP6_PARAM_PROB:
14260Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutParmProblems);
14270Sstevel@tonic-gate break;
14280Sstevel@tonic-gate
14290Sstevel@tonic-gate case ICMP6_PACKET_TOO_BIG:
14300Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutPktTooBigs);
14310Sstevel@tonic-gate break;
14320Sstevel@tonic-gate
14330Sstevel@tonic-gate case ICMP6_ECHO_REQUEST:
14340Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutEchos);
14350Sstevel@tonic-gate break;
14360Sstevel@tonic-gate
14370Sstevel@tonic-gate case ICMP6_ECHO_REPLY:
14380Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutEchoReplies);
14390Sstevel@tonic-gate break;
14400Sstevel@tonic-gate
14410Sstevel@tonic-gate case ND_ROUTER_SOLICIT:
14420Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutRouterSolicits);
14430Sstevel@tonic-gate break;
14440Sstevel@tonic-gate
14450Sstevel@tonic-gate case ND_ROUTER_ADVERT:
14460Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutRouterAdvertisements);
14470Sstevel@tonic-gate break;
14480Sstevel@tonic-gate
14490Sstevel@tonic-gate case ND_NEIGHBOR_SOLICIT:
14500Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutNeighborSolicits);
14510Sstevel@tonic-gate break;
14520Sstevel@tonic-gate
14530Sstevel@tonic-gate case ND_NEIGHBOR_ADVERT:
14540Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib,
14550Sstevel@tonic-gate ipv6IfIcmpOutNeighborAdvertisements);
14560Sstevel@tonic-gate break;
14570Sstevel@tonic-gate
14580Sstevel@tonic-gate case ND_REDIRECT:
14590Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutRedirects);
14600Sstevel@tonic-gate break;
14610Sstevel@tonic-gate
14620Sstevel@tonic-gate case MLD_LISTENER_QUERY:
14630Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutGroupMembQueries);
14640Sstevel@tonic-gate break;
14650Sstevel@tonic-gate
14660Sstevel@tonic-gate case MLD_LISTENER_REPORT:
14670Sstevel@tonic-gate case MLD_V2_LISTENER_REPORT:
14680Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutGroupMembResponses);
14690Sstevel@tonic-gate break;
14700Sstevel@tonic-gate
14710Sstevel@tonic-gate case MLD_LISTENER_REDUCTION:
14720Sstevel@tonic-gate BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutGroupMembReductions);
14730Sstevel@tonic-gate break;
14740Sstevel@tonic-gate }
14750Sstevel@tonic-gate }
14760Sstevel@tonic-gate
14770Sstevel@tonic-gate /*
14780Sstevel@tonic-gate * Check if it is ok to send an ICMPv6 error packet in
14790Sstevel@tonic-gate * response to the IP packet in mp.
14800Sstevel@tonic-gate * Free the message and return null if no
14810Sstevel@tonic-gate * ICMP error packet should be sent.
14820Sstevel@tonic-gate */
14830Sstevel@tonic-gate static mblk_t *
icmp_pkt_err_ok_v6(mblk_t * mp,boolean_t mcast_ok,ip_recv_attr_t * ira)148411042SErik.Nordmark@Sun.COM icmp_pkt_err_ok_v6(mblk_t *mp, boolean_t mcast_ok, ip_recv_attr_t *ira)
14850Sstevel@tonic-gate {
148611042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill;
148711042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst;
148811042SErik.Nordmark@Sun.COM boolean_t llbcast;
148911042SErik.Nordmark@Sun.COM ip6_t *ip6h;
14900Sstevel@tonic-gate
14910Sstevel@tonic-gate if (!mp)
14920Sstevel@tonic-gate return (NULL);
14930Sstevel@tonic-gate
149411042SErik.Nordmark@Sun.COM /* We view multicast and broadcast as the same.. */
149511042SErik.Nordmark@Sun.COM llbcast = (ira->ira_flags &
149611042SErik.Nordmark@Sun.COM (IRAF_L2DST_MULTICAST|IRAF_L2DST_BROADCAST)) != 0;
14970Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr;
14980Sstevel@tonic-gate
14990Sstevel@tonic-gate /* Check if source address uniquely identifies the host */
15000Sstevel@tonic-gate
15010Sstevel@tonic-gate if (IN6_IS_ADDR_MULTICAST(&ip6h->ip6_src) ||
15020Sstevel@tonic-gate IN6_IS_ADDR_V4MAPPED(&ip6h->ip6_src) ||
15030Sstevel@tonic-gate IN6_IS_ADDR_UNSPECIFIED(&ip6h->ip6_src)) {
15040Sstevel@tonic-gate freemsg(mp);
15050Sstevel@tonic-gate return (NULL);
15060Sstevel@tonic-gate }
15070Sstevel@tonic-gate
15080Sstevel@tonic-gate if (ip6h->ip6_nxt == IPPROTO_ICMPV6) {
15090Sstevel@tonic-gate size_t len_needed = IPV6_HDR_LEN + ICMP6_MINLEN;
15100Sstevel@tonic-gate icmp6_t *icmp6;
15110Sstevel@tonic-gate
15120Sstevel@tonic-gate if (mp->b_wptr - mp->b_rptr < len_needed) {
15130Sstevel@tonic-gate if (!pullupmsg(mp, len_needed)) {
151411042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_icmp6_mib,
151511042SErik.Nordmark@Sun.COM ipv6IfIcmpInErrors);
15160Sstevel@tonic-gate freemsg(mp);
15170Sstevel@tonic-gate return (NULL);
15180Sstevel@tonic-gate }
15190Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr;
15200Sstevel@tonic-gate }
15210Sstevel@tonic-gate icmp6 = (icmp6_t *)&ip6h[1];
15220Sstevel@tonic-gate /* Explicitly do not generate errors in response to redirects */
15230Sstevel@tonic-gate if (ICMP6_IS_ERROR(icmp6->icmp6_type) ||
15240Sstevel@tonic-gate icmp6->icmp6_type == ND_REDIRECT) {
15250Sstevel@tonic-gate freemsg(mp);
15260Sstevel@tonic-gate return (NULL);
15270Sstevel@tonic-gate }
15280Sstevel@tonic-gate }
15290Sstevel@tonic-gate /*
15300Sstevel@tonic-gate * Check that the destination is not multicast and that the packet
15310Sstevel@tonic-gate * was not sent on link layer broadcast or multicast. (Exception
15320Sstevel@tonic-gate * is Packet too big message as per the draft - when mcast_ok is set.)
15330Sstevel@tonic-gate */
15340Sstevel@tonic-gate if (!mcast_ok &&
15350Sstevel@tonic-gate (llbcast || IN6_IS_ADDR_MULTICAST(&ip6h->ip6_dst))) {
15360Sstevel@tonic-gate freemsg(mp);
15370Sstevel@tonic-gate return (NULL);
15380Sstevel@tonic-gate }
153911042SErik.Nordmark@Sun.COM /*
154011042SErik.Nordmark@Sun.COM * If this is a labeled system, then check to see if we're allowed to
154111042SErik.Nordmark@Sun.COM * send a response to this particular sender. If not, then just drop.
154211042SErik.Nordmark@Sun.COM */
154311042SErik.Nordmark@Sun.COM if (is_system_labeled() && !tsol_can_reply_error(mp, ira)) {
154411042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutErrors);
154511042SErik.Nordmark@Sun.COM freemsg(mp);
154611042SErik.Nordmark@Sun.COM return (NULL);
154711042SErik.Nordmark@Sun.COM }
154811042SErik.Nordmark@Sun.COM
15493448Sdh155122 if (icmp_err_rate_limit(ipst)) {
15500Sstevel@tonic-gate /*
15510Sstevel@tonic-gate * Only send ICMP error packets every so often.
15520Sstevel@tonic-gate * This should be done on a per port/source basis,
15530Sstevel@tonic-gate * but for now this will suffice.
15540Sstevel@tonic-gate */
15550Sstevel@tonic-gate freemsg(mp);
15560Sstevel@tonic-gate return (NULL);
15570Sstevel@tonic-gate }
15580Sstevel@tonic-gate return (mp);
15590Sstevel@tonic-gate }
15600Sstevel@tonic-gate
15610Sstevel@tonic-gate /*
156211042SErik.Nordmark@Sun.COM * Called when a packet was sent out the same link that it arrived on.
156311042SErik.Nordmark@Sun.COM * Check if it is ok to send a redirect and then send it.
156411042SErik.Nordmark@Sun.COM */
156511042SErik.Nordmark@Sun.COM void
ip_send_potential_redirect_v6(mblk_t * mp,ip6_t * ip6h,ire_t * ire,ip_recv_attr_t * ira)156611042SErik.Nordmark@Sun.COM ip_send_potential_redirect_v6(mblk_t *mp, ip6_t *ip6h, ire_t *ire,
156711042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira)
156811042SErik.Nordmark@Sun.COM {
156911042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill;
157011042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst;
157111042SErik.Nordmark@Sun.COM in6_addr_t *v6targ;
157211042SErik.Nordmark@Sun.COM ire_t *src_ire_v6 = NULL;
157311042SErik.Nordmark@Sun.COM mblk_t *mp1;
157411042SErik.Nordmark@Sun.COM ire_t *nhop_ire = NULL;
157511042SErik.Nordmark@Sun.COM
157611042SErik.Nordmark@Sun.COM /*
157711042SErik.Nordmark@Sun.COM * Don't send a redirect when forwarding a source
157811042SErik.Nordmark@Sun.COM * routed packet.
157911042SErik.Nordmark@Sun.COM */
158011042SErik.Nordmark@Sun.COM if (ip_source_routed_v6(ip6h, mp, ipst))
158111042SErik.Nordmark@Sun.COM return;
158211042SErik.Nordmark@Sun.COM
158311042SErik.Nordmark@Sun.COM if (ire->ire_type & IRE_ONLINK) {
158411042SErik.Nordmark@Sun.COM /* Target is directly connected */
158511042SErik.Nordmark@Sun.COM v6targ = &ip6h->ip6_dst;
158611042SErik.Nordmark@Sun.COM } else {
158711042SErik.Nordmark@Sun.COM /* Determine the most specific IRE used to send the packets */
158811042SErik.Nordmark@Sun.COM nhop_ire = ire_nexthop(ire);
158911042SErik.Nordmark@Sun.COM if (nhop_ire == NULL)
159011042SErik.Nordmark@Sun.COM return;
159111042SErik.Nordmark@Sun.COM
159211042SErik.Nordmark@Sun.COM /*
159311042SErik.Nordmark@Sun.COM * We won't send redirects to a router
159411042SErik.Nordmark@Sun.COM * that doesn't have a link local
159511042SErik.Nordmark@Sun.COM * address, but will forward.
159611042SErik.Nordmark@Sun.COM */
159711042SErik.Nordmark@Sun.COM if (!IN6_IS_ADDR_LINKLOCAL(&nhop_ire->ire_addr_v6)) {
159811042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInAddrErrors);
159911042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInAddrErrors", mp, ill);
160011042SErik.Nordmark@Sun.COM ire_refrele(nhop_ire);
160111042SErik.Nordmark@Sun.COM return;
160211042SErik.Nordmark@Sun.COM }
160311042SErik.Nordmark@Sun.COM v6targ = &nhop_ire->ire_addr_v6;
160411042SErik.Nordmark@Sun.COM }
160511042SErik.Nordmark@Sun.COM src_ire_v6 = ire_ftable_lookup_v6(&ip6h->ip6_src,
160611042SErik.Nordmark@Sun.COM NULL, NULL, IRE_INTERFACE, ire->ire_ill, ALL_ZONES, NULL,
160711042SErik.Nordmark@Sun.COM MATCH_IRE_ILL | MATCH_IRE_TYPE, 0, ipst, NULL);
160811042SErik.Nordmark@Sun.COM
160911042SErik.Nordmark@Sun.COM if (src_ire_v6 == NULL) {
161011042SErik.Nordmark@Sun.COM if (nhop_ire != NULL)
161111042SErik.Nordmark@Sun.COM ire_refrele(nhop_ire);
161211042SErik.Nordmark@Sun.COM return;
161311042SErik.Nordmark@Sun.COM }
161411042SErik.Nordmark@Sun.COM
161511042SErik.Nordmark@Sun.COM /*
161611042SErik.Nordmark@Sun.COM * The source is directly connected.
161711042SErik.Nordmark@Sun.COM */
161811042SErik.Nordmark@Sun.COM mp1 = copymsg(mp);
161911042SErik.Nordmark@Sun.COM if (mp1 != NULL)
162011042SErik.Nordmark@Sun.COM icmp_send_redirect_v6(mp1, v6targ, &ip6h->ip6_dst, ira);
162111042SErik.Nordmark@Sun.COM
162211042SErik.Nordmark@Sun.COM if (nhop_ire != NULL)
162311042SErik.Nordmark@Sun.COM ire_refrele(nhop_ire);
162411042SErik.Nordmark@Sun.COM ire_refrele(src_ire_v6);
162511042SErik.Nordmark@Sun.COM }
162611042SErik.Nordmark@Sun.COM
162711042SErik.Nordmark@Sun.COM /*
16280Sstevel@tonic-gate * Generate an ICMPv6 redirect message.
16290Sstevel@tonic-gate * Include target link layer address option if it exits.
16300Sstevel@tonic-gate * Always include redirect header.
16310Sstevel@tonic-gate */
16320Sstevel@tonic-gate static void
icmp_send_redirect_v6(mblk_t * mp,in6_addr_t * targetp,in6_addr_t * dest,ip_recv_attr_t * ira)163311042SErik.Nordmark@Sun.COM icmp_send_redirect_v6(mblk_t *mp, in6_addr_t *targetp, in6_addr_t *dest,
163411042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira)
16350Sstevel@tonic-gate {
16360Sstevel@tonic-gate nd_redirect_t *rd;
16370Sstevel@tonic-gate nd_opt_rd_hdr_t *rdh;
16380Sstevel@tonic-gate uchar_t *buf;
163911042SErik.Nordmark@Sun.COM ncec_t *ncec = NULL;
16400Sstevel@tonic-gate nd_opt_hdr_t *opt;
16410Sstevel@tonic-gate int len;
16420Sstevel@tonic-gate int ll_opt_len = 0;
16430Sstevel@tonic-gate int max_redir_hdr_data_len;
16440Sstevel@tonic-gate int pkt_len;
16450Sstevel@tonic-gate in6_addr_t *srcp;
164611042SErik.Nordmark@Sun.COM ill_t *ill;
164711042SErik.Nordmark@Sun.COM boolean_t need_refrele;
164811042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ira->ira_ill->ill_ipst;
164911042SErik.Nordmark@Sun.COM
165011042SErik.Nordmark@Sun.COM mp = icmp_pkt_err_ok_v6(mp, B_FALSE, ira);
16510Sstevel@tonic-gate if (mp == NULL)
16520Sstevel@tonic-gate return;
165311042SErik.Nordmark@Sun.COM
165411042SErik.Nordmark@Sun.COM if (IS_UNDER_IPMP(ira->ira_ill)) {
165511042SErik.Nordmark@Sun.COM ill = ipmp_ill_hold_ipmp_ill(ira->ira_ill);
165611042SErik.Nordmark@Sun.COM if (ill == NULL) {
165711042SErik.Nordmark@Sun.COM ill = ira->ira_ill;
165811042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects);
165911042SErik.Nordmark@Sun.COM ip_drop_output("no IPMP ill for sending redirect",
166011042SErik.Nordmark@Sun.COM mp, ill);
166111042SErik.Nordmark@Sun.COM freemsg(mp);
166211042SErik.Nordmark@Sun.COM return;
166311042SErik.Nordmark@Sun.COM }
166411042SErik.Nordmark@Sun.COM need_refrele = B_TRUE;
166511042SErik.Nordmark@Sun.COM } else {
166611042SErik.Nordmark@Sun.COM ill = ira->ira_ill;
166711042SErik.Nordmark@Sun.COM need_refrele = B_FALSE;
166811042SErik.Nordmark@Sun.COM }
166911042SErik.Nordmark@Sun.COM
167011042SErik.Nordmark@Sun.COM ncec = ncec_lookup_illgrp_v6(ill, targetp);
167111042SErik.Nordmark@Sun.COM if (ncec != NULL && ncec->ncec_state != ND_INCOMPLETE &&
167211042SErik.Nordmark@Sun.COM ncec->ncec_lladdr != NULL) {
16730Sstevel@tonic-gate ll_opt_len = (sizeof (nd_opt_hdr_t) +
16740Sstevel@tonic-gate ill->ill_phys_addr_length + 7)/8 * 8;
16750Sstevel@tonic-gate }
16760Sstevel@tonic-gate len = sizeof (nd_redirect_t) + sizeof (nd_opt_rd_hdr_t) + ll_opt_len;
16770Sstevel@tonic-gate ASSERT(len % 4 == 0);
16780Sstevel@tonic-gate buf = kmem_alloc(len, KM_NOSLEEP);
16790Sstevel@tonic-gate if (buf == NULL) {
168011042SErik.Nordmark@Sun.COM if (ncec != NULL)
168111042SErik.Nordmark@Sun.COM ncec_refrele(ncec);
168211042SErik.Nordmark@Sun.COM if (need_refrele)
168311042SErik.Nordmark@Sun.COM ill_refrele(ill);
16840Sstevel@tonic-gate freemsg(mp);
16850Sstevel@tonic-gate return;
16860Sstevel@tonic-gate }
16870Sstevel@tonic-gate
16880Sstevel@tonic-gate rd = (nd_redirect_t *)buf;
16890Sstevel@tonic-gate rd->nd_rd_type = (uint8_t)ND_REDIRECT;
16900Sstevel@tonic-gate rd->nd_rd_code = 0;
16910Sstevel@tonic-gate rd->nd_rd_reserved = 0;
16920Sstevel@tonic-gate rd->nd_rd_target = *targetp;
16930Sstevel@tonic-gate rd->nd_rd_dst = *dest;
16940Sstevel@tonic-gate
16950Sstevel@tonic-gate opt = (nd_opt_hdr_t *)(buf + sizeof (nd_redirect_t));
169611042SErik.Nordmark@Sun.COM if (ncec != NULL && ll_opt_len != 0) {
16970Sstevel@tonic-gate opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
16980Sstevel@tonic-gate opt->nd_opt_len = ll_opt_len/8;
169911042SErik.Nordmark@Sun.COM bcopy((char *)ncec->ncec_lladdr, &opt[1],
17000Sstevel@tonic-gate ill->ill_phys_addr_length);
17010Sstevel@tonic-gate }
170211042SErik.Nordmark@Sun.COM if (ncec != NULL)
170311042SErik.Nordmark@Sun.COM ncec_refrele(ncec);
17040Sstevel@tonic-gate rdh = (nd_opt_rd_hdr_t *)(buf + sizeof (nd_redirect_t) + ll_opt_len);
17050Sstevel@tonic-gate rdh->nd_opt_rh_type = (uint8_t)ND_OPT_REDIRECTED_HEADER;
17060Sstevel@tonic-gate /* max_redir_hdr_data_len and nd_opt_rh_len must be multiple of 8 */
17073448Sdh155122 max_redir_hdr_data_len =
17083448Sdh155122 (ipst->ips_ipv6_icmp_return - IPV6_HDR_LEN - len)/8*8;
17090Sstevel@tonic-gate pkt_len = msgdsize(mp);
17100Sstevel@tonic-gate /* Make sure mp is 8 byte aligned */
17110Sstevel@tonic-gate if (pkt_len > max_redir_hdr_data_len) {
17120Sstevel@tonic-gate rdh->nd_opt_rh_len = (max_redir_hdr_data_len +
17130Sstevel@tonic-gate sizeof (nd_opt_rd_hdr_t))/8;
17140Sstevel@tonic-gate (void) adjmsg(mp, max_redir_hdr_data_len - pkt_len);
17150Sstevel@tonic-gate } else {
17160Sstevel@tonic-gate rdh->nd_opt_rh_len = (pkt_len + sizeof (nd_opt_rd_hdr_t))/8;
17170Sstevel@tonic-gate (void) adjmsg(mp, -(pkt_len % 8));
17180Sstevel@tonic-gate }
17190Sstevel@tonic-gate rdh->nd_opt_rh_reserved1 = 0;
17200Sstevel@tonic-gate rdh->nd_opt_rh_reserved2 = 0;
172111042SErik.Nordmark@Sun.COM /* ipif_v6lcl_addr contains the link-local source address */
172211042SErik.Nordmark@Sun.COM srcp = &ill->ill_ipif->ipif_v6lcl_addr;
17238485SPeter.Memishian@Sun.COM
17242733Snordmark /* Redirects sent by router, and router is global zone */
172511042SErik.Nordmark@Sun.COM ASSERT(ira->ira_zoneid == ALL_ZONES);
172611042SErik.Nordmark@Sun.COM ira->ira_zoneid = GLOBAL_ZONEID;
172711042SErik.Nordmark@Sun.COM icmp_pkt_v6(mp, buf, len, srcp, ira);
17280Sstevel@tonic-gate kmem_free(buf, len);
172911042SErik.Nordmark@Sun.COM if (need_refrele)
173011042SErik.Nordmark@Sun.COM ill_refrele(ill);
17310Sstevel@tonic-gate }
17320Sstevel@tonic-gate
17330Sstevel@tonic-gate
17340Sstevel@tonic-gate /* Generate an ICMP time exceeded message. (May be called as writer.) */
17350Sstevel@tonic-gate void
icmp_time_exceeded_v6(mblk_t * mp,uint8_t code,boolean_t mcast_ok,ip_recv_attr_t * ira)173611042SErik.Nordmark@Sun.COM icmp_time_exceeded_v6(mblk_t *mp, uint8_t code, boolean_t mcast_ok,
173711042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira)
17380Sstevel@tonic-gate {
17390Sstevel@tonic-gate icmp6_t icmp6;
174011042SErik.Nordmark@Sun.COM
174111042SErik.Nordmark@Sun.COM mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira);
174211042SErik.Nordmark@Sun.COM if (mp == NULL)
17430Sstevel@tonic-gate return;
174411042SErik.Nordmark@Sun.COM
17450Sstevel@tonic-gate bzero(&icmp6, sizeof (icmp6_t));
17460Sstevel@tonic-gate icmp6.icmp6_type = ICMP6_TIME_EXCEEDED;
17470Sstevel@tonic-gate icmp6.icmp6_code = code;
174811042SErik.Nordmark@Sun.COM icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira);
17490Sstevel@tonic-gate }
17500Sstevel@tonic-gate
17510Sstevel@tonic-gate /*
17520Sstevel@tonic-gate * Generate an ICMP unreachable message.
175311042SErik.Nordmark@Sun.COM * When called from ip_output side a minimal ip_recv_attr_t needs to be
175411042SErik.Nordmark@Sun.COM * constructed by the caller.
17550Sstevel@tonic-gate */
17560Sstevel@tonic-gate void
icmp_unreachable_v6(mblk_t * mp,uint8_t code,boolean_t mcast_ok,ip_recv_attr_t * ira)175711042SErik.Nordmark@Sun.COM icmp_unreachable_v6(mblk_t *mp, uint8_t code, boolean_t mcast_ok,
175811042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira)
17590Sstevel@tonic-gate {
17600Sstevel@tonic-gate icmp6_t icmp6;
176111042SErik.Nordmark@Sun.COM
176211042SErik.Nordmark@Sun.COM mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira);
176311042SErik.Nordmark@Sun.COM if (mp == NULL)
17640Sstevel@tonic-gate return;
176511042SErik.Nordmark@Sun.COM
17660Sstevel@tonic-gate bzero(&icmp6, sizeof (icmp6_t));
17670Sstevel@tonic-gate icmp6.icmp6_type = ICMP6_DST_UNREACH;
17680Sstevel@tonic-gate icmp6.icmp6_code = code;
176911042SErik.Nordmark@Sun.COM icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira);
17700Sstevel@tonic-gate }
17710Sstevel@tonic-gate
17720Sstevel@tonic-gate /*
17730Sstevel@tonic-gate * Generate an ICMP pkt too big message.
177411042SErik.Nordmark@Sun.COM * When called from ip_output side a minimal ip_recv_attr_t needs to be
177511042SErik.Nordmark@Sun.COM * constructed by the caller.
17760Sstevel@tonic-gate */
177711042SErik.Nordmark@Sun.COM void
icmp_pkt2big_v6(mblk_t * mp,uint32_t mtu,boolean_t mcast_ok,ip_recv_attr_t * ira)177811042SErik.Nordmark@Sun.COM icmp_pkt2big_v6(mblk_t *mp, uint32_t mtu, boolean_t mcast_ok,
177911042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira)
17800Sstevel@tonic-gate {
17810Sstevel@tonic-gate icmp6_t icmp6;
178211042SErik.Nordmark@Sun.COM
178311042SErik.Nordmark@Sun.COM mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira);
178411042SErik.Nordmark@Sun.COM if (mp == NULL)
17850Sstevel@tonic-gate return;
178611042SErik.Nordmark@Sun.COM
17870Sstevel@tonic-gate bzero(&icmp6, sizeof (icmp6_t));
17880Sstevel@tonic-gate icmp6.icmp6_type = ICMP6_PACKET_TOO_BIG;
17890Sstevel@tonic-gate icmp6.icmp6_code = 0;
17900Sstevel@tonic-gate icmp6.icmp6_mtu = htonl(mtu);
17910Sstevel@tonic-gate
179211042SErik.Nordmark@Sun.COM icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira);
17930Sstevel@tonic-gate }
17940Sstevel@tonic-gate
17950Sstevel@tonic-gate /*
17960Sstevel@tonic-gate * Generate an ICMP parameter problem message. (May be called as writer.)
17970Sstevel@tonic-gate * 'offset' is the offset from the beginning of the packet in error.
179811042SErik.Nordmark@Sun.COM * When called from ip_output side a minimal ip_recv_attr_t needs to be
179911042SErik.Nordmark@Sun.COM * constructed by the caller.
18000Sstevel@tonic-gate */
18010Sstevel@tonic-gate static void
icmp_param_problem_v6(mblk_t * mp,uint8_t code,uint32_t offset,boolean_t mcast_ok,ip_recv_attr_t * ira)180211042SErik.Nordmark@Sun.COM icmp_param_problem_v6(mblk_t *mp, uint8_t code, uint32_t offset,
180311042SErik.Nordmark@Sun.COM boolean_t mcast_ok, ip_recv_attr_t *ira)
18040Sstevel@tonic-gate {
18050Sstevel@tonic-gate icmp6_t icmp6;
180611042SErik.Nordmark@Sun.COM
180711042SErik.Nordmark@Sun.COM mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira);
180811042SErik.Nordmark@Sun.COM if (mp == NULL)
18090Sstevel@tonic-gate return;
181011042SErik.Nordmark@Sun.COM
18110Sstevel@tonic-gate bzero((char *)&icmp6, sizeof (icmp6_t));
18120Sstevel@tonic-gate icmp6.icmp6_type = ICMP6_PARAM_PROB;
18130Sstevel@tonic-gate icmp6.icmp6_code = code;
18140Sstevel@tonic-gate icmp6.icmp6_pptr = htonl(offset);
181511042SErik.Nordmark@Sun.COM icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira);
181611042SErik.Nordmark@Sun.COM }
181711042SErik.Nordmark@Sun.COM
181811042SErik.Nordmark@Sun.COM void
icmp_param_problem_nexthdr_v6(mblk_t * mp,boolean_t mcast_ok,ip_recv_attr_t * ira)181911042SErik.Nordmark@Sun.COM icmp_param_problem_nexthdr_v6(mblk_t *mp, boolean_t mcast_ok,
182011042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira)
182111042SErik.Nordmark@Sun.COM {
182211042SErik.Nordmark@Sun.COM ip6_t *ip6h = (ip6_t *)mp->b_rptr;
182311042SErik.Nordmark@Sun.COM uint16_t hdr_length;
182411042SErik.Nordmark@Sun.COM uint8_t *nexthdrp;
182511042SErik.Nordmark@Sun.COM uint32_t offset;
182611042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill;
182711042SErik.Nordmark@Sun.COM
182811042SErik.Nordmark@Sun.COM /* Determine the offset of the bad nexthdr value */
182911042SErik.Nordmark@Sun.COM if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp)) {
183011042SErik.Nordmark@Sun.COM /* Malformed packet */
183111042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
183211042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards", mp, ill);
183311042SErik.Nordmark@Sun.COM freemsg(mp);
183411042SErik.Nordmark@Sun.COM return;
183511042SErik.Nordmark@Sun.COM }
183611042SErik.Nordmark@Sun.COM
183711042SErik.Nordmark@Sun.COM offset = nexthdrp - mp->b_rptr;
183811042SErik.Nordmark@Sun.COM icmp_param_problem_v6(mp, ICMP6_PARAMPROB_NEXTHEADER, offset,
183911042SErik.Nordmark@Sun.COM mcast_ok, ira);
18400Sstevel@tonic-gate }
18410Sstevel@tonic-gate
18420Sstevel@tonic-gate /*
184311042SErik.Nordmark@Sun.COM * Verify whether or not the IP address is a valid local address.
184411042SErik.Nordmark@Sun.COM * Could be a unicast, including one for a down interface.
184511042SErik.Nordmark@Sun.COM * If allow_mcbc then a multicast or broadcast address is also
184611042SErik.Nordmark@Sun.COM * acceptable.
184711042SErik.Nordmark@Sun.COM *
18480Sstevel@tonic-gate * In the case of a multicast address, however, the
18490Sstevel@tonic-gate * upper protocol is expected to reset the src address
185011042SErik.Nordmark@Sun.COM * to zero when we return IPVL_MCAST so that
18510Sstevel@tonic-gate * no packets are emitted with multicast address as
18520Sstevel@tonic-gate * source address.
18530Sstevel@tonic-gate * The addresses valid for bind are:
18540Sstevel@tonic-gate * (1) - in6addr_any
18550Sstevel@tonic-gate * (2) - IP address of an UP interface
18560Sstevel@tonic-gate * (3) - IP address of a DOWN interface
18570Sstevel@tonic-gate * (4) - a multicast address. In this case
18580Sstevel@tonic-gate * the conn will only receive packets destined to
18590Sstevel@tonic-gate * the specified multicast address. Note: the
18600Sstevel@tonic-gate * application still has to issue an
18610Sstevel@tonic-gate * IPV6_JOIN_GROUP socket option.
18620Sstevel@tonic-gate *
18630Sstevel@tonic-gate * In all the above cases, the bound address must be valid in the current zone.
18640Sstevel@tonic-gate * When the address is loopback or multicast, there might be many matching IREs
18650Sstevel@tonic-gate * so bind has to look up based on the zone.
18660Sstevel@tonic-gate */
186711042SErik.Nordmark@Sun.COM ip_laddr_t
ip_laddr_verify_v6(const in6_addr_t * v6src,zoneid_t zoneid,ip_stack_t * ipst,boolean_t allow_mcbc,uint_t scopeid)186811042SErik.Nordmark@Sun.COM ip_laddr_verify_v6(const in6_addr_t *v6src, zoneid_t zoneid,
186911042SErik.Nordmark@Sun.COM ip_stack_t *ipst, boolean_t allow_mcbc, uint_t scopeid)
187011042SErik.Nordmark@Sun.COM {
187111042SErik.Nordmark@Sun.COM ire_t *src_ire;
187211042SErik.Nordmark@Sun.COM uint_t match_flags;
187311042SErik.Nordmark@Sun.COM ill_t *ill = NULL;
187411042SErik.Nordmark@Sun.COM
187511042SErik.Nordmark@Sun.COM ASSERT(!IN6_IS_ADDR_V4MAPPED(v6src));
187611042SErik.Nordmark@Sun.COM ASSERT(!IN6_IS_ADDR_UNSPECIFIED(v6src));
187711042SErik.Nordmark@Sun.COM
187811042SErik.Nordmark@Sun.COM match_flags = MATCH_IRE_ZONEONLY;
187911042SErik.Nordmark@Sun.COM if (scopeid != 0) {
188011042SErik.Nordmark@Sun.COM ill = ill_lookup_on_ifindex(scopeid, B_TRUE, ipst);
188111042SErik.Nordmark@Sun.COM if (ill == NULL)
188211042SErik.Nordmark@Sun.COM return (IPVL_BAD);
188311042SErik.Nordmark@Sun.COM match_flags |= MATCH_IRE_ILL;
188411042SErik.Nordmark@Sun.COM }
188511042SErik.Nordmark@Sun.COM
188611042SErik.Nordmark@Sun.COM src_ire = ire_ftable_lookup_v6(v6src, NULL, NULL, 0,
188711042SErik.Nordmark@Sun.COM ill, zoneid, NULL, match_flags, 0, ipst, NULL);
188811042SErik.Nordmark@Sun.COM if (ill != NULL)
188911042SErik.Nordmark@Sun.COM ill_refrele(ill);
189011042SErik.Nordmark@Sun.COM
189111042SErik.Nordmark@Sun.COM /*
189211042SErik.Nordmark@Sun.COM * If an address other than in6addr_any is requested,
189311042SErik.Nordmark@Sun.COM * we verify that it is a valid address for bind
189411042SErik.Nordmark@Sun.COM * Note: Following code is in if-else-if form for
189511042SErik.Nordmark@Sun.COM * readability compared to a condition check.
189611042SErik.Nordmark@Sun.COM */
189711042SErik.Nordmark@Sun.COM if (src_ire != NULL && (src_ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK))) {
189811042SErik.Nordmark@Sun.COM /*
189911042SErik.Nordmark@Sun.COM * (2) Bind to address of local UP interface
190011042SErik.Nordmark@Sun.COM */
190111042SErik.Nordmark@Sun.COM ire_refrele(src_ire);
190211042SErik.Nordmark@Sun.COM return (IPVL_UNICAST_UP);
190311042SErik.Nordmark@Sun.COM } else if (IN6_IS_ADDR_MULTICAST(v6src)) {
190411042SErik.Nordmark@Sun.COM /* (4) bind to multicast address. */
190511042SErik.Nordmark@Sun.COM if (src_ire != NULL)
190611042SErik.Nordmark@Sun.COM ire_refrele(src_ire);
190711042SErik.Nordmark@Sun.COM
190811042SErik.Nordmark@Sun.COM /*
190911042SErik.Nordmark@Sun.COM * Note: caller should take IPV6_MULTICAST_IF
191011042SErik.Nordmark@Sun.COM * into account when selecting a real source address.
191111042SErik.Nordmark@Sun.COM */
191211042SErik.Nordmark@Sun.COM if (allow_mcbc)
191311042SErik.Nordmark@Sun.COM return (IPVL_MCAST);
191411042SErik.Nordmark@Sun.COM else
191511042SErik.Nordmark@Sun.COM return (IPVL_BAD);
191611042SErik.Nordmark@Sun.COM } else {
191711042SErik.Nordmark@Sun.COM ipif_t *ipif;
191811042SErik.Nordmark@Sun.COM
191911042SErik.Nordmark@Sun.COM /*
192011042SErik.Nordmark@Sun.COM * (3) Bind to address of local DOWN interface?
192111042SErik.Nordmark@Sun.COM * (ipif_lookup_addr() looks up all interfaces
192211042SErik.Nordmark@Sun.COM * but we do not get here for UP interfaces
192311042SErik.Nordmark@Sun.COM * - case (2) above)
192411042SErik.Nordmark@Sun.COM */
192511042SErik.Nordmark@Sun.COM if (src_ire != NULL)
192611042SErik.Nordmark@Sun.COM ire_refrele(src_ire);
192711042SErik.Nordmark@Sun.COM
192811042SErik.Nordmark@Sun.COM ipif = ipif_lookup_addr_v6(v6src, NULL, zoneid, ipst);
192911042SErik.Nordmark@Sun.COM if (ipif == NULL)
193011042SErik.Nordmark@Sun.COM return (IPVL_BAD);
193111042SErik.Nordmark@Sun.COM
193211042SErik.Nordmark@Sun.COM /* Not a useful source? */
193311042SErik.Nordmark@Sun.COM if (ipif->ipif_flags & (IPIF_NOLOCAL | IPIF_ANYCAST)) {
193411042SErik.Nordmark@Sun.COM ipif_refrele(ipif);
193511042SErik.Nordmark@Sun.COM return (IPVL_BAD);
193611042SErik.Nordmark@Sun.COM }
193711042SErik.Nordmark@Sun.COM ipif_refrele(ipif);
193811042SErik.Nordmark@Sun.COM return (IPVL_UNICAST_DOWN);
193911042SErik.Nordmark@Sun.COM }
194011042SErik.Nordmark@Sun.COM }
194111042SErik.Nordmark@Sun.COM
19428348SEric.Yu@Sun.COM /*
194311042SErik.Nordmark@Sun.COM * Verify that both the source and destination addresses are valid. If
194411042SErik.Nordmark@Sun.COM * IPDF_VERIFY_DST is not set, then the destination address may be unreachable,
194511042SErik.Nordmark@Sun.COM * i.e. have no route to it. Protocols like TCP want to verify destination
194611042SErik.Nordmark@Sun.COM * reachability, while tunnels do not.
194711042SErik.Nordmark@Sun.COM *
194811042SErik.Nordmark@Sun.COM * Determine the route, the interface, and (optionally) the source address
194911042SErik.Nordmark@Sun.COM * to use to reach a given destination.
195011042SErik.Nordmark@Sun.COM * Note that we allow connect to broadcast and multicast addresses when
195111042SErik.Nordmark@Sun.COM * IPDF_ALLOW_MCBC is set.
195211042SErik.Nordmark@Sun.COM * first_hop and dst_addr are normally the same, but if source routing
195311042SErik.Nordmark@Sun.COM * they will differ; in that case the first_hop is what we'll use for the
195411042SErik.Nordmark@Sun.COM * routing lookup but the dce and label checks will be done on dst_addr,
195511042SErik.Nordmark@Sun.COM *
195611042SErik.Nordmark@Sun.COM * If uinfo is set, then we fill in the best available information
195711042SErik.Nordmark@Sun.COM * we have for the destination. This is based on (in priority order) any
195811042SErik.Nordmark@Sun.COM * metrics and path MTU stored in a dce_t, route metrics, and finally the
1959*13123SErik.Nordmark@Sun.COM * ill_mtu/ill_mc_mtu.
196011042SErik.Nordmark@Sun.COM *
196111042SErik.Nordmark@Sun.COM * Tsol note: If we have a source route then dst_addr != firsthop. But we
196211042SErik.Nordmark@Sun.COM * always do the label check on dst_addr.
196311042SErik.Nordmark@Sun.COM *
196411042SErik.Nordmark@Sun.COM * Assumes that the caller has set ixa_scopeid for link-local communication.
19658348SEric.Yu@Sun.COM */
196611042SErik.Nordmark@Sun.COM int
ip_set_destination_v6(in6_addr_t * src_addrp,const in6_addr_t * dst_addr,const in6_addr_t * firsthop,ip_xmit_attr_t * ixa,iulp_t * uinfo,uint32_t flags,uint_t mac_mode)196711042SErik.Nordmark@Sun.COM ip_set_destination_v6(in6_addr_t *src_addrp, const in6_addr_t *dst_addr,
196811042SErik.Nordmark@Sun.COM const in6_addr_t *firsthop, ip_xmit_attr_t *ixa, iulp_t *uinfo,
196911042SErik.Nordmark@Sun.COM uint32_t flags, uint_t mac_mode)
19700Sstevel@tonic-gate {
197111042SErik.Nordmark@Sun.COM ire_t *ire;
19720Sstevel@tonic-gate int error = 0;
197311042SErik.Nordmark@Sun.COM in6_addr_t setsrc; /* RTF_SETSRC */
197411042SErik.Nordmark@Sun.COM zoneid_t zoneid = ixa->ixa_zoneid; /* Honors SO_ALLZONES */
197511042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst;
197611042SErik.Nordmark@Sun.COM dce_t *dce;
197711042SErik.Nordmark@Sun.COM uint_t pmtu;
197811042SErik.Nordmark@Sun.COM uint_t ifindex;
197911042SErik.Nordmark@Sun.COM uint_t generation;
198011042SErik.Nordmark@Sun.COM nce_t *nce;
198111042SErik.Nordmark@Sun.COM ill_t *ill = NULL;
198211042SErik.Nordmark@Sun.COM boolean_t multirt = B_FALSE;
198311042SErik.Nordmark@Sun.COM
198411042SErik.Nordmark@Sun.COM ASSERT(!IN6_IS_ADDR_V4MAPPED(dst_addr));
198511042SErik.Nordmark@Sun.COM
198611042SErik.Nordmark@Sun.COM ASSERT(!(ixa->ixa_flags & IXAF_IS_IPV4));
19870Sstevel@tonic-gate
19880Sstevel@tonic-gate /*
198911042SErik.Nordmark@Sun.COM * We never send to zero; the ULPs map it to the loopback address.
199011042SErik.Nordmark@Sun.COM * We can't allow it since we use zero to mean unitialized in some
199111042SErik.Nordmark@Sun.COM * places.
199211042SErik.Nordmark@Sun.COM */
199311042SErik.Nordmark@Sun.COM ASSERT(!IN6_IS_ADDR_UNSPECIFIED(dst_addr));
199411042SErik.Nordmark@Sun.COM
199511042SErik.Nordmark@Sun.COM if (is_system_labeled()) {
199611042SErik.Nordmark@Sun.COM ts_label_t *tsl = NULL;
199711042SErik.Nordmark@Sun.COM
199811042SErik.Nordmark@Sun.COM error = tsol_check_dest(ixa->ixa_tsl, dst_addr, IPV6_VERSION,
199911042SErik.Nordmark@Sun.COM mac_mode, (flags & IPDF_ZONE_IS_GLOBAL) != 0, &tsl);
200011042SErik.Nordmark@Sun.COM if (error != 0)
200111042SErik.Nordmark@Sun.COM return (error);
200211042SErik.Nordmark@Sun.COM if (tsl != NULL) {
200311042SErik.Nordmark@Sun.COM /* Update the label */
200411042SErik.Nordmark@Sun.COM ip_xmit_attr_replace_tsl(ixa, tsl);
200511042SErik.Nordmark@Sun.COM }
200611042SErik.Nordmark@Sun.COM }
200711042SErik.Nordmark@Sun.COM
200811042SErik.Nordmark@Sun.COM setsrc = ipv6_all_zeros;
200911042SErik.Nordmark@Sun.COM /*
201011042SErik.Nordmark@Sun.COM * Select a route; For IPMP interfaces, we would only select
201111042SErik.Nordmark@Sun.COM * a "hidden" route (i.e., going through a specific under_ill)
201211042SErik.Nordmark@Sun.COM * if ixa_ifindex has been specified.
20130Sstevel@tonic-gate */
201411681SSowmini.Varadhan@Sun.COM ire = ip_select_route_v6(firsthop, *src_addrp, ixa, &generation,
201511681SSowmini.Varadhan@Sun.COM &setsrc, &error, &multirt);
201611042SErik.Nordmark@Sun.COM ASSERT(ire != NULL); /* IRE_NOROUTE if none found */
201711042SErik.Nordmark@Sun.COM if (error != 0)
201811042SErik.Nordmark@Sun.COM goto bad_addr;
201911042SErik.Nordmark@Sun.COM
202011042SErik.Nordmark@Sun.COM /*
202111042SErik.Nordmark@Sun.COM * ire can't be a broadcast or multicast unless IPDF_ALLOW_MCBC is set.
202211042SErik.Nordmark@Sun.COM * If IPDF_VERIFY_DST is set, the destination must be reachable.
202311042SErik.Nordmark@Sun.COM * Otherwise the destination needn't be reachable.
202411042SErik.Nordmark@Sun.COM *
202511042SErik.Nordmark@Sun.COM * If we match on a reject or black hole, then we've got a
202611042SErik.Nordmark@Sun.COM * local failure. May as well fail out the connect() attempt,
202711042SErik.Nordmark@Sun.COM * since it's never going to succeed.
202811042SErik.Nordmark@Sun.COM */
202911042SErik.Nordmark@Sun.COM if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
20300Sstevel@tonic-gate /*
203111042SErik.Nordmark@Sun.COM * If we're verifying destination reachability, we always want
203211042SErik.Nordmark@Sun.COM * to complain here.
203311042SErik.Nordmark@Sun.COM *
203411042SErik.Nordmark@Sun.COM * If we're not verifying destination reachability but the
203511042SErik.Nordmark@Sun.COM * destination has a route, we still want to fail on the
203611042SErik.Nordmark@Sun.COM * temporary address and broadcast address tests.
203711042SErik.Nordmark@Sun.COM *
203811042SErik.Nordmark@Sun.COM * In both cases do we let the code continue so some reasonable
203911042SErik.Nordmark@Sun.COM * information is returned to the caller. That enables the
204011042SErik.Nordmark@Sun.COM * caller to use (and even cache) the IRE. conn_ip_ouput will
204111042SErik.Nordmark@Sun.COM * use the generation mismatch path to check for the unreachable
204211042SErik.Nordmark@Sun.COM * case thereby avoiding any specific check in the main path.
20430Sstevel@tonic-gate */
204411042SErik.Nordmark@Sun.COM ASSERT(generation == IRE_GENERATION_VERIFY);
204511042SErik.Nordmark@Sun.COM if (flags & IPDF_VERIFY_DST) {
20460Sstevel@tonic-gate /*
204711042SErik.Nordmark@Sun.COM * Set errno but continue to set up ixa_ire to be
204811042SErik.Nordmark@Sun.COM * the RTF_REJECT|RTF_BLACKHOLE IRE.
204911042SErik.Nordmark@Sun.COM * That allows callers to use ip_output to get an
205011042SErik.Nordmark@Sun.COM * ICMP error back.
20510Sstevel@tonic-gate */
205211042SErik.Nordmark@Sun.COM if (!(ire->ire_type & IRE_HOST))
205311042SErik.Nordmark@Sun.COM error = ENETUNREACH;
205411042SErik.Nordmark@Sun.COM else
205511042SErik.Nordmark@Sun.COM error = EHOSTUNREACH;
205611042SErik.Nordmark@Sun.COM }
205711042SErik.Nordmark@Sun.COM }
205811042SErik.Nordmark@Sun.COM
205911042SErik.Nordmark@Sun.COM if ((ire->ire_type & (IRE_BROADCAST|IRE_MULTICAST)) &&
206011042SErik.Nordmark@Sun.COM !(flags & IPDF_ALLOW_MCBC)) {
206111042SErik.Nordmark@Sun.COM ire_refrele(ire);
206211042SErik.Nordmark@Sun.COM ire = ire_reject(ipst, B_FALSE);
206311042SErik.Nordmark@Sun.COM generation = IRE_GENERATION_VERIFY;
206411042SErik.Nordmark@Sun.COM error = ENETUNREACH;
206511042SErik.Nordmark@Sun.COM }
206611042SErik.Nordmark@Sun.COM
206711042SErik.Nordmark@Sun.COM /* Cache things */
206811042SErik.Nordmark@Sun.COM if (ixa->ixa_ire != NULL)
206911042SErik.Nordmark@Sun.COM ire_refrele_notr(ixa->ixa_ire);
207011042SErik.Nordmark@Sun.COM #ifdef DEBUG
207111042SErik.Nordmark@Sun.COM ire_refhold_notr(ire);
207211042SErik.Nordmark@Sun.COM ire_refrele(ire);
207311042SErik.Nordmark@Sun.COM #endif
207411042SErik.Nordmark@Sun.COM ixa->ixa_ire = ire;
207511042SErik.Nordmark@Sun.COM ixa->ixa_ire_generation = generation;
207611042SErik.Nordmark@Sun.COM
207711042SErik.Nordmark@Sun.COM /*
207812328SSowmini.Varadhan@Sun.COM * Ensure that ixa_dce is always set any time that ixa_ire is set,
207912328SSowmini.Varadhan@Sun.COM * since some callers will send a packet to conn_ip_output() even if
208012328SSowmini.Varadhan@Sun.COM * there's an error.
208112328SSowmini.Varadhan@Sun.COM */
208212328SSowmini.Varadhan@Sun.COM ifindex = 0;
208312328SSowmini.Varadhan@Sun.COM if (IN6_IS_ADDR_LINKSCOPE(dst_addr)) {
208412328SSowmini.Varadhan@Sun.COM /* If we are creating a DCE we'd better have an ifindex */
208512328SSowmini.Varadhan@Sun.COM if (ill != NULL)
208612328SSowmini.Varadhan@Sun.COM ifindex = ill->ill_phyint->phyint_ifindex;
208712328SSowmini.Varadhan@Sun.COM else
208812328SSowmini.Varadhan@Sun.COM flags &= ~IPDF_UNIQUE_DCE;
208912328SSowmini.Varadhan@Sun.COM }
209012328SSowmini.Varadhan@Sun.COM
209112328SSowmini.Varadhan@Sun.COM if (flags & IPDF_UNIQUE_DCE) {
209212328SSowmini.Varadhan@Sun.COM /* Fallback to the default dce if allocation fails */
209312328SSowmini.Varadhan@Sun.COM dce = dce_lookup_and_add_v6(dst_addr, ifindex, ipst);
209412328SSowmini.Varadhan@Sun.COM if (dce != NULL) {
209512328SSowmini.Varadhan@Sun.COM generation = dce->dce_generation;
209612328SSowmini.Varadhan@Sun.COM } else {
209712328SSowmini.Varadhan@Sun.COM dce = dce_lookup_v6(dst_addr, ifindex, ipst,
209812328SSowmini.Varadhan@Sun.COM &generation);
209912328SSowmini.Varadhan@Sun.COM }
210012328SSowmini.Varadhan@Sun.COM } else {
210112328SSowmini.Varadhan@Sun.COM dce = dce_lookup_v6(dst_addr, ifindex, ipst, &generation);
210212328SSowmini.Varadhan@Sun.COM }
210312328SSowmini.Varadhan@Sun.COM ASSERT(dce != NULL);
210412328SSowmini.Varadhan@Sun.COM if (ixa->ixa_dce != NULL)
210512328SSowmini.Varadhan@Sun.COM dce_refrele_notr(ixa->ixa_dce);
210612328SSowmini.Varadhan@Sun.COM #ifdef DEBUG
210712328SSowmini.Varadhan@Sun.COM dce_refhold_notr(dce);
210812328SSowmini.Varadhan@Sun.COM dce_refrele(dce);
210912328SSowmini.Varadhan@Sun.COM #endif
211012328SSowmini.Varadhan@Sun.COM ixa->ixa_dce = dce;
211112328SSowmini.Varadhan@Sun.COM ixa->ixa_dce_generation = generation;
211212328SSowmini.Varadhan@Sun.COM
211312328SSowmini.Varadhan@Sun.COM
211412328SSowmini.Varadhan@Sun.COM /*
211511042SErik.Nordmark@Sun.COM * For multicast with multirt we have a flag passed back from
211611042SErik.Nordmark@Sun.COM * ire_lookup_multi_ill_v6 since we don't have an IRE for each
211711042SErik.Nordmark@Sun.COM * possible multicast address.
211811042SErik.Nordmark@Sun.COM * We also need a flag for multicast since we can't check
211911042SErik.Nordmark@Sun.COM * whether RTF_MULTIRT is set in ixa_ire for multicast.
212011042SErik.Nordmark@Sun.COM */
212111042SErik.Nordmark@Sun.COM if (multirt) {
212211042SErik.Nordmark@Sun.COM ixa->ixa_postfragfn = ip_postfrag_multirt_v6;
212311042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_MULTIRT_MULTICAST;
212411042SErik.Nordmark@Sun.COM } else {
212511042SErik.Nordmark@Sun.COM ixa->ixa_postfragfn = ire->ire_postfragfn;
212611042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_MULTIRT_MULTICAST;
212711042SErik.Nordmark@Sun.COM }
212811042SErik.Nordmark@Sun.COM if (!(ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE))) {
212911042SErik.Nordmark@Sun.COM /* Get an nce to cache. */
213011042SErik.Nordmark@Sun.COM nce = ire_to_nce(ire, NULL, firsthop);
213111042SErik.Nordmark@Sun.COM if (nce == NULL) {
213211042SErik.Nordmark@Sun.COM /* Allocation failure? */
213311042SErik.Nordmark@Sun.COM ixa->ixa_ire_generation = IRE_GENERATION_VERIFY;
21340Sstevel@tonic-gate } else {
213511042SErik.Nordmark@Sun.COM if (ixa->ixa_nce != NULL)
213611042SErik.Nordmark@Sun.COM nce_refrele(ixa->ixa_nce);
213711042SErik.Nordmark@Sun.COM ixa->ixa_nce = nce;
21380Sstevel@tonic-gate }
213911042SErik.Nordmark@Sun.COM }
214011042SErik.Nordmark@Sun.COM
214111042SErik.Nordmark@Sun.COM /*
214211042SErik.Nordmark@Sun.COM * If the source address is a loopback address, the
214311042SErik.Nordmark@Sun.COM * destination had best be local or multicast.
214411042SErik.Nordmark@Sun.COM * If we are sending to an IRE_LOCAL using a loopback source then
214511042SErik.Nordmark@Sun.COM * it had better be the same zoneid.
214611042SErik.Nordmark@Sun.COM */
214711042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_LOOPBACK(src_addrp)) {
214811042SErik.Nordmark@Sun.COM if ((ire->ire_type & IRE_LOCAL) && ire->ire_zoneid != zoneid) {
214911042SErik.Nordmark@Sun.COM ire = NULL; /* Stored in ixa_ire */
215011042SErik.Nordmark@Sun.COM error = EADDRNOTAVAIL;
215111042SErik.Nordmark@Sun.COM goto bad_addr;
215211042SErik.Nordmark@Sun.COM }
215311042SErik.Nordmark@Sun.COM if (!(ire->ire_type & (IRE_LOOPBACK|IRE_LOCAL|IRE_MULTICAST))) {
215411042SErik.Nordmark@Sun.COM ire = NULL; /* Stored in ixa_ire */
215511042SErik.Nordmark@Sun.COM error = EADDRNOTAVAIL;
21560Sstevel@tonic-gate goto bad_addr;
21570Sstevel@tonic-gate }
21580Sstevel@tonic-gate }
21590Sstevel@tonic-gate
21600Sstevel@tonic-gate /*
216111042SErik.Nordmark@Sun.COM * Does the caller want us to pick a source address?
21620Sstevel@tonic-gate */
216311042SErik.Nordmark@Sun.COM if (flags & IPDF_SELECT_SRC) {
216411042SErik.Nordmark@Sun.COM in6_addr_t src_addr;
216511042SErik.Nordmark@Sun.COM
216611131SErik.Nordmark@Sun.COM /*
216711131SErik.Nordmark@Sun.COM * We use use ire_nexthop_ill to avoid the under ipmp
216811131SErik.Nordmark@Sun.COM * interface for source address selection. Note that for ipmp
216911131SErik.Nordmark@Sun.COM * probe packets, ixa_ifindex would have been specified, and
217011131SErik.Nordmark@Sun.COM * the ip_select_route() invocation would have picked an ire
217111131SErik.Nordmark@Sun.COM * will ire_ill pointing at an under interface.
217211131SErik.Nordmark@Sun.COM */
217311131SErik.Nordmark@Sun.COM ill = ire_nexthop_ill(ire);
217411131SErik.Nordmark@Sun.COM
217511042SErik.Nordmark@Sun.COM /* If unreachable we have no ill but need some source */
217611042SErik.Nordmark@Sun.COM if (ill == NULL) {
217711042SErik.Nordmark@Sun.COM src_addr = ipv6_loopback;
217811042SErik.Nordmark@Sun.COM /* Make sure we look for a better source address */
217911042SErik.Nordmark@Sun.COM generation = SRC_GENERATION_VERIFY;
218011042SErik.Nordmark@Sun.COM } else {
218111042SErik.Nordmark@Sun.COM error = ip_select_source_v6(ill, &setsrc, dst_addr,
218211042SErik.Nordmark@Sun.COM zoneid, ipst, B_FALSE, ixa->ixa_src_preferences,
218311042SErik.Nordmark@Sun.COM &src_addr, &generation, NULL);
218411042SErik.Nordmark@Sun.COM if (error != 0) {
218511042SErik.Nordmark@Sun.COM ire = NULL; /* Stored in ixa_ire */
21860Sstevel@tonic-gate goto bad_addr;
21870Sstevel@tonic-gate }
21880Sstevel@tonic-gate }
218911042SErik.Nordmark@Sun.COM
21900Sstevel@tonic-gate /*
219111042SErik.Nordmark@Sun.COM * We allow the source address to to down.
219211042SErik.Nordmark@Sun.COM * However, we check that we don't use the loopback address
219311042SErik.Nordmark@Sun.COM * as a source when sending out on the wire.
21940Sstevel@tonic-gate */
219511042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_LOOPBACK(&src_addr) &&
219611042SErik.Nordmark@Sun.COM !(ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK|IRE_MULTICAST)) &&
219711042SErik.Nordmark@Sun.COM !(ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE))) {
219811042SErik.Nordmark@Sun.COM ire = NULL; /* Stored in ixa_ire */
219911042SErik.Nordmark@Sun.COM error = EADDRNOTAVAIL;
22008348SEric.Yu@Sun.COM goto bad_addr;
22018348SEric.Yu@Sun.COM }
220211042SErik.Nordmark@Sun.COM
220311042SErik.Nordmark@Sun.COM *src_addrp = src_addr;
220411042SErik.Nordmark@Sun.COM ixa->ixa_src_generation = generation;
22050Sstevel@tonic-gate }
22060Sstevel@tonic-gate
22070Sstevel@tonic-gate /*
220811042SErik.Nordmark@Sun.COM * Make sure we don't leave an unreachable ixa_nce in place
220911042SErik.Nordmark@Sun.COM * since ip_select_route is used when we unplumb i.e., remove
221011042SErik.Nordmark@Sun.COM * references on ixa_ire, ixa_nce, and ixa_dce.
22110Sstevel@tonic-gate */
221211042SErik.Nordmark@Sun.COM nce = ixa->ixa_nce;
221311042SErik.Nordmark@Sun.COM if (nce != NULL && nce->nce_is_condemned) {
221411042SErik.Nordmark@Sun.COM nce_refrele(nce);
221511042SErik.Nordmark@Sun.COM ixa->ixa_nce = NULL;
221611042SErik.Nordmark@Sun.COM ixa->ixa_ire_generation = IRE_GENERATION_VERIFY;
221711042SErik.Nordmark@Sun.COM }
221811042SErik.Nordmark@Sun.COM
22190Sstevel@tonic-gate /*
222011042SErik.Nordmark@Sun.COM * Note that IPv6 multicast supports PMTU discovery unlike IPv4
222111042SErik.Nordmark@Sun.COM * multicast. But pmtu discovery is only enabled for connected
222211042SErik.Nordmark@Sun.COM * sockets in general.
22230Sstevel@tonic-gate */
22240Sstevel@tonic-gate
22250Sstevel@tonic-gate /*
222611042SErik.Nordmark@Sun.COM * Set initial value for fragmentation limit. Either conn_ip_output
222711042SErik.Nordmark@Sun.COM * or ULP might updates it when there are routing changes.
222811042SErik.Nordmark@Sun.COM * Handles a NULL ixa_ire->ire_ill or a NULL ixa_nce for RTF_REJECT.
22290Sstevel@tonic-gate */
223011042SErik.Nordmark@Sun.COM pmtu = ip_get_pmtu(ixa);
223111042SErik.Nordmark@Sun.COM ixa->ixa_fragsize = pmtu;
223211042SErik.Nordmark@Sun.COM /* Make sure ixa_fragsize and ixa_pmtu remain identical */
223311042SErik.Nordmark@Sun.COM if (ixa->ixa_flags & IXAF_VERIFY_PMTU)
223411042SErik.Nordmark@Sun.COM ixa->ixa_pmtu = pmtu;
22358348SEric.Yu@Sun.COM
22368348SEric.Yu@Sun.COM /*
223711042SErik.Nordmark@Sun.COM * Extract information useful for some transports.
223811042SErik.Nordmark@Sun.COM * First we look for DCE metrics. Then we take what we have in
223911042SErik.Nordmark@Sun.COM * the metrics in the route, where the offlink is used if we have
224011042SErik.Nordmark@Sun.COM * one.
22418348SEric.Yu@Sun.COM */
224211042SErik.Nordmark@Sun.COM if (uinfo != NULL) {
224311042SErik.Nordmark@Sun.COM bzero(uinfo, sizeof (*uinfo));
224411042SErik.Nordmark@Sun.COM
224511042SErik.Nordmark@Sun.COM if (dce->dce_flags & DCEF_UINFO)
224611042SErik.Nordmark@Sun.COM *uinfo = dce->dce_uinfo;
224711042SErik.Nordmark@Sun.COM
224811042SErik.Nordmark@Sun.COM rts_merge_metrics(uinfo, &ire->ire_metrics);
224911042SErik.Nordmark@Sun.COM
225011042SErik.Nordmark@Sun.COM /* Allow ire_metrics to decrease the path MTU from above */
225111042SErik.Nordmark@Sun.COM if (uinfo->iulp_mtu == 0 || uinfo->iulp_mtu > pmtu)
225211042SErik.Nordmark@Sun.COM uinfo->iulp_mtu = pmtu;
225311042SErik.Nordmark@Sun.COM
225411042SErik.Nordmark@Sun.COM uinfo->iulp_localnet = (ire->ire_type & IRE_ONLINK) != 0;
225511042SErik.Nordmark@Sun.COM uinfo->iulp_loopback = (ire->ire_type & IRE_LOOPBACK) != 0;
225611042SErik.Nordmark@Sun.COM uinfo->iulp_local = (ire->ire_type & IRE_LOCAL) != 0;
22578348SEric.Yu@Sun.COM }
22588348SEric.Yu@Sun.COM
225911042SErik.Nordmark@Sun.COM if (ill != NULL)
226011042SErik.Nordmark@Sun.COM ill_refrele(ill);
226111042SErik.Nordmark@Sun.COM
226211042SErik.Nordmark@Sun.COM return (error);
22638348SEric.Yu@Sun.COM
22648348SEric.Yu@Sun.COM bad_addr:
226511042SErik.Nordmark@Sun.COM if (ire != NULL)
226611042SErik.Nordmark@Sun.COM ire_refrele(ire);
226711042SErik.Nordmark@Sun.COM
226811042SErik.Nordmark@Sun.COM if (ill != NULL)
226911042SErik.Nordmark@Sun.COM ill_refrele(ill);
227011042SErik.Nordmark@Sun.COM
227111042SErik.Nordmark@Sun.COM /*
227211042SErik.Nordmark@Sun.COM * Make sure we don't leave an unreachable ixa_nce in place
227311042SErik.Nordmark@Sun.COM * since ip_select_route is used when we unplumb i.e., remove
227411042SErik.Nordmark@Sun.COM * references on ixa_ire, ixa_nce, and ixa_dce.
227511042SErik.Nordmark@Sun.COM */
227611042SErik.Nordmark@Sun.COM nce = ixa->ixa_nce;
227711042SErik.Nordmark@Sun.COM if (nce != NULL && nce->nce_is_condemned) {
227811042SErik.Nordmark@Sun.COM nce_refrele(nce);
227911042SErik.Nordmark@Sun.COM ixa->ixa_nce = NULL;
228011042SErik.Nordmark@Sun.COM ixa->ixa_ire_generation = IRE_GENERATION_VERIFY;
22810Sstevel@tonic-gate }
228211042SErik.Nordmark@Sun.COM
228311042SErik.Nordmark@Sun.COM return (error);
22840Sstevel@tonic-gate }
22850Sstevel@tonic-gate
22860Sstevel@tonic-gate /*
22870Sstevel@tonic-gate * Handle protocols with which IP is less intimate. There
22880Sstevel@tonic-gate * can be more than one stream bound to a particular
22890Sstevel@tonic-gate * protocol. When this is the case, normally each one gets a copy
22900Sstevel@tonic-gate * of any incoming packets.
22910Sstevel@tonic-gate *
22920Sstevel@tonic-gate * Zones notes:
229311042SErik.Nordmark@Sun.COM * Packets will be distributed to conns in all zones. This is really only
22940Sstevel@tonic-gate * useful for ICMPv6 as only applications in the global zone can create raw
22950Sstevel@tonic-gate * sockets for other protocols.
22960Sstevel@tonic-gate */
229711042SErik.Nordmark@Sun.COM void
ip_fanout_proto_v6(mblk_t * mp,ip6_t * ip6h,ip_recv_attr_t * ira)229811042SErik.Nordmark@Sun.COM ip_fanout_proto_v6(mblk_t *mp, ip6_t *ip6h, ip_recv_attr_t *ira)
22990Sstevel@tonic-gate {
230011042SErik.Nordmark@Sun.COM mblk_t *mp1;
230111042SErik.Nordmark@Sun.COM in6_addr_t laddr = ip6h->ip6_dst;
230211042SErik.Nordmark@Sun.COM conn_t *connp, *first_connp, *next_connp;
230311042SErik.Nordmark@Sun.COM connf_t *connfp;
230411042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill;
230511042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst;
230611042SErik.Nordmark@Sun.COM
230711042SErik.Nordmark@Sun.COM connfp = &ipst->ips_ipcl_proto_fanout_v6[ira->ira_protocol];
23080Sstevel@tonic-gate mutex_enter(&connfp->connf_lock);
23090Sstevel@tonic-gate connp = connfp->connf_head;
23100Sstevel@tonic-gate for (connp = connfp->connf_head; connp != NULL;
23114459Skcpoon connp = connp->conn_next) {
231211042SErik.Nordmark@Sun.COM /* Note: IPCL_PROTO_MATCH_V6 includes conn_wantpacket */
231311042SErik.Nordmark@Sun.COM if (IPCL_PROTO_MATCH_V6(connp, ira, ip6h) &&
231411042SErik.Nordmark@Sun.COM (!(ira->ira_flags & IRAF_SYSTEM_LABELED) ||
231511042SErik.Nordmark@Sun.COM tsol_receive_local(mp, &laddr, IPV6_VERSION, ira, connp)))
23160Sstevel@tonic-gate break;
23170Sstevel@tonic-gate }
23180Sstevel@tonic-gate
23198348SEric.Yu@Sun.COM if (connp == NULL) {
23200Sstevel@tonic-gate /*
23210Sstevel@tonic-gate * No one bound to this port. Is
23220Sstevel@tonic-gate * there a client that wants all
23230Sstevel@tonic-gate * unclaimed datagrams?
23240Sstevel@tonic-gate */
23250Sstevel@tonic-gate mutex_exit(&connfp->connf_lock);
232611042SErik.Nordmark@Sun.COM ip_fanout_send_icmp_v6(mp, ICMP6_PARAM_PROB,
232711042SErik.Nordmark@Sun.COM ICMP6_PARAMPROB_NEXTHEADER, ira);
23280Sstevel@tonic-gate return;
23290Sstevel@tonic-gate }
23300Sstevel@tonic-gate
233111042SErik.Nordmark@Sun.COM ASSERT(IPCL_IS_NONSTR(connp) || connp->conn_rq != NULL);
23328348SEric.Yu@Sun.COM
23330Sstevel@tonic-gate CONN_INC_REF(connp);
23340Sstevel@tonic-gate first_connp = connp;
23350Sstevel@tonic-gate
23360Sstevel@tonic-gate /*
23370Sstevel@tonic-gate * XXX: Fix the multiple protocol listeners case. We should not
233811042SErik.Nordmark@Sun.COM * be walking the conn->conn_next list here.
23390Sstevel@tonic-gate */
234010616SSebastien.Roy@Sun.COM connp = connp->conn_next;
23410Sstevel@tonic-gate for (;;) {
23420Sstevel@tonic-gate while (connp != NULL) {
234311042SErik.Nordmark@Sun.COM /* Note: IPCL_PROTO_MATCH_V6 includes conn_wantpacket */
234411042SErik.Nordmark@Sun.COM if (IPCL_PROTO_MATCH_V6(connp, ira, ip6h) &&
234511042SErik.Nordmark@Sun.COM (!(ira->ira_flags & IRAF_SYSTEM_LABELED) ||
234611042SErik.Nordmark@Sun.COM tsol_receive_local(mp, &laddr, IPV6_VERSION,
234711042SErik.Nordmark@Sun.COM ira, connp)))
23480Sstevel@tonic-gate break;
23490Sstevel@tonic-gate connp = connp->conn_next;
23500Sstevel@tonic-gate }
23510Sstevel@tonic-gate
235211042SErik.Nordmark@Sun.COM if (connp == NULL) {
235311042SErik.Nordmark@Sun.COM /* No more interested clients */
23540Sstevel@tonic-gate connp = first_connp;
23550Sstevel@tonic-gate break;
23560Sstevel@tonic-gate }
235711042SErik.Nordmark@Sun.COM if (((mp1 = dupmsg(mp)) == NULL) &&
235811042SErik.Nordmark@Sun.COM ((mp1 = copymsg(mp)) == NULL)) {
235911042SErik.Nordmark@Sun.COM /* Memory allocation failed */
236011042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
236111042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards", mp, ill);
236211042SErik.Nordmark@Sun.COM connp = first_connp;
236311042SErik.Nordmark@Sun.COM break;
236411042SErik.Nordmark@Sun.COM }
236511042SErik.Nordmark@Sun.COM
23660Sstevel@tonic-gate CONN_INC_REF(connp);
23670Sstevel@tonic-gate mutex_exit(&connfp->connf_lock);
236811042SErik.Nordmark@Sun.COM
236911042SErik.Nordmark@Sun.COM ip_fanout_proto_conn(connp, mp1, NULL, (ip6_t *)mp1->b_rptr,
237011042SErik.Nordmark@Sun.COM ira);
237111042SErik.Nordmark@Sun.COM
23720Sstevel@tonic-gate mutex_enter(&connfp->connf_lock);
23730Sstevel@tonic-gate /* Follow the next pointer before releasing the conn. */
23740Sstevel@tonic-gate next_connp = connp->conn_next;
23750Sstevel@tonic-gate CONN_DEC_REF(connp);
23760Sstevel@tonic-gate connp = next_connp;
23770Sstevel@tonic-gate }
23780Sstevel@tonic-gate
23790Sstevel@tonic-gate /* Last one. Send it upstream. */
23800Sstevel@tonic-gate mutex_exit(&connfp->connf_lock);
23810Sstevel@tonic-gate
238211042SErik.Nordmark@Sun.COM ip_fanout_proto_conn(connp, mp, NULL, ip6h, ira);
238311042SErik.Nordmark@Sun.COM
23840Sstevel@tonic-gate CONN_DEC_REF(connp);
23850Sstevel@tonic-gate }
23860Sstevel@tonic-gate
23870Sstevel@tonic-gate /*
238811042SErik.Nordmark@Sun.COM * Called when it is conceptually a ULP that would sent the packet
238911042SErik.Nordmark@Sun.COM * e.g., port unreachable and nexthdr unknown. Check that the packet
239011042SErik.Nordmark@Sun.COM * would have passed the IPsec global policy before sending the error.
239111042SErik.Nordmark@Sun.COM *
239211042SErik.Nordmark@Sun.COM * Send an ICMP error after patching up the packet appropriately.
239311042SErik.Nordmark@Sun.COM * Uses ip_drop_input and bumps the appropriate MIB.
239411042SErik.Nordmark@Sun.COM * For ICMP6_PARAMPROB_NEXTHEADER we determine the offset to use.
23950Sstevel@tonic-gate */
239611042SErik.Nordmark@Sun.COM void
ip_fanout_send_icmp_v6(mblk_t * mp,uint_t icmp_type,uint8_t icmp_code,ip_recv_attr_t * ira)239711042SErik.Nordmark@Sun.COM ip_fanout_send_icmp_v6(mblk_t *mp, uint_t icmp_type, uint8_t icmp_code,
239811042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira)
23990Sstevel@tonic-gate {
240011042SErik.Nordmark@Sun.COM ip6_t *ip6h;
240111042SErik.Nordmark@Sun.COM boolean_t secure;
240211042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill;
240311042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst;
240411042SErik.Nordmark@Sun.COM netstack_t *ns = ipst->ips_netstack;
240511042SErik.Nordmark@Sun.COM ipsec_stack_t *ipss = ns->netstack_ipsec;
240611042SErik.Nordmark@Sun.COM
240711042SErik.Nordmark@Sun.COM secure = ira->ira_flags & IRAF_IPSEC_SECURE;
240811042SErik.Nordmark@Sun.COM
24090Sstevel@tonic-gate /*
24100Sstevel@tonic-gate * We are generating an icmp error for some inbound packet.
24110Sstevel@tonic-gate * Called from all ip_fanout_(udp, tcp, proto) functions.
24120Sstevel@tonic-gate * Before we generate an error, check with global policy
24130Sstevel@tonic-gate * to see whether this is allowed to enter the system. As
24140Sstevel@tonic-gate * there is no "conn", we are checking with global policy.
24150Sstevel@tonic-gate */
24160Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr;
24173448Sdh155122 if (secure || ipss->ipsec_inbound_v6_policy_present) {
241811042SErik.Nordmark@Sun.COM mp = ipsec_check_global_policy(mp, NULL, NULL, ip6h, ira, ns);
241911042SErik.Nordmark@Sun.COM if (mp == NULL)
242011042SErik.Nordmark@Sun.COM return;
24210Sstevel@tonic-gate }
24220Sstevel@tonic-gate
242311042SErik.Nordmark@Sun.COM /* We never send errors for protocols that we do implement */
242411042SErik.Nordmark@Sun.COM if (ira->ira_protocol == IPPROTO_ICMPV6) {
242511042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
242611042SErik.Nordmark@Sun.COM ip_drop_input("ip_fanout_send_icmp_v6", mp, ill);
242711042SErik.Nordmark@Sun.COM freemsg(mp);
24280Sstevel@tonic-gate return;
24290Sstevel@tonic-gate }
24300Sstevel@tonic-gate
243111042SErik.Nordmark@Sun.COM switch (icmp_type) {
243211042SErik.Nordmark@Sun.COM case ICMP6_DST_UNREACH:
243311042SErik.Nordmark@Sun.COM ASSERT(icmp_code == ICMP6_DST_UNREACH_NOPORT);
243411042SErik.Nordmark@Sun.COM
243511042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, udpIfStatsNoPorts);
243611042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsNoPorts", mp, ill);
243711042SErik.Nordmark@Sun.COM
243811042SErik.Nordmark@Sun.COM icmp_unreachable_v6(mp, icmp_code, B_FALSE, ira);
243911042SErik.Nordmark@Sun.COM break;
244011042SErik.Nordmark@Sun.COM case ICMP6_PARAM_PROB:
244111042SErik.Nordmark@Sun.COM ASSERT(icmp_code == ICMP6_PARAMPROB_NEXTHEADER);
244211042SErik.Nordmark@Sun.COM
244311042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInUnknownProtos);
244411042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInUnknownProtos", mp, ill);
244511042SErik.Nordmark@Sun.COM
244611042SErik.Nordmark@Sun.COM /* Let the system determine the offset for this one */
244711042SErik.Nordmark@Sun.COM icmp_param_problem_nexthdr_v6(mp, B_FALSE, ira);
244811042SErik.Nordmark@Sun.COM break;
244911042SErik.Nordmark@Sun.COM default:
245011042SErik.Nordmark@Sun.COM #ifdef DEBUG
245111042SErik.Nordmark@Sun.COM panic("ip_fanout_send_icmp_v6: wrong type");
245211042SErik.Nordmark@Sun.COM /*NOTREACHED*/
245311042SErik.Nordmark@Sun.COM #else
245411042SErik.Nordmark@Sun.COM freemsg(mp);
245511042SErik.Nordmark@Sun.COM break;
245611042SErik.Nordmark@Sun.COM #endif
24570Sstevel@tonic-gate }
24580Sstevel@tonic-gate }
24590Sstevel@tonic-gate
24600Sstevel@tonic-gate /*
246111042SErik.Nordmark@Sun.COM * Fanout for UDP packets that are multicast or ICMP errors.
246211042SErik.Nordmark@Sun.COM * (Unicast fanout is handled in ip_input_v6.)
246311042SErik.Nordmark@Sun.COM *
246411042SErik.Nordmark@Sun.COM * If SO_REUSEADDR is set all multicast packets
246511042SErik.Nordmark@Sun.COM * will be delivered to all conns bound to the same port.
246611042SErik.Nordmark@Sun.COM *
24670Sstevel@tonic-gate * Fanout for UDP packets.
24680Sstevel@tonic-gate * The caller puts <fport, lport> in the ports parameter.
24690Sstevel@tonic-gate * ire_type must be IRE_BROADCAST for multicast and broadcast packets.
24700Sstevel@tonic-gate *
24710Sstevel@tonic-gate * If SO_REUSEADDR is set all multicast and broadcast packets
247211042SErik.Nordmark@Sun.COM * will be delivered to all conns bound to the same port.
24730Sstevel@tonic-gate *
24740Sstevel@tonic-gate * Zones notes:
247511042SErik.Nordmark@Sun.COM * Earlier in ip_input on a system with multiple shared-IP zones we
247611042SErik.Nordmark@Sun.COM * duplicate the multicast and broadcast packets and send them up
247711042SErik.Nordmark@Sun.COM * with each explicit zoneid that exists on that ill.
247811042SErik.Nordmark@Sun.COM * This means that here we can match the zoneid with SO_ALLZONES being special.
24790Sstevel@tonic-gate */
248011042SErik.Nordmark@Sun.COM void
ip_fanout_udp_multi_v6(mblk_t * mp,ip6_t * ip6h,uint16_t lport,uint16_t fport,ip_recv_attr_t * ira)248111042SErik.Nordmark@Sun.COM ip_fanout_udp_multi_v6(mblk_t *mp, ip6_t *ip6h, uint16_t lport, uint16_t fport,
248211042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira)
24830Sstevel@tonic-gate {
248411042SErik.Nordmark@Sun.COM in6_addr_t laddr;
24850Sstevel@tonic-gate conn_t *connp;
24860Sstevel@tonic-gate connf_t *connfp;
248711042SErik.Nordmark@Sun.COM in6_addr_t faddr;
248811042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill;
248911042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst;
249011042SErik.Nordmark@Sun.COM
249111042SErik.Nordmark@Sun.COM ASSERT(ira->ira_flags & (IRAF_MULTIBROADCAST|IRAF_ICMP_ERROR));
249211042SErik.Nordmark@Sun.COM
249311042SErik.Nordmark@Sun.COM laddr = ip6h->ip6_dst;
249411042SErik.Nordmark@Sun.COM faddr = ip6h->ip6_src;
24951676Sjpk
24960Sstevel@tonic-gate /* Attempt to find a client stream based on destination port. */
249711042SErik.Nordmark@Sun.COM connfp = &ipst->ips_ipcl_udp_fanout[IPCL_UDP_HASH(lport, ipst)];
24980Sstevel@tonic-gate mutex_enter(&connfp->connf_lock);
24990Sstevel@tonic-gate connp = connfp->connf_head;
25000Sstevel@tonic-gate while (connp != NULL) {
250111042SErik.Nordmark@Sun.COM if ((IPCL_UDP_MATCH_V6(connp, lport, laddr, fport, faddr)) &&
250211042SErik.Nordmark@Sun.COM conn_wantpacket_v6(connp, ira, ip6h) &&
250311042SErik.Nordmark@Sun.COM (!(ira->ira_flags & IRAF_SYSTEM_LABELED) ||
250411042SErik.Nordmark@Sun.COM tsol_receive_local(mp, &laddr, IPV6_VERSION, ira, connp)))
25050Sstevel@tonic-gate break;
25060Sstevel@tonic-gate connp = connp->conn_next;
25070Sstevel@tonic-gate }
25080Sstevel@tonic-gate
250911042SErik.Nordmark@Sun.COM if (connp == NULL)
25100Sstevel@tonic-gate goto notfound;
25110Sstevel@tonic-gate
25120Sstevel@tonic-gate CONN_INC_REF(connp);
251311042SErik.Nordmark@Sun.COM
251411042SErik.Nordmark@Sun.COM if (connp->conn_reuseaddr) {
251511042SErik.Nordmark@Sun.COM conn_t *first_connp = connp;
251611042SErik.Nordmark@Sun.COM conn_t *next_connp;
251711042SErik.Nordmark@Sun.COM mblk_t *mp1;
251811042SErik.Nordmark@Sun.COM
251911042SErik.Nordmark@Sun.COM connp = connp->conn_next;
252011042SErik.Nordmark@Sun.COM for (;;) {
252111042SErik.Nordmark@Sun.COM while (connp != NULL) {
252211042SErik.Nordmark@Sun.COM if (IPCL_UDP_MATCH_V6(connp, lport, laddr,
252311042SErik.Nordmark@Sun.COM fport, faddr) &&
252411042SErik.Nordmark@Sun.COM conn_wantpacket_v6(connp, ira, ip6h) &&
252511042SErik.Nordmark@Sun.COM (!(ira->ira_flags & IRAF_SYSTEM_LABELED) ||
252611042SErik.Nordmark@Sun.COM tsol_receive_local(mp, &laddr, IPV6_VERSION,
252711042SErik.Nordmark@Sun.COM ira, connp)))
252811042SErik.Nordmark@Sun.COM break;
252911042SErik.Nordmark@Sun.COM connp = connp->conn_next;
253011042SErik.Nordmark@Sun.COM }
253111042SErik.Nordmark@Sun.COM if (connp == NULL) {
253211042SErik.Nordmark@Sun.COM /* No more interested clients */
253311042SErik.Nordmark@Sun.COM connp = first_connp;
25340Sstevel@tonic-gate break;
253511042SErik.Nordmark@Sun.COM }
253611042SErik.Nordmark@Sun.COM if (((mp1 = dupmsg(mp)) == NULL) &&
253711042SErik.Nordmark@Sun.COM ((mp1 = copymsg(mp)) == NULL)) {
253811042SErik.Nordmark@Sun.COM /* Memory allocation failed */
253911042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
254011042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards", mp, ill);
254111042SErik.Nordmark@Sun.COM connp = first_connp;
254211042SErik.Nordmark@Sun.COM break;
254311042SErik.Nordmark@Sun.COM }
254411042SErik.Nordmark@Sun.COM
254511042SErik.Nordmark@Sun.COM CONN_INC_REF(connp);
254611042SErik.Nordmark@Sun.COM mutex_exit(&connfp->connf_lock);
254711042SErik.Nordmark@Sun.COM
254811042SErik.Nordmark@Sun.COM IP6_STAT(ipst, ip6_udp_fanmb);
254911042SErik.Nordmark@Sun.COM ip_fanout_udp_conn(connp, mp1, NULL,
255011042SErik.Nordmark@Sun.COM (ip6_t *)mp1->b_rptr, ira);
255111042SErik.Nordmark@Sun.COM
255211042SErik.Nordmark@Sun.COM mutex_enter(&connfp->connf_lock);
255311042SErik.Nordmark@Sun.COM /* Follow the next pointer before releasing the conn. */
255411042SErik.Nordmark@Sun.COM next_connp = connp->conn_next;
255511042SErik.Nordmark@Sun.COM IP6_STAT(ipst, ip6_udp_fanmb);
255611042SErik.Nordmark@Sun.COM CONN_DEC_REF(connp);
255711042SErik.Nordmark@Sun.COM connp = next_connp;
25580Sstevel@tonic-gate }
25590Sstevel@tonic-gate }
25600Sstevel@tonic-gate
25610Sstevel@tonic-gate /* Last one. Send it upstream. */
25620Sstevel@tonic-gate mutex_exit(&connfp->connf_lock);
25630Sstevel@tonic-gate
25643448Sdh155122 IP6_STAT(ipst, ip6_udp_fanmb);
256511042SErik.Nordmark@Sun.COM ip_fanout_udp_conn(connp, mp, NULL, ip6h, ira);
25660Sstevel@tonic-gate CONN_DEC_REF(connp);
25670Sstevel@tonic-gate return;
25680Sstevel@tonic-gate
25690Sstevel@tonic-gate notfound:
25700Sstevel@tonic-gate mutex_exit(&connfp->connf_lock);
25710Sstevel@tonic-gate /*
25720Sstevel@tonic-gate * No one bound to this port. Is
25730Sstevel@tonic-gate * there a client that wants all
25740Sstevel@tonic-gate * unclaimed datagrams?
25750Sstevel@tonic-gate */
25763448Sdh155122 if (ipst->ips_ipcl_proto_fanout_v6[IPPROTO_UDP].connf_head != NULL) {
257711042SErik.Nordmark@Sun.COM ASSERT(ira->ira_protocol == IPPROTO_UDP);
257811042SErik.Nordmark@Sun.COM ip_fanout_proto_v6(mp, ip6h, ira);
25790Sstevel@tonic-gate } else {
258011042SErik.Nordmark@Sun.COM ip_fanout_send_icmp_v6(mp, ICMP6_DST_UNREACH,
258111042SErik.Nordmark@Sun.COM ICMP6_DST_UNREACH_NOPORT, ira);
25820Sstevel@tonic-gate }
25830Sstevel@tonic-gate }
25840Sstevel@tonic-gate
25850Sstevel@tonic-gate /*
25860Sstevel@tonic-gate * int ip_find_hdr_v6()
25870Sstevel@tonic-gate *
258811042SErik.Nordmark@Sun.COM * This routine is used by the upper layer protocols, iptun, and IPsec:
25890Sstevel@tonic-gate * - Set extension header pointers to appropriate locations
25900Sstevel@tonic-gate * - Determine IPv6 header length and return it
25910Sstevel@tonic-gate * - Return a pointer to the last nexthdr value
25920Sstevel@tonic-gate *
25930Sstevel@tonic-gate * The caller must initialize ipp_fields.
259411042SErik.Nordmark@Sun.COM * The upper layer protocols normally set label_separate which makes the
259511042SErik.Nordmark@Sun.COM * routine put the TX label in ipp_label_v6. If this is not set then
259611042SErik.Nordmark@Sun.COM * the hop-by-hop options including the label are placed in ipp_hopopts.
25970Sstevel@tonic-gate *
25980Sstevel@tonic-gate * NOTE: If multiple extension headers of the same type are present,
25990Sstevel@tonic-gate * ip_find_hdr_v6() will set the respective extension header pointers
26000Sstevel@tonic-gate * to the first one that it encounters in the IPv6 header. It also
26010Sstevel@tonic-gate * skips fragment headers. This routine deals with malformed packets
26020Sstevel@tonic-gate * of various sorts in which case the returned length is up to the
26030Sstevel@tonic-gate * malformed part.
26040Sstevel@tonic-gate */
26050Sstevel@tonic-gate int
ip_find_hdr_v6(mblk_t * mp,ip6_t * ip6h,boolean_t label_separate,ip_pkt_t * ipp,uint8_t * nexthdrp)260611042SErik.Nordmark@Sun.COM ip_find_hdr_v6(mblk_t *mp, ip6_t *ip6h, boolean_t label_separate, ip_pkt_t *ipp,
260711042SErik.Nordmark@Sun.COM uint8_t *nexthdrp)
26080Sstevel@tonic-gate {
26090Sstevel@tonic-gate uint_t length, ehdrlen;
26100Sstevel@tonic-gate uint8_t nexthdr;
26110Sstevel@tonic-gate uint8_t *whereptr, *endptr;
26120Sstevel@tonic-gate ip6_dest_t *tmpdstopts;
26130Sstevel@tonic-gate ip6_rthdr_t *tmprthdr;
26140Sstevel@tonic-gate ip6_hbh_t *tmphopopts;
26150Sstevel@tonic-gate ip6_frag_t *tmpfraghdr;
26160Sstevel@tonic-gate
261711042SErik.Nordmark@Sun.COM ipp->ipp_fields |= IPPF_HOPLIMIT | IPPF_TCLASS | IPPF_ADDR;
261811042SErik.Nordmark@Sun.COM ipp->ipp_hoplimit = ip6h->ip6_hops;
261911042SErik.Nordmark@Sun.COM ipp->ipp_tclass = IPV6_FLOW_TCLASS(ip6h->ip6_flow);
262011042SErik.Nordmark@Sun.COM ipp->ipp_addr = ip6h->ip6_dst;
262111042SErik.Nordmark@Sun.COM
26220Sstevel@tonic-gate length = IPV6_HDR_LEN;
26230Sstevel@tonic-gate whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */
26240Sstevel@tonic-gate endptr = mp->b_wptr;
26250Sstevel@tonic-gate
26260Sstevel@tonic-gate nexthdr = ip6h->ip6_nxt;
26270Sstevel@tonic-gate while (whereptr < endptr) {
26280Sstevel@tonic-gate /* Is there enough left for len + nexthdr? */
26290Sstevel@tonic-gate if (whereptr + MIN_EHDR_LEN > endptr)
26300Sstevel@tonic-gate goto done;
26310Sstevel@tonic-gate
26320Sstevel@tonic-gate switch (nexthdr) {
263311042SErik.Nordmark@Sun.COM case IPPROTO_HOPOPTS: {
263411042SErik.Nordmark@Sun.COM /* We check for any CIPSO */
263511042SErik.Nordmark@Sun.COM uchar_t *secopt;
263611042SErik.Nordmark@Sun.COM boolean_t hbh_needed;
263711042SErik.Nordmark@Sun.COM uchar_t *after_secopt;
263811042SErik.Nordmark@Sun.COM
26390Sstevel@tonic-gate tmphopopts = (ip6_hbh_t *)whereptr;
26400Sstevel@tonic-gate ehdrlen = 8 * (tmphopopts->ip6h_len + 1);
26410Sstevel@tonic-gate if ((uchar_t *)tmphopopts + ehdrlen > endptr)
26420Sstevel@tonic-gate goto done;
26430Sstevel@tonic-gate nexthdr = tmphopopts->ip6h_nxt;
264411042SErik.Nordmark@Sun.COM
264511042SErik.Nordmark@Sun.COM if (!label_separate) {
264611042SErik.Nordmark@Sun.COM secopt = NULL;
264711042SErik.Nordmark@Sun.COM after_secopt = whereptr;
264811042SErik.Nordmark@Sun.COM } else {
264911042SErik.Nordmark@Sun.COM /*
265011042SErik.Nordmark@Sun.COM * We have dropped packets with bad options in
265111042SErik.Nordmark@Sun.COM * ip6_input. No need to check return value
265211042SErik.Nordmark@Sun.COM * here.
265311042SErik.Nordmark@Sun.COM */
265411042SErik.Nordmark@Sun.COM (void) tsol_find_secopt_v6(whereptr, ehdrlen,
265511042SErik.Nordmark@Sun.COM &secopt, &after_secopt, &hbh_needed);
265611042SErik.Nordmark@Sun.COM }
265711042SErik.Nordmark@Sun.COM if (secopt != NULL && after_secopt - whereptr > 0) {
265811042SErik.Nordmark@Sun.COM ipp->ipp_fields |= IPPF_LABEL_V6;
265911042SErik.Nordmark@Sun.COM ipp->ipp_label_v6 = secopt;
266011042SErik.Nordmark@Sun.COM ipp->ipp_label_len_v6 = after_secopt - whereptr;
266111042SErik.Nordmark@Sun.COM } else {
266211042SErik.Nordmark@Sun.COM ipp->ipp_label_len_v6 = 0;
266311042SErik.Nordmark@Sun.COM after_secopt = whereptr;
266411042SErik.Nordmark@Sun.COM hbh_needed = B_TRUE;
266511042SErik.Nordmark@Sun.COM }
26660Sstevel@tonic-gate /* return only 1st hbh */
266711042SErik.Nordmark@Sun.COM if (hbh_needed && !(ipp->ipp_fields & IPPF_HOPOPTS)) {
26680Sstevel@tonic-gate ipp->ipp_fields |= IPPF_HOPOPTS;
266911042SErik.Nordmark@Sun.COM ipp->ipp_hopopts = (ip6_hbh_t *)after_secopt;
267011042SErik.Nordmark@Sun.COM ipp->ipp_hopoptslen = ehdrlen -
267111042SErik.Nordmark@Sun.COM ipp->ipp_label_len_v6;
26720Sstevel@tonic-gate }
26730Sstevel@tonic-gate break;
267411042SErik.Nordmark@Sun.COM }
26750Sstevel@tonic-gate case IPPROTO_DSTOPTS:
26760Sstevel@tonic-gate tmpdstopts = (ip6_dest_t *)whereptr;
26770Sstevel@tonic-gate ehdrlen = 8 * (tmpdstopts->ip6d_len + 1);
26780Sstevel@tonic-gate if ((uchar_t *)tmpdstopts + ehdrlen > endptr)
26790Sstevel@tonic-gate goto done;
26800Sstevel@tonic-gate nexthdr = tmpdstopts->ip6d_nxt;
26810Sstevel@tonic-gate /*
26820Sstevel@tonic-gate * ipp_dstopts is set to the destination header after a
26830Sstevel@tonic-gate * routing header.
26840Sstevel@tonic-gate * Assume it is a post-rthdr destination header
26850Sstevel@tonic-gate * and adjust when we find an rthdr.
26860Sstevel@tonic-gate */
26870Sstevel@tonic-gate if (!(ipp->ipp_fields & IPPF_DSTOPTS)) {
26880Sstevel@tonic-gate ipp->ipp_fields |= IPPF_DSTOPTS;
26890Sstevel@tonic-gate ipp->ipp_dstopts = tmpdstopts;
26900Sstevel@tonic-gate ipp->ipp_dstoptslen = ehdrlen;
26910Sstevel@tonic-gate }
26920Sstevel@tonic-gate break;
26930Sstevel@tonic-gate case IPPROTO_ROUTING:
26940Sstevel@tonic-gate tmprthdr = (ip6_rthdr_t *)whereptr;
26950Sstevel@tonic-gate ehdrlen = 8 * (tmprthdr->ip6r_len + 1);
26960Sstevel@tonic-gate if ((uchar_t *)tmprthdr + ehdrlen > endptr)
26970Sstevel@tonic-gate goto done;
26980Sstevel@tonic-gate nexthdr = tmprthdr->ip6r_nxt;
26990Sstevel@tonic-gate /* return only 1st rthdr */
27000Sstevel@tonic-gate if (!(ipp->ipp_fields & IPPF_RTHDR)) {
27010Sstevel@tonic-gate ipp->ipp_fields |= IPPF_RTHDR;
27020Sstevel@tonic-gate ipp->ipp_rthdr = tmprthdr;
27030Sstevel@tonic-gate ipp->ipp_rthdrlen = ehdrlen;
27040Sstevel@tonic-gate }
27050Sstevel@tonic-gate /*
27060Sstevel@tonic-gate * Make any destination header we've seen be a
27070Sstevel@tonic-gate * pre-rthdr destination header.
27080Sstevel@tonic-gate */
27090Sstevel@tonic-gate if (ipp->ipp_fields & IPPF_DSTOPTS) {
27100Sstevel@tonic-gate ipp->ipp_fields &= ~IPPF_DSTOPTS;
271111042SErik.Nordmark@Sun.COM ipp->ipp_fields |= IPPF_RTHDRDSTOPTS;
271211042SErik.Nordmark@Sun.COM ipp->ipp_rthdrdstopts = ipp->ipp_dstopts;
27130Sstevel@tonic-gate ipp->ipp_dstopts = NULL;
271411042SErik.Nordmark@Sun.COM ipp->ipp_rthdrdstoptslen = ipp->ipp_dstoptslen;
27150Sstevel@tonic-gate ipp->ipp_dstoptslen = 0;
27160Sstevel@tonic-gate }
27170Sstevel@tonic-gate break;
27180Sstevel@tonic-gate case IPPROTO_FRAGMENT:
27190Sstevel@tonic-gate tmpfraghdr = (ip6_frag_t *)whereptr;
27200Sstevel@tonic-gate ehdrlen = sizeof (ip6_frag_t);
27210Sstevel@tonic-gate if ((uchar_t *)tmpfraghdr + ehdrlen > endptr)
27220Sstevel@tonic-gate goto done;
27230Sstevel@tonic-gate nexthdr = tmpfraghdr->ip6f_nxt;
27243055Sdanmcd if (!(ipp->ipp_fields & IPPF_FRAGHDR)) {
27253055Sdanmcd ipp->ipp_fields |= IPPF_FRAGHDR;
27263055Sdanmcd ipp->ipp_fraghdr = tmpfraghdr;
27273055Sdanmcd ipp->ipp_fraghdrlen = ehdrlen;
27283055Sdanmcd }
27290Sstevel@tonic-gate break;
27300Sstevel@tonic-gate case IPPROTO_NONE:
27310Sstevel@tonic-gate default:
27320Sstevel@tonic-gate goto done;
27330Sstevel@tonic-gate }
27340Sstevel@tonic-gate length += ehdrlen;
27350Sstevel@tonic-gate whereptr += ehdrlen;
27360Sstevel@tonic-gate }
27370Sstevel@tonic-gate done:
27380Sstevel@tonic-gate if (nexthdrp != NULL)
27390Sstevel@tonic-gate *nexthdrp = nexthdr;
27400Sstevel@tonic-gate return (length);
27410Sstevel@tonic-gate }
27420Sstevel@tonic-gate
27430Sstevel@tonic-gate /*
27440Sstevel@tonic-gate * Try to determine where and what are the IPv6 header length and
27450Sstevel@tonic-gate * pointer to nexthdr value for the upper layer protocol (or an
27460Sstevel@tonic-gate * unknown next hdr).
27470Sstevel@tonic-gate *
27480Sstevel@tonic-gate * Parameters returns a pointer to the nexthdr value;
27490Sstevel@tonic-gate * Must handle malformed packets of various sorts.
27500Sstevel@tonic-gate * Function returns failure for malformed cases.
27510Sstevel@tonic-gate */
27520Sstevel@tonic-gate boolean_t
ip_hdr_length_nexthdr_v6(mblk_t * mp,ip6_t * ip6h,uint16_t * hdr_length_ptr,uint8_t ** nexthdrpp)27530Sstevel@tonic-gate ip_hdr_length_nexthdr_v6(mblk_t *mp, ip6_t *ip6h, uint16_t *hdr_length_ptr,
27540Sstevel@tonic-gate uint8_t **nexthdrpp)
27550Sstevel@tonic-gate {
27560Sstevel@tonic-gate uint16_t length;
27570Sstevel@tonic-gate uint_t ehdrlen;
27580Sstevel@tonic-gate uint8_t *nexthdrp;
27590Sstevel@tonic-gate uint8_t *whereptr;
27600Sstevel@tonic-gate uint8_t *endptr;
27610Sstevel@tonic-gate ip6_dest_t *desthdr;
27620Sstevel@tonic-gate ip6_rthdr_t *rthdr;
27630Sstevel@tonic-gate ip6_frag_t *fraghdr;
27640Sstevel@tonic-gate
276511042SErik.Nordmark@Sun.COM ASSERT(IPH_HDR_VERSION(ip6h) == IPV6_VERSION);
27660Sstevel@tonic-gate length = IPV6_HDR_LEN;
27670Sstevel@tonic-gate whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */
27680Sstevel@tonic-gate endptr = mp->b_wptr;
27690Sstevel@tonic-gate
27700Sstevel@tonic-gate nexthdrp = &ip6h->ip6_nxt;
27710Sstevel@tonic-gate while (whereptr < endptr) {
27720Sstevel@tonic-gate /* Is there enough left for len + nexthdr? */
27730Sstevel@tonic-gate if (whereptr + MIN_EHDR_LEN > endptr)
27740Sstevel@tonic-gate break;
27750Sstevel@tonic-gate
27760Sstevel@tonic-gate switch (*nexthdrp) {
27770Sstevel@tonic-gate case IPPROTO_HOPOPTS:
27780Sstevel@tonic-gate case IPPROTO_DSTOPTS:
27790Sstevel@tonic-gate /* Assumes the headers are identical for hbh and dst */
27800Sstevel@tonic-gate desthdr = (ip6_dest_t *)whereptr;
27810Sstevel@tonic-gate ehdrlen = 8 * (desthdr->ip6d_len + 1);
27820Sstevel@tonic-gate if ((uchar_t *)desthdr + ehdrlen > endptr)
27830Sstevel@tonic-gate return (B_FALSE);
27840Sstevel@tonic-gate nexthdrp = &desthdr->ip6d_nxt;
27850Sstevel@tonic-gate break;
27860Sstevel@tonic-gate case IPPROTO_ROUTING:
27870Sstevel@tonic-gate rthdr = (ip6_rthdr_t *)whereptr;
27880Sstevel@tonic-gate ehdrlen = 8 * (rthdr->ip6r_len + 1);
27890Sstevel@tonic-gate if ((uchar_t *)rthdr + ehdrlen > endptr)
27900Sstevel@tonic-gate return (B_FALSE);
27910Sstevel@tonic-gate nexthdrp = &rthdr->ip6r_nxt;
27920Sstevel@tonic-gate break;
27930Sstevel@tonic-gate case IPPROTO_FRAGMENT:
27940Sstevel@tonic-gate fraghdr = (ip6_frag_t *)whereptr;
27950Sstevel@tonic-gate ehdrlen = sizeof (ip6_frag_t);
27960Sstevel@tonic-gate if ((uchar_t *)&fraghdr[1] > endptr)
27970Sstevel@tonic-gate return (B_FALSE);
27980Sstevel@tonic-gate nexthdrp = &fraghdr->ip6f_nxt;
27990Sstevel@tonic-gate break;
28000Sstevel@tonic-gate case IPPROTO_NONE:
28010Sstevel@tonic-gate /* No next header means we're finished */
28020Sstevel@tonic-gate default:
28030Sstevel@tonic-gate *hdr_length_ptr = length;
28040Sstevel@tonic-gate *nexthdrpp = nexthdrp;
28050Sstevel@tonic-gate return (B_TRUE);
28060Sstevel@tonic-gate }
28070Sstevel@tonic-gate length += ehdrlen;
28080Sstevel@tonic-gate whereptr += ehdrlen;
28090Sstevel@tonic-gate *hdr_length_ptr = length;
28100Sstevel@tonic-gate *nexthdrpp = nexthdrp;
28110Sstevel@tonic-gate }
28120Sstevel@tonic-gate switch (*nexthdrp) {
28130Sstevel@tonic-gate case IPPROTO_HOPOPTS:
28140Sstevel@tonic-gate case IPPROTO_DSTOPTS:
28150Sstevel@tonic-gate case IPPROTO_ROUTING:
28160Sstevel@tonic-gate case IPPROTO_FRAGMENT:
28170Sstevel@tonic-gate /*
28180Sstevel@tonic-gate * If any know extension headers are still to be processed,
28190Sstevel@tonic-gate * the packet's malformed (or at least all the IP header(s) are
28200Sstevel@tonic-gate * not in the same mblk - and that should never happen.
28210Sstevel@tonic-gate */
28220Sstevel@tonic-gate return (B_FALSE);
28230Sstevel@tonic-gate
28240Sstevel@tonic-gate default:
28250Sstevel@tonic-gate /*
28260Sstevel@tonic-gate * If we get here, we know that all of the IP headers were in
28270Sstevel@tonic-gate * the same mblk, even if the ULP header is in the next mblk.
28280Sstevel@tonic-gate */
28290Sstevel@tonic-gate *hdr_length_ptr = length;
28300Sstevel@tonic-gate *nexthdrpp = nexthdrp;
28310Sstevel@tonic-gate return (B_TRUE);
28320Sstevel@tonic-gate }
28330Sstevel@tonic-gate }
28340Sstevel@tonic-gate
28350Sstevel@tonic-gate /*
28360Sstevel@tonic-gate * Return the length of the IPv6 related headers (including extension headers)
28370Sstevel@tonic-gate * Returns a length even if the packet is malformed.
28380Sstevel@tonic-gate */
28390Sstevel@tonic-gate int
ip_hdr_length_v6(mblk_t * mp,ip6_t * ip6h)28400Sstevel@tonic-gate ip_hdr_length_v6(mblk_t *mp, ip6_t *ip6h)
28410Sstevel@tonic-gate {
28420Sstevel@tonic-gate uint16_t hdr_len;
28430Sstevel@tonic-gate uint8_t *nexthdrp;
28440Sstevel@tonic-gate
28450Sstevel@tonic-gate (void) ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_len, &nexthdrp);
28460Sstevel@tonic-gate return (hdr_len);
28470Sstevel@tonic-gate }
28480Sstevel@tonic-gate
28490Sstevel@tonic-gate /*
28500Sstevel@tonic-gate * Parse and process any hop-by-hop or destination options.
28510Sstevel@tonic-gate *
28520Sstevel@tonic-gate * Assumes that q is an ill read queue so that ICMP errors for link-local
28530Sstevel@tonic-gate * destinations are sent out the correct interface.
28540Sstevel@tonic-gate *
28550Sstevel@tonic-gate * Returns -1 if there was an error and mp has been consumed.
28560Sstevel@tonic-gate * Returns 0 if no special action is needed.
28570Sstevel@tonic-gate * Returns 1 if the packet contained a router alert option for this node
28580Sstevel@tonic-gate * which is verified to be "interesting/known" for our implementation.
28590Sstevel@tonic-gate *
28600Sstevel@tonic-gate * XXX Note: In future as more hbh or dest options are defined,
28610Sstevel@tonic-gate * it may be better to have different routines for hbh and dest
28620Sstevel@tonic-gate * options as opt_type fields other than IP6OPT_PAD1 and IP6OPT_PADN
28630Sstevel@tonic-gate * may have same value in different namespaces. Or is it same namespace ??
28640Sstevel@tonic-gate * Current code checks for each opt_type (other than pads) if it is in
28650Sstevel@tonic-gate * the expected nexthdr (hbh or dest)
28660Sstevel@tonic-gate */
286711042SErik.Nordmark@Sun.COM int
ip_process_options_v6(mblk_t * mp,ip6_t * ip6h,uint8_t * optptr,uint_t optlen,uint8_t hdr_type,ip_recv_attr_t * ira)286811042SErik.Nordmark@Sun.COM ip_process_options_v6(mblk_t *mp, ip6_t *ip6h,
286911042SErik.Nordmark@Sun.COM uint8_t *optptr, uint_t optlen, uint8_t hdr_type, ip_recv_attr_t *ira)
28700Sstevel@tonic-gate {
28710Sstevel@tonic-gate uint8_t opt_type;
28720Sstevel@tonic-gate uint_t optused;
28730Sstevel@tonic-gate int ret = 0;
28741676Sjpk const char *errtype;
287511042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill;
287611042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst;
28770Sstevel@tonic-gate
28780Sstevel@tonic-gate while (optlen != 0) {
28790Sstevel@tonic-gate opt_type = *optptr;
28800Sstevel@tonic-gate if (opt_type == IP6OPT_PAD1) {
28810Sstevel@tonic-gate optused = 1;
28820Sstevel@tonic-gate } else {
28830Sstevel@tonic-gate if (optlen < 2)
28840Sstevel@tonic-gate goto bad_opt;
28851676Sjpk errtype = "malformed";
28861676Sjpk if (opt_type == ip6opt_ls) {
28871676Sjpk optused = 2 + optptr[1];
28881676Sjpk if (optused > optlen)
28891676Sjpk goto bad_opt;
28901676Sjpk } else switch (opt_type) {
28910Sstevel@tonic-gate case IP6OPT_PADN:
28920Sstevel@tonic-gate /*
28930Sstevel@tonic-gate * Note:We don't verify that (N-2) pad octets
28940Sstevel@tonic-gate * are zero as required by spec. Adhere to
28950Sstevel@tonic-gate * "be liberal in what you accept..." part of
28960Sstevel@tonic-gate * implementation philosophy (RFC791,RFC1122)
28970Sstevel@tonic-gate */
28980Sstevel@tonic-gate optused = 2 + optptr[1];
28990Sstevel@tonic-gate if (optused > optlen)
29000Sstevel@tonic-gate goto bad_opt;
29010Sstevel@tonic-gate break;
29020Sstevel@tonic-gate
29030Sstevel@tonic-gate case IP6OPT_JUMBO:
29040Sstevel@tonic-gate if (hdr_type != IPPROTO_HOPOPTS)
29050Sstevel@tonic-gate goto opt_error;
29060Sstevel@tonic-gate goto opt_error; /* XXX Not implemented! */
29070Sstevel@tonic-gate
29080Sstevel@tonic-gate case IP6OPT_ROUTER_ALERT: {
29090Sstevel@tonic-gate struct ip6_opt_router *or;
29100Sstevel@tonic-gate
29110Sstevel@tonic-gate if (hdr_type != IPPROTO_HOPOPTS)
29120Sstevel@tonic-gate goto opt_error;
29130Sstevel@tonic-gate optused = 2 + optptr[1];
29140Sstevel@tonic-gate if (optused > optlen)
29150Sstevel@tonic-gate goto bad_opt;
29160Sstevel@tonic-gate or = (struct ip6_opt_router *)optptr;
29170Sstevel@tonic-gate /* Check total length and alignment */
29180Sstevel@tonic-gate if (optused != sizeof (*or) ||
29190Sstevel@tonic-gate ((uintptr_t)or->ip6or_value & 0x1) != 0)
29200Sstevel@tonic-gate goto opt_error;
29210Sstevel@tonic-gate /* Check value */
29220Sstevel@tonic-gate switch (*((uint16_t *)or->ip6or_value)) {
29230Sstevel@tonic-gate case IP6_ALERT_MLD:
29240Sstevel@tonic-gate case IP6_ALERT_RSVP:
29250Sstevel@tonic-gate ret = 1;
29260Sstevel@tonic-gate }
29270Sstevel@tonic-gate break;
29280Sstevel@tonic-gate }
29290Sstevel@tonic-gate case IP6OPT_HOME_ADDRESS: {
29300Sstevel@tonic-gate /*
29310Sstevel@tonic-gate * Minimal support for the home address option
29320Sstevel@tonic-gate * (which is required by all IPv6 nodes).
29330Sstevel@tonic-gate * Implement by just swapping the home address
29340Sstevel@tonic-gate * and source address.
29350Sstevel@tonic-gate * XXX Note: this has IPsec implications since
29360Sstevel@tonic-gate * AH needs to take this into account.
29370Sstevel@tonic-gate * Also, when IPsec is used we need to ensure
29380Sstevel@tonic-gate * that this is only processed once
29390Sstevel@tonic-gate * in the received packet (to avoid swapping
29400Sstevel@tonic-gate * back and forth).
29410Sstevel@tonic-gate * NOTE:This option processing is considered
29420Sstevel@tonic-gate * to be unsafe and prone to a denial of
29430Sstevel@tonic-gate * service attack.
29440Sstevel@tonic-gate * The current processing is not safe even with
29450Sstevel@tonic-gate * IPsec secured IP packets. Since the home
29460Sstevel@tonic-gate * address option processing requirement still
29470Sstevel@tonic-gate * is in the IETF draft and in the process of
29480Sstevel@tonic-gate * being redefined for its usage, it has been
29490Sstevel@tonic-gate * decided to turn off the option by default.
29500Sstevel@tonic-gate * If this section of code needs to be executed,
29510Sstevel@tonic-gate * ndd variable ip6_ignore_home_address_opt
29520Sstevel@tonic-gate * should be set to 0 at the user's own risk.
29530Sstevel@tonic-gate */
29540Sstevel@tonic-gate struct ip6_opt_home_address *oh;
29550Sstevel@tonic-gate in6_addr_t tmp;
29560Sstevel@tonic-gate
29573448Sdh155122 if (ipst->ips_ipv6_ignore_home_address_opt)
29580Sstevel@tonic-gate goto opt_error;
29590Sstevel@tonic-gate
29600Sstevel@tonic-gate if (hdr_type != IPPROTO_DSTOPTS)
29610Sstevel@tonic-gate goto opt_error;
29620Sstevel@tonic-gate optused = 2 + optptr[1];
29630Sstevel@tonic-gate if (optused > optlen)
29640Sstevel@tonic-gate goto bad_opt;
29650Sstevel@tonic-gate
29660Sstevel@tonic-gate /*
29670Sstevel@tonic-gate * We did this dest. opt the first time
29680Sstevel@tonic-gate * around (i.e. before AH processing).
29690Sstevel@tonic-gate * If we've done AH... stop now.
29700Sstevel@tonic-gate */
297111042SErik.Nordmark@Sun.COM if ((ira->ira_flags & IRAF_IPSEC_SECURE) &&
297211042SErik.Nordmark@Sun.COM ira->ira_ipsec_ah_sa != NULL)
297311042SErik.Nordmark@Sun.COM break;
29740Sstevel@tonic-gate
29750Sstevel@tonic-gate oh = (struct ip6_opt_home_address *)optptr;
29760Sstevel@tonic-gate /* Check total length and alignment */
29770Sstevel@tonic-gate if (optused < sizeof (*oh) ||
29780Sstevel@tonic-gate ((uintptr_t)oh->ip6oh_addr & 0x7) != 0)
29790Sstevel@tonic-gate goto opt_error;
29800Sstevel@tonic-gate /* Swap ip6_src and the home address */
29810Sstevel@tonic-gate tmp = ip6h->ip6_src;
29820Sstevel@tonic-gate /* XXX Note: only 8 byte alignment option */
29830Sstevel@tonic-gate ip6h->ip6_src = *(in6_addr_t *)oh->ip6oh_addr;
29840Sstevel@tonic-gate *(in6_addr_t *)oh->ip6oh_addr = tmp;
29850Sstevel@tonic-gate break;
29860Sstevel@tonic-gate }
29870Sstevel@tonic-gate
29880Sstevel@tonic-gate case IP6OPT_TUNNEL_LIMIT:
29890Sstevel@tonic-gate if (hdr_type != IPPROTO_DSTOPTS) {
29900Sstevel@tonic-gate goto opt_error;
29910Sstevel@tonic-gate }
29920Sstevel@tonic-gate optused = 2 + optptr[1];
29930Sstevel@tonic-gate if (optused > optlen) {
29940Sstevel@tonic-gate goto bad_opt;
29950Sstevel@tonic-gate }
29960Sstevel@tonic-gate if (optused != 3) {
29970Sstevel@tonic-gate goto opt_error;
29980Sstevel@tonic-gate }
29990Sstevel@tonic-gate break;
30000Sstevel@tonic-gate
30010Sstevel@tonic-gate default:
30021676Sjpk errtype = "unknown";
30031676Sjpk /* FALLTHROUGH */
30040Sstevel@tonic-gate opt_error:
30052733Snordmark /* Determine which zone should send error */
30060Sstevel@tonic-gate switch (IP6OPT_TYPE(opt_type)) {
30070Sstevel@tonic-gate case IP6OPT_TYPE_SKIP:
30080Sstevel@tonic-gate optused = 2 + optptr[1];
30090Sstevel@tonic-gate if (optused > optlen)
30100Sstevel@tonic-gate goto bad_opt;
30111676Sjpk ip1dbg(("ip_process_options_v6: %s "
30121676Sjpk "opt 0x%x skipped\n",
30131676Sjpk errtype, opt_type));
30140Sstevel@tonic-gate break;
30150Sstevel@tonic-gate case IP6OPT_TYPE_DISCARD:
30161676Sjpk ip1dbg(("ip_process_options_v6: %s "
30171676Sjpk "opt 0x%x; packet dropped\n",
30181676Sjpk errtype, opt_type));
301911042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib,
302011042SErik.Nordmark@Sun.COM ipIfStatsInHdrErrors);
302111042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInHdrErrors",
302211042SErik.Nordmark@Sun.COM mp, ill);
302311042SErik.Nordmark@Sun.COM freemsg(mp);
30240Sstevel@tonic-gate return (-1);
30250Sstevel@tonic-gate case IP6OPT_TYPE_ICMP:
302611042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib,
302711042SErik.Nordmark@Sun.COM ipIfStatsInHdrErrors);
302811042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInHdrErrors",
302911042SErik.Nordmark@Sun.COM mp, ill);
303011042SErik.Nordmark@Sun.COM icmp_param_problem_v6(mp,
30310Sstevel@tonic-gate ICMP6_PARAMPROB_OPTION,
30320Sstevel@tonic-gate (uint32_t)(optptr -
30330Sstevel@tonic-gate (uint8_t *)ip6h),
303411042SErik.Nordmark@Sun.COM B_FALSE, ira);
30350Sstevel@tonic-gate return (-1);
30360Sstevel@tonic-gate case IP6OPT_TYPE_FORCEICMP:
303711042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib,
303811042SErik.Nordmark@Sun.COM ipIfStatsInHdrErrors);
303911042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInHdrErrors",
304011042SErik.Nordmark@Sun.COM mp, ill);
304111042SErik.Nordmark@Sun.COM icmp_param_problem_v6(mp,
30420Sstevel@tonic-gate ICMP6_PARAMPROB_OPTION,
30430Sstevel@tonic-gate (uint32_t)(optptr -
30440Sstevel@tonic-gate (uint8_t *)ip6h),
304511042SErik.Nordmark@Sun.COM B_TRUE, ira);
30460Sstevel@tonic-gate return (-1);
30471676Sjpk default:
30481676Sjpk ASSERT(0);
30490Sstevel@tonic-gate }
30500Sstevel@tonic-gate }
30510Sstevel@tonic-gate }
30520Sstevel@tonic-gate optlen -= optused;
30530Sstevel@tonic-gate optptr += optused;
30540Sstevel@tonic-gate }
30550Sstevel@tonic-gate return (ret);
30560Sstevel@tonic-gate
30570Sstevel@tonic-gate bad_opt:
30582733Snordmark /* Determine which zone should send error */
305911042SErik.Nordmark@Sun.COM ip_drop_input("ICMP_PARAM_PROBLEM", mp, ill);
306011042SErik.Nordmark@Sun.COM icmp_param_problem_v6(mp, ICMP6_PARAMPROB_OPTION,
306111042SErik.Nordmark@Sun.COM (uint32_t)(optptr - (uint8_t *)ip6h),
306211042SErik.Nordmark@Sun.COM B_FALSE, ira);
30630Sstevel@tonic-gate return (-1);
30640Sstevel@tonic-gate }
30650Sstevel@tonic-gate
30660Sstevel@tonic-gate /*
30670Sstevel@tonic-gate * Process a routing header that is not yet empty.
306810031SBrian.Utterback@Sun.COM * Because of RFC 5095, we now reject all route headers.
30690Sstevel@tonic-gate */
307011042SErik.Nordmark@Sun.COM void
ip_process_rthdr(mblk_t * mp,ip6_t * ip6h,ip6_rthdr_t * rth,ip_recv_attr_t * ira)307111042SErik.Nordmark@Sun.COM ip_process_rthdr(mblk_t *mp, ip6_t *ip6h, ip6_rthdr_t *rth,
307211042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira)
30730Sstevel@tonic-gate {
307411042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill;
30753448Sdh155122 ip_stack_t *ipst = ill->ill_ipst;
30760Sstevel@tonic-gate
30770Sstevel@tonic-gate ASSERT(rth->ip6r_segleft != 0);
30780Sstevel@tonic-gate
30793448Sdh155122 if (!ipst->ips_ipv6_forward_src_routed) {
30800Sstevel@tonic-gate /* XXX Check for source routed out same interface? */
30813284Sapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsForwProhibits);
30823284Sapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInAddrErrors);
308311042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInAddrErrors", mp, ill);
30840Sstevel@tonic-gate freemsg(mp);
30850Sstevel@tonic-gate return;
30860Sstevel@tonic-gate }
308711042SErik.Nordmark@Sun.COM
308811042SErik.Nordmark@Sun.COM ip_drop_input("ICMP_PARAM_PROBLEM", mp, ill);
308911042SErik.Nordmark@Sun.COM icmp_param_problem_v6(mp, ICMP6_PARAMPROB_HEADER,
309011042SErik.Nordmark@Sun.COM (uint32_t)((uchar_t *)&rth->ip6r_type - (uchar_t *)ip6h),
309111042SErik.Nordmark@Sun.COM B_FALSE, ira);
30920Sstevel@tonic-gate }
30930Sstevel@tonic-gate
30940Sstevel@tonic-gate /*
30950Sstevel@tonic-gate * Read side put procedure for IPv6 module.
30960Sstevel@tonic-gate */
30972958Sdr146992 void
ip_rput_v6(queue_t * q,mblk_t * mp)30980Sstevel@tonic-gate ip_rput_v6(queue_t *q, mblk_t *mp)
30990Sstevel@tonic-gate {
31000Sstevel@tonic-gate ill_t *ill;
31010Sstevel@tonic-gate
31020Sstevel@tonic-gate ill = (ill_t *)q->q_ptr;
310311042SErik.Nordmark@Sun.COM if (ill->ill_state_flags & (ILL_CONDEMNED | ILL_LL_SUBNET_PENDING)) {
31040Sstevel@tonic-gate union DL_primitives *dl;
31050Sstevel@tonic-gate
31060Sstevel@tonic-gate dl = (union DL_primitives *)mp->b_rptr;
31070Sstevel@tonic-gate /*
31080Sstevel@tonic-gate * Things are opening or closing - only accept DLPI
31090Sstevel@tonic-gate * ack messages. If the stream is closing and ip_wsrv
31100Sstevel@tonic-gate * has completed, ip_close is out of the qwait, but has
31110Sstevel@tonic-gate * not yet completed qprocsoff. Don't proceed any further
31120Sstevel@tonic-gate * because the ill has been cleaned up and things hanging
31130Sstevel@tonic-gate * off the ill have been freed.
31140Sstevel@tonic-gate */
31150Sstevel@tonic-gate if ((mp->b_datap->db_type != M_PCPROTO) ||
31160Sstevel@tonic-gate (dl->dl_primitive == DL_UNITDATA_IND)) {
3117741Smasputra inet_freemsg(mp);
31180Sstevel@tonic-gate return;
31190Sstevel@tonic-gate }
31200Sstevel@tonic-gate }
312111042SErik.Nordmark@Sun.COM if (DB_TYPE(mp) == M_DATA) {
312211042SErik.Nordmark@Sun.COM struct mac_header_info_s mhi;
312311042SErik.Nordmark@Sun.COM
312411042SErik.Nordmark@Sun.COM ip_mdata_to_mhi(ill, mp, &mhi);
312511042SErik.Nordmark@Sun.COM ip_input_v6(ill, NULL, mp, &mhi);
312611042SErik.Nordmark@Sun.COM } else {
312711042SErik.Nordmark@Sun.COM ip_rput_notdata(ill, mp);
31282546Scarlsonj }
31290Sstevel@tonic-gate }
31300Sstevel@tonic-gate
31310Sstevel@tonic-gate /*
31320Sstevel@tonic-gate * Walk through the IPv6 packet in mp and see if there's an AH header
31330Sstevel@tonic-gate * in it. See if the AH header needs to get done before other headers in
31340Sstevel@tonic-gate * the packet. (Worker function for ipsec_early_ah_v6().)
31350Sstevel@tonic-gate */
31360Sstevel@tonic-gate #define IPSEC_HDR_DONT_PROCESS 0
31370Sstevel@tonic-gate #define IPSEC_HDR_PROCESS 1
31388626Ssommerfeld@sun.com #define IPSEC_MEMORY_ERROR 2 /* or malformed packet */
31390Sstevel@tonic-gate static int
ipsec_needs_processing_v6(mblk_t * mp,uint8_t * nexthdr)31400Sstevel@tonic-gate ipsec_needs_processing_v6(mblk_t *mp, uint8_t *nexthdr)
31410Sstevel@tonic-gate {
31420Sstevel@tonic-gate uint_t length;
31430Sstevel@tonic-gate uint_t ehdrlen;
31440Sstevel@tonic-gate uint8_t *whereptr;
31450Sstevel@tonic-gate uint8_t *endptr;
31460Sstevel@tonic-gate uint8_t *nexthdrp;
31470Sstevel@tonic-gate ip6_dest_t *desthdr;
31480Sstevel@tonic-gate ip6_rthdr_t *rthdr;
31490Sstevel@tonic-gate ip6_t *ip6h;
31500Sstevel@tonic-gate
31510Sstevel@tonic-gate /*
31520Sstevel@tonic-gate * For now just pullup everything. In general, the less pullups,
31530Sstevel@tonic-gate * the better, but there's so much squirrelling through anyway,
31540Sstevel@tonic-gate * it's just easier this way.
31550Sstevel@tonic-gate */
31560Sstevel@tonic-gate if (!pullupmsg(mp, -1)) {
31570Sstevel@tonic-gate return (IPSEC_MEMORY_ERROR);
31580Sstevel@tonic-gate }
31590Sstevel@tonic-gate
31600Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr;
31610Sstevel@tonic-gate length = IPV6_HDR_LEN;
31620Sstevel@tonic-gate whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */
31630Sstevel@tonic-gate endptr = mp->b_wptr;
31640Sstevel@tonic-gate
31650Sstevel@tonic-gate /*
31660Sstevel@tonic-gate * We can't just use the argument nexthdr in the place
31670Sstevel@tonic-gate * of nexthdrp becaue we don't dereference nexthdrp
31680Sstevel@tonic-gate * till we confirm whether it is a valid address.
31690Sstevel@tonic-gate */
31700Sstevel@tonic-gate nexthdrp = &ip6h->ip6_nxt;
31710Sstevel@tonic-gate while (whereptr < endptr) {
31720Sstevel@tonic-gate /* Is there enough left for len + nexthdr? */
31730Sstevel@tonic-gate if (whereptr + MIN_EHDR_LEN > endptr)
31740Sstevel@tonic-gate return (IPSEC_MEMORY_ERROR);
31750Sstevel@tonic-gate
31760Sstevel@tonic-gate switch (*nexthdrp) {
31770Sstevel@tonic-gate case IPPROTO_HOPOPTS:
31780Sstevel@tonic-gate case IPPROTO_DSTOPTS:
31790Sstevel@tonic-gate /* Assumes the headers are identical for hbh and dst */
31800Sstevel@tonic-gate desthdr = (ip6_dest_t *)whereptr;
31810Sstevel@tonic-gate ehdrlen = 8 * (desthdr->ip6d_len + 1);
31820Sstevel@tonic-gate if ((uchar_t *)desthdr + ehdrlen > endptr)
31830Sstevel@tonic-gate return (IPSEC_MEMORY_ERROR);
31840Sstevel@tonic-gate /*
31854823Sseb * Return DONT_PROCESS because the destination
31864823Sseb * options header may be for each hop in a
31874823Sseb * routing-header, and we only want AH if we're
31884823Sseb * finished with routing headers.
31890Sstevel@tonic-gate */
31900Sstevel@tonic-gate if (*nexthdrp == IPPROTO_DSTOPTS)
31910Sstevel@tonic-gate return (IPSEC_HDR_DONT_PROCESS);
31920Sstevel@tonic-gate nexthdrp = &desthdr->ip6d_nxt;
31930Sstevel@tonic-gate break;
31940Sstevel@tonic-gate case IPPROTO_ROUTING:
31950Sstevel@tonic-gate rthdr = (ip6_rthdr_t *)whereptr;
31960Sstevel@tonic-gate
31970Sstevel@tonic-gate /*
31980Sstevel@tonic-gate * If there's more hops left on the routing header,
31990Sstevel@tonic-gate * return now with DON'T PROCESS.
32000Sstevel@tonic-gate */
32010Sstevel@tonic-gate if (rthdr->ip6r_segleft > 0)
32020Sstevel@tonic-gate return (IPSEC_HDR_DONT_PROCESS);
32030Sstevel@tonic-gate
32040Sstevel@tonic-gate ehdrlen = 8 * (rthdr->ip6r_len + 1);
32050Sstevel@tonic-gate if ((uchar_t *)rthdr + ehdrlen > endptr)
32060Sstevel@tonic-gate return (IPSEC_MEMORY_ERROR);
32070Sstevel@tonic-gate nexthdrp = &rthdr->ip6r_nxt;
32080Sstevel@tonic-gate break;
32090Sstevel@tonic-gate case IPPROTO_FRAGMENT:
32100Sstevel@tonic-gate /* Wait for reassembly */
32110Sstevel@tonic-gate return (IPSEC_HDR_DONT_PROCESS);
32120Sstevel@tonic-gate case IPPROTO_AH:
32130Sstevel@tonic-gate *nexthdr = IPPROTO_AH;
32140Sstevel@tonic-gate return (IPSEC_HDR_PROCESS);
32150Sstevel@tonic-gate case IPPROTO_NONE:
32160Sstevel@tonic-gate /* No next header means we're finished */
32170Sstevel@tonic-gate default:
32180Sstevel@tonic-gate return (IPSEC_HDR_DONT_PROCESS);
32190Sstevel@tonic-gate }
32200Sstevel@tonic-gate length += ehdrlen;
32210Sstevel@tonic-gate whereptr += ehdrlen;
32220Sstevel@tonic-gate }
32238626Ssommerfeld@sun.com /*
32248626Ssommerfeld@sun.com * Malformed/truncated packet.
32258626Ssommerfeld@sun.com */
32268626Ssommerfeld@sun.com return (IPSEC_MEMORY_ERROR);
32270Sstevel@tonic-gate }
32280Sstevel@tonic-gate
32290Sstevel@tonic-gate /*
323011042SErik.Nordmark@Sun.COM * Path for AH if options are present.
323111042SErik.Nordmark@Sun.COM * Returns NULL if the mblk was consumed.
32320Sstevel@tonic-gate *
32330Sstevel@tonic-gate * Sometimes AH needs to be done before other IPv6 headers for security
32340Sstevel@tonic-gate * reasons. This function (and its ipsec_needs_processing_v6() above)
32350Sstevel@tonic-gate * indicates if that is so, and fans out to the appropriate IPsec protocol
32360Sstevel@tonic-gate * for the datagram passed in.
32370Sstevel@tonic-gate */
323811042SErik.Nordmark@Sun.COM mblk_t *
ipsec_early_ah_v6(mblk_t * mp,ip_recv_attr_t * ira)323911042SErik.Nordmark@Sun.COM ipsec_early_ah_v6(mblk_t *mp, ip_recv_attr_t *ira)
32400Sstevel@tonic-gate {
32410Sstevel@tonic-gate uint8_t nexthdr;
32420Sstevel@tonic-gate ah_t *ah;
324311042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill;
32443448Sdh155122 ip_stack_t *ipst = ill->ill_ipst;
324511042SErik.Nordmark@Sun.COM ipsec_stack_t *ipss = ipst->ips_netstack->netstack_ipsec;
324611042SErik.Nordmark@Sun.COM
324711042SErik.Nordmark@Sun.COM switch (ipsec_needs_processing_v6(mp, &nexthdr)) {
32480Sstevel@tonic-gate case IPSEC_MEMORY_ERROR:
32493284Sapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
325011042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards", mp, ill);
325111042SErik.Nordmark@Sun.COM freemsg(mp);
325211042SErik.Nordmark@Sun.COM return (NULL);
32530Sstevel@tonic-gate case IPSEC_HDR_DONT_PROCESS:
325411042SErik.Nordmark@Sun.COM return (mp);
32550Sstevel@tonic-gate }
32560Sstevel@tonic-gate
32570Sstevel@tonic-gate /* Default means send it to AH! */
32580Sstevel@tonic-gate ASSERT(nexthdr == IPPROTO_AH);
32590Sstevel@tonic-gate
32603448Sdh155122 if (!ipsec_loaded(ipss)) {
326111042SErik.Nordmark@Sun.COM ip_proto_not_sup(mp, ira);
326211042SErik.Nordmark@Sun.COM return (NULL);
32630Sstevel@tonic-gate }
32640Sstevel@tonic-gate
326511042SErik.Nordmark@Sun.COM mp = ipsec_inbound_ah_sa(mp, ira, &ah);
326611042SErik.Nordmark@Sun.COM if (mp == NULL)
326711042SErik.Nordmark@Sun.COM return (NULL);
326811042SErik.Nordmark@Sun.COM ASSERT(ah != NULL);
326911042SErik.Nordmark@Sun.COM ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE);
327011042SErik.Nordmark@Sun.COM ASSERT(ira->ira_ipsec_ah_sa != NULL);
327111042SErik.Nordmark@Sun.COM ASSERT(ira->ira_ipsec_ah_sa->ipsa_input_func != NULL);
327211042SErik.Nordmark@Sun.COM mp = ira->ira_ipsec_ah_sa->ipsa_input_func(mp, ah, ira);
327311042SErik.Nordmark@Sun.COM
327411042SErik.Nordmark@Sun.COM if (mp == NULL) {
327511042SErik.Nordmark@Sun.COM /*
327611042SErik.Nordmark@Sun.COM * Either it failed or is pending. In the former case
327711042SErik.Nordmark@Sun.COM * ipIfStatsInDiscards was increased.
327811042SErik.Nordmark@Sun.COM */
327911042SErik.Nordmark@Sun.COM return (NULL);
32800Sstevel@tonic-gate }
328111042SErik.Nordmark@Sun.COM
328211042SErik.Nordmark@Sun.COM /* we're done with IPsec processing, send it up */
328311042SErik.Nordmark@Sun.COM ip_input_post_ipsec(mp, ira);
328411042SErik.Nordmark@Sun.COM return (NULL);
32850Sstevel@tonic-gate }
32860Sstevel@tonic-gate
32870Sstevel@tonic-gate /*
32880Sstevel@tonic-gate * Reassemble fragment.
32890Sstevel@tonic-gate * When it returns a completed message the first mblk will only contain
329011042SErik.Nordmark@Sun.COM * the headers prior to the fragment header, with the nexthdr value updated
329111042SErik.Nordmark@Sun.COM * to be the header after the fragment header.
32920Sstevel@tonic-gate */
329311042SErik.Nordmark@Sun.COM mblk_t *
ip_input_fragment_v6(mblk_t * mp,ip6_t * ip6h,ip6_frag_t * fraghdr,uint_t remlen,ip_recv_attr_t * ira)329411042SErik.Nordmark@Sun.COM ip_input_fragment_v6(mblk_t *mp, ip6_t *ip6h,
329511042SErik.Nordmark@Sun.COM ip6_frag_t *fraghdr, uint_t remlen, ip_recv_attr_t *ira)
32960Sstevel@tonic-gate {
32970Sstevel@tonic-gate uint32_t ident = ntohl(fraghdr->ip6f_ident);
32980Sstevel@tonic-gate uint16_t offset;
32990Sstevel@tonic-gate boolean_t more_frags;
33000Sstevel@tonic-gate uint8_t nexthdr = fraghdr->ip6f_nxt;
33010Sstevel@tonic-gate in6_addr_t *v6dst_ptr;
33020Sstevel@tonic-gate in6_addr_t *v6src_ptr;
33030Sstevel@tonic-gate uint_t end;
33040Sstevel@tonic-gate uint_t hdr_length;
33050Sstevel@tonic-gate size_t count;
33060Sstevel@tonic-gate ipf_t *ipf;
33070Sstevel@tonic-gate ipf_t **ipfp;
33080Sstevel@tonic-gate ipfb_t *ipfb;
33090Sstevel@tonic-gate mblk_t *mp1;
33100Sstevel@tonic-gate uint8_t ecn_info = 0;
33110Sstevel@tonic-gate size_t msg_len;
33120Sstevel@tonic-gate mblk_t *tail_mp;
33130Sstevel@tonic-gate mblk_t *t_mp;
33140Sstevel@tonic-gate boolean_t pruned = B_FALSE;
3315741Smasputra uint32_t sum_val;
3316741Smasputra uint16_t sum_flags;
331711042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill;
33183448Sdh155122 ip_stack_t *ipst = ill->ill_ipst;
331911042SErik.Nordmark@Sun.COM uint_t prev_nexthdr_offset;
332011042SErik.Nordmark@Sun.COM uint8_t prev_nexthdr;
332111042SErik.Nordmark@Sun.COM uint8_t *ptr;
332211042SErik.Nordmark@Sun.COM uint32_t packet_size;
3323741Smasputra
3324741Smasputra /*
3325741Smasputra * We utilize hardware computed checksum info only for UDP since
3326741Smasputra * IP fragmentation is a normal occurence for the protocol. In
3327741Smasputra * addition, checksum offload support for IP fragments carrying
3328741Smasputra * UDP payload is commonly implemented across network adapters.
3329741Smasputra */
333011042SErik.Nordmark@Sun.COM ASSERT(ira->ira_rill != NULL);
333111042SErik.Nordmark@Sun.COM if (nexthdr == IPPROTO_UDP && dohwcksum &&
333211042SErik.Nordmark@Sun.COM ILL_HCKSUM_CAPABLE(ira->ira_rill) &&
3333741Smasputra (DB_CKSUMFLAGS(mp) & (HCK_FULLCKSUM | HCK_PARTIALCKSUM))) {
3334741Smasputra mblk_t *mp1 = mp->b_cont;
3335741Smasputra int32_t len;
3336741Smasputra
3337741Smasputra /* Record checksum information from the packet */
3338741Smasputra sum_val = (uint32_t)DB_CKSUM16(mp);
3339741Smasputra sum_flags = DB_CKSUMFLAGS(mp);
3340741Smasputra
3341741Smasputra /* fragmented payload offset from beginning of mblk */
3342741Smasputra offset = (uint16_t)((uchar_t *)&fraghdr[1] - mp->b_rptr);
3343741Smasputra
3344741Smasputra if ((sum_flags & HCK_PARTIALCKSUM) &&
3345741Smasputra (mp1 == NULL || mp1->b_cont == NULL) &&
334611042SErik.Nordmark@Sun.COM offset >= DB_CKSUMSTART(mp) &&
334711042SErik.Nordmark@Sun.COM ((len = offset - DB_CKSUMSTART(mp)) & 1) == 0) {
3348741Smasputra uint32_t adj;
3349741Smasputra /*
3350741Smasputra * Partial checksum has been calculated by hardware
3351741Smasputra * and attached to the packet; in addition, any
3352741Smasputra * prepended extraneous data is even byte aligned.
3353741Smasputra * If any such data exists, we adjust the checksum;
3354741Smasputra * this would also handle any postpended data.
3355741Smasputra */
3356741Smasputra IP_ADJCKSUM_PARTIAL(mp->b_rptr + DB_CKSUMSTART(mp),
3357741Smasputra mp, mp1, len, adj);
3358741Smasputra
3359741Smasputra /* One's complement subtract extraneous checksum */
3360741Smasputra if (adj >= sum_val)
3361741Smasputra sum_val = ~(adj - sum_val) & 0xFFFF;
3362741Smasputra else
3363741Smasputra sum_val -= adj;
3364741Smasputra }
3365741Smasputra } else {
3366741Smasputra sum_val = 0;
3367741Smasputra sum_flags = 0;
3368741Smasputra }
3369741Smasputra
3370741Smasputra /* Clear hardware checksumming flag */
3371741Smasputra DB_CKSUMFLAGS(mp) = 0;
33720Sstevel@tonic-gate
33730Sstevel@tonic-gate /*
337411042SErik.Nordmark@Sun.COM * Determine the offset (from the begining of the IP header)
337511042SErik.Nordmark@Sun.COM * of the nexthdr value which has IPPROTO_FRAGMENT. We use
337611042SErik.Nordmark@Sun.COM * this when removing the fragment header from the packet.
337711042SErik.Nordmark@Sun.COM * This packet consists of the IPv6 header, a potential
337811042SErik.Nordmark@Sun.COM * hop-by-hop options header, a potential pre-routing-header
337911042SErik.Nordmark@Sun.COM * destination options header, and a potential routing header.
338011042SErik.Nordmark@Sun.COM */
338111042SErik.Nordmark@Sun.COM prev_nexthdr_offset = (uint8_t *)&ip6h->ip6_nxt - (uint8_t *)ip6h;
338211042SErik.Nordmark@Sun.COM prev_nexthdr = ip6h->ip6_nxt;
338311042SErik.Nordmark@Sun.COM ptr = (uint8_t *)&ip6h[1];
338411042SErik.Nordmark@Sun.COM
338511042SErik.Nordmark@Sun.COM if (prev_nexthdr == IPPROTO_HOPOPTS) {
338611042SErik.Nordmark@Sun.COM ip6_hbh_t *hbh_hdr;
338711042SErik.Nordmark@Sun.COM uint_t hdr_len;
338811042SErik.Nordmark@Sun.COM
338911042SErik.Nordmark@Sun.COM hbh_hdr = (ip6_hbh_t *)ptr;
339011042SErik.Nordmark@Sun.COM hdr_len = 8 * (hbh_hdr->ip6h_len + 1);
339111042SErik.Nordmark@Sun.COM prev_nexthdr = hbh_hdr->ip6h_nxt;
339211042SErik.Nordmark@Sun.COM prev_nexthdr_offset = (uint8_t *)&hbh_hdr->ip6h_nxt
339311042SErik.Nordmark@Sun.COM - (uint8_t *)ip6h;
339411042SErik.Nordmark@Sun.COM ptr += hdr_len;
339511042SErik.Nordmark@Sun.COM }
339611042SErik.Nordmark@Sun.COM if (prev_nexthdr == IPPROTO_DSTOPTS) {
339711042SErik.Nordmark@Sun.COM ip6_dest_t *dest_hdr;
339811042SErik.Nordmark@Sun.COM uint_t hdr_len;
339911042SErik.Nordmark@Sun.COM
340011042SErik.Nordmark@Sun.COM dest_hdr = (ip6_dest_t *)ptr;
340111042SErik.Nordmark@Sun.COM hdr_len = 8 * (dest_hdr->ip6d_len + 1);
340211042SErik.Nordmark@Sun.COM prev_nexthdr = dest_hdr->ip6d_nxt;
340311042SErik.Nordmark@Sun.COM prev_nexthdr_offset = (uint8_t *)&dest_hdr->ip6d_nxt
340411042SErik.Nordmark@Sun.COM - (uint8_t *)ip6h;
340511042SErik.Nordmark@Sun.COM ptr += hdr_len;
340611042SErik.Nordmark@Sun.COM }
340711042SErik.Nordmark@Sun.COM if (prev_nexthdr == IPPROTO_ROUTING) {
340811042SErik.Nordmark@Sun.COM ip6_rthdr_t *rthdr;
340911042SErik.Nordmark@Sun.COM uint_t hdr_len;
341011042SErik.Nordmark@Sun.COM
341111042SErik.Nordmark@Sun.COM rthdr = (ip6_rthdr_t *)ptr;
341211042SErik.Nordmark@Sun.COM prev_nexthdr = rthdr->ip6r_nxt;
341311042SErik.Nordmark@Sun.COM prev_nexthdr_offset = (uint8_t *)&rthdr->ip6r_nxt
341411042SErik.Nordmark@Sun.COM - (uint8_t *)ip6h;
341511042SErik.Nordmark@Sun.COM hdr_len = 8 * (rthdr->ip6r_len + 1);
341611042SErik.Nordmark@Sun.COM ptr += hdr_len;
341711042SErik.Nordmark@Sun.COM }
341811042SErik.Nordmark@Sun.COM if (prev_nexthdr != IPPROTO_FRAGMENT) {
341911042SErik.Nordmark@Sun.COM /* Can't handle other headers before the fragment header */
342011042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors);
342111042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInHdrErrors", mp, ill);
342211042SErik.Nordmark@Sun.COM freemsg(mp);
342311042SErik.Nordmark@Sun.COM return (NULL);
342411042SErik.Nordmark@Sun.COM }
342511042SErik.Nordmark@Sun.COM
342611042SErik.Nordmark@Sun.COM /*
34270Sstevel@tonic-gate * Note: Fragment offset in header is in 8-octet units.
34280Sstevel@tonic-gate * Clearing least significant 3 bits not only extracts
34290Sstevel@tonic-gate * it but also gets it in units of octets.
34300Sstevel@tonic-gate */
34310Sstevel@tonic-gate offset = ntohs(fraghdr->ip6f_offlg) & ~7;
34320Sstevel@tonic-gate more_frags = (fraghdr->ip6f_offlg & IP6F_MORE_FRAG);
34330Sstevel@tonic-gate
34340Sstevel@tonic-gate /*
34350Sstevel@tonic-gate * Is the more frags flag on and the payload length not a multiple
34360Sstevel@tonic-gate * of eight?
34370Sstevel@tonic-gate */
34380Sstevel@tonic-gate if (more_frags && (ntohs(ip6h->ip6_plen) & 7)) {
343911042SErik.Nordmark@Sun.COM ip_drop_input("ICMP_PARAM_PROBLEM", mp, ill);
344011042SErik.Nordmark@Sun.COM icmp_param_problem_v6(mp, ICMP6_PARAMPROB_HEADER,
34410Sstevel@tonic-gate (uint32_t)((char *)&ip6h->ip6_plen -
344211042SErik.Nordmark@Sun.COM (char *)ip6h), B_FALSE, ira);
34430Sstevel@tonic-gate return (NULL);
34440Sstevel@tonic-gate }
34450Sstevel@tonic-gate
34460Sstevel@tonic-gate v6src_ptr = &ip6h->ip6_src;
34470Sstevel@tonic-gate v6dst_ptr = &ip6h->ip6_dst;
34480Sstevel@tonic-gate end = remlen;
34490Sstevel@tonic-gate
34500Sstevel@tonic-gate hdr_length = (uint_t)((char *)&fraghdr[1] - (char *)ip6h);
34510Sstevel@tonic-gate end += offset;
34520Sstevel@tonic-gate
34530Sstevel@tonic-gate /*
34540Sstevel@tonic-gate * Would fragment cause reassembled packet to have a payload length
34550Sstevel@tonic-gate * greater than IP_MAXPACKET - the max payload size?
34560Sstevel@tonic-gate */
34570Sstevel@tonic-gate if (end > IP_MAXPACKET) {
34583284Sapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors);
345911042SErik.Nordmark@Sun.COM ip_drop_input("Reassembled packet too large", mp, ill);
346011042SErik.Nordmark@Sun.COM icmp_param_problem_v6(mp, ICMP6_PARAMPROB_HEADER,
34610Sstevel@tonic-gate (uint32_t)((char *)&fraghdr->ip6f_offlg -
346211042SErik.Nordmark@Sun.COM (char *)ip6h), B_FALSE, ira);
34630Sstevel@tonic-gate return (NULL);
34640Sstevel@tonic-gate }
34650Sstevel@tonic-gate
34660Sstevel@tonic-gate /*
34670Sstevel@tonic-gate * This packet just has one fragment. Reassembly not
34680Sstevel@tonic-gate * needed.
34690Sstevel@tonic-gate */
34700Sstevel@tonic-gate if (!more_frags && offset == 0) {
34710Sstevel@tonic-gate goto reass_done;
34720Sstevel@tonic-gate }
34730Sstevel@tonic-gate
34740Sstevel@tonic-gate /*
34750Sstevel@tonic-gate * Drop the fragmented as early as possible, if
34760Sstevel@tonic-gate * we don't have resource(s) to re-assemble.
34770Sstevel@tonic-gate */
34783448Sdh155122 if (ipst->ips_ip_reass_queue_bytes == 0) {
34790Sstevel@tonic-gate freemsg(mp);
34800Sstevel@tonic-gate return (NULL);
34810Sstevel@tonic-gate }
34820Sstevel@tonic-gate
34830Sstevel@tonic-gate /* Record the ECN field info. */
34840Sstevel@tonic-gate ecn_info = (uint8_t)(ntohl(ip6h->ip6_vcf & htonl(~0xFFCFFFFF)) >> 20);
34850Sstevel@tonic-gate /*
34860Sstevel@tonic-gate * If this is not the first fragment, dump the unfragmentable
34870Sstevel@tonic-gate * portion of the packet.
34880Sstevel@tonic-gate */
34890Sstevel@tonic-gate if (offset)
34900Sstevel@tonic-gate mp->b_rptr = (uchar_t *)&fraghdr[1];
34910Sstevel@tonic-gate
34920Sstevel@tonic-gate /*
34930Sstevel@tonic-gate * Fragmentation reassembly. Each ILL has a hash table for
34940Sstevel@tonic-gate * queueing packets undergoing reassembly for all IPIFs
34950Sstevel@tonic-gate * associated with the ILL. The hash is based on the packet
34960Sstevel@tonic-gate * IP ident field. The ILL frag hash table was allocated
34970Sstevel@tonic-gate * as a timer block at the time the ILL was created. Whenever
34980Sstevel@tonic-gate * there is anything on the reassembly queue, the timer will
34990Sstevel@tonic-gate * be running.
35000Sstevel@tonic-gate */
350111042SErik.Nordmark@Sun.COM /* Handle vnic loopback of fragments */
350211042SErik.Nordmark@Sun.COM if (mp->b_datap->db_ref > 2)
350311042SErik.Nordmark@Sun.COM msg_len = 0;
350411042SErik.Nordmark@Sun.COM else
350511042SErik.Nordmark@Sun.COM msg_len = MBLKSIZE(mp);
350611042SErik.Nordmark@Sun.COM
35070Sstevel@tonic-gate tail_mp = mp;
35080Sstevel@tonic-gate while (tail_mp->b_cont != NULL) {
35090Sstevel@tonic-gate tail_mp = tail_mp->b_cont;
351011042SErik.Nordmark@Sun.COM if (tail_mp->b_datap->db_ref <= 2)
351111042SErik.Nordmark@Sun.COM msg_len += MBLKSIZE(tail_mp);
35120Sstevel@tonic-gate }
35130Sstevel@tonic-gate /*
35140Sstevel@tonic-gate * If the reassembly list for this ILL will get too big
35150Sstevel@tonic-gate * prune it.
35160Sstevel@tonic-gate */
35170Sstevel@tonic-gate
35180Sstevel@tonic-gate if ((msg_len + sizeof (*ipf) + ill->ill_frag_count) >=
35193448Sdh155122 ipst->ips_ip_reass_queue_bytes) {
352011042SErik.Nordmark@Sun.COM DTRACE_PROBE3(ip_reass_queue_bytes, uint_t, msg_len,
352111042SErik.Nordmark@Sun.COM uint_t, ill->ill_frag_count,
352211042SErik.Nordmark@Sun.COM uint_t, ipst->ips_ip_reass_queue_bytes);
35233448Sdh155122 ill_frag_prune(ill,
35243448Sdh155122 (ipst->ips_ip_reass_queue_bytes < msg_len) ? 0 :
35253448Sdh155122 (ipst->ips_ip_reass_queue_bytes - msg_len));
35260Sstevel@tonic-gate pruned = B_TRUE;
35270Sstevel@tonic-gate }
35280Sstevel@tonic-gate
35290Sstevel@tonic-gate ipfb = &ill->ill_frag_hash_tbl[ILL_FRAG_HASH_V6(*v6src_ptr, ident)];
35300Sstevel@tonic-gate mutex_enter(&ipfb->ipfb_lock);
35310Sstevel@tonic-gate
35320Sstevel@tonic-gate ipfp = &ipfb->ipfb_ipf;
35330Sstevel@tonic-gate /* Try to find an existing fragment queue for this packet. */
35340Sstevel@tonic-gate for (;;) {
35350Sstevel@tonic-gate ipf = ipfp[0];
35360Sstevel@tonic-gate if (ipf) {
35370Sstevel@tonic-gate /*
35380Sstevel@tonic-gate * It has to match on ident, source address, and
35390Sstevel@tonic-gate * dest address.
35400Sstevel@tonic-gate */
35410Sstevel@tonic-gate if (ipf->ipf_ident == ident &&
35420Sstevel@tonic-gate IN6_ARE_ADDR_EQUAL(&ipf->ipf_v6src, v6src_ptr) &&
35430Sstevel@tonic-gate IN6_ARE_ADDR_EQUAL(&ipf->ipf_v6dst, v6dst_ptr)) {
35440Sstevel@tonic-gate
35450Sstevel@tonic-gate /*
35460Sstevel@tonic-gate * If we have received too many
35470Sstevel@tonic-gate * duplicate fragments for this packet
35480Sstevel@tonic-gate * free it.
35490Sstevel@tonic-gate */
35500Sstevel@tonic-gate if (ipf->ipf_num_dups > ip_max_frag_dups) {
35510Sstevel@tonic-gate ill_frag_free_pkts(ill, ipfb, ipf, 1);
35520Sstevel@tonic-gate freemsg(mp);
35530Sstevel@tonic-gate mutex_exit(&ipfb->ipfb_lock);
35540Sstevel@tonic-gate return (NULL);
35550Sstevel@tonic-gate }
35560Sstevel@tonic-gate
35570Sstevel@tonic-gate break;
35580Sstevel@tonic-gate }
35590Sstevel@tonic-gate ipfp = &ipf->ipf_hash_next;
35600Sstevel@tonic-gate continue;
35610Sstevel@tonic-gate }
35620Sstevel@tonic-gate
35630Sstevel@tonic-gate
35640Sstevel@tonic-gate /*
35650Sstevel@tonic-gate * If we pruned the list, do we want to store this new
35660Sstevel@tonic-gate * fragment?. We apply an optimization here based on the
35670Sstevel@tonic-gate * fact that most fragments will be received in order.
35680Sstevel@tonic-gate * So if the offset of this incoming fragment is zero,
35690Sstevel@tonic-gate * it is the first fragment of a new packet. We will
35700Sstevel@tonic-gate * keep it. Otherwise drop the fragment, as we have
35710Sstevel@tonic-gate * probably pruned the packet already (since the
35720Sstevel@tonic-gate * packet cannot be found).
35730Sstevel@tonic-gate */
35740Sstevel@tonic-gate
35750Sstevel@tonic-gate if (pruned && offset != 0) {
35760Sstevel@tonic-gate mutex_exit(&ipfb->ipfb_lock);
35770Sstevel@tonic-gate freemsg(mp);
35780Sstevel@tonic-gate return (NULL);
35790Sstevel@tonic-gate }
35800Sstevel@tonic-gate
35810Sstevel@tonic-gate /* New guy. Allocate a frag message. */
35820Sstevel@tonic-gate mp1 = allocb(sizeof (*ipf), BPRI_MED);
35830Sstevel@tonic-gate if (!mp1) {
35843284Sapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
358511042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards", mp, ill);
35860Sstevel@tonic-gate freemsg(mp);
35870Sstevel@tonic-gate partial_reass_done:
35880Sstevel@tonic-gate mutex_exit(&ipfb->ipfb_lock);
35890Sstevel@tonic-gate return (NULL);
35900Sstevel@tonic-gate }
35910Sstevel@tonic-gate
35923448Sdh155122 if (ipfb->ipfb_frag_pkts >= MAX_FRAG_PKTS(ipst)) {
35930Sstevel@tonic-gate /*
35940Sstevel@tonic-gate * Too many fragmented packets in this hash bucket.
35950Sstevel@tonic-gate * Free the oldest.
35960Sstevel@tonic-gate */
35970Sstevel@tonic-gate ill_frag_free_pkts(ill, ipfb, ipfb->ipfb_ipf, 1);
35980Sstevel@tonic-gate }
35990Sstevel@tonic-gate
36000Sstevel@tonic-gate mp1->b_cont = mp;
36010Sstevel@tonic-gate
36020Sstevel@tonic-gate /* Initialize the fragment header. */
36030Sstevel@tonic-gate ipf = (ipf_t *)mp1->b_rptr;
36040Sstevel@tonic-gate ipf->ipf_mp = mp1;
36050Sstevel@tonic-gate ipf->ipf_ptphn = ipfp;
36060Sstevel@tonic-gate ipfp[0] = ipf;
36070Sstevel@tonic-gate ipf->ipf_hash_next = NULL;
36080Sstevel@tonic-gate ipf->ipf_ident = ident;
36090Sstevel@tonic-gate ipf->ipf_v6src = *v6src_ptr;
36100Sstevel@tonic-gate ipf->ipf_v6dst = *v6dst_ptr;
36110Sstevel@tonic-gate /* Record reassembly start time. */
36120Sstevel@tonic-gate ipf->ipf_timestamp = gethrestime_sec();
36130Sstevel@tonic-gate /* Record ipf generation and account for frag header */
36140Sstevel@tonic-gate ipf->ipf_gen = ill->ill_ipf_gen++;
3615741Smasputra ipf->ipf_count = MBLKSIZE(mp1);
36160Sstevel@tonic-gate ipf->ipf_protocol = nexthdr;
36170Sstevel@tonic-gate ipf->ipf_nf_hdr_len = 0;
36180Sstevel@tonic-gate ipf->ipf_prev_nexthdr_offset = 0;
36190Sstevel@tonic-gate ipf->ipf_last_frag_seen = B_FALSE;
36200Sstevel@tonic-gate ipf->ipf_ecn = ecn_info;
36210Sstevel@tonic-gate ipf->ipf_num_dups = 0;
36220Sstevel@tonic-gate ipfb->ipfb_frag_pkts++;
3623741Smasputra ipf->ipf_checksum = 0;
3624741Smasputra ipf->ipf_checksum_flags = 0;
3625741Smasputra
3626741Smasputra /* Store checksum value in fragment header */
3627741Smasputra if (sum_flags != 0) {
3628741Smasputra sum_val = (sum_val & 0xFFFF) + (sum_val >> 16);
3629741Smasputra sum_val = (sum_val & 0xFFFF) + (sum_val >> 16);
3630741Smasputra ipf->ipf_checksum = sum_val;
3631741Smasputra ipf->ipf_checksum_flags = sum_flags;
3632741Smasputra }
36330Sstevel@tonic-gate
36340Sstevel@tonic-gate /*
36350Sstevel@tonic-gate * We handle reassembly two ways. In the easy case,
36360Sstevel@tonic-gate * where all the fragments show up in order, we do
36370Sstevel@tonic-gate * minimal bookkeeping, and just clip new pieces on
36380Sstevel@tonic-gate * the end. If we ever see a hole, then we go off
36390Sstevel@tonic-gate * to ip_reassemble which has to mark the pieces and
36400Sstevel@tonic-gate * keep track of the number of holes, etc. Obviously,
36410Sstevel@tonic-gate * the point of having both mechanisms is so we can
36420Sstevel@tonic-gate * handle the easy case as efficiently as possible.
36430Sstevel@tonic-gate */
36440Sstevel@tonic-gate if (offset == 0) {
36450Sstevel@tonic-gate /* Easy case, in-order reassembly so far. */
36460Sstevel@tonic-gate /* Update the byte count */
36470Sstevel@tonic-gate ipf->ipf_count += msg_len;
36480Sstevel@tonic-gate ipf->ipf_tail_mp = tail_mp;
36490Sstevel@tonic-gate /*
36500Sstevel@tonic-gate * Keep track of next expected offset in
36510Sstevel@tonic-gate * ipf_end.
36520Sstevel@tonic-gate */
36530Sstevel@tonic-gate ipf->ipf_end = end;
36540Sstevel@tonic-gate ipf->ipf_nf_hdr_len = hdr_length;
365511042SErik.Nordmark@Sun.COM ipf->ipf_prev_nexthdr_offset = prev_nexthdr_offset;
36560Sstevel@tonic-gate } else {
36570Sstevel@tonic-gate /* Hard case, hole at the beginning. */
36580Sstevel@tonic-gate ipf->ipf_tail_mp = NULL;
36590Sstevel@tonic-gate /*
36600Sstevel@tonic-gate * ipf_end == 0 means that we have given up
36610Sstevel@tonic-gate * on easy reassembly.
36620Sstevel@tonic-gate */
36630Sstevel@tonic-gate ipf->ipf_end = 0;
3664741Smasputra
3665741Smasputra /* Forget checksum offload from now on */
3666741Smasputra ipf->ipf_checksum_flags = 0;
3667741Smasputra
36680Sstevel@tonic-gate /*
36690Sstevel@tonic-gate * ipf_hole_cnt is set by ip_reassemble.
36700Sstevel@tonic-gate * ipf_count is updated by ip_reassemble.
36710Sstevel@tonic-gate * No need to check for return value here
36720Sstevel@tonic-gate * as we don't expect reassembly to complete or
36730Sstevel@tonic-gate * fail for the first fragment itself.
36740Sstevel@tonic-gate */
36750Sstevel@tonic-gate (void) ip_reassemble(mp, ipf, offset, more_frags, ill,
36760Sstevel@tonic-gate msg_len);
36770Sstevel@tonic-gate }
36780Sstevel@tonic-gate /* Update per ipfb and ill byte counts */
36790Sstevel@tonic-gate ipfb->ipfb_count += ipf->ipf_count;
36800Sstevel@tonic-gate ASSERT(ipfb->ipfb_count > 0); /* Wraparound */
36816759Sgeorges atomic_add_32(&ill->ill_frag_count, ipf->ipf_count);
36820Sstevel@tonic-gate /* If the frag timer wasn't already going, start it. */
36830Sstevel@tonic-gate mutex_enter(&ill->ill_lock);
36840Sstevel@tonic-gate ill_frag_timer_start(ill);
36850Sstevel@tonic-gate mutex_exit(&ill->ill_lock);
36860Sstevel@tonic-gate goto partial_reass_done;
36870Sstevel@tonic-gate }
36880Sstevel@tonic-gate
36890Sstevel@tonic-gate /*
3690741Smasputra * If the packet's flag has changed (it could be coming up
3691741Smasputra * from an interface different than the previous, therefore
3692741Smasputra * possibly different checksum capability), then forget about
3693741Smasputra * any stored checksum states. Otherwise add the value to
3694741Smasputra * the existing one stored in the fragment header.
3695741Smasputra */
3696741Smasputra if (sum_flags != 0 && sum_flags == ipf->ipf_checksum_flags) {
3697741Smasputra sum_val += ipf->ipf_checksum;
3698741Smasputra sum_val = (sum_val & 0xFFFF) + (sum_val >> 16);
3699741Smasputra sum_val = (sum_val & 0xFFFF) + (sum_val >> 16);
3700741Smasputra ipf->ipf_checksum = sum_val;
3701741Smasputra } else if (ipf->ipf_checksum_flags != 0) {
3702741Smasputra /* Forget checksum offload from now on */
3703741Smasputra ipf->ipf_checksum_flags = 0;
3704741Smasputra }
3705741Smasputra
3706741Smasputra /*
37070Sstevel@tonic-gate * We have a new piece of a datagram which is already being
37080Sstevel@tonic-gate * reassembled. Update the ECN info if all IP fragments
37090Sstevel@tonic-gate * are ECN capable. If there is one which is not, clear
37100Sstevel@tonic-gate * all the info. If there is at least one which has CE
37110Sstevel@tonic-gate * code point, IP needs to report that up to transport.
37120Sstevel@tonic-gate */
37130Sstevel@tonic-gate if (ecn_info != IPH_ECN_NECT && ipf->ipf_ecn != IPH_ECN_NECT) {
37140Sstevel@tonic-gate if (ecn_info == IPH_ECN_CE)
37150Sstevel@tonic-gate ipf->ipf_ecn = IPH_ECN_CE;
37160Sstevel@tonic-gate } else {
37170Sstevel@tonic-gate ipf->ipf_ecn = IPH_ECN_NECT;
37180Sstevel@tonic-gate }
37190Sstevel@tonic-gate
37200Sstevel@tonic-gate if (offset && ipf->ipf_end == offset) {
37210Sstevel@tonic-gate /* The new fragment fits at the end */
37220Sstevel@tonic-gate ipf->ipf_tail_mp->b_cont = mp;
37230Sstevel@tonic-gate /* Update the byte count */
37240Sstevel@tonic-gate ipf->ipf_count += msg_len;
37250Sstevel@tonic-gate /* Update per ipfb and ill byte counts */
37260Sstevel@tonic-gate ipfb->ipfb_count += msg_len;
37270Sstevel@tonic-gate ASSERT(ipfb->ipfb_count > 0); /* Wraparound */
37286759Sgeorges atomic_add_32(&ill->ill_frag_count, msg_len);
37290Sstevel@tonic-gate if (more_frags) {
37300Sstevel@tonic-gate /* More to come. */
37310Sstevel@tonic-gate ipf->ipf_end = end;
37320Sstevel@tonic-gate ipf->ipf_tail_mp = tail_mp;
37330Sstevel@tonic-gate goto partial_reass_done;
37340Sstevel@tonic-gate }
37350Sstevel@tonic-gate } else {
37360Sstevel@tonic-gate /*
37370Sstevel@tonic-gate * Go do the hard cases.
37380Sstevel@tonic-gate * Call ip_reassemble().
37390Sstevel@tonic-gate */
37400Sstevel@tonic-gate int ret;
37410Sstevel@tonic-gate
37420Sstevel@tonic-gate if (offset == 0) {
37430Sstevel@tonic-gate if (ipf->ipf_prev_nexthdr_offset == 0) {
37440Sstevel@tonic-gate ipf->ipf_nf_hdr_len = hdr_length;
37450Sstevel@tonic-gate ipf->ipf_prev_nexthdr_offset =
374611042SErik.Nordmark@Sun.COM prev_nexthdr_offset;
37470Sstevel@tonic-gate }
37480Sstevel@tonic-gate }
37490Sstevel@tonic-gate /* Save current byte count */
37500Sstevel@tonic-gate count = ipf->ipf_count;
37510Sstevel@tonic-gate ret = ip_reassemble(mp, ipf, offset, more_frags, ill, msg_len);
37520Sstevel@tonic-gate
37530Sstevel@tonic-gate /* Count of bytes added and subtracted (freeb()ed) */
37540Sstevel@tonic-gate count = ipf->ipf_count - count;
37550Sstevel@tonic-gate if (count) {
37560Sstevel@tonic-gate /* Update per ipfb and ill byte counts */
37570Sstevel@tonic-gate ipfb->ipfb_count += count;
37580Sstevel@tonic-gate ASSERT(ipfb->ipfb_count > 0); /* Wraparound */
37596759Sgeorges atomic_add_32(&ill->ill_frag_count, count);
37600Sstevel@tonic-gate }
37610Sstevel@tonic-gate if (ret == IP_REASS_PARTIAL) {
37620Sstevel@tonic-gate goto partial_reass_done;
37630Sstevel@tonic-gate } else if (ret == IP_REASS_FAILED) {
37640Sstevel@tonic-gate /* Reassembly failed. Free up all resources */
37650Sstevel@tonic-gate ill_frag_free_pkts(ill, ipfb, ipf, 1);
37660Sstevel@tonic-gate for (t_mp = mp; t_mp != NULL; t_mp = t_mp->b_cont) {
37670Sstevel@tonic-gate IP_REASS_SET_START(t_mp, 0);
37680Sstevel@tonic-gate IP_REASS_SET_END(t_mp, 0);
37690Sstevel@tonic-gate }
37700Sstevel@tonic-gate freemsg(mp);
37710Sstevel@tonic-gate goto partial_reass_done;
37720Sstevel@tonic-gate }
37730Sstevel@tonic-gate
37740Sstevel@tonic-gate /* We will reach here iff 'ret' is IP_REASS_COMPLETE */
37750Sstevel@tonic-gate }
37760Sstevel@tonic-gate /*
37770Sstevel@tonic-gate * We have completed reassembly. Unhook the frag header from
37780Sstevel@tonic-gate * the reassembly list.
37790Sstevel@tonic-gate *
37800Sstevel@tonic-gate * Grab the unfragmentable header length next header value out
37810Sstevel@tonic-gate * of the first fragment
37820Sstevel@tonic-gate */
37830Sstevel@tonic-gate ASSERT(ipf->ipf_nf_hdr_len != 0);
37840Sstevel@tonic-gate hdr_length = ipf->ipf_nf_hdr_len;
37850Sstevel@tonic-gate
37860Sstevel@tonic-gate /*
37870Sstevel@tonic-gate * Before we free the frag header, record the ECN info
37880Sstevel@tonic-gate * to report back to the transport.
37890Sstevel@tonic-gate */
37900Sstevel@tonic-gate ecn_info = ipf->ipf_ecn;
37910Sstevel@tonic-gate
37920Sstevel@tonic-gate /*
37930Sstevel@tonic-gate * Store the nextheader field in the header preceding the fragment
37940Sstevel@tonic-gate * header
37950Sstevel@tonic-gate */
37960Sstevel@tonic-gate nexthdr = ipf->ipf_protocol;
379711042SErik.Nordmark@Sun.COM prev_nexthdr_offset = ipf->ipf_prev_nexthdr_offset;
37980Sstevel@tonic-gate ipfp = ipf->ipf_ptphn;
3799741Smasputra
3800741Smasputra /* We need to supply these to caller */
3801741Smasputra if ((sum_flags = ipf->ipf_checksum_flags) != 0)
3802741Smasputra sum_val = ipf->ipf_checksum;
3803741Smasputra else
3804741Smasputra sum_val = 0;
3805741Smasputra
38060Sstevel@tonic-gate mp1 = ipf->ipf_mp;
38070Sstevel@tonic-gate count = ipf->ipf_count;
38080Sstevel@tonic-gate ipf = ipf->ipf_hash_next;
38090Sstevel@tonic-gate if (ipf)
38100Sstevel@tonic-gate ipf->ipf_ptphn = ipfp;
38110Sstevel@tonic-gate ipfp[0] = ipf;
38126759Sgeorges atomic_add_32(&ill->ill_frag_count, -count);
38130Sstevel@tonic-gate ASSERT(ipfb->ipfb_count >= count);
38140Sstevel@tonic-gate ipfb->ipfb_count -= count;
38150Sstevel@tonic-gate ipfb->ipfb_frag_pkts--;
38160Sstevel@tonic-gate mutex_exit(&ipfb->ipfb_lock);
38170Sstevel@tonic-gate /* Ditch the frag header. */
38180Sstevel@tonic-gate mp = mp1->b_cont;
38190Sstevel@tonic-gate freeb(mp1);
38200Sstevel@tonic-gate
38210Sstevel@tonic-gate /*
38220Sstevel@tonic-gate * Make sure the packet is good by doing some sanity
38230Sstevel@tonic-gate * check. If bad we can silentely drop the packet.
38240Sstevel@tonic-gate */
38250Sstevel@tonic-gate reass_done:
38260Sstevel@tonic-gate if (hdr_length < sizeof (ip6_frag_t)) {
38273284Sapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors);
382811042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInHdrErrors", mp, ill);
382911042SErik.Nordmark@Sun.COM ip1dbg(("ip_input_fragment_v6: bad packet\n"));
38300Sstevel@tonic-gate freemsg(mp);
38310Sstevel@tonic-gate return (NULL);
38320Sstevel@tonic-gate }
38330Sstevel@tonic-gate
38340Sstevel@tonic-gate /*
38350Sstevel@tonic-gate * Remove the fragment header from the initial header by
38360Sstevel@tonic-gate * splitting the mblk into the non-fragmentable header and
38370Sstevel@tonic-gate * everthing after the fragment extension header. This has the
38380Sstevel@tonic-gate * side effect of putting all the headers that need destination
38390Sstevel@tonic-gate * processing into the b_cont block-- on return this fact is
38400Sstevel@tonic-gate * used in order to avoid having to look at the extensions
38410Sstevel@tonic-gate * already processed.
38420Sstevel@tonic-gate *
38430Sstevel@tonic-gate * Note that this code assumes that the unfragmentable portion
38440Sstevel@tonic-gate * of the header is in the first mblk and increments
38450Sstevel@tonic-gate * the read pointer past it. If this assumption is broken
38460Sstevel@tonic-gate * this code fails badly.
38470Sstevel@tonic-gate */
38480Sstevel@tonic-gate if (mp->b_rptr + hdr_length != mp->b_wptr) {
38490Sstevel@tonic-gate mblk_t *nmp;
38500Sstevel@tonic-gate
38510Sstevel@tonic-gate if (!(nmp = dupb(mp))) {
385211042SErik.Nordmark@Sun.COM ip1dbg(("ip_input_fragment_v6: dupb failed\n"));
38533284Sapersson BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
385411042SErik.Nordmark@Sun.COM ip_drop_input("ipIfStatsInDiscards", mp, ill);
38550Sstevel@tonic-gate freemsg(mp);
38560Sstevel@tonic-gate return (NULL);
38570Sstevel@tonic-gate }
38580Sstevel@tonic-gate nmp->b_cont = mp->b_cont;
38590Sstevel@tonic-gate mp->b_cont = nmp;
38600Sstevel@tonic-gate nmp->b_rptr += hdr_length;
38610Sstevel@tonic-gate }
38620Sstevel@tonic-gate mp->b_wptr = mp->b_rptr + hdr_length - sizeof (ip6_frag_t);
38630Sstevel@tonic-gate
38640Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr;
386511042SErik.Nordmark@Sun.COM ((char *)ip6h)[prev_nexthdr_offset] = nexthdr;
38660Sstevel@tonic-gate
38670Sstevel@tonic-gate /* Restore original IP length in header. */
386811042SErik.Nordmark@Sun.COM packet_size = msgdsize(mp);
386911042SErik.Nordmark@Sun.COM ip6h->ip6_plen = htons((uint16_t)(packet_size - IPV6_HDR_LEN));
38700Sstevel@tonic-gate /* Record the ECN info. */
38710Sstevel@tonic-gate ip6h->ip6_vcf &= htonl(0xFFCFFFFF);
38720Sstevel@tonic-gate ip6h->ip6_vcf |= htonl(ecn_info << 20);
38730Sstevel@tonic-gate
387411042SErik.Nordmark@Sun.COM /* Update the receive attributes */
387511042SErik.Nordmark@Sun.COM ira->ira_pktlen = packet_size;
387611042SErik.Nordmark@Sun.COM ira->ira_ip_hdr_length = hdr_length - sizeof (ip6_frag_t);
387711042SErik.Nordmark@Sun.COM ira->ira_protocol = nexthdr;
387811042SErik.Nordmark@Sun.COM
387911042SErik.Nordmark@Sun.COM /* Reassembly is successful; set checksum information in packet */
388011042SErik.Nordmark@Sun.COM DB_CKSUM16(mp) = (uint16_t)sum_val;
388111042SErik.Nordmark@Sun.COM DB_CKSUMFLAGS(mp) = sum_flags;
388211042SErik.Nordmark@Sun.COM DB_CKSUMSTART(mp) = ira->ira_ip_hdr_length;
3883741Smasputra
38840Sstevel@tonic-gate return (mp);
38850Sstevel@tonic-gate }
38860Sstevel@tonic-gate
38870Sstevel@tonic-gate /*
38888847Sdanmcd@sun.com * Given an mblk and a ptr, find the destination address in an IPv6 routing
38898847Sdanmcd@sun.com * header.
38908847Sdanmcd@sun.com */
38918847Sdanmcd@sun.com static in6_addr_t
pluck_out_dst(const mblk_t * mp,uint8_t * whereptr,in6_addr_t oldrv)389211042SErik.Nordmark@Sun.COM pluck_out_dst(const mblk_t *mp, uint8_t *whereptr, in6_addr_t oldrv)
38938847Sdanmcd@sun.com {
38948847Sdanmcd@sun.com ip6_rthdr0_t *rt0;
38958847Sdanmcd@sun.com int segleft, numaddr;
38968847Sdanmcd@sun.com in6_addr_t *ap, rv = oldrv;
38978847Sdanmcd@sun.com
38988847Sdanmcd@sun.com rt0 = (ip6_rthdr0_t *)whereptr;
38998847Sdanmcd@sun.com if (rt0->ip6r0_type != 0 && rt0->ip6r0_type != 2) {
39008847Sdanmcd@sun.com DTRACE_PROBE2(pluck_out_dst_unknown_type, mblk_t *, mp,
39018847Sdanmcd@sun.com uint8_t *, whereptr);
39028847Sdanmcd@sun.com return (rv);
39038847Sdanmcd@sun.com }
39048847Sdanmcd@sun.com segleft = rt0->ip6r0_segleft;
39058847Sdanmcd@sun.com numaddr = rt0->ip6r0_len / 2;
39068847Sdanmcd@sun.com
39078847Sdanmcd@sun.com if ((rt0->ip6r0_len & 0x1) ||
390811042SErik.Nordmark@Sun.COM (mp != NULL && whereptr + (rt0->ip6r0_len + 1) * 8 > mp->b_wptr) ||
39098847Sdanmcd@sun.com (segleft > rt0->ip6r0_len / 2)) {
39108847Sdanmcd@sun.com /*
39118847Sdanmcd@sun.com * Corrupt packet. Either the routing header length is odd
39128847Sdanmcd@sun.com * (can't happen) or mismatched compared to the packet, or the
39138847Sdanmcd@sun.com * number of addresses is. Return what we can. This will
39148847Sdanmcd@sun.com * only be a problem on forwarded packets that get squeezed
39158847Sdanmcd@sun.com * through an outbound tunnel enforcing IPsec Tunnel Mode.
39168847Sdanmcd@sun.com */
39178847Sdanmcd@sun.com DTRACE_PROBE2(pluck_out_dst_badpkt, mblk_t *, mp, uint8_t *,
39188847Sdanmcd@sun.com whereptr);
39198847Sdanmcd@sun.com return (rv);
39208847Sdanmcd@sun.com }
39218847Sdanmcd@sun.com
39228847Sdanmcd@sun.com if (segleft != 0) {
39238847Sdanmcd@sun.com ap = (in6_addr_t *)((char *)rt0 + sizeof (*rt0));
39248847Sdanmcd@sun.com rv = ap[numaddr - 1];
39258847Sdanmcd@sun.com }
39268847Sdanmcd@sun.com
39278847Sdanmcd@sun.com return (rv);
39288847Sdanmcd@sun.com }
39298847Sdanmcd@sun.com
39308847Sdanmcd@sun.com /*
39310Sstevel@tonic-gate * Walk through the options to see if there is a routing header.
39320Sstevel@tonic-gate * If present get the destination which is the last address of
39330Sstevel@tonic-gate * the option.
393411042SErik.Nordmark@Sun.COM * mp needs to be provided in cases when the extension headers might span
393511042SErik.Nordmark@Sun.COM * b_cont; mp is never modified by this function.
39360Sstevel@tonic-gate */
39370Sstevel@tonic-gate in6_addr_t
ip_get_dst_v6(ip6_t * ip6h,const mblk_t * mp,boolean_t * is_fragment)393811042SErik.Nordmark@Sun.COM ip_get_dst_v6(ip6_t *ip6h, const mblk_t *mp, boolean_t *is_fragment)
39390Sstevel@tonic-gate {
394011042SErik.Nordmark@Sun.COM const mblk_t *current_mp = mp;
39410Sstevel@tonic-gate uint8_t nexthdr;
39420Sstevel@tonic-gate uint8_t *whereptr;
39430Sstevel@tonic-gate int ehdrlen;
39448847Sdanmcd@sun.com in6_addr_t rv;
39458705Sdanmcd@sun.com
39468705Sdanmcd@sun.com whereptr = (uint8_t *)ip6h;
39478705Sdanmcd@sun.com ehdrlen = sizeof (ip6_t);
39488705Sdanmcd@sun.com
39498847Sdanmcd@sun.com /* We assume at least the IPv6 base header is within one mblk. */
395011042SErik.Nordmark@Sun.COM ASSERT(mp == NULL ||
395111042SErik.Nordmark@Sun.COM (mp->b_rptr <= whereptr && mp->b_wptr >= whereptr + ehdrlen));
39528847Sdanmcd@sun.com
39538847Sdanmcd@sun.com rv = ip6h->ip6_dst;
39548847Sdanmcd@sun.com nexthdr = ip6h->ip6_nxt;
39558847Sdanmcd@sun.com if (is_fragment != NULL)
39568847Sdanmcd@sun.com *is_fragment = B_FALSE;
39578847Sdanmcd@sun.com
39588847Sdanmcd@sun.com /*
39598847Sdanmcd@sun.com * We also assume (thanks to ipsec_tun_outbound()'s pullup) that
39608847Sdanmcd@sun.com * no extension headers will be split across mblks.
39618847Sdanmcd@sun.com */
39628847Sdanmcd@sun.com
39638847Sdanmcd@sun.com while (nexthdr == IPPROTO_HOPOPTS || nexthdr == IPPROTO_DSTOPTS ||
39648847Sdanmcd@sun.com nexthdr == IPPROTO_ROUTING) {
39658847Sdanmcd@sun.com if (nexthdr == IPPROTO_ROUTING)
39668847Sdanmcd@sun.com rv = pluck_out_dst(current_mp, whereptr, rv);
39678847Sdanmcd@sun.com
39688847Sdanmcd@sun.com /*
39698847Sdanmcd@sun.com * All IPv6 extension headers have the next-header in byte
39708847Sdanmcd@sun.com * 0, and the (length - 8) in 8-byte-words.
39718847Sdanmcd@sun.com */
397211042SErik.Nordmark@Sun.COM while (current_mp != NULL &&
397311042SErik.Nordmark@Sun.COM whereptr + ehdrlen >= current_mp->b_wptr) {
39748705Sdanmcd@sun.com ehdrlen -= (current_mp->b_wptr - whereptr);
39758705Sdanmcd@sun.com current_mp = current_mp->b_cont;
39768705Sdanmcd@sun.com if (current_mp == NULL) {
39778705Sdanmcd@sun.com /* Bad packet. Return what we can. */
39788847Sdanmcd@sun.com DTRACE_PROBE3(ip_get_dst_v6_badpkt, mblk_t *,
39798847Sdanmcd@sun.com mp, mblk_t *, current_mp, ip6_t *, ip6h);
39808705Sdanmcd@sun.com goto done;
39818705Sdanmcd@sun.com }
39828705Sdanmcd@sun.com whereptr = current_mp->b_rptr;
39838705Sdanmcd@sun.com }
39848705Sdanmcd@sun.com whereptr += ehdrlen;
39858705Sdanmcd@sun.com
39868847Sdanmcd@sun.com nexthdr = *whereptr;
398711042SErik.Nordmark@Sun.COM ASSERT(current_mp == NULL || whereptr + 1 < current_mp->b_wptr);
39888847Sdanmcd@sun.com ehdrlen = (*(whereptr + 1) + 1) * 8;
39890Sstevel@tonic-gate }
39900Sstevel@tonic-gate
39910Sstevel@tonic-gate done:
39928847Sdanmcd@sun.com if (nexthdr == IPPROTO_FRAGMENT && is_fragment != NULL)
39938847Sdanmcd@sun.com *is_fragment = B_TRUE;
39940Sstevel@tonic-gate return (rv);
39950Sstevel@tonic-gate }
39960Sstevel@tonic-gate
39970Sstevel@tonic-gate /*
39980Sstevel@tonic-gate * ip_source_routed_v6:
399911042SErik.Nordmark@Sun.COM * This function is called by redirect code (called from ip_input_v6) to
40000Sstevel@tonic-gate * know whether this packet is source routed through this node i.e
40010Sstevel@tonic-gate * whether this node (router) is part of the journey. This
40020Sstevel@tonic-gate * function is called under two cases :
40030Sstevel@tonic-gate *
40040Sstevel@tonic-gate * case 1 : Routing header was processed by this node and
40050Sstevel@tonic-gate * ip_process_rthdr replaced ip6_dst with the next hop
40068348SEric.Yu@Sun.COM * and we are forwarding the packet to the next hop.
40070Sstevel@tonic-gate *
40080Sstevel@tonic-gate * case 2 : Routing header was not processed by this node and we
40090Sstevel@tonic-gate * are just forwarding the packet.
40100Sstevel@tonic-gate *
40110Sstevel@tonic-gate * For case (1) we don't want to send redirects. For case(2) we
40120Sstevel@tonic-gate * want to send redirects.
40130Sstevel@tonic-gate */
40140Sstevel@tonic-gate static boolean_t
ip_source_routed_v6(ip6_t * ip6h,mblk_t * mp,ip_stack_t * ipst)40153448Sdh155122 ip_source_routed_v6(ip6_t *ip6h, mblk_t *mp, ip_stack_t *ipst)
40160Sstevel@tonic-gate {
40170Sstevel@tonic-gate uint8_t nexthdr;
40180Sstevel@tonic-gate in6_addr_t *addrptr;
40190Sstevel@tonic-gate ip6_rthdr0_t *rthdr;
40200Sstevel@tonic-gate uint8_t numaddr;
40210Sstevel@tonic-gate ip6_hbh_t *hbhhdr;
40220Sstevel@tonic-gate uint_t ehdrlen;
40230Sstevel@tonic-gate uint8_t *byteptr;
40240Sstevel@tonic-gate
40250Sstevel@tonic-gate ip2dbg(("ip_source_routed_v6\n"));
40260Sstevel@tonic-gate nexthdr = ip6h->ip6_nxt;
40270Sstevel@tonic-gate ehdrlen = IPV6_HDR_LEN;
40280Sstevel@tonic-gate
40290Sstevel@tonic-gate /* if a routing hdr is preceeded by HOPOPT or DSTOPT */
40300Sstevel@tonic-gate while (nexthdr == IPPROTO_HOPOPTS ||
40310Sstevel@tonic-gate nexthdr == IPPROTO_DSTOPTS) {
40320Sstevel@tonic-gate byteptr = (uint8_t *)ip6h + ehdrlen;
40330Sstevel@tonic-gate /*
40340Sstevel@tonic-gate * Check if we have already processed
40350Sstevel@tonic-gate * packets or we are just a forwarding
40360Sstevel@tonic-gate * router which only pulled up msgs up
40370Sstevel@tonic-gate * to IPV6HDR and one HBH ext header
40380Sstevel@tonic-gate */
40390Sstevel@tonic-gate if (byteptr + MIN_EHDR_LEN > mp->b_wptr) {
40400Sstevel@tonic-gate ip2dbg(("ip_source_routed_v6: Extension"
40410Sstevel@tonic-gate " headers not processed\n"));
40420Sstevel@tonic-gate return (B_FALSE);
40430Sstevel@tonic-gate }
40440Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)byteptr;
40450Sstevel@tonic-gate nexthdr = hbhhdr->ip6h_nxt;
40460Sstevel@tonic-gate ehdrlen = ehdrlen + 8 * (hbhhdr->ip6h_len + 1);
40470Sstevel@tonic-gate }
40480Sstevel@tonic-gate switch (nexthdr) {
40490Sstevel@tonic-gate case IPPROTO_ROUTING:
40500Sstevel@tonic-gate byteptr = (uint8_t *)ip6h + ehdrlen;
40510Sstevel@tonic-gate /*
40520Sstevel@tonic-gate * If for some reason, we haven't pulled up
40530Sstevel@tonic-gate * the routing hdr data mblk, then we must
40540Sstevel@tonic-gate * not have processed it at all. So for sure
40550Sstevel@tonic-gate * we are not part of the source routed journey.
40560Sstevel@tonic-gate */
40570Sstevel@tonic-gate if (byteptr + MIN_EHDR_LEN > mp->b_wptr) {
40580Sstevel@tonic-gate ip2dbg(("ip_source_routed_v6: Routing"
40590Sstevel@tonic-gate " header not processed\n"));
40600Sstevel@tonic-gate return (B_FALSE);
40610Sstevel@tonic-gate }
40620Sstevel@tonic-gate rthdr = (ip6_rthdr0_t *)byteptr;
40630Sstevel@tonic-gate /*
40640Sstevel@tonic-gate * Either we are an intermediate router or the
40650Sstevel@tonic-gate * last hop before destination and we have
40660Sstevel@tonic-gate * already processed the routing header.
40670Sstevel@tonic-gate * If segment_left is greater than or equal to zero,
40680Sstevel@tonic-gate * then we must be the (numaddr - segleft) entry
40690Sstevel@tonic-gate * of the routing header. Although ip6r0_segleft
40700Sstevel@tonic-gate * is a unit8_t variable, we still check for zero
40710Sstevel@tonic-gate * or greater value, if in case the data type
40720Sstevel@tonic-gate * is changed someday in future.
40730Sstevel@tonic-gate */
40740Sstevel@tonic-gate if (rthdr->ip6r0_segleft > 0 ||
40750Sstevel@tonic-gate rthdr->ip6r0_segleft == 0) {
40760Sstevel@tonic-gate numaddr = rthdr->ip6r0_len / 2;
40770Sstevel@tonic-gate addrptr = (in6_addr_t *)((char *)rthdr +
40780Sstevel@tonic-gate sizeof (*rthdr));
40790Sstevel@tonic-gate addrptr += (numaddr - (rthdr->ip6r0_segleft + 1));
40800Sstevel@tonic-gate if (addrptr != NULL) {
408111042SErik.Nordmark@Sun.COM if (ip_type_v6(addrptr, ipst) == IRE_LOCAL)
40820Sstevel@tonic-gate return (B_TRUE);
408311042SErik.Nordmark@Sun.COM ip1dbg(("ip_source_routed_v6: Not local\n"));
40840Sstevel@tonic-gate }
40850Sstevel@tonic-gate }
40860Sstevel@tonic-gate /* FALLTHRU */
40870Sstevel@tonic-gate default:
40880Sstevel@tonic-gate ip2dbg(("ip_source_routed_v6: Not source routed here\n"));
40890Sstevel@tonic-gate return (B_FALSE);
40900Sstevel@tonic-gate }
40910Sstevel@tonic-gate }
40920Sstevel@tonic-gate
40930Sstevel@tonic-gate /*
40940Sstevel@tonic-gate * IPv6 fragmentation. Essentially the same as IPv4 fragmentation.
40950Sstevel@tonic-gate * We have not optimized this in terms of number of mblks
40960Sstevel@tonic-gate * allocated. For instance, for each fragment sent we always allocate a
40970Sstevel@tonic-gate * mblk to hold the IPv6 header and fragment header.
40980Sstevel@tonic-gate *
409911042SErik.Nordmark@Sun.COM * Assumes that all the extension headers are contained in the first mblk
410011042SErik.Nordmark@Sun.COM * and that the fragment header has has already been added by calling
410111042SErik.Nordmark@Sun.COM * ip_fraghdr_add_v6.
41020Sstevel@tonic-gate */
410311042SErik.Nordmark@Sun.COM int
ip_fragment_v6(mblk_t * mp,nce_t * nce,iaflags_t ixaflags,uint_t pkt_len,uint32_t max_frag,uint32_t xmit_hint,zoneid_t szone,zoneid_t nolzid,pfirepostfrag_t postfragfn,uintptr_t * ixa_cookie)410411042SErik.Nordmark@Sun.COM ip_fragment_v6(mblk_t *mp, nce_t *nce, iaflags_t ixaflags, uint_t pkt_len,
410511042SErik.Nordmark@Sun.COM uint32_t max_frag, uint32_t xmit_hint, zoneid_t szone, zoneid_t nolzid,
410611042SErik.Nordmark@Sun.COM pfirepostfrag_t postfragfn, uintptr_t *ixa_cookie)
41070Sstevel@tonic-gate {
41080Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)mp->b_rptr;
41090Sstevel@tonic-gate ip6_t *fip6h;
41100Sstevel@tonic-gate mblk_t *hmp;
41110Sstevel@tonic-gate mblk_t *hmp0;
41120Sstevel@tonic-gate mblk_t *dmp;
41130Sstevel@tonic-gate ip6_frag_t *fraghdr;
41140Sstevel@tonic-gate size_t unfragmentable_len;
41150Sstevel@tonic-gate size_t mlen;
41160Sstevel@tonic-gate size_t max_chunk;
41170Sstevel@tonic-gate uint16_t off_flags;
41180Sstevel@tonic-gate uint16_t offset = 0;
411911042SErik.Nordmark@Sun.COM ill_t *ill = nce->nce_ill;
412011042SErik.Nordmark@Sun.COM uint8_t nexthdr;
412111042SErik.Nordmark@Sun.COM uint8_t *ptr;
412211042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst;
412311042SErik.Nordmark@Sun.COM uint_t priority = mp->b_band;
412411042SErik.Nordmark@Sun.COM int error = 0;
412511042SErik.Nordmark@Sun.COM
412611042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragReqds);
412711042SErik.Nordmark@Sun.COM if (max_frag == 0) {
412811042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
412911042SErik.Nordmark@Sun.COM ip_drop_output("FragFails: zero max_frag", mp, ill);
413011042SErik.Nordmark@Sun.COM freemsg(mp);
413111042SErik.Nordmark@Sun.COM return (EINVAL);
413211042SErik.Nordmark@Sun.COM }
413311042SErik.Nordmark@Sun.COM
413411042SErik.Nordmark@Sun.COM /*
413511042SErik.Nordmark@Sun.COM * Caller should have added fraghdr_t to pkt_len, and also
413611042SErik.Nordmark@Sun.COM * updated ip6_plen.
413711042SErik.Nordmark@Sun.COM */
413811042SErik.Nordmark@Sun.COM ASSERT(ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN == pkt_len);
413911042SErik.Nordmark@Sun.COM ASSERT(msgdsize(mp) == pkt_len);
414011042SErik.Nordmark@Sun.COM
414111042SErik.Nordmark@Sun.COM /*
414211042SErik.Nordmark@Sun.COM * Determine the length of the unfragmentable portion of this
414311042SErik.Nordmark@Sun.COM * datagram. This consists of the IPv6 header, a potential
414411042SErik.Nordmark@Sun.COM * hop-by-hop options header, a potential pre-routing-header
414511042SErik.Nordmark@Sun.COM * destination options header, and a potential routing header.
414611042SErik.Nordmark@Sun.COM */
414711042SErik.Nordmark@Sun.COM nexthdr = ip6h->ip6_nxt;
414811042SErik.Nordmark@Sun.COM ptr = (uint8_t *)&ip6h[1];
414911042SErik.Nordmark@Sun.COM
415011042SErik.Nordmark@Sun.COM if (nexthdr == IPPROTO_HOPOPTS) {
415111042SErik.Nordmark@Sun.COM ip6_hbh_t *hbh_hdr;
415211042SErik.Nordmark@Sun.COM uint_t hdr_len;
415311042SErik.Nordmark@Sun.COM
415411042SErik.Nordmark@Sun.COM hbh_hdr = (ip6_hbh_t *)ptr;
415511042SErik.Nordmark@Sun.COM hdr_len = 8 * (hbh_hdr->ip6h_len + 1);
415611042SErik.Nordmark@Sun.COM nexthdr = hbh_hdr->ip6h_nxt;
415711042SErik.Nordmark@Sun.COM ptr += hdr_len;
415811042SErik.Nordmark@Sun.COM }
415911042SErik.Nordmark@Sun.COM if (nexthdr == IPPROTO_DSTOPTS) {
416011042SErik.Nordmark@Sun.COM ip6_dest_t *dest_hdr;
416111042SErik.Nordmark@Sun.COM uint_t hdr_len;
416211042SErik.Nordmark@Sun.COM
416311042SErik.Nordmark@Sun.COM dest_hdr = (ip6_dest_t *)ptr;
416411042SErik.Nordmark@Sun.COM if (dest_hdr->ip6d_nxt == IPPROTO_ROUTING) {
416511042SErik.Nordmark@Sun.COM hdr_len = 8 * (dest_hdr->ip6d_len + 1);
416611042SErik.Nordmark@Sun.COM nexthdr = dest_hdr->ip6d_nxt;
416711042SErik.Nordmark@Sun.COM ptr += hdr_len;
416811042SErik.Nordmark@Sun.COM }
416911042SErik.Nordmark@Sun.COM }
417011042SErik.Nordmark@Sun.COM if (nexthdr == IPPROTO_ROUTING) {
417111042SErik.Nordmark@Sun.COM ip6_rthdr_t *rthdr;
417211042SErik.Nordmark@Sun.COM uint_t hdr_len;
417311042SErik.Nordmark@Sun.COM
417411042SErik.Nordmark@Sun.COM rthdr = (ip6_rthdr_t *)ptr;
417511042SErik.Nordmark@Sun.COM nexthdr = rthdr->ip6r_nxt;
417611042SErik.Nordmark@Sun.COM hdr_len = 8 * (rthdr->ip6r_len + 1);
417711042SErik.Nordmark@Sun.COM ptr += hdr_len;
417811042SErik.Nordmark@Sun.COM }
417911042SErik.Nordmark@Sun.COM if (nexthdr != IPPROTO_FRAGMENT) {
418011042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
418111042SErik.Nordmark@Sun.COM ip_drop_output("FragFails: bad nexthdr", mp, ill);
418211042SErik.Nordmark@Sun.COM freemsg(mp);
418311042SErik.Nordmark@Sun.COM return (EINVAL);
418411042SErik.Nordmark@Sun.COM }
418511042SErik.Nordmark@Sun.COM unfragmentable_len = (uint_t)(ptr - (uint8_t *)ip6h);
418611042SErik.Nordmark@Sun.COM unfragmentable_len += sizeof (ip6_frag_t);
418711042SErik.Nordmark@Sun.COM
418811042SErik.Nordmark@Sun.COM max_chunk = (max_frag - unfragmentable_len) & ~7;
418911042SErik.Nordmark@Sun.COM
419011042SErik.Nordmark@Sun.COM /*
419111042SErik.Nordmark@Sun.COM * Allocate an mblk with enough room for the link-layer
419211042SErik.Nordmark@Sun.COM * header and the unfragmentable part of the datagram, which includes
419311042SErik.Nordmark@Sun.COM * the fragment header. This (or a copy) will be used as the
419411042SErik.Nordmark@Sun.COM * first mblk for each fragment we send.
419511042SErik.Nordmark@Sun.COM */
419611042SErik.Nordmark@Sun.COM hmp = allocb_tmpl(unfragmentable_len + ipst->ips_ip_wroff_extra, mp);
419711042SErik.Nordmark@Sun.COM if (hmp == NULL) {
419811042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
419911042SErik.Nordmark@Sun.COM ip_drop_output("FragFails: no hmp", mp, ill);
420011042SErik.Nordmark@Sun.COM freemsg(mp);
420111042SErik.Nordmark@Sun.COM return (ENOBUFS);
420211042SErik.Nordmark@Sun.COM }
420311042SErik.Nordmark@Sun.COM hmp->b_rptr += ipst->ips_ip_wroff_extra;
420411042SErik.Nordmark@Sun.COM hmp->b_wptr = hmp->b_rptr + unfragmentable_len;
420511042SErik.Nordmark@Sun.COM
420611042SErik.Nordmark@Sun.COM fip6h = (ip6_t *)hmp->b_rptr;
420711042SErik.Nordmark@Sun.COM bcopy(ip6h, fip6h, unfragmentable_len);
420811042SErik.Nordmark@Sun.COM
420911042SErik.Nordmark@Sun.COM /*
421011042SErik.Nordmark@Sun.COM * pkt_len is set to the total length of the fragmentable data in this
421111042SErik.Nordmark@Sun.COM * datagram. For each fragment sent, we will decrement pkt_len
421211042SErik.Nordmark@Sun.COM * by the amount of fragmentable data sent in that fragment
421311042SErik.Nordmark@Sun.COM * until len reaches zero.
421411042SErik.Nordmark@Sun.COM */
421511042SErik.Nordmark@Sun.COM pkt_len -= unfragmentable_len;
421611042SErik.Nordmark@Sun.COM
421711042SErik.Nordmark@Sun.COM /*
421811042SErik.Nordmark@Sun.COM * Move read ptr past unfragmentable portion, we don't want this part
421911042SErik.Nordmark@Sun.COM * of the data in our fragments.
422011042SErik.Nordmark@Sun.COM */
422111042SErik.Nordmark@Sun.COM mp->b_rptr += unfragmentable_len;
422211042SErik.Nordmark@Sun.COM if (mp->b_rptr == mp->b_wptr) {
422311042SErik.Nordmark@Sun.COM mblk_t *mp1 = mp->b_cont;
422411042SErik.Nordmark@Sun.COM freeb(mp);
422511042SErik.Nordmark@Sun.COM mp = mp1;
422611042SErik.Nordmark@Sun.COM }
422711042SErik.Nordmark@Sun.COM
422811042SErik.Nordmark@Sun.COM while (pkt_len != 0) {
422911042SErik.Nordmark@Sun.COM mlen = MIN(pkt_len, max_chunk);
423011042SErik.Nordmark@Sun.COM pkt_len -= mlen;
423111042SErik.Nordmark@Sun.COM if (pkt_len != 0) {
423211042SErik.Nordmark@Sun.COM /* Not last */
423311042SErik.Nordmark@Sun.COM hmp0 = copyb(hmp);
423411042SErik.Nordmark@Sun.COM if (hmp0 == NULL) {
423511042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib,
423611042SErik.Nordmark@Sun.COM ipIfStatsOutFragFails);
423711042SErik.Nordmark@Sun.COM ip_drop_output("FragFails: copyb failed",
423811042SErik.Nordmark@Sun.COM mp, ill);
423911042SErik.Nordmark@Sun.COM freeb(hmp);
424011042SErik.Nordmark@Sun.COM freemsg(mp);
424111042SErik.Nordmark@Sun.COM ip1dbg(("ip_fragment_v6: copyb failed\n"));
424211042SErik.Nordmark@Sun.COM return (ENOBUFS);
424311042SErik.Nordmark@Sun.COM }
424411042SErik.Nordmark@Sun.COM off_flags = IP6F_MORE_FRAG;
424511042SErik.Nordmark@Sun.COM } else {
424611042SErik.Nordmark@Sun.COM /* Last fragment */
424711042SErik.Nordmark@Sun.COM hmp0 = hmp;
424811042SErik.Nordmark@Sun.COM hmp = NULL;
424911042SErik.Nordmark@Sun.COM off_flags = 0;
425011042SErik.Nordmark@Sun.COM }
425111042SErik.Nordmark@Sun.COM fip6h = (ip6_t *)(hmp0->b_rptr);
425211042SErik.Nordmark@Sun.COM fraghdr = (ip6_frag_t *)(hmp0->b_rptr + unfragmentable_len -
425311042SErik.Nordmark@Sun.COM sizeof (ip6_frag_t));
425411042SErik.Nordmark@Sun.COM
425511042SErik.Nordmark@Sun.COM fip6h->ip6_plen = htons((uint16_t)(mlen +
425611042SErik.Nordmark@Sun.COM unfragmentable_len - IPV6_HDR_LEN));
425711042SErik.Nordmark@Sun.COM /*
425811042SErik.Nordmark@Sun.COM * Note: Optimization alert.
425911042SErik.Nordmark@Sun.COM * In IPv6 (and IPv4) protocol header, Fragment Offset
426011042SErik.Nordmark@Sun.COM * ("offset") is 13 bits wide and in 8-octet units.
426111042SErik.Nordmark@Sun.COM * In IPv6 protocol header (unlike IPv4) in a 16 bit field,
426211042SErik.Nordmark@Sun.COM * it occupies the most significant 13 bits.
426311042SErik.Nordmark@Sun.COM * (least significant 13 bits in IPv4).
426411042SErik.Nordmark@Sun.COM * We do not do any shifts here. Not shifting is same effect
426511042SErik.Nordmark@Sun.COM * as taking offset value in octet units, dividing by 8 and
426611042SErik.Nordmark@Sun.COM * then shifting 3 bits left to line it up in place in proper
426711042SErik.Nordmark@Sun.COM * place protocol header.
426811042SErik.Nordmark@Sun.COM */
426911042SErik.Nordmark@Sun.COM fraghdr->ip6f_offlg = htons(offset) | off_flags;
427011042SErik.Nordmark@Sun.COM
427111042SErik.Nordmark@Sun.COM if (!(dmp = ip_carve_mp(&mp, mlen))) {
427211042SErik.Nordmark@Sun.COM /* mp has already been freed by ip_carve_mp() */
427311042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
427411042SErik.Nordmark@Sun.COM ip_drop_output("FragFails: could not carve mp",
427511042SErik.Nordmark@Sun.COM hmp0, ill);
427611042SErik.Nordmark@Sun.COM if (hmp != NULL)
427711042SErik.Nordmark@Sun.COM freeb(hmp);
427811042SErik.Nordmark@Sun.COM freeb(hmp0);
427911042SErik.Nordmark@Sun.COM ip1dbg(("ip_carve_mp: failed\n"));
428011042SErik.Nordmark@Sun.COM return (ENOBUFS);
428111042SErik.Nordmark@Sun.COM }
428211042SErik.Nordmark@Sun.COM hmp0->b_cont = dmp;
428311042SErik.Nordmark@Sun.COM /* Get the priority marking, if any */
428411042SErik.Nordmark@Sun.COM hmp0->b_band = priority;
428511042SErik.Nordmark@Sun.COM
428611042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragCreates);
428711042SErik.Nordmark@Sun.COM
428811042SErik.Nordmark@Sun.COM error = postfragfn(hmp0, nce, ixaflags,
428911042SErik.Nordmark@Sun.COM mlen + unfragmentable_len, xmit_hint, szone, nolzid,
429011042SErik.Nordmark@Sun.COM ixa_cookie);
429111042SErik.Nordmark@Sun.COM if (error != 0 && error != EWOULDBLOCK && hmp != NULL) {
429211042SErik.Nordmark@Sun.COM /* No point in sending the other fragments */
429311042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
429411042SErik.Nordmark@Sun.COM ip_drop_output("FragFails: postfragfn failed",
429511042SErik.Nordmark@Sun.COM hmp, ill);
429611042SErik.Nordmark@Sun.COM freeb(hmp);
429711042SErik.Nordmark@Sun.COM freemsg(mp);
429811042SErik.Nordmark@Sun.COM return (error);
429911042SErik.Nordmark@Sun.COM }
430011042SErik.Nordmark@Sun.COM /* No need to redo state machine in loop */
430111042SErik.Nordmark@Sun.COM ixaflags &= ~IXAF_REACH_CONF;
430211042SErik.Nordmark@Sun.COM
430311042SErik.Nordmark@Sun.COM offset += mlen;
430411042SErik.Nordmark@Sun.COM }
430511042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragOKs);
430611042SErik.Nordmark@Sun.COM return (error);
430711042SErik.Nordmark@Sun.COM }
430811042SErik.Nordmark@Sun.COM
430911042SErik.Nordmark@Sun.COM /*
431011042SErik.Nordmark@Sun.COM * Add a fragment header to an IPv6 packet.
431111042SErik.Nordmark@Sun.COM * Assumes that all the extension headers are contained in the first mblk.
431211042SErik.Nordmark@Sun.COM *
431311042SErik.Nordmark@Sun.COM * The fragment header is inserted after an hop-by-hop options header
431411042SErik.Nordmark@Sun.COM * and after [an optional destinations header followed by] a routing header.
431511042SErik.Nordmark@Sun.COM */
431611042SErik.Nordmark@Sun.COM mblk_t *
ip_fraghdr_add_v6(mblk_t * mp,uint32_t ident,ip_xmit_attr_t * ixa)431711042SErik.Nordmark@Sun.COM ip_fraghdr_add_v6(mblk_t *mp, uint32_t ident, ip_xmit_attr_t *ixa)
431811042SErik.Nordmark@Sun.COM {
431911042SErik.Nordmark@Sun.COM ip6_t *ip6h = (ip6_t *)mp->b_rptr;
432011042SErik.Nordmark@Sun.COM ip6_t *fip6h;
432111042SErik.Nordmark@Sun.COM mblk_t *hmp;
432211042SErik.Nordmark@Sun.COM ip6_frag_t *fraghdr;
432311042SErik.Nordmark@Sun.COM size_t unfragmentable_len;
43240Sstevel@tonic-gate uint8_t nexthdr;
43250Sstevel@tonic-gate uint_t prev_nexthdr_offset;
43260Sstevel@tonic-gate uint8_t *ptr;
432711042SErik.Nordmark@Sun.COM uint_t priority = mp->b_band;
432811042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst;
43293284Sapersson
43300Sstevel@tonic-gate /*
43310Sstevel@tonic-gate * Determine the length of the unfragmentable portion of this
43320Sstevel@tonic-gate * datagram. This consists of the IPv6 header, a potential
43330Sstevel@tonic-gate * hop-by-hop options header, a potential pre-routing-header
43340Sstevel@tonic-gate * destination options header, and a potential routing header.
43350Sstevel@tonic-gate */
43360Sstevel@tonic-gate nexthdr = ip6h->ip6_nxt;
43370Sstevel@tonic-gate prev_nexthdr_offset = (uint8_t *)&ip6h->ip6_nxt - (uint8_t *)ip6h;
43380Sstevel@tonic-gate ptr = (uint8_t *)&ip6h[1];
43390Sstevel@tonic-gate
43400Sstevel@tonic-gate if (nexthdr == IPPROTO_HOPOPTS) {
43410Sstevel@tonic-gate ip6_hbh_t *hbh_hdr;
43420Sstevel@tonic-gate uint_t hdr_len;
43430Sstevel@tonic-gate
43440Sstevel@tonic-gate hbh_hdr = (ip6_hbh_t *)ptr;
43450Sstevel@tonic-gate hdr_len = 8 * (hbh_hdr->ip6h_len + 1);
43460Sstevel@tonic-gate nexthdr = hbh_hdr->ip6h_nxt;
43470Sstevel@tonic-gate prev_nexthdr_offset = (uint8_t *)&hbh_hdr->ip6h_nxt
43480Sstevel@tonic-gate - (uint8_t *)ip6h;
43490Sstevel@tonic-gate ptr += hdr_len;
43500Sstevel@tonic-gate }
43510Sstevel@tonic-gate if (nexthdr == IPPROTO_DSTOPTS) {
43520Sstevel@tonic-gate ip6_dest_t *dest_hdr;
43530Sstevel@tonic-gate uint_t hdr_len;
43540Sstevel@tonic-gate
43550Sstevel@tonic-gate dest_hdr = (ip6_dest_t *)ptr;
43560Sstevel@tonic-gate if (dest_hdr->ip6d_nxt == IPPROTO_ROUTING) {
43570Sstevel@tonic-gate hdr_len = 8 * (dest_hdr->ip6d_len + 1);
43580Sstevel@tonic-gate nexthdr = dest_hdr->ip6d_nxt;
43590Sstevel@tonic-gate prev_nexthdr_offset = (uint8_t *)&dest_hdr->ip6d_nxt
43600Sstevel@tonic-gate - (uint8_t *)ip6h;
43610Sstevel@tonic-gate ptr += hdr_len;
43620Sstevel@tonic-gate }
43630Sstevel@tonic-gate }
43640Sstevel@tonic-gate if (nexthdr == IPPROTO_ROUTING) {
43650Sstevel@tonic-gate ip6_rthdr_t *rthdr;
43660Sstevel@tonic-gate uint_t hdr_len;
43670Sstevel@tonic-gate
43680Sstevel@tonic-gate rthdr = (ip6_rthdr_t *)ptr;
43690Sstevel@tonic-gate nexthdr = rthdr->ip6r_nxt;
43700Sstevel@tonic-gate prev_nexthdr_offset = (uint8_t *)&rthdr->ip6r_nxt
43710Sstevel@tonic-gate - (uint8_t *)ip6h;
43720Sstevel@tonic-gate hdr_len = 8 * (rthdr->ip6r_len + 1);
43730Sstevel@tonic-gate ptr += hdr_len;
43740Sstevel@tonic-gate }
43750Sstevel@tonic-gate unfragmentable_len = (uint_t)(ptr - (uint8_t *)ip6h);
43760Sstevel@tonic-gate
43770Sstevel@tonic-gate /*
43780Sstevel@tonic-gate * Allocate an mblk with enough room for the link-layer
43790Sstevel@tonic-gate * header, the unfragmentable part of the datagram, and the
438011042SErik.Nordmark@Sun.COM * fragment header.
43810Sstevel@tonic-gate */
43828778SErik.Nordmark@Sun.COM hmp = allocb_tmpl(unfragmentable_len + sizeof (ip6_frag_t) +
43838778SErik.Nordmark@Sun.COM ipst->ips_ip_wroff_extra, mp);
43840Sstevel@tonic-gate if (hmp == NULL) {
438511042SErik.Nordmark@Sun.COM ill_t *ill = ixa->ixa_nce->nce_ill;
438611042SErik.Nordmark@Sun.COM
438711042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
438811042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards: allocb failure", mp, ill);
43890Sstevel@tonic-gate freemsg(mp);
439011042SErik.Nordmark@Sun.COM return (NULL);
43910Sstevel@tonic-gate }
43923448Sdh155122 hmp->b_rptr += ipst->ips_ip_wroff_extra;
43930Sstevel@tonic-gate hmp->b_wptr = hmp->b_rptr + unfragmentable_len + sizeof (ip6_frag_t);
43940Sstevel@tonic-gate
43950Sstevel@tonic-gate fip6h = (ip6_t *)hmp->b_rptr;
43960Sstevel@tonic-gate fraghdr = (ip6_frag_t *)(hmp->b_rptr + unfragmentable_len);
43970Sstevel@tonic-gate
43980Sstevel@tonic-gate bcopy(ip6h, fip6h, unfragmentable_len);
439911042SErik.Nordmark@Sun.COM fip6h->ip6_plen = htons(ntohs(fip6h->ip6_plen) + sizeof (ip6_frag_t));
44000Sstevel@tonic-gate hmp->b_rptr[prev_nexthdr_offset] = IPPROTO_FRAGMENT;
44010Sstevel@tonic-gate
44020Sstevel@tonic-gate fraghdr->ip6f_nxt = nexthdr;
44030Sstevel@tonic-gate fraghdr->ip6f_reserved = 0;
4404741Smasputra fraghdr->ip6f_offlg = 0;
44050Sstevel@tonic-gate fraghdr->ip6f_ident = htonl(ident);
44060Sstevel@tonic-gate
440711042SErik.Nordmark@Sun.COM /* Get the priority marking, if any */
440811042SErik.Nordmark@Sun.COM hmp->b_band = priority;
44090Sstevel@tonic-gate
44100Sstevel@tonic-gate /*
44110Sstevel@tonic-gate * Move read ptr past unfragmentable portion, we don't want this part
44120Sstevel@tonic-gate * of the data in our fragments.
44130Sstevel@tonic-gate */
44140Sstevel@tonic-gate mp->b_rptr += unfragmentable_len;
441511042SErik.Nordmark@Sun.COM hmp->b_cont = mp;
441611042SErik.Nordmark@Sun.COM return (hmp);
44170Sstevel@tonic-gate }
44180Sstevel@tonic-gate
44190Sstevel@tonic-gate /*
44200Sstevel@tonic-gate * Determine if the ill and multicast aspects of that packets
44210Sstevel@tonic-gate * "matches" the conn.
44220Sstevel@tonic-gate */
44230Sstevel@tonic-gate boolean_t
conn_wantpacket_v6(conn_t * connp,ip_recv_attr_t * ira,ip6_t * ip6h)442411042SErik.Nordmark@Sun.COM conn_wantpacket_v6(conn_t *connp, ip_recv_attr_t *ira, ip6_t *ip6h)
44250Sstevel@tonic-gate {
442611042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_rill;
442711042SErik.Nordmark@Sun.COM zoneid_t zoneid = ira->ira_zoneid;
442811042SErik.Nordmark@Sun.COM uint_t in_ifindex;
442911042SErik.Nordmark@Sun.COM in6_addr_t *v6dst_ptr = &ip6h->ip6_dst;
443011042SErik.Nordmark@Sun.COM in6_addr_t *v6src_ptr = &ip6h->ip6_src;
44310Sstevel@tonic-gate
44320Sstevel@tonic-gate /*
443311042SErik.Nordmark@Sun.COM * conn_incoming_ifindex is set by IPV6_BOUND_IF and as link-local
443411042SErik.Nordmark@Sun.COM * scopeid. This is used to limit
443511042SErik.Nordmark@Sun.COM * unicast and multicast reception to conn_incoming_ifindex.
44360Sstevel@tonic-gate * conn_wantpacket_v6 is called both for unicast and
443711042SErik.Nordmark@Sun.COM * multicast packets.
44388485SPeter.Memishian@Sun.COM */
443911042SErik.Nordmark@Sun.COM in_ifindex = connp->conn_incoming_ifindex;
444011042SErik.Nordmark@Sun.COM
444111042SErik.Nordmark@Sun.COM /* mpathd can bind to the under IPMP interface, which we allow */
444211042SErik.Nordmark@Sun.COM if (in_ifindex != 0 && in_ifindex != ill->ill_phyint->phyint_ifindex) {
444311042SErik.Nordmark@Sun.COM if (!IS_UNDER_IPMP(ill))
444411042SErik.Nordmark@Sun.COM return (B_FALSE);
444511042SErik.Nordmark@Sun.COM
444611042SErik.Nordmark@Sun.COM if (in_ifindex != ipmp_ill_get_ipmp_ifindex(ill))
444711042SErik.Nordmark@Sun.COM return (B_FALSE);
44480Sstevel@tonic-gate }
44490Sstevel@tonic-gate
445011042SErik.Nordmark@Sun.COM if (!IPCL_ZONE_MATCH(connp, zoneid))
445111042SErik.Nordmark@Sun.COM return (B_FALSE);
445211042SErik.Nordmark@Sun.COM
445311042SErik.Nordmark@Sun.COM if (!(ira->ira_flags & IRAF_MULTICAST))
445411042SErik.Nordmark@Sun.COM return (B_TRUE);
445511042SErik.Nordmark@Sun.COM
44560Sstevel@tonic-gate if (connp->conn_multi_router)
44570Sstevel@tonic-gate return (B_TRUE);
44580Sstevel@tonic-gate
445911042SErik.Nordmark@Sun.COM if (ira->ira_protocol == IPPROTO_RSVP)
446011042SErik.Nordmark@Sun.COM return (B_TRUE);
446111042SErik.Nordmark@Sun.COM
446211042SErik.Nordmark@Sun.COM return (conn_hasmembers_ill_withsrc_v6(connp, v6dst_ptr, v6src_ptr,
446311042SErik.Nordmark@Sun.COM ira->ira_ill));
44640Sstevel@tonic-gate }
44650Sstevel@tonic-gate
44660Sstevel@tonic-gate /*
44670Sstevel@tonic-gate * pr_addr_dbg function provides the needed buffer space to call
44680Sstevel@tonic-gate * inet_ntop() function's 3rd argument. This function should be
44690Sstevel@tonic-gate * used by any kernel routine which wants to save INET6_ADDRSTRLEN
44700Sstevel@tonic-gate * stack buffer space in it's own stack frame. This function uses
44710Sstevel@tonic-gate * a buffer from it's own stack and prints the information.
44720Sstevel@tonic-gate * Example: pr_addr_dbg("func: no route for %s\n ", AF_INET, addr)
44730Sstevel@tonic-gate *
44740Sstevel@tonic-gate * Note: This function can call inet_ntop() once.
44750Sstevel@tonic-gate */
44760Sstevel@tonic-gate void
pr_addr_dbg(char * fmt1,int af,const void * addr)44770Sstevel@tonic-gate pr_addr_dbg(char *fmt1, int af, const void *addr)
44780Sstevel@tonic-gate {
44790Sstevel@tonic-gate char buf[INET6_ADDRSTRLEN];
44800Sstevel@tonic-gate
44810Sstevel@tonic-gate if (fmt1 == NULL) {
44820Sstevel@tonic-gate ip0dbg(("pr_addr_dbg: Wrong arguments\n"));
44830Sstevel@tonic-gate return;
44840Sstevel@tonic-gate }
44850Sstevel@tonic-gate
44860Sstevel@tonic-gate /*
44870Sstevel@tonic-gate * This does not compare debug level and just prints
44880Sstevel@tonic-gate * out. Thus it is the responsibility of the caller
44890Sstevel@tonic-gate * to check the appropriate debug-level before calling
44900Sstevel@tonic-gate * this function.
44910Sstevel@tonic-gate */
44920Sstevel@tonic-gate if (ip_debug > 0) {
44930Sstevel@tonic-gate printf(fmt1, inet_ntop(af, addr, buf, sizeof (buf)));
44940Sstevel@tonic-gate }
44950Sstevel@tonic-gate
44960Sstevel@tonic-gate
44970Sstevel@tonic-gate }
44980Sstevel@tonic-gate
44990Sstevel@tonic-gate
45000Sstevel@tonic-gate /*
450111042SErik.Nordmark@Sun.COM * Return the length in bytes of the IPv6 headers (base header
450211042SErik.Nordmark@Sun.COM * extension headers) that will be needed based on the
450311042SErik.Nordmark@Sun.COM * ip_pkt_t structure passed by the caller.
45040Sstevel@tonic-gate *
45050Sstevel@tonic-gate * The returned length does not include the length of the upper level
45060Sstevel@tonic-gate * protocol (ULP) header.
45070Sstevel@tonic-gate */
45080Sstevel@tonic-gate int
ip_total_hdrs_len_v6(const ip_pkt_t * ipp)450911042SErik.Nordmark@Sun.COM ip_total_hdrs_len_v6(const ip_pkt_t *ipp)
45100Sstevel@tonic-gate {
45110Sstevel@tonic-gate int len;
45120Sstevel@tonic-gate
45130Sstevel@tonic-gate len = IPV6_HDR_LEN;
451411042SErik.Nordmark@Sun.COM
451511042SErik.Nordmark@Sun.COM /*
451611042SErik.Nordmark@Sun.COM * If there's a security label here, then we ignore any hop-by-hop
451711042SErik.Nordmark@Sun.COM * options the user may try to set.
451811042SErik.Nordmark@Sun.COM */
451911042SErik.Nordmark@Sun.COM if (ipp->ipp_fields & IPPF_LABEL_V6) {
452011042SErik.Nordmark@Sun.COM uint_t hopoptslen;
452111042SErik.Nordmark@Sun.COM /*
452211042SErik.Nordmark@Sun.COM * Note that ipp_label_len_v6 is just the option - not
452311042SErik.Nordmark@Sun.COM * the hopopts extension header. It also needs to be padded
452411042SErik.Nordmark@Sun.COM * to a multiple of 8 bytes.
452511042SErik.Nordmark@Sun.COM */
452611042SErik.Nordmark@Sun.COM ASSERT(ipp->ipp_label_len_v6 != 0);
452711042SErik.Nordmark@Sun.COM hopoptslen = ipp->ipp_label_len_v6 + sizeof (ip6_hbh_t);
452811042SErik.Nordmark@Sun.COM hopoptslen = (hopoptslen + 7)/8 * 8;
452911042SErik.Nordmark@Sun.COM len += hopoptslen;
453011042SErik.Nordmark@Sun.COM } else if (ipp->ipp_fields & IPPF_HOPOPTS) {
45310Sstevel@tonic-gate ASSERT(ipp->ipp_hopoptslen != 0);
45320Sstevel@tonic-gate len += ipp->ipp_hopoptslen;
45330Sstevel@tonic-gate }
453411042SErik.Nordmark@Sun.COM
453511042SErik.Nordmark@Sun.COM /*
453611042SErik.Nordmark@Sun.COM * En-route destination options
453711042SErik.Nordmark@Sun.COM * Only do them if there's a routing header as well
453811042SErik.Nordmark@Sun.COM */
453911042SErik.Nordmark@Sun.COM if ((ipp->ipp_fields & (IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) ==
454011042SErik.Nordmark@Sun.COM (IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) {
454111042SErik.Nordmark@Sun.COM ASSERT(ipp->ipp_rthdrdstoptslen != 0);
454211042SErik.Nordmark@Sun.COM len += ipp->ipp_rthdrdstoptslen;
454311042SErik.Nordmark@Sun.COM }
45440Sstevel@tonic-gate if (ipp->ipp_fields & IPPF_RTHDR) {
45450Sstevel@tonic-gate ASSERT(ipp->ipp_rthdrlen != 0);
45460Sstevel@tonic-gate len += ipp->ipp_rthdrlen;
45470Sstevel@tonic-gate }
45480Sstevel@tonic-gate if (ipp->ipp_fields & IPPF_DSTOPTS) {
45490Sstevel@tonic-gate ASSERT(ipp->ipp_dstoptslen != 0);
45500Sstevel@tonic-gate len += ipp->ipp_dstoptslen;
45510Sstevel@tonic-gate }
45520Sstevel@tonic-gate return (len);
45530Sstevel@tonic-gate }
45540Sstevel@tonic-gate
45550Sstevel@tonic-gate /*
45560Sstevel@tonic-gate * All-purpose routine to build a header chain of an IPv6 header
455711042SErik.Nordmark@Sun.COM * followed by any required extension headers and a proto header.
45580Sstevel@tonic-gate *
455911042SErik.Nordmark@Sun.COM * The caller has to set the source and destination address as well as
456011042SErik.Nordmark@Sun.COM * ip6_plen. The caller has to massage any routing header and compensate
456111042SErik.Nordmark@Sun.COM * for the ULP pseudo-header checksum due to the source route.
45620Sstevel@tonic-gate *
456311042SErik.Nordmark@Sun.COM * The extension headers will all be fully filled in.
45640Sstevel@tonic-gate */
45650Sstevel@tonic-gate void
ip_build_hdrs_v6(uchar_t * buf,uint_t buf_len,const ip_pkt_t * ipp,uint8_t protocol,uint32_t flowinfo)456611042SErik.Nordmark@Sun.COM ip_build_hdrs_v6(uchar_t *buf, uint_t buf_len, const ip_pkt_t *ipp,
456711042SErik.Nordmark@Sun.COM uint8_t protocol, uint32_t flowinfo)
45680Sstevel@tonic-gate {
45690Sstevel@tonic-gate uint8_t *nxthdr_ptr;
45700Sstevel@tonic-gate uint8_t *cp;
457111042SErik.Nordmark@Sun.COM ip6_t *ip6h = (ip6_t *)buf;
457211042SErik.Nordmark@Sun.COM
457311042SErik.Nordmark@Sun.COM /* Initialize IPv6 header */
457411042SErik.Nordmark@Sun.COM ip6h->ip6_vcf =
457511042SErik.Nordmark@Sun.COM (IPV6_DEFAULT_VERS_AND_FLOW & IPV6_VERS_AND_FLOW_MASK) |
457611042SErik.Nordmark@Sun.COM (flowinfo & ~IPV6_VERS_AND_FLOW_MASK);
457711042SErik.Nordmark@Sun.COM
457811042SErik.Nordmark@Sun.COM if (ipp->ipp_fields & IPPF_TCLASS) {
457911042SErik.Nordmark@Sun.COM /* Overrides the class part of flowinfo */
458011042SErik.Nordmark@Sun.COM ip6h->ip6_vcf = IPV6_TCLASS_FLOW(ip6h->ip6_vcf,
458111042SErik.Nordmark@Sun.COM ipp->ipp_tclass);
45820Sstevel@tonic-gate }
458311042SErik.Nordmark@Sun.COM
458411042SErik.Nordmark@Sun.COM if (ipp->ipp_fields & IPPF_HOPLIMIT)
458511042SErik.Nordmark@Sun.COM ip6h->ip6_hops = ipp->ipp_hoplimit;
458611042SErik.Nordmark@Sun.COM else
458711042SErik.Nordmark@Sun.COM ip6h->ip6_hops = ipp->ipp_unicast_hops;
458811042SErik.Nordmark@Sun.COM
458911042SErik.Nordmark@Sun.COM if ((ipp->ipp_fields & IPPF_ADDR) &&
459011042SErik.Nordmark@Sun.COM !IN6_IS_ADDR_V4MAPPED(&ipp->ipp_addr))
45910Sstevel@tonic-gate ip6h->ip6_src = ipp->ipp_addr;
45920Sstevel@tonic-gate
45930Sstevel@tonic-gate nxthdr_ptr = (uint8_t *)&ip6h->ip6_nxt;
45940Sstevel@tonic-gate cp = (uint8_t *)&ip6h[1];
45950Sstevel@tonic-gate /*
45960Sstevel@tonic-gate * Here's where we have to start stringing together
45970Sstevel@tonic-gate * any extension headers in the right order:
45980Sstevel@tonic-gate * Hop-by-hop, destination, routing, and final destination opts.
45990Sstevel@tonic-gate */
460011042SErik.Nordmark@Sun.COM /*
460111042SErik.Nordmark@Sun.COM * If there's a security label here, then we ignore any hop-by-hop
460211042SErik.Nordmark@Sun.COM * options the user may try to set.
460311042SErik.Nordmark@Sun.COM */
460411042SErik.Nordmark@Sun.COM if (ipp->ipp_fields & IPPF_LABEL_V6) {
460511042SErik.Nordmark@Sun.COM /*
460611042SErik.Nordmark@Sun.COM * Hop-by-hop options with the label.
460711042SErik.Nordmark@Sun.COM * Note that ipp_label_v6 is just the option - not
460811042SErik.Nordmark@Sun.COM * the hopopts extension header. It also needs to be padded
460911042SErik.Nordmark@Sun.COM * to a multiple of 8 bytes.
461011042SErik.Nordmark@Sun.COM */
461111042SErik.Nordmark@Sun.COM ip6_hbh_t *hbh = (ip6_hbh_t *)cp;
461211042SErik.Nordmark@Sun.COM uint_t hopoptslen;
461311042SErik.Nordmark@Sun.COM uint_t padlen;
461411042SErik.Nordmark@Sun.COM
461511042SErik.Nordmark@Sun.COM padlen = ipp->ipp_label_len_v6 + sizeof (ip6_hbh_t);
461611042SErik.Nordmark@Sun.COM hopoptslen = (padlen + 7)/8 * 8;
461711042SErik.Nordmark@Sun.COM padlen = hopoptslen - padlen;
461811042SErik.Nordmark@Sun.COM
461911042SErik.Nordmark@Sun.COM *nxthdr_ptr = IPPROTO_HOPOPTS;
462011042SErik.Nordmark@Sun.COM nxthdr_ptr = &hbh->ip6h_nxt;
462111042SErik.Nordmark@Sun.COM hbh->ip6h_len = hopoptslen/8 - 1;
462211042SErik.Nordmark@Sun.COM cp += sizeof (ip6_hbh_t);
462311042SErik.Nordmark@Sun.COM bcopy(ipp->ipp_label_v6, cp, ipp->ipp_label_len_v6);
462411042SErik.Nordmark@Sun.COM cp += ipp->ipp_label_len_v6;
462511042SErik.Nordmark@Sun.COM
462611042SErik.Nordmark@Sun.COM ASSERT(padlen <= 7);
462711042SErik.Nordmark@Sun.COM switch (padlen) {
462811042SErik.Nordmark@Sun.COM case 0:
462911042SErik.Nordmark@Sun.COM break;
463011042SErik.Nordmark@Sun.COM case 1:
463111042SErik.Nordmark@Sun.COM cp[0] = IP6OPT_PAD1;
463211042SErik.Nordmark@Sun.COM break;
463311042SErik.Nordmark@Sun.COM default:
463411042SErik.Nordmark@Sun.COM cp[0] = IP6OPT_PADN;
463511042SErik.Nordmark@Sun.COM cp[1] = padlen - 2;
463611042SErik.Nordmark@Sun.COM bzero(&cp[2], padlen - 2);
463711042SErik.Nordmark@Sun.COM break;
463811042SErik.Nordmark@Sun.COM }
463911042SErik.Nordmark@Sun.COM cp += padlen;
464011042SErik.Nordmark@Sun.COM } else if (ipp->ipp_fields & IPPF_HOPOPTS) {
46410Sstevel@tonic-gate /* Hop-by-hop options */
46420Sstevel@tonic-gate ip6_hbh_t *hbh = (ip6_hbh_t *)cp;
46430Sstevel@tonic-gate
46440Sstevel@tonic-gate *nxthdr_ptr = IPPROTO_HOPOPTS;
46450Sstevel@tonic-gate nxthdr_ptr = &hbh->ip6h_nxt;
46460Sstevel@tonic-gate
46470Sstevel@tonic-gate bcopy(ipp->ipp_hopopts, cp, ipp->ipp_hopoptslen);
46480Sstevel@tonic-gate cp += ipp->ipp_hopoptslen;
46490Sstevel@tonic-gate }
46500Sstevel@tonic-gate /*
46510Sstevel@tonic-gate * En-route destination options
46520Sstevel@tonic-gate * Only do them if there's a routing header as well
46530Sstevel@tonic-gate */
465411042SErik.Nordmark@Sun.COM if ((ipp->ipp_fields & (IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) ==
465511042SErik.Nordmark@Sun.COM (IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) {
46560Sstevel@tonic-gate ip6_dest_t *dst = (ip6_dest_t *)cp;
46570Sstevel@tonic-gate
46580Sstevel@tonic-gate *nxthdr_ptr = IPPROTO_DSTOPTS;
46590Sstevel@tonic-gate nxthdr_ptr = &dst->ip6d_nxt;
46600Sstevel@tonic-gate
466111042SErik.Nordmark@Sun.COM bcopy(ipp->ipp_rthdrdstopts, cp, ipp->ipp_rthdrdstoptslen);
466211042SErik.Nordmark@Sun.COM cp += ipp->ipp_rthdrdstoptslen;
46630Sstevel@tonic-gate }
46640Sstevel@tonic-gate /*
46650Sstevel@tonic-gate * Routing header next
46660Sstevel@tonic-gate */
46670Sstevel@tonic-gate if (ipp->ipp_fields & IPPF_RTHDR) {
46680Sstevel@tonic-gate ip6_rthdr_t *rt = (ip6_rthdr_t *)cp;
46690Sstevel@tonic-gate
46700Sstevel@tonic-gate *nxthdr_ptr = IPPROTO_ROUTING;
46710Sstevel@tonic-gate nxthdr_ptr = &rt->ip6r_nxt;
46720Sstevel@tonic-gate
46730Sstevel@tonic-gate bcopy(ipp->ipp_rthdr, cp, ipp->ipp_rthdrlen);
46740Sstevel@tonic-gate cp += ipp->ipp_rthdrlen;
46750Sstevel@tonic-gate }
46760Sstevel@tonic-gate /*
46770Sstevel@tonic-gate * Do ultimate destination options
46780Sstevel@tonic-gate */
46790Sstevel@tonic-gate if (ipp->ipp_fields & IPPF_DSTOPTS) {
46800Sstevel@tonic-gate ip6_dest_t *dest = (ip6_dest_t *)cp;
46810Sstevel@tonic-gate
46820Sstevel@tonic-gate *nxthdr_ptr = IPPROTO_DSTOPTS;
46830Sstevel@tonic-gate nxthdr_ptr = &dest->ip6d_nxt;
46840Sstevel@tonic-gate
46850Sstevel@tonic-gate bcopy(ipp->ipp_dstopts, cp, ipp->ipp_dstoptslen);
46860Sstevel@tonic-gate cp += ipp->ipp_dstoptslen;
46870Sstevel@tonic-gate }
46880Sstevel@tonic-gate /*
46890Sstevel@tonic-gate * Now set the last header pointer to the proto passed in
46900Sstevel@tonic-gate */
46910Sstevel@tonic-gate *nxthdr_ptr = protocol;
469211042SErik.Nordmark@Sun.COM ASSERT((int)(cp - buf) == buf_len);
46930Sstevel@tonic-gate }
46940Sstevel@tonic-gate
46950Sstevel@tonic-gate /*
46960Sstevel@tonic-gate * Return a pointer to the routing header extension header
46970Sstevel@tonic-gate * in the IPv6 header(s) chain passed in.
46980Sstevel@tonic-gate * If none found, return NULL
46990Sstevel@tonic-gate * Assumes that all extension headers are in same mblk as the v6 header
47000Sstevel@tonic-gate */
47010Sstevel@tonic-gate ip6_rthdr_t *
ip_find_rthdr_v6(ip6_t * ip6h,uint8_t * endptr)47020Sstevel@tonic-gate ip_find_rthdr_v6(ip6_t *ip6h, uint8_t *endptr)
47030Sstevel@tonic-gate {
47040Sstevel@tonic-gate ip6_dest_t *desthdr;
47050Sstevel@tonic-gate ip6_frag_t *fraghdr;
47060Sstevel@tonic-gate uint_t hdrlen;
47070Sstevel@tonic-gate uint8_t nexthdr;
47080Sstevel@tonic-gate uint8_t *ptr = (uint8_t *)&ip6h[1];
47090Sstevel@tonic-gate
47100Sstevel@tonic-gate if (ip6h->ip6_nxt == IPPROTO_ROUTING)
47110Sstevel@tonic-gate return ((ip6_rthdr_t *)ptr);
47120Sstevel@tonic-gate
47130Sstevel@tonic-gate /*
47140Sstevel@tonic-gate * The routing header will precede all extension headers
47150Sstevel@tonic-gate * other than the hop-by-hop and destination options
47160Sstevel@tonic-gate * extension headers, so if we see anything other than those,
47170Sstevel@tonic-gate * we're done and didn't find it.
47180Sstevel@tonic-gate * We could see a destination options header alone but no
47190Sstevel@tonic-gate * routing header, in which case we'll return NULL as soon as
47200Sstevel@tonic-gate * we see anything after that.
47210Sstevel@tonic-gate * Hop-by-hop and destination option headers are identical,
47220Sstevel@tonic-gate * so we can use either one we want as a template.
47230Sstevel@tonic-gate */
47240Sstevel@tonic-gate nexthdr = ip6h->ip6_nxt;
47250Sstevel@tonic-gate while (ptr < endptr) {
47260Sstevel@tonic-gate /* Is there enough left for len + nexthdr? */
47270Sstevel@tonic-gate if (ptr + MIN_EHDR_LEN > endptr)
47280Sstevel@tonic-gate return (NULL);
47290Sstevel@tonic-gate
47300Sstevel@tonic-gate switch (nexthdr) {
47310Sstevel@tonic-gate case IPPROTO_HOPOPTS:
47320Sstevel@tonic-gate case IPPROTO_DSTOPTS:
47330Sstevel@tonic-gate /* Assumes the headers are identical for hbh and dst */
47340Sstevel@tonic-gate desthdr = (ip6_dest_t *)ptr;
47350Sstevel@tonic-gate hdrlen = 8 * (desthdr->ip6d_len + 1);
47360Sstevel@tonic-gate nexthdr = desthdr->ip6d_nxt;
47370Sstevel@tonic-gate break;
47380Sstevel@tonic-gate
47390Sstevel@tonic-gate case IPPROTO_ROUTING:
47400Sstevel@tonic-gate return ((ip6_rthdr_t *)ptr);
47410Sstevel@tonic-gate
47420Sstevel@tonic-gate case IPPROTO_FRAGMENT:
47430Sstevel@tonic-gate fraghdr = (ip6_frag_t *)ptr;
47440Sstevel@tonic-gate hdrlen = sizeof (ip6_frag_t);
47450Sstevel@tonic-gate nexthdr = fraghdr->ip6f_nxt;
47460Sstevel@tonic-gate break;
47470Sstevel@tonic-gate
47480Sstevel@tonic-gate default:
47490Sstevel@tonic-gate return (NULL);
47500Sstevel@tonic-gate }
47510Sstevel@tonic-gate ptr += hdrlen;
47520Sstevel@tonic-gate }
47530Sstevel@tonic-gate return (NULL);
47540Sstevel@tonic-gate }
47550Sstevel@tonic-gate
47560Sstevel@tonic-gate /*
47570Sstevel@tonic-gate * Called for source-routed packets originating on this node.
47580Sstevel@tonic-gate * Manipulates the original routing header by moving every entry up
47590Sstevel@tonic-gate * one slot, placing the first entry in the v6 header's v6_dst field,
47600Sstevel@tonic-gate * and placing the ultimate destination in the routing header's last
47610Sstevel@tonic-gate * slot.
47620Sstevel@tonic-gate *
47630Sstevel@tonic-gate * Returns the checksum diference between the ultimate destination
47640Sstevel@tonic-gate * (last hop in the routing header when the packet is sent) and
47650Sstevel@tonic-gate * the first hop (ip6_dst when the packet is sent)
47660Sstevel@tonic-gate */
47673448Sdh155122 /* ARGSUSED2 */
47680Sstevel@tonic-gate uint32_t
ip_massage_options_v6(ip6_t * ip6h,ip6_rthdr_t * rth,netstack_t * ns)47693448Sdh155122 ip_massage_options_v6(ip6_t *ip6h, ip6_rthdr_t *rth, netstack_t *ns)
47700Sstevel@tonic-gate {
47710Sstevel@tonic-gate uint_t numaddr;
47720Sstevel@tonic-gate uint_t i;
47730Sstevel@tonic-gate in6_addr_t *addrptr;
47740Sstevel@tonic-gate in6_addr_t tmp;
47750Sstevel@tonic-gate ip6_rthdr0_t *rthdr = (ip6_rthdr0_t *)rth;
47760Sstevel@tonic-gate uint32_t cksm;
47770Sstevel@tonic-gate uint32_t addrsum = 0;
47780Sstevel@tonic-gate uint16_t *ptr;
47790Sstevel@tonic-gate
47800Sstevel@tonic-gate /*
47810Sstevel@tonic-gate * Perform any processing needed for source routing.
47820Sstevel@tonic-gate * We know that all extension headers will be in the same mblk
47830Sstevel@tonic-gate * as the IPv6 header.
47840Sstevel@tonic-gate */
47850Sstevel@tonic-gate
47860Sstevel@tonic-gate /*
47870Sstevel@tonic-gate * If no segments left in header, or the header length field is zero,
47880Sstevel@tonic-gate * don't move hop addresses around;
47890Sstevel@tonic-gate * Checksum difference is zero.
47900Sstevel@tonic-gate */
47910Sstevel@tonic-gate if ((rthdr->ip6r0_segleft == 0) || (rthdr->ip6r0_len == 0))
47920Sstevel@tonic-gate return (0);
47930Sstevel@tonic-gate
47940Sstevel@tonic-gate ptr = (uint16_t *)&ip6h->ip6_dst;
47950Sstevel@tonic-gate cksm = 0;
47960Sstevel@tonic-gate for (i = 0; i < (sizeof (in6_addr_t) / sizeof (uint16_t)); i++) {
47970Sstevel@tonic-gate cksm += ptr[i];
47980Sstevel@tonic-gate }
47990Sstevel@tonic-gate cksm = (cksm & 0xFFFF) + (cksm >> 16);
48000Sstevel@tonic-gate
48010Sstevel@tonic-gate /*
48020Sstevel@tonic-gate * Here's where the fun begins - we have to
48030Sstevel@tonic-gate * move all addresses up one spot, take the
48040Sstevel@tonic-gate * first hop and make it our first ip6_dst,
48050Sstevel@tonic-gate * and place the ultimate destination in the
48060Sstevel@tonic-gate * newly-opened last slot.
48070Sstevel@tonic-gate */
48080Sstevel@tonic-gate addrptr = (in6_addr_t *)((char *)rthdr + sizeof (*rthdr));
48090Sstevel@tonic-gate numaddr = rthdr->ip6r0_len / 2;
48100Sstevel@tonic-gate tmp = *addrptr;
48110Sstevel@tonic-gate for (i = 0; i < (numaddr - 1); addrptr++, i++) {
48120Sstevel@tonic-gate *addrptr = addrptr[1];
48130Sstevel@tonic-gate }
48140Sstevel@tonic-gate *addrptr = ip6h->ip6_dst;
48150Sstevel@tonic-gate ip6h->ip6_dst = tmp;
48160Sstevel@tonic-gate
48170Sstevel@tonic-gate /*
48180Sstevel@tonic-gate * From the checksummed ultimate destination subtract the checksummed
48190Sstevel@tonic-gate * current ip6_dst (the first hop address). Return that number.
48200Sstevel@tonic-gate * (In the v4 case, the second part of this is done in each routine
48210Sstevel@tonic-gate * that calls ip_massage_options(). We do it all in this one place
48220Sstevel@tonic-gate * for v6).
48230Sstevel@tonic-gate */
48240Sstevel@tonic-gate ptr = (uint16_t *)&ip6h->ip6_dst;
48250Sstevel@tonic-gate for (i = 0; i < (sizeof (in6_addr_t) / sizeof (uint16_t)); i++) {
48260Sstevel@tonic-gate addrsum += ptr[i];
48270Sstevel@tonic-gate }
48280Sstevel@tonic-gate cksm -= ((addrsum >> 16) + (addrsum & 0xFFFF));
48290Sstevel@tonic-gate if ((int)cksm < 0)
48300Sstevel@tonic-gate cksm--;
48310Sstevel@tonic-gate cksm = (cksm & 0xFFFF) + (cksm >> 16);
48320Sstevel@tonic-gate
48330Sstevel@tonic-gate return (cksm);
48340Sstevel@tonic-gate }
48350Sstevel@tonic-gate
48360Sstevel@tonic-gate void
ip6_kstat_init(netstackid_t stackid,ip6_stat_t * ip6_statisticsp)48373448Sdh155122 *ip6_kstat_init(netstackid_t stackid, ip6_stat_t *ip6_statisticsp)
48380Sstevel@tonic-gate {
48393448Sdh155122 kstat_t *ksp;
48403448Sdh155122
48413448Sdh155122 ip6_stat_t template = {
48423448Sdh155122 { "ip6_udp_fannorm", KSTAT_DATA_UINT64 },
48433448Sdh155122 { "ip6_udp_fanmb", KSTAT_DATA_UINT64 },
484411042SErik.Nordmark@Sun.COM { "ip6_recv_pullup", KSTAT_DATA_UINT64 },
484511042SErik.Nordmark@Sun.COM { "ip6_db_ref", KSTAT_DATA_UINT64 },
484611042SErik.Nordmark@Sun.COM { "ip6_notaligned", KSTAT_DATA_UINT64 },
484711042SErik.Nordmark@Sun.COM { "ip6_multimblk", KSTAT_DATA_UINT64 },
484811042SErik.Nordmark@Sun.COM { "ipsec_proto_ahesp", KSTAT_DATA_UINT64 },
48493448Sdh155122 { "ip6_out_sw_cksum", KSTAT_DATA_UINT64 },
485011042SErik.Nordmark@Sun.COM { "ip6_out_sw_cksum_bytes", KSTAT_DATA_UINT64 },
48513448Sdh155122 { "ip6_in_sw_cksum", KSTAT_DATA_UINT64 },
48523448Sdh155122 { "ip6_tcp_in_full_hw_cksum_err", KSTAT_DATA_UINT64 },
48533448Sdh155122 { "ip6_tcp_in_part_hw_cksum_err", KSTAT_DATA_UINT64 },
48543448Sdh155122 { "ip6_tcp_in_sw_cksum_err", KSTAT_DATA_UINT64 },
48553448Sdh155122 { "ip6_udp_in_full_hw_cksum_err", KSTAT_DATA_UINT64 },
48563448Sdh155122 { "ip6_udp_in_part_hw_cksum_err", KSTAT_DATA_UINT64 },
48573448Sdh155122 { "ip6_udp_in_sw_cksum_err", KSTAT_DATA_UINT64 },
48583448Sdh155122 };
48593448Sdh155122 ksp = kstat_create_netstack("ip", 0, "ip6stat", "net",
48603448Sdh155122 KSTAT_TYPE_NAMED, sizeof (template) / sizeof (kstat_named_t),
48613448Sdh155122 KSTAT_FLAG_VIRTUAL, stackid);
48623448Sdh155122
48633448Sdh155122 if (ksp == NULL)
48643448Sdh155122 return (NULL);
48653448Sdh155122
48663448Sdh155122 bcopy(&template, ip6_statisticsp, sizeof (template));
48673448Sdh155122 ksp->ks_data = (void *)ip6_statisticsp;
48683448Sdh155122 ksp->ks_private = (void *)(uintptr_t)stackid;
48693448Sdh155122
48703448Sdh155122 kstat_install(ksp);
48713448Sdh155122 return (ksp);
48723448Sdh155122 }
48733448Sdh155122
48743448Sdh155122 void
ip6_kstat_fini(netstackid_t stackid,kstat_t * ksp)48753448Sdh155122 ip6_kstat_fini(netstackid_t stackid, kstat_t *ksp)
48763448Sdh155122 {
48773448Sdh155122 if (ksp != NULL) {
48783448Sdh155122 ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private);
48793448Sdh155122 kstat_delete_netstack(ksp, stackid);
48800Sstevel@tonic-gate }
48810Sstevel@tonic-gate }
48820Sstevel@tonic-gate
48830Sstevel@tonic-gate /*
48840Sstevel@tonic-gate * The following two functions set and get the value for the
48850Sstevel@tonic-gate * IPV6_SRC_PREFERENCES socket option.
48860Sstevel@tonic-gate */
48870Sstevel@tonic-gate int
ip6_set_src_preferences(ip_xmit_attr_t * ixa,uint32_t prefs)488811042SErik.Nordmark@Sun.COM ip6_set_src_preferences(ip_xmit_attr_t *ixa, uint32_t prefs)
48890Sstevel@tonic-gate {
48900Sstevel@tonic-gate /*
48910Sstevel@tonic-gate * We only support preferences that are covered by
48920Sstevel@tonic-gate * IPV6_PREFER_SRC_MASK.
48930Sstevel@tonic-gate */
48940Sstevel@tonic-gate if (prefs & ~IPV6_PREFER_SRC_MASK)
48950Sstevel@tonic-gate return (EINVAL);
48960Sstevel@tonic-gate
48970Sstevel@tonic-gate /*
48980Sstevel@tonic-gate * Look for conflicting preferences or default preferences. If
48990Sstevel@tonic-gate * both bits of a related pair are clear, the application wants the
49000Sstevel@tonic-gate * system's default value for that pair. Both bits in a pair can't
49010Sstevel@tonic-gate * be set.
49020Sstevel@tonic-gate */
49030Sstevel@tonic-gate if ((prefs & IPV6_PREFER_SRC_MIPMASK) == 0) {
49040Sstevel@tonic-gate prefs |= IPV6_PREFER_SRC_MIPDEFAULT;
49050Sstevel@tonic-gate } else if ((prefs & IPV6_PREFER_SRC_MIPMASK) ==
49060Sstevel@tonic-gate IPV6_PREFER_SRC_MIPMASK) {
49070Sstevel@tonic-gate return (EINVAL);
49080Sstevel@tonic-gate }
49090Sstevel@tonic-gate if ((prefs & IPV6_PREFER_SRC_TMPMASK) == 0) {
49100Sstevel@tonic-gate prefs |= IPV6_PREFER_SRC_TMPDEFAULT;
49110Sstevel@tonic-gate } else if ((prefs & IPV6_PREFER_SRC_TMPMASK) ==
49120Sstevel@tonic-gate IPV6_PREFER_SRC_TMPMASK) {
49130Sstevel@tonic-gate return (EINVAL);
49140Sstevel@tonic-gate }
49150Sstevel@tonic-gate if ((prefs & IPV6_PREFER_SRC_CGAMASK) == 0) {
49160Sstevel@tonic-gate prefs |= IPV6_PREFER_SRC_CGADEFAULT;
49170Sstevel@tonic-gate } else if ((prefs & IPV6_PREFER_SRC_CGAMASK) ==
49180Sstevel@tonic-gate IPV6_PREFER_SRC_CGAMASK) {
49190Sstevel@tonic-gate return (EINVAL);
49200Sstevel@tonic-gate }
49210Sstevel@tonic-gate
492211042SErik.Nordmark@Sun.COM ixa->ixa_src_preferences = prefs;
49230Sstevel@tonic-gate return (0);
49240Sstevel@tonic-gate }
49250Sstevel@tonic-gate
49260Sstevel@tonic-gate size_t
ip6_get_src_preferences(ip_xmit_attr_t * ixa,uint32_t * val)492711042SErik.Nordmark@Sun.COM ip6_get_src_preferences(ip_xmit_attr_t *ixa, uint32_t *val)
49280Sstevel@tonic-gate {
492911042SErik.Nordmark@Sun.COM *val = ixa->ixa_src_preferences;
493011042SErik.Nordmark@Sun.COM return (sizeof (ixa->ixa_src_preferences));
49310Sstevel@tonic-gate }
49320Sstevel@tonic-gate
49330Sstevel@tonic-gate /*
49340Sstevel@tonic-gate * Get the size of the IP options (including the IP headers size)
49350Sstevel@tonic-gate * without including the AH header's size. If till_ah is B_FALSE,
49360Sstevel@tonic-gate * and if AH header is present, dest options beyond AH header will
49370Sstevel@tonic-gate * also be included in the returned size.
49380Sstevel@tonic-gate */
49390Sstevel@tonic-gate int
ipsec_ah_get_hdr_size_v6(mblk_t * mp,boolean_t till_ah)49400Sstevel@tonic-gate ipsec_ah_get_hdr_size_v6(mblk_t *mp, boolean_t till_ah)
49410Sstevel@tonic-gate {
49420Sstevel@tonic-gate ip6_t *ip6h;
49430Sstevel@tonic-gate uint8_t nexthdr;
49440Sstevel@tonic-gate uint8_t *whereptr;
49450Sstevel@tonic-gate ip6_hbh_t *hbhhdr;
49460Sstevel@tonic-gate ip6_dest_t *dsthdr;
49470Sstevel@tonic-gate ip6_rthdr_t *rthdr;
49480Sstevel@tonic-gate int ehdrlen;
49490Sstevel@tonic-gate int size;
49500Sstevel@tonic-gate ah_t *ah;
49510Sstevel@tonic-gate
49520Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr;
49530Sstevel@tonic-gate size = IPV6_HDR_LEN;
49540Sstevel@tonic-gate nexthdr = ip6h->ip6_nxt;
49550Sstevel@tonic-gate whereptr = (uint8_t *)&ip6h[1];
49560Sstevel@tonic-gate for (;;) {
49570Sstevel@tonic-gate /* Assume IP has already stripped it */
495811042SErik.Nordmark@Sun.COM ASSERT(nexthdr != IPPROTO_FRAGMENT);
49590Sstevel@tonic-gate switch (nexthdr) {
49600Sstevel@tonic-gate case IPPROTO_HOPOPTS:
49610Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)whereptr;
49620Sstevel@tonic-gate nexthdr = hbhhdr->ip6h_nxt;
49630Sstevel@tonic-gate ehdrlen = 8 * (hbhhdr->ip6h_len + 1);
49640Sstevel@tonic-gate break;
49650Sstevel@tonic-gate case IPPROTO_DSTOPTS:
49660Sstevel@tonic-gate dsthdr = (ip6_dest_t *)whereptr;
49670Sstevel@tonic-gate nexthdr = dsthdr->ip6d_nxt;
49680Sstevel@tonic-gate ehdrlen = 8 * (dsthdr->ip6d_len + 1);
49690Sstevel@tonic-gate break;
49700Sstevel@tonic-gate case IPPROTO_ROUTING:
49710Sstevel@tonic-gate rthdr = (ip6_rthdr_t *)whereptr;
49720Sstevel@tonic-gate nexthdr = rthdr->ip6r_nxt;
49730Sstevel@tonic-gate ehdrlen = 8 * (rthdr->ip6r_len + 1);
49740Sstevel@tonic-gate break;
49750Sstevel@tonic-gate default :
49760Sstevel@tonic-gate if (till_ah) {
49770Sstevel@tonic-gate ASSERT(nexthdr == IPPROTO_AH);
49780Sstevel@tonic-gate return (size);
49790Sstevel@tonic-gate }
49800Sstevel@tonic-gate /*
49810Sstevel@tonic-gate * If we don't have a AH header to traverse,
49820Sstevel@tonic-gate * return now. This happens normally for
49830Sstevel@tonic-gate * outbound datagrams where we have not inserted
49840Sstevel@tonic-gate * the AH header.
49850Sstevel@tonic-gate */
49860Sstevel@tonic-gate if (nexthdr != IPPROTO_AH) {
49870Sstevel@tonic-gate return (size);
49880Sstevel@tonic-gate }
49890Sstevel@tonic-gate
49900Sstevel@tonic-gate /*
49910Sstevel@tonic-gate * We don't include the AH header's size
49920Sstevel@tonic-gate * to be symmetrical with other cases where
49930Sstevel@tonic-gate * we either don't have a AH header (outbound)
49940Sstevel@tonic-gate * or peek into the AH header yet (inbound and
49950Sstevel@tonic-gate * not pulled up yet).
49960Sstevel@tonic-gate */
49970Sstevel@tonic-gate ah = (ah_t *)whereptr;
49980Sstevel@tonic-gate nexthdr = ah->ah_nexthdr;
49990Sstevel@tonic-gate ehdrlen = (ah->ah_length << 2) + 8;
50000Sstevel@tonic-gate
50010Sstevel@tonic-gate if (nexthdr == IPPROTO_DSTOPTS) {
50020Sstevel@tonic-gate if (whereptr + ehdrlen >= mp->b_wptr) {
50030Sstevel@tonic-gate /*
50040Sstevel@tonic-gate * The destination options header
50050Sstevel@tonic-gate * is not part of the first mblk.
50060Sstevel@tonic-gate */
50070Sstevel@tonic-gate whereptr = mp->b_cont->b_rptr;
50080Sstevel@tonic-gate } else {
50090Sstevel@tonic-gate whereptr += ehdrlen;
50100Sstevel@tonic-gate }
50110Sstevel@tonic-gate
50120Sstevel@tonic-gate dsthdr = (ip6_dest_t *)whereptr;
50130Sstevel@tonic-gate ehdrlen = 8 * (dsthdr->ip6d_len + 1);
50140Sstevel@tonic-gate size += ehdrlen;
50150Sstevel@tonic-gate }
50160Sstevel@tonic-gate return (size);
50170Sstevel@tonic-gate }
50180Sstevel@tonic-gate whereptr += ehdrlen;
50190Sstevel@tonic-gate size += ehdrlen;
50200Sstevel@tonic-gate }
50210Sstevel@tonic-gate }
50228485SPeter.Memishian@Sun.COM
50238485SPeter.Memishian@Sun.COM /*
50248485SPeter.Memishian@Sun.COM * Utility routine that checks if `v6srcp' is a valid address on underlying
50258485SPeter.Memishian@Sun.COM * interface `ill'. If `ipifp' is non-NULL, it's set to a held ipif
50268485SPeter.Memishian@Sun.COM * associated with `v6srcp' on success. NOTE: if this is not called from
50278485SPeter.Memishian@Sun.COM * inside the IPSQ (ill_g_lock is not held), `ill' may be removed from the
50288485SPeter.Memishian@Sun.COM * group during or after this lookup.
50298485SPeter.Memishian@Sun.COM */
503011042SErik.Nordmark@Sun.COM boolean_t
ipif_lookup_testaddr_v6(ill_t * ill,const in6_addr_t * v6srcp,ipif_t ** ipifp)50318485SPeter.Memishian@Sun.COM ipif_lookup_testaddr_v6(ill_t *ill, const in6_addr_t *v6srcp, ipif_t **ipifp)
50328485SPeter.Memishian@Sun.COM {
50338485SPeter.Memishian@Sun.COM ipif_t *ipif;
50348485SPeter.Memishian@Sun.COM
503511042SErik.Nordmark@Sun.COM
50368485SPeter.Memishian@Sun.COM ipif = ipif_lookup_addr_exact_v6(v6srcp, ill, ill->ill_ipst);
50378485SPeter.Memishian@Sun.COM if (ipif != NULL) {
50388485SPeter.Memishian@Sun.COM if (ipifp != NULL)
50398485SPeter.Memishian@Sun.COM *ipifp = ipif;
50408485SPeter.Memishian@Sun.COM else
50418485SPeter.Memishian@Sun.COM ipif_refrele(ipif);
50428485SPeter.Memishian@Sun.COM return (B_TRUE);
50438485SPeter.Memishian@Sun.COM }
50448485SPeter.Memishian@Sun.COM
50458485SPeter.Memishian@Sun.COM if (ip_debug > 2) {
50468485SPeter.Memishian@Sun.COM pr_addr_dbg("ipif_lookup_testaddr_v6: cannot find ipif for "
50478485SPeter.Memishian@Sun.COM "src %s\n", AF_INET6, v6srcp);
50488485SPeter.Memishian@Sun.COM }
50498485SPeter.Memishian@Sun.COM return (B_FALSE);
50508485SPeter.Memishian@Sun.COM }
5051