1*11042SErik.Nordmark@Sun.COM /* 2*11042SErik.Nordmark@Sun.COM * CDDL HEADER START 3*11042SErik.Nordmark@Sun.COM * 4*11042SErik.Nordmark@Sun.COM * The contents of this file are subject to the terms of the 5*11042SErik.Nordmark@Sun.COM * Common Development and Distribution License (the "License"). 6*11042SErik.Nordmark@Sun.COM * You may not use this file except in compliance with the License. 7*11042SErik.Nordmark@Sun.COM * 8*11042SErik.Nordmark@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*11042SErik.Nordmark@Sun.COM * or http://www.opensolaris.org/os/licensing. 10*11042SErik.Nordmark@Sun.COM * See the License for the specific language governing permissions 11*11042SErik.Nordmark@Sun.COM * and limitations under the License. 12*11042SErik.Nordmark@Sun.COM * 13*11042SErik.Nordmark@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 14*11042SErik.Nordmark@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*11042SErik.Nordmark@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 16*11042SErik.Nordmark@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 17*11042SErik.Nordmark@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 18*11042SErik.Nordmark@Sun.COM * 19*11042SErik.Nordmark@Sun.COM * CDDL HEADER END 20*11042SErik.Nordmark@Sun.COM */ 21*11042SErik.Nordmark@Sun.COM 22*11042SErik.Nordmark@Sun.COM /* 23*11042SErik.Nordmark@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24*11042SErik.Nordmark@Sun.COM * Use is subject to license terms. 25*11042SErik.Nordmark@Sun.COM */ 26*11042SErik.Nordmark@Sun.COM /* Copyright (c) 1990 Mentat Inc. */ 27*11042SErik.Nordmark@Sun.COM 28*11042SErik.Nordmark@Sun.COM #include <sys/types.h> 29*11042SErik.Nordmark@Sun.COM #include <sys/stream.h> 30*11042SErik.Nordmark@Sun.COM #include <sys/strsubr.h> 31*11042SErik.Nordmark@Sun.COM #include <sys/dlpi.h> 32*11042SErik.Nordmark@Sun.COM #include <sys/strsun.h> 33*11042SErik.Nordmark@Sun.COM #include <sys/zone.h> 34*11042SErik.Nordmark@Sun.COM #include <sys/ddi.h> 35*11042SErik.Nordmark@Sun.COM #include <sys/sunddi.h> 36*11042SErik.Nordmark@Sun.COM #include <sys/cmn_err.h> 37*11042SErik.Nordmark@Sun.COM #include <sys/debug.h> 38*11042SErik.Nordmark@Sun.COM #include <sys/atomic.h> 39*11042SErik.Nordmark@Sun.COM 40*11042SErik.Nordmark@Sun.COM #include <sys/systm.h> 41*11042SErik.Nordmark@Sun.COM #include <sys/param.h> 42*11042SErik.Nordmark@Sun.COM #include <sys/kmem.h> 43*11042SErik.Nordmark@Sun.COM #include <sys/sdt.h> 44*11042SErik.Nordmark@Sun.COM #include <sys/socket.h> 45*11042SErik.Nordmark@Sun.COM #include <sys/mac.h> 46*11042SErik.Nordmark@Sun.COM #include <net/if.h> 47*11042SErik.Nordmark@Sun.COM #include <net/if_arp.h> 48*11042SErik.Nordmark@Sun.COM #include <net/route.h> 49*11042SErik.Nordmark@Sun.COM #include <sys/sockio.h> 50*11042SErik.Nordmark@Sun.COM #include <netinet/in.h> 51*11042SErik.Nordmark@Sun.COM #include <net/if_dl.h> 52*11042SErik.Nordmark@Sun.COM 53*11042SErik.Nordmark@Sun.COM #include <inet/common.h> 54*11042SErik.Nordmark@Sun.COM #include <inet/mi.h> 55*11042SErik.Nordmark@Sun.COM #include <inet/mib2.h> 56*11042SErik.Nordmark@Sun.COM #include <inet/nd.h> 57*11042SErik.Nordmark@Sun.COM #include <inet/arp.h> 58*11042SErik.Nordmark@Sun.COM #include <inet/snmpcom.h> 59*11042SErik.Nordmark@Sun.COM #include <inet/kstatcom.h> 60*11042SErik.Nordmark@Sun.COM 61*11042SErik.Nordmark@Sun.COM #include <netinet/igmp_var.h> 62*11042SErik.Nordmark@Sun.COM #include <netinet/ip6.h> 63*11042SErik.Nordmark@Sun.COM #include <netinet/icmp6.h> 64*11042SErik.Nordmark@Sun.COM #include <netinet/sctp.h> 65*11042SErik.Nordmark@Sun.COM 66*11042SErik.Nordmark@Sun.COM #include <inet/ip.h> 67*11042SErik.Nordmark@Sun.COM #include <inet/ip_impl.h> 68*11042SErik.Nordmark@Sun.COM #include <inet/ip6.h> 69*11042SErik.Nordmark@Sun.COM #include <inet/ip6_asp.h> 70*11042SErik.Nordmark@Sun.COM #include <inet/tcp.h> 71*11042SErik.Nordmark@Sun.COM #include <inet/ip_multi.h> 72*11042SErik.Nordmark@Sun.COM #include <inet/ip_if.h> 73*11042SErik.Nordmark@Sun.COM #include <inet/ip_ire.h> 74*11042SErik.Nordmark@Sun.COM #include <inet/ip_ftable.h> 75*11042SErik.Nordmark@Sun.COM #include <inet/ip_rts.h> 76*11042SErik.Nordmark@Sun.COM #include <inet/optcom.h> 77*11042SErik.Nordmark@Sun.COM #include <inet/ip_ndp.h> 78*11042SErik.Nordmark@Sun.COM #include <inet/ip_listutils.h> 79*11042SErik.Nordmark@Sun.COM #include <netinet/igmp.h> 80*11042SErik.Nordmark@Sun.COM #include <netinet/ip_mroute.h> 81*11042SErik.Nordmark@Sun.COM #include <inet/ipp_common.h> 82*11042SErik.Nordmark@Sun.COM 83*11042SErik.Nordmark@Sun.COM #include <net/pfkeyv2.h> 84*11042SErik.Nordmark@Sun.COM #include <inet/sadb.h> 85*11042SErik.Nordmark@Sun.COM #include <inet/ipsec_impl.h> 86*11042SErik.Nordmark@Sun.COM #include <inet/ipdrop.h> 87*11042SErik.Nordmark@Sun.COM #include <inet/ip_netinfo.h> 88*11042SErik.Nordmark@Sun.COM 89*11042SErik.Nordmark@Sun.COM #include <sys/pattr.h> 90*11042SErik.Nordmark@Sun.COM #include <inet/ipclassifier.h> 91*11042SErik.Nordmark@Sun.COM #include <inet/sctp_ip.h> 92*11042SErik.Nordmark@Sun.COM #include <inet/sctp/sctp_impl.h> 93*11042SErik.Nordmark@Sun.COM #include <inet/udp_impl.h> 94*11042SErik.Nordmark@Sun.COM #include <sys/sunddi.h> 95*11042SErik.Nordmark@Sun.COM 96*11042SErik.Nordmark@Sun.COM #include <sys/tsol/label.h> 97*11042SErik.Nordmark@Sun.COM #include <sys/tsol/tnet.h> 98*11042SErik.Nordmark@Sun.COM 99*11042SErik.Nordmark@Sun.COM #ifdef DEBUG 100*11042SErik.Nordmark@Sun.COM extern boolean_t skip_sctp_cksum; 101*11042SErik.Nordmark@Sun.COM #endif 102*11042SErik.Nordmark@Sun.COM 103*11042SErik.Nordmark@Sun.COM int 104*11042SErik.Nordmark@Sun.COM ip_output_simple_v6(mblk_t *mp, ip_xmit_attr_t *ixa) 105*11042SErik.Nordmark@Sun.COM { 106*11042SErik.Nordmark@Sun.COM ip6_t *ip6h; 107*11042SErik.Nordmark@Sun.COM in6_addr_t firsthop; /* In IP header */ 108*11042SErik.Nordmark@Sun.COM in6_addr_t dst; /* End of source route, or ip6_dst if none */ 109*11042SErik.Nordmark@Sun.COM ire_t *ire; 110*11042SErik.Nordmark@Sun.COM in6_addr_t setsrc; 111*11042SErik.Nordmark@Sun.COM int error; 112*11042SErik.Nordmark@Sun.COM ill_t *ill = NULL; 113*11042SErik.Nordmark@Sun.COM dce_t *dce = NULL; 114*11042SErik.Nordmark@Sun.COM nce_t *nce; 115*11042SErik.Nordmark@Sun.COM iaflags_t ixaflags = ixa->ixa_flags; 116*11042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst; 117*11042SErik.Nordmark@Sun.COM uint8_t *nexthdrp; 118*11042SErik.Nordmark@Sun.COM boolean_t repeat = B_FALSE; 119*11042SErik.Nordmark@Sun.COM boolean_t multirt = B_FALSE; 120*11042SErik.Nordmark@Sun.COM uint_t ifindex; 121*11042SErik.Nordmark@Sun.COM 122*11042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr; 123*11042SErik.Nordmark@Sun.COM ASSERT(IPH_HDR_VERSION(ip6h) == IPV6_VERSION); 124*11042SErik.Nordmark@Sun.COM 125*11042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_nce == NULL); 126*11042SErik.Nordmark@Sun.COM 127*11042SErik.Nordmark@Sun.COM ixa->ixa_pktlen = ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN; 128*11042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_pktlen == msgdsize(mp)); 129*11042SErik.Nordmark@Sun.COM if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &ixa->ixa_ip_hdr_length, 130*11042SErik.Nordmark@Sun.COM &nexthdrp)) { 131*11042SErik.Nordmark@Sun.COM /* Malformed packet */ 132*11042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsHCOutRequests); 133*11042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards); 134*11042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards", mp, NULL); 135*11042SErik.Nordmark@Sun.COM freemsg(mp); 136*11042SErik.Nordmark@Sun.COM return (EINVAL); 137*11042SErik.Nordmark@Sun.COM } 138*11042SErik.Nordmark@Sun.COM ixa->ixa_protocol = *nexthdrp; 139*11042SErik.Nordmark@Sun.COM 140*11042SErik.Nordmark@Sun.COM /* 141*11042SErik.Nordmark@Sun.COM * Assumes that source routed packets have already been massaged by 142*11042SErik.Nordmark@Sun.COM * the ULP (ip_massage_options_v6) and as a result ip6_dst is the next 143*11042SErik.Nordmark@Sun.COM * hop in the source route. The final destination is used for IPsec 144*11042SErik.Nordmark@Sun.COM * policy and DCE lookup. 145*11042SErik.Nordmark@Sun.COM */ 146*11042SErik.Nordmark@Sun.COM firsthop = ip6h->ip6_dst; 147*11042SErik.Nordmark@Sun.COM dst = ip_get_dst_v6(ip6h, mp, NULL); 148*11042SErik.Nordmark@Sun.COM 149*11042SErik.Nordmark@Sun.COM repeat_ire: 150*11042SErik.Nordmark@Sun.COM error = 0; 151*11042SErik.Nordmark@Sun.COM setsrc = ipv6_all_zeros; 152*11042SErik.Nordmark@Sun.COM ire = ip_select_route_v6(&firsthop, ixa, NULL, &setsrc, &error, 153*11042SErik.Nordmark@Sun.COM &multirt); 154*11042SErik.Nordmark@Sun.COM ASSERT(ire != NULL); /* IRE_NOROUTE if none found */ 155*11042SErik.Nordmark@Sun.COM if (error != 0) { 156*11042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsHCOutRequests); 157*11042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards); 158*11042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards", mp, NULL); 159*11042SErik.Nordmark@Sun.COM freemsg(mp); 160*11042SErik.Nordmark@Sun.COM goto done; 161*11042SErik.Nordmark@Sun.COM } 162*11042SErik.Nordmark@Sun.COM 163*11042SErik.Nordmark@Sun.COM if (ire->ire_flags & (RTF_BLACKHOLE|RTF_REJECT)) { 164*11042SErik.Nordmark@Sun.COM /* ire_ill might be NULL hence need to skip some code */ 165*11042SErik.Nordmark@Sun.COM if (ixaflags & IXAF_SET_SOURCE) 166*11042SErik.Nordmark@Sun.COM ip6h->ip6_src = ipv6_loopback; 167*11042SErik.Nordmark@Sun.COM ixa->ixa_fragsize = IP_MAXPACKET; 168*11042SErik.Nordmark@Sun.COM ire->ire_ob_pkt_count++; 169*11042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsHCOutRequests); 170*11042SErik.Nordmark@Sun.COM /* No dce yet; use default one */ 171*11042SErik.Nordmark@Sun.COM error = (ire->ire_sendfn)(ire, mp, ip6h, ixa, 172*11042SErik.Nordmark@Sun.COM &ipst->ips_dce_default->dce_ident); 173*11042SErik.Nordmark@Sun.COM goto done; 174*11042SErik.Nordmark@Sun.COM } 175*11042SErik.Nordmark@Sun.COM 176*11042SErik.Nordmark@Sun.COM /* Note that ip6_dst is only used for IRE_MULTICAST */ 177*11042SErik.Nordmark@Sun.COM nce = ire_to_nce(ire, INADDR_ANY, &ip6h->ip6_dst); 178*11042SErik.Nordmark@Sun.COM if (nce == NULL) { 179*11042SErik.Nordmark@Sun.COM /* Allocation failure? */ 180*11042SErik.Nordmark@Sun.COM ip_drop_output("ire_to_nce", mp, ill); 181*11042SErik.Nordmark@Sun.COM freemsg(mp); 182*11042SErik.Nordmark@Sun.COM error = ENOBUFS; 183*11042SErik.Nordmark@Sun.COM goto done; 184*11042SErik.Nordmark@Sun.COM } 185*11042SErik.Nordmark@Sun.COM if (nce->nce_is_condemned) { 186*11042SErik.Nordmark@Sun.COM nce_t *nce1; 187*11042SErik.Nordmark@Sun.COM 188*11042SErik.Nordmark@Sun.COM nce1 = ire_handle_condemned_nce(nce, ire, NULL, ip6h, B_TRUE); 189*11042SErik.Nordmark@Sun.COM nce_refrele(nce); 190*11042SErik.Nordmark@Sun.COM if (nce1 == NULL) { 191*11042SErik.Nordmark@Sun.COM if (!repeat) { 192*11042SErik.Nordmark@Sun.COM /* Try finding a better IRE */ 193*11042SErik.Nordmark@Sun.COM repeat = B_TRUE; 194*11042SErik.Nordmark@Sun.COM ire_refrele(ire); 195*11042SErik.Nordmark@Sun.COM goto repeat_ire; 196*11042SErik.Nordmark@Sun.COM } 197*11042SErik.Nordmark@Sun.COM /* Tried twice - drop packet */ 198*11042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards); 199*11042SErik.Nordmark@Sun.COM ip_drop_output("No nce", mp, ill); 200*11042SErik.Nordmark@Sun.COM freemsg(mp); 201*11042SErik.Nordmark@Sun.COM error = ENOBUFS; 202*11042SErik.Nordmark@Sun.COM goto done; 203*11042SErik.Nordmark@Sun.COM } 204*11042SErik.Nordmark@Sun.COM nce = nce1; 205*11042SErik.Nordmark@Sun.COM } 206*11042SErik.Nordmark@Sun.COM /* 207*11042SErik.Nordmark@Sun.COM * For multicast with multirt we have a flag passed back from 208*11042SErik.Nordmark@Sun.COM * ire_lookup_multi_ill_v6 since we don't have an IRE for each 209*11042SErik.Nordmark@Sun.COM * possible multicast address. 210*11042SErik.Nordmark@Sun.COM * We also need a flag for multicast since we can't check 211*11042SErik.Nordmark@Sun.COM * whether RTF_MULTIRT is set in ixa_ire for multicast. 212*11042SErik.Nordmark@Sun.COM */ 213*11042SErik.Nordmark@Sun.COM if (multirt) { 214*11042SErik.Nordmark@Sun.COM ixa->ixa_postfragfn = ip_postfrag_multirt_v6; 215*11042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_MULTIRT_MULTICAST; 216*11042SErik.Nordmark@Sun.COM } else { 217*11042SErik.Nordmark@Sun.COM ixa->ixa_postfragfn = ire->ire_postfragfn; 218*11042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_MULTIRT_MULTICAST; 219*11042SErik.Nordmark@Sun.COM } 220*11042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_nce == NULL); 221*11042SErik.Nordmark@Sun.COM ixa->ixa_nce = nce; 222*11042SErik.Nordmark@Sun.COM 223*11042SErik.Nordmark@Sun.COM /* 224*11042SErik.Nordmark@Sun.COM * Check for a dce_t with a path mtu. 225*11042SErik.Nordmark@Sun.COM */ 226*11042SErik.Nordmark@Sun.COM ifindex = 0; 227*11042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_LINKSCOPE(&dst)) 228*11042SErik.Nordmark@Sun.COM ifindex = nce->nce_common->ncec_ill->ill_phyint->phyint_ifindex; 229*11042SErik.Nordmark@Sun.COM 230*11042SErik.Nordmark@Sun.COM dce = dce_lookup_v6(&dst, ifindex, ipst, NULL); 231*11042SErik.Nordmark@Sun.COM ASSERT(dce != NULL); 232*11042SErik.Nordmark@Sun.COM 233*11042SErik.Nordmark@Sun.COM if (!(ixaflags & IXAF_PMTU_DISCOVERY)) { 234*11042SErik.Nordmark@Sun.COM ixa->ixa_fragsize = IPV6_MIN_MTU; 235*11042SErik.Nordmark@Sun.COM } else if (dce->dce_flags & DCEF_PMTU) { 236*11042SErik.Nordmark@Sun.COM /* 237*11042SErik.Nordmark@Sun.COM * To avoid a periodic timer to increase the path MTU we 238*11042SErik.Nordmark@Sun.COM * look at dce_last_change_time each time we send a packet. 239*11042SErik.Nordmark@Sun.COM */ 240*11042SErik.Nordmark@Sun.COM if (TICK_TO_SEC(lbolt64) - dce->dce_last_change_time > 241*11042SErik.Nordmark@Sun.COM ipst->ips_ip_pathmtu_interval) { 242*11042SErik.Nordmark@Sun.COM /* 243*11042SErik.Nordmark@Sun.COM * Older than 20 minutes. Drop the path MTU information. 244*11042SErik.Nordmark@Sun.COM */ 245*11042SErik.Nordmark@Sun.COM mutex_enter(&dce->dce_lock); 246*11042SErik.Nordmark@Sun.COM dce->dce_flags &= ~(DCEF_PMTU|DCEF_TOO_SMALL_PMTU); 247*11042SErik.Nordmark@Sun.COM dce->dce_last_change_time = TICK_TO_SEC(lbolt64); 248*11042SErik.Nordmark@Sun.COM mutex_exit(&dce->dce_lock); 249*11042SErik.Nordmark@Sun.COM dce_increment_generation(dce); 250*11042SErik.Nordmark@Sun.COM ixa->ixa_fragsize = ip_get_base_mtu(nce->nce_ill, ire); 251*11042SErik.Nordmark@Sun.COM } else { 252*11042SErik.Nordmark@Sun.COM uint_t fragsize; 253*11042SErik.Nordmark@Sun.COM 254*11042SErik.Nordmark@Sun.COM fragsize = ip_get_base_mtu(nce->nce_ill, ire); 255*11042SErik.Nordmark@Sun.COM if (fragsize > dce->dce_pmtu) 256*11042SErik.Nordmark@Sun.COM fragsize = dce->dce_pmtu; 257*11042SErik.Nordmark@Sun.COM ixa->ixa_fragsize = fragsize; 258*11042SErik.Nordmark@Sun.COM } 259*11042SErik.Nordmark@Sun.COM } else { 260*11042SErik.Nordmark@Sun.COM ixa->ixa_fragsize = ip_get_base_mtu(nce->nce_ill, ire); 261*11042SErik.Nordmark@Sun.COM } 262*11042SErik.Nordmark@Sun.COM 263*11042SErik.Nordmark@Sun.COM /* 264*11042SErik.Nordmark@Sun.COM * We use use ire_nexthop_ill (and not ncec_ill) to avoid the under ipmp 265*11042SErik.Nordmark@Sun.COM * interface for source address selection. 266*11042SErik.Nordmark@Sun.COM */ 267*11042SErik.Nordmark@Sun.COM ill = ire_nexthop_ill(ire); 268*11042SErik.Nordmark@Sun.COM 269*11042SErik.Nordmark@Sun.COM if (ixaflags & IXAF_SET_SOURCE) { 270*11042SErik.Nordmark@Sun.COM in6_addr_t src; 271*11042SErik.Nordmark@Sun.COM 272*11042SErik.Nordmark@Sun.COM /* 273*11042SErik.Nordmark@Sun.COM * We use the final destination to get 274*11042SErik.Nordmark@Sun.COM * correct selection for source routed packets 275*11042SErik.Nordmark@Sun.COM */ 276*11042SErik.Nordmark@Sun.COM 277*11042SErik.Nordmark@Sun.COM /* If unreachable we have no ill but need some source */ 278*11042SErik.Nordmark@Sun.COM if (ill == NULL) { 279*11042SErik.Nordmark@Sun.COM src = ipv6_loopback; 280*11042SErik.Nordmark@Sun.COM error = 0; 281*11042SErik.Nordmark@Sun.COM } else { 282*11042SErik.Nordmark@Sun.COM error = ip_select_source_v6(ill, &setsrc, &dst, 283*11042SErik.Nordmark@Sun.COM ixa->ixa_zoneid, ipst, B_FALSE, 284*11042SErik.Nordmark@Sun.COM ixa->ixa_src_preferences, &src, NULL, NULL); 285*11042SErik.Nordmark@Sun.COM } 286*11042SErik.Nordmark@Sun.COM if (error != 0) { 287*11042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCOutRequests); 288*11042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards); 289*11042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards - no source", 290*11042SErik.Nordmark@Sun.COM mp, ill); 291*11042SErik.Nordmark@Sun.COM freemsg(mp); 292*11042SErik.Nordmark@Sun.COM goto done; 293*11042SErik.Nordmark@Sun.COM } 294*11042SErik.Nordmark@Sun.COM ip6h->ip6_src = src; 295*11042SErik.Nordmark@Sun.COM } else if (ixaflags & IXAF_VERIFY_SOURCE) { 296*11042SErik.Nordmark@Sun.COM /* Check if the IP source is assigned to the host. */ 297*11042SErik.Nordmark@Sun.COM if (!ip_verify_src(mp, ixa, NULL)) { 298*11042SErik.Nordmark@Sun.COM /* Don't send a packet with a source that isn't ours */ 299*11042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsHCOutRequests); 300*11042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards); 301*11042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards - invalid source", 302*11042SErik.Nordmark@Sun.COM mp, ill); 303*11042SErik.Nordmark@Sun.COM freemsg(mp); 304*11042SErik.Nordmark@Sun.COM error = EADDRNOTAVAIL; 305*11042SErik.Nordmark@Sun.COM goto done; 306*11042SErik.Nordmark@Sun.COM } 307*11042SErik.Nordmark@Sun.COM } 308*11042SErik.Nordmark@Sun.COM 309*11042SErik.Nordmark@Sun.COM /* 310*11042SErik.Nordmark@Sun.COM * Check against global IPsec policy to set the AH/ESP attributes. 311*11042SErik.Nordmark@Sun.COM * IPsec will set IXAF_IPSEC_* and ixa_ipsec_* as appropriate. 312*11042SErik.Nordmark@Sun.COM */ 313*11042SErik.Nordmark@Sun.COM if (!(ixaflags & (IXAF_NO_IPSEC|IXAF_IPSEC_SECURE))) { 314*11042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_ipsec_policy == NULL); 315*11042SErik.Nordmark@Sun.COM mp = ip_output_attach_policy(mp, NULL, ip6h, NULL, ixa); 316*11042SErik.Nordmark@Sun.COM if (mp == NULL) { 317*11042SErik.Nordmark@Sun.COM /* MIB and ip_drop_packet already done */ 318*11042SErik.Nordmark@Sun.COM return (EHOSTUNREACH); /* IPsec policy failure */ 319*11042SErik.Nordmark@Sun.COM } 320*11042SErik.Nordmark@Sun.COM } 321*11042SErik.Nordmark@Sun.COM 322*11042SErik.Nordmark@Sun.COM if (ill != NULL) { 323*11042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCOutRequests); 324*11042SErik.Nordmark@Sun.COM } else { 325*11042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsHCOutRequests); 326*11042SErik.Nordmark@Sun.COM } 327*11042SErik.Nordmark@Sun.COM 328*11042SErik.Nordmark@Sun.COM /* 329*11042SErik.Nordmark@Sun.COM * We update the statistics on the most specific IRE i.e., the first 330*11042SErik.Nordmark@Sun.COM * one we found. 331*11042SErik.Nordmark@Sun.COM * We don't have an IRE when we fragment, hence ire_ob_pkt_count 332*11042SErik.Nordmark@Sun.COM * can only count the use prior to fragmentation. However the MIB 333*11042SErik.Nordmark@Sun.COM * counters on the ill will be incremented in post fragmentation. 334*11042SErik.Nordmark@Sun.COM */ 335*11042SErik.Nordmark@Sun.COM ire->ire_ob_pkt_count++; 336*11042SErik.Nordmark@Sun.COM 337*11042SErik.Nordmark@Sun.COM /* 338*11042SErik.Nordmark@Sun.COM * Based on ire_type and ire_flags call one of: 339*11042SErik.Nordmark@Sun.COM * ire_send_local_v6 - for IRE_LOCAL and IRE_LOOPBACK 340*11042SErik.Nordmark@Sun.COM * ire_send_multirt_v6 - if RTF_MULTIRT 341*11042SErik.Nordmark@Sun.COM * ire_send_noroute_v6 - if RTF_REJECT or RTF_BLACHOLE 342*11042SErik.Nordmark@Sun.COM * ire_send_multicast_v6 - for IRE_MULTICAST 343*11042SErik.Nordmark@Sun.COM * ire_send_wire_v6 - for the rest. 344*11042SErik.Nordmark@Sun.COM */ 345*11042SErik.Nordmark@Sun.COM error = (ire->ire_sendfn)(ire, mp, ip6h, ixa, &dce->dce_ident); 346*11042SErik.Nordmark@Sun.COM done: 347*11042SErik.Nordmark@Sun.COM ire_refrele(ire); 348*11042SErik.Nordmark@Sun.COM if (dce != NULL) 349*11042SErik.Nordmark@Sun.COM dce_refrele(dce); 350*11042SErik.Nordmark@Sun.COM if (ill != NULL) 351*11042SErik.Nordmark@Sun.COM ill_refrele(ill); 352*11042SErik.Nordmark@Sun.COM if (ixa->ixa_nce != NULL) 353*11042SErik.Nordmark@Sun.COM nce_refrele(ixa->ixa_nce); 354*11042SErik.Nordmark@Sun.COM ixa->ixa_nce = NULL; 355*11042SErik.Nordmark@Sun.COM return (error); 356*11042SErik.Nordmark@Sun.COM } 357*11042SErik.Nordmark@Sun.COM 358*11042SErik.Nordmark@Sun.COM /* 359*11042SErik.Nordmark@Sun.COM * ire_sendfn() functions. 360*11042SErik.Nordmark@Sun.COM * These functions use the following xmit_attr: 361*11042SErik.Nordmark@Sun.COM * - ixa_fragsize - read to determine whether or not to fragment 362*11042SErik.Nordmark@Sun.COM * - IXAF_IPSEC_SECURE - to determine whether or not to invoke IPsec 363*11042SErik.Nordmark@Sun.COM * - ixa_ipsec_* are used inside IPsec 364*11042SErik.Nordmark@Sun.COM * - IXAF_LOOPBACK_COPY - for multicast 365*11042SErik.Nordmark@Sun.COM */ 366*11042SErik.Nordmark@Sun.COM 367*11042SErik.Nordmark@Sun.COM 368*11042SErik.Nordmark@Sun.COM /* 369*11042SErik.Nordmark@Sun.COM * ire_sendfn for IRE_LOCAL and IRE_LOOPBACK 370*11042SErik.Nordmark@Sun.COM * 371*11042SErik.Nordmark@Sun.COM * The checks for restrict_interzone_loopback are done in ire_route_recursive. 372*11042SErik.Nordmark@Sun.COM */ 373*11042SErik.Nordmark@Sun.COM /* ARGSUSED4 */ 374*11042SErik.Nordmark@Sun.COM int 375*11042SErik.Nordmark@Sun.COM ire_send_local_v6(ire_t *ire, mblk_t *mp, void *iph_arg, 376*11042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa, uint32_t *identp) 377*11042SErik.Nordmark@Sun.COM { 378*11042SErik.Nordmark@Sun.COM ip6_t *ip6h = (ip6_t *)iph_arg; 379*11042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst; 380*11042SErik.Nordmark@Sun.COM ill_t *ill = ire->ire_ill; 381*11042SErik.Nordmark@Sun.COM ip_recv_attr_t iras; /* NOTE: No bzero for performance */ 382*11042SErik.Nordmark@Sun.COM uint_t pktlen = ixa->ixa_pktlen; 383*11042SErik.Nordmark@Sun.COM 384*11042SErik.Nordmark@Sun.COM /* 385*11042SErik.Nordmark@Sun.COM * No fragmentation, no nce, and no application of IPsec. 386*11042SErik.Nordmark@Sun.COM * 387*11042SErik.Nordmark@Sun.COM * 388*11042SErik.Nordmark@Sun.COM * Note different order between IP provider and FW_HOOKS than in 389*11042SErik.Nordmark@Sun.COM * send_wire case. 390*11042SErik.Nordmark@Sun.COM */ 391*11042SErik.Nordmark@Sun.COM 392*11042SErik.Nordmark@Sun.COM /* 393*11042SErik.Nordmark@Sun.COM * DTrace this as ip:::send. A packet blocked by FW_HOOKS will fire the 394*11042SErik.Nordmark@Sun.COM * send probe, but not the receive probe. 395*11042SErik.Nordmark@Sun.COM */ 396*11042SErik.Nordmark@Sun.COM DTRACE_IP7(send, mblk_t *, mp, conn_t *, NULL, void_ip_t *, 397*11042SErik.Nordmark@Sun.COM ip6h, __dtrace_ipsr_ill_t *, ill, ipha_t *, NULL, ip6_t *, ip6h, 398*11042SErik.Nordmark@Sun.COM int, 1); 399*11042SErik.Nordmark@Sun.COM 400*11042SErik.Nordmark@Sun.COM DTRACE_PROBE4(ip6__loopback__out__start, 401*11042SErik.Nordmark@Sun.COM ill_t *, NULL, ill_t *, ill, 402*11042SErik.Nordmark@Sun.COM ip6_t *, ip6h, mblk_t *, mp); 403*11042SErik.Nordmark@Sun.COM 404*11042SErik.Nordmark@Sun.COM if (HOOKS6_INTERESTED_LOOPBACK_OUT(ipst)) { 405*11042SErik.Nordmark@Sun.COM int error; 406*11042SErik.Nordmark@Sun.COM 407*11042SErik.Nordmark@Sun.COM FW_HOOKS(ipst->ips_ip6_loopback_out_event, 408*11042SErik.Nordmark@Sun.COM ipst->ips_ipv6firewall_loopback_out, 409*11042SErik.Nordmark@Sun.COM NULL, ill, ip6h, mp, mp, 0, ipst, error); 410*11042SErik.Nordmark@Sun.COM 411*11042SErik.Nordmark@Sun.COM DTRACE_PROBE1(ip6__loopback__out__end, mblk_t *, mp); 412*11042SErik.Nordmark@Sun.COM if (mp == NULL) 413*11042SErik.Nordmark@Sun.COM return (error); 414*11042SErik.Nordmark@Sun.COM 415*11042SErik.Nordmark@Sun.COM /* 416*11042SErik.Nordmark@Sun.COM * Even if the destination was changed by the filter we use the 417*11042SErik.Nordmark@Sun.COM * forwarding decision that was made based on the address 418*11042SErik.Nordmark@Sun.COM * in ip_output/ip_set_destination. 419*11042SErik.Nordmark@Sun.COM */ 420*11042SErik.Nordmark@Sun.COM /* Length could be different */ 421*11042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr; 422*11042SErik.Nordmark@Sun.COM pktlen = ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN; 423*11042SErik.Nordmark@Sun.COM } 424*11042SErik.Nordmark@Sun.COM 425*11042SErik.Nordmark@Sun.COM /* 426*11042SErik.Nordmark@Sun.COM * If a callback is enabled then we need to know the 427*11042SErik.Nordmark@Sun.COM * source and destination zoneids for the packet. We already 428*11042SErik.Nordmark@Sun.COM * have those handy. 429*11042SErik.Nordmark@Sun.COM */ 430*11042SErik.Nordmark@Sun.COM if (ipst->ips_ip6_observe.he_interested) { 431*11042SErik.Nordmark@Sun.COM zoneid_t szone, dzone; 432*11042SErik.Nordmark@Sun.COM zoneid_t stackzoneid; 433*11042SErik.Nordmark@Sun.COM 434*11042SErik.Nordmark@Sun.COM stackzoneid = netstackid_to_zoneid( 435*11042SErik.Nordmark@Sun.COM ipst->ips_netstack->netstack_stackid); 436*11042SErik.Nordmark@Sun.COM 437*11042SErik.Nordmark@Sun.COM if (stackzoneid == GLOBAL_ZONEID) { 438*11042SErik.Nordmark@Sun.COM /* Shared-IP zone */ 439*11042SErik.Nordmark@Sun.COM dzone = ire->ire_zoneid; 440*11042SErik.Nordmark@Sun.COM szone = ixa->ixa_zoneid; 441*11042SErik.Nordmark@Sun.COM } else { 442*11042SErik.Nordmark@Sun.COM szone = dzone = stackzoneid; 443*11042SErik.Nordmark@Sun.COM } 444*11042SErik.Nordmark@Sun.COM ipobs_hook(mp, IPOBS_HOOK_LOCAL, szone, dzone, ill, ipst); 445*11042SErik.Nordmark@Sun.COM } 446*11042SErik.Nordmark@Sun.COM 447*11042SErik.Nordmark@Sun.COM /* Handle lo0 stats */ 448*11042SErik.Nordmark@Sun.COM ipst->ips_loopback_packets++; 449*11042SErik.Nordmark@Sun.COM 450*11042SErik.Nordmark@Sun.COM /* 451*11042SErik.Nordmark@Sun.COM * Update output mib stats. Note that we can't move into the icmp 452*11042SErik.Nordmark@Sun.COM * sender (icmp_output etc) since they don't know the ill and the 453*11042SErik.Nordmark@Sun.COM * stats are per ill. 454*11042SErik.Nordmark@Sun.COM */ 455*11042SErik.Nordmark@Sun.COM if (ixa->ixa_protocol == IPPROTO_ICMPV6) { 456*11042SErik.Nordmark@Sun.COM icmp6_t *icmp6; 457*11042SErik.Nordmark@Sun.COM 458*11042SErik.Nordmark@Sun.COM icmp6 = (icmp6_t *)((uchar_t *)ip6h + ixa->ixa_ip_hdr_length); 459*11042SErik.Nordmark@Sun.COM icmp_update_out_mib_v6(ill, icmp6); 460*11042SErik.Nordmark@Sun.COM } 461*11042SErik.Nordmark@Sun.COM 462*11042SErik.Nordmark@Sun.COM DTRACE_PROBE4(ip6__loopback__in__start, 463*11042SErik.Nordmark@Sun.COM ill_t *, ill, ill_t *, NULL, 464*11042SErik.Nordmark@Sun.COM ip6_t *, ip6h, mblk_t *, mp); 465*11042SErik.Nordmark@Sun.COM 466*11042SErik.Nordmark@Sun.COM if (HOOKS6_INTERESTED_LOOPBACK_IN(ipst)) { 467*11042SErik.Nordmark@Sun.COM int error; 468*11042SErik.Nordmark@Sun.COM 469*11042SErik.Nordmark@Sun.COM FW_HOOKS(ipst->ips_ip6_loopback_in_event, 470*11042SErik.Nordmark@Sun.COM ipst->ips_ipv6firewall_loopback_in, 471*11042SErik.Nordmark@Sun.COM ill, NULL, ip6h, mp, mp, 0, ipst, error); 472*11042SErik.Nordmark@Sun.COM 473*11042SErik.Nordmark@Sun.COM DTRACE_PROBE1(ip6__loopback__in__end, mblk_t *, mp); 474*11042SErik.Nordmark@Sun.COM if (mp == NULL) 475*11042SErik.Nordmark@Sun.COM return (error); 476*11042SErik.Nordmark@Sun.COM 477*11042SErik.Nordmark@Sun.COM /* 478*11042SErik.Nordmark@Sun.COM * Even if the destination was changed by the filter we use the 479*11042SErik.Nordmark@Sun.COM * forwarding decision that was made based on the address 480*11042SErik.Nordmark@Sun.COM * in ip_output/ip_set_destination. 481*11042SErik.Nordmark@Sun.COM */ 482*11042SErik.Nordmark@Sun.COM /* Length could be different */ 483*11042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr; 484*11042SErik.Nordmark@Sun.COM pktlen = ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN; 485*11042SErik.Nordmark@Sun.COM } 486*11042SErik.Nordmark@Sun.COM 487*11042SErik.Nordmark@Sun.COM DTRACE_IP7(receive, mblk_t *, mp, conn_t *, NULL, void_ip_t *, 488*11042SErik.Nordmark@Sun.COM ip6h, __dtrace_ipsr_ill_t *, ill, ipha_t *, NULL, ip6_t *, ip6h, 489*11042SErik.Nordmark@Sun.COM int, 1); 490*11042SErik.Nordmark@Sun.COM 491*11042SErik.Nordmark@Sun.COM /* Map ixa to ira including IPsec policies */ 492*11042SErik.Nordmark@Sun.COM ipsec_out_to_in(ixa, ill, &iras); 493*11042SErik.Nordmark@Sun.COM iras.ira_pktlen = pktlen; 494*11042SErik.Nordmark@Sun.COM 495*11042SErik.Nordmark@Sun.COM ire->ire_ib_pkt_count++; 496*11042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCInReceives); 497*11042SErik.Nordmark@Sun.COM UPDATE_MIB(ill->ill_ip_mib, ipIfStatsHCInOctets, pktlen); 498*11042SErik.Nordmark@Sun.COM 499*11042SErik.Nordmark@Sun.COM /* Destined to ire_zoneid - use that for fanout */ 500*11042SErik.Nordmark@Sun.COM iras.ira_zoneid = ire->ire_zoneid; 501*11042SErik.Nordmark@Sun.COM 502*11042SErik.Nordmark@Sun.COM if (is_system_labeled()) { 503*11042SErik.Nordmark@Sun.COM iras.ira_flags |= IRAF_SYSTEM_LABELED; 504*11042SErik.Nordmark@Sun.COM 505*11042SErik.Nordmark@Sun.COM /* 506*11042SErik.Nordmark@Sun.COM * This updates ira_cred, ira_tsl and ira_free_flags based 507*11042SErik.Nordmark@Sun.COM * on the label. We don't expect this to ever fail for 508*11042SErik.Nordmark@Sun.COM * loopback packets, so we silently drop the packet should it 509*11042SErik.Nordmark@Sun.COM * fail. 510*11042SErik.Nordmark@Sun.COM */ 511*11042SErik.Nordmark@Sun.COM if (!tsol_get_pkt_label(mp, IPV6_VERSION, &iras)) { 512*11042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards); 513*11042SErik.Nordmark@Sun.COM ip_drop_input("tsol_get_pkt_label", mp, ill); 514*11042SErik.Nordmark@Sun.COM freemsg(mp); 515*11042SErik.Nordmark@Sun.COM return (0); 516*11042SErik.Nordmark@Sun.COM } 517*11042SErik.Nordmark@Sun.COM ASSERT(iras.ira_tsl != NULL); 518*11042SErik.Nordmark@Sun.COM 519*11042SErik.Nordmark@Sun.COM /* tsol_get_pkt_label sometimes does pullupmsg */ 520*11042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr; 521*11042SErik.Nordmark@Sun.COM } 522*11042SErik.Nordmark@Sun.COM 523*11042SErik.Nordmark@Sun.COM ip_fanout_v6(mp, ip6h, &iras); 524*11042SErik.Nordmark@Sun.COM 525*11042SErik.Nordmark@Sun.COM /* We moved any IPsec refs from ixa to iras */ 526*11042SErik.Nordmark@Sun.COM ira_cleanup(&iras, B_FALSE); 527*11042SErik.Nordmark@Sun.COM return (0); 528*11042SErik.Nordmark@Sun.COM } 529*11042SErik.Nordmark@Sun.COM 530*11042SErik.Nordmark@Sun.COM static void 531*11042SErik.Nordmark@Sun.COM multirt_check_v6(ire_t *ire, ip6_t *ip6h, ip_xmit_attr_t *ixa) 532*11042SErik.Nordmark@Sun.COM { 533*11042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst; 534*11042SErik.Nordmark@Sun.COM 535*11042SErik.Nordmark@Sun.COM /* Limit the TTL on multirt packets. Do this even if IPV6_HOPLIMIT */ 536*11042SErik.Nordmark@Sun.COM if (ire->ire_type & IRE_MULTICAST) { 537*11042SErik.Nordmark@Sun.COM if (ip6h->ip6_hops > 1) { 538*11042SErik.Nordmark@Sun.COM ip2dbg(("ire_send_multirt_v6: forcing multicast " 539*11042SErik.Nordmark@Sun.COM "multirt TTL to 1 (was %d)\n", ip6h->ip6_hops)); 540*11042SErik.Nordmark@Sun.COM ip6h->ip6_hops = 1; 541*11042SErik.Nordmark@Sun.COM } 542*11042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_NO_TTL_CHANGE; 543*11042SErik.Nordmark@Sun.COM } else if ((ipst->ips_ip_multirt_ttl > 0) && 544*11042SErik.Nordmark@Sun.COM (ip6h->ip6_hops > ipst->ips_ip_multirt_ttl)) { 545*11042SErik.Nordmark@Sun.COM ip6h->ip6_hops = ipst->ips_ip_multirt_ttl; 546*11042SErik.Nordmark@Sun.COM /* 547*11042SErik.Nordmark@Sun.COM * Need to ensure we don't increase the ttl should we go through 548*11042SErik.Nordmark@Sun.COM * ire_send_multicast. 549*11042SErik.Nordmark@Sun.COM */ 550*11042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_NO_TTL_CHANGE; 551*11042SErik.Nordmark@Sun.COM } 552*11042SErik.Nordmark@Sun.COM 553*11042SErik.Nordmark@Sun.COM /* For IPv6 this also needs to insert a fragment header */ 554*11042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_IPV6_ADD_FRAGHDR; 555*11042SErik.Nordmark@Sun.COM } 556*11042SErik.Nordmark@Sun.COM 557*11042SErik.Nordmark@Sun.COM /* 558*11042SErik.Nordmark@Sun.COM * ire_sendfn for IRE_MULTICAST 559*11042SErik.Nordmark@Sun.COM * 560*11042SErik.Nordmark@Sun.COM * Note that we do path MTU discovery by default for IPv6 multicast. But 561*11042SErik.Nordmark@Sun.COM * since unconnected UDP and RAW sockets don't set IXAF_PMTU_DISCOVERY 562*11042SErik.Nordmark@Sun.COM * only connected sockets get this by default. 563*11042SErik.Nordmark@Sun.COM */ 564*11042SErik.Nordmark@Sun.COM int 565*11042SErik.Nordmark@Sun.COM ire_send_multicast_v6(ire_t *ire, mblk_t *mp, void *iph_arg, 566*11042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa, uint32_t *identp) 567*11042SErik.Nordmark@Sun.COM { 568*11042SErik.Nordmark@Sun.COM ip6_t *ip6h = (ip6_t *)iph_arg; 569*11042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst; 570*11042SErik.Nordmark@Sun.COM ill_t *ill = ire->ire_ill; 571*11042SErik.Nordmark@Sun.COM iaflags_t ixaflags = ixa->ixa_flags; 572*11042SErik.Nordmark@Sun.COM 573*11042SErik.Nordmark@Sun.COM /* 574*11042SErik.Nordmark@Sun.COM * The IRE_MULTICAST is the same whether or not multirt is in use. 575*11042SErik.Nordmark@Sun.COM * Hence we need special-case code. 576*11042SErik.Nordmark@Sun.COM */ 577*11042SErik.Nordmark@Sun.COM if (ixaflags & IXAF_MULTIRT_MULTICAST) 578*11042SErik.Nordmark@Sun.COM multirt_check_v6(ire, ip6h, ixa); 579*11042SErik.Nordmark@Sun.COM 580*11042SErik.Nordmark@Sun.COM /* 581*11042SErik.Nordmark@Sun.COM * Check if anything in ip_input_v6 wants a copy of the transmitted 582*11042SErik.Nordmark@Sun.COM * packet (after IPsec and fragmentation) 583*11042SErik.Nordmark@Sun.COM * 584*11042SErik.Nordmark@Sun.COM * 1. Multicast routers always need a copy unless SO_DONTROUTE is set 585*11042SErik.Nordmark@Sun.COM * RSVP and the rsvp daemon is an example of a 586*11042SErik.Nordmark@Sun.COM * protocol and user level process that 587*11042SErik.Nordmark@Sun.COM * handles it's own routing. Hence, it uses the 588*11042SErik.Nordmark@Sun.COM * SO_DONTROUTE option to accomplish this. 589*11042SErik.Nordmark@Sun.COM * 2. If the sender has set IP_MULTICAST_LOOP, then we just 590*11042SErik.Nordmark@Sun.COM * check whether there are any receivers for the group on the ill 591*11042SErik.Nordmark@Sun.COM * (ignoring the zoneid). 592*11042SErik.Nordmark@Sun.COM * 3. If IP_MULTICAST_LOOP is not set, then we check if there are 593*11042SErik.Nordmark@Sun.COM * any members in other shared-IP zones. 594*11042SErik.Nordmark@Sun.COM * If such members exist, then we indicate that the sending zone 595*11042SErik.Nordmark@Sun.COM * shouldn't get a loopback copy to preserve the IP_MULTICAST_LOOP 596*11042SErik.Nordmark@Sun.COM * behavior. 597*11042SErik.Nordmark@Sun.COM * 598*11042SErik.Nordmark@Sun.COM * When we loopback we skip hardware checksum to make sure loopback 599*11042SErik.Nordmark@Sun.COM * copy is checksumed. 600*11042SErik.Nordmark@Sun.COM * 601*11042SErik.Nordmark@Sun.COM * Note that ire_ill is the upper in the case of IPMP. 602*11042SErik.Nordmark@Sun.COM */ 603*11042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~(IXAF_LOOPBACK_COPY | IXAF_NO_HW_CKSUM); 604*11042SErik.Nordmark@Sun.COM if (ipst->ips_ip_g_mrouter && ill->ill_mrouter_cnt > 0 && 605*11042SErik.Nordmark@Sun.COM !(ixaflags & IXAF_DONTROUTE)) { 606*11042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_LOOPBACK_COPY | IXAF_NO_HW_CKSUM; 607*11042SErik.Nordmark@Sun.COM } else if (ixaflags & IXAF_MULTICAST_LOOP) { 608*11042SErik.Nordmark@Sun.COM /* 609*11042SErik.Nordmark@Sun.COM * If this zone or any other zone has members then loopback 610*11042SErik.Nordmark@Sun.COM * a copy. 611*11042SErik.Nordmark@Sun.COM */ 612*11042SErik.Nordmark@Sun.COM if (ill_hasmembers_v6(ill, &ip6h->ip6_dst)) 613*11042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_LOOPBACK_COPY | IXAF_NO_HW_CKSUM; 614*11042SErik.Nordmark@Sun.COM } else if (ipst->ips_netstack->netstack_numzones > 1) { 615*11042SErik.Nordmark@Sun.COM /* 616*11042SErik.Nordmark@Sun.COM * This zone should not have a copy. But there are some other 617*11042SErik.Nordmark@Sun.COM * zones which might have members. 618*11042SErik.Nordmark@Sun.COM */ 619*11042SErik.Nordmark@Sun.COM if (ill_hasmembers_otherzones_v6(ill, &ip6h->ip6_dst, 620*11042SErik.Nordmark@Sun.COM ixa->ixa_zoneid)) { 621*11042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_NO_LOOP_ZONEID_SET; 622*11042SErik.Nordmark@Sun.COM ixa->ixa_no_loop_zoneid = ixa->ixa_zoneid; 623*11042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_LOOPBACK_COPY | IXAF_NO_HW_CKSUM; 624*11042SErik.Nordmark@Sun.COM } 625*11042SErik.Nordmark@Sun.COM } 626*11042SErik.Nordmark@Sun.COM 627*11042SErik.Nordmark@Sun.COM /* 628*11042SErik.Nordmark@Sun.COM * Unless IPV6_HOPLIMIT or ire_send_multirt_v6 already set a ttl, 629*11042SErik.Nordmark@Sun.COM * force the ttl to the IP_MULTICAST_TTL value 630*11042SErik.Nordmark@Sun.COM */ 631*11042SErik.Nordmark@Sun.COM if (!(ixaflags & IXAF_NO_TTL_CHANGE)) { 632*11042SErik.Nordmark@Sun.COM ip6h->ip6_hops = ixa->ixa_multicast_ttl; 633*11042SErik.Nordmark@Sun.COM } 634*11042SErik.Nordmark@Sun.COM 635*11042SErik.Nordmark@Sun.COM return (ire_send_wire_v6(ire, mp, ip6h, ixa, identp)); 636*11042SErik.Nordmark@Sun.COM } 637*11042SErik.Nordmark@Sun.COM 638*11042SErik.Nordmark@Sun.COM /* 639*11042SErik.Nordmark@Sun.COM * ire_sendfn for IREs with RTF_MULTIRT 640*11042SErik.Nordmark@Sun.COM */ 641*11042SErik.Nordmark@Sun.COM int 642*11042SErik.Nordmark@Sun.COM ire_send_multirt_v6(ire_t *ire, mblk_t *mp, void *iph_arg, 643*11042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa, uint32_t *identp) 644*11042SErik.Nordmark@Sun.COM { 645*11042SErik.Nordmark@Sun.COM ip6_t *ip6h = (ip6_t *)iph_arg; 646*11042SErik.Nordmark@Sun.COM 647*11042SErik.Nordmark@Sun.COM multirt_check_v6(ire, ip6h, ixa); 648*11042SErik.Nordmark@Sun.COM 649*11042SErik.Nordmark@Sun.COM if (ire->ire_type & IRE_MULTICAST) 650*11042SErik.Nordmark@Sun.COM return (ire_send_multicast_v6(ire, mp, ip6h, ixa, identp)); 651*11042SErik.Nordmark@Sun.COM else 652*11042SErik.Nordmark@Sun.COM return (ire_send_wire_v6(ire, mp, ip6h, ixa, identp)); 653*11042SErik.Nordmark@Sun.COM } 654*11042SErik.Nordmark@Sun.COM 655*11042SErik.Nordmark@Sun.COM /* 656*11042SErik.Nordmark@Sun.COM * ire_sendfn for IREs with RTF_REJECT/RTF_BLACKHOLE, including IRE_NOROUTE 657*11042SErik.Nordmark@Sun.COM */ 658*11042SErik.Nordmark@Sun.COM /* ARGSUSED4 */ 659*11042SErik.Nordmark@Sun.COM int 660*11042SErik.Nordmark@Sun.COM ire_send_noroute_v6(ire_t *ire, mblk_t *mp, void *iph_arg, 661*11042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa, uint32_t *identp) 662*11042SErik.Nordmark@Sun.COM { 663*11042SErik.Nordmark@Sun.COM ip6_t *ip6h = (ip6_t *)iph_arg; 664*11042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst; 665*11042SErik.Nordmark@Sun.COM ill_t *ill; 666*11042SErik.Nordmark@Sun.COM ip_recv_attr_t iras; 667*11042SErik.Nordmark@Sun.COM boolean_t dummy; 668*11042SErik.Nordmark@Sun.COM 669*11042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutNoRoutes); 670*11042SErik.Nordmark@Sun.COM 671*11042SErik.Nordmark@Sun.COM if (ire->ire_type & IRE_NOROUTE) { 672*11042SErik.Nordmark@Sun.COM /* A lack of a route as opposed to RTF_REJECT|BLACKHOLE */ 673*11042SErik.Nordmark@Sun.COM ip_rts_change_v6(RTM_MISS, &ip6h->ip6_dst, 0, 0, 0, 0, 0, 0, 674*11042SErik.Nordmark@Sun.COM RTA_DST, ipst); 675*11042SErik.Nordmark@Sun.COM } 676*11042SErik.Nordmark@Sun.COM 677*11042SErik.Nordmark@Sun.COM if (ire->ire_flags & RTF_BLACKHOLE) { 678*11042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutNoRoutes RTF_BLACKHOLE", mp, NULL); 679*11042SErik.Nordmark@Sun.COM freemsg(mp); 680*11042SErik.Nordmark@Sun.COM /* No error even for local senders - silent blackhole */ 681*11042SErik.Nordmark@Sun.COM return (0); 682*11042SErik.Nordmark@Sun.COM } 683*11042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutNoRoutes RTF_REJECT", mp, NULL); 684*11042SErik.Nordmark@Sun.COM 685*11042SErik.Nordmark@Sun.COM /* 686*11042SErik.Nordmark@Sun.COM * We need an ill_t for the ip_recv_attr_t even though this packet 687*11042SErik.Nordmark@Sun.COM * was never received and icmp_unreachable doesn't currently use 688*11042SErik.Nordmark@Sun.COM * ira_ill. 689*11042SErik.Nordmark@Sun.COM */ 690*11042SErik.Nordmark@Sun.COM ill = ill_lookup_on_name("lo0", B_FALSE, 691*11042SErik.Nordmark@Sun.COM !(ixa->ixa_flags & IRAF_IS_IPV4), &dummy, ipst); 692*11042SErik.Nordmark@Sun.COM if (ill == NULL) { 693*11042SErik.Nordmark@Sun.COM freemsg(mp); 694*11042SErik.Nordmark@Sun.COM return (EHOSTUNREACH); 695*11042SErik.Nordmark@Sun.COM } 696*11042SErik.Nordmark@Sun.COM 697*11042SErik.Nordmark@Sun.COM bzero(&iras, sizeof (iras)); 698*11042SErik.Nordmark@Sun.COM /* Map ixa to ira including IPsec policies */ 699*11042SErik.Nordmark@Sun.COM ipsec_out_to_in(ixa, ill, &iras); 700*11042SErik.Nordmark@Sun.COM 701*11042SErik.Nordmark@Sun.COM icmp_unreachable_v6(mp, ICMP6_DST_UNREACH_NOROUTE, B_FALSE, &iras); 702*11042SErik.Nordmark@Sun.COM /* We moved any IPsec refs from ixa to iras */ 703*11042SErik.Nordmark@Sun.COM ira_cleanup(&iras, B_FALSE); 704*11042SErik.Nordmark@Sun.COM 705*11042SErik.Nordmark@Sun.COM ill_refrele(ill); 706*11042SErik.Nordmark@Sun.COM return (EHOSTUNREACH); 707*11042SErik.Nordmark@Sun.COM } 708*11042SErik.Nordmark@Sun.COM 709*11042SErik.Nordmark@Sun.COM /* 710*11042SErik.Nordmark@Sun.COM * Calculate a checksum ignoring any hardware capabilities 711*11042SErik.Nordmark@Sun.COM * 712*11042SErik.Nordmark@Sun.COM * Returns B_FALSE if the packet was too short for the checksum. Caller 713*11042SErik.Nordmark@Sun.COM * should free and do stats. 714*11042SErik.Nordmark@Sun.COM */ 715*11042SErik.Nordmark@Sun.COM static boolean_t 716*11042SErik.Nordmark@Sun.COM ip_output_sw_cksum_v6(mblk_t *mp, ip6_t *ip6h, ip_xmit_attr_t *ixa) 717*11042SErik.Nordmark@Sun.COM { 718*11042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst; 719*11042SErik.Nordmark@Sun.COM uint_t pktlen = ixa->ixa_pktlen; 720*11042SErik.Nordmark@Sun.COM uint16_t *cksump; 721*11042SErik.Nordmark@Sun.COM uint32_t cksum; 722*11042SErik.Nordmark@Sun.COM uint8_t protocol = ixa->ixa_protocol; 723*11042SErik.Nordmark@Sun.COM uint16_t ip_hdr_length = ixa->ixa_ip_hdr_length; 724*11042SErik.Nordmark@Sun.COM 725*11042SErik.Nordmark@Sun.COM #define iphs ((uint16_t *)ip6h) 726*11042SErik.Nordmark@Sun.COM 727*11042SErik.Nordmark@Sun.COM /* Just in case it contained garbage */ 728*11042SErik.Nordmark@Sun.COM DB_CKSUMFLAGS(mp) &= ~HCK_FLAGS; 729*11042SErik.Nordmark@Sun.COM 730*11042SErik.Nordmark@Sun.COM /* 731*11042SErik.Nordmark@Sun.COM * Calculate ULP checksum 732*11042SErik.Nordmark@Sun.COM */ 733*11042SErik.Nordmark@Sun.COM if (protocol == IPPROTO_TCP) { 734*11042SErik.Nordmark@Sun.COM cksump = IPH_TCPH_CHECKSUMP(ip6h, ip_hdr_length); 735*11042SErik.Nordmark@Sun.COM cksum = IP_TCP_CSUM_COMP; 736*11042SErik.Nordmark@Sun.COM } else if (protocol == IPPROTO_UDP) { 737*11042SErik.Nordmark@Sun.COM cksump = IPH_UDPH_CHECKSUMP(ip6h, ip_hdr_length); 738*11042SErik.Nordmark@Sun.COM cksum = IP_UDP_CSUM_COMP; 739*11042SErik.Nordmark@Sun.COM } else if (protocol == IPPROTO_SCTP) { 740*11042SErik.Nordmark@Sun.COM sctp_hdr_t *sctph; 741*11042SErik.Nordmark@Sun.COM 742*11042SErik.Nordmark@Sun.COM ASSERT(MBLKL(mp) >= (ip_hdr_length + sizeof (*sctph))); 743*11042SErik.Nordmark@Sun.COM sctph = (sctp_hdr_t *)(mp->b_rptr + ip_hdr_length); 744*11042SErik.Nordmark@Sun.COM /* 745*11042SErik.Nordmark@Sun.COM * Zero out the checksum field to ensure proper 746*11042SErik.Nordmark@Sun.COM * checksum calculation. 747*11042SErik.Nordmark@Sun.COM */ 748*11042SErik.Nordmark@Sun.COM sctph->sh_chksum = 0; 749*11042SErik.Nordmark@Sun.COM #ifdef DEBUG 750*11042SErik.Nordmark@Sun.COM if (!skip_sctp_cksum) 751*11042SErik.Nordmark@Sun.COM #endif 752*11042SErik.Nordmark@Sun.COM sctph->sh_chksum = sctp_cksum(mp, ip_hdr_length); 753*11042SErik.Nordmark@Sun.COM return (B_TRUE); 754*11042SErik.Nordmark@Sun.COM } else if (ixa->ixa_flags & IXAF_SET_RAW_CKSUM) { 755*11042SErik.Nordmark@Sun.COM /* 756*11042SErik.Nordmark@Sun.COM * icmp has placed length and routing 757*11042SErik.Nordmark@Sun.COM * header adjustment in the checksum field. 758*11042SErik.Nordmark@Sun.COM */ 759*11042SErik.Nordmark@Sun.COM cksump = (uint16_t *)(((uint8_t *)ip6h) + ip_hdr_length + 760*11042SErik.Nordmark@Sun.COM ixa->ixa_raw_cksum_offset); 761*11042SErik.Nordmark@Sun.COM cksum = htons(protocol); 762*11042SErik.Nordmark@Sun.COM } else if (protocol == IPPROTO_ICMPV6) { 763*11042SErik.Nordmark@Sun.COM cksump = IPH_ICMPV6_CHECKSUMP(ip6h, ip_hdr_length); 764*11042SErik.Nordmark@Sun.COM cksum = IP_ICMPV6_CSUM_COMP; /* Pseudo-header cksum */ 765*11042SErik.Nordmark@Sun.COM } else { 766*11042SErik.Nordmark@Sun.COM return (B_TRUE); 767*11042SErik.Nordmark@Sun.COM } 768*11042SErik.Nordmark@Sun.COM 769*11042SErik.Nordmark@Sun.COM /* ULP puts the checksum field is in the first mblk */ 770*11042SErik.Nordmark@Sun.COM ASSERT(((uchar_t *)cksump) + sizeof (uint16_t) <= mp->b_wptr); 771*11042SErik.Nordmark@Sun.COM 772*11042SErik.Nordmark@Sun.COM /* 773*11042SErik.Nordmark@Sun.COM * We accumulate the pseudo header checksum in cksum. 774*11042SErik.Nordmark@Sun.COM * This is pretty hairy code, so watch close. One 775*11042SErik.Nordmark@Sun.COM * thing to keep in mind is that UDP and TCP have 776*11042SErik.Nordmark@Sun.COM * stored their respective datagram lengths in their 777*11042SErik.Nordmark@Sun.COM * checksum fields. This lines things up real nice. 778*11042SErik.Nordmark@Sun.COM */ 779*11042SErik.Nordmark@Sun.COM cksum += iphs[4] + iphs[5] + iphs[6] + iphs[7] + 780*11042SErik.Nordmark@Sun.COM iphs[8] + iphs[9] + iphs[10] + iphs[11] + 781*11042SErik.Nordmark@Sun.COM iphs[12] + iphs[13] + iphs[14] + iphs[15] + 782*11042SErik.Nordmark@Sun.COM iphs[16] + iphs[17] + iphs[18] + iphs[19]; 783*11042SErik.Nordmark@Sun.COM cksum = IP_CSUM(mp, ip_hdr_length, cksum); 784*11042SErik.Nordmark@Sun.COM 785*11042SErik.Nordmark@Sun.COM /* 786*11042SErik.Nordmark@Sun.COM * For UDP/IPv6 a zero UDP checksum is not allowed. 787*11042SErik.Nordmark@Sun.COM * Change to 0xffff 788*11042SErik.Nordmark@Sun.COM */ 789*11042SErik.Nordmark@Sun.COM if (protocol == IPPROTO_UDP && cksum == 0) 790*11042SErik.Nordmark@Sun.COM *cksump = ~cksum; 791*11042SErik.Nordmark@Sun.COM else 792*11042SErik.Nordmark@Sun.COM *cksump = cksum; 793*11042SErik.Nordmark@Sun.COM 794*11042SErik.Nordmark@Sun.COM IP6_STAT(ipst, ip6_out_sw_cksum); 795*11042SErik.Nordmark@Sun.COM IP6_STAT_UPDATE(ipst, ip6_out_sw_cksum_bytes, pktlen); 796*11042SErik.Nordmark@Sun.COM 797*11042SErik.Nordmark@Sun.COM /* No IP header checksum for IPv6 */ 798*11042SErik.Nordmark@Sun.COM 799*11042SErik.Nordmark@Sun.COM return (B_TRUE); 800*11042SErik.Nordmark@Sun.COM #undef iphs 801*11042SErik.Nordmark@Sun.COM } 802*11042SErik.Nordmark@Sun.COM 803*11042SErik.Nordmark@Sun.COM /* There are drivers that can't do partial checksum for ICMPv6 */ 804*11042SErik.Nordmark@Sun.COM int nxge_cksum_workaround = 1; 805*11042SErik.Nordmark@Sun.COM 806*11042SErik.Nordmark@Sun.COM /* 807*11042SErik.Nordmark@Sun.COM * Calculate the ULP checksum - try to use hardware. 808*11042SErik.Nordmark@Sun.COM * In the case of MULTIRT or multicast the 809*11042SErik.Nordmark@Sun.COM * IXAF_NO_HW_CKSUM is set in which case we use software. 810*11042SErik.Nordmark@Sun.COM * 811*11042SErik.Nordmark@Sun.COM * Returns B_FALSE if the packet was too short for the checksum. Caller 812*11042SErik.Nordmark@Sun.COM * should free and do stats. 813*11042SErik.Nordmark@Sun.COM */ 814*11042SErik.Nordmark@Sun.COM static boolean_t 815*11042SErik.Nordmark@Sun.COM ip_output_cksum_v6(iaflags_t ixaflags, mblk_t *mp, ip6_t *ip6h, 816*11042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa, ill_t *ill) 817*11042SErik.Nordmark@Sun.COM { 818*11042SErik.Nordmark@Sun.COM uint_t pktlen = ixa->ixa_pktlen; 819*11042SErik.Nordmark@Sun.COM uint16_t *cksump; 820*11042SErik.Nordmark@Sun.COM uint16_t hck_flags; 821*11042SErik.Nordmark@Sun.COM uint32_t cksum; 822*11042SErik.Nordmark@Sun.COM uint8_t protocol = ixa->ixa_protocol; 823*11042SErik.Nordmark@Sun.COM uint16_t ip_hdr_length = ixa->ixa_ip_hdr_length; 824*11042SErik.Nordmark@Sun.COM 825*11042SErik.Nordmark@Sun.COM #define iphs ((uint16_t *)ip6h) 826*11042SErik.Nordmark@Sun.COM 827*11042SErik.Nordmark@Sun.COM if ((ixaflags & IXAF_NO_HW_CKSUM) || !ILL_HCKSUM_CAPABLE(ill) || 828*11042SErik.Nordmark@Sun.COM !dohwcksum) { 829*11042SErik.Nordmark@Sun.COM return (ip_output_sw_cksum_v6(mp, ip6h, ixa)); 830*11042SErik.Nordmark@Sun.COM } 831*11042SErik.Nordmark@Sun.COM 832*11042SErik.Nordmark@Sun.COM /* 833*11042SErik.Nordmark@Sun.COM * Calculate ULP checksum. Note that we don't use cksump and cksum 834*11042SErik.Nordmark@Sun.COM * if the ill has FULL support. 835*11042SErik.Nordmark@Sun.COM */ 836*11042SErik.Nordmark@Sun.COM if (protocol == IPPROTO_TCP) { 837*11042SErik.Nordmark@Sun.COM cksump = IPH_TCPH_CHECKSUMP(ip6h, ip_hdr_length); 838*11042SErik.Nordmark@Sun.COM cksum = IP_TCP_CSUM_COMP; /* Pseudo-header cksum */ 839*11042SErik.Nordmark@Sun.COM } else if (protocol == IPPROTO_UDP) { 840*11042SErik.Nordmark@Sun.COM cksump = IPH_UDPH_CHECKSUMP(ip6h, ip_hdr_length); 841*11042SErik.Nordmark@Sun.COM cksum = IP_UDP_CSUM_COMP; /* Pseudo-header cksum */ 842*11042SErik.Nordmark@Sun.COM } else if (protocol == IPPROTO_SCTP) { 843*11042SErik.Nordmark@Sun.COM sctp_hdr_t *sctph; 844*11042SErik.Nordmark@Sun.COM 845*11042SErik.Nordmark@Sun.COM ASSERT(MBLKL(mp) >= (ip_hdr_length + sizeof (*sctph))); 846*11042SErik.Nordmark@Sun.COM sctph = (sctp_hdr_t *)(mp->b_rptr + ip_hdr_length); 847*11042SErik.Nordmark@Sun.COM /* 848*11042SErik.Nordmark@Sun.COM * Zero out the checksum field to ensure proper 849*11042SErik.Nordmark@Sun.COM * checksum calculation. 850*11042SErik.Nordmark@Sun.COM */ 851*11042SErik.Nordmark@Sun.COM sctph->sh_chksum = 0; 852*11042SErik.Nordmark@Sun.COM #ifdef DEBUG 853*11042SErik.Nordmark@Sun.COM if (!skip_sctp_cksum) 854*11042SErik.Nordmark@Sun.COM #endif 855*11042SErik.Nordmark@Sun.COM sctph->sh_chksum = sctp_cksum(mp, ip_hdr_length); 856*11042SErik.Nordmark@Sun.COM goto ip_hdr_cksum; 857*11042SErik.Nordmark@Sun.COM } else if (ixa->ixa_flags & IXAF_SET_RAW_CKSUM) { 858*11042SErik.Nordmark@Sun.COM /* 859*11042SErik.Nordmark@Sun.COM * icmp has placed length and routing 860*11042SErik.Nordmark@Sun.COM * header adjustment in the checksum field. 861*11042SErik.Nordmark@Sun.COM */ 862*11042SErik.Nordmark@Sun.COM cksump = (uint16_t *)(((uint8_t *)ip6h) + ip_hdr_length + 863*11042SErik.Nordmark@Sun.COM ixa->ixa_raw_cksum_offset); 864*11042SErik.Nordmark@Sun.COM cksum = htons(protocol); 865*11042SErik.Nordmark@Sun.COM } else if (protocol == IPPROTO_ICMPV6) { 866*11042SErik.Nordmark@Sun.COM cksump = IPH_ICMPV6_CHECKSUMP(ip6h, ip_hdr_length); 867*11042SErik.Nordmark@Sun.COM cksum = IP_ICMPV6_CSUM_COMP; /* Pseudo-header cksum */ 868*11042SErik.Nordmark@Sun.COM } else { 869*11042SErik.Nordmark@Sun.COM ip_hdr_cksum: 870*11042SErik.Nordmark@Sun.COM /* No IP header checksum for IPv6 */ 871*11042SErik.Nordmark@Sun.COM return (B_TRUE); 872*11042SErik.Nordmark@Sun.COM } 873*11042SErik.Nordmark@Sun.COM 874*11042SErik.Nordmark@Sun.COM /* ULP puts the checksum field is in the first mblk */ 875*11042SErik.Nordmark@Sun.COM ASSERT(((uchar_t *)cksump) + sizeof (uint16_t) <= mp->b_wptr); 876*11042SErik.Nordmark@Sun.COM 877*11042SErik.Nordmark@Sun.COM /* 878*11042SErik.Nordmark@Sun.COM * Underlying interface supports hardware checksum offload for 879*11042SErik.Nordmark@Sun.COM * the payload; leave the payload checksum for the hardware to 880*11042SErik.Nordmark@Sun.COM * calculate. N.B: We only need to set up checksum info on the 881*11042SErik.Nordmark@Sun.COM * first mblk. 882*11042SErik.Nordmark@Sun.COM */ 883*11042SErik.Nordmark@Sun.COM hck_flags = ill->ill_hcksum_capab->ill_hcksum_txflags; 884*11042SErik.Nordmark@Sun.COM 885*11042SErik.Nordmark@Sun.COM DB_CKSUMFLAGS(mp) &= ~HCK_FLAGS; 886*11042SErik.Nordmark@Sun.COM if (hck_flags & HCKSUM_INET_FULL_V6) { 887*11042SErik.Nordmark@Sun.COM /* 888*11042SErik.Nordmark@Sun.COM * Hardware calculates pseudo-header, header and the 889*11042SErik.Nordmark@Sun.COM * payload checksums, so clear the checksum field in 890*11042SErik.Nordmark@Sun.COM * the protocol header. 891*11042SErik.Nordmark@Sun.COM */ 892*11042SErik.Nordmark@Sun.COM *cksump = 0; 893*11042SErik.Nordmark@Sun.COM DB_CKSUMFLAGS(mp) |= HCK_FULLCKSUM; 894*11042SErik.Nordmark@Sun.COM return (B_TRUE); 895*11042SErik.Nordmark@Sun.COM } 896*11042SErik.Nordmark@Sun.COM if (((hck_flags) & HCKSUM_INET_PARTIAL) && 897*11042SErik.Nordmark@Sun.COM (protocol != IPPROTO_ICMPV6 || !nxge_cksum_workaround)) { 898*11042SErik.Nordmark@Sun.COM /* 899*11042SErik.Nordmark@Sun.COM * Partial checksum offload has been enabled. Fill 900*11042SErik.Nordmark@Sun.COM * the checksum field in the protocol header with the 901*11042SErik.Nordmark@Sun.COM * pseudo-header checksum value. 902*11042SErik.Nordmark@Sun.COM * 903*11042SErik.Nordmark@Sun.COM * We accumulate the pseudo header checksum in cksum. 904*11042SErik.Nordmark@Sun.COM * This is pretty hairy code, so watch close. One 905*11042SErik.Nordmark@Sun.COM * thing to keep in mind is that UDP and TCP have 906*11042SErik.Nordmark@Sun.COM * stored their respective datagram lengths in their 907*11042SErik.Nordmark@Sun.COM * checksum fields. This lines things up real nice. 908*11042SErik.Nordmark@Sun.COM */ 909*11042SErik.Nordmark@Sun.COM cksum += iphs[4] + iphs[5] + iphs[6] + iphs[7] + 910*11042SErik.Nordmark@Sun.COM iphs[8] + iphs[9] + iphs[10] + iphs[11] + 911*11042SErik.Nordmark@Sun.COM iphs[12] + iphs[13] + iphs[14] + iphs[15] + 912*11042SErik.Nordmark@Sun.COM iphs[16] + iphs[17] + iphs[18] + iphs[19]; 913*11042SErik.Nordmark@Sun.COM cksum += *(cksump); 914*11042SErik.Nordmark@Sun.COM cksum = (cksum & 0xFFFF) + (cksum >> 16); 915*11042SErik.Nordmark@Sun.COM *(cksump) = (cksum & 0xFFFF) + (cksum >> 16); 916*11042SErik.Nordmark@Sun.COM 917*11042SErik.Nordmark@Sun.COM /* 918*11042SErik.Nordmark@Sun.COM * Offsets are relative to beginning of IP header. 919*11042SErik.Nordmark@Sun.COM */ 920*11042SErik.Nordmark@Sun.COM DB_CKSUMSTART(mp) = ip_hdr_length; 921*11042SErik.Nordmark@Sun.COM DB_CKSUMSTUFF(mp) = (uint8_t *)cksump - (uint8_t *)ip6h; 922*11042SErik.Nordmark@Sun.COM DB_CKSUMEND(mp) = pktlen; 923*11042SErik.Nordmark@Sun.COM DB_CKSUMFLAGS(mp) |= HCK_PARTIALCKSUM; 924*11042SErik.Nordmark@Sun.COM return (B_TRUE); 925*11042SErik.Nordmark@Sun.COM } 926*11042SErik.Nordmark@Sun.COM /* Hardware capabilities include neither full nor partial IPv6 */ 927*11042SErik.Nordmark@Sun.COM return (ip_output_sw_cksum_v6(mp, ip6h, ixa)); 928*11042SErik.Nordmark@Sun.COM #undef iphs 929*11042SErik.Nordmark@Sun.COM } 930*11042SErik.Nordmark@Sun.COM 931*11042SErik.Nordmark@Sun.COM /* 932*11042SErik.Nordmark@Sun.COM * ire_sendfn for offlink and onlink destinations. 933*11042SErik.Nordmark@Sun.COM * Also called from the multicast, and multirt send functions. 934*11042SErik.Nordmark@Sun.COM * 935*11042SErik.Nordmark@Sun.COM * Assumes that the caller has a hold on the ire. 936*11042SErik.Nordmark@Sun.COM * 937*11042SErik.Nordmark@Sun.COM * This function doesn't care if the IRE just became condemned since that 938*11042SErik.Nordmark@Sun.COM * can happen at any time. 939*11042SErik.Nordmark@Sun.COM */ 940*11042SErik.Nordmark@Sun.COM /* ARGSUSED */ 941*11042SErik.Nordmark@Sun.COM int 942*11042SErik.Nordmark@Sun.COM ire_send_wire_v6(ire_t *ire, mblk_t *mp, void *iph_arg, 943*11042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa, uint32_t *identp) 944*11042SErik.Nordmark@Sun.COM { 945*11042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst; 946*11042SErik.Nordmark@Sun.COM ip6_t *ip6h = (ip6_t *)iph_arg; 947*11042SErik.Nordmark@Sun.COM iaflags_t ixaflags = ixa->ixa_flags; 948*11042SErik.Nordmark@Sun.COM ill_t *ill; 949*11042SErik.Nordmark@Sun.COM uint32_t pktlen = ixa->ixa_pktlen; 950*11042SErik.Nordmark@Sun.COM 951*11042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_nce != NULL); 952*11042SErik.Nordmark@Sun.COM ill = ixa->ixa_nce->nce_ill; 953*11042SErik.Nordmark@Sun.COM 954*11042SErik.Nordmark@Sun.COM /* 955*11042SErik.Nordmark@Sun.COM * Update output mib stats. Note that we can't move into the icmp 956*11042SErik.Nordmark@Sun.COM * sender (icmp_output etc) since they don't know the ill and the 957*11042SErik.Nordmark@Sun.COM * stats are per ill. 958*11042SErik.Nordmark@Sun.COM * 959*11042SErik.Nordmark@Sun.COM * With IPMP we record the stats on the upper ill. 960*11042SErik.Nordmark@Sun.COM */ 961*11042SErik.Nordmark@Sun.COM if (ixa->ixa_protocol == IPPROTO_ICMPV6) { 962*11042SErik.Nordmark@Sun.COM icmp6_t *icmp6; 963*11042SErik.Nordmark@Sun.COM 964*11042SErik.Nordmark@Sun.COM icmp6 = (icmp6_t *)((uchar_t *)ip6h + ixa->ixa_ip_hdr_length); 965*11042SErik.Nordmark@Sun.COM icmp_update_out_mib_v6(ixa->ixa_nce->nce_common->ncec_ill, 966*11042SErik.Nordmark@Sun.COM icmp6); 967*11042SErik.Nordmark@Sun.COM } 968*11042SErik.Nordmark@Sun.COM 969*11042SErik.Nordmark@Sun.COM if (ixaflags & IXAF_DONTROUTE) 970*11042SErik.Nordmark@Sun.COM ip6h->ip6_hops = 1; 971*11042SErik.Nordmark@Sun.COM 972*11042SErik.Nordmark@Sun.COM /* 973*11042SErik.Nordmark@Sun.COM * This might set b_band, thus the IPsec and fragmentation 974*11042SErik.Nordmark@Sun.COM * code in IP ensures that b_band is updated in the first mblk. 975*11042SErik.Nordmark@Sun.COM */ 976*11042SErik.Nordmark@Sun.COM if (IPP_ENABLED(IPP_LOCAL_OUT, ipst)) { 977*11042SErik.Nordmark@Sun.COM /* ip_process translates an IS_UNDER_IPMP */ 978*11042SErik.Nordmark@Sun.COM mp = ip_process(IPP_LOCAL_OUT, mp, ill, ill); 979*11042SErik.Nordmark@Sun.COM if (mp == NULL) { 980*11042SErik.Nordmark@Sun.COM /* ip_drop_packet and MIB done */ 981*11042SErik.Nordmark@Sun.COM return (0); /* Might just be delayed */ 982*11042SErik.Nordmark@Sun.COM } 983*11042SErik.Nordmark@Sun.COM } 984*11042SErik.Nordmark@Sun.COM 985*11042SErik.Nordmark@Sun.COM /* 986*11042SErik.Nordmark@Sun.COM * To handle IPsec/iptun's labeling needs we need to tag packets 987*11042SErik.Nordmark@Sun.COM * while we still have ixa_tsl 988*11042SErik.Nordmark@Sun.COM */ 989*11042SErik.Nordmark@Sun.COM if (is_system_labeled() && ixa->ixa_tsl != NULL && 990*11042SErik.Nordmark@Sun.COM (ill->ill_mactype == DL_6TO4 || ill->ill_mactype == DL_IPV4 || 991*11042SErik.Nordmark@Sun.COM ill->ill_mactype == DL_IPV6)) { 992*11042SErik.Nordmark@Sun.COM cred_t *newcr; 993*11042SErik.Nordmark@Sun.COM 994*11042SErik.Nordmark@Sun.COM newcr = copycred_from_tslabel(ixa->ixa_cred, ixa->ixa_tsl, 995*11042SErik.Nordmark@Sun.COM KM_NOSLEEP); 996*11042SErik.Nordmark@Sun.COM if (newcr == NULL) { 997*11042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards); 998*11042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards - newcr", 999*11042SErik.Nordmark@Sun.COM mp, ill); 1000*11042SErik.Nordmark@Sun.COM freemsg(mp); 1001*11042SErik.Nordmark@Sun.COM return (ENOBUFS); 1002*11042SErik.Nordmark@Sun.COM } 1003*11042SErik.Nordmark@Sun.COM mblk_setcred(mp, newcr, NOPID); 1004*11042SErik.Nordmark@Sun.COM crfree(newcr); /* mblk_setcred did its own crhold */ 1005*11042SErik.Nordmark@Sun.COM } 1006*11042SErik.Nordmark@Sun.COM 1007*11042SErik.Nordmark@Sun.COM /* 1008*11042SErik.Nordmark@Sun.COM * IXAF_IPV6_ADD_FRAGHDR is set for CGTP so that we will add a 1009*11042SErik.Nordmark@Sun.COM * fragment header without fragmenting. CGTP on the receiver will 1010*11042SErik.Nordmark@Sun.COM * filter duplicates on the ident field. 1011*11042SErik.Nordmark@Sun.COM */ 1012*11042SErik.Nordmark@Sun.COM if (pktlen > ixa->ixa_fragsize || 1013*11042SErik.Nordmark@Sun.COM (ixaflags & (IXAF_IPSEC_SECURE|IXAF_IPV6_ADD_FRAGHDR))) { 1014*11042SErik.Nordmark@Sun.COM uint32_t ident; 1015*11042SErik.Nordmark@Sun.COM 1016*11042SErik.Nordmark@Sun.COM if (ixaflags & IXAF_IPSEC_SECURE) 1017*11042SErik.Nordmark@Sun.COM pktlen += ipsec_out_extra_length(ixa); 1018*11042SErik.Nordmark@Sun.COM 1019*11042SErik.Nordmark@Sun.COM if (pktlen > IP_MAXPACKET) 1020*11042SErik.Nordmark@Sun.COM return (EMSGSIZE); 1021*11042SErik.Nordmark@Sun.COM 1022*11042SErik.Nordmark@Sun.COM if (ixaflags & IXAF_SET_ULP_CKSUM) { 1023*11042SErik.Nordmark@Sun.COM /* 1024*11042SErik.Nordmark@Sun.COM * Compute ULP checksum using software 1025*11042SErik.Nordmark@Sun.COM */ 1026*11042SErik.Nordmark@Sun.COM if (!ip_output_sw_cksum_v6(mp, ip6h, ixa)) { 1027*11042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards); 1028*11042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards", mp, ill); 1029*11042SErik.Nordmark@Sun.COM freemsg(mp); 1030*11042SErik.Nordmark@Sun.COM return (EINVAL); 1031*11042SErik.Nordmark@Sun.COM } 1032*11042SErik.Nordmark@Sun.COM /* Avoid checksum again below if we only add fraghdr */ 1033*11042SErik.Nordmark@Sun.COM ixaflags &= ~IXAF_SET_ULP_CKSUM; 1034*11042SErik.Nordmark@Sun.COM } 1035*11042SErik.Nordmark@Sun.COM 1036*11042SErik.Nordmark@Sun.COM /* 1037*11042SErik.Nordmark@Sun.COM * If we need a fragment header, pick the ident and insert 1038*11042SErik.Nordmark@Sun.COM * the header before IPsec to we have a place to store 1039*11042SErik.Nordmark@Sun.COM * the ident value. 1040*11042SErik.Nordmark@Sun.COM */ 1041*11042SErik.Nordmark@Sun.COM if ((ixaflags & IXAF_IPV6_ADD_FRAGHDR) || 1042*11042SErik.Nordmark@Sun.COM pktlen > ixa->ixa_fragsize) { 1043*11042SErik.Nordmark@Sun.COM /* 1044*11042SErik.Nordmark@Sun.COM * If this packet would generate a icmp_frag_needed 1045*11042SErik.Nordmark@Sun.COM * message, we need to handle it before we do the IPsec 1046*11042SErik.Nordmark@Sun.COM * processing. Otherwise, we need to strip the IPsec 1047*11042SErik.Nordmark@Sun.COM * headers before we send up the message to the ULPs 1048*11042SErik.Nordmark@Sun.COM * which becomes messy and difficult. 1049*11042SErik.Nordmark@Sun.COM */ 1050*11042SErik.Nordmark@Sun.COM if ((pktlen > ixa->ixa_fragsize) && 1051*11042SErik.Nordmark@Sun.COM (ixaflags & IXAF_DONTFRAG)) { 1052*11042SErik.Nordmark@Sun.COM /* Generate ICMP and return error */ 1053*11042SErik.Nordmark@Sun.COM ip_recv_attr_t iras; 1054*11042SErik.Nordmark@Sun.COM 1055*11042SErik.Nordmark@Sun.COM DTRACE_PROBE4(ip6__fragsize__fail, 1056*11042SErik.Nordmark@Sun.COM uint_t, pktlen, uint_t, ixa->ixa_fragsize, 1057*11042SErik.Nordmark@Sun.COM uint_t, ixa->ixa_pktlen, 1058*11042SErik.Nordmark@Sun.COM uint_t, ixa->ixa_pmtu); 1059*11042SErik.Nordmark@Sun.COM 1060*11042SErik.Nordmark@Sun.COM bzero(&iras, sizeof (iras)); 1061*11042SErik.Nordmark@Sun.COM /* Map ixa to ira including IPsec policies */ 1062*11042SErik.Nordmark@Sun.COM ipsec_out_to_in(ixa, ill, &iras); 1063*11042SErik.Nordmark@Sun.COM 1064*11042SErik.Nordmark@Sun.COM ip_drop_output("ICMP6_PKT_TOO_BIG", mp, ill); 1065*11042SErik.Nordmark@Sun.COM icmp_pkt2big_v6(mp, ixa->ixa_fragsize, B_TRUE, 1066*11042SErik.Nordmark@Sun.COM &iras); 1067*11042SErik.Nordmark@Sun.COM /* We moved any IPsec refs from ixa to iras */ 1068*11042SErik.Nordmark@Sun.COM ira_cleanup(&iras, B_FALSE); 1069*11042SErik.Nordmark@Sun.COM return (EMSGSIZE); 1070*11042SErik.Nordmark@Sun.COM } 1071*11042SErik.Nordmark@Sun.COM DTRACE_PROBE4(ip6__fragsize__ok, uint_t, pktlen, 1072*11042SErik.Nordmark@Sun.COM uint_t, ixa->ixa_fragsize, uint_t, ixa->ixa_pktlen, 1073*11042SErik.Nordmark@Sun.COM uint_t, ixa->ixa_pmtu); 1074*11042SErik.Nordmark@Sun.COM /* 1075*11042SErik.Nordmark@Sun.COM * Assign an ident value for this packet. There could 1076*11042SErik.Nordmark@Sun.COM * be other threads targeting the same destination, so 1077*11042SErik.Nordmark@Sun.COM * we have to arrange for a atomic increment. 1078*11042SErik.Nordmark@Sun.COM * Normally ixa_extra_ident is 0, but in the case of 1079*11042SErik.Nordmark@Sun.COM * LSO it will be the number of TCP segments that the 1080*11042SErik.Nordmark@Sun.COM * driver/hardware will extraly construct. 1081*11042SErik.Nordmark@Sun.COM * 1082*11042SErik.Nordmark@Sun.COM * Note that cl_inet_ipident has only been used for 1083*11042SErik.Nordmark@Sun.COM * IPv4. We don't use it here. 1084*11042SErik.Nordmark@Sun.COM */ 1085*11042SErik.Nordmark@Sun.COM ident = atomic_add_32_nv(identp, ixa->ixa_extra_ident + 1086*11042SErik.Nordmark@Sun.COM 1); 1087*11042SErik.Nordmark@Sun.COM #ifndef _BIG_ENDIAN 1088*11042SErik.Nordmark@Sun.COM ident = htonl(ident); 1089*11042SErik.Nordmark@Sun.COM #endif 1090*11042SErik.Nordmark@Sun.COM ixa->ixa_ident = ident; /* In case we do IPsec */ 1091*11042SErik.Nordmark@Sun.COM } 1092*11042SErik.Nordmark@Sun.COM if (ixaflags & IXAF_IPSEC_SECURE) { 1093*11042SErik.Nordmark@Sun.COM /* 1094*11042SErik.Nordmark@Sun.COM * Pass in sufficient information so that 1095*11042SErik.Nordmark@Sun.COM * IPsec can determine whether to fragment, and 1096*11042SErik.Nordmark@Sun.COM * which function to call after fragmentation. 1097*11042SErik.Nordmark@Sun.COM */ 1098*11042SErik.Nordmark@Sun.COM return (ipsec_out_process(mp, ixa)); 1099*11042SErik.Nordmark@Sun.COM } 1100*11042SErik.Nordmark@Sun.COM 1101*11042SErik.Nordmark@Sun.COM mp = ip_fraghdr_add_v6(mp, ident, ixa); 1102*11042SErik.Nordmark@Sun.COM if (mp == NULL) { 1103*11042SErik.Nordmark@Sun.COM /* MIB and ip_drop_output already done */ 1104*11042SErik.Nordmark@Sun.COM return (ENOMEM); 1105*11042SErik.Nordmark@Sun.COM } 1106*11042SErik.Nordmark@Sun.COM ASSERT(pktlen == ixa->ixa_pktlen); 1107*11042SErik.Nordmark@Sun.COM pktlen += sizeof (ip6_frag_t); 1108*11042SErik.Nordmark@Sun.COM 1109*11042SErik.Nordmark@Sun.COM if (pktlen > ixa->ixa_fragsize) { 1110*11042SErik.Nordmark@Sun.COM return (ip_fragment_v6(mp, ixa->ixa_nce, ixaflags, 1111*11042SErik.Nordmark@Sun.COM pktlen, ixa->ixa_fragsize, 1112*11042SErik.Nordmark@Sun.COM ixa->ixa_xmit_hint, ixa->ixa_zoneid, 1113*11042SErik.Nordmark@Sun.COM ixa->ixa_no_loop_zoneid, ixa->ixa_postfragfn, 1114*11042SErik.Nordmark@Sun.COM &ixa->ixa_cookie)); 1115*11042SErik.Nordmark@Sun.COM } 1116*11042SErik.Nordmark@Sun.COM } 1117*11042SErik.Nordmark@Sun.COM if (ixaflags & IXAF_SET_ULP_CKSUM) { 1118*11042SErik.Nordmark@Sun.COM /* Compute ULP checksum and IP header checksum */ 1119*11042SErik.Nordmark@Sun.COM /* An IS_UNDER_IPMP ill is ok here */ 1120*11042SErik.Nordmark@Sun.COM if (!ip_output_cksum_v6(ixaflags, mp, ip6h, ixa, ill)) { 1121*11042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards); 1122*11042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards", mp, ill); 1123*11042SErik.Nordmark@Sun.COM freemsg(mp); 1124*11042SErik.Nordmark@Sun.COM return (EINVAL); 1125*11042SErik.Nordmark@Sun.COM } 1126*11042SErik.Nordmark@Sun.COM } 1127*11042SErik.Nordmark@Sun.COM return ((ixa->ixa_postfragfn)(mp, ixa->ixa_nce, ixaflags, 1128*11042SErik.Nordmark@Sun.COM pktlen, ixa->ixa_xmit_hint, ixa->ixa_zoneid, 1129*11042SErik.Nordmark@Sun.COM ixa->ixa_no_loop_zoneid, &ixa->ixa_cookie)); 1130*11042SErik.Nordmark@Sun.COM } 1131*11042SErik.Nordmark@Sun.COM 1132*11042SErik.Nordmark@Sun.COM /* 1133*11042SErik.Nordmark@Sun.COM * Post fragmentation function for RTF_MULTIRT routes. 1134*11042SErik.Nordmark@Sun.COM * Since IRE_MULTICASTs might have RTF_MULTIRT, this function 1135*11042SErik.Nordmark@Sun.COM * checks IXAF_LOOPBACK_COPY. 1136*11042SErik.Nordmark@Sun.COM * 1137*11042SErik.Nordmark@Sun.COM * If no packet is sent due to failures then we return an errno, but if at 1138*11042SErik.Nordmark@Sun.COM * least one succeeded we return zero. 1139*11042SErik.Nordmark@Sun.COM */ 1140*11042SErik.Nordmark@Sun.COM int 1141*11042SErik.Nordmark@Sun.COM ip_postfrag_multirt_v6(mblk_t *mp, nce_t *nce, iaflags_t ixaflags, 1142*11042SErik.Nordmark@Sun.COM uint_t pkt_len, uint32_t xmit_hint, zoneid_t szone, zoneid_t nolzid, 1143*11042SErik.Nordmark@Sun.COM uintptr_t *ixacookie) 1144*11042SErik.Nordmark@Sun.COM { 1145*11042SErik.Nordmark@Sun.COM irb_t *irb; 1146*11042SErik.Nordmark@Sun.COM ip6_t *ip6h = (ip6_t *)mp->b_rptr; 1147*11042SErik.Nordmark@Sun.COM ire_t *ire; 1148*11042SErik.Nordmark@Sun.COM ire_t *ire1; 1149*11042SErik.Nordmark@Sun.COM mblk_t *mp1; 1150*11042SErik.Nordmark@Sun.COM nce_t *nce1; 1151*11042SErik.Nordmark@Sun.COM ill_t *ill = nce->nce_ill; 1152*11042SErik.Nordmark@Sun.COM ill_t *ill1; 1153*11042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst; 1154*11042SErik.Nordmark@Sun.COM int error = 0; 1155*11042SErik.Nordmark@Sun.COM int num_sent = 0; 1156*11042SErik.Nordmark@Sun.COM int err; 1157*11042SErik.Nordmark@Sun.COM uint_t ire_type; 1158*11042SErik.Nordmark@Sun.COM in6_addr_t nexthop; 1159*11042SErik.Nordmark@Sun.COM 1160*11042SErik.Nordmark@Sun.COM ASSERT(!(ixaflags & IXAF_IS_IPV4)); 1161*11042SErik.Nordmark@Sun.COM 1162*11042SErik.Nordmark@Sun.COM /* Check for IXAF_LOOPBACK_COPY */ 1163*11042SErik.Nordmark@Sun.COM if (ixaflags & IXAF_LOOPBACK_COPY) { 1164*11042SErik.Nordmark@Sun.COM mblk_t *mp1; 1165*11042SErik.Nordmark@Sun.COM 1166*11042SErik.Nordmark@Sun.COM mp1 = copymsg(mp); 1167*11042SErik.Nordmark@Sun.COM if (mp1 == NULL) { 1168*11042SErik.Nordmark@Sun.COM /* Failed to deliver the loopback copy. */ 1169*11042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards); 1170*11042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards", mp, ill); 1171*11042SErik.Nordmark@Sun.COM error = ENOBUFS; 1172*11042SErik.Nordmark@Sun.COM } else { 1173*11042SErik.Nordmark@Sun.COM ip_postfrag_loopback(mp1, nce, ixaflags, pkt_len, 1174*11042SErik.Nordmark@Sun.COM nolzid); 1175*11042SErik.Nordmark@Sun.COM } 1176*11042SErik.Nordmark@Sun.COM } 1177*11042SErik.Nordmark@Sun.COM 1178*11042SErik.Nordmark@Sun.COM /* 1179*11042SErik.Nordmark@Sun.COM * Loop over RTF_MULTIRT for ip6_dst in the same bucket. Send 1180*11042SErik.Nordmark@Sun.COM * a copy to each one. 1181*11042SErik.Nordmark@Sun.COM * Use the nce (nexthop) and ip6_dst to find the ire. 1182*11042SErik.Nordmark@Sun.COM * 1183*11042SErik.Nordmark@Sun.COM * MULTIRT is not designed to work with shared-IP zones thus we don't 1184*11042SErik.Nordmark@Sun.COM * need to pass a zoneid or a label to the IRE lookup. 1185*11042SErik.Nordmark@Sun.COM */ 1186*11042SErik.Nordmark@Sun.COM if (IN6_ARE_ADDR_EQUAL(&nce->nce_addr, &ip6h->ip6_dst)) { 1187*11042SErik.Nordmark@Sun.COM /* Broadcast and multicast case */ 1188*11042SErik.Nordmark@Sun.COM ire = ire_ftable_lookup_v6(&ip6h->ip6_dst, 0, 0, 0, NULL, 1189*11042SErik.Nordmark@Sun.COM ALL_ZONES, NULL, MATCH_IRE_DSTONLY, 0, ipst, NULL); 1190*11042SErik.Nordmark@Sun.COM } else { 1191*11042SErik.Nordmark@Sun.COM /* Unicast case */ 1192*11042SErik.Nordmark@Sun.COM ire = ire_ftable_lookup_v6(&ip6h->ip6_dst, 0, &nce->nce_addr, 1193*11042SErik.Nordmark@Sun.COM 0, NULL, ALL_ZONES, NULL, MATCH_IRE_GW, 0, ipst, NULL); 1194*11042SErik.Nordmark@Sun.COM } 1195*11042SErik.Nordmark@Sun.COM 1196*11042SErik.Nordmark@Sun.COM if (ire == NULL || 1197*11042SErik.Nordmark@Sun.COM (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) || 1198*11042SErik.Nordmark@Sun.COM !(ire->ire_flags & RTF_MULTIRT)) { 1199*11042SErik.Nordmark@Sun.COM /* Drop */ 1200*11042SErik.Nordmark@Sun.COM ip_drop_output("ip_postfrag_multirt didn't find route", 1201*11042SErik.Nordmark@Sun.COM mp, nce->nce_ill); 1202*11042SErik.Nordmark@Sun.COM if (ire != NULL) 1203*11042SErik.Nordmark@Sun.COM ire_refrele(ire); 1204*11042SErik.Nordmark@Sun.COM return (ENETUNREACH); 1205*11042SErik.Nordmark@Sun.COM } 1206*11042SErik.Nordmark@Sun.COM 1207*11042SErik.Nordmark@Sun.COM irb = ire->ire_bucket; 1208*11042SErik.Nordmark@Sun.COM irb_refhold(irb); 1209*11042SErik.Nordmark@Sun.COM for (ire1 = irb->irb_ire; ire1 != NULL; ire1 = ire1->ire_next) { 1210*11042SErik.Nordmark@Sun.COM if (IRE_IS_CONDEMNED(ire1) || 1211*11042SErik.Nordmark@Sun.COM !(ire1->ire_flags & RTF_MULTIRT)) 1212*11042SErik.Nordmark@Sun.COM continue; 1213*11042SErik.Nordmark@Sun.COM 1214*11042SErik.Nordmark@Sun.COM /* Note: When IPv6 uses radix tree we don't need this check */ 1215*11042SErik.Nordmark@Sun.COM if (!IN6_ARE_ADDR_EQUAL(&ire->ire_addr_v6, &ire1->ire_addr_v6)) 1216*11042SErik.Nordmark@Sun.COM continue; 1217*11042SErik.Nordmark@Sun.COM 1218*11042SErik.Nordmark@Sun.COM /* Do the ire argument one after the loop */ 1219*11042SErik.Nordmark@Sun.COM if (ire1 == ire) 1220*11042SErik.Nordmark@Sun.COM continue; 1221*11042SErik.Nordmark@Sun.COM 1222*11042SErik.Nordmark@Sun.COM ill1 = ire_nexthop_ill(ire1); 1223*11042SErik.Nordmark@Sun.COM if (ill1 == NULL) { 1224*11042SErik.Nordmark@Sun.COM /* 1225*11042SErik.Nordmark@Sun.COM * This ire might not have been picked by 1226*11042SErik.Nordmark@Sun.COM * ire_route_recursive, in which case ire_dep might 1227*11042SErik.Nordmark@Sun.COM * not have been setup yet. 1228*11042SErik.Nordmark@Sun.COM * We kick ire_route_recursive to try to resolve 1229*11042SErik.Nordmark@Sun.COM * starting at ire1. 1230*11042SErik.Nordmark@Sun.COM */ 1231*11042SErik.Nordmark@Sun.COM ire_t *ire2; 1232*11042SErik.Nordmark@Sun.COM 1233*11042SErik.Nordmark@Sun.COM ire2 = ire_route_recursive_impl_v6(ire1, 1234*11042SErik.Nordmark@Sun.COM &ire1->ire_addr_v6, ire1->ire_type, ire1->ire_ill, 1235*11042SErik.Nordmark@Sun.COM ire1->ire_zoneid, NULL, MATCH_IRE_DSTONLY, 1236*11042SErik.Nordmark@Sun.COM B_TRUE, 0, ipst, NULL, NULL, NULL); 1237*11042SErik.Nordmark@Sun.COM if (ire2 != NULL) 1238*11042SErik.Nordmark@Sun.COM ire_refrele(ire2); 1239*11042SErik.Nordmark@Sun.COM ill1 = ire_nexthop_ill(ire1); 1240*11042SErik.Nordmark@Sun.COM } 1241*11042SErik.Nordmark@Sun.COM if (ill1 == NULL) { 1242*11042SErik.Nordmark@Sun.COM BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards); 1243*11042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards - no ill", 1244*11042SErik.Nordmark@Sun.COM mp, ill); 1245*11042SErik.Nordmark@Sun.COM error = ENETUNREACH; 1246*11042SErik.Nordmark@Sun.COM continue; 1247*11042SErik.Nordmark@Sun.COM } 1248*11042SErik.Nordmark@Sun.COM /* Pick the addr and type to use for ndp_nce_init */ 1249*11042SErik.Nordmark@Sun.COM if (nce->nce_common->ncec_flags & NCE_F_MCAST) { 1250*11042SErik.Nordmark@Sun.COM ire_type = IRE_MULTICAST; 1251*11042SErik.Nordmark@Sun.COM nexthop = ip6h->ip6_dst; 1252*11042SErik.Nordmark@Sun.COM } else { 1253*11042SErik.Nordmark@Sun.COM ire_type = ire1->ire_type; /* Doesn't matter */ 1254*11042SErik.Nordmark@Sun.COM nexthop = ire1->ire_gateway_addr_v6; 1255*11042SErik.Nordmark@Sun.COM } 1256*11042SErik.Nordmark@Sun.COM 1257*11042SErik.Nordmark@Sun.COM /* If IPMP meta or under, then we just drop */ 1258*11042SErik.Nordmark@Sun.COM if (ill1->ill_grp != NULL) { 1259*11042SErik.Nordmark@Sun.COM BUMP_MIB(ill1->ill_ip_mib, ipIfStatsOutDiscards); 1260*11042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards - IPMP", 1261*11042SErik.Nordmark@Sun.COM mp, ill1); 1262*11042SErik.Nordmark@Sun.COM ill_refrele(ill1); 1263*11042SErik.Nordmark@Sun.COM error = ENETUNREACH; 1264*11042SErik.Nordmark@Sun.COM continue; 1265*11042SErik.Nordmark@Sun.COM } 1266*11042SErik.Nordmark@Sun.COM 1267*11042SErik.Nordmark@Sun.COM nce1 = ndp_nce_init(ill1, &nexthop, ire_type); 1268*11042SErik.Nordmark@Sun.COM if (nce1 == NULL) { 1269*11042SErik.Nordmark@Sun.COM BUMP_MIB(ill1->ill_ip_mib, ipIfStatsOutDiscards); 1270*11042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards - no nce", 1271*11042SErik.Nordmark@Sun.COM mp, ill1); 1272*11042SErik.Nordmark@Sun.COM ill_refrele(ill1); 1273*11042SErik.Nordmark@Sun.COM error = ENOBUFS; 1274*11042SErik.Nordmark@Sun.COM continue; 1275*11042SErik.Nordmark@Sun.COM } 1276*11042SErik.Nordmark@Sun.COM mp1 = copymsg(mp); 1277*11042SErik.Nordmark@Sun.COM if (mp1 == NULL) { 1278*11042SErik.Nordmark@Sun.COM BUMP_MIB(ill1->ill_ip_mib, ipIfStatsOutDiscards); 1279*11042SErik.Nordmark@Sun.COM ip_drop_output("ipIfStatsOutDiscards", mp, ill1); 1280*11042SErik.Nordmark@Sun.COM nce_refrele(nce1); 1281*11042SErik.Nordmark@Sun.COM ill_refrele(ill1); 1282*11042SErik.Nordmark@Sun.COM error = ENOBUFS; 1283*11042SErik.Nordmark@Sun.COM continue; 1284*11042SErik.Nordmark@Sun.COM } 1285*11042SErik.Nordmark@Sun.COM /* Preserve HW checksum for this copy */ 1286*11042SErik.Nordmark@Sun.COM DB_CKSUMSTART(mp1) = DB_CKSUMSTART(mp); 1287*11042SErik.Nordmark@Sun.COM DB_CKSUMSTUFF(mp1) = DB_CKSUMSTUFF(mp); 1288*11042SErik.Nordmark@Sun.COM DB_CKSUMEND(mp1) = DB_CKSUMEND(mp); 1289*11042SErik.Nordmark@Sun.COM DB_CKSUMFLAGS(mp1) = DB_CKSUMFLAGS(mp); 1290*11042SErik.Nordmark@Sun.COM DB_LSOMSS(mp1) = DB_LSOMSS(mp); 1291*11042SErik.Nordmark@Sun.COM 1292*11042SErik.Nordmark@Sun.COM ire1->ire_ob_pkt_count++; 1293*11042SErik.Nordmark@Sun.COM err = ip_xmit(mp1, nce1, ixaflags, pkt_len, xmit_hint, szone, 1294*11042SErik.Nordmark@Sun.COM 0, ixacookie); 1295*11042SErik.Nordmark@Sun.COM if (err == 0) 1296*11042SErik.Nordmark@Sun.COM num_sent++; 1297*11042SErik.Nordmark@Sun.COM else 1298*11042SErik.Nordmark@Sun.COM error = err; 1299*11042SErik.Nordmark@Sun.COM nce_refrele(nce1); 1300*11042SErik.Nordmark@Sun.COM ill_refrele(ill1); 1301*11042SErik.Nordmark@Sun.COM } 1302*11042SErik.Nordmark@Sun.COM irb_refrele(irb); 1303*11042SErik.Nordmark@Sun.COM ire_refrele(ire); 1304*11042SErik.Nordmark@Sun.COM /* Finally, the main one */ 1305*11042SErik.Nordmark@Sun.COM err = ip_xmit(mp, nce, ixaflags, pkt_len, xmit_hint, szone, 0, 1306*11042SErik.Nordmark@Sun.COM ixacookie); 1307*11042SErik.Nordmark@Sun.COM if (err == 0) 1308*11042SErik.Nordmark@Sun.COM num_sent++; 1309*11042SErik.Nordmark@Sun.COM else 1310*11042SErik.Nordmark@Sun.COM error = err; 1311*11042SErik.Nordmark@Sun.COM if (num_sent > 0) 1312*11042SErik.Nordmark@Sun.COM return (0); 1313*11042SErik.Nordmark@Sun.COM else 1314*11042SErik.Nordmark@Sun.COM return (error); 1315*11042SErik.Nordmark@Sun.COM } 1316