/*
- * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2017 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <net/if.h>
#include <net/route.h>
#include <net/dlil.h>
+#include <net/net_api_stats.h>
#include <net/net_osdep.h>
#include <net/net_perf.h>
+#include <netinet/ip.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/ip_var.h>
static void ip6_output_checksum(struct ifnet *, uint32_t, struct mbuf *,
int, uint32_t, uint32_t);
extern int udp_ctloutput(struct socket *, struct sockopt *);
-static int ip6_do_fragmentation(struct mbuf **morig,
- uint32_t optlen, struct ifnet *ifp, uint32_t unfragpartlen,
- struct ip6_hdr *ip6, struct ip6_exthdrs *exthdrsp, uint32_t mtu,
- int nxt0);
static int ip6_fragment_packet(struct mbuf **m,
struct ip6_pktopts *opt, struct ip6_exthdrs *exthdrsp, struct ifnet *ifp,
uint32_t mtu, boolean_t alwaysfrag, uint32_t unfragpartlen,
struct mbuf *m, *mprev;
struct mbuf *sendchain = NULL, *sendchain_last = NULL;
struct mbuf *inputchain = NULL;
- int nxt0;
+ int nxt0 = 0;
struct route_in6 *ro_pmtu = NULL;
struct rtentry *rt = NULL;
- struct sockaddr_in6 *dst, src_sa, dst_sa;
+ struct sockaddr_in6 *dst = NULL, src_sa, dst_sa;
int error = 0;
struct in6_ifaddr *ia = NULL, *src_ia = NULL;
- u_int32_t mtu;
+ u_int32_t mtu = 0;
boolean_t alwaysfrag = FALSE;
u_int32_t optlen = 0, plen = 0, unfragpartlen = 0;
struct ip6_rthdr *rh;
* layer.
*/
error = EHOSTUNREACH;
+ ip6stat.ip6s_necp_policy_drop++;
goto freehdrs;
case NECP_KERNEL_POLICY_RESULT_IP_TUNNEL: {
/*
goto skip_ipsec;
} else {
error = ENETUNREACH;
+ ip6stat.ip6s_necp_policy_drop++;
goto freehdrs;
}
}
exthdrs.ip6e_ip6 = m;
ipsec_state.m = m;
- route_copyout(&ipsec_state.ro, (struct route *)ro,
- sizeof (ipsec_state.ro));
+ route_copyout((struct route *)&ipsec_state.ro, (struct route *)ro,
+ sizeof (struct route_in6));
ipsec_state.dst = SA(dst);
/* So that we can see packets inside the tunnel */
/* Catch-all to check if the interface is allowed */
if (!necp_packet_is_allowed_over_interface(m, ifp)) {
error = EHOSTUNREACH;
+ ip6stat.ip6s_necp_policy_drop++;
goto bad;
}
#endif /* NECP */
size_t tlen = m->m_pkthdr.len;
boolean_t dontfrag = (opt != NULL && (opt->ip6po_flags & IP6PO_DONTFRAG));
- if (m->m_pkthdr.pkt_flags & PKTF_FORWARDED)
+ if (m->m_pkthdr.pkt_flags & PKTF_FORWARDED) {
dontfrag = TRUE;
+ /*
+ * Discard partial sum information if this packet originated
+ * from another interface; the packet would already have the
+ * final checksum and we shouldn't recompute it.
+ */
+ if ((m->m_pkthdr.csum_flags & (CSUM_DATA_VALID|CSUM_PARTIAL)) ==
+ (CSUM_DATA_VALID|CSUM_PARTIAL)) {
+ m->m_pkthdr.csum_flags &= ~CSUM_TX_FLAGS;
+ m->m_pkthdr.csum_data = 0;
+ }
+ }
if (dontfrag && alwaysfrag) { /* case 4 */
/* conflicting request - can't transmit */
* of fragments is linked into the packet chain where morig existed. Otherwise,
* an errno is returned.
*/
-static int
+int
ip6_do_fragmentation(struct mbuf **mptr, uint32_t optlen, struct ifnet *ifp,
uint32_t unfragpartlen, struct ip6_hdr *ip6, struct ip6_exthdrs *exthdrsp,
uint32_t mtu, int nxt0)
ip6_out_cksum_stats(nxt, plen - olen);
/* RFC1122 4.1.3.4 */
- if (csum == 0 && (m->m_pkthdr.csum_flags & CSUM_UDPIPV6))
+ if (csum == 0 &&
+ (m->m_pkthdr.csum_flags & (CSUM_UDPIPV6|CSUM_ZERO_INVERT)))
csum = 0xffff;
/* Insert the checksum in the ULP csum field */
} else {
bcopy(&csum, (mtod(m, char *) + offset), sizeof (csum));
}
- m->m_pkthdr.csum_flags &=
- ~(CSUM_DELAY_IPV6_DATA | CSUM_DATA_VALID | CSUM_PARTIAL);
+ m->m_pkthdr.csum_flags &= ~(CSUM_DELAY_IPV6_DATA | CSUM_DATA_VALID |
+ CSUM_PARTIAL | CSUM_ZERO_INVERT);
done:
return (sw_csum);
u_int32_t mtu = 0;
boolean_t alwaysfrag = FALSE;
int error = 0;
+ boolean_t is_local = FALSE;
+
+ if (IN6_IS_SCOPE_LINKLOCAL(dst))
+ is_local = TRUE;
if (ro_pmtu != ro) {
/* The first hop and the final destination may differ. */
}
*mtup = mtu;
- if (alwaysfragp != NULL)
+ if ((alwaysfragp != NULL) && !is_local)
*alwaysfragp = alwaysfrag;
return (error);
}
privileged = (proc_suser(p) == 0);
if (level == IPPROTO_IPV6) {
+ boolean_t capture_exthdrstat_in = FALSE;
switch (op) {
case SOPT_SET:
switch (optname) {
break;
}
OPTSET(IN6P_HOPOPTS);
+ capture_exthdrstat_in = TRUE;
break;
case IPV6_RECVDSTOPTS:
break;
}
OPTSET(IN6P_DSTOPTS);
+ capture_exthdrstat_in = TRUE;
break;
case IPV6_RECVRTHDRDSTOPTS:
break;
}
OPTSET(IN6P_RTHDRDSTOPTS);
+ capture_exthdrstat_in = TRUE;
break;
case IPV6_RECVRTHDR:
break;
}
OPTSET(IN6P_RTHDR);
+ capture_exthdrstat_in = TRUE;
break;
case IPV6_RECVPATHMTU:
optp = &in6p->in6p_outputopts;
error = ip6_pcbopt(optname, (u_char *)&optval,
sizeof (optval), optp, uproto);
+
+ if (optname == IPV6_TCLASS) {
+ // Add in the ECN flags
+ u_int8_t tos = (in6p->inp_ip_tos & ~IPTOS_ECN_MASK);
+ u_int8_t ecn = optval & IPTOS_ECN_MASK;
+ in6p->inp_ip_tos = tos | ecn;
+ }
break;
}
if (!privileged)
return (EPERM);
OPTSET2292(IN6P_HOPOPTS);
+ capture_exthdrstat_in = TRUE;
break;
case IPV6_2292DSTOPTS:
if (!privileged)
return (EPERM);
OPTSET2292(IN6P_DSTOPTS|
IN6P_RTHDRDSTOPTS); /* XXX */
+ capture_exthdrstat_in = TRUE;
break;
case IPV6_2292RTHDR:
OPTSET2292(IN6P_RTHDR);
+ capture_exthdrstat_in = TRUE;
break;
}
break;
error = ENOPROTOOPT;
break;
}
+ if (capture_exthdrstat_in) {
+ if (uproto == IPPROTO_TCP) {
+ INC_ATOMIC_INT64_LIM(net_api_stats.nas_sock_inet6_stream_exthdr_in);
+ } else if (uproto == IPPROTO_UDP) {
+ INC_ATOMIC_INT64_LIM(net_api_stats.nas_sock_inet6_dgram_exthdr_in);
+ }
+ }
break;
case SOPT_GET:
{
int minmtupolicy, preftemp;
int error;
+ boolean_t capture_exthdrstat_out = FALSE;
if (!sticky && !cmsg) {
#ifdef DIAGNOSTIC
if (opt->ip6po_hbh == NULL)
return (ENOBUFS);
bcopy(hbh, opt->ip6po_hbh, hbhlen);
-
+ capture_exthdrstat_out = TRUE;
break;
}
if (*newdest == NULL)
return (ENOBUFS);
bcopy(dest, *newdest, destlen);
+ capture_exthdrstat_out = TRUE;
break;
}
if (opt->ip6po_rthdr == NULL)
return (ENOBUFS);
bcopy(rth, opt->ip6po_rthdr, rthlen);
+ capture_exthdrstat_out = TRUE;
break;
}
return (ENOPROTOOPT);
} /* end of switch */
+ if (capture_exthdrstat_out) {
+ if (uproto == IPPROTO_TCP) {
+ INC_ATOMIC_INT64_LIM(net_api_stats.nas_sock_inet6_stream_exthdr_out);
+ } else if (uproto == IPPROTO_UDP) {
+ INC_ATOMIC_INT64_LIM(net_api_stats.nas_sock_inet6_dgram_exthdr_out);
+ }
+ }
+
return (0);
}
} else if (!(sw_csum & CSUM_DELAY_IPV6_DATA) &&
(hwcap & CSUM_PARTIAL)) {
/*
- * Partial checksum offload, ere), if no extension
- * headers, and TCP only (no UDP support, as the
- * hardware may not be able to convert +0 to
- * -0 (0xffff) per RFC1122 4.1.3.4.)
+ * Partial checksum offload, ere), if no extension headers,
+ * and TCP only (no UDP support, as the hardware may not be
+ * able to convert +0 to -0 (0xffff) per RFC1122 4.1.3.4.
+ * unless the interface supports "invert zero" capability.)
*/
if (hwcksum_tx && !tso &&
- (m->m_pkthdr.csum_flags & CSUM_TCPIPV6) &&
+ ((m->m_pkthdr.csum_flags & CSUM_TCPIPV6) ||
+ ((hwcap & CSUM_ZERO_INVERT) &&
+ (m->m_pkthdr.csum_flags & CSUM_ZERO_INVERT))) &&
tlen <= mtu) {
uint16_t start = sizeof (struct ip6_hdr);
uint16_t ulpoff =