Home | History | Annotate | Line # | Download | only in xen
      1 /*	$NetBSD: xennet_checksum.c,v 1.14 2020/05/04 08:22:45 jdolecek Exp $	*/
      2 
      3 /*-
      4  * Copyright (c)2006 YAMAMOTO Takashi,
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: xennet_checksum.c,v 1.14 2020/05/04 08:22:45 jdolecek Exp $");
     31 
     32 #ifdef _KERNEL_OPT
     33 #include "opt_inet.h"
     34 #endif
     35 
     36 #include <sys/types.h>
     37 #include <sys/param.h>
     38 
     39 #include <net/if.h>
     40 #include <net/if_dl.h>
     41 #include <net/if_ether.h>
     42 #include <net/if_vlanvar.h>
     43 
     44 #include <netinet/in.h>
     45 #include <netinet/in_systm.h>
     46 #include <netinet/in_offload.h>
     47 #include <netinet/ip.h>
     48 #include <netinet/tcp.h>
     49 #include <netinet/udp.h>
     50 #include <netinet/ip6.h>
     51 #include <netinet6/in6_offload.h>
     52 
     53 #include <xen/xennet_checksum.h>
     54 
     55 #ifdef XENNET_DEBUG
     56 /* ratecheck(9) for checksum validation failures */
     57 static const struct timeval xn_cksum_errintvl = { 600, 0 };  /* 10 min, each */
     58 #endif
     59 
     60 static void *
     61 m_extract(struct mbuf *m, int off, int len)
     62 {
     63 	if (m->m_len >= off + len)
     64 		return mtod(m, char *) + off;
     65 	else
     66 		return NULL;
     67 }
     68 
     69 /*
     70  * xennet_checksum_fill: fill TCP/UDP checksum, or arrange
     71  * for hw offload to do it
     72  */
     73 int
     74 xennet_checksum_fill(struct ifnet *ifp, struct mbuf *m,
     75     struct evcnt *cksum_blank, struct evcnt *cksum_undefer)
     76 {
     77 	const struct ether_header *eh;
     78 	struct ip *iph = NULL;
     79 #ifdef INET6
     80 	struct ip6_hdr *ip6h = NULL;
     81 #endif
     82 	int ehlen;
     83 	int iphlen;
     84 	int iplen;
     85 	uint16_t etype;
     86 	uint8_t nxt;
     87 	int error = 0;
     88 	int sw_csum;
     89 
     90 	KASSERT(!M_READONLY(m));
     91 	KASSERT((m->m_flags & M_PKTHDR) != 0);
     92 
     93 	eh = m_extract(m, 0, sizeof(*eh));
     94 	if (eh == NULL) {
     95 		/* Too short, packet will be dropped by upper layer */
     96 		return EINVAL;
     97 	}
     98 	etype = eh->ether_type;
     99 	ehlen = ETHER_HDR_LEN;
    100 	if (__predict_false(etype == htons(ETHERTYPE_VLAN))) {
    101 		struct ether_vlan_header *evl = m_extract(m, 0, sizeof(*evl));
    102 		if (evl == NULL) {
    103 			/* Too short, packet will be dropped by upper layer */
    104 			return EINVAL;
    105 		}
    106 		ehlen += ETHER_VLAN_ENCAP_LEN;
    107 		etype = ntohs(evl->evl_proto);
    108 	}
    109 
    110 	switch (etype) {
    111 	case htons(ETHERTYPE_IP):
    112 		iph = m_extract(m, ehlen, sizeof(*iph));
    113 		if (iph == NULL) {
    114 			/* Too short, packet will be dropped by upper layer */
    115 			return EINVAL;
    116 		}
    117 		nxt = iph->ip_p;
    118 		iphlen = iph->ip_hl << 2;
    119 		iplen = ntohs(iph->ip_len);
    120 		break;
    121 #ifdef INET6
    122 	case htons(ETHERTYPE_IPV6):
    123 		ip6h = m_extract(m, ehlen, sizeof(*ip6h));
    124 		if (ip6h == NULL) {
    125 			/* Too short, packet will be dropped by upper layer */
    126 			return EINVAL;
    127 		}
    128 		if ((ip6h->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) {
    129 			/* Bad version */
    130 			return EINVAL;
    131 		}
    132 		nxt = ip6h->ip6_nxt;
    133 		iphlen = sizeof(*ip6h);
    134 		iplen = ntohs(ip6h->ip6_plen);
    135 		break;
    136 #endif
    137 	default:
    138 		/* Not supported ethernet type */
    139 		return EOPNOTSUPP;
    140 	}
    141 
    142 	if (ehlen + iplen > m->m_pkthdr.len) {
    143 		/* Too short, packet will be dropped by upper layer */
    144 		return EINVAL;
    145 	}
    146 
    147 	switch (nxt) {
    148 	case IPPROTO_UDP:
    149 		if (iph)
    150 			m->m_pkthdr.csum_flags = M_CSUM_UDPv4;
    151 #ifdef INET6
    152 		else
    153 			m->m_pkthdr.csum_flags = M_CSUM_UDPv6;
    154 #endif
    155 		m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
    156 		m->m_pkthdr.csum_data |= iphlen << 16;
    157 		break;
    158 	case IPPROTO_TCP:
    159 		if (iph)
    160 			m->m_pkthdr.csum_flags = M_CSUM_TCPv4;
    161 #ifdef INET6
    162 		else
    163 			m->m_pkthdr.csum_flags = M_CSUM_TCPv6;
    164 #endif
    165 		m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
    166 		m->m_pkthdr.csum_data |= iphlen << 16;
    167 		break;
    168 	case IPPROTO_ICMP:
    169 	case IPPROTO_IGMP:
    170 	case IPPROTO_HOPOPTS:
    171 	case IPPROTO_ICMPV6:
    172 	case IPPROTO_FRAGMENT:
    173 		/* nothing to do */
    174 		error = 0;
    175 		goto out;
    176 		/* NOTREACHED */
    177 	default:
    178 	    {
    179 #ifdef XENNET_DEBUG
    180 		static struct timeval lasttime;
    181 		if (ratecheck(&lasttime, &xn_cksum_errintvl))
    182 			printf("%s: unknown proto %d passed no checksum\n",
    183 			    ifp->if_xname, nxt);
    184 #endif /* XENNET_DEBUG */
    185 		error = EOPNOTSUPP;
    186 		goto out;
    187 	    }
    188 	}
    189 
    190 	/*
    191 	 * Only compute the checksum if impossible to defer.
    192 	 */
    193 	sw_csum = m->m_pkthdr.csum_flags & ~ifp->if_csum_flags_rx;
    194 
    195 	if (sw_csum & (M_CSUM_UDPv4|M_CSUM_TCPv4)) {
    196 		in_undefer_cksum(m, ehlen,
    197 		    sw_csum & (M_CSUM_UDPv4|M_CSUM_TCPv4));
    198 	}
    199 
    200 #ifdef INET6
    201 	if (sw_csum & (M_CSUM_UDPv6|M_CSUM_TCPv6)) {
    202 		in6_undefer_cksum(m, ehlen,
    203 		    sw_csum & (M_CSUM_UDPv6|M_CSUM_TCPv6));
    204 	}
    205 #endif
    206 
    207 	if (m->m_pkthdr.csum_flags != 0) {
    208 		if (sw_csum)
    209 			cksum_undefer->ev_count++;
    210 		cksum_blank->ev_count++;
    211 #ifdef M_CSUM_BLANK
    212 		m->m_pkthdr.csum_flags |= M_CSUM_BLANK;
    213 #endif
    214 	} else {
    215 		cksum_undefer->ev_count++;
    216 	}
    217 
    218     out:
    219 	return error;
    220 }
    221