#include <sys/types.h>
#include <sys/stream.h>
#include <sys/strsubr.h>
#include <sys/dlpi.h>
#include <sys/strsun.h>
#include <sys/zone.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>
#include <sys/atomic.h>
#include <sys/systm.h>
#include <sys/param.h>
#include <sys/kmem.h>
#include <sys/sdt.h>
#include <sys/socket.h>
#include <sys/mac.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/route.h>
#include <sys/sockio.h>
#include <netinet/in.h>
#include <net/if_dl.h>
#include <inet/common.h>
#include <inet/mi.h>
#include <inet/mib2.h>
#include <inet/nd.h>
#include <inet/arp.h>
#include <inet/snmpcom.h>
#include <inet/kstatcom.h>
#include <netinet/igmp_var.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/sctp.h>
#include <inet/ip.h>
#include <inet/ip_impl.h>
#include <inet/ip6.h>
#include <inet/ip6_asp.h>
#include <inet/tcp.h>
#include <inet/ip_multi.h>
#include <inet/ip_if.h>
#include <inet/ip_ire.h>
#include <inet/ip_ftable.h>
#include <inet/ip_rts.h>
#include <inet/optcom.h>
#include <inet/ip_ndp.h>
#include <inet/ip_listutils.h>
#include <netinet/igmp.h>
#include <netinet/ip_mroute.h>
#include <inet/ipp_common.h>
#include <net/pfkeyv2.h>
#include <inet/sadb.h>
#include <inet/ipsec_impl.h>
#include <inet/ipdrop.h>
#include <inet/ip_netinfo.h>
#include <sys/pattr.h>
#include <inet/ipclassifier.h>
#include <inet/sctp_ip.h>
#include <inet/sctp/sctp_impl.h>
#include <inet/udp_impl.h>
#include <sys/sunddi.h>
#include <sys/tsol/label.h>
#include <sys/tsol/tnet.h>
#include <sys/clock_impl.h>
#ifdef DEBUG
extern boolean_t skip_sctp_cksum;
#endif
static int ip_verify_nce(mblk_t *, ip_xmit_attr_t *);
static int ip_verify_dce(mblk_t *, ip_xmit_attr_t *);
static boolean_t ip_verify_lso(ill_t *, ip_xmit_attr_t *);
static boolean_t ip_verify_zcopy(ill_t *, ip_xmit_attr_t *);
static void ip_output_simple_broadcast(ip_xmit_attr_t *, mblk_t *);
int
conn_ip_output(mblk_t *mp, ip_xmit_attr_t *ixa)
{
iaflags_t ixaflags = ixa->ixa_flags;
ire_t *ire;
nce_t *nce;
dce_t *dce;
ill_t *ill;
ip_stack_t *ipst = ixa->ixa_ipst;
int error;
ASSERT(ixa->ixa_ire != NULL);
ASSERT(ixa->ixa_dce != NULL);
#ifdef DEBUG
ASSERT(ixa->ixa_curthread == NULL);
ixa->ixa_curthread = curthread;
#endif
ire = ixa->ixa_ire;
if (ixaflags & IXAF_REACH_CONF)
ire->ire_badcnt = 0;
if (ire->ire_generation != ixa->ixa_ire_generation) {
error = ip_verify_ire(mp, ixa);
if (error != 0) {
ip_drop_output("ipIfStatsOutDiscards - verify ire",
mp, NULL);
goto drop;
}
ire = ixa->ixa_ire;
ASSERT(ire != NULL);
if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
#ifdef DEBUG
ASSERT(ixa->ixa_curthread == curthread);
ixa->ixa_curthread = NULL;
#endif
ire->ire_ob_pkt_count++;
return ((ire->ire_sendfn)(ire, mp, mp->b_rptr, ixa,
&ipst->ips_dce_default->dce_ident));
}
}
ASSERT(ixa->ixa_nce != NULL);
nce = ixa->ixa_nce;
if (nce->nce_is_condemned) {
error = ip_verify_nce(mp, ixa);
switch (error) {
case 0:
break;
case ENOTSUP: {
mblk_t *nmp;
if ((nmp = copymsg(mp)) != NULL) {
freemsg(mp);
mp = nmp;
break;
}
}
default:
ip_drop_output("ipIfStatsOutDiscards - verify nce",
mp, NULL);
goto drop;
}
ire = ixa->ixa_ire;
ASSERT(ire != NULL);
if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
#ifdef DEBUG
ASSERT(ixa->ixa_curthread == curthread);
ixa->ixa_curthread = NULL;
#endif
ire->ire_ob_pkt_count++;
return ((ire->ire_sendfn)(ire, mp, mp->b_rptr,
ixa, &ipst->ips_dce_default->dce_ident));
}
ASSERT(ixa->ixa_nce != NULL);
nce = ixa->ixa_nce;
}
dce = ixa->ixa_dce;
if (dce->dce_flags & DCEF_PMTU) {
int64_t now = LBOLT_FASTPATH64;
if ((TICK_TO_SEC(now) - dce->dce_last_change_time >
ipst->ips_ip_pathmtu_interval)) {
mutex_enter(&dce->dce_lock);
dce->dce_flags &= ~(DCEF_PMTU|DCEF_TOO_SMALL_PMTU);
dce->dce_last_change_time = TICK_TO_SEC(now);
mutex_exit(&dce->dce_lock);
dce_increment_generation(dce);
}
}
if (dce->dce_generation != ixa->ixa_dce_generation) {
error = ip_verify_dce(mp, ixa);
if (error != 0) {
ip_drop_output("ipIfStatsOutDiscards - verify dce",
mp, NULL);
goto drop;
}
dce = ixa->ixa_dce;
}
ill = nce->nce_ill;
if ((ixaflags & IXAF_VERIFY_SOURCE) &&
ixa->ixa_src_generation != ipst->ips_src_generation) {
uint_t gen;
if (!ip_verify_src(mp, ixa, &gen)) {
error = EADDRNOTAVAIL;
ip_drop_output("ipIfStatsOutDiscards - invalid src",
mp, NULL);
goto drop;
}
ixa->ixa_src_generation = gen;
}
ire->ire_ob_pkt_count++;
BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCOutRequests);
#ifdef DEBUG
ASSERT(ixa->ixa_curthread == curthread);
ixa->ixa_curthread = NULL;
#endif
return ((ire->ire_sendfn)(ire, mp, mp->b_rptr, ixa, &dce->dce_ident));
drop:
if (ixaflags & IXAF_IS_IPV4) {
BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsHCOutRequests);
BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards);
} else {
BUMP_MIB(&ipst->ips_ip6_mib, ipIfStatsHCOutRequests);
BUMP_MIB(&ipst->ips_ip6_mib, ipIfStatsOutDiscards);
}
freemsg(mp);
#ifdef DEBUG
ASSERT(ixa->ixa_curthread == curthread);
ixa->ixa_curthread = NULL;
#endif
return (error);
}
boolean_t
ip_verify_src(mblk_t *mp, ip_xmit_attr_t *ixa, uint_t *generationp)
{
ip_stack_t *ipst = ixa->ixa_ipst;
if (generationp != NULL)
*generationp = ipst->ips_src_generation;
if (ixa->ixa_flags & IXAF_IS_IPV4) {
ipha_t *ipha = (ipha_t *)mp->b_rptr;
if (ipha->ipha_src == INADDR_ANY)
return (B_FALSE);
return (ip_laddr_verify_v4(ipha->ipha_src, ixa->ixa_zoneid,
ipst, B_FALSE) != IPVL_BAD);
} else {
ip6_t *ip6h = (ip6_t *)mp->b_rptr;
uint_t scopeid;
if (IN6_IS_ADDR_UNSPECIFIED(&ip6h->ip6_src))
return (B_FALSE);
if (ixa->ixa_flags & IXAF_SCOPEID_SET)
scopeid = ixa->ixa_scopeid;
else
scopeid = 0;
return (ip_laddr_verify_v6(&ip6h->ip6_src, ixa->ixa_zoneid,
ipst, B_FALSE, scopeid) != IPVL_BAD);
}
}
int
ip_verify_ire(mblk_t *mp, ip_xmit_attr_t *ixa)
{
uint_t gen;
ire_t *ire;
nce_t *nce;
int error;
boolean_t multirt = B_FALSE;
error = 0;
ire = ip_select_route_pkt(mp, ixa, &gen, &error, &multirt);
ASSERT(ire != NULL);
if (error != 0) {
ire_refrele(ire);
return (error);
}
if (ixa->ixa_ire != NULL)
ire_refrele_notr(ixa->ixa_ire);
#ifdef DEBUG
ire_refhold_notr(ire);
ire_refrele(ire);
#endif
ixa->ixa_ire = ire;
ixa->ixa_ire_generation = gen;
if (multirt) {
if (ixa->ixa_flags & IXAF_IS_IPV4)
ixa->ixa_postfragfn = ip_postfrag_multirt_v4;
else
ixa->ixa_postfragfn = ip_postfrag_multirt_v6;
ixa->ixa_flags |= IXAF_MULTIRT_MULTICAST;
} else {
ixa->ixa_postfragfn = ire->ire_postfragfn;
ixa->ixa_flags &= ~IXAF_MULTIRT_MULTICAST;
}
if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
ASSERT(ixa->ixa_ire_generation == IRE_GENERATION_VERIFY);
ixa->ixa_dce_generation = DCE_GENERATION_VERIFY;
return (0);
}
nce = ire_to_nce_pkt(ire, mp);
if (nce == NULL) {
ixa->ixa_ire_generation = IRE_GENERATION_VERIFY;
ixa->ixa_dce_generation = DCE_GENERATION_VERIFY;
return (ENOBUFS);
}
if (nce == ixa->ixa_nce) {
nce_refrele(nce);
return (0);
}
ixa->ixa_dce_generation = DCE_GENERATION_VERIFY;
if (ixa->ixa_nce != NULL)
nce_refrele(ixa->ixa_nce);
ixa->ixa_nce = nce;
return (0);
}
static int
ip_verify_nce(mblk_t *mp, ip_xmit_attr_t *ixa)
{
ire_t *ire = ixa->ixa_ire;
nce_t *nce;
int error = 0;
ipha_t *ipha = NULL;
ip6_t *ip6h = NULL;
if (ire->ire_ipversion == IPV4_VERSION)
ipha = (ipha_t *)mp->b_rptr;
else
ip6h = (ip6_t *)mp->b_rptr;
nce = ire_handle_condemned_nce(ixa->ixa_nce, ire, ipha, ip6h, B_TRUE);
if (nce == NULL) {
return (ip_verify_ire(mp, ixa));
}
if (ixa->ixa_flags & IXAF_VERIFY_LSO) {
if (!ip_verify_lso(nce->nce_ill, ixa)) {
ASSERT(ixa->ixa_notify != NULL);
ixa->ixa_notify(ixa->ixa_notify_cookie, ixa,
IXAN_LSO, 0);
error = ENOTSUP;
}
}
if (ixa->ixa_flags & IXAF_VERIFY_ZCOPY) {
if (!ip_verify_zcopy(nce->nce_ill, ixa)) {
ASSERT(ixa->ixa_notify != NULL);
ixa->ixa_notify(ixa->ixa_notify_cookie, ixa,
IXAN_ZCOPY, 0);
if ((ixa->ixa_flags & IXAF_ZCOPY_CAPAB) == 0)
error = ENOTSUP;
}
}
ixa->ixa_dce_generation = DCE_GENERATION_VERIFY;
nce_refrele(ixa->ixa_nce);
ixa->ixa_nce = nce;
return (error);
}
static int
ip_verify_dce(mblk_t *mp, ip_xmit_attr_t *ixa)
{
dce_t *dce;
uint_t gen;
uint_t pmtu;
dce = dce_lookup_pkt(mp, ixa, &gen);
ASSERT(dce != NULL);
dce_refrele_notr(ixa->ixa_dce);
#ifdef DEBUG
dce_refhold_notr(dce);
dce_refrele(dce);
#endif
ixa->ixa_dce = dce;
ixa->ixa_dce_generation = gen;
pmtu = ip_get_pmtu(ixa);
if (ixa->ixa_flags & IXAF_VERIFY_PMTU) {
if (ixa->ixa_pmtu != pmtu) {
uint_t oldmtu = ixa->ixa_pmtu;
DTRACE_PROBE2(verify_pmtu, uint32_t, pmtu,
uint32_t, ixa->ixa_pmtu);
ASSERT(ixa->ixa_notify != NULL);
ixa->ixa_notify(ixa->ixa_notify_cookie, ixa,
IXAN_PMTU, pmtu);
if (pmtu < oldmtu)
return (EMSGSIZE);
}
} else {
ixa->ixa_fragsize = pmtu;
}
return (0);
}
static boolean_t
ip_verify_lso(ill_t *ill, ip_xmit_attr_t *ixa)
{
ill_lso_capab_t *lsoc = &ixa->ixa_lso_capab;
ill_lso_capab_t *new_lsoc = ill->ill_lso_capab;
if (ixa->ixa_flags & IXAF_LSO_CAPAB) {
if (!dohwcksum ||
(ixa->ixa_flags & IXAF_IPSEC_SECURE) ||
(ixa->ixa_ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK)) ||
(ixa->ixa_ire->ire_flags & RTF_MULTIRT) ||
((ixa->ixa_flags & IXAF_IS_IPV4) ?
!ILL_LSO_TCP_IPV4_USABLE(ill) :
!ILL_LSO_TCP_IPV6_USABLE(ill))) {
ixa->ixa_flags &= ~IXAF_LSO_CAPAB;
return (B_FALSE);
}
if (lsoc->ill_lso_max_tcpv4 != new_lsoc->ill_lso_max_tcpv4 ||
lsoc->ill_lso_max_tcpv6 != new_lsoc->ill_lso_max_tcpv6) {
*lsoc = *new_lsoc;
return (B_FALSE);
}
} else {
if (dohwcksum &&
!(ixa->ixa_flags & IXAF_IPSEC_SECURE) &&
!(ixa->ixa_ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK)) &&
!(ixa->ixa_ire->ire_flags & RTF_MULTIRT) &&
((ixa->ixa_flags & IXAF_IS_IPV4) ?
ILL_LSO_TCP_IPV4_USABLE(ill) :
ILL_LSO_TCP_IPV6_USABLE(ill))) {
*lsoc = *new_lsoc;
ixa->ixa_flags |= IXAF_LSO_CAPAB;
return (B_FALSE);
}
}
return (B_TRUE);
}
static boolean_t
ip_verify_zcopy(ill_t *ill, ip_xmit_attr_t *ixa)
{
if (ixa->ixa_flags & IXAF_ZCOPY_CAPAB) {
if ((ixa->ixa_flags & IXAF_IPSEC_SECURE) ||
(ixa->ixa_ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK)) ||
(ixa->ixa_ire->ire_flags & RTF_MULTIRT) ||
!ILL_ZCOPY_USABLE(ill)) {
ixa->ixa_flags &= ~IXAF_ZCOPY_CAPAB;
return (B_FALSE);
}
} else {
if (!(ixa->ixa_flags & IXAF_IPSEC_SECURE) &&
!(ixa->ixa_ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK)) &&
!(ixa->ixa_ire->ire_flags & RTF_MULTIRT) &&
ILL_ZCOPY_USABLE(ill)) {
ixa->ixa_flags |= IXAF_ZCOPY_CAPAB;
return (B_FALSE);
}
}
return (B_TRUE);
}
int
ip_output_simple(mblk_t *mp, ip_xmit_attr_t *ixa)
{
ts_label_t *effective_tsl = NULL;
int err;
ASSERT(ixa->ixa_ipst != NULL);
if (is_system_labeled()) {
ip_stack_t *ipst = ixa->ixa_ipst;
if (ixa->ixa_flags & IXAF_IS_IPV4) {
err = tsol_check_label_v4(ixa->ixa_tsl, ixa->ixa_zoneid,
&mp, CONN_MAC_DEFAULT, B_FALSE, ixa->ixa_ipst,
&effective_tsl);
} else {
err = tsol_check_label_v6(ixa->ixa_tsl, ixa->ixa_zoneid,
&mp, CONN_MAC_DEFAULT, B_FALSE, ixa->ixa_ipst,
&effective_tsl);
}
if (err != 0) {
ip2dbg(("tsol_check: label check failed (%d)\n", err));
BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsHCOutRequests);
BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("tsol_check_label", mp, NULL);
freemsg(mp);
return (err);
}
if (effective_tsl != NULL) {
ip_xmit_attr_replace_tsl(ixa, effective_tsl);
}
}
if (ixa->ixa_flags & IXAF_IS_IPV4)
return (ip_output_simple_v4(mp, ixa));
else
return (ip_output_simple_v6(mp, ixa));
}
int
ip_output_simple_v4(mblk_t *mp, ip_xmit_attr_t *ixa)
{
ipha_t *ipha;
ipaddr_t firsthop;
ipaddr_t dst;
ire_t *ire;
ipaddr_t setsrc;
int error;
ill_t *ill = NULL;
dce_t *dce = NULL;
nce_t *nce;
iaflags_t ixaflags = ixa->ixa_flags;
ip_stack_t *ipst = ixa->ixa_ipst;
boolean_t repeat = B_FALSE;
boolean_t multirt = B_FALSE;
int64_t now;
ipha = (ipha_t *)mp->b_rptr;
ASSERT(IPH_HDR_VERSION(ipha) == IPV4_VERSION);
ASSERT(ixa->ixa_flags & IXAF_IS_IPV4);
ASSERT(ixa->ixa_nce == NULL);
ixa->ixa_pktlen = ntohs(ipha->ipha_length);
ASSERT(ixa->ixa_pktlen == msgdsize(mp));
ixa->ixa_ip_hdr_length = IPH_HDR_LENGTH(ipha);
ixa->ixa_protocol = ipha->ipha_protocol;
firsthop = ipha->ipha_dst;
dst = ip_get_dst(ipha);
repeat_ire:
error = 0;
setsrc = INADDR_ANY;
ire = ip_select_route_v4(firsthop, ipha->ipha_src, ixa, NULL,
&setsrc, &error, &multirt);
ASSERT(ire != NULL);
if (error != 0) {
BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsHCOutRequests);
BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards - select route", mp, NULL);
freemsg(mp);
goto done;
}
if (ire->ire_flags & (RTF_BLACKHOLE|RTF_REJECT)) {
if (ixaflags & IXAF_SET_SOURCE)
ipha->ipha_src = htonl(INADDR_LOOPBACK);
ixa->ixa_fragsize = IP_MAXPACKET;
ill = NULL;
nce = NULL;
ire->ire_ob_pkt_count++;
BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsHCOutRequests);
error = (ire->ire_sendfn)(ire, mp, ipha, ixa,
&ipst->ips_dce_default->dce_ident);
goto done;
}
nce = ire_to_nce(ire, ipha->ipha_dst, NULL);
if (nce == NULL) {
ip_drop_output("ire_to_nce", mp, ill);
freemsg(mp);
error = ENOBUFS;
goto done;
}
if (nce->nce_is_condemned) {
nce_t *nce1;
nce1 = ire_handle_condemned_nce(nce, ire, ipha, NULL, B_TRUE);
nce_refrele(nce);
if (nce1 == NULL) {
if (!repeat) {
repeat = B_TRUE;
ire_refrele(ire);
goto repeat_ire;
}
BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("No nce", mp, ill);
freemsg(mp);
error = ENOBUFS;
goto done;
}
nce = nce1;
}
if (multirt) {
ixa->ixa_postfragfn = ip_postfrag_multirt_v4;
ixa->ixa_flags |= IXAF_MULTIRT_MULTICAST;
} else {
ixa->ixa_postfragfn = ire->ire_postfragfn;
ixa->ixa_flags &= ~IXAF_MULTIRT_MULTICAST;
}
ASSERT(ixa->ixa_nce == NULL);
ixa->ixa_nce = nce;
dce = dce_lookup_v4(dst, ipst, NULL);
ASSERT(dce != NULL);
if (!(ixaflags & IXAF_PMTU_DISCOVERY)) {
ixa->ixa_fragsize = ip_get_base_mtu(nce->nce_ill, ire);
} else if (dce->dce_flags & DCEF_PMTU) {
now = ddi_get_lbolt64();
if (TICK_TO_SEC(now) - dce->dce_last_change_time >
ipst->ips_ip_pathmtu_interval) {
mutex_enter(&dce->dce_lock);
dce->dce_flags &= ~(DCEF_PMTU|DCEF_TOO_SMALL_PMTU);
dce->dce_last_change_time = TICK_TO_SEC(now);
mutex_exit(&dce->dce_lock);
dce_increment_generation(dce);
ixa->ixa_fragsize = ip_get_base_mtu(nce->nce_ill, ire);
} else {
uint_t fragsize;
fragsize = ip_get_base_mtu(nce->nce_ill, ire);
if (fragsize > dce->dce_pmtu)
fragsize = dce->dce_pmtu;
ixa->ixa_fragsize = fragsize;
}
} else {
ixa->ixa_fragsize = ip_get_base_mtu(nce->nce_ill, ire);
}
ill = ire_nexthop_ill(ire);
if (ixaflags & IXAF_SET_SOURCE) {
ipaddr_t src;
if (ill == NULL) {
src = htonl(INADDR_LOOPBACK);
error = 0;
} else {
error = ip_select_source_v4(ill, setsrc, dst,
ixa->ixa_multicast_ifaddr, ixa->ixa_zoneid, ipst,
&src, NULL, NULL);
}
if (error != 0) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCOutRequests);
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards - no source",
mp, ill);
freemsg(mp);
goto done;
}
ipha->ipha_src = src;
} else if (ixaflags & IXAF_VERIFY_SOURCE) {
if (!ip_verify_src(mp, ixa, NULL)) {
BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsHCOutRequests);
BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards - invalid source",
mp, ill);
freemsg(mp);
error = EADDRNOTAVAIL;
goto done;
}
}
if (!(ixaflags & (IXAF_NO_IPSEC|IXAF_IPSEC_SECURE))) {
ASSERT(ixa->ixa_ipsec_policy == NULL);
mp = ip_output_attach_policy(mp, ipha, NULL, NULL, ixa);
if (mp == NULL) {
return (EHOSTUNREACH);
}
}
if (ill != NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCOutRequests);
} else {
BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsHCOutRequests);
}
ire->ire_ob_pkt_count++;
error = (ire->ire_sendfn)(ire, mp, ipha, ixa, &dce->dce_ident);
done:
ire_refrele(ire);
if (dce != NULL)
dce_refrele(dce);
if (ill != NULL)
ill_refrele(ill);
if (ixa->ixa_nce != NULL)
nce_refrele(ixa->ixa_nce);
ixa->ixa_nce = NULL;
return (error);
}
int
ire_send_local_v4(ire_t *ire, mblk_t *mp, void *iph_arg,
ip_xmit_attr_t *ixa, uint32_t *identp)
{
ipha_t *ipha = (ipha_t *)iph_arg;
ip_stack_t *ipst = ixa->ixa_ipst;
ill_t *ill = ire->ire_ill;
ip_recv_attr_t iras;
uint_t pktlen = ixa->ixa_pktlen;
DTRACE_IP7(send, mblk_t *, mp, conn_t *, NULL, void_ip_t *,
ipha, __dtrace_ipsr_ill_t *, ill, ipha_t *, ipha, ip6_t *, NULL,
int, 1);
if (HOOKS4_INTERESTED_LOOPBACK_OUT(ipst)) {
int error = 0;
DTRACE_PROBE4(ip4__loopback__out__start, ill_t *, NULL,
ill_t *, ill, ipha_t *, ipha, mblk_t *, mp);
FW_HOOKS(ipst->ips_ip4_loopback_out_event,
ipst->ips_ipv4firewall_loopback_out,
NULL, ill, ipha, mp, mp, 0, ipst, error);
DTRACE_PROBE1(ip4__loopback__out__end, mblk_t *, mp);
if (mp == NULL)
return (error);
ipha = (ipha_t *)mp->b_rptr;
pktlen = ntohs(ipha->ipha_length);
}
if (ipst->ips_ip4_observe.he_interested) {
zoneid_t szone, dzone;
zoneid_t stackzoneid;
stackzoneid = netstackid_to_zoneid(
ipst->ips_netstack->netstack_stackid);
if (stackzoneid == GLOBAL_ZONEID) {
dzone = ire->ire_zoneid;
szone = ixa->ixa_zoneid;
} else {
szone = dzone = stackzoneid;
}
ipobs_hook(mp, IPOBS_HOOK_LOCAL, szone, dzone, ill, ipst);
}
ipst->ips_loopback_packets++;
ipsec_out_to_in(ixa, ill, &iras);
iras.ira_pktlen = pktlen;
iras.ira_ttl = ipha->ipha_ttl;
if (!IS_SIMPLE_IPH(ipha)) {
ip_output_local_options(ipha, ipst);
iras.ira_flags |= IRAF_IPV4_OPTIONS;
}
if (HOOKS4_INTERESTED_LOOPBACK_IN(ipst)) {
int error = 0;
DTRACE_PROBE4(ip4__loopback__in__start, ill_t *, ill,
ill_t *, NULL, ipha_t *, ipha, mblk_t *, mp);
FW_HOOKS(ipst->ips_ip4_loopback_in_event,
ipst->ips_ipv4firewall_loopback_in,
ill, NULL, ipha, mp, mp, 0, ipst, error);
DTRACE_PROBE1(ip4__loopback__in__end, mblk_t *, mp);
if (mp == NULL) {
ira_cleanup(&iras, B_FALSE);
return (error);
}
ipha = (ipha_t *)mp->b_rptr;
pktlen = iras.ira_pktlen = ntohs(ipha->ipha_length);
}
DTRACE_IP7(receive, mblk_t *, mp, conn_t *, NULL, void_ip_t *,
ipha, __dtrace_ipsr_ill_t *, ill, ipha_t *, ipha, ip6_t *, NULL,
int, 1);
ire->ire_ib_pkt_count++;
BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCInReceives);
UPDATE_MIB(ill->ill_ip_mib, ipIfStatsHCInOctets, pktlen);
iras.ira_zoneid = ire->ire_zoneid;
if (is_system_labeled()) {
iras.ira_flags |= IRAF_SYSTEM_LABELED;
if (!tsol_get_pkt_label(mp, IPV4_VERSION, &iras)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("tsol_get_pkt_label", mp, ill);
freemsg(mp);
return (0);
}
ASSERT(iras.ira_tsl != NULL);
ipha = (ipha_t *)mp->b_rptr;
}
ip_fanout_v4(mp, ipha, &iras);
ira_cleanup(&iras, B_FALSE);
return (0);
}
int
ire_send_broadcast_v4(ire_t *ire, mblk_t *mp, void *iph_arg,
ip_xmit_attr_t *ixa, uint32_t *identp)
{
ipha_t *ipha = (ipha_t *)iph_arg;
ip_stack_t *ipst = ixa->ixa_ipst;
irb_t *irb = ire->ire_bucket;
ire_t *ire1;
mblk_t *mp1;
ipha_t *ipha1;
iaflags_t ixaflags = ixa->ixa_flags;
nce_t *nce1, *nce_orig;
if (!(ixa->ixa_flags & IXAF_NO_TTL_CHANGE)) {
if (ixaflags & IXAF_BROADCAST_TTL_SET)
ipha->ipha_ttl = ixa->ixa_broadcast_ttl;
else
ipha->ipha_ttl = ipst->ips_ip_broadcast_ttl;
}
ixa->ixa_flags |= IXAF_LOOPBACK_COPY | IXAF_NO_HW_CKSUM;
if (irb->irb_ire_cnt == 1 || ixa->ixa_ifindex != 0)
return (ire_send_wire_v4(ire, mp, ipha, ixa, identp));
irb_refhold(irb);
for (ire1 = irb->irb_ire; ire1 != NULL; ire1 = ire1->ire_next) {
if (ire1 == ire)
continue;
ASSERT(ire1->ire_addr == ire->ire_addr);
if (!(ire1->ire_type & IRE_BROADCAST))
continue;
if (IRE_IS_CONDEMNED(ire1))
continue;
if (ixa->ixa_zoneid != ALL_ZONES &&
ire->ire_zoneid != ire1->ire_zoneid)
continue;
ASSERT(ire->ire_ill != ire1->ire_ill && ire1->ire_ill != NULL);
if (ire1->ire_flags & RTF_MULTIRT)
break;
if (IS_UNDER_IPMP(ire1->ire_ill))
continue;
mp1 = copymsg(mp);
if (mp1 == NULL) {
BUMP_MIB(ire1->ire_ill->ill_ip_mib,
ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards",
mp, ire1->ire_ill);
continue;
}
ipha1 = (ipha_t *)mp1->b_rptr;
if (ixa->ixa_flags & IXAF_SET_SOURCE) {
if (ixaflags & IXAF_IPSEC_GLOBAL_POLICY) {
ip_output_simple_broadcast(ixa, mp1);
continue;
}
if (ip_select_source_v4(ire1->ire_ill, INADDR_ANY,
ipha1->ipha_dst, INADDR_ANY, ixa->ixa_zoneid, ipst,
&ipha1->ipha_src, NULL, NULL) != 0) {
BUMP_MIB(ire1->ire_ill->ill_ip_mib,
ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards - select "
"broadcast source", mp1, ire1->ire_ill);
freemsg(mp1);
continue;
}
if (!(ixaflags & (IXAF_NO_IPSEC|IXAF_IPSEC_SECURE))) {
ASSERT(ixa->ixa_ipsec_policy == NULL);
mp1 = ip_output_attach_policy(mp1, ipha, NULL,
NULL, ixa);
if (mp1 == NULL) {
continue;
}
}
}
nce1 = arp_nce_init(ire1->ire_ill, ire1->ire_addr,
ire1->ire_type);
if (nce1 == NULL) {
BUMP_MIB(ire1->ire_ill->ill_ip_mib,
ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards - broadcast nce",
mp1, ire1->ire_ill);
freemsg(mp1);
continue;
}
nce_orig = ixa->ixa_nce;
ixa->ixa_nce = nce1;
ire_refhold(ire1);
(void) ire_send_wire_v4(ire1, mp1, ipha1, ixa, identp);
ire_refrele(ire1);
ixa->ixa_nce = nce_orig;
nce_refrele(nce1);
ixa->ixa_flags &= ~IXAF_LOOPBACK_COPY;
}
irb_refrele(irb);
if (IS_UNDER_IPMP(ire->ire_ill)) {
freemsg(mp);
return (0);
}
return (ire_send_wire_v4(ire, mp, ipha, ixa, identp));
}
static void
ip_output_simple_broadcast(ip_xmit_attr_t *ixa, mblk_t *mp)
{
ip_xmit_attr_t ixas;
bzero(&ixas, sizeof (ixas));
ixas.ixa_flags = IXAF_BASIC_SIMPLE_V4;
ixas.ixa_zoneid = ixa->ixa_zoneid;
ixas.ixa_ifindex = 0;
ixas.ixa_ipst = ixa->ixa_ipst;
ixas.ixa_cred = ixa->ixa_cred;
ixas.ixa_cpid = ixa->ixa_cpid;
ixas.ixa_tsl = ixa->ixa_tsl;
ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
(void) ip_output_simple(mp, &ixas);
ixa_cleanup(&ixas);
}
static void
multirt_check_v4(ire_t *ire, ipha_t *ipha, ip_xmit_attr_t *ixa)
{
ip_stack_t *ipst = ixa->ixa_ipst;
if (ire->ire_type & IRE_MULTICAST) {
if (ipha->ipha_ttl > 1) {
ip2dbg(("ire_send_multirt_v4: forcing multicast "
"multirt TTL to 1 (was %d), dst 0x%08x\n",
ipha->ipha_ttl, ntohl(ire->ire_addr)));
ipha->ipha_ttl = 1;
}
ixa->ixa_flags |= IXAF_NO_TTL_CHANGE;
} else if ((ipst->ips_ip_multirt_ttl > 0) &&
(ipha->ipha_ttl > ipst->ips_ip_multirt_ttl)) {
ipha->ipha_ttl = ipst->ips_ip_multirt_ttl;
ixa->ixa_flags |= IXAF_NO_TTL_CHANGE;
}
}
int
ire_send_multicast_v4(ire_t *ire, mblk_t *mp, void *iph_arg,
ip_xmit_attr_t *ixa, uint32_t *identp)
{
ipha_t *ipha = (ipha_t *)iph_arg;
ip_stack_t *ipst = ixa->ixa_ipst;
ill_t *ill = ire->ire_ill;
iaflags_t ixaflags = ixa->ixa_flags;
if (ixaflags & IXAF_MULTIRT_MULTICAST)
multirt_check_v4(ire, ipha, ixa);
ixa->ixa_flags &= ~(IXAF_LOOPBACK_COPY | IXAF_NO_HW_CKSUM);
if (ipst->ips_ip_g_mrouter && ill->ill_mrouter_cnt > 0 &&
!(ixaflags & IXAF_DONTROUTE)) {
ixa->ixa_flags |= IXAF_LOOPBACK_COPY | IXAF_NO_HW_CKSUM;
} else if (ixaflags & IXAF_MULTICAST_LOOP) {
if (ill_hasmembers_v4(ill, ipha->ipha_dst))
ixa->ixa_flags |= IXAF_LOOPBACK_COPY | IXAF_NO_HW_CKSUM;
} else if (ipst->ips_netstack->netstack_numzones > 1) {
if (ill_hasmembers_otherzones_v4(ill, ipha->ipha_dst,
ixa->ixa_zoneid)) {
ixa->ixa_flags |= IXAF_NO_LOOP_ZONEID_SET;
ixa->ixa_no_loop_zoneid = ixa->ixa_zoneid;
ixa->ixa_flags |= IXAF_LOOPBACK_COPY | IXAF_NO_HW_CKSUM;
}
}
if (!(ixaflags & IXAF_NO_TTL_CHANGE)) {
ipha->ipha_ttl = ixa->ixa_multicast_ttl;
}
return (ire_send_wire_v4(ire, mp, ipha, ixa, identp));
}
int
ire_send_multirt_v4(ire_t *ire, mblk_t *mp, void *iph_arg,
ip_xmit_attr_t *ixa, uint32_t *identp)
{
ipha_t *ipha = (ipha_t *)iph_arg;
multirt_check_v4(ire, ipha, ixa);
if (ire->ire_type & IRE_MULTICAST)
return (ire_send_multicast_v4(ire, mp, ipha, ixa, identp));
else if (ire->ire_type & IRE_BROADCAST)
return (ire_send_broadcast_v4(ire, mp, ipha, ixa, identp));
else
return (ire_send_wire_v4(ire, mp, ipha, ixa, identp));
}
int
ire_send_noroute_v4(ire_t *ire, mblk_t *mp, void *iph_arg,
ip_xmit_attr_t *ixa, uint32_t *identp)
{
ip_stack_t *ipst = ixa->ixa_ipst;
ipha_t *ipha = (ipha_t *)iph_arg;
ill_t *ill;
ip_recv_attr_t iras;
boolean_t dummy;
ipha->ipha_ident = atomic_inc_32_nv(identp);
BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutNoRoutes);
if (ire->ire_type & IRE_NOROUTE) {
ip_rts_change(RTM_MISS, ipha->ipha_dst, 0, 0, 0, 0, 0, 0,
RTA_DST, ipst);
}
if (ire->ire_flags & RTF_BLACKHOLE) {
ip_drop_output("ipIfStatsOutNoRoutes RTF_BLACKHOLE", mp, NULL);
freemsg(mp);
return (0);
}
ip_drop_output("ipIfStatsOutNoRoutes RTF_REJECT", mp, NULL);
ill = ill_lookup_on_name("lo0", B_FALSE,
!(ixa->ixa_flags & IRAF_IS_IPV4), &dummy, ipst);
if (ill == NULL) {
freemsg(mp);
return (EHOSTUNREACH);
}
bzero(&iras, sizeof (iras));
ipsec_out_to_in(ixa, ill, &iras);
if (ip_source_routed(ipha, ipst)) {
icmp_unreachable(mp, ICMP_SOURCE_ROUTE_FAILED, &iras);
} else {
icmp_unreachable(mp, ICMP_HOST_UNREACHABLE, &iras);
}
ira_cleanup(&iras, B_FALSE);
ill_refrele(ill);
return (EHOSTUNREACH);
}
static boolean_t
ip_output_sw_cksum_v4(mblk_t *mp, ipha_t *ipha, ip_xmit_attr_t *ixa)
{
ip_stack_t *ipst = ixa->ixa_ipst;
uint_t pktlen = ixa->ixa_pktlen;
uint16_t *cksump;
uint32_t cksum;
uint8_t protocol = ixa->ixa_protocol;
uint16_t ip_hdr_length = ixa->ixa_ip_hdr_length;
ipaddr_t dst = ipha->ipha_dst;
ipaddr_t src = ipha->ipha_src;
DB_CKSUMFLAGS(mp) &= ~HCK_FLAGS;
if (protocol == IPPROTO_TCP) {
cksump = IPH_TCPH_CHECKSUMP(ipha, ip_hdr_length);
cksum = IP_TCP_CSUM_COMP;
} else if (protocol == IPPROTO_UDP) {
cksump = IPH_UDPH_CHECKSUMP(ipha, ip_hdr_length);
cksum = IP_UDP_CSUM_COMP;
} else if (protocol == IPPROTO_SCTP) {
sctp_hdr_t *sctph;
ASSERT(MBLKL(mp) >= (ip_hdr_length + sizeof (*sctph)));
sctph = (sctp_hdr_t *)(mp->b_rptr + ip_hdr_length);
sctph->sh_chksum = 0;
#ifdef DEBUG
if (!skip_sctp_cksum)
#endif
sctph->sh_chksum = sctp_cksum(mp, ip_hdr_length);
goto ip_hdr_cksum;
} else {
goto ip_hdr_cksum;
}
ASSERT(((uchar_t *)cksump) + sizeof (uint16_t) <= mp->b_wptr);
cksum += (dst >> 16) + (dst & 0xFFFF) + (src >> 16) + (src & 0xFFFF);
cksum = IP_CSUM(mp, ip_hdr_length, cksum);
if (protocol == IPPROTO_UDP && cksum == 0)
*cksump = ~cksum;
else
*cksump = cksum;
IP_STAT(ipst, ip_out_sw_cksum);
IP_STAT_UPDATE(ipst, ip_out_sw_cksum_bytes, pktlen);
ip_hdr_cksum:
ipha->ipha_hdr_checksum = 0;
ipha->ipha_hdr_checksum = ip_csum_hdr(ipha);
return (B_TRUE);
}
static boolean_t
ip_output_cksum_v4(iaflags_t ixaflags, mblk_t *mp, ipha_t *ipha,
ip_xmit_attr_t *ixa, ill_t *ill)
{
uint_t pktlen = ixa->ixa_pktlen;
uint16_t *cksump;
uint16_t hck_flags;
uint32_t cksum;
uint8_t protocol = ixa->ixa_protocol;
uint16_t ip_hdr_length = ixa->ixa_ip_hdr_length;
if ((ixaflags & IXAF_NO_HW_CKSUM) || !ILL_HCKSUM_CAPABLE(ill) ||
!dohwcksum) {
return (ip_output_sw_cksum_v4(mp, ipha, ixa));
}
if (protocol == IPPROTO_TCP) {
cksump = IPH_TCPH_CHECKSUMP(ipha, ip_hdr_length);
cksum = IP_TCP_CSUM_COMP;
} else if (protocol == IPPROTO_UDP) {
cksump = IPH_UDPH_CHECKSUMP(ipha, ip_hdr_length);
cksum = IP_UDP_CSUM_COMP;
} else if (protocol == IPPROTO_SCTP) {
sctp_hdr_t *sctph;
ASSERT(MBLKL(mp) >= (ip_hdr_length + sizeof (*sctph)));
sctph = (sctp_hdr_t *)(mp->b_rptr + ip_hdr_length);
sctph->sh_chksum = 0;
#ifdef DEBUG
if (!skip_sctp_cksum)
#endif
sctph->sh_chksum = sctp_cksum(mp, ip_hdr_length);
goto ip_hdr_cksum;
} else if (protocol == IPPROTO_ICMP) {
return (ip_output_sw_cksum_v4(mp, ipha, ixa));
} else {
ip_hdr_cksum:
ipha->ipha_hdr_checksum = 0;
ipha->ipha_hdr_checksum = ip_csum_hdr(ipha);
return (B_TRUE);
}
ASSERT(((uchar_t *)cksump) + sizeof (uint16_t) <= mp->b_wptr);
hck_flags = ill->ill_hcksum_capab->ill_hcksum_txflags;
DB_CKSUMFLAGS(mp) &= ~HCK_FLAGS;
if (hck_flags & HCKSUM_INET_FULL_V4) {
*cksump = 0;
DB_CKSUMFLAGS(mp) |= HCK_FULLCKSUM;
ipha->ipha_hdr_checksum = 0;
if (hck_flags & HCKSUM_IPHDRCKSUM) {
DB_CKSUMFLAGS(mp) |= HCK_IPV4_HDRCKSUM;
} else {
ipha->ipha_hdr_checksum = ip_csum_hdr(ipha);
}
return (B_TRUE);
}
if ((hck_flags) & HCKSUM_INET_PARTIAL) {
ipaddr_t dst = ipha->ipha_dst;
ipaddr_t src = ipha->ipha_src;
cksum += (dst >> 16) + (dst & 0xFFFF) +
(src >> 16) + (src & 0xFFFF);
cksum += *(cksump);
cksum = (cksum & 0xFFFF) + (cksum >> 16);
*(cksump) = (cksum & 0xFFFF) + (cksum >> 16);
DB_CKSUMSTART(mp) = ip_hdr_length;
DB_CKSUMSTUFF(mp) = (uint8_t *)cksump - (uint8_t *)ipha;
DB_CKSUMEND(mp) = pktlen;
DB_CKSUMFLAGS(mp) |= HCK_PARTIALCKSUM;
ipha->ipha_hdr_checksum = 0;
if (hck_flags & HCKSUM_IPHDRCKSUM) {
DB_CKSUMFLAGS(mp) |= HCK_IPV4_HDRCKSUM;
} else {
ipha->ipha_hdr_checksum = ip_csum_hdr(ipha);
}
return (B_TRUE);
}
return (ip_output_sw_cksum_v4(mp, ipha, ixa));
}
int
ire_send_wire_v4(ire_t *ire, mblk_t *mp, void *iph_arg,
ip_xmit_attr_t *ixa, uint32_t *identp)
{
ip_stack_t *ipst = ixa->ixa_ipst;
ipha_t *ipha = (ipha_t *)iph_arg;
iaflags_t ixaflags = ixa->ixa_flags;
ill_t *ill;
ASSERT(ixa->ixa_nce != NULL);
ill = ixa->ixa_nce->nce_ill;
if (ixaflags & IXAF_DONTROUTE)
ipha->ipha_ttl = 1;
if (cl_inet_ipident != NULL) {
ipaddr_t src = ipha->ipha_src;
ipaddr_t dst = ipha->ipha_dst;
netstackid_t stack_id = ipst->ips_netstack->netstack_stackid;
ASSERT(cl_inet_isclusterwide != NULL);
if ((*cl_inet_isclusterwide)(stack_id, IPPROTO_IP,
AF_INET, (uint8_t *)(uintptr_t)src, NULL)) {
ipha->ipha_ident = (*cl_inet_ipident)(stack_id,
IPPROTO_IP, AF_INET, (uint8_t *)(uintptr_t)src,
(uint8_t *)(uintptr_t)dst, NULL);
} else {
ipha->ipha_ident = atomic_add_32_nv(identp,
ixa->ixa_extra_ident + 1);
}
} else {
ipha->ipha_ident = atomic_add_32_nv(identp,
ixa->ixa_extra_ident + 1);
}
#ifndef _BIG_ENDIAN
ipha->ipha_ident = htons(ipha->ipha_ident);
#endif
if (IPP_ENABLED(IPP_LOCAL_OUT, ipst)) {
mp = ip_process(IPP_LOCAL_OUT, mp, ill, ill);
if (mp == NULL) {
return (0);
}
}
if (!IS_SIMPLE_IPH(ipha)) {
ixaflags = ixa->ixa_flags |= IXAF_NO_HW_CKSUM;
if (ip_output_options(mp, ipha, ixa, ill)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
return (EINVAL);
}
}
if (is_system_labeled() && ixa->ixa_tsl != NULL &&
(ill->ill_mactype == DL_6TO4 || ill->ill_mactype == DL_IPV4 ||
ill->ill_mactype == DL_IPV6)) {
cred_t *newcr;
newcr = copycred_from_tslabel(ixa->ixa_cred, ixa->ixa_tsl,
KM_NOSLEEP);
if (newcr == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards - newcr",
mp, ill);
freemsg(mp);
return (ENOBUFS);
}
mblk_setcred(mp, newcr, NOPID);
crfree(newcr);
}
if (ixa->ixa_pktlen > ixa->ixa_fragsize ||
(ixaflags & IXAF_IPSEC_SECURE)) {
uint32_t pktlen;
pktlen = ixa->ixa_pktlen;
if (ixaflags & IXAF_IPSEC_SECURE)
pktlen += ipsec_out_extra_length(ixa);
if (pktlen > IP_MAXPACKET)
return (EMSGSIZE);
if (ixaflags & IXAF_SET_ULP_CKSUM) {
if (!ip_output_sw_cksum_v4(mp, ipha, ixa)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards", mp, ill);
freemsg(mp);
return (EINVAL);
}
} else {
ipha->ipha_hdr_checksum = 0;
ipha->ipha_hdr_checksum = ip_csum_hdr(ipha);
}
if ((pktlen > ixa->ixa_fragsize) &&
(ixaflags & IXAF_DONTFRAG)) {
ip_recv_attr_t iras;
DTRACE_PROBE4(ip4__fragsize__fail, uint_t, pktlen,
uint_t, ixa->ixa_fragsize, uint_t, ixa->ixa_pktlen,
uint_t, ixa->ixa_pmtu);
bzero(&iras, sizeof (iras));
ipsec_out_to_in(ixa, ill, &iras);
ip_drop_output("ICMP_FRAG_NEEDED", mp, ill);
icmp_frag_needed(mp, ixa->ixa_fragsize, &iras);
ira_cleanup(&iras, B_FALSE);
return (EMSGSIZE);
}
DTRACE_PROBE4(ip4__fragsize__ok, uint_t, pktlen,
uint_t, ixa->ixa_fragsize, uint_t, ixa->ixa_pktlen,
uint_t, ixa->ixa_pmtu);
if (ixaflags & IXAF_IPSEC_SECURE) {
return (ipsec_out_process(mp, ixa));
}
return (ip_fragment_v4(mp, ixa->ixa_nce, ixaflags,
ixa->ixa_pktlen, ixa->ixa_fragsize, ixa->ixa_xmit_hint,
ixa->ixa_zoneid, ixa->ixa_no_loop_zoneid,
ixa->ixa_postfragfn, &ixa->ixa_cookie));
}
if (ixaflags & IXAF_SET_ULP_CKSUM) {
if (!ip_output_cksum_v4(ixaflags, mp, ipha, ixa, ill)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards", mp, ill);
freemsg(mp);
return (EINVAL);
}
} else {
ipha->ipha_hdr_checksum = 0;
ipha->ipha_hdr_checksum = ip_csum_hdr(ipha);
}
return ((ixa->ixa_postfragfn)(mp, ixa->ixa_nce, ixaflags,
ixa->ixa_pktlen, ixa->ixa_xmit_hint, ixa->ixa_zoneid,
ixa->ixa_no_loop_zoneid, &ixa->ixa_cookie));
}
void
ip_postfrag_loopback(mblk_t *mp, nce_t *nce, iaflags_t ixaflags,
uint_t pkt_len, zoneid_t nolzid)
{
rtc_t rtc;
ill_t *ill = nce->nce_ill;
ip_recv_attr_t iras;
ncec_t *ncec;
ncec = nce->nce_common;
iras.ira_flags = IRAF_VERIFY_IP_CKSUM | IRAF_VERIFY_ULP_CKSUM |
IRAF_LOOPBACK | IRAF_L2SRC_LOOPBACK;
if (ncec->ncec_flags & NCE_F_BCAST)
iras.ira_flags |= IRAF_L2DST_BROADCAST;
else if (ncec->ncec_flags & NCE_F_MCAST)
iras.ira_flags |= IRAF_L2DST_MULTICAST;
iras.ira_free_flags = 0;
iras.ira_cred = NULL;
iras.ira_cpid = NOPID;
iras.ira_tsl = NULL;
iras.ira_zoneid = ALL_ZONES;
iras.ira_pktlen = pkt_len;
UPDATE_MIB(ill->ill_ip_mib, ipIfStatsHCInOctets, iras.ira_pktlen);
BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCInReceives);
if (ixaflags & IXAF_IS_IPV4)
iras.ira_flags |= IRAF_IS_IPV4;
iras.ira_ill = iras.ira_rill = ill;
iras.ira_ruifindex = ill->ill_phyint->phyint_ifindex;
iras.ira_rifindex = iras.ira_ruifindex;
iras.ira_mhip = NULL;
iras.ira_flags |= ixaflags & IAF_MASK;
iras.ira_no_loop_zoneid = nolzid;
iras.ira_sqp = NULL;
rtc.rtc_ire = NULL;
if (ixaflags & IXAF_IS_IPV4) {
ipha_t *ipha = (ipha_t *)mp->b_rptr;
rtc.rtc_ipaddr = INADDR_ANY;
(*ill->ill_inputfn)(mp, ipha, &ipha->ipha_dst, &iras, &rtc);
if (rtc.rtc_ire != NULL) {
ASSERT(rtc.rtc_ipaddr != INADDR_ANY);
ire_refrele(rtc.rtc_ire);
}
} else {
ip6_t *ip6h = (ip6_t *)mp->b_rptr;
rtc.rtc_ip6addr = ipv6_all_zeros;
(*ill->ill_inputfn)(mp, ip6h, &ip6h->ip6_dst, &iras, &rtc);
if (rtc.rtc_ire != NULL) {
ASSERT(!IN6_IS_ADDR_UNSPECIFIED(&rtc.rtc_ip6addr));
ire_refrele(rtc.rtc_ire);
}
}
if (iras.ira_flags & (IRAF_IPSEC_SECURE|IRAF_SYSTEM_LABELED))
ira_cleanup(&iras, B_FALSE);
}
int
ip_postfrag_loopcheck(mblk_t *mp, nce_t *nce, iaflags_t ixaflags,
uint_t pkt_len, uint32_t xmit_hint, zoneid_t szone, zoneid_t nolzid,
uintptr_t *ixacookie)
{
ill_t *ill = nce->nce_ill;
int error = 0;
if (ixaflags & IXAF_LOOPBACK_COPY) {
mblk_t *mp1;
mp1 = copymsg(mp);
if (mp1 == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards", mp, ill);
error = ENOBUFS;
} else {
ip_postfrag_loopback(mp1, nce, ixaflags, pkt_len,
nolzid);
}
}
if (ixaflags & IXAF_IS_IPV4) {
ipha_t *ipha = (ipha_t *)mp->b_rptr;
if (ipha->ipha_ttl == 0) {
ip_drop_output("multicast ipha_ttl not sent to wire",
mp, ill);
freemsg(mp);
return (error);
}
} else {
ip6_t *ip6h = (ip6_t *)mp->b_rptr;
if (ip6h->ip6_hops == 0) {
ip_drop_output("multicast ipha_ttl not sent to wire",
mp, ill);
freemsg(mp);
return (error);
}
}
if (nce->nce_ill->ill_wq == NULL) {
ip_drop_output("multicast on lo0 not sent to wire", mp, ill);
freemsg(mp);
return (error);
}
return (ip_xmit(mp, nce, ixaflags, pkt_len, xmit_hint, szone, 0,
ixacookie));
}
int
ip_postfrag_multirt_v4(mblk_t *mp, nce_t *nce, iaflags_t ixaflags,
uint_t pkt_len, uint32_t xmit_hint, zoneid_t szone, zoneid_t nolzid,
uintptr_t *ixacookie)
{
irb_t *irb;
ipha_t *ipha = (ipha_t *)mp->b_rptr;
ire_t *ire;
ire_t *ire1;
mblk_t *mp1;
nce_t *nce1;
ill_t *ill = nce->nce_ill;
ill_t *ill1;
ip_stack_t *ipst = ill->ill_ipst;
int error = 0;
int num_sent = 0;
int err;
uint_t ire_type;
ipaddr_t nexthop;
ASSERT(ixaflags & IXAF_IS_IPV4);
if (ixaflags & IXAF_LOOPBACK_COPY) {
mblk_t *mp1;
mp1 = copymsg(mp);
if (mp1 == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards", mp, ill);
error = ENOBUFS;
} else {
ip_postfrag_loopback(mp1, nce, ixaflags, pkt_len,
nolzid);
}
}
if (V4_PART_OF_V6(nce->nce_addr) == ipha->ipha_dst) {
ire = ire_ftable_lookup_v4(ipha->ipha_dst, 0, 0, 0,
NULL, ALL_ZONES, NULL, MATCH_IRE_DSTONLY, 0, ipst, NULL);
} else {
ipaddr_t v4addr = V4_PART_OF_V6(nce->nce_addr);
ire = ire_ftable_lookup_v4(ipha->ipha_dst, 0, v4addr, 0,
NULL, ALL_ZONES, NULL, MATCH_IRE_GW, 0, ipst, NULL);
}
if (ire == NULL ||
(ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) ||
!(ire->ire_flags & RTF_MULTIRT)) {
ip_drop_output("ip_postfrag_multirt didn't find route",
mp, nce->nce_ill);
if (ire != NULL)
ire_refrele(ire);
return (ENETUNREACH);
}
irb = ire->ire_bucket;
irb_refhold(irb);
for (ire1 = irb->irb_ire; ire1 != NULL; ire1 = ire1->ire_next) {
if (IRE_IS_CONDEMNED(ire1) ||
!(ire1->ire_flags & RTF_MULTIRT) ||
ire1->ire_type != ire->ire_type)
continue;
if (ire1 == ire)
continue;
ill1 = ire_nexthop_ill(ire1);
if (ill1 == NULL) {
ire_t *ire2;
uint_t match_flags = MATCH_IRE_DSTONLY;
if (ire1->ire_ill != NULL)
match_flags |= MATCH_IRE_ILL;
ire2 = ire_route_recursive_impl_v4(ire1,
ire1->ire_addr, ire1->ire_type, ire1->ire_ill,
ire1->ire_zoneid, NULL, match_flags,
IRR_ALLOCATE, 0, ipst, NULL, NULL, NULL);
if (ire2 != NULL)
ire_refrele(ire2);
ill1 = ire_nexthop_ill(ire1);
}
if (ill1 == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards - no ill",
mp, ill);
error = ENETUNREACH;
continue;
}
if (nce->nce_common->ncec_flags & NCE_F_BCAST) {
ire_type = IRE_BROADCAST;
nexthop = ire1->ire_gateway_addr;
} else if (nce->nce_common->ncec_flags & NCE_F_MCAST) {
ire_type = IRE_MULTICAST;
nexthop = ipha->ipha_dst;
} else {
ire_type = ire1->ire_type;
nexthop = ire1->ire_gateway_addr;
}
if (ill1->ill_grp != NULL) {
BUMP_MIB(ill1->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards - IPMP",
mp, ill1);
ill_refrele(ill1);
error = ENETUNREACH;
continue;
}
nce1 = arp_nce_init(ill1, nexthop, ire_type);
if (nce1 == NULL) {
BUMP_MIB(ill1->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards - no nce",
mp, ill1);
ill_refrele(ill1);
error = ENETUNREACH;
continue;
}
mp1 = copymsg(mp);
if (mp1 == NULL) {
BUMP_MIB(ill1->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards", mp, ill1);
nce_refrele(nce1);
ill_refrele(ill1);
error = ENOBUFS;
continue;
}
DB_CKSUMSTART(mp1) = DB_CKSUMSTART(mp);
DB_CKSUMSTUFF(mp1) = DB_CKSUMSTUFF(mp);
DB_CKSUMEND(mp1) = DB_CKSUMEND(mp);
DB_CKSUMFLAGS(mp1) = DB_CKSUMFLAGS(mp);
DB_LSOMSS(mp1) = DB_LSOMSS(mp);
ire1->ire_ob_pkt_count++;
err = ip_xmit(mp1, nce1, ixaflags, pkt_len, xmit_hint, szone,
0, ixacookie);
if (err == 0)
num_sent++;
else
error = err;
nce_refrele(nce1);
ill_refrele(ill1);
}
irb_refrele(irb);
ire_refrele(ire);
err = ip_xmit(mp, nce, ixaflags, pkt_len, xmit_hint, szone, 0,
ixacookie);
if (err == 0)
num_sent++;
else
error = err;
if (num_sent > 0)
return (0);
else
return (error);
}
boolean_t
ip_output_verify_local(ip_xmit_attr_t *ixa)
{
ire_t *ire = ixa->ixa_ire;
if (!(ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK)))
return (B_FALSE);
return (ixa->ixa_ire->ire_generation == ixa->ixa_ire_generation);
}
mblk_t *
ip_output_process_local(mblk_t *mp, ip_xmit_attr_t *ixa, boolean_t hooks_out,
boolean_t hooks_in, conn_t *peer_connp)
{
ill_t *ill = ixa->ixa_ire->ire_ill;
ipha_t *ipha = NULL;
ip6_t *ip6h = NULL;
ip_stack_t *ipst = ixa->ixa_ipst;
iaflags_t ixaflags = ixa->ixa_flags;
ip_recv_attr_t iras;
int error;
ASSERT(mp != NULL);
if (ixaflags & IXAF_IS_IPV4) {
ipha = (ipha_t *)mp->b_rptr;
if (ipst->ips_ip4_observe.he_interested) {
zoneid_t szone, dzone;
zoneid_t stackzoneid;
stackzoneid = netstackid_to_zoneid(
ipst->ips_netstack->netstack_stackid);
if (stackzoneid == GLOBAL_ZONEID) {
dzone = ixa->ixa_ire->ire_zoneid;
szone = ixa->ixa_zoneid;
} else {
szone = dzone = stackzoneid;
}
ipobs_hook(mp, IPOBS_HOOK_LOCAL, szone, dzone, ill,
ipst);
}
DTRACE_IP7(send, mblk_t *, mp, conn_t *, NULL, void_ip_t *,
ipha, __dtrace_ipsr_ill_t *, ill, ipha_t *, ipha, ip6_t *,
NULL, int, 1);
if (hooks_out) {
DTRACE_PROBE4(ip4__loopback__out__start, ill_t *, NULL,
ill_t *, ill, ipha_t *, ipha, mblk_t *, mp);
FW_HOOKS(ipst->ips_ip4_loopback_out_event,
ipst->ips_ipv4firewall_loopback_out,
NULL, ill, ipha, mp, mp, 0, ipst, error);
DTRACE_PROBE1(ip4__loopback__out__end, mblk_t *, mp);
}
if (mp == NULL)
return (NULL);
if (hooks_in) {
DTRACE_PROBE4(ip4__loopback__in__start, ill_t *, ill,
ill_t *, NULL, ipha_t *, ipha, mblk_t *, mp);
FW_HOOKS(ipst->ips_ip4_loopback_in_event,
ipst->ips_ipv4firewall_loopback_in,
ill, NULL, ipha, mp, mp, 0, ipst, error);
DTRACE_PROBE1(ip4__loopback__in__end, mblk_t *, mp);
}
if (mp == NULL)
return (NULL);
DTRACE_IP7(receive, mblk_t *, mp, conn_t *, NULL, void_ip_t *,
ipha, __dtrace_ipsr_ill_t *, ill, ipha_t *, ipha, ip6_t *,
NULL, int, 1);
if (peer_connp != NULL) {
ipsec_out_to_in(ixa, ill, &iras);
mp = ipsec_check_inbound_policy(mp, peer_connp, ipha,
NULL, &iras);
}
} else {
ip6h = (ip6_t *)mp->b_rptr;
if (ipst->ips_ip6_observe.he_interested) {
zoneid_t szone, dzone;
zoneid_t stackzoneid;
stackzoneid = netstackid_to_zoneid(
ipst->ips_netstack->netstack_stackid);
if (stackzoneid == GLOBAL_ZONEID) {
dzone = ixa->ixa_ire->ire_zoneid;
szone = ixa->ixa_zoneid;
} else {
szone = dzone = stackzoneid;
}
ipobs_hook(mp, IPOBS_HOOK_LOCAL, szone, dzone, ill,
ipst);
}
DTRACE_IP7(send, mblk_t *, mp, conn_t *, NULL, void_ip_t *,
ip6h, __dtrace_ipsr_ill_t *, ill, ipha_t *, NULL, ip6_t *,
ip6h, int, 1);
if (hooks_out) {
DTRACE_PROBE4(ip6__loopback__out__start, ill_t *, NULL,
ill_t *, ill, ip6_t *, ip6h, mblk_t *, mp);
FW_HOOKS6(ipst->ips_ip6_loopback_out_event,
ipst->ips_ipv6firewall_loopback_out,
NULL, ill, ip6h, mp, mp, 0, ipst, error);
DTRACE_PROBE1(ip6__loopback__out__end, mblk_t *, mp);
}
if (mp == NULL)
return (NULL);
if (hooks_in) {
DTRACE_PROBE4(ip6__loopback__in__start, ill_t *, ill,
ill_t *, NULL, ip6_t *, ip6h, mblk_t *, mp);
FW_HOOKS6(ipst->ips_ip6_loopback_in_event,
ipst->ips_ipv6firewall_loopback_in,
ill, NULL, ip6h, mp, mp, 0, ipst, error);
DTRACE_PROBE1(ip6__loopback__in__end, mblk_t *, mp);
}
if (mp == NULL)
return (NULL);
DTRACE_IP7(receive, mblk_t *, mp, conn_t *, NULL, void_ip_t *,
ip6h, __dtrace_ipsr_ill_t *, ill, ipha_t *, NULL, ip6_t *,
ip6h, int, 1);
if (peer_connp != NULL) {
ipsec_out_to_in(ixa, ill, &iras);
mp = ipsec_check_inbound_policy(mp, peer_connp, NULL,
ip6h, &iras);
}
}
if (mp == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", NULL, ill);
}
return (mp);
}