#include <sys/types.h>
#include <sys/stream.h>
#include <sys/dlpi.h>
#include <sys/stropts.h>
#include <sys/sysmacros.h>
#include <sys/strsun.h>
#include <sys/strlog.h>
#include <sys/strsubr.h>
#define _SUN_TPI_VERSION 2
#include <sys/tihdr.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>
#include <sys/sdt.h>
#include <sys/kobj.h>
#include <sys/zone.h>
#include <sys/neti.h>
#include <sys/hook.h>
#include <sys/kmem.h>
#include <sys/systm.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/vtrace.h>
#include <sys/isa_defs.h>
#include <sys/atomic.h>
#include <sys/policy.h>
#include <sys/mac.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/route.h>
#include <net/if_dl.h>
#include <sys/sockio.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/sctp.h>
#include <inet/common.h>
#include <inet/mi.h>
#include <inet/optcom.h>
#include <inet/mib2.h>
#include <inet/nd.h>
#include <inet/arp.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/tcp_impl.h>
#include <inet/udp_impl.h>
#include <inet/ipp_common.h>
#include <inet/ip_multi.h>
#include <inet/ip_if.h>
#include <inet/ip_ire.h>
#include <inet/ip_rts.h>
#include <inet/ip_ndp.h>
#include <net/pfkeyv2.h>
#include <inet/sadb.h>
#include <inet/ipsec_impl.h>
#include <inet/iptun/iptun_impl.h>
#include <inet/sctp_ip.h>
#include <sys/pattr.h>
#include <inet/ipclassifier.h>
#include <inet/ipsecah.h>
#include <inet/rawip_impl.h>
#include <inet/rts_impl.h>
#include <sys/squeue_impl.h>
#include <sys/squeue.h>
#include <sys/tsol/label.h>
#include <sys/tsol/tnet.h>
#include <sys/ethernet.h>
uchar_t ip6opt_ls;
const in6_addr_t ipv6_all_ones =
{ 0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU };
const in6_addr_t ipv6_all_zeros = { 0, 0, 0, 0 };
#ifdef _BIG_ENDIAN
const in6_addr_t ipv6_unspecified_group = { 0xff000000U, 0, 0, 0 };
#else
const in6_addr_t ipv6_unspecified_group = { 0x000000ffU, 0, 0, 0 };
#endif
#ifdef _BIG_ENDIAN
const in6_addr_t ipv6_loopback = { 0, 0, 0, 0x00000001U };
#else
const in6_addr_t ipv6_loopback = { 0, 0, 0, 0x01000000U };
#endif
#ifdef _BIG_ENDIAN
const in6_addr_t ipv6_all_hosts_mcast = { 0xff020000U, 0, 0, 0x00000001U };
#else
const in6_addr_t ipv6_all_hosts_mcast = { 0x000002ffU, 0, 0, 0x01000000U };
#endif
#ifdef _BIG_ENDIAN
const in6_addr_t ipv6_all_rtrs_mcast = { 0xff020000U, 0, 0, 0x00000002U };
#else
const in6_addr_t ipv6_all_rtrs_mcast = { 0x000002ffU, 0, 0, 0x02000000U };
#endif
#ifdef _BIG_ENDIAN
const in6_addr_t ipv6_all_v2rtrs_mcast = { 0xff020000U, 0, 0, 0x00000016U };
#else
const in6_addr_t ipv6_all_v2rtrs_mcast = { 0x000002ffU, 0, 0, 0x16000000U };
#endif
#ifdef _BIG_ENDIAN
const in6_addr_t ipv6_solicited_node_mcast =
{ 0xff020000U, 0, 0x00000001U, 0xff000000U };
#else
const in6_addr_t ipv6_solicited_node_mcast =
{ 0x000002ffU, 0, 0x01000000U, 0x000000ffU };
#endif
static boolean_t icmp_inbound_verify_v6(mblk_t *, icmp6_t *, ip_recv_attr_t *);
static void icmp_inbound_too_big_v6(icmp6_t *, ip_recv_attr_t *);
static void icmp_pkt_v6(mblk_t *, void *, size_t, const in6_addr_t *,
ip_recv_attr_t *);
static void icmp_redirect_v6(mblk_t *, ip6_t *, nd_redirect_t *,
ip_recv_attr_t *);
static void icmp_send_redirect_v6(mblk_t *, in6_addr_t *,
in6_addr_t *, ip_recv_attr_t *);
static void icmp_send_reply_v6(mblk_t *, ip6_t *, icmp6_t *,
ip_recv_attr_t *);
static boolean_t ip_source_routed_v6(ip6_t *, mblk_t *, ip_stack_t *);
mblk_t *
icmp_inbound_v6(mblk_t *mp, ip_recv_attr_t *ira)
{
icmp6_t *icmp6;
ip6_t *ip6h;
int ip_hdr_length;
boolean_t interested;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
mblk_t *mp_ret = NULL;
ip6h = (ip6_t *)mp->b_rptr;
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInMsgs);
if (IN6_IS_ADDR_MULTICAST(&ip6h->ip6_src)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInAddrErrors);
ip_drop_input("ipIfStatsInAddrErrors: mcast src", mp, ill);
freemsg(mp);
return (NULL);
}
if (!(ira->ira_flags & IRAF_L2SRC_SET))
ip_setl2src(mp, ira, ira->ira_rill);
ip_hdr_length = ira->ira_ip_hdr_length;
if ((mp->b_wptr - mp->b_rptr) < (ip_hdr_length + ICMP6_MINLEN)) {
if (ira->ira_pktlen < (ip_hdr_length + ICMP6_MINLEN)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInTruncatedPkts);
ip_drop_input("ipIfStatsInTruncatedPkts", mp, ill);
freemsg(mp);
return (NULL);
}
ip6h = ip_pullup(mp, ip_hdr_length + ICMP6_MINLEN, ira);
if (ip6h == NULL) {
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
freemsg(mp);
return (NULL);
}
}
icmp6 = (icmp6_t *)(&mp->b_rptr[ip_hdr_length]);
DTRACE_PROBE2(icmp__inbound__v6, ip6_t *, ip6h, icmp6_t *, icmp6);
ip2dbg(("icmp_inbound_v6: type %d code %d\n", icmp6->icmp6_type,
icmp6->icmp6_code));
interested = !(icmp6->icmp6_type & ICMP6_INFOMSG_MASK);
switch (icmp6->icmp6_type) {
case ICMP6_DST_UNREACH:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInDestUnreachs);
if (icmp6->icmp6_code == ICMP6_DST_UNREACH_ADMIN)
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInAdminProhibs);
break;
case ICMP6_TIME_EXCEEDED:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInTimeExcds);
break;
case ICMP6_PARAM_PROB:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInParmProblems);
break;
case ICMP6_PACKET_TOO_BIG:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInPktTooBigs);
break;
case ICMP6_ECHO_REQUEST:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInEchos);
if (IN6_IS_ADDR_MULTICAST(&ip6h->ip6_dst) &&
!ipst->ips_ipv6_resp_echo_mcast)
break;
if (mp->b_datap->db_ref > 1) {
mblk_t *mp1;
mp1 = copymsg(mp);
if (mp1 == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards - copymsg",
mp, ill);
freemsg(mp);
return (NULL);
}
freemsg(mp);
mp = mp1;
ip6h = (ip6_t *)mp->b_rptr;
icmp6 = (icmp6_t *)(&mp->b_rptr[ip_hdr_length]);
}
icmp6->icmp6_type = ICMP6_ECHO_REPLY;
icmp_send_reply_v6(mp, ip6h, icmp6, ira);
return (NULL);
case ICMP6_ECHO_REPLY:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInEchoReplies);
break;
case ND_ROUTER_SOLICIT:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInRouterSolicits);
break;
case ND_ROUTER_ADVERT:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInRouterAdvertisements);
break;
case ND_NEIGHBOR_SOLICIT:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInNeighborSolicits);
ndp_input(mp, ira);
return (NULL);
case ND_NEIGHBOR_ADVERT:
BUMP_MIB(ill->ill_icmp6_mib,
ipv6IfIcmpInNeighborAdvertisements);
ndp_input(mp, ira);
return (NULL);
case ND_REDIRECT:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInRedirects);
if (ipst->ips_ipv6_ignore_redirect)
break;
interested = B_TRUE;
break;
case MLD_LISTENER_QUERY:
case MLD_LISTENER_REPORT:
case MLD_LISTENER_REDUCTION:
mp = mld_input(mp, ira);
return (mp);
default:
break;
}
if (ipst->ips_ipcl_proto_fanout_v6[IPPROTO_ICMPV6].connf_head != NULL) {
if (!interested) {
return (mp);
}
mp_ret = copymsg(mp);
if (mp_ret == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards - copymsg", mp, ill);
}
} else if (!interested) {
freemsg(mp);
return (NULL);
}
if (mp->b_cont != NULL) {
if (ip_pullup(mp, -1, ira) == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards - ip_pullup",
mp, ill);
freemsg(mp);
return (mp_ret);
}
}
if (mp->b_datap->db_ref > 1) {
mblk_t *mp1;
mp1 = copymsg(mp);
if (mp1 == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards - copymsg", mp, ill);
freemsg(mp);
return (mp_ret);
}
freemsg(mp);
mp = mp1;
}
ip6h = (ip6_t *)mp->b_rptr;
icmp6 = (icmp6_t *)(&mp->b_rptr[ip_hdr_length]);
if (!icmp_inbound_verify_v6(mp, icmp6, ira)) {
freemsg(mp);
return (mp_ret);
}
switch (icmp6->icmp6_type) {
case ND_REDIRECT:
icmp_redirect_v6(mp, ip6h, (nd_redirect_t *)icmp6, ira);
break;
case ICMP6_PACKET_TOO_BIG:
icmp_inbound_too_big_v6(icmp6, ira);
default:
icmp_inbound_error_fanout_v6(mp, icmp6, ira);
break;
}
return (mp_ret);
}
static void
icmp_send_reply_v6(mblk_t *mp, ip6_t *ip6h, icmp6_t *icmp6,
ip_recv_attr_t *ira)
{
uint_t ip_hdr_length = ira->ira_ip_hdr_length;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
ip_xmit_attr_t ixas;
in6_addr_t origsrc;
if (ip_hdr_length != IPV6_HDR_LEN) {
int i;
for (i = 0; i < IPV6_HDR_LEN; i++) {
mp->b_rptr[ip_hdr_length - i - 1] =
mp->b_rptr[IPV6_HDR_LEN - i - 1];
}
mp->b_rptr += (ip_hdr_length - IPV6_HDR_LEN);
ip6h = (ip6_t *)mp->b_rptr;
ip6h->ip6_nxt = IPPROTO_ICMPV6;
i = ntohs(ip6h->ip6_plen);
i -= (ip_hdr_length - IPV6_HDR_LEN);
ip6h->ip6_plen = htons(i);
ip_hdr_length = IPV6_HDR_LEN;
ASSERT(ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN == msgdsize(mp));
}
ip6h->ip6_vcf &= ~IPV6_FLOWINFO_FLOWLABEL;
origsrc = ip6h->ip6_src;
ip6h->ip6_src = ip6h->ip6_dst;
ip6h->ip6_dst = origsrc;
ip6h->ip6_hops = ipst->ips_ipv6_def_hops;
icmp6->icmp6_cksum = ip6h->ip6_plen;
bzero(&ixas, sizeof (ixas));
ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6;
ixas.ixa_zoneid = ira->ira_zoneid;
ixas.ixa_cred = kcred;
ixas.ixa_cpid = NOPID;
ixas.ixa_tsl = ira->ira_tsl;
ixas.ixa_ifindex = 0;
ixas.ixa_ipst = ipst;
ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) {
ixas.ixa_flags |= IXAF_NO_IPSEC;
} else {
if (!ipsec_in_to_out(ira, &ixas, mp, NULL, ip6h)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
return;
}
}
if (IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_src)) {
ixas.ixa_flags |= IXAF_SCOPEID_SET;
if (IS_UNDER_IPMP(ill))
ixas.ixa_scopeid = ill_get_upper_ifindex(ill);
else
ixas.ixa_scopeid = ill->ill_phyint->phyint_ifindex;
}
if (ira->ira_flags & IRAF_MULTIBROADCAST) {
ip6h->ip6_src = ipv6_all_zeros;
ixas.ixa_flags |= IXAF_SET_SOURCE;
}
if (ipst->ips_ipv6_icmp_return_pmtu)
ixas.ixa_flags |= IXAF_PMTU_DISCOVERY;
(void) ip_output_simple(mp, &ixas);
ixa_cleanup(&ixas);
}
static boolean_t
icmp_inbound_verify_v6(mblk_t *mp, icmp6_t *icmp6, ip_recv_attr_t *ira)
{
ill_t *ill = ira->ira_ill;
uint16_t hdr_length;
uint8_t *nexthdrp;
uint8_t nexthdr;
ip_stack_t *ipst = ill->ill_ipst;
conn_t *connp;
ip6_t *ip6h;
ip6h = (ip6_t *)&icmp6[1];
if ((uchar_t *)ip6h + IPV6_HDR_LEN > mp->b_wptr)
goto truncated;
if (icmp6->icmp6_type == ND_REDIRECT) {
hdr_length = sizeof (nd_redirect_t);
} else {
if ((IPH_HDR_VERSION(ip6h) != IPV6_VERSION))
goto discard_pkt;
hdr_length = IPV6_HDR_LEN;
}
if ((uchar_t *)ip6h + hdr_length > mp->b_wptr)
goto truncated;
if (icmp6->icmp6_type == ND_REDIRECT)
return (B_TRUE);
if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp))
goto discard_pkt;
nexthdr = *nexthdrp;
switch (nexthdr) {
case IPPROTO_UDP:
if ((uchar_t *)ip6h + hdr_length + ICMP_MIN_TP_HDR_LEN >
mp->b_wptr)
goto truncated;
break;
case IPPROTO_TCP: {
tcpha_t *tcpha;
if ((uchar_t *)ip6h + hdr_length + ICMP_MIN_TP_HDR_LEN >
mp->b_wptr)
goto truncated;
tcpha = (tcpha_t *)((uchar_t *)ip6h + hdr_length);
connp = ipcl_tcp_lookup_reversed_ipv6(ip6h, tcpha, TCPS_LISTEN,
ill->ill_phyint->phyint_ifindex, ipst);
if (connp == NULL)
goto discard_pkt;
if ((connp->conn_verifyicmp != NULL) &&
!connp->conn_verifyicmp(connp, tcpha, NULL, icmp6, ira)) {
CONN_DEC_REF(connp);
goto discard_pkt;
}
CONN_DEC_REF(connp);
break;
}
case IPPROTO_SCTP:
if ((uchar_t *)ip6h + hdr_length + ICMP_MIN_TP_HDR_LEN >
mp->b_wptr)
goto truncated;
break;
case IPPROTO_ESP:
case IPPROTO_AH:
break;
case IPPROTO_ENCAP:
case IPPROTO_IPV6: {
ip6_t *in_ip6h;
in_ip6h = (ip6_t *)((uint8_t *)ip6h + hdr_length);
if ((uint8_t *)in_ip6h + (nexthdr == IPPROTO_ENCAP ?
sizeof (ipha_t) : sizeof (ip6_t)) > mp->b_wptr)
goto truncated;
break;
}
default:
break;
}
return (B_TRUE);
discard_pkt:
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
return (B_FALSE);
truncated:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
return (B_FALSE);
}
static void
icmp_inbound_too_big_v6(icmp6_t *icmp6, ip_recv_attr_t *ira)
{
uint32_t mtu;
dce_t *dce;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
int old_max_frag;
in6_addr_t final_dst;
ip6_t *ip6h;
ip6h = (ip6_t *)&icmp6[1];
final_dst = ip_get_dst_v6(ip6h, NULL, NULL);
mtu = ntohl(icmp6->icmp6_mtu);
if (mtu < IPV6_MIN_MTU) {
ip1dbg(("Received mtu less than IPv6 "
"min mtu %d: %d\n", IPV6_MIN_MTU, mtu));
DTRACE_PROBE1(icmp6__too__small__mtu, uint32_t, mtu);
return;
}
if (IN6_IS_ADDR_LINKSCOPE(&final_dst)) {
dce = dce_lookup_and_add_v6(&final_dst,
ill->ill_phyint->phyint_ifindex, ipst);
} else {
dce = dce_lookup_and_add_v6(&final_dst, 0, ipst);
}
if (dce == NULL) {
if (ip_debug > 2) {
pr_addr_dbg("icmp_inbound_too_big_v6:"
"no dce for dst %s\n", AF_INET6,
&final_dst);
}
return;
}
mutex_enter(&dce->dce_lock);
if (dce->dce_flags & DCEF_PMTU)
old_max_frag = dce->dce_pmtu;
else if (IN6_IS_ADDR_MULTICAST(&final_dst))
old_max_frag = ill->ill_mc_mtu;
else
old_max_frag = ill->ill_mtu;
ip1dbg(("Received mtu from router: %d\n", mtu));
DTRACE_PROBE1(icmp6__received__mtu, uint32_t, mtu);
dce->dce_pmtu = MIN(old_max_frag, mtu);
icmp6->icmp6_mtu = htonl(dce->dce_pmtu);
dce->dce_flags |= DCEF_PMTU;
dce->dce_last_change_time = TICK_TO_SEC(ddi_get_lbolt64());
mutex_exit(&dce->dce_lock);
dce_increment_generation(dce);
dce_refrele(dce);
}
void
icmp_inbound_error_fanout_v6(mblk_t *mp, icmp6_t *icmp6, ip_recv_attr_t *ira)
{
uint16_t *up;
uint32_t ports;
ip6_t rip6h;
ip6_t *ip6h;
uint16_t hdr_length;
uint8_t *nexthdrp;
uint8_t nexthdr;
tcpha_t *tcpha;
conn_t *connp;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
ipsec_stack_t *ipss = ipst->ips_netstack->netstack_ipsec;
ip6h = (ip6_t *)&icmp6[1];
ASSERT(mp->b_cont == NULL);
ASSERT((uchar_t *)&ip6h[1] <= mp->b_wptr);
if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp))
goto drop_pkt;
nexthdr = *nexthdrp;
ira->ira_protocol = nexthdr;
rip6h.ip6_src = ip6h->ip6_dst;
rip6h.ip6_dst = ip6h->ip6_src;
rip6h.ip6_nxt = nexthdr;
switch (nexthdr) {
case IPPROTO_UDP: {
up = (uint16_t *)((uchar_t *)ip6h + hdr_length);
ira->ira_flags |= IRAF_ICMP_ERROR;
ip_fanout_udp_multi_v6(mp, &rip6h, up[0], up[1], ira);
ira->ira_flags &= ~IRAF_ICMP_ERROR;
return;
}
case IPPROTO_TCP: {
tcpha = (tcpha_t *)((uchar_t *)ip6h + hdr_length);
connp = ipcl_tcp_lookup_reversed_ipv6(ip6h, tcpha,
TCPS_LISTEN, ill->ill_phyint->phyint_ifindex, ipst);
if (connp == NULL) {
goto drop_pkt;
}
if (CONN_INBOUND_POLICY_PRESENT_V6(connp, ipss) ||
(ira->ira_flags & IRAF_IPSEC_SECURE)) {
mp = ipsec_check_inbound_policy(mp, connp,
NULL, ip6h, ira);
if (mp == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
CONN_DEC_REF(connp);
return;
}
}
ira->ira_flags |= IRAF_ICMP_ERROR;
if (IPCL_IS_TCP(connp)) {
SQUEUE_ENTER_ONE(connp->conn_sqp, mp,
connp->conn_recvicmp, connp, ira, SQ_FILL,
SQTAG_TCP6_INPUT_ICMP_ERR);
} else {
ill_t *rill = ira->ira_rill;
ira->ira_ill = ira->ira_rill = NULL;
(connp->conn_recv)(connp, mp, NULL, ira);
CONN_DEC_REF(connp);
ira->ira_ill = ill;
ira->ira_rill = rill;
}
ira->ira_flags &= ~IRAF_ICMP_ERROR;
return;
}
case IPPROTO_SCTP:
up = (uint16_t *)((uchar_t *)ip6h + hdr_length);
((uint16_t *)&ports)[0] = up[1];
((uint16_t *)&ports)[1] = up[0];
ira->ira_flags |= IRAF_ICMP_ERROR;
ip_fanout_sctp(mp, NULL, &rip6h, ports, ira);
ira->ira_flags &= ~IRAF_ICMP_ERROR;
return;
case IPPROTO_ESP:
case IPPROTO_AH:
if (!ipsec_loaded(ipss)) {
ip_proto_not_sup(mp, ira);
return;
}
if (nexthdr == IPPROTO_ESP)
mp = ipsecesp_icmp_error(mp, ira);
else
mp = ipsecah_icmp_error(mp, ira);
if (mp == NULL)
return;
if (mp->b_cont != NULL) {
if (!pullupmsg(mp, -1))
goto drop_pkt;
}
if (mp->b_wptr - mp->b_rptr < IPV6_HDR_LEN)
goto drop_pkt;
ip6h = (ip6_t *)mp->b_rptr;
if (ip6h->ip6_nxt != IPPROTO_ICMPV6)
hdr_length = ip_hdr_length_v6(mp, ip6h);
else
hdr_length = IPV6_HDR_LEN;
icmp6 = (icmp6_t *)(&mp->b_rptr[hdr_length]);
if (!icmp_inbound_verify_v6(mp, icmp6, ira)) {
freemsg(mp);
return;
}
icmp_inbound_error_fanout_v6(mp, icmp6, ira);
return;
case IPPROTO_IPV6: {
ip6_t *in_ip6h;
in_ip6h = (ip6_t *)((uint8_t *)ip6h + hdr_length);
if (IN6_ARE_ADDR_EQUAL(&in_ip6h->ip6_src, &ip6h->ip6_src) &&
IN6_ARE_ADDR_EQUAL(&in_ip6h->ip6_dst, &ip6h->ip6_dst)) {
uint16_t unused_len;
if (!ip_hdr_length_nexthdr_v6(mp, in_ip6h,
&unused_len, &nexthdrp) ||
*nexthdrp == IPPROTO_IPV6) {
goto drop_pkt;
}
bcopy(in_ip6h, ip6h, mp->b_wptr - (uchar_t *)in_ip6h);
mp->b_wptr -= hdr_length;
ip6h = (ip6_t *)mp->b_rptr;
if (ip6h->ip6_nxt != IPPROTO_ICMPV6)
hdr_length = ip_hdr_length_v6(mp, ip6h);
else
hdr_length = IPV6_HDR_LEN;
icmp6 = (icmp6_t *)(&mp->b_rptr[hdr_length]);
if (!icmp_inbound_verify_v6(mp, icmp6, ira)) {
freemsg(mp);
return;
}
icmp_inbound_error_fanout_v6(mp, icmp6, ira);
return;
}
}
case IPPROTO_ENCAP:
if ((connp = ipcl_iptun_classify_v6(&rip6h.ip6_src,
&rip6h.ip6_dst, ipst)) != NULL) {
ira->ira_flags |= IRAF_ICMP_ERROR;
connp->conn_recvicmp(connp, mp, NULL, ira);
CONN_DEC_REF(connp);
ira->ira_flags &= ~IRAF_ICMP_ERROR;
return;
}
default:
ira->ira_flags |= IRAF_ICMP_ERROR;
ASSERT(ira->ira_protocol == nexthdr);
ip_fanout_proto_v6(mp, &rip6h, ira);
ira->ira_flags &= ~IRAF_ICMP_ERROR;
return;
}
drop_pkt:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
ip1dbg(("icmp_inbound_error_fanout_v6: drop pkt\n"));
freemsg(mp);
}
static void
icmp_redirect_v6(mblk_t *mp, ip6_t *ip6h, nd_redirect_t *rd,
ip_recv_attr_t *ira)
{
ire_t *ire, *nire;
ire_t *prev_ire = NULL;
ire_t *redir_ire;
in6_addr_t *src, *dst, *gateway;
nd_opt_hdr_t *opt;
nce_t *nce;
int ncec_flags = 0;
int err = 0;
boolean_t redirect_to_router = B_FALSE;
int len;
int optlen;
ill_t *ill = ira->ira_rill;
ill_t *rill = ira->ira_rill;
ip_stack_t *ipst = ill->ill_ipst;
if (IS_UNDER_IPMP(rill)) {
rill = ipmp_ill_hold_ipmp_ill(rill);
if (rill == NULL) {
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects);
ip_drop_input("ipv6IfIcmpInBadRedirects - IPMP ill",
mp, ill);
freemsg(mp);
return;
}
ASSERT(rill != ira->ira_rill);
}
len = mp->b_wptr - (uchar_t *)rd;
src = &ip6h->ip6_src;
dst = &rd->nd_rd_dst;
gateway = &rd->nd_rd_target;
if (!IN6_IS_ADDR_LINKLOCAL(src) ||
(ip6h->ip6_hops != IPV6_MAX_HOPS) ||
(rd->nd_rd_code != 0) ||
(len < sizeof (nd_redirect_t)) ||
(IN6_IS_ADDR_V4MAPPED(dst)) ||
(IN6_IS_ADDR_MULTICAST(dst))) {
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects);
ip_drop_input("ipv6IfIcmpInBadRedirects - addr/len", mp, ill);
goto fail_redirect;
}
if (!(IN6_IS_ADDR_LINKLOCAL(gateway) ||
IN6_ARE_ADDR_EQUAL(gateway, dst))) {
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects);
ip_drop_input("ipv6IfIcmpInBadRedirects - bad gateway",
mp, ill);
goto fail_redirect;
}
optlen = len - sizeof (nd_redirect_t);
if (optlen != 0) {
if (!ndp_verify_optlen((nd_opt_hdr_t *)&rd[1], optlen)) {
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects);
ip_drop_input("ipv6IfIcmpInBadRedirects - options",
mp, ill);
goto fail_redirect;
}
}
if (!IN6_ARE_ADDR_EQUAL(gateway, dst)) {
redirect_to_router = B_TRUE;
ncec_flags |= NCE_F_ISROUTER;
} else {
gateway = dst;
}
prev_ire = ire_ftable_lookup_v6(dst, 0, 0, 0, rill,
ALL_ZONES, NULL, MATCH_IRE_ILL, 0, ipst, NULL);
if (prev_ire == NULL ||
(prev_ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK)) ||
(prev_ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) ||
!IN6_ARE_ADDR_EQUAL(src, &prev_ire->ire_gateway_addr_v6)) {
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects);
ip_drop_input("ipv6IfIcmpInBadRedirects - ire", mp, ill);
goto fail_redirect;
}
ASSERT(prev_ire->ire_ill != NULL);
if (prev_ire->ire_ill->ill_flags & ILLF_NONUD)
ncec_flags |= NCE_F_NONUD;
opt = (nd_opt_hdr_t *)&rd[1];
opt = ndp_get_option(opt, optlen, ND_OPT_TARGET_LINKADDR);
if (opt != NULL) {
err = nce_lookup_then_add_v6(rill,
(uchar_t *)&opt[1],
rill->ill_phys_addr_length,
gateway, ncec_flags, ND_STALE, &nce);
switch (err) {
case 0:
nce_refrele(nce);
break;
case EEXIST:
nce_process(nce->nce_common,
(uchar_t *)&opt[1], 0, B_FALSE);
nce_refrele(nce);
break;
default:
ip1dbg(("icmp_redirect_v6: NCE create failed %d\n",
err));
goto fail_redirect;
}
}
if (redirect_to_router) {
ASSERT(IN6_IS_ADDR_LINKLOCAL(gateway));
ire = ire_create_v6(
dst,
&ipv6_all_ones,
gateway,
IRE_HOST,
prev_ire->ire_ill,
ALL_ZONES,
(RTF_DYNAMIC | RTF_GATEWAY | RTF_HOST),
NULL,
ipst);
} else {
ipif_t *ipif;
in6_addr_t gw;
mutex_enter(&rill->ill_lock);
for (ipif = rill->ill_ipif; ipif != NULL;
ipif = ipif->ipif_next) {
if (!(ipif->ipif_state_flags & IPIF_CONDEMNED) &&
IN6_IS_ADDR_LINKLOCAL(&ipif->ipif_v6lcl_addr))
break;
}
if (ipif == NULL) {
mutex_exit(&rill->ill_lock);
goto fail_redirect;
}
gw = ipif->ipif_v6lcl_addr;
mutex_exit(&rill->ill_lock);
ire = ire_create_v6(
dst,
&ipv6_all_ones,
&gw,
rill->ill_net_type,
prev_ire->ire_ill,
ALL_ZONES,
(RTF_DYNAMIC | RTF_HOST),
NULL,
ipst);
}
if (ire == NULL)
goto fail_redirect;
nire = ire_add(ire);
if (nire != NULL && nire != ire) {
ASSERT(nire->ire_identical_ref > 1);
ire_delete(nire);
ire_refrele(nire);
nire = NULL;
}
ire = nire;
if (ire != NULL) {
ire_refrele(ire);
ip_rts_change_v6(RTM_REDIRECT,
&rd->nd_rd_dst,
&rd->nd_rd_target,
&ipv6_all_ones, 0, src,
(RTF_DYNAMIC | RTF_GATEWAY | RTF_HOST), 0,
(RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_AUTHOR), ipst);
redir_ire = ire_ftable_lookup_v6(dst, 0, src, IRE_HOST,
prev_ire->ire_ill, ALL_ZONES, NULL,
(MATCH_IRE_GW | MATCH_IRE_TYPE | MATCH_IRE_ILL), 0, ipst,
NULL);
if (redir_ire != NULL) {
if (redir_ire->ire_flags & RTF_DYNAMIC)
ire_delete(redir_ire);
ire_refrele(redir_ire);
}
}
ire_refrele(prev_ire);
prev_ire = NULL;
fail_redirect:
if (prev_ire != NULL)
ire_refrele(prev_ire);
freemsg(mp);
if (rill != ira->ira_rill)
ill_refrele(rill);
}
static void
icmp_pkt_v6(mblk_t *mp, void *stuff, size_t len,
const in6_addr_t *v6src_ptr, ip_recv_attr_t *ira)
{
ip6_t *ip6h;
in6_addr_t v6dst;
size_t len_needed;
size_t msg_len;
mblk_t *mp1;
icmp6_t *icmp6;
in6_addr_t v6src;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
ip_xmit_attr_t ixas;
ip6h = (ip6_t *)mp->b_rptr;
bzero(&ixas, sizeof (ixas));
ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6;
ixas.ixa_zoneid = ira->ira_zoneid;
ixas.ixa_ifindex = 0;
ixas.ixa_ipst = ipst;
ixas.ixa_cred = kcred;
ixas.ixa_cpid = NOPID;
ixas.ixa_tsl = ira->ira_tsl;
ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
if (IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_src)) {
ixas.ixa_flags |= IXAF_SCOPEID_SET;
if (IS_UNDER_IPMP(ill))
ixas.ixa_scopeid = ill_get_upper_ifindex(ill);
else
ixas.ixa_scopeid = ill->ill_phyint->phyint_ifindex;
}
if (ira->ira_flags & IRAF_IPSEC_SECURE) {
if (!ipsec_in_to_out(ira, &ixas, mp, NULL, ip6h)) {
BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards);
return;
}
} else {
ixas.ixa_flags |= IXAF_NO_IPSEC;
}
if (v6src_ptr != NULL) {
v6src = *v6src_ptr;
} else {
ire_t *ire;
uint_t match_flags = MATCH_IRE_TYPE | MATCH_IRE_ZONEONLY;
if (IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src) ||
IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_dst))
match_flags |= MATCH_IRE_ILL;
ire = ire_ftable_lookup_v6(&ip6h->ip6_dst, 0, 0,
(IRE_LOCAL|IRE_LOOPBACK), ill, ira->ira_zoneid, NULL,
match_flags, 0, ipst, NULL);
if (ire != NULL) {
v6src = ip6h->ip6_dst;
ire_refrele(ire);
} else {
v6src = ipv6_all_zeros;
ixas.ixa_flags |= IXAF_SET_SOURCE;
}
}
v6dst = ip6h->ip6_src;
len_needed = ipst->ips_ipv6_icmp_return - IPV6_HDR_LEN - len;
msg_len = msgdsize(mp);
if (msg_len > len_needed) {
if (!adjmsg(mp, len_needed - msg_len)) {
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutErrors);
freemsg(mp);
return;
}
msg_len = len_needed;
}
mp1 = allocb(IPV6_HDR_LEN + len, BPRI_MED);
if (mp1 == NULL) {
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutErrors);
freemsg(mp);
return;
}
mp1->b_cont = mp;
mp = mp1;
ixas.ixa_flags |= IXAF_TRUSTED_ICMP;
ip6h = (ip6_t *)mp->b_rptr;
mp1->b_wptr = (uchar_t *)ip6h + (IPV6_HDR_LEN + len);
ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
ip6h->ip6_nxt = IPPROTO_ICMPV6;
ip6h->ip6_hops = ipst->ips_ipv6_def_hops;
ip6h->ip6_dst = v6dst;
ip6h->ip6_src = v6src;
msg_len += IPV6_HDR_LEN + len;
if (msg_len > IP_MAXPACKET + IPV6_HDR_LEN) {
(void) adjmsg(mp, IP_MAXPACKET + IPV6_HDR_LEN - msg_len);
msg_len = IP_MAXPACKET + IPV6_HDR_LEN;
}
ip6h->ip6_plen = htons((uint16_t)(msgdsize(mp) - IPV6_HDR_LEN));
icmp6 = (icmp6_t *)&ip6h[1];
bcopy(stuff, (char *)icmp6, len);
icmp6->icmp6_cksum = ip6h->ip6_plen;
if (icmp6->icmp6_type == ND_REDIRECT) {
ip6h->ip6_hops = IPV6_MAX_HOPS;
}
(void) ip_output_simple(mp, &ixas);
ixa_cleanup(&ixas);
}
void
icmp_update_out_mib_v6(ill_t *ill, icmp6_t *icmp6)
{
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutMsgs);
switch (icmp6->icmp6_type) {
case ICMP6_DST_UNREACH:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutDestUnreachs);
if (icmp6->icmp6_code == ICMP6_DST_UNREACH_ADMIN)
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutAdminProhibs);
break;
case ICMP6_TIME_EXCEEDED:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutTimeExcds);
break;
case ICMP6_PARAM_PROB:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutParmProblems);
break;
case ICMP6_PACKET_TOO_BIG:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutPktTooBigs);
break;
case ICMP6_ECHO_REQUEST:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutEchos);
break;
case ICMP6_ECHO_REPLY:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutEchoReplies);
break;
case ND_ROUTER_SOLICIT:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutRouterSolicits);
break;
case ND_ROUTER_ADVERT:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutRouterAdvertisements);
break;
case ND_NEIGHBOR_SOLICIT:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutNeighborSolicits);
break;
case ND_NEIGHBOR_ADVERT:
BUMP_MIB(ill->ill_icmp6_mib,
ipv6IfIcmpOutNeighborAdvertisements);
break;
case ND_REDIRECT:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutRedirects);
break;
case MLD_LISTENER_QUERY:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutGroupMembQueries);
break;
case MLD_LISTENER_REPORT:
case MLD_V2_LISTENER_REPORT:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutGroupMembResponses);
break;
case MLD_LISTENER_REDUCTION:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutGroupMembReductions);
break;
}
}
static mblk_t *
icmp_pkt_err_ok_v6(mblk_t *mp, boolean_t mcast_ok, ip_recv_attr_t *ira)
{
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
boolean_t llbcast;
ip6_t *ip6h;
if (!mp)
return (NULL);
llbcast = (ira->ira_flags &
(IRAF_L2DST_MULTICAST|IRAF_L2DST_BROADCAST)) != 0;
ip6h = (ip6_t *)mp->b_rptr;
if (IN6_IS_ADDR_MULTICAST(&ip6h->ip6_src) ||
IN6_IS_ADDR_V4MAPPED(&ip6h->ip6_src) ||
IN6_IS_ADDR_UNSPECIFIED(&ip6h->ip6_src)) {
freemsg(mp);
return (NULL);
}
if (ip6h->ip6_nxt == IPPROTO_ICMPV6) {
size_t len_needed = IPV6_HDR_LEN + ICMP6_MINLEN;
icmp6_t *icmp6;
if (mp->b_wptr - mp->b_rptr < len_needed) {
if (!pullupmsg(mp, len_needed)) {
BUMP_MIB(ill->ill_icmp6_mib,
ipv6IfIcmpInErrors);
freemsg(mp);
return (NULL);
}
ip6h = (ip6_t *)mp->b_rptr;
}
icmp6 = (icmp6_t *)&ip6h[1];
if (ICMP6_IS_ERROR(icmp6->icmp6_type) ||
icmp6->icmp6_type == ND_REDIRECT) {
freemsg(mp);
return (NULL);
}
}
if (!mcast_ok &&
(llbcast || IN6_IS_ADDR_MULTICAST(&ip6h->ip6_dst))) {
freemsg(mp);
return (NULL);
}
if (is_system_labeled() && !tsol_can_reply_error(mp, ira)) {
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutErrors);
freemsg(mp);
return (NULL);
}
if (icmp_err_rate_limit(ipst)) {
freemsg(mp);
return (NULL);
}
return (mp);
}
void
ip_send_potential_redirect_v6(mblk_t *mp, ip6_t *ip6h, ire_t *ire,
ip_recv_attr_t *ira)
{
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
in6_addr_t *v6targ;
ire_t *src_ire_v6 = NULL;
mblk_t *mp1;
ire_t *nhop_ire = NULL;
if (ip_source_routed_v6(ip6h, mp, ipst))
return;
if (ire->ire_type & IRE_ONLINK) {
v6targ = &ip6h->ip6_dst;
} else {
nhop_ire = ire_nexthop(ire);
if (nhop_ire == NULL)
return;
if (!IN6_IS_ADDR_LINKLOCAL(&nhop_ire->ire_addr_v6)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInAddrErrors);
ip_drop_input("ipIfStatsInAddrErrors", mp, ill);
ire_refrele(nhop_ire);
return;
}
v6targ = &nhop_ire->ire_addr_v6;
}
src_ire_v6 = ire_ftable_lookup_v6(&ip6h->ip6_src,
NULL, NULL, IRE_INTERFACE, ire->ire_ill, ALL_ZONES, NULL,
MATCH_IRE_ILL | MATCH_IRE_TYPE, 0, ipst, NULL);
if (src_ire_v6 == NULL) {
if (nhop_ire != NULL)
ire_refrele(nhop_ire);
return;
}
mp1 = copymsg(mp);
if (mp1 != NULL)
icmp_send_redirect_v6(mp1, v6targ, &ip6h->ip6_dst, ira);
if (nhop_ire != NULL)
ire_refrele(nhop_ire);
ire_refrele(src_ire_v6);
}
static void
icmp_send_redirect_v6(mblk_t *mp, in6_addr_t *targetp, in6_addr_t *dest,
ip_recv_attr_t *ira)
{
nd_redirect_t *rd;
nd_opt_rd_hdr_t *rdh;
uchar_t *buf;
ncec_t *ncec = NULL;
nd_opt_hdr_t *opt;
int len;
int ll_opt_len = 0;
int max_redir_hdr_data_len;
int pkt_len;
in6_addr_t *srcp;
ill_t *ill;
boolean_t need_refrele;
ip_stack_t *ipst = ira->ira_ill->ill_ipst;
mp = icmp_pkt_err_ok_v6(mp, B_FALSE, ira);
if (mp == NULL)
return;
if (IS_UNDER_IPMP(ira->ira_ill)) {
ill = ipmp_ill_hold_ipmp_ill(ira->ira_ill);
if (ill == NULL) {
ill = ira->ira_ill;
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects);
ip_drop_output("no IPMP ill for sending redirect",
mp, ill);
freemsg(mp);
return;
}
need_refrele = B_TRUE;
} else {
ill = ira->ira_ill;
need_refrele = B_FALSE;
}
ncec = ncec_lookup_illgrp_v6(ill, targetp);
if (ncec != NULL && ncec->ncec_state != ND_INCOMPLETE &&
ncec->ncec_lladdr != NULL) {
ll_opt_len = (sizeof (nd_opt_hdr_t) +
ill->ill_phys_addr_length + 7)/8 * 8;
}
len = sizeof (nd_redirect_t) + sizeof (nd_opt_rd_hdr_t) + ll_opt_len;
ASSERT(len % 4 == 0);
buf = kmem_alloc(len, KM_NOSLEEP);
if (buf == NULL) {
if (ncec != NULL)
ncec_refrele(ncec);
if (need_refrele)
ill_refrele(ill);
freemsg(mp);
return;
}
rd = (nd_redirect_t *)buf;
rd->nd_rd_type = (uint8_t)ND_REDIRECT;
rd->nd_rd_code = 0;
rd->nd_rd_reserved = 0;
rd->nd_rd_target = *targetp;
rd->nd_rd_dst = *dest;
opt = (nd_opt_hdr_t *)(buf + sizeof (nd_redirect_t));
if (ncec != NULL && ll_opt_len != 0) {
opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
opt->nd_opt_len = ll_opt_len/8;
bcopy((char *)ncec->ncec_lladdr, &opt[1],
ill->ill_phys_addr_length);
}
if (ncec != NULL)
ncec_refrele(ncec);
rdh = (nd_opt_rd_hdr_t *)(buf + sizeof (nd_redirect_t) + ll_opt_len);
rdh->nd_opt_rh_type = (uint8_t)ND_OPT_REDIRECTED_HEADER;
max_redir_hdr_data_len =
(ipst->ips_ipv6_icmp_return - IPV6_HDR_LEN - len)/8*8;
pkt_len = msgdsize(mp);
if (pkt_len > max_redir_hdr_data_len) {
rdh->nd_opt_rh_len = (max_redir_hdr_data_len +
sizeof (nd_opt_rd_hdr_t))/8;
(void) adjmsg(mp, max_redir_hdr_data_len - pkt_len);
} else {
rdh->nd_opt_rh_len = (pkt_len + sizeof (nd_opt_rd_hdr_t))/8;
(void) adjmsg(mp, -(pkt_len % 8));
}
rdh->nd_opt_rh_reserved1 = 0;
rdh->nd_opt_rh_reserved2 = 0;
srcp = &ill->ill_ipif->ipif_v6lcl_addr;
ASSERT(ira->ira_zoneid == ALL_ZONES);
ira->ira_zoneid = GLOBAL_ZONEID;
icmp_pkt_v6(mp, buf, len, srcp, ira);
kmem_free(buf, len);
if (need_refrele)
ill_refrele(ill);
}
void
icmp_time_exceeded_v6(mblk_t *mp, uint8_t code, boolean_t mcast_ok,
ip_recv_attr_t *ira)
{
icmp6_t icmp6;
mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira);
if (mp == NULL)
return;
bzero(&icmp6, sizeof (icmp6_t));
icmp6.icmp6_type = ICMP6_TIME_EXCEEDED;
icmp6.icmp6_code = code;
icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira);
}
void
icmp_unreachable_v6(mblk_t *mp, uint8_t code, boolean_t mcast_ok,
ip_recv_attr_t *ira)
{
icmp6_t icmp6;
mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira);
if (mp == NULL)
return;
bzero(&icmp6, sizeof (icmp6_t));
icmp6.icmp6_type = ICMP6_DST_UNREACH;
icmp6.icmp6_code = code;
icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira);
}
void
icmp_pkt2big_v6(mblk_t *mp, uint32_t mtu, boolean_t mcast_ok,
ip_recv_attr_t *ira)
{
icmp6_t icmp6;
mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira);
if (mp == NULL)
return;
bzero(&icmp6, sizeof (icmp6_t));
icmp6.icmp6_type = ICMP6_PACKET_TOO_BIG;
icmp6.icmp6_code = 0;
icmp6.icmp6_mtu = htonl(mtu);
icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira);
}
static void
icmp_param_problem_v6(mblk_t *mp, uint8_t code, uint32_t offset,
boolean_t mcast_ok, ip_recv_attr_t *ira)
{
icmp6_t icmp6;
mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira);
if (mp == NULL)
return;
bzero((char *)&icmp6, sizeof (icmp6_t));
icmp6.icmp6_type = ICMP6_PARAM_PROB;
icmp6.icmp6_code = code;
icmp6.icmp6_pptr = htonl(offset);
icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira);
}
void
icmp_param_problem_nexthdr_v6(mblk_t *mp, boolean_t mcast_ok,
ip_recv_attr_t *ira)
{
ip6_t *ip6h = (ip6_t *)mp->b_rptr;
uint16_t hdr_length;
uint8_t *nexthdrp;
uint32_t offset;
ill_t *ill = ira->ira_ill;
if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
freemsg(mp);
return;
}
offset = nexthdrp - mp->b_rptr;
icmp_param_problem_v6(mp, ICMP6_PARAMPROB_NEXTHEADER, offset,
mcast_ok, ira);
}
ip_laddr_t
ip_laddr_verify_v6(const in6_addr_t *v6src, zoneid_t zoneid,
ip_stack_t *ipst, boolean_t allow_mcbc, uint_t scopeid)
{
ire_t *src_ire;
uint_t match_flags;
ill_t *ill = NULL;
ASSERT(!IN6_IS_ADDR_V4MAPPED(v6src));
ASSERT(!IN6_IS_ADDR_UNSPECIFIED(v6src));
match_flags = MATCH_IRE_ZONEONLY;
if (scopeid != 0) {
ill = ill_lookup_on_ifindex(scopeid, B_TRUE, ipst);
if (ill == NULL)
return (IPVL_BAD);
match_flags |= MATCH_IRE_ILL;
}
src_ire = ire_ftable_lookup_v6(v6src, NULL, NULL, 0,
ill, zoneid, NULL, match_flags, 0, ipst, NULL);
if (ill != NULL)
ill_refrele(ill);
if (src_ire != NULL && (src_ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK))) {
ire_refrele(src_ire);
return (IPVL_UNICAST_UP);
} else if (IN6_IS_ADDR_MULTICAST(v6src)) {
if (src_ire != NULL)
ire_refrele(src_ire);
if (allow_mcbc)
return (IPVL_MCAST);
else
return (IPVL_BAD);
} else {
ipif_t *ipif;
if (src_ire != NULL)
ire_refrele(src_ire);
ipif = ipif_lookup_addr_v6(v6src, NULL, zoneid, ipst);
if (ipif == NULL)
return (IPVL_BAD);
if (ipif->ipif_flags & (IPIF_NOLOCAL | IPIF_ANYCAST)) {
ipif_refrele(ipif);
return (IPVL_BAD);
}
ipif_refrele(ipif);
return (IPVL_UNICAST_DOWN);
}
}
int
ip_set_destination_v6(in6_addr_t *src_addrp, const in6_addr_t *dst_addr,
const in6_addr_t *firsthop, ip_xmit_attr_t *ixa, iulp_t *uinfo,
uint32_t flags, uint_t mac_mode)
{
ire_t *ire;
int error = 0;
in6_addr_t setsrc;
zoneid_t zoneid = ixa->ixa_zoneid;
ip_stack_t *ipst = ixa->ixa_ipst;
dce_t *dce;
uint_t pmtu;
uint_t ifindex;
uint_t generation;
nce_t *nce;
ill_t *ill = NULL;
boolean_t multirt = B_FALSE;
ASSERT(!IN6_IS_ADDR_V4MAPPED(dst_addr));
ASSERT(!(ixa->ixa_flags & IXAF_IS_IPV4));
ASSERT(!IN6_IS_ADDR_UNSPECIFIED(dst_addr));
if (is_system_labeled()) {
ts_label_t *tsl = NULL;
error = tsol_check_dest(ixa->ixa_tsl, dst_addr, IPV6_VERSION,
mac_mode, (flags & IPDF_ZONE_IS_GLOBAL) != 0, &tsl);
if (error != 0)
return (error);
if (tsl != NULL) {
ip_xmit_attr_replace_tsl(ixa, tsl);
}
}
setsrc = ipv6_all_zeros;
ire = ip_select_route_v6(firsthop, *src_addrp, ixa, &generation,
&setsrc, &error, &multirt);
ASSERT(ire != NULL);
if (error != 0)
goto bad_addr;
if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
ASSERT(generation == IRE_GENERATION_VERIFY);
if (flags & IPDF_VERIFY_DST) {
if (!(ire->ire_type & IRE_HOST))
error = ENETUNREACH;
else
error = EHOSTUNREACH;
}
}
if ((ire->ire_type & (IRE_BROADCAST|IRE_MULTICAST)) &&
!(flags & IPDF_ALLOW_MCBC)) {
ire_refrele(ire);
ire = ire_reject(ipst, B_FALSE);
generation = IRE_GENERATION_VERIFY;
error = ENETUNREACH;
}
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 = generation;
ifindex = 0;
if (IN6_IS_ADDR_LINKSCOPE(dst_addr)) {
if (ill != NULL)
ifindex = ill->ill_phyint->phyint_ifindex;
else
flags &= ~IPDF_UNIQUE_DCE;
}
if (flags & IPDF_UNIQUE_DCE) {
dce = dce_lookup_and_add_v6(dst_addr, ifindex, ipst);
if (dce != NULL) {
generation = dce->dce_generation;
} else {
dce = dce_lookup_v6(dst_addr, ifindex, ipst,
&generation);
}
} else {
dce = dce_lookup_v6(dst_addr, ifindex, ipst, &generation);
}
ASSERT(dce != NULL);
if (ixa->ixa_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 = generation;
if (multirt) {
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))) {
nce = ire_to_nce(ire, 0, firsthop);
if (nce == NULL) {
ixa->ixa_ire_generation = IRE_GENERATION_VERIFY;
} else {
if (ixa->ixa_nce != NULL)
nce_refrele(ixa->ixa_nce);
ixa->ixa_nce = nce;
}
}
if (IN6_IS_ADDR_LOOPBACK(src_addrp)) {
if ((ire->ire_type & IRE_LOCAL) && ire->ire_zoneid != zoneid) {
ire = NULL;
error = EADDRNOTAVAIL;
goto bad_addr;
}
if (!(ire->ire_type & (IRE_LOOPBACK|IRE_LOCAL|IRE_MULTICAST))) {
ire = NULL;
error = EADDRNOTAVAIL;
goto bad_addr;
}
}
if (flags & IPDF_SELECT_SRC) {
in6_addr_t src_addr;
ill = ire_nexthop_ill(ire);
if (ill == NULL) {
src_addr = ipv6_loopback;
generation = SRC_GENERATION_VERIFY;
} else {
error = ip_select_source_v6(ill, &setsrc, dst_addr,
zoneid, ipst, B_FALSE, ixa->ixa_src_preferences,
&src_addr, &generation, NULL);
if (error != 0) {
ire = NULL;
goto bad_addr;
}
}
if (IN6_IS_ADDR_LOOPBACK(&src_addr) &&
!(ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK|IRE_MULTICAST)) &&
!(ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE))) {
ire = NULL;
error = EADDRNOTAVAIL;
goto bad_addr;
}
*src_addrp = src_addr;
ixa->ixa_src_generation = generation;
}
nce = ixa->ixa_nce;
if (nce != NULL && nce->nce_is_condemned) {
nce_refrele(nce);
ixa->ixa_nce = NULL;
ixa->ixa_ire_generation = IRE_GENERATION_VERIFY;
}
pmtu = ip_get_pmtu(ixa);
ixa->ixa_fragsize = pmtu;
if (ixa->ixa_flags & IXAF_VERIFY_PMTU)
ixa->ixa_pmtu = pmtu;
if (uinfo != NULL) {
bzero(uinfo, sizeof (*uinfo));
if (dce->dce_flags & DCEF_UINFO)
*uinfo = dce->dce_uinfo;
rts_merge_metrics(uinfo, &ire->ire_metrics);
if (uinfo->iulp_mtu == 0 || uinfo->iulp_mtu > pmtu)
uinfo->iulp_mtu = pmtu;
uinfo->iulp_localnet = (ire->ire_type & IRE_ONLINK) != 0;
uinfo->iulp_loopback = (ire->ire_type & IRE_LOOPBACK) != 0;
uinfo->iulp_local = (ire->ire_type & IRE_LOCAL) != 0;
}
if (ill != NULL)
ill_refrele(ill);
return (error);
bad_addr:
if (ire != NULL)
ire_refrele(ire);
if (ill != NULL)
ill_refrele(ill);
nce = ixa->ixa_nce;
if (nce != NULL && nce->nce_is_condemned) {
nce_refrele(nce);
ixa->ixa_nce = NULL;
ixa->ixa_ire_generation = IRE_GENERATION_VERIFY;
}
return (error);
}
void
ip_fanout_proto_v6(mblk_t *mp, ip6_t *ip6h, ip_recv_attr_t *ira)
{
mblk_t *mp1;
in6_addr_t laddr = ip6h->ip6_dst;
conn_t *connp, *first_connp, *next_connp;
connf_t *connfp;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
connfp = &ipst->ips_ipcl_proto_fanout_v6[ira->ira_protocol];
mutex_enter(&connfp->connf_lock);
connp = connfp->connf_head;
for (connp = connfp->connf_head; connp != NULL;
connp = connp->conn_next) {
if (IPCL_PROTO_MATCH_V6(connp, ira, ip6h) &&
(!(ira->ira_flags & IRAF_SYSTEM_LABELED) ||
tsol_receive_local(mp, &laddr, IPV6_VERSION, ira, connp)))
break;
}
if (connp == NULL) {
mutex_exit(&connfp->connf_lock);
ip_fanout_send_icmp_v6(mp, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_NEXTHEADER, ira);
return;
}
ASSERT(IPCL_IS_NONSTR(connp) || connp->conn_rq != NULL);
CONN_INC_REF(connp);
first_connp = connp;
connp = connp->conn_next;
for (;;) {
while (connp != NULL) {
if (IPCL_PROTO_MATCH_V6(connp, ira, ip6h) &&
(!(ira->ira_flags & IRAF_SYSTEM_LABELED) ||
tsol_receive_local(mp, &laddr, IPV6_VERSION,
ira, connp)))
break;
connp = connp->conn_next;
}
if (connp == NULL) {
connp = first_connp;
break;
}
if (((mp1 = dupmsg(mp)) == NULL) &&
((mp1 = copymsg(mp)) == NULL)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
connp = first_connp;
break;
}
CONN_INC_REF(connp);
mutex_exit(&connfp->connf_lock);
ip_fanout_proto_conn(connp, mp1, NULL, (ip6_t *)mp1->b_rptr,
ira);
mutex_enter(&connfp->connf_lock);
next_connp = connp->conn_next;
CONN_DEC_REF(connp);
connp = next_connp;
}
mutex_exit(&connfp->connf_lock);
ip_fanout_proto_conn(connp, mp, NULL, ip6h, ira);
CONN_DEC_REF(connp);
}
void
ip_fanout_send_icmp_v6(mblk_t *mp, uint_t icmp_type, uint8_t icmp_code,
ip_recv_attr_t *ira)
{
ip6_t *ip6h;
boolean_t secure;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
netstack_t *ns = ipst->ips_netstack;
ipsec_stack_t *ipss = ns->netstack_ipsec;
secure = ira->ira_flags & IRAF_IPSEC_SECURE;
ip6h = (ip6_t *)mp->b_rptr;
if (secure || ipss->ipsec_inbound_v6_policy_present) {
mp = ipsec_check_global_policy(mp, NULL, NULL, ip6h, ira, ns);
if (mp == NULL)
return;
}
if (ira->ira_protocol == IPPROTO_ICMPV6) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ip_fanout_send_icmp_v6", mp, ill);
freemsg(mp);
return;
}
switch (icmp_type) {
case ICMP6_DST_UNREACH:
ASSERT(icmp_code == ICMP6_DST_UNREACH_NOPORT);
BUMP_MIB(ill->ill_ip_mib, udpIfStatsNoPorts);
ip_drop_input("ipIfStatsNoPorts", mp, ill);
icmp_unreachable_v6(mp, icmp_code, B_FALSE, ira);
break;
case ICMP6_PARAM_PROB:
ASSERT(icmp_code == ICMP6_PARAMPROB_NEXTHEADER);
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInUnknownProtos);
ip_drop_input("ipIfStatsInUnknownProtos", mp, ill);
icmp_param_problem_nexthdr_v6(mp, B_FALSE, ira);
break;
default:
#ifdef DEBUG
panic("ip_fanout_send_icmp_v6: wrong type");
#else
freemsg(mp);
break;
#endif
}
}
void
ip_fanout_udp_multi_v6(mblk_t *mp, ip6_t *ip6h, uint16_t lport, uint16_t fport,
ip_recv_attr_t *ira)
{
in6_addr_t laddr;
conn_t *connp;
connf_t *connfp;
in6_addr_t faddr;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
ASSERT(ira->ira_flags & (IRAF_MULTIBROADCAST|IRAF_ICMP_ERROR));
laddr = ip6h->ip6_dst;
faddr = ip6h->ip6_src;
connfp = &ipst->ips_ipcl_udp_fanout[IPCL_UDP_HASH(lport, ipst)];
mutex_enter(&connfp->connf_lock);
connp = connfp->connf_head;
while (connp != NULL) {
if ((IPCL_UDP_MATCH_V6(connp, lport, laddr, fport, faddr)) &&
conn_wantpacket_v6(connp, ira, ip6h) &&
(!(ira->ira_flags & IRAF_SYSTEM_LABELED) ||
tsol_receive_local(mp, &laddr, IPV6_VERSION, ira, connp)))
break;
connp = connp->conn_next;
}
if (connp == NULL)
goto notfound;
CONN_INC_REF(connp);
if (connp->conn_reuseaddr) {
conn_t *first_connp = connp;
conn_t *next_connp;
mblk_t *mp1;
connp = connp->conn_next;
for (;;) {
while (connp != NULL) {
if (IPCL_UDP_MATCH_V6(connp, lport, laddr,
fport, faddr) &&
conn_wantpacket_v6(connp, ira, ip6h) &&
(!(ira->ira_flags & IRAF_SYSTEM_LABELED) ||
tsol_receive_local(mp, &laddr, IPV6_VERSION,
ira, connp)))
break;
connp = connp->conn_next;
}
if (connp == NULL) {
connp = first_connp;
break;
}
if (((mp1 = dupmsg(mp)) == NULL) &&
((mp1 = copymsg(mp)) == NULL)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
connp = first_connp;
break;
}
CONN_INC_REF(connp);
mutex_exit(&connfp->connf_lock);
IP6_STAT(ipst, ip6_udp_fanmb);
ip_fanout_udp_conn(connp, mp1, NULL,
(ip6_t *)mp1->b_rptr, ira);
mutex_enter(&connfp->connf_lock);
next_connp = connp->conn_next;
IP6_STAT(ipst, ip6_udp_fanmb);
CONN_DEC_REF(connp);
connp = next_connp;
}
}
mutex_exit(&connfp->connf_lock);
IP6_STAT(ipst, ip6_udp_fanmb);
ip_fanout_udp_conn(connp, mp, NULL, ip6h, ira);
CONN_DEC_REF(connp);
return;
notfound:
mutex_exit(&connfp->connf_lock);
if (ipst->ips_ipcl_proto_fanout_v6[IPPROTO_UDP].connf_head != NULL) {
ASSERT(ira->ira_protocol == IPPROTO_UDP);
ip_fanout_proto_v6(mp, ip6h, ira);
} else {
ip_fanout_send_icmp_v6(mp, ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_NOPORT, ira);
}
}
int
ip_find_hdr_v6(mblk_t *mp, ip6_t *ip6h, boolean_t label_separate, ip_pkt_t *ipp,
uint8_t *nexthdrp)
{
uint_t length, ehdrlen;
uint8_t nexthdr;
uint8_t *whereptr, *endptr;
ip6_dest_t *tmpdstopts;
ip6_rthdr_t *tmprthdr;
ip6_hbh_t *tmphopopts;
ip6_frag_t *tmpfraghdr;
ipp->ipp_fields |= IPPF_HOPLIMIT | IPPF_TCLASS | IPPF_ADDR;
ipp->ipp_hoplimit = ip6h->ip6_hops;
ipp->ipp_tclass = IPV6_FLOW_TCLASS(ip6h->ip6_flow);
ipp->ipp_addr = ip6h->ip6_dst;
length = IPV6_HDR_LEN;
whereptr = ((uint8_t *)&ip6h[1]);
endptr = mp->b_wptr;
nexthdr = ip6h->ip6_nxt;
while (whereptr < endptr) {
if (whereptr + MIN_EHDR_LEN > endptr)
goto done;
switch (nexthdr) {
case IPPROTO_HOPOPTS: {
uchar_t *secopt;
boolean_t hbh_needed;
uchar_t *after_secopt;
tmphopopts = (ip6_hbh_t *)whereptr;
ehdrlen = 8 * (tmphopopts->ip6h_len + 1);
if ((uchar_t *)tmphopopts + ehdrlen > endptr)
goto done;
nexthdr = tmphopopts->ip6h_nxt;
if (!label_separate) {
secopt = NULL;
after_secopt = whereptr;
} else {
(void) tsol_find_secopt_v6(whereptr, ehdrlen,
&secopt, &after_secopt, &hbh_needed);
}
if (secopt != NULL && after_secopt - whereptr > 0) {
ipp->ipp_fields |= IPPF_LABEL_V6;
ipp->ipp_label_v6 = secopt;
ipp->ipp_label_len_v6 = after_secopt - whereptr;
} else {
ipp->ipp_label_len_v6 = 0;
after_secopt = whereptr;
hbh_needed = B_TRUE;
}
if (hbh_needed && !(ipp->ipp_fields & IPPF_HOPOPTS)) {
ipp->ipp_fields |= IPPF_HOPOPTS;
ipp->ipp_hopopts = (ip6_hbh_t *)after_secopt;
ipp->ipp_hopoptslen = ehdrlen -
ipp->ipp_label_len_v6;
}
break;
}
case IPPROTO_DSTOPTS:
tmpdstopts = (ip6_dest_t *)whereptr;
ehdrlen = 8 * (tmpdstopts->ip6d_len + 1);
if ((uchar_t *)tmpdstopts + ehdrlen > endptr)
goto done;
nexthdr = tmpdstopts->ip6d_nxt;
if (!(ipp->ipp_fields & IPPF_DSTOPTS)) {
ipp->ipp_fields |= IPPF_DSTOPTS;
ipp->ipp_dstopts = tmpdstopts;
ipp->ipp_dstoptslen = ehdrlen;
}
break;
case IPPROTO_ROUTING:
tmprthdr = (ip6_rthdr_t *)whereptr;
ehdrlen = 8 * (tmprthdr->ip6r_len + 1);
if ((uchar_t *)tmprthdr + ehdrlen > endptr)
goto done;
nexthdr = tmprthdr->ip6r_nxt;
if (!(ipp->ipp_fields & IPPF_RTHDR)) {
ipp->ipp_fields |= IPPF_RTHDR;
ipp->ipp_rthdr = tmprthdr;
ipp->ipp_rthdrlen = ehdrlen;
}
if (ipp->ipp_fields & IPPF_DSTOPTS) {
ipp->ipp_fields &= ~IPPF_DSTOPTS;
ipp->ipp_fields |= IPPF_RTHDRDSTOPTS;
ipp->ipp_rthdrdstopts = ipp->ipp_dstopts;
ipp->ipp_dstopts = NULL;
ipp->ipp_rthdrdstoptslen = ipp->ipp_dstoptslen;
ipp->ipp_dstoptslen = 0;
}
break;
case IPPROTO_FRAGMENT:
tmpfraghdr = (ip6_frag_t *)whereptr;
ehdrlen = sizeof (ip6_frag_t);
if ((uchar_t *)tmpfraghdr + ehdrlen > endptr)
goto done;
nexthdr = tmpfraghdr->ip6f_nxt;
if (!(ipp->ipp_fields & IPPF_FRAGHDR)) {
ipp->ipp_fields |= IPPF_FRAGHDR;
ipp->ipp_fraghdr = tmpfraghdr;
ipp->ipp_fraghdrlen = ehdrlen;
}
break;
case IPPROTO_NONE:
default:
goto done;
}
length += ehdrlen;
whereptr += ehdrlen;
}
done:
if (nexthdrp != NULL)
*nexthdrp = nexthdr;
return (length);
}
uint16_t
ip_hdr_length_v6(mblk_t *mp, ip6_t *ip6h)
{
uint16_t hdr_len;
if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_len, NULL))
hdr_len = sizeof (*ip6h);
return (hdr_len);
}
int
ip_process_options_v6(mblk_t *mp, ip6_t *ip6h,
uint8_t *optptr, uint_t optlen, uint8_t hdr_type, ip_recv_attr_t *ira)
{
uint8_t opt_type;
uint_t optused = 0;
int ret = 0;
const char *errtype;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
while (optlen != 0) {
opt_type = *optptr;
if (opt_type == IP6OPT_PAD1) {
optused = 1;
} else {
if (optlen < 2)
goto bad_opt;
errtype = "malformed";
if (opt_type == ip6opt_ls) {
optused = 2 + optptr[1];
if (optused > optlen)
goto bad_opt;
} else switch (opt_type) {
case IP6OPT_PADN:
optused = 2 + optptr[1];
if (optused > optlen)
goto bad_opt;
break;
case IP6OPT_JUMBO:
if (hdr_type != IPPROTO_HOPOPTS)
goto opt_error;
goto opt_error;
case IP6OPT_ROUTER_ALERT: {
struct ip6_opt_router *or;
if (hdr_type != IPPROTO_HOPOPTS)
goto opt_error;
optused = 2 + optptr[1];
if (optused > optlen)
goto bad_opt;
or = (struct ip6_opt_router *)optptr;
if (optused != sizeof (*or) ||
((uintptr_t)or->ip6or_value & 0x1) != 0)
goto opt_error;
switch (*((uint16_t *)or->ip6or_value)) {
case IP6_ALERT_MLD:
case IP6_ALERT_RSVP:
ret = 1;
}
break;
}
case IP6OPT_HOME_ADDRESS: {
struct ip6_opt_home_address *oh;
in6_addr_t tmp;
if (ipst->ips_ipv6_ignore_home_address_opt)
goto opt_error;
if (hdr_type != IPPROTO_DSTOPTS)
goto opt_error;
optused = 2 + optptr[1];
if (optused > optlen)
goto bad_opt;
if ((ira->ira_flags & IRAF_IPSEC_SECURE) &&
ira->ira_ipsec_ah_sa != NULL)
break;
oh = (struct ip6_opt_home_address *)optptr;
if (optused < sizeof (*oh) ||
((uintptr_t)oh->ip6oh_addr & 0x7) != 0)
goto opt_error;
tmp = ip6h->ip6_src;
ip6h->ip6_src = *(in6_addr_t *)oh->ip6oh_addr;
*(in6_addr_t *)oh->ip6oh_addr = tmp;
break;
}
case IP6OPT_TUNNEL_LIMIT:
if (hdr_type != IPPROTO_DSTOPTS) {
goto opt_error;
}
optused = 2 + optptr[1];
if (optused > optlen) {
goto bad_opt;
}
if (optused != 3) {
goto opt_error;
}
break;
default:
errtype = "unknown";
opt_error:
switch (IP6OPT_TYPE(opt_type)) {
case IP6OPT_TYPE_SKIP:
optused = 2 + optptr[1];
if (optused > optlen)
goto bad_opt;
ip1dbg(("ip_process_options_v6: %s "
"opt 0x%x skipped\n",
errtype, opt_type));
break;
case IP6OPT_TYPE_DISCARD:
ip1dbg(("ip_process_options_v6: %s "
"opt 0x%x; packet dropped\n",
errtype, opt_type));
BUMP_MIB(ill->ill_ip_mib,
ipIfStatsInHdrErrors);
ip_drop_input("ipIfStatsInHdrErrors",
mp, ill);
freemsg(mp);
return (-1);
case IP6OPT_TYPE_ICMP:
BUMP_MIB(ill->ill_ip_mib,
ipIfStatsInHdrErrors);
ip_drop_input("ipIfStatsInHdrErrors",
mp, ill);
icmp_param_problem_v6(mp,
ICMP6_PARAMPROB_OPTION,
(uint32_t)(optptr -
(uint8_t *)ip6h),
B_FALSE, ira);
return (-1);
case IP6OPT_TYPE_FORCEICMP:
BUMP_MIB(ill->ill_ip_mib,
ipIfStatsInHdrErrors);
ip_drop_input("ipIfStatsInHdrErrors",
mp, ill);
icmp_param_problem_v6(mp,
ICMP6_PARAMPROB_OPTION,
(uint32_t)(optptr -
(uint8_t *)ip6h),
B_TRUE, ira);
return (-1);
default:
ASSERT(0);
}
}
}
optlen -= optused;
optptr += optused;
}
return (ret);
bad_opt:
ip_drop_input("ICMP_PARAM_PROBLEM", mp, ill);
icmp_param_problem_v6(mp, ICMP6_PARAMPROB_OPTION,
(uint32_t)(optptr - (uint8_t *)ip6h),
B_FALSE, ira);
return (-1);
}
void
ip_process_rthdr(mblk_t *mp, ip6_t *ip6h, ip6_rthdr_t *rth,
ip_recv_attr_t *ira)
{
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
ASSERT(rth->ip6r_segleft != 0);
if (!ipst->ips_ipv6_forward_src_routed) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsForwProhibits);
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInAddrErrors);
ip_drop_input("ipIfStatsInAddrErrors", mp, ill);
freemsg(mp);
return;
}
ip_drop_input("ICMP_PARAM_PROBLEM", mp, ill);
icmp_param_problem_v6(mp, ICMP6_PARAMPROB_HEADER,
(uint32_t)((uchar_t *)&rth->ip6r_type - (uchar_t *)ip6h),
B_FALSE, ira);
}
int
ip_rput_v6(queue_t *q, mblk_t *mp)
{
ill_t *ill;
ill = (ill_t *)q->q_ptr;
if (ill->ill_state_flags & (ILL_CONDEMNED | ILL_LL_SUBNET_PENDING)) {
union DL_primitives *dl;
dl = (union DL_primitives *)mp->b_rptr;
if ((mp->b_datap->db_type != M_PCPROTO) ||
(dl->dl_primitive == DL_UNITDATA_IND)) {
inet_freemsg(mp);
return (0);
}
}
if (DB_TYPE(mp) == M_DATA) {
struct mac_header_info_s mhi;
ip_mdata_to_mhi(ill, mp, &mhi);
ip_input_v6(ill, NULL, mp, &mhi);
} else {
ip_rput_notdata(ill, mp);
}
return (0);
}
#define IPSEC_HDR_DONT_PROCESS 0
#define IPSEC_HDR_PROCESS 1
#define IPSEC_MEMORY_ERROR 2
static int
ipsec_needs_processing_v6(mblk_t *mp, uint8_t *nexthdr)
{
uint_t length;
uint_t ehdrlen;
uint8_t *whereptr;
uint8_t *endptr;
uint8_t *nexthdrp;
ip6_dest_t *desthdr;
ip6_rthdr_t *rthdr;
ip6_t *ip6h;
if (!pullupmsg(mp, -1)) {
return (IPSEC_MEMORY_ERROR);
}
ip6h = (ip6_t *)mp->b_rptr;
length = IPV6_HDR_LEN;
whereptr = ((uint8_t *)&ip6h[1]);
endptr = mp->b_wptr;
nexthdrp = &ip6h->ip6_nxt;
while (whereptr < endptr) {
if (whereptr + MIN_EHDR_LEN > endptr)
return (IPSEC_MEMORY_ERROR);
switch (*nexthdrp) {
case IPPROTO_HOPOPTS:
case IPPROTO_DSTOPTS:
desthdr = (ip6_dest_t *)whereptr;
ehdrlen = 8 * (desthdr->ip6d_len + 1);
if ((uchar_t *)desthdr + ehdrlen > endptr)
return (IPSEC_MEMORY_ERROR);
if (*nexthdrp == IPPROTO_DSTOPTS)
return (IPSEC_HDR_DONT_PROCESS);
nexthdrp = &desthdr->ip6d_nxt;
break;
case IPPROTO_ROUTING:
rthdr = (ip6_rthdr_t *)whereptr;
if (rthdr->ip6r_segleft > 0)
return (IPSEC_HDR_DONT_PROCESS);
ehdrlen = 8 * (rthdr->ip6r_len + 1);
if ((uchar_t *)rthdr + ehdrlen > endptr)
return (IPSEC_MEMORY_ERROR);
nexthdrp = &rthdr->ip6r_nxt;
break;
case IPPROTO_FRAGMENT:
return (IPSEC_HDR_DONT_PROCESS);
case IPPROTO_AH:
*nexthdr = IPPROTO_AH;
return (IPSEC_HDR_PROCESS);
case IPPROTO_NONE:
default:
return (IPSEC_HDR_DONT_PROCESS);
}
length += ehdrlen;
whereptr += ehdrlen;
}
return (IPSEC_MEMORY_ERROR);
}
mblk_t *
ipsec_early_ah_v6(mblk_t *mp, ip_recv_attr_t *ira)
{
uint8_t nexthdr;
ah_t *ah;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
ipsec_stack_t *ipss = ipst->ips_netstack->netstack_ipsec;
switch (ipsec_needs_processing_v6(mp, &nexthdr)) {
case IPSEC_MEMORY_ERROR:
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
freemsg(mp);
return (NULL);
case IPSEC_HDR_DONT_PROCESS:
return (mp);
}
ASSERT(nexthdr == IPPROTO_AH);
if (!ipsec_loaded(ipss)) {
ip_proto_not_sup(mp, ira);
return (NULL);
}
mp = ipsec_inbound_ah_sa(mp, ira, &ah);
if (mp == NULL)
return (NULL);
ASSERT(ah != NULL);
ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE);
ASSERT(ira->ira_ipsec_ah_sa != NULL);
ASSERT(ira->ira_ipsec_ah_sa->ipsa_input_func != NULL);
mp = ira->ira_ipsec_ah_sa->ipsa_input_func(mp, ah, ira);
if (mp == NULL) {
return (NULL);
}
ip_input_post_ipsec(mp, ira);
return (NULL);
}
mblk_t *
ip_input_fragment_v6(mblk_t *mp, ip6_t *ip6h,
ip6_frag_t *fraghdr, uint_t remlen, ip_recv_attr_t *ira)
{
uint32_t ident = ntohl(fraghdr->ip6f_ident);
uint16_t offset;
boolean_t more_frags;
uint8_t nexthdr = fraghdr->ip6f_nxt;
in6_addr_t *v6dst_ptr;
in6_addr_t *v6src_ptr;
uint_t end;
uint_t hdr_length;
size_t count;
ipf_t *ipf;
ipf_t **ipfp;
ipfb_t *ipfb;
mblk_t *mp1;
uint8_t ecn_info = 0;
size_t msg_len;
mblk_t *tail_mp;
mblk_t *t_mp;
boolean_t pruned = B_FALSE;
uint32_t sum_val;
uint16_t sum_flags;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
uint_t prev_nexthdr_offset;
uint8_t prev_nexthdr;
uint8_t *ptr;
uint32_t packet_size;
ASSERT(ira->ira_rill != NULL);
if (nexthdr == IPPROTO_UDP && dohwcksum &&
ILL_HCKSUM_CAPABLE(ira->ira_rill) &&
(DB_CKSUMFLAGS(mp) & (HCK_FULLCKSUM | HCK_PARTIALCKSUM))) {
mblk_t *mp1 = mp->b_cont;
int32_t len;
sum_val = (uint32_t)DB_CKSUM16(mp);
sum_flags = DB_CKSUMFLAGS(mp);
offset = (uint16_t)((uchar_t *)&fraghdr[1] - mp->b_rptr);
if ((sum_flags & HCK_PARTIALCKSUM) &&
(mp1 == NULL || mp1->b_cont == NULL) &&
offset >= DB_CKSUMSTART(mp) &&
((len = offset - DB_CKSUMSTART(mp)) & 1) == 0) {
uint32_t adj;
IP_ADJCKSUM_PARTIAL(mp->b_rptr + DB_CKSUMSTART(mp),
mp, mp1, len, adj);
if (adj >= sum_val)
sum_val = ~(adj - sum_val) & 0xFFFF;
else
sum_val -= adj;
}
} else {
sum_val = 0;
sum_flags = 0;
}
DB_CKSUMFLAGS(mp) = 0;
prev_nexthdr_offset = (uint8_t *)&ip6h->ip6_nxt - (uint8_t *)ip6h;
prev_nexthdr = ip6h->ip6_nxt;
ptr = (uint8_t *)&ip6h[1];
if (prev_nexthdr == IPPROTO_HOPOPTS) {
ip6_hbh_t *hbh_hdr;
uint_t hdr_len;
hbh_hdr = (ip6_hbh_t *)ptr;
hdr_len = 8 * (hbh_hdr->ip6h_len + 1);
prev_nexthdr = hbh_hdr->ip6h_nxt;
prev_nexthdr_offset = (uint8_t *)&hbh_hdr->ip6h_nxt
- (uint8_t *)ip6h;
ptr += hdr_len;
}
if (prev_nexthdr == IPPROTO_DSTOPTS) {
ip6_dest_t *dest_hdr;
uint_t hdr_len;
dest_hdr = (ip6_dest_t *)ptr;
hdr_len = 8 * (dest_hdr->ip6d_len + 1);
prev_nexthdr = dest_hdr->ip6d_nxt;
prev_nexthdr_offset = (uint8_t *)&dest_hdr->ip6d_nxt
- (uint8_t *)ip6h;
ptr += hdr_len;
}
if (prev_nexthdr == IPPROTO_ROUTING) {
ip6_rthdr_t *rthdr;
uint_t hdr_len;
rthdr = (ip6_rthdr_t *)ptr;
prev_nexthdr = rthdr->ip6r_nxt;
prev_nexthdr_offset = (uint8_t *)&rthdr->ip6r_nxt
- (uint8_t *)ip6h;
hdr_len = 8 * (rthdr->ip6r_len + 1);
ptr += hdr_len;
}
if (prev_nexthdr != IPPROTO_FRAGMENT) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors);
ip_drop_input("ipIfStatsInHdrErrors", mp, ill);
freemsg(mp);
return (NULL);
}
offset = ntohs(fraghdr->ip6f_offlg) & ~7;
more_frags = (fraghdr->ip6f_offlg & IP6F_MORE_FRAG);
if (more_frags && (ntohs(ip6h->ip6_plen) & 7)) {
ip_drop_input("ICMP_PARAM_PROBLEM", mp, ill);
icmp_param_problem_v6(mp, ICMP6_PARAMPROB_HEADER,
(uint32_t)((char *)&ip6h->ip6_plen -
(char *)ip6h), B_FALSE, ira);
return (NULL);
}
v6src_ptr = &ip6h->ip6_src;
v6dst_ptr = &ip6h->ip6_dst;
end = remlen;
hdr_length = (uint_t)((char *)&fraghdr[1] - (char *)ip6h);
end += offset;
if (end > IP_MAXPACKET) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors);
ip_drop_input("Reassembled packet too large", mp, ill);
icmp_param_problem_v6(mp, ICMP6_PARAMPROB_HEADER,
(uint32_t)((char *)&fraghdr->ip6f_offlg -
(char *)ip6h), B_FALSE, ira);
return (NULL);
}
if (!more_frags && offset == 0) {
goto reass_done;
}
if (ipst->ips_ip_reass_queue_bytes == 0) {
freemsg(mp);
return (NULL);
}
ecn_info = (uint8_t)(ntohl(ip6h->ip6_vcf & htonl(~0xFFCFFFFF)) >> 20);
if (offset)
mp->b_rptr = (uchar_t *)&fraghdr[1];
if (mp->b_datap->db_ref > 2)
msg_len = 0;
else
msg_len = MBLKSIZE(mp);
tail_mp = mp;
while (tail_mp->b_cont != NULL) {
tail_mp = tail_mp->b_cont;
if (tail_mp->b_datap->db_ref <= 2)
msg_len += MBLKSIZE(tail_mp);
}
if ((msg_len + sizeof (*ipf) + ill->ill_frag_count) >=
ipst->ips_ip_reass_queue_bytes) {
DTRACE_PROBE3(ip_reass_queue_bytes, uint_t, msg_len,
uint_t, ill->ill_frag_count,
uint_t, ipst->ips_ip_reass_queue_bytes);
ill_frag_prune(ill,
(ipst->ips_ip_reass_queue_bytes < msg_len) ? 0 :
(ipst->ips_ip_reass_queue_bytes - msg_len));
pruned = B_TRUE;
}
ipfb = &ill->ill_frag_hash_tbl[ILL_FRAG_HASH_V6(*v6src_ptr, ident)];
mutex_enter(&ipfb->ipfb_lock);
ipfp = &ipfb->ipfb_ipf;
for (;;) {
ipf = ipfp[0];
if (ipf) {
if (ipf->ipf_ident == ident &&
IN6_ARE_ADDR_EQUAL(&ipf->ipf_v6src, v6src_ptr) &&
IN6_ARE_ADDR_EQUAL(&ipf->ipf_v6dst, v6dst_ptr)) {
if (ipf->ipf_num_dups > ip_max_frag_dups) {
ill_frag_free_pkts(ill, ipfb, ipf, 1);
freemsg(mp);
mutex_exit(&ipfb->ipfb_lock);
return (NULL);
}
break;
}
ipfp = &ipf->ipf_hash_next;
continue;
}
if (pruned && offset != 0) {
mutex_exit(&ipfb->ipfb_lock);
freemsg(mp);
return (NULL);
}
mp1 = allocb(sizeof (*ipf), BPRI_MED);
if (!mp1) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
freemsg(mp);
partial_reass_done:
mutex_exit(&ipfb->ipfb_lock);
return (NULL);
}
if (ipfb->ipfb_frag_pkts >= MAX_FRAG_PKTS(ipst)) {
ill_frag_free_pkts(ill, ipfb, ipfb->ipfb_ipf, 1);
}
mp1->b_cont = mp;
ipf = (ipf_t *)mp1->b_rptr;
ipf->ipf_mp = mp1;
ipf->ipf_ptphn = ipfp;
ipfp[0] = ipf;
ipf->ipf_hash_next = NULL;
ipf->ipf_ident = ident;
ipf->ipf_v6src = *v6src_ptr;
ipf->ipf_v6dst = *v6dst_ptr;
ipf->ipf_timestamp = gethrestime_sec();
ipf->ipf_gen = ill->ill_ipf_gen++;
ipf->ipf_count = MBLKSIZE(mp1);
ipf->ipf_protocol = nexthdr;
ipf->ipf_nf_hdr_len = 0;
ipf->ipf_prev_nexthdr_offset = 0;
ipf->ipf_last_frag_seen = B_FALSE;
ipf->ipf_ecn = ecn_info;
ipf->ipf_num_dups = 0;
ipfb->ipfb_frag_pkts++;
ipf->ipf_checksum = 0;
ipf->ipf_checksum_flags = 0;
if (sum_flags != 0) {
sum_val = (sum_val & 0xFFFF) + (sum_val >> 16);
sum_val = (sum_val & 0xFFFF) + (sum_val >> 16);
ipf->ipf_checksum = sum_val;
ipf->ipf_checksum_flags = sum_flags;
}
if (offset == 0) {
ipf->ipf_count += msg_len;
ipf->ipf_tail_mp = tail_mp;
ipf->ipf_end = end;
ipf->ipf_nf_hdr_len = hdr_length;
ipf->ipf_prev_nexthdr_offset = prev_nexthdr_offset;
} else {
ipf->ipf_tail_mp = NULL;
ipf->ipf_end = 0;
ipf->ipf_checksum_flags = 0;
(void) ip_reassemble(mp, ipf, offset, more_frags, ill,
msg_len);
}
ipfb->ipfb_count += ipf->ipf_count;
ASSERT(ipfb->ipfb_count > 0);
atomic_add_32(&ill->ill_frag_count, ipf->ipf_count);
mutex_enter(&ill->ill_lock);
ill_frag_timer_start(ill);
mutex_exit(&ill->ill_lock);
goto partial_reass_done;
}
if (sum_flags != 0 && sum_flags == ipf->ipf_checksum_flags) {
sum_val += ipf->ipf_checksum;
sum_val = (sum_val & 0xFFFF) + (sum_val >> 16);
sum_val = (sum_val & 0xFFFF) + (sum_val >> 16);
ipf->ipf_checksum = sum_val;
} else if (ipf->ipf_checksum_flags != 0) {
ipf->ipf_checksum_flags = 0;
}
if (ecn_info != IPH_ECN_NECT && ipf->ipf_ecn != IPH_ECN_NECT) {
if (ecn_info == IPH_ECN_CE)
ipf->ipf_ecn = IPH_ECN_CE;
} else {
ipf->ipf_ecn = IPH_ECN_NECT;
}
if (offset && ipf->ipf_end == offset) {
ipf->ipf_tail_mp->b_cont = mp;
ipf->ipf_count += msg_len;
ipfb->ipfb_count += msg_len;
ASSERT(ipfb->ipfb_count > 0);
atomic_add_32(&ill->ill_frag_count, msg_len);
if (more_frags) {
ipf->ipf_end = end;
ipf->ipf_tail_mp = tail_mp;
goto partial_reass_done;
}
} else {
int ret;
if (offset == 0) {
if (ipf->ipf_prev_nexthdr_offset == 0) {
ipf->ipf_nf_hdr_len = hdr_length;
ipf->ipf_prev_nexthdr_offset =
prev_nexthdr_offset;
}
}
count = ipf->ipf_count;
ret = ip_reassemble(mp, ipf, offset, more_frags, ill, msg_len);
count = ipf->ipf_count - count;
if (count) {
ipfb->ipfb_count += count;
ASSERT(ipfb->ipfb_count > 0);
atomic_add_32(&ill->ill_frag_count, count);
}
if (ret == IP_REASS_PARTIAL) {
goto partial_reass_done;
} else if (ret == IP_REASS_FAILED) {
ill_frag_free_pkts(ill, ipfb, ipf, 1);
for (t_mp = mp; t_mp != NULL; t_mp = t_mp->b_cont) {
IP_REASS_SET_START(t_mp, 0);
IP_REASS_SET_END(t_mp, 0);
}
freemsg(mp);
goto partial_reass_done;
}
}
ASSERT(ipf->ipf_nf_hdr_len != 0);
hdr_length = ipf->ipf_nf_hdr_len;
ecn_info = ipf->ipf_ecn;
nexthdr = ipf->ipf_protocol;
prev_nexthdr_offset = ipf->ipf_prev_nexthdr_offset;
ipfp = ipf->ipf_ptphn;
if ((sum_flags = ipf->ipf_checksum_flags) != 0)
sum_val = ipf->ipf_checksum;
else
sum_val = 0;
mp1 = ipf->ipf_mp;
count = ipf->ipf_count;
ipf = ipf->ipf_hash_next;
if (ipf)
ipf->ipf_ptphn = ipfp;
ipfp[0] = ipf;
atomic_add_32(&ill->ill_frag_count, -count);
ASSERT(ipfb->ipfb_count >= count);
ipfb->ipfb_count -= count;
ipfb->ipfb_frag_pkts--;
mutex_exit(&ipfb->ipfb_lock);
mp = mp1->b_cont;
freeb(mp1);
reass_done:
if (hdr_length < sizeof (ip6_frag_t)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors);
ip_drop_input("ipIfStatsInHdrErrors", mp, ill);
ip1dbg(("ip_input_fragment_v6: bad packet\n"));
freemsg(mp);
return (NULL);
}
if (mp->b_rptr + hdr_length != mp->b_wptr) {
mblk_t *nmp;
if (!(nmp = dupb(mp))) {
ip1dbg(("ip_input_fragment_v6: dupb failed\n"));
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
freemsg(mp);
return (NULL);
}
nmp->b_cont = mp->b_cont;
mp->b_cont = nmp;
nmp->b_rptr += hdr_length;
}
mp->b_wptr = mp->b_rptr + hdr_length - sizeof (ip6_frag_t);
ip6h = (ip6_t *)mp->b_rptr;
((char *)ip6h)[prev_nexthdr_offset] = nexthdr;
packet_size = msgdsize(mp);
ip6h->ip6_plen = htons((uint16_t)(packet_size - IPV6_HDR_LEN));
ip6h->ip6_vcf &= htonl(0xFFCFFFFF);
ip6h->ip6_vcf |= htonl(ecn_info << 20);
ira->ira_pktlen = packet_size;
ira->ira_ip_hdr_length = hdr_length - sizeof (ip6_frag_t);
ira->ira_protocol = nexthdr;
DB_CKSUM16(mp) = (uint16_t)sum_val;
DB_CKSUMFLAGS(mp) = sum_flags;
DB_CKSUMSTART(mp) = ira->ira_ip_hdr_length;
return (mp);
}
static in6_addr_t
pluck_out_dst(const mblk_t *mp, uint8_t *whereptr, in6_addr_t oldrv)
{
ip6_rthdr0_t *rt0;
int segleft, numaddr;
in6_addr_t *ap, rv = oldrv;
rt0 = (ip6_rthdr0_t *)whereptr;
if (rt0->ip6r0_type != 0 && rt0->ip6r0_type != 2) {
DTRACE_PROBE2(pluck_out_dst_unknown_type, mblk_t *, mp,
uint8_t *, whereptr);
return (rv);
}
segleft = rt0->ip6r0_segleft;
numaddr = rt0->ip6r0_len / 2;
if ((rt0->ip6r0_len & 0x1) ||
(mp != NULL && whereptr + (rt0->ip6r0_len + 1) * 8 > mp->b_wptr) ||
(segleft > rt0->ip6r0_len / 2)) {
DTRACE_PROBE2(pluck_out_dst_badpkt, mblk_t *, mp, uint8_t *,
whereptr);
return (rv);
}
if (segleft != 0) {
ap = (in6_addr_t *)((char *)rt0 + sizeof (*rt0));
rv = ap[numaddr - 1];
}
return (rv);
}
in6_addr_t
ip_get_dst_v6(ip6_t *ip6h, const mblk_t *mp, boolean_t *is_fragment)
{
const mblk_t *current_mp = mp;
uint8_t nexthdr;
uint8_t *whereptr;
int ehdrlen;
in6_addr_t rv;
whereptr = (uint8_t *)ip6h;
ehdrlen = sizeof (ip6_t);
ASSERT(mp == NULL ||
(mp->b_rptr <= whereptr && mp->b_wptr >= whereptr + ehdrlen));
rv = ip6h->ip6_dst;
nexthdr = ip6h->ip6_nxt;
if (is_fragment != NULL)
*is_fragment = B_FALSE;
while (nexthdr == IPPROTO_HOPOPTS || nexthdr == IPPROTO_DSTOPTS ||
nexthdr == IPPROTO_ROUTING) {
if (nexthdr == IPPROTO_ROUTING)
rv = pluck_out_dst(current_mp, whereptr, rv);
while (current_mp != NULL &&
whereptr + ehdrlen >= current_mp->b_wptr) {
ehdrlen -= (current_mp->b_wptr - whereptr);
current_mp = current_mp->b_cont;
if (current_mp == NULL) {
DTRACE_PROBE3(ip_get_dst_v6_badpkt, mblk_t *,
mp, mblk_t *, current_mp, ip6_t *, ip6h);
goto done;
}
whereptr = current_mp->b_rptr;
}
whereptr += ehdrlen;
nexthdr = *whereptr;
ASSERT(current_mp == NULL || whereptr + 1 < current_mp->b_wptr);
ehdrlen = (*(whereptr + 1) + 1) * 8;
}
done:
if (nexthdr == IPPROTO_FRAGMENT && is_fragment != NULL)
*is_fragment = B_TRUE;
return (rv);
}
static boolean_t
ip_source_routed_v6(ip6_t *ip6h, mblk_t *mp, ip_stack_t *ipst)
{
uint8_t nexthdr;
in6_addr_t *addrptr;
ip6_rthdr0_t *rthdr;
uint8_t numaddr;
ip6_hbh_t *hbhhdr;
uint_t ehdrlen;
uint8_t *byteptr;
ip2dbg(("ip_source_routed_v6\n"));
nexthdr = ip6h->ip6_nxt;
ehdrlen = IPV6_HDR_LEN;
while (nexthdr == IPPROTO_HOPOPTS ||
nexthdr == IPPROTO_DSTOPTS) {
byteptr = (uint8_t *)ip6h + ehdrlen;
if (byteptr + MIN_EHDR_LEN > mp->b_wptr) {
ip2dbg(("ip_source_routed_v6: Extension"
" headers not processed\n"));
return (B_FALSE);
}
hbhhdr = (ip6_hbh_t *)byteptr;
nexthdr = hbhhdr->ip6h_nxt;
ehdrlen = ehdrlen + 8 * (hbhhdr->ip6h_len + 1);
}
switch (nexthdr) {
case IPPROTO_ROUTING:
byteptr = (uint8_t *)ip6h + ehdrlen;
if (byteptr + MIN_EHDR_LEN > mp->b_wptr) {
ip2dbg(("ip_source_routed_v6: Routing"
" header not processed\n"));
return (B_FALSE);
}
rthdr = (ip6_rthdr0_t *)byteptr;
if (rthdr->ip6r0_segleft > 0 ||
rthdr->ip6r0_segleft == 0) {
numaddr = rthdr->ip6r0_len / 2;
addrptr = (in6_addr_t *)((char *)rthdr +
sizeof (*rthdr));
addrptr += (numaddr - (rthdr->ip6r0_segleft + 1));
if (addrptr != NULL) {
if (ip_type_v6(addrptr, ipst) == IRE_LOCAL)
return (B_TRUE);
ip1dbg(("ip_source_routed_v6: Not local\n"));
}
}
default:
ip2dbg(("ip_source_routed_v6: Not source routed here\n"));
return (B_FALSE);
}
}
int
ip_fragment_v6(mblk_t *mp, nce_t *nce, iaflags_t ixaflags, uint_t pkt_len,
uint32_t max_frag, uint32_t xmit_hint, zoneid_t szone, zoneid_t nolzid,
pfirepostfrag_t postfragfn, uintptr_t *ixa_cookie)
{
ip6_t *ip6h = (ip6_t *)mp->b_rptr;
ip6_t *fip6h;
mblk_t *hmp;
mblk_t *hmp0;
mblk_t *dmp;
ip6_frag_t *fraghdr;
size_t unfragmentable_len;
size_t mlen;
size_t max_chunk;
uint16_t off_flags;
uint16_t offset = 0;
ill_t *ill = nce->nce_ill;
uint8_t nexthdr;
uint8_t *ptr;
ip_stack_t *ipst = ill->ill_ipst;
uint_t priority = mp->b_band;
int error = 0;
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragReqds);
if (max_frag == 0) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
ip_drop_output("FragFails: zero max_frag", mp, ill);
freemsg(mp);
return (EINVAL);
}
ASSERT(ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN == pkt_len);
ASSERT(msgdsize(mp) == pkt_len);
nexthdr = ip6h->ip6_nxt;
ptr = (uint8_t *)&ip6h[1];
if (nexthdr == IPPROTO_HOPOPTS) {
ip6_hbh_t *hbh_hdr;
uint_t hdr_len;
hbh_hdr = (ip6_hbh_t *)ptr;
hdr_len = 8 * (hbh_hdr->ip6h_len + 1);
nexthdr = hbh_hdr->ip6h_nxt;
ptr += hdr_len;
}
if (nexthdr == IPPROTO_DSTOPTS) {
ip6_dest_t *dest_hdr;
uint_t hdr_len;
dest_hdr = (ip6_dest_t *)ptr;
if (dest_hdr->ip6d_nxt == IPPROTO_ROUTING) {
hdr_len = 8 * (dest_hdr->ip6d_len + 1);
nexthdr = dest_hdr->ip6d_nxt;
ptr += hdr_len;
}
}
if (nexthdr == IPPROTO_ROUTING) {
ip6_rthdr_t *rthdr;
uint_t hdr_len;
rthdr = (ip6_rthdr_t *)ptr;
nexthdr = rthdr->ip6r_nxt;
hdr_len = 8 * (rthdr->ip6r_len + 1);
ptr += hdr_len;
}
if (nexthdr != IPPROTO_FRAGMENT) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
ip_drop_output("FragFails: bad nexthdr", mp, ill);
freemsg(mp);
return (EINVAL);
}
unfragmentable_len = (uint_t)(ptr - (uint8_t *)ip6h);
unfragmentable_len += sizeof (ip6_frag_t);
max_chunk = (max_frag - unfragmentable_len) & ~7;
hmp = allocb_tmpl(unfragmentable_len + ipst->ips_ip_wroff_extra, mp);
if (hmp == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
ip_drop_output("FragFails: no hmp", mp, ill);
freemsg(mp);
return (ENOBUFS);
}
hmp->b_rptr += ipst->ips_ip_wroff_extra;
hmp->b_wptr = hmp->b_rptr + unfragmentable_len;
fip6h = (ip6_t *)hmp->b_rptr;
bcopy(ip6h, fip6h, unfragmentable_len);
pkt_len -= unfragmentable_len;
mp->b_rptr += unfragmentable_len;
if (mp->b_rptr == mp->b_wptr) {
mblk_t *mp1 = mp->b_cont;
freeb(mp);
mp = mp1;
}
while (pkt_len != 0) {
mlen = MIN(pkt_len, max_chunk);
pkt_len -= mlen;
if (pkt_len != 0) {
hmp0 = copyb(hmp);
if (hmp0 == NULL) {
BUMP_MIB(ill->ill_ip_mib,
ipIfStatsOutFragFails);
ip_drop_output("FragFails: copyb failed",
mp, ill);
freeb(hmp);
freemsg(mp);
ip1dbg(("ip_fragment_v6: copyb failed\n"));
return (ENOBUFS);
}
off_flags = IP6F_MORE_FRAG;
} else {
hmp0 = hmp;
hmp = NULL;
off_flags = 0;
}
fip6h = (ip6_t *)(hmp0->b_rptr);
fraghdr = (ip6_frag_t *)(hmp0->b_rptr + unfragmentable_len -
sizeof (ip6_frag_t));
fip6h->ip6_plen = htons((uint16_t)(mlen +
unfragmentable_len - IPV6_HDR_LEN));
fraghdr->ip6f_offlg = htons(offset) | off_flags;
if (!(dmp = ip_carve_mp(&mp, mlen))) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
ip_drop_output("FragFails: could not carve mp",
hmp0, ill);
if (hmp != NULL)
freeb(hmp);
freeb(hmp0);
ip1dbg(("ip_carve_mp: failed\n"));
return (ENOBUFS);
}
hmp0->b_cont = dmp;
hmp0->b_band = priority;
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragCreates);
error = postfragfn(hmp0, nce, ixaflags,
mlen + unfragmentable_len, xmit_hint, szone, nolzid,
ixa_cookie);
if (error != 0 && error != EWOULDBLOCK && hmp != NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
ip_drop_output("FragFails: postfragfn failed",
hmp, ill);
freeb(hmp);
freemsg(mp);
return (error);
}
ixaflags &= ~IXAF_REACH_CONF;
offset += mlen;
}
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragOKs);
return (error);
}
mblk_t *
ip_fraghdr_add_v6(mblk_t *mp, uint32_t ident, ip_xmit_attr_t *ixa)
{
ip6_t *ip6h = (ip6_t *)mp->b_rptr;
ip6_t *fip6h;
mblk_t *hmp;
ip6_frag_t *fraghdr;
size_t unfragmentable_len;
uint8_t nexthdr;
uint_t prev_nexthdr_offset;
uint8_t *ptr;
uint_t priority = mp->b_band;
ip_stack_t *ipst = ixa->ixa_ipst;
nexthdr = ip6h->ip6_nxt;
prev_nexthdr_offset = (uint8_t *)&ip6h->ip6_nxt - (uint8_t *)ip6h;
ptr = (uint8_t *)&ip6h[1];
if (nexthdr == IPPROTO_HOPOPTS) {
ip6_hbh_t *hbh_hdr;
uint_t hdr_len;
hbh_hdr = (ip6_hbh_t *)ptr;
hdr_len = 8 * (hbh_hdr->ip6h_len + 1);
nexthdr = hbh_hdr->ip6h_nxt;
prev_nexthdr_offset = (uint8_t *)&hbh_hdr->ip6h_nxt
- (uint8_t *)ip6h;
ptr += hdr_len;
}
if (nexthdr == IPPROTO_DSTOPTS) {
ip6_dest_t *dest_hdr;
uint_t hdr_len;
dest_hdr = (ip6_dest_t *)ptr;
if (dest_hdr->ip6d_nxt == IPPROTO_ROUTING) {
hdr_len = 8 * (dest_hdr->ip6d_len + 1);
nexthdr = dest_hdr->ip6d_nxt;
prev_nexthdr_offset = (uint8_t *)&dest_hdr->ip6d_nxt
- (uint8_t *)ip6h;
ptr += hdr_len;
}
}
if (nexthdr == IPPROTO_ROUTING) {
ip6_rthdr_t *rthdr;
uint_t hdr_len;
rthdr = (ip6_rthdr_t *)ptr;
nexthdr = rthdr->ip6r_nxt;
prev_nexthdr_offset = (uint8_t *)&rthdr->ip6r_nxt
- (uint8_t *)ip6h;
hdr_len = 8 * (rthdr->ip6r_len + 1);
ptr += hdr_len;
}
unfragmentable_len = (uint_t)(ptr - (uint8_t *)ip6h);
hmp = allocb_tmpl(unfragmentable_len + sizeof (ip6_frag_t) +
ipst->ips_ip_wroff_extra, mp);
if (hmp == NULL) {
ill_t *ill = ixa->ixa_nce->nce_ill;
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards: allocb failure", mp, ill);
freemsg(mp);
return (NULL);
}
hmp->b_rptr += ipst->ips_ip_wroff_extra;
hmp->b_wptr = hmp->b_rptr + unfragmentable_len + sizeof (ip6_frag_t);
fip6h = (ip6_t *)hmp->b_rptr;
fraghdr = (ip6_frag_t *)(hmp->b_rptr + unfragmentable_len);
bcopy(ip6h, fip6h, unfragmentable_len);
fip6h->ip6_plen = htons(ntohs(fip6h->ip6_plen) + sizeof (ip6_frag_t));
hmp->b_rptr[prev_nexthdr_offset] = IPPROTO_FRAGMENT;
fraghdr->ip6f_nxt = nexthdr;
fraghdr->ip6f_reserved = 0;
fraghdr->ip6f_offlg = 0;
fraghdr->ip6f_ident = htonl(ident);
hmp->b_band = priority;
mp->b_rptr += unfragmentable_len;
hmp->b_cont = mp;
return (hmp);
}
boolean_t
conn_wantpacket_v6(conn_t *connp, ip_recv_attr_t *ira, ip6_t *ip6h)
{
ill_t *ill = ira->ira_rill;
zoneid_t zoneid = ira->ira_zoneid;
uint_t in_ifindex;
in6_addr_t *v6dst_ptr = &ip6h->ip6_dst;
in6_addr_t *v6src_ptr = &ip6h->ip6_src;
in_ifindex = connp->conn_incoming_ifindex;
if (in_ifindex != 0 && in_ifindex != ill->ill_phyint->phyint_ifindex) {
if (!IS_UNDER_IPMP(ill))
return (B_FALSE);
if (in_ifindex != ipmp_ill_get_ipmp_ifindex(ill))
return (B_FALSE);
}
if (!IPCL_ZONE_MATCH(connp, zoneid))
return (B_FALSE);
if (!(ira->ira_flags & IRAF_MULTICAST))
return (B_TRUE);
if (connp->conn_multi_router)
return (B_TRUE);
if (ira->ira_protocol == IPPROTO_RSVP)
return (B_TRUE);
return (conn_hasmembers_ill_withsrc_v6(connp, v6dst_ptr, v6src_ptr,
ira->ira_ill));
}
void
pr_addr_dbg(char *fmt1, int af, const void *addr)
{
char buf[INET6_ADDRSTRLEN];
if (fmt1 == NULL) {
ip0dbg(("pr_addr_dbg: Wrong arguments\n"));
return;
}
if (ip_debug > 0) {
printf(fmt1, inet_ntop(af, addr, buf, sizeof (buf)));
}
}
int
ip_total_hdrs_len_v6(const ip_pkt_t *ipp)
{
int len;
len = IPV6_HDR_LEN;
if (ipp->ipp_fields & IPPF_LABEL_V6) {
uint_t hopoptslen;
ASSERT(ipp->ipp_label_len_v6 != 0);
hopoptslen = ipp->ipp_label_len_v6 + sizeof (ip6_hbh_t);
hopoptslen = (hopoptslen + 7)/8 * 8;
len += hopoptslen;
} else if (ipp->ipp_fields & IPPF_HOPOPTS) {
ASSERT(ipp->ipp_hopoptslen != 0);
len += ipp->ipp_hopoptslen;
}
if ((ipp->ipp_fields & (IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) ==
(IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) {
ASSERT(ipp->ipp_rthdrdstoptslen != 0);
len += ipp->ipp_rthdrdstoptslen;
}
if (ipp->ipp_fields & IPPF_RTHDR) {
ASSERT(ipp->ipp_rthdrlen != 0);
len += ipp->ipp_rthdrlen;
}
if (ipp->ipp_fields & IPPF_DSTOPTS) {
ASSERT(ipp->ipp_dstoptslen != 0);
len += ipp->ipp_dstoptslen;
}
return (len);
}
void
ip_build_hdrs_v6(uchar_t *buf, uint_t buf_len, const ip_pkt_t *ipp,
uint8_t protocol, uint32_t flowinfo)
{
uint8_t *nxthdr_ptr;
uint8_t *cp;
ip6_t *ip6h = (ip6_t *)buf;
ip6h->ip6_vcf =
(IPV6_DEFAULT_VERS_AND_FLOW & IPV6_VERS_AND_FLOW_MASK) |
(flowinfo & ~IPV6_VERS_AND_FLOW_MASK);
if (ipp->ipp_fields & IPPF_TCLASS) {
ip6h->ip6_vcf = IPV6_TCLASS_FLOW(ip6h->ip6_vcf,
ipp->ipp_tclass);
}
if (ipp->ipp_fields & IPPF_HOPLIMIT)
ip6h->ip6_hops = ipp->ipp_hoplimit;
else
ip6h->ip6_hops = ipp->ipp_unicast_hops;
if ((ipp->ipp_fields & IPPF_ADDR) &&
!IN6_IS_ADDR_V4MAPPED(&ipp->ipp_addr))
ip6h->ip6_src = ipp->ipp_addr;
nxthdr_ptr = (uint8_t *)&ip6h->ip6_nxt;
cp = (uint8_t *)&ip6h[1];
if (ipp->ipp_fields & IPPF_LABEL_V6) {
ip6_hbh_t *hbh = (ip6_hbh_t *)cp;
uint_t hopoptslen;
uint_t padlen;
padlen = ipp->ipp_label_len_v6 + sizeof (ip6_hbh_t);
hopoptslen = (padlen + 7)/8 * 8;
padlen = hopoptslen - padlen;
*nxthdr_ptr = IPPROTO_HOPOPTS;
nxthdr_ptr = &hbh->ip6h_nxt;
hbh->ip6h_len = hopoptslen/8 - 1;
cp += sizeof (ip6_hbh_t);
bcopy(ipp->ipp_label_v6, cp, ipp->ipp_label_len_v6);
cp += ipp->ipp_label_len_v6;
ASSERT(padlen <= 7);
switch (padlen) {
case 0:
break;
case 1:
cp[0] = IP6OPT_PAD1;
break;
default:
cp[0] = IP6OPT_PADN;
cp[1] = padlen - 2;
bzero(&cp[2], padlen - 2);
break;
}
cp += padlen;
} else if (ipp->ipp_fields & IPPF_HOPOPTS) {
ip6_hbh_t *hbh = (ip6_hbh_t *)cp;
*nxthdr_ptr = IPPROTO_HOPOPTS;
nxthdr_ptr = &hbh->ip6h_nxt;
bcopy(ipp->ipp_hopopts, cp, ipp->ipp_hopoptslen);
cp += ipp->ipp_hopoptslen;
}
if ((ipp->ipp_fields & (IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) ==
(IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) {
ip6_dest_t *dst = (ip6_dest_t *)cp;
*nxthdr_ptr = IPPROTO_DSTOPTS;
nxthdr_ptr = &dst->ip6d_nxt;
bcopy(ipp->ipp_rthdrdstopts, cp, ipp->ipp_rthdrdstoptslen);
cp += ipp->ipp_rthdrdstoptslen;
}
if (ipp->ipp_fields & IPPF_RTHDR) {
ip6_rthdr_t *rt = (ip6_rthdr_t *)cp;
*nxthdr_ptr = IPPROTO_ROUTING;
nxthdr_ptr = &rt->ip6r_nxt;
bcopy(ipp->ipp_rthdr, cp, ipp->ipp_rthdrlen);
cp += ipp->ipp_rthdrlen;
}
if (ipp->ipp_fields & IPPF_DSTOPTS) {
ip6_dest_t *dest = (ip6_dest_t *)cp;
*nxthdr_ptr = IPPROTO_DSTOPTS;
nxthdr_ptr = &dest->ip6d_nxt;
bcopy(ipp->ipp_dstopts, cp, ipp->ipp_dstoptslen);
cp += ipp->ipp_dstoptslen;
}
*nxthdr_ptr = protocol;
ASSERT((int)(cp - buf) == buf_len);
}
ip6_rthdr_t *
ip_find_rthdr_v6(ip6_t *ip6h, uint8_t *endptr)
{
ip6_dest_t *desthdr;
ip6_frag_t *fraghdr;
uint_t hdrlen;
uint8_t nexthdr;
uint8_t *ptr = (uint8_t *)&ip6h[1];
if (ip6h->ip6_nxt == IPPROTO_ROUTING)
return ((ip6_rthdr_t *)ptr);
nexthdr = ip6h->ip6_nxt;
while (ptr < endptr) {
if (ptr + MIN_EHDR_LEN > endptr)
return (NULL);
switch (nexthdr) {
case IPPROTO_HOPOPTS:
case IPPROTO_DSTOPTS:
desthdr = (ip6_dest_t *)ptr;
hdrlen = 8 * (desthdr->ip6d_len + 1);
nexthdr = desthdr->ip6d_nxt;
break;
case IPPROTO_ROUTING:
return ((ip6_rthdr_t *)ptr);
case IPPROTO_FRAGMENT:
fraghdr = (ip6_frag_t *)ptr;
hdrlen = sizeof (ip6_frag_t);
nexthdr = fraghdr->ip6f_nxt;
break;
default:
return (NULL);
}
ptr += hdrlen;
}
return (NULL);
}
uint32_t
ip_massage_options_v6(ip6_t *ip6h, ip6_rthdr_t *rth, netstack_t *ns)
{
uint_t numaddr;
uint_t i;
in6_addr_t *addrptr;
in6_addr_t tmp;
ip6_rthdr0_t *rthdr = (ip6_rthdr0_t *)rth;
uint32_t cksm;
uint32_t addrsum = 0;
uint16_t *ptr;
if ((rthdr->ip6r0_segleft == 0) || (rthdr->ip6r0_len == 0))
return (0);
ptr = (uint16_t *)&ip6h->ip6_dst;
cksm = 0;
for (i = 0; i < (sizeof (in6_addr_t) / sizeof (uint16_t)); i++) {
cksm += ptr[i];
}
cksm = (cksm & 0xFFFF) + (cksm >> 16);
addrptr = (in6_addr_t *)((char *)rthdr + sizeof (*rthdr));
numaddr = rthdr->ip6r0_len / 2;
tmp = *addrptr;
for (i = 0; i < (numaddr - 1); addrptr++, i++) {
*addrptr = addrptr[1];
}
*addrptr = ip6h->ip6_dst;
ip6h->ip6_dst = tmp;
ptr = (uint16_t *)&ip6h->ip6_dst;
for (i = 0; i < (sizeof (in6_addr_t) / sizeof (uint16_t)); i++) {
addrsum += ptr[i];
}
cksm -= ((addrsum >> 16) + (addrsum & 0xFFFF));
if ((int)cksm < 0)
cksm--;
cksm = (cksm & 0xFFFF) + (cksm >> 16);
return (cksm);
}
void
*ip6_kstat_init(netstackid_t stackid, ip6_stat_t *ip6_statisticsp)
{
kstat_t *ksp;
ip6_stat_t template = {
{ "ip6_udp_fannorm", KSTAT_DATA_UINT64 },
{ "ip6_udp_fanmb", KSTAT_DATA_UINT64 },
{ "ip6_recv_pullup", KSTAT_DATA_UINT64 },
{ "ip6_db_ref", KSTAT_DATA_UINT64 },
{ "ip6_notaligned", KSTAT_DATA_UINT64 },
{ "ip6_multimblk", KSTAT_DATA_UINT64 },
{ "ipsec_proto_ahesp", KSTAT_DATA_UINT64 },
{ "ip6_out_sw_cksum", KSTAT_DATA_UINT64 },
{ "ip6_out_sw_cksum_bytes", KSTAT_DATA_UINT64 },
{ "ip6_in_sw_cksum", KSTAT_DATA_UINT64 },
{ "ip6_tcp_in_full_hw_cksum_err", KSTAT_DATA_UINT64 },
{ "ip6_tcp_in_part_hw_cksum_err", KSTAT_DATA_UINT64 },
{ "ip6_tcp_in_sw_cksum_err", KSTAT_DATA_UINT64 },
{ "ip6_udp_in_full_hw_cksum_err", KSTAT_DATA_UINT64 },
{ "ip6_udp_in_part_hw_cksum_err", KSTAT_DATA_UINT64 },
{ "ip6_udp_in_sw_cksum_err", KSTAT_DATA_UINT64 },
};
ksp = kstat_create_netstack("ip", 0, "ip6stat", "net",
KSTAT_TYPE_NAMED, sizeof (template) / sizeof (kstat_named_t),
KSTAT_FLAG_VIRTUAL, stackid);
if (ksp == NULL)
return (NULL);
bcopy(&template, ip6_statisticsp, sizeof (template));
ksp->ks_data = (void *)ip6_statisticsp;
ksp->ks_private = (void *)(uintptr_t)stackid;
kstat_install(ksp);
return (ksp);
}
void
ip6_kstat_fini(netstackid_t stackid, kstat_t *ksp)
{
if (ksp != NULL) {
ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private);
kstat_delete_netstack(ksp, stackid);
}
}
int
ip6_set_src_preferences(ip_xmit_attr_t *ixa, uint32_t prefs)
{
if (prefs & ~IPV6_PREFER_SRC_MASK)
return (EINVAL);
if ((prefs & IPV6_PREFER_SRC_MIPMASK) == 0) {
prefs |= IPV6_PREFER_SRC_MIPDEFAULT;
} else if ((prefs & IPV6_PREFER_SRC_MIPMASK) ==
IPV6_PREFER_SRC_MIPMASK) {
return (EINVAL);
}
if ((prefs & IPV6_PREFER_SRC_TMPMASK) == 0) {
prefs |= IPV6_PREFER_SRC_TMPDEFAULT;
} else if ((prefs & IPV6_PREFER_SRC_TMPMASK) ==
IPV6_PREFER_SRC_TMPMASK) {
return (EINVAL);
}
if ((prefs & IPV6_PREFER_SRC_CGAMASK) == 0) {
prefs |= IPV6_PREFER_SRC_CGADEFAULT;
} else if ((prefs & IPV6_PREFER_SRC_CGAMASK) ==
IPV6_PREFER_SRC_CGAMASK) {
return (EINVAL);
}
ixa->ixa_src_preferences = prefs;
return (0);
}
size_t
ip6_get_src_preferences(ip_xmit_attr_t *ixa, uint32_t *val)
{
*val = ixa->ixa_src_preferences;
return (sizeof (ixa->ixa_src_preferences));
}
int
ipsec_ah_get_hdr_size_v6(mblk_t *mp, boolean_t till_ah)
{
ip6_t *ip6h;
uint8_t nexthdr;
uint8_t *whereptr;
ip6_hbh_t *hbhhdr;
ip6_dest_t *dsthdr;
ip6_rthdr_t *rthdr;
int ehdrlen;
int size;
ah_t *ah;
ip6h = (ip6_t *)mp->b_rptr;
size = IPV6_HDR_LEN;
nexthdr = ip6h->ip6_nxt;
whereptr = (uint8_t *)&ip6h[1];
for (;;) {
ASSERT(nexthdr != IPPROTO_FRAGMENT);
switch (nexthdr) {
case IPPROTO_HOPOPTS:
hbhhdr = (ip6_hbh_t *)whereptr;
nexthdr = hbhhdr->ip6h_nxt;
ehdrlen = 8 * (hbhhdr->ip6h_len + 1);
break;
case IPPROTO_DSTOPTS:
dsthdr = (ip6_dest_t *)whereptr;
nexthdr = dsthdr->ip6d_nxt;
ehdrlen = 8 * (dsthdr->ip6d_len + 1);
break;
case IPPROTO_ROUTING:
rthdr = (ip6_rthdr_t *)whereptr;
nexthdr = rthdr->ip6r_nxt;
ehdrlen = 8 * (rthdr->ip6r_len + 1);
break;
default :
if (till_ah) {
ASSERT(nexthdr == IPPROTO_AH);
return (size);
}
if (nexthdr != IPPROTO_AH) {
return (size);
}
ah = (ah_t *)whereptr;
nexthdr = ah->ah_nexthdr;
ehdrlen = (ah->ah_length << 2) + 8;
if (nexthdr == IPPROTO_DSTOPTS) {
if (whereptr + ehdrlen >= mp->b_wptr) {
whereptr = mp->b_cont->b_rptr;
} else {
whereptr += ehdrlen;
}
dsthdr = (ip6_dest_t *)whereptr;
ehdrlen = 8 * (dsthdr->ip6d_len + 1);
size += ehdrlen;
}
return (size);
}
whereptr += ehdrlen;
size += ehdrlen;
}
}
boolean_t
ipif_lookup_testaddr_v6(ill_t *ill, const in6_addr_t *v6srcp, ipif_t **ipifp)
{
ipif_t *ipif;
ipif = ipif_lookup_addr_exact_v6(v6srcp, ill, ill->ill_ipst);
if (ipif != NULL) {
if (ipifp != NULL)
*ipifp = ipif;
else
ipif_refrele(ipif);
return (B_TRUE);
}
if (ip_debug > 2) {
pr_addr_dbg("ipif_lookup_testaddr_v6: cannot find ipif for "
"src %s\n", AF_INET6, v6srcp);
}
return (B_FALSE);
}