111042SErik.Nordmark@Sun.COM /* 211042SErik.Nordmark@Sun.COM * CDDL HEADER START 311042SErik.Nordmark@Sun.COM * 411042SErik.Nordmark@Sun.COM * The contents of this file are subject to the terms of the 511042SErik.Nordmark@Sun.COM * Common Development and Distribution License (the "License"). 611042SErik.Nordmark@Sun.COM * You may not use this file except in compliance with the License. 711042SErik.Nordmark@Sun.COM * 811042SErik.Nordmark@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 911042SErik.Nordmark@Sun.COM * or http://www.opensolaris.org/os/licensing. 1011042SErik.Nordmark@Sun.COM * See the License for the specific language governing permissions 1111042SErik.Nordmark@Sun.COM * and limitations under the License. 1211042SErik.Nordmark@Sun.COM * 1311042SErik.Nordmark@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 1411042SErik.Nordmark@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1511042SErik.Nordmark@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 1611042SErik.Nordmark@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 1711042SErik.Nordmark@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 1811042SErik.Nordmark@Sun.COM * 1911042SErik.Nordmark@Sun.COM * CDDL HEADER END 2011042SErik.Nordmark@Sun.COM */ 2111042SErik.Nordmark@Sun.COM 2211042SErik.Nordmark@Sun.COM /* 23*11457SErik.Nordmark@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 2411042SErik.Nordmark@Sun.COM * Use is subject to license terms. 2511042SErik.Nordmark@Sun.COM */ 2611042SErik.Nordmark@Sun.COM /* Copyright (c) 1990 Mentat Inc. */ 2711042SErik.Nordmark@Sun.COM 2811042SErik.Nordmark@Sun.COM #include <sys/types.h> 2911042SErik.Nordmark@Sun.COM #include <sys/stream.h> 3011042SErik.Nordmark@Sun.COM #include <sys/strsubr.h> 3111042SErik.Nordmark@Sun.COM #include <sys/dlpi.h> 3211042SErik.Nordmark@Sun.COM #include <sys/strsun.h> 3311042SErik.Nordmark@Sun.COM #include <sys/zone.h> 3411042SErik.Nordmark@Sun.COM #include <sys/ddi.h> 3511042SErik.Nordmark@Sun.COM #include <sys/sunddi.h> 3611042SErik.Nordmark@Sun.COM #include <sys/cmn_err.h> 3711042SErik.Nordmark@Sun.COM #include <sys/debug.h> 3811042SErik.Nordmark@Sun.COM #include <sys/atomic.h> 3911042SErik.Nordmark@Sun.COM 4011042SErik.Nordmark@Sun.COM #include <sys/systm.h> 4111042SErik.Nordmark@Sun.COM #include <sys/param.h> 4211042SErik.Nordmark@Sun.COM #include <sys/kmem.h> 4311042SErik.Nordmark@Sun.COM #include <sys/sdt.h> 4411042SErik.Nordmark@Sun.COM #include <sys/socket.h> 4511042SErik.Nordmark@Sun.COM #include <sys/mac.h> 4611042SErik.Nordmark@Sun.COM #include <net/if.h> 4711042SErik.Nordmark@Sun.COM #include <net/if_arp.h> 4811042SErik.Nordmark@Sun.COM #include <net/route.h> 4911042SErik.Nordmark@Sun.COM #include <sys/sockio.h> 5011042SErik.Nordmark@Sun.COM #include <netinet/in.h> 5111042SErik.Nordmark@Sun.COM #include <net/if_dl.h> 5211042SErik.Nordmark@Sun.COM 5311042SErik.Nordmark@Sun.COM #include <inet/common.h> 5411042SErik.Nordmark@Sun.COM #include <inet/mi.h> 5511042SErik.Nordmark@Sun.COM #include <inet/mib2.h> 5611042SErik.Nordmark@Sun.COM #include <inet/nd.h> 5711042SErik.Nordmark@Sun.COM #include <inet/arp.h> 5811042SErik.Nordmark@Sun.COM #include <inet/snmpcom.h> 5911042SErik.Nordmark@Sun.COM #include <inet/kstatcom.h> 6011042SErik.Nordmark@Sun.COM 6111042SErik.Nordmark@Sun.COM #include <netinet/igmp_var.h> 6211042SErik.Nordmark@Sun.COM #include <netinet/ip6.h> 6311042SErik.Nordmark@Sun.COM #include <netinet/icmp6.h> 6411042SErik.Nordmark@Sun.COM #include <netinet/sctp.h> 6511042SErik.Nordmark@Sun.COM 6611042SErik.Nordmark@Sun.COM #include <inet/ip.h> 6711042SErik.Nordmark@Sun.COM #include <inet/ip_impl.h> 6811042SErik.Nordmark@Sun.COM #include <inet/ip6.h> 6911042SErik.Nordmark@Sun.COM #include <inet/ip6_asp.h> 7011042SErik.Nordmark@Sun.COM #include <inet/tcp.h> 7111042SErik.Nordmark@Sun.COM #include <inet/ip_multi.h> 7211042SErik.Nordmark@Sun.COM #include <inet/ip_if.h> 7311042SErik.Nordmark@Sun.COM #include <inet/ip_ire.h> 7411042SErik.Nordmark@Sun.COM #include <inet/ip_ftable.h> 7511042SErik.Nordmark@Sun.COM #include <inet/ip_rts.h> 7611042SErik.Nordmark@Sun.COM #include <inet/optcom.h> 7711042SErik.Nordmark@Sun.COM #include <inet/ip_ndp.h> 7811042SErik.Nordmark@Sun.COM #include <inet/ip_listutils.h> 7911042SErik.Nordmark@Sun.COM #include <netinet/igmp.h> 8011042SErik.Nordmark@Sun.COM #include <netinet/ip_mroute.h> 8111042SErik.Nordmark@Sun.COM #include <inet/ipp_common.h> 8211042SErik.Nordmark@Sun.COM 8311042SErik.Nordmark@Sun.COM #include <net/pfkeyv2.h> 8411042SErik.Nordmark@Sun.COM #include <inet/sadb.h> 8511042SErik.Nordmark@Sun.COM #include <inet/ipsec_impl.h> 8611042SErik.Nordmark@Sun.COM #include <inet/ipdrop.h> 8711042SErik.Nordmark@Sun.COM #include <inet/ip_netinfo.h> 8811042SErik.Nordmark@Sun.COM 8911042SErik.Nordmark@Sun.COM #include <sys/pattr.h> 9011042SErik.Nordmark@Sun.COM #include <inet/ipclassifier.h> 9111042SErik.Nordmark@Sun.COM #include <inet/sctp_ip.h> 9211042SErik.Nordmark@Sun.COM #include <inet/sctp/sctp_impl.h> 9311042SErik.Nordmark@Sun.COM #include <inet/udp_impl.h> 9411042SErik.Nordmark@Sun.COM #include <sys/sunddi.h> 9511042SErik.Nordmark@Sun.COM 9611042SErik.Nordmark@Sun.COM #include <sys/tsol/label.h> 9711042SErik.Nordmark@Sun.COM #include <sys/tsol/tnet.h> 9811042SErik.Nordmark@Sun.COM 9911042SErik.Nordmark@Sun.COM #ifdef DEBUG 10011042SErik.Nordmark@Sun.COM extern boolean_t skip_sctp_cksum; 10111042SErik.Nordmark@Sun.COM #endif 10211042SErik.Nordmark@Sun.COM 10311042SErik.Nordmark@Sun.COM int 10411042SErik.Nordmark@Sun.COM ip_output_simple_v6(mblk_t *mp, ip_xmit_attr_t *ixa) 10511042SErik.Nordmark@Sun.COM { 10611042SErik.Nordmark@Sun.COM ip6_t *ip6h; 10711042SErik.Nordmark@Sun.COM in6_addr_t firsthop; /* In IP header */ 10811042SErik.Nordmark@Sun.COM in6_addr_t dst; /* End of source route, or ip6_dst if none */ 10911042SErik.Nordmark@Sun.COM ire_t *ire; 11011042SErik.Nordmark@Sun.COM in6_addr_t setsrc; 11111042SErik.Nordmark@Sun.COM int error; 11211042SErik.Nordmark@Sun.COM ill_t *ill = NULL; 11311042SErik.Nordmark@Sun.COM dce_t *dce = NULL; 11411042SErik.Nordmark@Sun.COM nce_t *nce; 11511042SErik.Nordmark@Sun.COM iaflags_t ixaflags = ixa->ixa_flags; 11611042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst; 11711042SErik.Nordmark@Sun.COM uint8_t *nexthdrp; 11811042SErik.Nordmark@Sun.COM boolean_t repeat = B_FALSE; 11911042SErik.Nordmark@Sun.COM boolean_t multirt = B_FALSE; 12011042SErik.Nordmark@Sun.COM uint_t ifindex; 12111066Srafael.vanoni@sun.com int64_t now; 12211042SErik.Nordmark@Sun.COM 12311042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr; 12411042SErik.Nordmark@Sun.COM ASSERT(IPH_HDR_VERSION(ip6h) == IPV6_VERSION); 12511042SErik.Nordmark@Sun.COM 12611042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_nce == NULL); 12711042SErik.Nordmark@Sun.COM 12811042SErik.Nordmark@Sun.COM ixa->ixa_pktlen = ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN; 12911042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_pktlen == msgdsize(mp)); 13011042SErik.Nordmark@Sun.COM if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &ixa->ixa_ip_hdr_length, 13111042SErik.Nordmark@Sun.COM &nexthdrp)) { 13211042SErik.Nordmark@Sun.COM /* Malformed packet */ 13311042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsHCOutRequests); 13411042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards); 13511042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards", mp, NULL); 13611042SErik.Nordmark@Sun.COM freemsg(mp); 13711042SErik.Nordmark@Sun.COM return (EINVAL); 13811042SErik.Nordmark@Sun.COM } 13911042SErik.Nordmark@Sun.COM ixa->ixa_protocol = *nexthdrp; 14011042SErik.Nordmark@Sun.COM 14111042SErik.Nordmark@Sun.COM /* 14211042SErik.Nordmark@Sun.COM * Assumes that source routed packets have already been massaged by 14311042SErik.Nordmark@Sun.COM * the ULP (ip_massage_options_v6) and as a result ip6_dst is the next 14411042SErik.Nordmark@Sun.COM * hop in the source route. The final destination is used for IPsec 14511042SErik.Nordmark@Sun.COM * policy and DCE lookup. 14611042SErik.Nordmark@Sun.COM */ 14711042SErik.Nordmark@Sun.COM firsthop = ip6h->ip6_dst; 14811042SErik.Nordmark@Sun.COM dst = ip_get_dst_v6(ip6h, mp, NULL); 14911042SErik.Nordmark@Sun.COM 15011042SErik.Nordmark@Sun.COM repeat_ire: 15111042SErik.Nordmark@Sun.COM error = 0; 15211042SErik.Nordmark@Sun.COM setsrc = ipv6_all_zeros; 15311042SErik.Nordmark@Sun.COM ire = ip_select_route_v6(&firsthop, ixa, NULL, &setsrc, &error, 15411042SErik.Nordmark@Sun.COM &multirt); 15511042SErik.Nordmark@Sun.COM ASSERT(ire != NULL); /* IRE_NOROUTE if none found */ 15611042SErik.Nordmark@Sun.COM if (error != 0) { 15711042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsHCOutRequests); 15811042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards); 15911042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards", mp, NULL); 16011042SErik.Nordmark@Sun.COM freemsg(mp); 16111042SErik.Nordmark@Sun.COM goto done; 16211042SErik.Nordmark@Sun.COM } 16311042SErik.Nordmark@Sun.COM 16411042SErik.Nordmark@Sun.COM if (ire->ire_flags & (RTF_BLACKHOLE|RTF_REJECT)) { 16511042SErik.Nordmark@Sun.COM /* ire_ill might be NULL hence need to skip some code */ 16611042SErik.Nordmark@Sun.COM if (ixaflags & IXAF_SET_SOURCE) 16711042SErik.Nordmark@Sun.COM ip6h->ip6_src = ipv6_loopback; 16811042SErik.Nordmark@Sun.COM ixa->ixa_fragsize = IP_MAXPACKET; 16911042SErik.Nordmark@Sun.COM ire->ire_ob_pkt_count++; 17011042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsHCOutRequests); 17111042SErik.Nordmark@Sun.COM /* No dce yet; use default one */ 17211042SErik.Nordmark@Sun.COM error = (ire->ire_sendfn)(ire, mp, ip6h, ixa, 17311042SErik.Nordmark@Sun.COM &ipst->ips_dce_default->dce_ident); 17411042SErik.Nordmark@Sun.COM goto done; 17511042SErik.Nordmark@Sun.COM } 17611042SErik.Nordmark@Sun.COM 17711042SErik.Nordmark@Sun.COM /* Note that ip6_dst is only used for IRE_MULTICAST */ 17811042SErik.Nordmark@Sun.COM nce = ire_to_nce(ire, INADDR_ANY, &ip6h->ip6_dst); 17911042SErik.Nordmark@Sun.COM if (nce == NULL) { 18011042SErik.Nordmark@Sun.COM /* Allocation failure? */ 18111042SErik.Nordmark@Sun.COM ip_drop_output("ire_to_nce", mp, ill); 18211042SErik.Nordmark@Sun.COM freemsg(mp); 18311042SErik.Nordmark@Sun.COM error = ENOBUFS; 18411042SErik.Nordmark@Sun.COM goto done; 18511042SErik.Nordmark@Sun.COM } 18611042SErik.Nordmark@Sun.COM if (nce->nce_is_condemned) { 18711042SErik.Nordmark@Sun.COM nce_t *nce1; 18811042SErik.Nordmark@Sun.COM 18911042SErik.Nordmark@Sun.COM nce1 = ire_handle_condemned_nce(nce, ire, NULL, ip6h, B_TRUE); 19011042SErik.Nordmark@Sun.COM nce_refrele(nce); 19111042SErik.Nordmark@Sun.COM if (nce1 == NULL) { 19211042SErik.Nordmark@Sun.COM if (!repeat) { 19311042SErik.Nordmark@Sun.COM /* Try finding a better IRE */ 19411042SErik.Nordmark@Sun.COM repeat = B_TRUE; 19511042SErik.Nordmark@Sun.COM ire_refrele(ire); 19611042SErik.Nordmark@Sun.COM goto repeat_ire; 19711042SErik.Nordmark@Sun.COM } 19811042SErik.Nordmark@Sun.COM /* Tried twice - drop packet */ 19911042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards); 20011042SErik.Nordmark@Sun.COM ip_drop_output("No nce", mp, ill); 20111042SErik.Nordmark@Sun.COM freemsg(mp); 20211042SErik.Nordmark@Sun.COM error = ENOBUFS; 20311042SErik.Nordmark@Sun.COM goto done; 20411042SErik.Nordmark@Sun.COM } 20511042SErik.Nordmark@Sun.COM nce = nce1; 20611042SErik.Nordmark@Sun.COM } 20711042SErik.Nordmark@Sun.COM /* 20811042SErik.Nordmark@Sun.COM * For multicast with multirt we have a flag passed back from 20911042SErik.Nordmark@Sun.COM * ire_lookup_multi_ill_v6 since we don't have an IRE for each 21011042SErik.Nordmark@Sun.COM * possible multicast address. 21111042SErik.Nordmark@Sun.COM * We also need a flag for multicast since we can't check 21211042SErik.Nordmark@Sun.COM * whether RTF_MULTIRT is set in ixa_ire for multicast. 21311042SErik.Nordmark@Sun.COM */ 21411042SErik.Nordmark@Sun.COM if (multirt) { 21511042SErik.Nordmark@Sun.COM ixa->ixa_postfragfn = ip_postfrag_multirt_v6; 21611042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_MULTIRT_MULTICAST; 21711042SErik.Nordmark@Sun.COM } else { 21811042SErik.Nordmark@Sun.COM ixa->ixa_postfragfn = ire->ire_postfragfn; 21911042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_MULTIRT_MULTICAST; 22011042SErik.Nordmark@Sun.COM } 22111042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_nce == NULL); 22211042SErik.Nordmark@Sun.COM ixa->ixa_nce = nce; 22311042SErik.Nordmark@Sun.COM 22411042SErik.Nordmark@Sun.COM /* 22511042SErik.Nordmark@Sun.COM * Check for a dce_t with a path mtu. 22611042SErik.Nordmark@Sun.COM */ 22711042SErik.Nordmark@Sun.COM ifindex = 0; 22811042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_LINKSCOPE(&dst)) 22911042SErik.Nordmark@Sun.COM ifindex = nce->nce_common->ncec_ill->ill_phyint->phyint_ifindex; 23011042SErik.Nordmark@Sun.COM 23111042SErik.Nordmark@Sun.COM dce = dce_lookup_v6(&dst, ifindex, ipst, NULL); 23211042SErik.Nordmark@Sun.COM ASSERT(dce != NULL); 23311042SErik.Nordmark@Sun.COM 23411042SErik.Nordmark@Sun.COM if (!(ixaflags & IXAF_PMTU_DISCOVERY)) { 23511042SErik.Nordmark@Sun.COM ixa->ixa_fragsize = IPV6_MIN_MTU; 23611042SErik.Nordmark@Sun.COM } else if (dce->dce_flags & DCEF_PMTU) { 23711042SErik.Nordmark@Sun.COM /* 23811042SErik.Nordmark@Sun.COM * To avoid a periodic timer to increase the path MTU we 23911042SErik.Nordmark@Sun.COM * look at dce_last_change_time each time we send a packet. 24011042SErik.Nordmark@Sun.COM */ 24111066Srafael.vanoni@sun.com now = ddi_get_lbolt64(); 24211066Srafael.vanoni@sun.com if (TICK_TO_SEC(now) - dce->dce_last_change_time > 24311042SErik.Nordmark@Sun.COM ipst->ips_ip_pathmtu_interval) { 24411042SErik.Nordmark@Sun.COM /* 24511042SErik.Nordmark@Sun.COM * Older than 20 minutes. Drop the path MTU information. 24611042SErik.Nordmark@Sun.COM */ 24711042SErik.Nordmark@Sun.COM mutex_enter(&dce->dce_lock); 24811042SErik.Nordmark@Sun.COM dce->dce_flags &= ~(DCEF_PMTU|DCEF_TOO_SMALL_PMTU); 24911066Srafael.vanoni@sun.com dce->dce_last_change_time = TICK_TO_SEC(now); 25011042SErik.Nordmark@Sun.COM mutex_exit(&dce->dce_lock); 25111042SErik.Nordmark@Sun.COM dce_increment_generation(dce); 25211042SErik.Nordmark@Sun.COM ixa->ixa_fragsize = ip_get_base_mtu(nce->nce_ill, ire); 25311042SErik.Nordmark@Sun.COM } else { 25411042SErik.Nordmark@Sun.COM uint_t fragsize; 25511042SErik.Nordmark@Sun.COM 25611042SErik.Nordmark@Sun.COM fragsize = ip_get_base_mtu(nce->nce_ill, ire); 25711042SErik.Nordmark@Sun.COM if (fragsize > dce->dce_pmtu) 25811042SErik.Nordmark@Sun.COM fragsize = dce->dce_pmtu; 25911042SErik.Nordmark@Sun.COM ixa->ixa_fragsize = fragsize; 26011042SErik.Nordmark@Sun.COM } 26111042SErik.Nordmark@Sun.COM } else { 26211042SErik.Nordmark@Sun.COM ixa->ixa_fragsize = ip_get_base_mtu(nce->nce_ill, ire); 26311042SErik.Nordmark@Sun.COM } 26411042SErik.Nordmark@Sun.COM 26511042SErik.Nordmark@Sun.COM /* 26611042SErik.Nordmark@Sun.COM * We use use ire_nexthop_ill (and not ncec_ill) to avoid the under ipmp 26711042SErik.Nordmark@Sun.COM * interface for source address selection. 26811042SErik.Nordmark@Sun.COM */ 26911042SErik.Nordmark@Sun.COM ill = ire_nexthop_ill(ire); 27011042SErik.Nordmark@Sun.COM 27111042SErik.Nordmark@Sun.COM if (ixaflags & IXAF_SET_SOURCE) { 27211042SErik.Nordmark@Sun.COM in6_addr_t src; 27311042SErik.Nordmark@Sun.COM 27411042SErik.Nordmark@Sun.COM /* 27511042SErik.Nordmark@Sun.COM * We use the final destination to get 27611042SErik.Nordmark@Sun.COM * correct selection for source routed packets 27711042SErik.Nordmark@Sun.COM */ 27811042SErik.Nordmark@Sun.COM 27911042SErik.Nordmark@Sun.COM /* If unreachable we have no ill but need some source */ 28011042SErik.Nordmark@Sun.COM if (ill == NULL) { 28111042SErik.Nordmark@Sun.COM src = ipv6_loopback; 28211042SErik.Nordmark@Sun.COM error = 0; 28311042SErik.Nordmark@Sun.COM } else { 28411042SErik.Nordmark@Sun.COM error = ip_select_source_v6(ill, &setsrc, &dst, 28511042SErik.Nordmark@Sun.COM ixa->ixa_zoneid, ipst, B_FALSE, 28611042SErik.Nordmark@Sun.COM ixa->ixa_src_preferences, &src, NULL, NULL); 28711042SErik.Nordmark@Sun.COM } 28811042SErik.Nordmark@Sun.COM if (error != 0) { 28911042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCOutRequests); 29011042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards); 29111042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards - no source", 29211042SErik.Nordmark@Sun.COM mp, ill); 29311042SErik.Nordmark@Sun.COM freemsg(mp); 29411042SErik.Nordmark@Sun.COM goto done; 29511042SErik.Nordmark@Sun.COM } 29611042SErik.Nordmark@Sun.COM ip6h->ip6_src = src; 29711042SErik.Nordmark@Sun.COM } else if (ixaflags & IXAF_VERIFY_SOURCE) { 29811042SErik.Nordmark@Sun.COM /* Check if the IP source is assigned to the host. */ 29911042SErik.Nordmark@Sun.COM if (!ip_verify_src(mp, ixa, NULL)) { 30011042SErik.Nordmark@Sun.COM /* Don't send a packet with a source that isn't ours */ 30111042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsHCOutRequests); 30211042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards); 30311042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards - invalid source", 30411042SErik.Nordmark@Sun.COM mp, ill); 30511042SErik.Nordmark@Sun.COM freemsg(mp); 30611042SErik.Nordmark@Sun.COM error = EADDRNOTAVAIL; 30711042SErik.Nordmark@Sun.COM goto done; 30811042SErik.Nordmark@Sun.COM } 30911042SErik.Nordmark@Sun.COM } 31011042SErik.Nordmark@Sun.COM 31111042SErik.Nordmark@Sun.COM /* 31211042SErik.Nordmark@Sun.COM * Check against global IPsec policy to set the AH/ESP attributes. 31311042SErik.Nordmark@Sun.COM * IPsec will set IXAF_IPSEC_* and ixa_ipsec_* as appropriate. 31411042SErik.Nordmark@Sun.COM */ 31511042SErik.Nordmark@Sun.COM if (!(ixaflags & (IXAF_NO_IPSEC|IXAF_IPSEC_SECURE))) { 31611042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_ipsec_policy == NULL); 31711042SErik.Nordmark@Sun.COM mp = ip_output_attach_policy(mp, NULL, ip6h, NULL, ixa); 31811042SErik.Nordmark@Sun.COM if (mp == NULL) { 31911042SErik.Nordmark@Sun.COM /* MIB and ip_drop_packet already done */ 32011042SErik.Nordmark@Sun.COM return (EHOSTUNREACH); /* IPsec policy failure */ 32111042SErik.Nordmark@Sun.COM } 32211042SErik.Nordmark@Sun.COM } 32311042SErik.Nordmark@Sun.COM 32411042SErik.Nordmark@Sun.COM if (ill != NULL) { 32511042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCOutRequests); 32611042SErik.Nordmark@Sun.COM } else { 32711042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsHCOutRequests); 32811042SErik.Nordmark@Sun.COM } 32911042SErik.Nordmark@Sun.COM 33011042SErik.Nordmark@Sun.COM /* 33111042SErik.Nordmark@Sun.COM * We update the statistics on the most specific IRE i.e., the first 33211042SErik.Nordmark@Sun.COM * one we found. 33311042SErik.Nordmark@Sun.COM * We don't have an IRE when we fragment, hence ire_ob_pkt_count 33411042SErik.Nordmark@Sun.COM * can only count the use prior to fragmentation. However the MIB 33511042SErik.Nordmark@Sun.COM * counters on the ill will be incremented in post fragmentation. 33611042SErik.Nordmark@Sun.COM */ 33711042SErik.Nordmark@Sun.COM ire->ire_ob_pkt_count++; 33811042SErik.Nordmark@Sun.COM 33911042SErik.Nordmark@Sun.COM /* 34011042SErik.Nordmark@Sun.COM * Based on ire_type and ire_flags call one of: 34111042SErik.Nordmark@Sun.COM * ire_send_local_v6 - for IRE_LOCAL and IRE_LOOPBACK 34211042SErik.Nordmark@Sun.COM * ire_send_multirt_v6 - if RTF_MULTIRT 34311042SErik.Nordmark@Sun.COM * ire_send_noroute_v6 - if RTF_REJECT or RTF_BLACHOLE 34411042SErik.Nordmark@Sun.COM * ire_send_multicast_v6 - for IRE_MULTICAST 34511042SErik.Nordmark@Sun.COM * ire_send_wire_v6 - for the rest. 34611042SErik.Nordmark@Sun.COM */ 34711042SErik.Nordmark@Sun.COM error = (ire->ire_sendfn)(ire, mp, ip6h, ixa, &dce->dce_ident); 34811042SErik.Nordmark@Sun.COM done: 34911042SErik.Nordmark@Sun.COM ire_refrele(ire); 35011042SErik.Nordmark@Sun.COM if (dce != NULL) 35111042SErik.Nordmark@Sun.COM dce_refrele(dce); 35211042SErik.Nordmark@Sun.COM if (ill != NULL) 35311042SErik.Nordmark@Sun.COM ill_refrele(ill); 35411042SErik.Nordmark@Sun.COM if (ixa->ixa_nce != NULL) 35511042SErik.Nordmark@Sun.COM nce_refrele(ixa->ixa_nce); 35611042SErik.Nordmark@Sun.COM ixa->ixa_nce = NULL; 35711042SErik.Nordmark@Sun.COM return (error); 35811042SErik.Nordmark@Sun.COM } 35911042SErik.Nordmark@Sun.COM 36011042SErik.Nordmark@Sun.COM /* 36111042SErik.Nordmark@Sun.COM * ire_sendfn() functions. 36211042SErik.Nordmark@Sun.COM * These functions use the following xmit_attr: 36311042SErik.Nordmark@Sun.COM * - ixa_fragsize - read to determine whether or not to fragment 36411042SErik.Nordmark@Sun.COM * - IXAF_IPSEC_SECURE - to determine whether or not to invoke IPsec 36511042SErik.Nordmark@Sun.COM * - ixa_ipsec_* are used inside IPsec 36611042SErik.Nordmark@Sun.COM * - IXAF_LOOPBACK_COPY - for multicast 36711042SErik.Nordmark@Sun.COM */ 36811042SErik.Nordmark@Sun.COM 36911042SErik.Nordmark@Sun.COM 37011042SErik.Nordmark@Sun.COM /* 37111042SErik.Nordmark@Sun.COM * ire_sendfn for IRE_LOCAL and IRE_LOOPBACK 37211042SErik.Nordmark@Sun.COM * 37311042SErik.Nordmark@Sun.COM * The checks for restrict_interzone_loopback are done in ire_route_recursive. 37411042SErik.Nordmark@Sun.COM */ 37511042SErik.Nordmark@Sun.COM /* ARGSUSED4 */ 37611042SErik.Nordmark@Sun.COM int 37711042SErik.Nordmark@Sun.COM ire_send_local_v6(ire_t *ire, mblk_t *mp, void *iph_arg, 37811042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa, uint32_t *identp) 37911042SErik.Nordmark@Sun.COM { 38011042SErik.Nordmark@Sun.COM ip6_t *ip6h = (ip6_t *)iph_arg; 38111042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst; 38211042SErik.Nordmark@Sun.COM ill_t *ill = ire->ire_ill; 38311042SErik.Nordmark@Sun.COM ip_recv_attr_t iras; /* NOTE: No bzero for performance */ 38411042SErik.Nordmark@Sun.COM uint_t pktlen = ixa->ixa_pktlen; 38511042SErik.Nordmark@Sun.COM 38611042SErik.Nordmark@Sun.COM /* 38711042SErik.Nordmark@Sun.COM * No fragmentation, no nce, and no application of IPsec. 38811042SErik.Nordmark@Sun.COM * 38911042SErik.Nordmark@Sun.COM * 39011042SErik.Nordmark@Sun.COM * Note different order between IP provider and FW_HOOKS than in 39111042SErik.Nordmark@Sun.COM * send_wire case. 39211042SErik.Nordmark@Sun.COM */ 39311042SErik.Nordmark@Sun.COM 39411042SErik.Nordmark@Sun.COM /* 39511042SErik.Nordmark@Sun.COM * DTrace this as ip:::send. A packet blocked by FW_HOOKS will fire the 39611042SErik.Nordmark@Sun.COM * send probe, but not the receive probe. 39711042SErik.Nordmark@Sun.COM */ 39811042SErik.Nordmark@Sun.COM DTRACE_IP7(send, mblk_t *, mp, conn_t *, NULL, void_ip_t *, 39911042SErik.Nordmark@Sun.COM ip6h, __dtrace_ipsr_ill_t *, ill, ipha_t *, NULL, ip6_t *, ip6h, 40011042SErik.Nordmark@Sun.COM int, 1); 40111042SErik.Nordmark@Sun.COM 40211042SErik.Nordmark@Sun.COM DTRACE_PROBE4(ip6__loopback__out__start, 40311042SErik.Nordmark@Sun.COM ill_t *, NULL, ill_t *, ill, 40411042SErik.Nordmark@Sun.COM ip6_t *, ip6h, mblk_t *, mp); 40511042SErik.Nordmark@Sun.COM 40611042SErik.Nordmark@Sun.COM if (HOOKS6_INTERESTED_LOOPBACK_OUT(ipst)) { 40711042SErik.Nordmark@Sun.COM int error; 40811042SErik.Nordmark@Sun.COM 40911042SErik.Nordmark@Sun.COM FW_HOOKS(ipst->ips_ip6_loopback_out_event, 41011042SErik.Nordmark@Sun.COM ipst->ips_ipv6firewall_loopback_out, 41111042SErik.Nordmark@Sun.COM NULL, ill, ip6h, mp, mp, 0, ipst, error); 41211042SErik.Nordmark@Sun.COM 41311042SErik.Nordmark@Sun.COM DTRACE_PROBE1(ip6__loopback__out__end, mblk_t *, mp); 41411042SErik.Nordmark@Sun.COM if (mp == NULL) 41511042SErik.Nordmark@Sun.COM return (error); 41611042SErik.Nordmark@Sun.COM 41711042SErik.Nordmark@Sun.COM /* 41811042SErik.Nordmark@Sun.COM * Even if the destination was changed by the filter we use the 41911042SErik.Nordmark@Sun.COM * forwarding decision that was made based on the address 42011042SErik.Nordmark@Sun.COM * in ip_output/ip_set_destination. 42111042SErik.Nordmark@Sun.COM */ 42211042SErik.Nordmark@Sun.COM /* Length could be different */ 42311042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr; 42411042SErik.Nordmark@Sun.COM pktlen = ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN; 42511042SErik.Nordmark@Sun.COM } 42611042SErik.Nordmark@Sun.COM 42711042SErik.Nordmark@Sun.COM /* 42811042SErik.Nordmark@Sun.COM * If a callback is enabled then we need to know the 42911042SErik.Nordmark@Sun.COM * source and destination zoneids for the packet. We already 43011042SErik.Nordmark@Sun.COM * have those handy. 43111042SErik.Nordmark@Sun.COM */ 43211042SErik.Nordmark@Sun.COM if (ipst->ips_ip6_observe.he_interested) { 43311042SErik.Nordmark@Sun.COM zoneid_t szone, dzone; 43411042SErik.Nordmark@Sun.COM zoneid_t stackzoneid; 43511042SErik.Nordmark@Sun.COM 43611042SErik.Nordmark@Sun.COM stackzoneid = netstackid_to_zoneid( 43711042SErik.Nordmark@Sun.COM ipst->ips_netstack->netstack_stackid); 43811042SErik.Nordmark@Sun.COM 43911042SErik.Nordmark@Sun.COM if (stackzoneid == GLOBAL_ZONEID) { 44011042SErik.Nordmark@Sun.COM /* Shared-IP zone */ 44111042SErik.Nordmark@Sun.COM dzone = ire->ire_zoneid; 44211042SErik.Nordmark@Sun.COM szone = ixa->ixa_zoneid; 44311042SErik.Nordmark@Sun.COM } else { 44411042SErik.Nordmark@Sun.COM szone = dzone = stackzoneid; 44511042SErik.Nordmark@Sun.COM } 44611042SErik.Nordmark@Sun.COM ipobs_hook(mp, IPOBS_HOOK_LOCAL, szone, dzone, ill, ipst); 44711042SErik.Nordmark@Sun.COM } 44811042SErik.Nordmark@Sun.COM 44911042SErik.Nordmark@Sun.COM /* Handle lo0 stats */ 45011042SErik.Nordmark@Sun.COM ipst->ips_loopback_packets++; 45111042SErik.Nordmark@Sun.COM 45211042SErik.Nordmark@Sun.COM /* 45311042SErik.Nordmark@Sun.COM * Update output mib stats. Note that we can't move into the icmp 45411042SErik.Nordmark@Sun.COM * sender (icmp_output etc) since they don't know the ill and the 45511042SErik.Nordmark@Sun.COM * stats are per ill. 45611042SErik.Nordmark@Sun.COM */ 45711042SErik.Nordmark@Sun.COM if (ixa->ixa_protocol == IPPROTO_ICMPV6) { 45811042SErik.Nordmark@Sun.COM icmp6_t *icmp6; 45911042SErik.Nordmark@Sun.COM 46011042SErik.Nordmark@Sun.COM icmp6 = (icmp6_t *)((uchar_t *)ip6h + ixa->ixa_ip_hdr_length); 46111042SErik.Nordmark@Sun.COM icmp_update_out_mib_v6(ill, icmp6); 46211042SErik.Nordmark@Sun.COM } 46311042SErik.Nordmark@Sun.COM 46411042SErik.Nordmark@Sun.COM DTRACE_PROBE4(ip6__loopback__in__start, 46511042SErik.Nordmark@Sun.COM ill_t *, ill, ill_t *, NULL, 46611042SErik.Nordmark@Sun.COM ip6_t *, ip6h, mblk_t *, mp); 46711042SErik.Nordmark@Sun.COM 46811042SErik.Nordmark@Sun.COM if (HOOKS6_INTERESTED_LOOPBACK_IN(ipst)) { 46911042SErik.Nordmark@Sun.COM int error; 47011042SErik.Nordmark@Sun.COM 47111042SErik.Nordmark@Sun.COM FW_HOOKS(ipst->ips_ip6_loopback_in_event, 47211042SErik.Nordmark@Sun.COM ipst->ips_ipv6firewall_loopback_in, 47311042SErik.Nordmark@Sun.COM ill, NULL, ip6h, mp, mp, 0, ipst, error); 47411042SErik.Nordmark@Sun.COM 47511042SErik.Nordmark@Sun.COM DTRACE_PROBE1(ip6__loopback__in__end, mblk_t *, mp); 47611042SErik.Nordmark@Sun.COM if (mp == NULL) 47711042SErik.Nordmark@Sun.COM return (error); 47811042SErik.Nordmark@Sun.COM 47911042SErik.Nordmark@Sun.COM /* 48011042SErik.Nordmark@Sun.COM * Even if the destination was changed by the filter we use the 48111042SErik.Nordmark@Sun.COM * forwarding decision that was made based on the address 48211042SErik.Nordmark@Sun.COM * in ip_output/ip_set_destination. 48311042SErik.Nordmark@Sun.COM */ 48411042SErik.Nordmark@Sun.COM /* Length could be different */ 48511042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr; 48611042SErik.Nordmark@Sun.COM pktlen = ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN; 48711042SErik.Nordmark@Sun.COM } 48811042SErik.Nordmark@Sun.COM 48911042SErik.Nordmark@Sun.COM DTRACE_IP7(receive, mblk_t *, mp, conn_t *, NULL, void_ip_t *, 49011042SErik.Nordmark@Sun.COM ip6h, __dtrace_ipsr_ill_t *, ill, ipha_t *, NULL, ip6_t *, ip6h, 49111042SErik.Nordmark@Sun.COM int, 1); 49211042SErik.Nordmark@Sun.COM 49311042SErik.Nordmark@Sun.COM /* Map ixa to ira including IPsec policies */ 49411042SErik.Nordmark@Sun.COM ipsec_out_to_in(ixa, ill, &iras); 49511042SErik.Nordmark@Sun.COM iras.ira_pktlen = pktlen; 49611042SErik.Nordmark@Sun.COM 49711042SErik.Nordmark@Sun.COM ire->ire_ib_pkt_count++; 49811042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCInReceives); 49911042SErik.Nordmark@Sun.COM UPDATE_MIB(ill->ill_ip_mib, ipIfStatsHCInOctets, pktlen); 50011042SErik.Nordmark@Sun.COM 50111042SErik.Nordmark@Sun.COM /* Destined to ire_zoneid - use that for fanout */ 50211042SErik.Nordmark@Sun.COM iras.ira_zoneid = ire->ire_zoneid; 50311042SErik.Nordmark@Sun.COM 50411042SErik.Nordmark@Sun.COM if (is_system_labeled()) { 50511042SErik.Nordmark@Sun.COM iras.ira_flags |= IRAF_SYSTEM_LABELED; 50611042SErik.Nordmark@Sun.COM 50711042SErik.Nordmark@Sun.COM /* 50811042SErik.Nordmark@Sun.COM * This updates ira_cred, ira_tsl and ira_free_flags based 50911042SErik.Nordmark@Sun.COM * on the label. We don't expect this to ever fail for 51011042SErik.Nordmark@Sun.COM * loopback packets, so we silently drop the packet should it 51111042SErik.Nordmark@Sun.COM * fail. 51211042SErik.Nordmark@Sun.COM */ 51311042SErik.Nordmark@Sun.COM if (!tsol_get_pkt_label(mp, IPV6_VERSION, &iras)) { 51411042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 51511042SErik.Nordmark@Sun.COM ip_drop_input("tsol_get_pkt_label", mp, ill); 51611042SErik.Nordmark@Sun.COM freemsg(mp); 51711042SErik.Nordmark@Sun.COM return (0); 51811042SErik.Nordmark@Sun.COM } 51911042SErik.Nordmark@Sun.COM ASSERT(iras.ira_tsl != NULL); 52011042SErik.Nordmark@Sun.COM 52111042SErik.Nordmark@Sun.COM /* tsol_get_pkt_label sometimes does pullupmsg */ 52211042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr; 52311042SErik.Nordmark@Sun.COM } 52411042SErik.Nordmark@Sun.COM 52511042SErik.Nordmark@Sun.COM ip_fanout_v6(mp, ip6h, &iras); 52611042SErik.Nordmark@Sun.COM 52711042SErik.Nordmark@Sun.COM /* We moved any IPsec refs from ixa to iras */ 52811042SErik.Nordmark@Sun.COM ira_cleanup(&iras, B_FALSE); 52911042SErik.Nordmark@Sun.COM return (0); 53011042SErik.Nordmark@Sun.COM } 53111042SErik.Nordmark@Sun.COM 53211042SErik.Nordmark@Sun.COM static void 53311042SErik.Nordmark@Sun.COM multirt_check_v6(ire_t *ire, ip6_t *ip6h, ip_xmit_attr_t *ixa) 53411042SErik.Nordmark@Sun.COM { 53511042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst; 53611042SErik.Nordmark@Sun.COM 53711042SErik.Nordmark@Sun.COM /* Limit the TTL on multirt packets. Do this even if IPV6_HOPLIMIT */ 53811042SErik.Nordmark@Sun.COM if (ire->ire_type & IRE_MULTICAST) { 53911042SErik.Nordmark@Sun.COM if (ip6h->ip6_hops > 1) { 54011042SErik.Nordmark@Sun.COM ip2dbg(("ire_send_multirt_v6: forcing multicast " 54111042SErik.Nordmark@Sun.COM "multirt TTL to 1 (was %d)\n", ip6h->ip6_hops)); 54211042SErik.Nordmark@Sun.COM ip6h->ip6_hops = 1; 54311042SErik.Nordmark@Sun.COM } 54411042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_NO_TTL_CHANGE; 54511042SErik.Nordmark@Sun.COM } else if ((ipst->ips_ip_multirt_ttl > 0) && 54611042SErik.Nordmark@Sun.COM (ip6h->ip6_hops > ipst->ips_ip_multirt_ttl)) { 54711042SErik.Nordmark@Sun.COM ip6h->ip6_hops = ipst->ips_ip_multirt_ttl; 54811042SErik.Nordmark@Sun.COM /* 54911042SErik.Nordmark@Sun.COM * Need to ensure we don't increase the ttl should we go through 55011042SErik.Nordmark@Sun.COM * ire_send_multicast. 55111042SErik.Nordmark@Sun.COM */ 55211042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_NO_TTL_CHANGE; 55311042SErik.Nordmark@Sun.COM } 55411042SErik.Nordmark@Sun.COM 55511042SErik.Nordmark@Sun.COM /* For IPv6 this also needs to insert a fragment header */ 55611042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_IPV6_ADD_FRAGHDR; 55711042SErik.Nordmark@Sun.COM } 55811042SErik.Nordmark@Sun.COM 55911042SErik.Nordmark@Sun.COM /* 56011042SErik.Nordmark@Sun.COM * ire_sendfn for IRE_MULTICAST 56111042SErik.Nordmark@Sun.COM * 56211042SErik.Nordmark@Sun.COM * Note that we do path MTU discovery by default for IPv6 multicast. But 56311042SErik.Nordmark@Sun.COM * since unconnected UDP and RAW sockets don't set IXAF_PMTU_DISCOVERY 56411042SErik.Nordmark@Sun.COM * only connected sockets get this by default. 56511042SErik.Nordmark@Sun.COM */ 56611042SErik.Nordmark@Sun.COM int 56711042SErik.Nordmark@Sun.COM ire_send_multicast_v6(ire_t *ire, mblk_t *mp, void *iph_arg, 56811042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa, uint32_t *identp) 56911042SErik.Nordmark@Sun.COM { 57011042SErik.Nordmark@Sun.COM ip6_t *ip6h = (ip6_t *)iph_arg; 57111042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst; 57211042SErik.Nordmark@Sun.COM ill_t *ill = ire->ire_ill; 57311042SErik.Nordmark@Sun.COM iaflags_t ixaflags = ixa->ixa_flags; 57411042SErik.Nordmark@Sun.COM 57511042SErik.Nordmark@Sun.COM /* 57611042SErik.Nordmark@Sun.COM * The IRE_MULTICAST is the same whether or not multirt is in use. 57711042SErik.Nordmark@Sun.COM * Hence we need special-case code. 57811042SErik.Nordmark@Sun.COM */ 57911042SErik.Nordmark@Sun.COM if (ixaflags & IXAF_MULTIRT_MULTICAST) 58011042SErik.Nordmark@Sun.COM multirt_check_v6(ire, ip6h, ixa); 58111042SErik.Nordmark@Sun.COM 58211042SErik.Nordmark@Sun.COM /* 58311042SErik.Nordmark@Sun.COM * Check if anything in ip_input_v6 wants a copy of the transmitted 58411042SErik.Nordmark@Sun.COM * packet (after IPsec and fragmentation) 58511042SErik.Nordmark@Sun.COM * 58611042SErik.Nordmark@Sun.COM * 1. Multicast routers always need a copy unless SO_DONTROUTE is set 58711042SErik.Nordmark@Sun.COM * RSVP and the rsvp daemon is an example of a 58811042SErik.Nordmark@Sun.COM * protocol and user level process that 58911042SErik.Nordmark@Sun.COM * handles it's own routing. Hence, it uses the 59011042SErik.Nordmark@Sun.COM * SO_DONTROUTE option to accomplish this. 59111042SErik.Nordmark@Sun.COM * 2. If the sender has set IP_MULTICAST_LOOP, then we just 59211042SErik.Nordmark@Sun.COM * check whether there are any receivers for the group on the ill 59311042SErik.Nordmark@Sun.COM * (ignoring the zoneid). 59411042SErik.Nordmark@Sun.COM * 3. If IP_MULTICAST_LOOP is not set, then we check if there are 59511042SErik.Nordmark@Sun.COM * any members in other shared-IP zones. 59611042SErik.Nordmark@Sun.COM * If such members exist, then we indicate that the sending zone 59711042SErik.Nordmark@Sun.COM * shouldn't get a loopback copy to preserve the IP_MULTICAST_LOOP 59811042SErik.Nordmark@Sun.COM * behavior. 59911042SErik.Nordmark@Sun.COM * 60011042SErik.Nordmark@Sun.COM * When we loopback we skip hardware checksum to make sure loopback 60111042SErik.Nordmark@Sun.COM * copy is checksumed. 60211042SErik.Nordmark@Sun.COM * 60311042SErik.Nordmark@Sun.COM * Note that ire_ill is the upper in the case of IPMP. 60411042SErik.Nordmark@Sun.COM */ 60511042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~(IXAF_LOOPBACK_COPY | IXAF_NO_HW_CKSUM); 60611042SErik.Nordmark@Sun.COM if (ipst->ips_ip_g_mrouter && ill->ill_mrouter_cnt > 0 && 60711042SErik.Nordmark@Sun.COM !(ixaflags & IXAF_DONTROUTE)) { 60811042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_LOOPBACK_COPY | IXAF_NO_HW_CKSUM; 60911042SErik.Nordmark@Sun.COM } else if (ixaflags & IXAF_MULTICAST_LOOP) { 61011042SErik.Nordmark@Sun.COM /* 61111042SErik.Nordmark@Sun.COM * If this zone or any other zone has members then loopback 61211042SErik.Nordmark@Sun.COM * a copy. 61311042SErik.Nordmark@Sun.COM */ 61411042SErik.Nordmark@Sun.COM if (ill_hasmembers_v6(ill, &ip6h->ip6_dst)) 61511042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_LOOPBACK_COPY | IXAF_NO_HW_CKSUM; 61611042SErik.Nordmark@Sun.COM } else if (ipst->ips_netstack->netstack_numzones > 1) { 61711042SErik.Nordmark@Sun.COM /* 61811042SErik.Nordmark@Sun.COM * This zone should not have a copy. But there are some other 61911042SErik.Nordmark@Sun.COM * zones which might have members. 62011042SErik.Nordmark@Sun.COM */ 62111042SErik.Nordmark@Sun.COM if (ill_hasmembers_otherzones_v6(ill, &ip6h->ip6_dst, 62211042SErik.Nordmark@Sun.COM ixa->ixa_zoneid)) { 62311042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_NO_LOOP_ZONEID_SET; 62411042SErik.Nordmark@Sun.COM ixa->ixa_no_loop_zoneid = ixa->ixa_zoneid; 62511042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_LOOPBACK_COPY | IXAF_NO_HW_CKSUM; 62611042SErik.Nordmark@Sun.COM } 62711042SErik.Nordmark@Sun.COM } 62811042SErik.Nordmark@Sun.COM 62911042SErik.Nordmark@Sun.COM /* 63011042SErik.Nordmark@Sun.COM * Unless IPV6_HOPLIMIT or ire_send_multirt_v6 already set a ttl, 63111042SErik.Nordmark@Sun.COM * force the ttl to the IP_MULTICAST_TTL value 63211042SErik.Nordmark@Sun.COM */ 63311042SErik.Nordmark@Sun.COM if (!(ixaflags & IXAF_NO_TTL_CHANGE)) { 63411042SErik.Nordmark@Sun.COM ip6h->ip6_hops = ixa->ixa_multicast_ttl; 63511042SErik.Nordmark@Sun.COM } 63611042SErik.Nordmark@Sun.COM 63711042SErik.Nordmark@Sun.COM return (ire_send_wire_v6(ire, mp, ip6h, ixa, identp)); 63811042SErik.Nordmark@Sun.COM } 63911042SErik.Nordmark@Sun.COM 64011042SErik.Nordmark@Sun.COM /* 64111042SErik.Nordmark@Sun.COM * ire_sendfn for IREs with RTF_MULTIRT 64211042SErik.Nordmark@Sun.COM */ 64311042SErik.Nordmark@Sun.COM int 64411042SErik.Nordmark@Sun.COM ire_send_multirt_v6(ire_t *ire, mblk_t *mp, void *iph_arg, 64511042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa, uint32_t *identp) 64611042SErik.Nordmark@Sun.COM { 64711042SErik.Nordmark@Sun.COM ip6_t *ip6h = (ip6_t *)iph_arg; 64811042SErik.Nordmark@Sun.COM 64911042SErik.Nordmark@Sun.COM multirt_check_v6(ire, ip6h, ixa); 65011042SErik.Nordmark@Sun.COM 65111042SErik.Nordmark@Sun.COM if (ire->ire_type & IRE_MULTICAST) 65211042SErik.Nordmark@Sun.COM return (ire_send_multicast_v6(ire, mp, ip6h, ixa, identp)); 65311042SErik.Nordmark@Sun.COM else 65411042SErik.Nordmark@Sun.COM return (ire_send_wire_v6(ire, mp, ip6h, ixa, identp)); 65511042SErik.Nordmark@Sun.COM } 65611042SErik.Nordmark@Sun.COM 65711042SErik.Nordmark@Sun.COM /* 65811042SErik.Nordmark@Sun.COM * ire_sendfn for IREs with RTF_REJECT/RTF_BLACKHOLE, including IRE_NOROUTE 65911042SErik.Nordmark@Sun.COM */ 66011042SErik.Nordmark@Sun.COM /* ARGSUSED4 */ 66111042SErik.Nordmark@Sun.COM int 66211042SErik.Nordmark@Sun.COM ire_send_noroute_v6(ire_t *ire, mblk_t *mp, void *iph_arg, 66311042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa, uint32_t *identp) 66411042SErik.Nordmark@Sun.COM { 66511042SErik.Nordmark@Sun.COM ip6_t *ip6h = (ip6_t *)iph_arg; 66611042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst; 66711042SErik.Nordmark@Sun.COM ill_t *ill; 66811042SErik.Nordmark@Sun.COM ip_recv_attr_t iras; 66911042SErik.Nordmark@Sun.COM boolean_t dummy; 67011042SErik.Nordmark@Sun.COM 67111042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutNoRoutes); 67211042SErik.Nordmark@Sun.COM 67311042SErik.Nordmark@Sun.COM if (ire->ire_type & IRE_NOROUTE) { 67411042SErik.Nordmark@Sun.COM /* A lack of a route as opposed to RTF_REJECT|BLACKHOLE */ 67511042SErik.Nordmark@Sun.COM ip_rts_change_v6(RTM_MISS, &ip6h->ip6_dst, 0, 0, 0, 0, 0, 0, 67611042SErik.Nordmark@Sun.COM RTA_DST, ipst); 67711042SErik.Nordmark@Sun.COM } 67811042SErik.Nordmark@Sun.COM 67911042SErik.Nordmark@Sun.COM if (ire->ire_flags & RTF_BLACKHOLE) { 68011042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutNoRoutes RTF_BLACKHOLE", mp, NULL); 68111042SErik.Nordmark@Sun.COM freemsg(mp); 68211042SErik.Nordmark@Sun.COM /* No error even for local senders - silent blackhole */ 68311042SErik.Nordmark@Sun.COM return (0); 68411042SErik.Nordmark@Sun.COM } 68511042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutNoRoutes RTF_REJECT", mp, NULL); 68611042SErik.Nordmark@Sun.COM 68711042SErik.Nordmark@Sun.COM /* 68811042SErik.Nordmark@Sun.COM * We need an ill_t for the ip_recv_attr_t even though this packet 68911042SErik.Nordmark@Sun.COM * was never received and icmp_unreachable doesn't currently use 69011042SErik.Nordmark@Sun.COM * ira_ill. 69111042SErik.Nordmark@Sun.COM */ 69211042SErik.Nordmark@Sun.COM ill = ill_lookup_on_name("lo0", B_FALSE, 69311042SErik.Nordmark@Sun.COM !(ixa->ixa_flags & IRAF_IS_IPV4), &dummy, ipst); 69411042SErik.Nordmark@Sun.COM if (ill == NULL) { 69511042SErik.Nordmark@Sun.COM freemsg(mp); 69611042SErik.Nordmark@Sun.COM return (EHOSTUNREACH); 69711042SErik.Nordmark@Sun.COM } 69811042SErik.Nordmark@Sun.COM 69911042SErik.Nordmark@Sun.COM bzero(&iras, sizeof (iras)); 70011042SErik.Nordmark@Sun.COM /* Map ixa to ira including IPsec policies */ 70111042SErik.Nordmark@Sun.COM ipsec_out_to_in(ixa, ill, &iras); 70211042SErik.Nordmark@Sun.COM 70311042SErik.Nordmark@Sun.COM icmp_unreachable_v6(mp, ICMP6_DST_UNREACH_NOROUTE, B_FALSE, &iras); 70411042SErik.Nordmark@Sun.COM /* We moved any IPsec refs from ixa to iras */ 70511042SErik.Nordmark@Sun.COM ira_cleanup(&iras, B_FALSE); 70611042SErik.Nordmark@Sun.COM 70711042SErik.Nordmark@Sun.COM ill_refrele(ill); 70811042SErik.Nordmark@Sun.COM return (EHOSTUNREACH); 70911042SErik.Nordmark@Sun.COM } 71011042SErik.Nordmark@Sun.COM 71111042SErik.Nordmark@Sun.COM /* 71211042SErik.Nordmark@Sun.COM * Calculate a checksum ignoring any hardware capabilities 71311042SErik.Nordmark@Sun.COM * 71411042SErik.Nordmark@Sun.COM * Returns B_FALSE if the packet was too short for the checksum. Caller 71511042SErik.Nordmark@Sun.COM * should free and do stats. 71611042SErik.Nordmark@Sun.COM */ 71711042SErik.Nordmark@Sun.COM static boolean_t 71811042SErik.Nordmark@Sun.COM ip_output_sw_cksum_v6(mblk_t *mp, ip6_t *ip6h, ip_xmit_attr_t *ixa) 71911042SErik.Nordmark@Sun.COM { 72011042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst; 72111042SErik.Nordmark@Sun.COM uint_t pktlen = ixa->ixa_pktlen; 72211042SErik.Nordmark@Sun.COM uint16_t *cksump; 72311042SErik.Nordmark@Sun.COM uint32_t cksum; 72411042SErik.Nordmark@Sun.COM uint8_t protocol = ixa->ixa_protocol; 72511042SErik.Nordmark@Sun.COM uint16_t ip_hdr_length = ixa->ixa_ip_hdr_length; 72611042SErik.Nordmark@Sun.COM 72711042SErik.Nordmark@Sun.COM #define iphs ((uint16_t *)ip6h) 72811042SErik.Nordmark@Sun.COM 72911042SErik.Nordmark@Sun.COM /* Just in case it contained garbage */ 73011042SErik.Nordmark@Sun.COM DB_CKSUMFLAGS(mp) &= ~HCK_FLAGS; 73111042SErik.Nordmark@Sun.COM 73211042SErik.Nordmark@Sun.COM /* 73311042SErik.Nordmark@Sun.COM * Calculate ULP checksum 73411042SErik.Nordmark@Sun.COM */ 73511042SErik.Nordmark@Sun.COM if (protocol == IPPROTO_TCP) { 73611042SErik.Nordmark@Sun.COM cksump = IPH_TCPH_CHECKSUMP(ip6h, ip_hdr_length); 73711042SErik.Nordmark@Sun.COM cksum = IP_TCP_CSUM_COMP; 73811042SErik.Nordmark@Sun.COM } else if (protocol == IPPROTO_UDP) { 73911042SErik.Nordmark@Sun.COM cksump = IPH_UDPH_CHECKSUMP(ip6h, ip_hdr_length); 74011042SErik.Nordmark@Sun.COM cksum = IP_UDP_CSUM_COMP; 74111042SErik.Nordmark@Sun.COM } else if (protocol == IPPROTO_SCTP) { 74211042SErik.Nordmark@Sun.COM sctp_hdr_t *sctph; 74311042SErik.Nordmark@Sun.COM 74411042SErik.Nordmark@Sun.COM ASSERT(MBLKL(mp) >= (ip_hdr_length + sizeof (*sctph))); 74511042SErik.Nordmark@Sun.COM sctph = (sctp_hdr_t *)(mp->b_rptr + ip_hdr_length); 74611042SErik.Nordmark@Sun.COM /* 74711042SErik.Nordmark@Sun.COM * Zero out the checksum field to ensure proper 74811042SErik.Nordmark@Sun.COM * checksum calculation. 74911042SErik.Nordmark@Sun.COM */ 75011042SErik.Nordmark@Sun.COM sctph->sh_chksum = 0; 75111042SErik.Nordmark@Sun.COM #ifdef DEBUG 75211042SErik.Nordmark@Sun.COM if (!skip_sctp_cksum) 75311042SErik.Nordmark@Sun.COM #endif 75411042SErik.Nordmark@Sun.COM sctph->sh_chksum = sctp_cksum(mp, ip_hdr_length); 75511042SErik.Nordmark@Sun.COM return (B_TRUE); 75611042SErik.Nordmark@Sun.COM } else if (ixa->ixa_flags & IXAF_SET_RAW_CKSUM) { 75711042SErik.Nordmark@Sun.COM /* 75811042SErik.Nordmark@Sun.COM * icmp has placed length and routing 75911042SErik.Nordmark@Sun.COM * header adjustment in the checksum field. 76011042SErik.Nordmark@Sun.COM */ 76111042SErik.Nordmark@Sun.COM cksump = (uint16_t *)(((uint8_t *)ip6h) + ip_hdr_length + 76211042SErik.Nordmark@Sun.COM ixa->ixa_raw_cksum_offset); 76311042SErik.Nordmark@Sun.COM cksum = htons(protocol); 76411042SErik.Nordmark@Sun.COM } else if (protocol == IPPROTO_ICMPV6) { 76511042SErik.Nordmark@Sun.COM cksump = IPH_ICMPV6_CHECKSUMP(ip6h, ip_hdr_length); 76611042SErik.Nordmark@Sun.COM cksum = IP_ICMPV6_CSUM_COMP; /* Pseudo-header cksum */ 76711042SErik.Nordmark@Sun.COM } else { 76811042SErik.Nordmark@Sun.COM return (B_TRUE); 76911042SErik.Nordmark@Sun.COM } 77011042SErik.Nordmark@Sun.COM 77111042SErik.Nordmark@Sun.COM /* ULP puts the checksum field is in the first mblk */ 77211042SErik.Nordmark@Sun.COM ASSERT(((uchar_t *)cksump) + sizeof (uint16_t) <= mp->b_wptr); 77311042SErik.Nordmark@Sun.COM 77411042SErik.Nordmark@Sun.COM /* 77511042SErik.Nordmark@Sun.COM * We accumulate the pseudo header checksum in cksum. 77611042SErik.Nordmark@Sun.COM * This is pretty hairy code, so watch close. One 77711042SErik.Nordmark@Sun.COM * thing to keep in mind is that UDP and TCP have 77811042SErik.Nordmark@Sun.COM * stored their respective datagram lengths in their 77911042SErik.Nordmark@Sun.COM * checksum fields. This lines things up real nice. 78011042SErik.Nordmark@Sun.COM */ 78111042SErik.Nordmark@Sun.COM cksum += iphs[4] + iphs[5] + iphs[6] + iphs[7] + 78211042SErik.Nordmark@Sun.COM iphs[8] + iphs[9] + iphs[10] + iphs[11] + 78311042SErik.Nordmark@Sun.COM iphs[12] + iphs[13] + iphs[14] + iphs[15] + 78411042SErik.Nordmark@Sun.COM iphs[16] + iphs[17] + iphs[18] + iphs[19]; 78511042SErik.Nordmark@Sun.COM cksum = IP_CSUM(mp, ip_hdr_length, cksum); 78611042SErik.Nordmark@Sun.COM 78711042SErik.Nordmark@Sun.COM /* 78811042SErik.Nordmark@Sun.COM * For UDP/IPv6 a zero UDP checksum is not allowed. 78911042SErik.Nordmark@Sun.COM * Change to 0xffff 79011042SErik.Nordmark@Sun.COM */ 79111042SErik.Nordmark@Sun.COM if (protocol == IPPROTO_UDP && cksum == 0) 79211042SErik.Nordmark@Sun.COM *cksump = ~cksum; 79311042SErik.Nordmark@Sun.COM else 79411042SErik.Nordmark@Sun.COM *cksump = cksum; 79511042SErik.Nordmark@Sun.COM 79611042SErik.Nordmark@Sun.COM IP6_STAT(ipst, ip6_out_sw_cksum); 79711042SErik.Nordmark@Sun.COM IP6_STAT_UPDATE(ipst, ip6_out_sw_cksum_bytes, pktlen); 79811042SErik.Nordmark@Sun.COM 79911042SErik.Nordmark@Sun.COM /* No IP header checksum for IPv6 */ 80011042SErik.Nordmark@Sun.COM 80111042SErik.Nordmark@Sun.COM return (B_TRUE); 80211042SErik.Nordmark@Sun.COM #undef iphs 80311042SErik.Nordmark@Sun.COM } 80411042SErik.Nordmark@Sun.COM 80511042SErik.Nordmark@Sun.COM /* There are drivers that can't do partial checksum for ICMPv6 */ 80611042SErik.Nordmark@Sun.COM int nxge_cksum_workaround = 1; 80711042SErik.Nordmark@Sun.COM 80811042SErik.Nordmark@Sun.COM /* 80911042SErik.Nordmark@Sun.COM * Calculate the ULP checksum - try to use hardware. 81011042SErik.Nordmark@Sun.COM * In the case of MULTIRT or multicast the 81111042SErik.Nordmark@Sun.COM * IXAF_NO_HW_CKSUM is set in which case we use software. 81211042SErik.Nordmark@Sun.COM * 81311042SErik.Nordmark@Sun.COM * Returns B_FALSE if the packet was too short for the checksum. Caller 81411042SErik.Nordmark@Sun.COM * should free and do stats. 81511042SErik.Nordmark@Sun.COM */ 81611042SErik.Nordmark@Sun.COM static boolean_t 81711042SErik.Nordmark@Sun.COM ip_output_cksum_v6(iaflags_t ixaflags, mblk_t *mp, ip6_t *ip6h, 81811042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa, ill_t *ill) 81911042SErik.Nordmark@Sun.COM { 82011042SErik.Nordmark@Sun.COM uint_t pktlen = ixa->ixa_pktlen; 82111042SErik.Nordmark@Sun.COM uint16_t *cksump; 82211042SErik.Nordmark@Sun.COM uint16_t hck_flags; 82311042SErik.Nordmark@Sun.COM uint32_t cksum; 82411042SErik.Nordmark@Sun.COM uint8_t protocol = ixa->ixa_protocol; 82511042SErik.Nordmark@Sun.COM uint16_t ip_hdr_length = ixa->ixa_ip_hdr_length; 82611042SErik.Nordmark@Sun.COM 82711042SErik.Nordmark@Sun.COM #define iphs ((uint16_t *)ip6h) 82811042SErik.Nordmark@Sun.COM 82911042SErik.Nordmark@Sun.COM if ((ixaflags & IXAF_NO_HW_CKSUM) || !ILL_HCKSUM_CAPABLE(ill) || 83011042SErik.Nordmark@Sun.COM !dohwcksum) { 83111042SErik.Nordmark@Sun.COM return (ip_output_sw_cksum_v6(mp, ip6h, ixa)); 83211042SErik.Nordmark@Sun.COM } 83311042SErik.Nordmark@Sun.COM 83411042SErik.Nordmark@Sun.COM /* 83511042SErik.Nordmark@Sun.COM * Calculate ULP checksum. Note that we don't use cksump and cksum 83611042SErik.Nordmark@Sun.COM * if the ill has FULL support. 83711042SErik.Nordmark@Sun.COM */ 83811042SErik.Nordmark@Sun.COM if (protocol == IPPROTO_TCP) { 83911042SErik.Nordmark@Sun.COM cksump = IPH_TCPH_CHECKSUMP(ip6h, ip_hdr_length); 84011042SErik.Nordmark@Sun.COM cksum = IP_TCP_CSUM_COMP; /* Pseudo-header cksum */ 84111042SErik.Nordmark@Sun.COM } else if (protocol == IPPROTO_UDP) { 84211042SErik.Nordmark@Sun.COM cksump = IPH_UDPH_CHECKSUMP(ip6h, ip_hdr_length); 84311042SErik.Nordmark@Sun.COM cksum = IP_UDP_CSUM_COMP; /* Pseudo-header cksum */ 84411042SErik.Nordmark@Sun.COM } else if (protocol == IPPROTO_SCTP) { 84511042SErik.Nordmark@Sun.COM sctp_hdr_t *sctph; 84611042SErik.Nordmark@Sun.COM 84711042SErik.Nordmark@Sun.COM ASSERT(MBLKL(mp) >= (ip_hdr_length + sizeof (*sctph))); 84811042SErik.Nordmark@Sun.COM sctph = (sctp_hdr_t *)(mp->b_rptr + ip_hdr_length); 84911042SErik.Nordmark@Sun.COM /* 85011042SErik.Nordmark@Sun.COM * Zero out the checksum field to ensure proper 85111042SErik.Nordmark@Sun.COM * checksum calculation. 85211042SErik.Nordmark@Sun.COM */ 85311042SErik.Nordmark@Sun.COM sctph->sh_chksum = 0; 85411042SErik.Nordmark@Sun.COM #ifdef DEBUG 85511042SErik.Nordmark@Sun.COM if (!skip_sctp_cksum) 85611042SErik.Nordmark@Sun.COM #endif 85711042SErik.Nordmark@Sun.COM sctph->sh_chksum = sctp_cksum(mp, ip_hdr_length); 85811042SErik.Nordmark@Sun.COM goto ip_hdr_cksum; 85911042SErik.Nordmark@Sun.COM } else if (ixa->ixa_flags & IXAF_SET_RAW_CKSUM) { 86011042SErik.Nordmark@Sun.COM /* 86111042SErik.Nordmark@Sun.COM * icmp has placed length and routing 86211042SErik.Nordmark@Sun.COM * header adjustment in the checksum field. 86311042SErik.Nordmark@Sun.COM */ 86411042SErik.Nordmark@Sun.COM cksump = (uint16_t *)(((uint8_t *)ip6h) + ip_hdr_length + 86511042SErik.Nordmark@Sun.COM ixa->ixa_raw_cksum_offset); 86611042SErik.Nordmark@Sun.COM cksum = htons(protocol); 86711042SErik.Nordmark@Sun.COM } else if (protocol == IPPROTO_ICMPV6) { 86811042SErik.Nordmark@Sun.COM cksump = IPH_ICMPV6_CHECKSUMP(ip6h, ip_hdr_length); 86911042SErik.Nordmark@Sun.COM cksum = IP_ICMPV6_CSUM_COMP; /* Pseudo-header cksum */ 87011042SErik.Nordmark@Sun.COM } else { 87111042SErik.Nordmark@Sun.COM ip_hdr_cksum: 87211042SErik.Nordmark@Sun.COM /* No IP header checksum for IPv6 */ 87311042SErik.Nordmark@Sun.COM return (B_TRUE); 87411042SErik.Nordmark@Sun.COM } 87511042SErik.Nordmark@Sun.COM 87611042SErik.Nordmark@Sun.COM /* ULP puts the checksum field is in the first mblk */ 87711042SErik.Nordmark@Sun.COM ASSERT(((uchar_t *)cksump) + sizeof (uint16_t) <= mp->b_wptr); 87811042SErik.Nordmark@Sun.COM 87911042SErik.Nordmark@Sun.COM /* 88011042SErik.Nordmark@Sun.COM * Underlying interface supports hardware checksum offload for 88111042SErik.Nordmark@Sun.COM * the payload; leave the payload checksum for the hardware to 88211042SErik.Nordmark@Sun.COM * calculate. N.B: We only need to set up checksum info on the 88311042SErik.Nordmark@Sun.COM * first mblk. 88411042SErik.Nordmark@Sun.COM */ 88511042SErik.Nordmark@Sun.COM hck_flags = ill->ill_hcksum_capab->ill_hcksum_txflags; 88611042SErik.Nordmark@Sun.COM 88711042SErik.Nordmark@Sun.COM DB_CKSUMFLAGS(mp) &= ~HCK_FLAGS; 88811042SErik.Nordmark@Sun.COM if (hck_flags & HCKSUM_INET_FULL_V6) { 88911042SErik.Nordmark@Sun.COM /* 89011042SErik.Nordmark@Sun.COM * Hardware calculates pseudo-header, header and the 89111042SErik.Nordmark@Sun.COM * payload checksums, so clear the checksum field in 89211042SErik.Nordmark@Sun.COM * the protocol header. 89311042SErik.Nordmark@Sun.COM */ 89411042SErik.Nordmark@Sun.COM *cksump = 0; 89511042SErik.Nordmark@Sun.COM DB_CKSUMFLAGS(mp) |= HCK_FULLCKSUM; 89611042SErik.Nordmark@Sun.COM return (B_TRUE); 89711042SErik.Nordmark@Sun.COM } 89811042SErik.Nordmark@Sun.COM if (((hck_flags) & HCKSUM_INET_PARTIAL) && 89911042SErik.Nordmark@Sun.COM (protocol != IPPROTO_ICMPV6 || !nxge_cksum_workaround)) { 90011042SErik.Nordmark@Sun.COM /* 90111042SErik.Nordmark@Sun.COM * Partial checksum offload has been enabled. Fill 90211042SErik.Nordmark@Sun.COM * the checksum field in the protocol header with the 90311042SErik.Nordmark@Sun.COM * pseudo-header checksum value. 90411042SErik.Nordmark@Sun.COM * 90511042SErik.Nordmark@Sun.COM * We accumulate the pseudo header checksum in cksum. 90611042SErik.Nordmark@Sun.COM * This is pretty hairy code, so watch close. One 90711042SErik.Nordmark@Sun.COM * thing to keep in mind is that UDP and TCP have 90811042SErik.Nordmark@Sun.COM * stored their respective datagram lengths in their 90911042SErik.Nordmark@Sun.COM * checksum fields. This lines things up real nice. 91011042SErik.Nordmark@Sun.COM */ 91111042SErik.Nordmark@Sun.COM cksum += iphs[4] + iphs[5] + iphs[6] + iphs[7] + 91211042SErik.Nordmark@Sun.COM iphs[8] + iphs[9] + iphs[10] + iphs[11] + 91311042SErik.Nordmark@Sun.COM iphs[12] + iphs[13] + iphs[14] + iphs[15] + 91411042SErik.Nordmark@Sun.COM iphs[16] + iphs[17] + iphs[18] + iphs[19]; 91511042SErik.Nordmark@Sun.COM cksum += *(cksump); 91611042SErik.Nordmark@Sun.COM cksum = (cksum & 0xFFFF) + (cksum >> 16); 91711042SErik.Nordmark@Sun.COM *(cksump) = (cksum & 0xFFFF) + (cksum >> 16); 91811042SErik.Nordmark@Sun.COM 91911042SErik.Nordmark@Sun.COM /* 92011042SErik.Nordmark@Sun.COM * Offsets are relative to beginning of IP header. 92111042SErik.Nordmark@Sun.COM */ 92211042SErik.Nordmark@Sun.COM DB_CKSUMSTART(mp) = ip_hdr_length; 92311042SErik.Nordmark@Sun.COM DB_CKSUMSTUFF(mp) = (uint8_t *)cksump - (uint8_t *)ip6h; 92411042SErik.Nordmark@Sun.COM DB_CKSUMEND(mp) = pktlen; 92511042SErik.Nordmark@Sun.COM DB_CKSUMFLAGS(mp) |= HCK_PARTIALCKSUM; 92611042SErik.Nordmark@Sun.COM return (B_TRUE); 92711042SErik.Nordmark@Sun.COM } 92811042SErik.Nordmark@Sun.COM /* Hardware capabilities include neither full nor partial IPv6 */ 92911042SErik.Nordmark@Sun.COM return (ip_output_sw_cksum_v6(mp, ip6h, ixa)); 93011042SErik.Nordmark@Sun.COM #undef iphs 93111042SErik.Nordmark@Sun.COM } 93211042SErik.Nordmark@Sun.COM 93311042SErik.Nordmark@Sun.COM /* 93411042SErik.Nordmark@Sun.COM * ire_sendfn for offlink and onlink destinations. 93511042SErik.Nordmark@Sun.COM * Also called from the multicast, and multirt send functions. 93611042SErik.Nordmark@Sun.COM * 93711042SErik.Nordmark@Sun.COM * Assumes that the caller has a hold on the ire. 93811042SErik.Nordmark@Sun.COM * 93911042SErik.Nordmark@Sun.COM * This function doesn't care if the IRE just became condemned since that 94011042SErik.Nordmark@Sun.COM * can happen at any time. 94111042SErik.Nordmark@Sun.COM */ 94211042SErik.Nordmark@Sun.COM /* ARGSUSED */ 94311042SErik.Nordmark@Sun.COM int 94411042SErik.Nordmark@Sun.COM ire_send_wire_v6(ire_t *ire, mblk_t *mp, void *iph_arg, 94511042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa, uint32_t *identp) 94611042SErik.Nordmark@Sun.COM { 94711042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst; 94811042SErik.Nordmark@Sun.COM ip6_t *ip6h = (ip6_t *)iph_arg; 94911042SErik.Nordmark@Sun.COM iaflags_t ixaflags = ixa->ixa_flags; 95011042SErik.Nordmark@Sun.COM ill_t *ill; 95111042SErik.Nordmark@Sun.COM uint32_t pktlen = ixa->ixa_pktlen; 95211042SErik.Nordmark@Sun.COM 95311042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_nce != NULL); 95411042SErik.Nordmark@Sun.COM ill = ixa->ixa_nce->nce_ill; 95511042SErik.Nordmark@Sun.COM 95611042SErik.Nordmark@Sun.COM /* 95711042SErik.Nordmark@Sun.COM * Update output mib stats. Note that we can't move into the icmp 95811042SErik.Nordmark@Sun.COM * sender (icmp_output etc) since they don't know the ill and the 95911042SErik.Nordmark@Sun.COM * stats are per ill. 96011042SErik.Nordmark@Sun.COM * 96111042SErik.Nordmark@Sun.COM * With IPMP we record the stats on the upper ill. 96211042SErik.Nordmark@Sun.COM */ 96311042SErik.Nordmark@Sun.COM if (ixa->ixa_protocol == IPPROTO_ICMPV6) { 96411042SErik.Nordmark@Sun.COM icmp6_t *icmp6; 96511042SErik.Nordmark@Sun.COM 96611042SErik.Nordmark@Sun.COM icmp6 = (icmp6_t *)((uchar_t *)ip6h + ixa->ixa_ip_hdr_length); 96711042SErik.Nordmark@Sun.COM icmp_update_out_mib_v6(ixa->ixa_nce->nce_common->ncec_ill, 96811042SErik.Nordmark@Sun.COM icmp6); 96911042SErik.Nordmark@Sun.COM } 97011042SErik.Nordmark@Sun.COM 97111042SErik.Nordmark@Sun.COM if (ixaflags & IXAF_DONTROUTE) 97211042SErik.Nordmark@Sun.COM ip6h->ip6_hops = 1; 97311042SErik.Nordmark@Sun.COM 97411042SErik.Nordmark@Sun.COM /* 97511042SErik.Nordmark@Sun.COM * This might set b_band, thus the IPsec and fragmentation 97611042SErik.Nordmark@Sun.COM * code in IP ensures that b_band is updated in the first mblk. 97711042SErik.Nordmark@Sun.COM */ 97811042SErik.Nordmark@Sun.COM if (IPP_ENABLED(IPP_LOCAL_OUT, ipst)) { 97911042SErik.Nordmark@Sun.COM /* ip_process translates an IS_UNDER_IPMP */ 98011042SErik.Nordmark@Sun.COM mp = ip_process(IPP_LOCAL_OUT, mp, ill, ill); 98111042SErik.Nordmark@Sun.COM if (mp == NULL) { 98211042SErik.Nordmark@Sun.COM /* ip_drop_packet and MIB done */ 98311042SErik.Nordmark@Sun.COM return (0); /* Might just be delayed */ 98411042SErik.Nordmark@Sun.COM } 98511042SErik.Nordmark@Sun.COM } 98611042SErik.Nordmark@Sun.COM 98711042SErik.Nordmark@Sun.COM /* 98811042SErik.Nordmark@Sun.COM * To handle IPsec/iptun's labeling needs we need to tag packets 98911042SErik.Nordmark@Sun.COM * while we still have ixa_tsl 99011042SErik.Nordmark@Sun.COM */ 99111042SErik.Nordmark@Sun.COM if (is_system_labeled() && ixa->ixa_tsl != NULL && 99211042SErik.Nordmark@Sun.COM (ill->ill_mactype == DL_6TO4 || ill->ill_mactype == DL_IPV4 || 99311042SErik.Nordmark@Sun.COM ill->ill_mactype == DL_IPV6)) { 99411042SErik.Nordmark@Sun.COM cred_t *newcr; 99511042SErik.Nordmark@Sun.COM 99611042SErik.Nordmark@Sun.COM newcr = copycred_from_tslabel(ixa->ixa_cred, ixa->ixa_tsl, 99711042SErik.Nordmark@Sun.COM KM_NOSLEEP); 99811042SErik.Nordmark@Sun.COM if (newcr == NULL) { 99911042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards); 100011042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards - newcr", 100111042SErik.Nordmark@Sun.COM mp, ill); 100211042SErik.Nordmark@Sun.COM freemsg(mp); 100311042SErik.Nordmark@Sun.COM return (ENOBUFS); 100411042SErik.Nordmark@Sun.COM } 100511042SErik.Nordmark@Sun.COM mblk_setcred(mp, newcr, NOPID); 100611042SErik.Nordmark@Sun.COM crfree(newcr); /* mblk_setcred did its own crhold */ 100711042SErik.Nordmark@Sun.COM } 100811042SErik.Nordmark@Sun.COM 100911042SErik.Nordmark@Sun.COM /* 101011042SErik.Nordmark@Sun.COM * IXAF_IPV6_ADD_FRAGHDR is set for CGTP so that we will add a 101111042SErik.Nordmark@Sun.COM * fragment header without fragmenting. CGTP on the receiver will 101211042SErik.Nordmark@Sun.COM * filter duplicates on the ident field. 101311042SErik.Nordmark@Sun.COM */ 101411042SErik.Nordmark@Sun.COM if (pktlen > ixa->ixa_fragsize || 101511042SErik.Nordmark@Sun.COM (ixaflags & (IXAF_IPSEC_SECURE|IXAF_IPV6_ADD_FRAGHDR))) { 101611042SErik.Nordmark@Sun.COM uint32_t ident; 101711042SErik.Nordmark@Sun.COM 101811042SErik.Nordmark@Sun.COM if (ixaflags & IXAF_IPSEC_SECURE) 101911042SErik.Nordmark@Sun.COM pktlen += ipsec_out_extra_length(ixa); 102011042SErik.Nordmark@Sun.COM 102111042SErik.Nordmark@Sun.COM if (pktlen > IP_MAXPACKET) 102211042SErik.Nordmark@Sun.COM return (EMSGSIZE); 102311042SErik.Nordmark@Sun.COM 102411042SErik.Nordmark@Sun.COM if (ixaflags & IXAF_SET_ULP_CKSUM) { 102511042SErik.Nordmark@Sun.COM /* 102611042SErik.Nordmark@Sun.COM * Compute ULP checksum using software 102711042SErik.Nordmark@Sun.COM */ 102811042SErik.Nordmark@Sun.COM if (!ip_output_sw_cksum_v6(mp, ip6h, ixa)) { 102911042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards); 103011042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards", mp, ill); 103111042SErik.Nordmark@Sun.COM freemsg(mp); 103211042SErik.Nordmark@Sun.COM return (EINVAL); 103311042SErik.Nordmark@Sun.COM } 103411042SErik.Nordmark@Sun.COM /* Avoid checksum again below if we only add fraghdr */ 103511042SErik.Nordmark@Sun.COM ixaflags &= ~IXAF_SET_ULP_CKSUM; 103611042SErik.Nordmark@Sun.COM } 103711042SErik.Nordmark@Sun.COM 103811042SErik.Nordmark@Sun.COM /* 103911042SErik.Nordmark@Sun.COM * If we need a fragment header, pick the ident and insert 104011042SErik.Nordmark@Sun.COM * the header before IPsec to we have a place to store 104111042SErik.Nordmark@Sun.COM * the ident value. 104211042SErik.Nordmark@Sun.COM */ 104311042SErik.Nordmark@Sun.COM if ((ixaflags & IXAF_IPV6_ADD_FRAGHDR) || 104411042SErik.Nordmark@Sun.COM pktlen > ixa->ixa_fragsize) { 104511042SErik.Nordmark@Sun.COM /* 104611042SErik.Nordmark@Sun.COM * If this packet would generate a icmp_frag_needed 104711042SErik.Nordmark@Sun.COM * message, we need to handle it before we do the IPsec 104811042SErik.Nordmark@Sun.COM * processing. Otherwise, we need to strip the IPsec 104911042SErik.Nordmark@Sun.COM * headers before we send up the message to the ULPs 105011042SErik.Nordmark@Sun.COM * which becomes messy and difficult. 105111042SErik.Nordmark@Sun.COM */ 105211042SErik.Nordmark@Sun.COM if ((pktlen > ixa->ixa_fragsize) && 105311042SErik.Nordmark@Sun.COM (ixaflags & IXAF_DONTFRAG)) { 105411042SErik.Nordmark@Sun.COM /* Generate ICMP and return error */ 105511042SErik.Nordmark@Sun.COM ip_recv_attr_t iras; 105611042SErik.Nordmark@Sun.COM 105711042SErik.Nordmark@Sun.COM DTRACE_PROBE4(ip6__fragsize__fail, 105811042SErik.Nordmark@Sun.COM uint_t, pktlen, uint_t, ixa->ixa_fragsize, 105911042SErik.Nordmark@Sun.COM uint_t, ixa->ixa_pktlen, 106011042SErik.Nordmark@Sun.COM uint_t, ixa->ixa_pmtu); 106111042SErik.Nordmark@Sun.COM 106211042SErik.Nordmark@Sun.COM bzero(&iras, sizeof (iras)); 106311042SErik.Nordmark@Sun.COM /* Map ixa to ira including IPsec policies */ 106411042SErik.Nordmark@Sun.COM ipsec_out_to_in(ixa, ill, &iras); 106511042SErik.Nordmark@Sun.COM 106611042SErik.Nordmark@Sun.COM ip_drop_output("ICMP6_PKT_TOO_BIG", mp, ill); 106711042SErik.Nordmark@Sun.COM icmp_pkt2big_v6(mp, ixa->ixa_fragsize, B_TRUE, 106811042SErik.Nordmark@Sun.COM &iras); 106911042SErik.Nordmark@Sun.COM /* We moved any IPsec refs from ixa to iras */ 107011042SErik.Nordmark@Sun.COM ira_cleanup(&iras, B_FALSE); 107111042SErik.Nordmark@Sun.COM return (EMSGSIZE); 107211042SErik.Nordmark@Sun.COM } 107311042SErik.Nordmark@Sun.COM DTRACE_PROBE4(ip6__fragsize__ok, uint_t, pktlen, 107411042SErik.Nordmark@Sun.COM uint_t, ixa->ixa_fragsize, uint_t, ixa->ixa_pktlen, 107511042SErik.Nordmark@Sun.COM uint_t, ixa->ixa_pmtu); 107611042SErik.Nordmark@Sun.COM /* 107711042SErik.Nordmark@Sun.COM * Assign an ident value for this packet. There could 107811042SErik.Nordmark@Sun.COM * be other threads targeting the same destination, so 107911042SErik.Nordmark@Sun.COM * we have to arrange for a atomic increment. 108011042SErik.Nordmark@Sun.COM * Normally ixa_extra_ident is 0, but in the case of 108111042SErik.Nordmark@Sun.COM * LSO it will be the number of TCP segments that the 108211042SErik.Nordmark@Sun.COM * driver/hardware will extraly construct. 108311042SErik.Nordmark@Sun.COM * 108411042SErik.Nordmark@Sun.COM * Note that cl_inet_ipident has only been used for 108511042SErik.Nordmark@Sun.COM * IPv4. We don't use it here. 108611042SErik.Nordmark@Sun.COM */ 108711042SErik.Nordmark@Sun.COM ident = atomic_add_32_nv(identp, ixa->ixa_extra_ident + 108811042SErik.Nordmark@Sun.COM 1); 108911042SErik.Nordmark@Sun.COM ixa->ixa_ident = ident; /* In case we do IPsec */ 109011042SErik.Nordmark@Sun.COM } 109111042SErik.Nordmark@Sun.COM if (ixaflags & IXAF_IPSEC_SECURE) { 109211042SErik.Nordmark@Sun.COM /* 109311042SErik.Nordmark@Sun.COM * Pass in sufficient information so that 109411042SErik.Nordmark@Sun.COM * IPsec can determine whether to fragment, and 109511042SErik.Nordmark@Sun.COM * which function to call after fragmentation. 109611042SErik.Nordmark@Sun.COM */ 109711042SErik.Nordmark@Sun.COM return (ipsec_out_process(mp, ixa)); 109811042SErik.Nordmark@Sun.COM } 109911042SErik.Nordmark@Sun.COM 110011042SErik.Nordmark@Sun.COM mp = ip_fraghdr_add_v6(mp, ident, ixa); 110111042SErik.Nordmark@Sun.COM if (mp == NULL) { 110211042SErik.Nordmark@Sun.COM /* MIB and ip_drop_output already done */ 110311042SErik.Nordmark@Sun.COM return (ENOMEM); 110411042SErik.Nordmark@Sun.COM } 110511042SErik.Nordmark@Sun.COM ASSERT(pktlen == ixa->ixa_pktlen); 110611042SErik.Nordmark@Sun.COM pktlen += sizeof (ip6_frag_t); 110711042SErik.Nordmark@Sun.COM 110811042SErik.Nordmark@Sun.COM if (pktlen > ixa->ixa_fragsize) { 110911042SErik.Nordmark@Sun.COM return (ip_fragment_v6(mp, ixa->ixa_nce, ixaflags, 111011042SErik.Nordmark@Sun.COM pktlen, ixa->ixa_fragsize, 111111042SErik.Nordmark@Sun.COM ixa->ixa_xmit_hint, ixa->ixa_zoneid, 111211042SErik.Nordmark@Sun.COM ixa->ixa_no_loop_zoneid, ixa->ixa_postfragfn, 111311042SErik.Nordmark@Sun.COM &ixa->ixa_cookie)); 111411042SErik.Nordmark@Sun.COM } 111511042SErik.Nordmark@Sun.COM } 111611042SErik.Nordmark@Sun.COM if (ixaflags & IXAF_SET_ULP_CKSUM) { 111711042SErik.Nordmark@Sun.COM /* Compute ULP checksum and IP header checksum */ 111811042SErik.Nordmark@Sun.COM /* An IS_UNDER_IPMP ill is ok here */ 111911042SErik.Nordmark@Sun.COM if (!ip_output_cksum_v6(ixaflags, mp, ip6h, ixa, ill)) { 112011042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards); 112111042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards", mp, ill); 112211042SErik.Nordmark@Sun.COM freemsg(mp); 112311042SErik.Nordmark@Sun.COM return (EINVAL); 112411042SErik.Nordmark@Sun.COM } 112511042SErik.Nordmark@Sun.COM } 112611042SErik.Nordmark@Sun.COM return ((ixa->ixa_postfragfn)(mp, ixa->ixa_nce, ixaflags, 112711042SErik.Nordmark@Sun.COM pktlen, ixa->ixa_xmit_hint, ixa->ixa_zoneid, 112811042SErik.Nordmark@Sun.COM ixa->ixa_no_loop_zoneid, &ixa->ixa_cookie)); 112911042SErik.Nordmark@Sun.COM } 113011042SErik.Nordmark@Sun.COM 113111042SErik.Nordmark@Sun.COM /* 113211042SErik.Nordmark@Sun.COM * Post fragmentation function for RTF_MULTIRT routes. 113311042SErik.Nordmark@Sun.COM * Since IRE_MULTICASTs might have RTF_MULTIRT, this function 113411042SErik.Nordmark@Sun.COM * checks IXAF_LOOPBACK_COPY. 113511042SErik.Nordmark@Sun.COM * 113611042SErik.Nordmark@Sun.COM * If no packet is sent due to failures then we return an errno, but if at 113711042SErik.Nordmark@Sun.COM * least one succeeded we return zero. 113811042SErik.Nordmark@Sun.COM */ 113911042SErik.Nordmark@Sun.COM int 114011042SErik.Nordmark@Sun.COM ip_postfrag_multirt_v6(mblk_t *mp, nce_t *nce, iaflags_t ixaflags, 114111042SErik.Nordmark@Sun.COM uint_t pkt_len, uint32_t xmit_hint, zoneid_t szone, zoneid_t nolzid, 114211042SErik.Nordmark@Sun.COM uintptr_t *ixacookie) 114311042SErik.Nordmark@Sun.COM { 114411042SErik.Nordmark@Sun.COM irb_t *irb; 114511042SErik.Nordmark@Sun.COM ip6_t *ip6h = (ip6_t *)mp->b_rptr; 114611042SErik.Nordmark@Sun.COM ire_t *ire; 114711042SErik.Nordmark@Sun.COM ire_t *ire1; 114811042SErik.Nordmark@Sun.COM mblk_t *mp1; 114911042SErik.Nordmark@Sun.COM nce_t *nce1; 115011042SErik.Nordmark@Sun.COM ill_t *ill = nce->nce_ill; 115111042SErik.Nordmark@Sun.COM ill_t *ill1; 115211042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst; 115311042SErik.Nordmark@Sun.COM int error = 0; 115411042SErik.Nordmark@Sun.COM int num_sent = 0; 115511042SErik.Nordmark@Sun.COM int err; 115611042SErik.Nordmark@Sun.COM uint_t ire_type; 115711042SErik.Nordmark@Sun.COM in6_addr_t nexthop; 115811042SErik.Nordmark@Sun.COM 115911042SErik.Nordmark@Sun.COM ASSERT(!(ixaflags & IXAF_IS_IPV4)); 116011042SErik.Nordmark@Sun.COM 116111042SErik.Nordmark@Sun.COM /* Check for IXAF_LOOPBACK_COPY */ 116211042SErik.Nordmark@Sun.COM if (ixaflags & IXAF_LOOPBACK_COPY) { 116311042SErik.Nordmark@Sun.COM mblk_t *mp1; 116411042SErik.Nordmark@Sun.COM 116511042SErik.Nordmark@Sun.COM mp1 = copymsg(mp); 116611042SErik.Nordmark@Sun.COM if (mp1 == NULL) { 116711042SErik.Nordmark@Sun.COM /* Failed to deliver the loopback copy. */ 116811042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards); 116911042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards", mp, ill); 117011042SErik.Nordmark@Sun.COM error = ENOBUFS; 117111042SErik.Nordmark@Sun.COM } else { 117211042SErik.Nordmark@Sun.COM ip_postfrag_loopback(mp1, nce, ixaflags, pkt_len, 117311042SErik.Nordmark@Sun.COM nolzid); 117411042SErik.Nordmark@Sun.COM } 117511042SErik.Nordmark@Sun.COM } 117611042SErik.Nordmark@Sun.COM 117711042SErik.Nordmark@Sun.COM /* 117811042SErik.Nordmark@Sun.COM * Loop over RTF_MULTIRT for ip6_dst in the same bucket. Send 117911042SErik.Nordmark@Sun.COM * a copy to each one. 118011042SErik.Nordmark@Sun.COM * Use the nce (nexthop) and ip6_dst to find the ire. 118111042SErik.Nordmark@Sun.COM * 118211042SErik.Nordmark@Sun.COM * MULTIRT is not designed to work with shared-IP zones thus we don't 118311042SErik.Nordmark@Sun.COM * need to pass a zoneid or a label to the IRE lookup. 118411042SErik.Nordmark@Sun.COM */ 118511042SErik.Nordmark@Sun.COM if (IN6_ARE_ADDR_EQUAL(&nce->nce_addr, &ip6h->ip6_dst)) { 118611042SErik.Nordmark@Sun.COM /* Broadcast and multicast case */ 118711042SErik.Nordmark@Sun.COM ire = ire_ftable_lookup_v6(&ip6h->ip6_dst, 0, 0, 0, NULL, 118811042SErik.Nordmark@Sun.COM ALL_ZONES, NULL, MATCH_IRE_DSTONLY, 0, ipst, NULL); 118911042SErik.Nordmark@Sun.COM } else { 119011042SErik.Nordmark@Sun.COM /* Unicast case */ 119111042SErik.Nordmark@Sun.COM ire = ire_ftable_lookup_v6(&ip6h->ip6_dst, 0, &nce->nce_addr, 119211042SErik.Nordmark@Sun.COM 0, NULL, ALL_ZONES, NULL, MATCH_IRE_GW, 0, ipst, NULL); 119311042SErik.Nordmark@Sun.COM } 119411042SErik.Nordmark@Sun.COM 119511042SErik.Nordmark@Sun.COM if (ire == NULL || 119611042SErik.Nordmark@Sun.COM (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) || 119711042SErik.Nordmark@Sun.COM !(ire->ire_flags & RTF_MULTIRT)) { 119811042SErik.Nordmark@Sun.COM /* Drop */ 119911042SErik.Nordmark@Sun.COM ip_drop_output("ip_postfrag_multirt didn't find route", 120011042SErik.Nordmark@Sun.COM mp, nce->nce_ill); 120111042SErik.Nordmark@Sun.COM if (ire != NULL) 120211042SErik.Nordmark@Sun.COM ire_refrele(ire); 120311042SErik.Nordmark@Sun.COM return (ENETUNREACH); 120411042SErik.Nordmark@Sun.COM } 120511042SErik.Nordmark@Sun.COM 120611042SErik.Nordmark@Sun.COM irb = ire->ire_bucket; 120711042SErik.Nordmark@Sun.COM irb_refhold(irb); 120811042SErik.Nordmark@Sun.COM for (ire1 = irb->irb_ire; ire1 != NULL; ire1 = ire1->ire_next) { 120911042SErik.Nordmark@Sun.COM if (IRE_IS_CONDEMNED(ire1) || 121011042SErik.Nordmark@Sun.COM !(ire1->ire_flags & RTF_MULTIRT)) 121111042SErik.Nordmark@Sun.COM continue; 121211042SErik.Nordmark@Sun.COM 121311042SErik.Nordmark@Sun.COM /* Note: When IPv6 uses radix tree we don't need this check */ 121411042SErik.Nordmark@Sun.COM if (!IN6_ARE_ADDR_EQUAL(&ire->ire_addr_v6, &ire1->ire_addr_v6)) 121511042SErik.Nordmark@Sun.COM continue; 121611042SErik.Nordmark@Sun.COM 121711042SErik.Nordmark@Sun.COM /* Do the ire argument one after the loop */ 121811042SErik.Nordmark@Sun.COM if (ire1 == ire) 121911042SErik.Nordmark@Sun.COM continue; 122011042SErik.Nordmark@Sun.COM 122111042SErik.Nordmark@Sun.COM ill1 = ire_nexthop_ill(ire1); 122211042SErik.Nordmark@Sun.COM if (ill1 == NULL) { 122311042SErik.Nordmark@Sun.COM /* 122411042SErik.Nordmark@Sun.COM * This ire might not have been picked by 122511042SErik.Nordmark@Sun.COM * ire_route_recursive, in which case ire_dep might 122611042SErik.Nordmark@Sun.COM * not have been setup yet. 122711042SErik.Nordmark@Sun.COM * We kick ire_route_recursive to try to resolve 122811042SErik.Nordmark@Sun.COM * starting at ire1. 122911042SErik.Nordmark@Sun.COM */ 123011042SErik.Nordmark@Sun.COM ire_t *ire2; 123111042SErik.Nordmark@Sun.COM 123211042SErik.Nordmark@Sun.COM ire2 = ire_route_recursive_impl_v6(ire1, 123311042SErik.Nordmark@Sun.COM &ire1->ire_addr_v6, ire1->ire_type, ire1->ire_ill, 123411042SErik.Nordmark@Sun.COM ire1->ire_zoneid, NULL, MATCH_IRE_DSTONLY, 1235*11457SErik.Nordmark@Sun.COM IRR_ALLOCATE, 0, ipst, NULL, NULL, NULL); 123611042SErik.Nordmark@Sun.COM if (ire2 != NULL) 123711042SErik.Nordmark@Sun.COM ire_refrele(ire2); 123811042SErik.Nordmark@Sun.COM ill1 = ire_nexthop_ill(ire1); 123911042SErik.Nordmark@Sun.COM } 124011042SErik.Nordmark@Sun.COM if (ill1 == NULL) { 124111042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards); 124211042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards - no ill", 124311042SErik.Nordmark@Sun.COM mp, ill); 124411042SErik.Nordmark@Sun.COM error = ENETUNREACH; 124511042SErik.Nordmark@Sun.COM continue; 124611042SErik.Nordmark@Sun.COM } 124711042SErik.Nordmark@Sun.COM /* Pick the addr and type to use for ndp_nce_init */ 124811042SErik.Nordmark@Sun.COM if (nce->nce_common->ncec_flags & NCE_F_MCAST) { 124911042SErik.Nordmark@Sun.COM ire_type = IRE_MULTICAST; 125011042SErik.Nordmark@Sun.COM nexthop = ip6h->ip6_dst; 125111042SErik.Nordmark@Sun.COM } else { 125211042SErik.Nordmark@Sun.COM ire_type = ire1->ire_type; /* Doesn't matter */ 125311042SErik.Nordmark@Sun.COM nexthop = ire1->ire_gateway_addr_v6; 125411042SErik.Nordmark@Sun.COM } 125511042SErik.Nordmark@Sun.COM 125611042SErik.Nordmark@Sun.COM /* If IPMP meta or under, then we just drop */ 125711042SErik.Nordmark@Sun.COM if (ill1->ill_grp != NULL) { 125811042SErik.Nordmark@Sun.COM BUMP_MIB(ill1->ill_ip_mib, ipIfStatsOutDiscards); 125911042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards - IPMP", 126011042SErik.Nordmark@Sun.COM mp, ill1); 126111042SErik.Nordmark@Sun.COM ill_refrele(ill1); 126211042SErik.Nordmark@Sun.COM error = ENETUNREACH; 126311042SErik.Nordmark@Sun.COM continue; 126411042SErik.Nordmark@Sun.COM } 126511042SErik.Nordmark@Sun.COM 126611042SErik.Nordmark@Sun.COM nce1 = ndp_nce_init(ill1, &nexthop, ire_type); 126711042SErik.Nordmark@Sun.COM if (nce1 == NULL) { 126811042SErik.Nordmark@Sun.COM BUMP_MIB(ill1->ill_ip_mib, ipIfStatsOutDiscards); 126911042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards - no nce", 127011042SErik.Nordmark@Sun.COM mp, ill1); 127111042SErik.Nordmark@Sun.COM ill_refrele(ill1); 127211042SErik.Nordmark@Sun.COM error = ENOBUFS; 127311042SErik.Nordmark@Sun.COM continue; 127411042SErik.Nordmark@Sun.COM } 127511042SErik.Nordmark@Sun.COM mp1 = copymsg(mp); 127611042SErik.Nordmark@Sun.COM if (mp1 == NULL) { 127711042SErik.Nordmark@Sun.COM BUMP_MIB(ill1->ill_ip_mib, ipIfStatsOutDiscards); 127811042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards", mp, ill1); 127911042SErik.Nordmark@Sun.COM nce_refrele(nce1); 128011042SErik.Nordmark@Sun.COM ill_refrele(ill1); 128111042SErik.Nordmark@Sun.COM error = ENOBUFS; 128211042SErik.Nordmark@Sun.COM continue; 128311042SErik.Nordmark@Sun.COM } 128411042SErik.Nordmark@Sun.COM /* Preserve HW checksum for this copy */ 128511042SErik.Nordmark@Sun.COM DB_CKSUMSTART(mp1) = DB_CKSUMSTART(mp); 128611042SErik.Nordmark@Sun.COM DB_CKSUMSTUFF(mp1) = DB_CKSUMSTUFF(mp); 128711042SErik.Nordmark@Sun.COM DB_CKSUMEND(mp1) = DB_CKSUMEND(mp); 128811042SErik.Nordmark@Sun.COM DB_CKSUMFLAGS(mp1) = DB_CKSUMFLAGS(mp); 128911042SErik.Nordmark@Sun.COM DB_LSOMSS(mp1) = DB_LSOMSS(mp); 129011042SErik.Nordmark@Sun.COM 129111042SErik.Nordmark@Sun.COM ire1->ire_ob_pkt_count++; 129211042SErik.Nordmark@Sun.COM err = ip_xmit(mp1, nce1, ixaflags, pkt_len, xmit_hint, szone, 129311042SErik.Nordmark@Sun.COM 0, ixacookie); 129411042SErik.Nordmark@Sun.COM if (err == 0) 129511042SErik.Nordmark@Sun.COM num_sent++; 129611042SErik.Nordmark@Sun.COM else 129711042SErik.Nordmark@Sun.COM error = err; 129811042SErik.Nordmark@Sun.COM nce_refrele(nce1); 129911042SErik.Nordmark@Sun.COM ill_refrele(ill1); 130011042SErik.Nordmark@Sun.COM } 130111042SErik.Nordmark@Sun.COM irb_refrele(irb); 130211042SErik.Nordmark@Sun.COM ire_refrele(ire); 130311042SErik.Nordmark@Sun.COM /* Finally, the main one */ 130411042SErik.Nordmark@Sun.COM err = ip_xmit(mp, nce, ixaflags, pkt_len, xmit_hint, szone, 0, 130511042SErik.Nordmark@Sun.COM ixacookie); 130611042SErik.Nordmark@Sun.COM if (err == 0) 130711042SErik.Nordmark@Sun.COM num_sent++; 130811042SErik.Nordmark@Sun.COM else 130911042SErik.Nordmark@Sun.COM error = err; 131011042SErik.Nordmark@Sun.COM if (num_sent > 0) 131111042SErik.Nordmark@Sun.COM return (0); 131211042SErik.Nordmark@Sun.COM else 131311042SErik.Nordmark@Sun.COM return (error); 131411042SErik.Nordmark@Sun.COM } 1315