1/* $NetBSD: xennet_checksum.c,v 1.3 2007/11/22 16:17:10 bouyer 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.3 2007/11/22 16:17:10 bouyer Exp $");
31
32#include <sys/types.h>
33#include <sys/param.h>
34
35#include <net/if.h>
36#include <net/if_dl.h>
37#include <net/if_ether.h>
38#include <net/if_vlanvar.h>
39
40#include <netinet/in.h>
41#include <netinet/in_systm.h>
42#include <netinet/ip.h>
43#include <netinet/tcp.h>
44#include <netinet/udp.h>
45
46#include <xen/xennet_checksum.h>
47
48static const void *m_extract(struct mbuf *, int, int, void *);
49static void *m_extract_write(struct mbuf *, int, int, void *);
50
51static void *m_extract1(struct mbuf *, int, int, void *, int);
52#define MBUF_EXTRACT_WRITE 1
53
54static void *
55m_extract1(struct mbuf *m, int off, int len, void *buf, int flags)
56{
57 void *result;
58
59 KASSERT((m->m_flags & M_PKTHDR) != 0);
60
61 if (m->m_pkthdr.len < off + len) {
62 result = NULL;
63 } else if (m->m_len >= off + len &&
64 ((flags & MBUF_EXTRACT_WRITE) != 0 || !M_READONLY(m))) {
65 result = mtod(m, char *) + off;
66 } else {
67 m_copydata(m, off, len, buf);
68 result = buf;
69 }
70
71 return result;
72}
73
74static const void *
75m_extract(struct mbuf *m, int off, int len, void *buf)
76{
77
78 return m_extract1(m, off, len, buf, 0);
79}
80
81static void *
82m_extract_write(struct mbuf *m, int off, int len, void *buf)
83{
84
85 return m_extract1(m, off, len, buf, MBUF_EXTRACT_WRITE);
86}
87
88/*
89 * xennet_checksum_fill: fill TCP/UDP checksum
90 */
91
92int
93xennet_checksum_fill(struct mbuf **mp)
94{
95 struct mbuf *m = *mp;
96 struct ether_header eh_store;
97 const struct ether_header *eh;
98 struct ip iph_store;
99 const struct ip *iph;
100 int ehlen;
101 int iphlen;
102 int iplen;
103 uint16_t etype;
104 uint8_t nxt;
105 int error = 0;
106
107 eh = m_extract(m, 0, sizeof(*eh), &eh_store);
108 if (eh == NULL) {
109 return EINVAL;
110 }
111 etype = eh->ether_type;
112 if (etype == htobe16(ETHERTYPE_VLAN)) {
113 ehlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
114 } else if (etype == htobe16(ETHERTYPE_IP)) {
115 ehlen = ETHER_HDR_LEN;
116 } else {
117 return EINVAL;
118 }
119
120 iph = m_extract(m, ehlen, sizeof(*iph), &iph_store);
121 if (iph == NULL) {
122 return EINVAL;
123 }
124 nxt = iph->ip_p;
125 iphlen = iph->ip_hl * 4;
126 iplen = ntohs(iph->ip_len);
127 if (ehlen + iplen != m->m_pkthdr.len) {
128 return EINVAL;
129 }
130 if (nxt == IPPROTO_UDP) {
131 struct udphdr uh_store;
132 struct udphdr *uh;
133 int ulen;
134
135 uh = m_extract_write(m, ehlen + iphlen, sizeof(*uh), &uh_store);
136 ulen = ntohs(uh->uh_ulen);
137 if (ehlen + iphlen + ulen > m->m_pkthdr.len) {
138 return EINVAL;
139 }
140 m->m_len -= ehlen;
141 m->m_data += ehlen;
142 uh->uh_sum = 0;
143 uh->uh_sum = in4_cksum(m, nxt, iphlen, iplen - iphlen);
144 m->m_len += ehlen;
145 m->m_data -= ehlen;
146 if (uh != &uh_store) {
147 m = m_copyback_cow(m, ehlen + iphlen, sizeof(*uh), uh,
148 M_DONTWAIT);
149 if (m == NULL) {
150 error = ENOMEM;
151 }
152 }
153 } else if (nxt == IPPROTO_TCP) {
154 struct tcphdr th_store;
155 struct tcphdr *th;
156 int thlen;
157
158 th = m_extract_write(m, ehlen + iphlen, sizeof(*th), &th_store);
159 thlen = th->th_off * 4;
160 if (ehlen + iphlen + thlen > m->m_pkthdr.len) {
161 return EINVAL;
162 }
163 m->m_len -= ehlen;
164 m->m_data += ehlen;
165 th->th_sum = 0;
166 th->th_sum = in4_cksum(m, nxt, iphlen, iplen - iphlen);
167 m->m_len += ehlen;
168 m->m_data -= ehlen;
169 if (th != &th_store) {
170 m = m_copyback_cow(m, ehlen + iphlen, sizeof(*th), th,
171 M_DONTWAIT);
172 if (m == NULL) {
173 error = ENOMEM;
174 }
175 }
176 } else {
177 error = EINVAL;
178 }
179
180 *mp = m;
181 return error;
182}
183