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