1*f0ea3689SLuigi Rizzo /*
2*f0ea3689SLuigi Rizzo  * Copyright (C) 2014 Vincenzo Maffione. All rights reserved.
3*f0ea3689SLuigi Rizzo  *
4*f0ea3689SLuigi Rizzo  * Redistribution and use in source and binary forms, with or without
5*f0ea3689SLuigi Rizzo  * modification, are permitted provided that the following conditions
6*f0ea3689SLuigi Rizzo  * are met:
7*f0ea3689SLuigi Rizzo  *   1. Redistributions of source code must retain the above copyright
8*f0ea3689SLuigi Rizzo  *      notice, this list of conditions and the following disclaimer.
9*f0ea3689SLuigi Rizzo  *   2. Redistributions in binary form must reproduce the above copyright
10*f0ea3689SLuigi Rizzo  *      notice, this list of conditions and the following disclaimer in the
11*f0ea3689SLuigi Rizzo  *      documentation and/or other materials provided with the distribution.
12*f0ea3689SLuigi Rizzo  *
13*f0ea3689SLuigi Rizzo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14*f0ea3689SLuigi Rizzo  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15*f0ea3689SLuigi Rizzo  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16*f0ea3689SLuigi Rizzo  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17*f0ea3689SLuigi Rizzo  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18*f0ea3689SLuigi Rizzo  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19*f0ea3689SLuigi Rizzo  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20*f0ea3689SLuigi Rizzo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21*f0ea3689SLuigi Rizzo  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22*f0ea3689SLuigi Rizzo  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23*f0ea3689SLuigi Rizzo  * SUCH DAMAGE.
24*f0ea3689SLuigi Rizzo  */
25*f0ea3689SLuigi Rizzo 
26*f0ea3689SLuigi Rizzo /* $FreeBSD$ */
27*f0ea3689SLuigi Rizzo 
28*f0ea3689SLuigi Rizzo #if defined(__FreeBSD__)
29*f0ea3689SLuigi Rizzo #include <sys/cdefs.h> /* prerequisite */
30*f0ea3689SLuigi Rizzo 
31*f0ea3689SLuigi Rizzo #include <sys/types.h>
32*f0ea3689SLuigi Rizzo #include <sys/errno.h>
33*f0ea3689SLuigi Rizzo #include <sys/param.h>	/* defines used in kernel.h */
34*f0ea3689SLuigi Rizzo #include <sys/kernel.h>	/* types used in module initialization */
35*f0ea3689SLuigi Rizzo #include <sys/sockio.h>
36*f0ea3689SLuigi Rizzo #include <sys/socketvar.h>	/* struct socket */
37*f0ea3689SLuigi Rizzo #include <sys/socket.h> /* sockaddrs */
38*f0ea3689SLuigi Rizzo #include <net/if.h>
39*f0ea3689SLuigi Rizzo #include <net/if_var.h>
40*f0ea3689SLuigi Rizzo #include <machine/bus.h>	/* bus_dmamap_* */
41*f0ea3689SLuigi Rizzo #include <sys/endian.h>
42*f0ea3689SLuigi Rizzo 
43*f0ea3689SLuigi Rizzo #elif defined(linux)
44*f0ea3689SLuigi Rizzo 
45*f0ea3689SLuigi Rizzo #include "bsd_glue.h"
46*f0ea3689SLuigi Rizzo 
47*f0ea3689SLuigi Rizzo #elif defined(__APPLE__)
48*f0ea3689SLuigi Rizzo 
49*f0ea3689SLuigi Rizzo #warning OSX support is only partial
50*f0ea3689SLuigi Rizzo #include "osx_glue.h"
51*f0ea3689SLuigi Rizzo 
52*f0ea3689SLuigi Rizzo #else
53*f0ea3689SLuigi Rizzo 
54*f0ea3689SLuigi Rizzo #error	Unsupported platform
55*f0ea3689SLuigi Rizzo 
56*f0ea3689SLuigi Rizzo #endif /* unsupported */
57*f0ea3689SLuigi Rizzo 
58*f0ea3689SLuigi Rizzo #include <net/netmap.h>
59*f0ea3689SLuigi Rizzo #include <dev/netmap/netmap_kern.h>
60*f0ea3689SLuigi Rizzo 
61*f0ea3689SLuigi Rizzo 
62*f0ea3689SLuigi Rizzo 
63*f0ea3689SLuigi Rizzo /* This routine is called by bdg_mismatch_datapath() when it finishes
64*f0ea3689SLuigi Rizzo  * accumulating bytes for a segment, in order to fix some fields in the
65*f0ea3689SLuigi Rizzo  * segment headers (which still contain the same content as the header
66*f0ea3689SLuigi Rizzo  * of the original GSO packet). 'buf' points to the beginning (e.g.
67*f0ea3689SLuigi Rizzo  * the ethernet header) of the segment, and 'len' is its length.
68*f0ea3689SLuigi Rizzo  */
69*f0ea3689SLuigi Rizzo static void gso_fix_segment(uint8_t *buf, size_t len, u_int idx,
70*f0ea3689SLuigi Rizzo 			    u_int segmented_bytes, u_int last_segment,
71*f0ea3689SLuigi Rizzo 			    u_int tcp, u_int iphlen)
72*f0ea3689SLuigi Rizzo {
73*f0ea3689SLuigi Rizzo 	struct nm_iphdr *iph = (struct nm_iphdr *)(buf + 14);
74*f0ea3689SLuigi Rizzo 	struct nm_ipv6hdr *ip6h = (struct nm_ipv6hdr *)(buf + 14);
75*f0ea3689SLuigi Rizzo 	uint16_t *check = NULL;
76*f0ea3689SLuigi Rizzo 	uint8_t *check_data = NULL;
77*f0ea3689SLuigi Rizzo 
78*f0ea3689SLuigi Rizzo 	if (iphlen == 20) {
79*f0ea3689SLuigi Rizzo 		/* Set the IPv4 "Total Length" field. */
80*f0ea3689SLuigi Rizzo 		iph->tot_len = htobe16(len-14);
81*f0ea3689SLuigi Rizzo 		ND("ip total length %u", be16toh(ip->tot_len));
82*f0ea3689SLuigi Rizzo 
83*f0ea3689SLuigi Rizzo 		/* Set the IPv4 "Identification" field. */
84*f0ea3689SLuigi Rizzo 		iph->id = htobe16(be16toh(iph->id) + idx);
85*f0ea3689SLuigi Rizzo 		ND("ip identification %u", be16toh(iph->id));
86*f0ea3689SLuigi Rizzo 
87*f0ea3689SLuigi Rizzo 		/* Compute and insert the IPv4 header checksum. */
88*f0ea3689SLuigi Rizzo 		iph->check = 0;
89*f0ea3689SLuigi Rizzo 		iph->check = nm_csum_ipv4(iph);
90*f0ea3689SLuigi Rizzo 		ND("IP csum %x", be16toh(iph->check));
91*f0ea3689SLuigi Rizzo 	} else {/* if (iphlen == 40) */
92*f0ea3689SLuigi Rizzo 		/* Set the IPv6 "Payload Len" field. */
93*f0ea3689SLuigi Rizzo 		ip6h->payload_len = htobe16(len-14-iphlen);
94*f0ea3689SLuigi Rizzo 	}
95*f0ea3689SLuigi Rizzo 
96*f0ea3689SLuigi Rizzo 	if (tcp) {
97*f0ea3689SLuigi Rizzo 		struct nm_tcphdr *tcph = (struct nm_tcphdr *)(buf + 14 + iphlen);
98*f0ea3689SLuigi Rizzo 
99*f0ea3689SLuigi Rizzo 		/* Set the TCP sequence number. */
100*f0ea3689SLuigi Rizzo 		tcph->seq = htobe32(be32toh(tcph->seq) + segmented_bytes);
101*f0ea3689SLuigi Rizzo 		ND("tcp seq %u", be32toh(tcph->seq));
102*f0ea3689SLuigi Rizzo 
103*f0ea3689SLuigi Rizzo 		/* Zero the PSH and FIN TCP flags if this is not the last
104*f0ea3689SLuigi Rizzo 		   segment. */
105*f0ea3689SLuigi Rizzo 		if (!last_segment)
106*f0ea3689SLuigi Rizzo 			tcph->flags &= ~(0x8 | 0x1);
107*f0ea3689SLuigi Rizzo 		ND("last_segment %u", last_segment);
108*f0ea3689SLuigi Rizzo 
109*f0ea3689SLuigi Rizzo 		check = &tcph->check;
110*f0ea3689SLuigi Rizzo 		check_data = (uint8_t *)tcph;
111*f0ea3689SLuigi Rizzo 	} else { /* UDP */
112*f0ea3689SLuigi Rizzo 		struct nm_udphdr *udph = (struct nm_udphdr *)(buf + 14 + iphlen);
113*f0ea3689SLuigi Rizzo 
114*f0ea3689SLuigi Rizzo 		/* Set the UDP 'Length' field. */
115*f0ea3689SLuigi Rizzo 		udph->len = htobe16(len-14-iphlen);
116*f0ea3689SLuigi Rizzo 
117*f0ea3689SLuigi Rizzo 		check = &udph->check;
118*f0ea3689SLuigi Rizzo 		check_data = (uint8_t *)udph;
119*f0ea3689SLuigi Rizzo 	}
120*f0ea3689SLuigi Rizzo 
121*f0ea3689SLuigi Rizzo 	/* Compute and insert TCP/UDP checksum. */
122*f0ea3689SLuigi Rizzo 	*check = 0;
123*f0ea3689SLuigi Rizzo 	if (iphlen == 20)
124*f0ea3689SLuigi Rizzo 		nm_csum_tcpudp_ipv4(iph, check_data, len-14-iphlen, check);
125*f0ea3689SLuigi Rizzo 	else
126*f0ea3689SLuigi Rizzo 		nm_csum_tcpudp_ipv6(ip6h, check_data, len-14-iphlen, check);
127*f0ea3689SLuigi Rizzo 
128*f0ea3689SLuigi Rizzo 	ND("TCP/UDP csum %x", be16toh(*check));
129*f0ea3689SLuigi Rizzo }
130*f0ea3689SLuigi Rizzo 
131*f0ea3689SLuigi Rizzo 
132*f0ea3689SLuigi Rizzo /* The VALE mismatch datapath implementation. */
133*f0ea3689SLuigi Rizzo void bdg_mismatch_datapath(struct netmap_vp_adapter *na,
134*f0ea3689SLuigi Rizzo 			   struct netmap_vp_adapter *dst_na,
135*f0ea3689SLuigi Rizzo 			   struct nm_bdg_fwd *ft_p, struct netmap_ring *ring,
136*f0ea3689SLuigi Rizzo 			   u_int *j, u_int lim, u_int *howmany)
137*f0ea3689SLuigi Rizzo {
138*f0ea3689SLuigi Rizzo 	struct netmap_slot *slot = NULL;
139*f0ea3689SLuigi Rizzo 	struct nm_vnet_hdr *vh = NULL;
140*f0ea3689SLuigi Rizzo 	/* Number of source slots to process. */
141*f0ea3689SLuigi Rizzo 	u_int frags = ft_p->ft_frags;
142*f0ea3689SLuigi Rizzo 	struct nm_bdg_fwd *ft_end = ft_p + frags;
143*f0ea3689SLuigi Rizzo 
144*f0ea3689SLuigi Rizzo 	/* Source and destination pointers. */
145*f0ea3689SLuigi Rizzo 	uint8_t *dst, *src;
146*f0ea3689SLuigi Rizzo 	size_t src_len, dst_len;
147*f0ea3689SLuigi Rizzo 
148*f0ea3689SLuigi Rizzo 	u_int j_start = *j;
149*f0ea3689SLuigi Rizzo 	u_int dst_slots = 0;
150*f0ea3689SLuigi Rizzo 
151*f0ea3689SLuigi Rizzo 	/* If the source port uses the offloadings, while destination doesn't,
152*f0ea3689SLuigi Rizzo 	 * we grab the source virtio-net header and do the offloadings here.
153*f0ea3689SLuigi Rizzo 	 */
154*f0ea3689SLuigi Rizzo 	if (na->virt_hdr_len && !dst_na->virt_hdr_len) {
155*f0ea3689SLuigi Rizzo 		vh = (struct nm_vnet_hdr *)ft_p->ft_buf;
156*f0ea3689SLuigi Rizzo 	}
157*f0ea3689SLuigi Rizzo 
158*f0ea3689SLuigi Rizzo 	/* Init source and dest pointers. */
159*f0ea3689SLuigi Rizzo 	src = ft_p->ft_buf;
160*f0ea3689SLuigi Rizzo 	src_len = ft_p->ft_len;
161*f0ea3689SLuigi Rizzo 	slot = &ring->slot[*j];
162*f0ea3689SLuigi Rizzo 	dst = BDG_NMB(&dst_na->up, slot);
163*f0ea3689SLuigi Rizzo 	dst_len = src_len;
164*f0ea3689SLuigi Rizzo 
165*f0ea3689SLuigi Rizzo 	/* We are processing the first input slot and there is a mismatch
166*f0ea3689SLuigi Rizzo 	 * between source and destination virt_hdr_len (SHL and DHL).
167*f0ea3689SLuigi Rizzo 	 * When the a client is using virtio-net headers, the header length
168*f0ea3689SLuigi Rizzo 	 * can be:
169*f0ea3689SLuigi Rizzo 	 *    - 10: the header corresponds to the struct nm_vnet_hdr
170*f0ea3689SLuigi Rizzo 	 *    - 12: the first 10 bytes correspond to the struct
171*f0ea3689SLuigi Rizzo 	 *          virtio_net_hdr, and the last 2 bytes store the
172*f0ea3689SLuigi Rizzo 	 *          "mergeable buffers" info, which is an optional
173*f0ea3689SLuigi Rizzo 	 *	    hint that can be zeroed for compability
174*f0ea3689SLuigi Rizzo 	 *
175*f0ea3689SLuigi Rizzo 	 * The destination header is therefore built according to the
176*f0ea3689SLuigi Rizzo 	 * following table:
177*f0ea3689SLuigi Rizzo 	 *
178*f0ea3689SLuigi Rizzo 	 * SHL | DHL | destination header
179*f0ea3689SLuigi Rizzo 	 * -----------------------------
180*f0ea3689SLuigi Rizzo 	 *   0 |  10 | zero
181*f0ea3689SLuigi Rizzo 	 *   0 |  12 | zero
182*f0ea3689SLuigi Rizzo 	 *  10 |   0 | doesn't exist
183*f0ea3689SLuigi Rizzo 	 *  10 |  12 | first 10 bytes are copied from source header, last 2 are zero
184*f0ea3689SLuigi Rizzo 	 *  12 |   0 | doesn't exist
185*f0ea3689SLuigi Rizzo 	 *  12 |  10 | copied from the first 10 bytes of source header
186*f0ea3689SLuigi Rizzo 	 */
187*f0ea3689SLuigi Rizzo 	bzero(dst, dst_na->virt_hdr_len);
188*f0ea3689SLuigi Rizzo 	if (na->virt_hdr_len && dst_na->virt_hdr_len)
189*f0ea3689SLuigi Rizzo 		memcpy(dst, src, sizeof(struct nm_vnet_hdr));
190*f0ea3689SLuigi Rizzo 	/* Skip the virtio-net headers. */
191*f0ea3689SLuigi Rizzo 	src += na->virt_hdr_len;
192*f0ea3689SLuigi Rizzo 	src_len -= na->virt_hdr_len;
193*f0ea3689SLuigi Rizzo 	dst += dst_na->virt_hdr_len;
194*f0ea3689SLuigi Rizzo 	dst_len = dst_na->virt_hdr_len + src_len;
195*f0ea3689SLuigi Rizzo 
196*f0ea3689SLuigi Rizzo 	/* Here it could be dst_len == 0 (which implies src_len == 0),
197*f0ea3689SLuigi Rizzo 	 * so we avoid passing a zero length fragment.
198*f0ea3689SLuigi Rizzo 	 */
199*f0ea3689SLuigi Rizzo 	if (dst_len == 0) {
200*f0ea3689SLuigi Rizzo 		ft_p++;
201*f0ea3689SLuigi Rizzo 		src = ft_p->ft_buf;
202*f0ea3689SLuigi Rizzo 		src_len = ft_p->ft_len;
203*f0ea3689SLuigi Rizzo 		dst_len = src_len;
204*f0ea3689SLuigi Rizzo 	}
205*f0ea3689SLuigi Rizzo 
206*f0ea3689SLuigi Rizzo 	if (vh && vh->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
207*f0ea3689SLuigi Rizzo 		u_int gso_bytes = 0;
208*f0ea3689SLuigi Rizzo 		/* Length of the GSO packet header. */
209*f0ea3689SLuigi Rizzo 		u_int gso_hdr_len = 0;
210*f0ea3689SLuigi Rizzo 		/* Pointer to the GSO packet header. Assume it is in a single fragment. */
211*f0ea3689SLuigi Rizzo 		uint8_t *gso_hdr = NULL;
212*f0ea3689SLuigi Rizzo 		/* Index of the current segment. */
213*f0ea3689SLuigi Rizzo 		u_int gso_idx = 0;
214*f0ea3689SLuigi Rizzo 		/* Payload data bytes segmented so far (e.g. TCP data bytes). */
215*f0ea3689SLuigi Rizzo 		u_int segmented_bytes = 0;
216*f0ea3689SLuigi Rizzo 		/* Length of the IP header (20 if IPv4, 40 if IPv6). */
217*f0ea3689SLuigi Rizzo 		u_int iphlen = 0;
218*f0ea3689SLuigi Rizzo 		/* Is this a TCP or an UDP GSO packet? */
219*f0ea3689SLuigi Rizzo 		u_int tcp = ((vh->gso_type & ~VIRTIO_NET_HDR_GSO_ECN)
220*f0ea3689SLuigi Rizzo 				== VIRTIO_NET_HDR_GSO_UDP) ? 0 : 1;
221*f0ea3689SLuigi Rizzo 
222*f0ea3689SLuigi Rizzo 		/* Segment the GSO packet contained into the input slots (frags). */
223*f0ea3689SLuigi Rizzo 		while (ft_p != ft_end) {
224*f0ea3689SLuigi Rizzo 			size_t copy;
225*f0ea3689SLuigi Rizzo 
226*f0ea3689SLuigi Rizzo 			/* Grab the GSO header if we don't have it. */
227*f0ea3689SLuigi Rizzo 			if (!gso_hdr) {
228*f0ea3689SLuigi Rizzo 				uint16_t ethertype;
229*f0ea3689SLuigi Rizzo 
230*f0ea3689SLuigi Rizzo 				gso_hdr = src;
231*f0ea3689SLuigi Rizzo 
232*f0ea3689SLuigi Rizzo 				/* Look at the 'Ethertype' field to see if this packet
233*f0ea3689SLuigi Rizzo 				 * is IPv4 or IPv6.
234*f0ea3689SLuigi Rizzo 				 */
235*f0ea3689SLuigi Rizzo 				ethertype = be16toh(*((uint16_t *)(gso_hdr  + 12)));
236*f0ea3689SLuigi Rizzo 				if (ethertype == 0x0800)
237*f0ea3689SLuigi Rizzo 					iphlen = 20;
238*f0ea3689SLuigi Rizzo 				else /* if (ethertype == 0x86DD) */
239*f0ea3689SLuigi Rizzo 					iphlen = 40;
240*f0ea3689SLuigi Rizzo 				ND(3, "type=%04x", ethertype);
241*f0ea3689SLuigi Rizzo 
242*f0ea3689SLuigi Rizzo 				/* Compute gso_hdr_len. For TCP we need to read the
243*f0ea3689SLuigi Rizzo 				 * content of the 'Data Offset' field.
244*f0ea3689SLuigi Rizzo 				 */
245*f0ea3689SLuigi Rizzo 				if (tcp) {
246*f0ea3689SLuigi Rizzo 					struct nm_tcphdr *tcph =
247*f0ea3689SLuigi Rizzo 						(struct nm_tcphdr *)&gso_hdr[14+iphlen];
248*f0ea3689SLuigi Rizzo 
249*f0ea3689SLuigi Rizzo 					gso_hdr_len = 14 + iphlen + 4*(tcph->doff >> 4);
250*f0ea3689SLuigi Rizzo 				} else
251*f0ea3689SLuigi Rizzo 					gso_hdr_len = 14 + iphlen + 8; /* UDP */
252*f0ea3689SLuigi Rizzo 
253*f0ea3689SLuigi Rizzo 				ND(3, "gso_hdr_len %u gso_mtu %d", gso_hdr_len,
254*f0ea3689SLuigi Rizzo 								dst_na->mfs);
255*f0ea3689SLuigi Rizzo 
256*f0ea3689SLuigi Rizzo 				/* Advance source pointers. */
257*f0ea3689SLuigi Rizzo 				src += gso_hdr_len;
258*f0ea3689SLuigi Rizzo 				src_len -= gso_hdr_len;
259*f0ea3689SLuigi Rizzo 				if (src_len == 0) {
260*f0ea3689SLuigi Rizzo 					ft_p++;
261*f0ea3689SLuigi Rizzo 					if (ft_p == ft_end)
262*f0ea3689SLuigi Rizzo 						break;
263*f0ea3689SLuigi Rizzo 					src = ft_p->ft_buf;
264*f0ea3689SLuigi Rizzo 					src_len = ft_p->ft_len;
265*f0ea3689SLuigi Rizzo 					continue;
266*f0ea3689SLuigi Rizzo 				}
267*f0ea3689SLuigi Rizzo 			}
268*f0ea3689SLuigi Rizzo 
269*f0ea3689SLuigi Rizzo 			/* Fill in the header of the current segment. */
270*f0ea3689SLuigi Rizzo 			if (gso_bytes == 0) {
271*f0ea3689SLuigi Rizzo 				memcpy(dst, gso_hdr, gso_hdr_len);
272*f0ea3689SLuigi Rizzo 				gso_bytes = gso_hdr_len;
273*f0ea3689SLuigi Rizzo 			}
274*f0ea3689SLuigi Rizzo 
275*f0ea3689SLuigi Rizzo 			/* Fill in data and update source and dest pointers. */
276*f0ea3689SLuigi Rizzo 			copy = src_len;
277*f0ea3689SLuigi Rizzo 			if (gso_bytes + copy > dst_na->mfs)
278*f0ea3689SLuigi Rizzo 				copy = dst_na->mfs - gso_bytes;
279*f0ea3689SLuigi Rizzo 			memcpy(dst + gso_bytes, src, copy);
280*f0ea3689SLuigi Rizzo 			gso_bytes += copy;
281*f0ea3689SLuigi Rizzo 			src += copy;
282*f0ea3689SLuigi Rizzo 			src_len -= copy;
283*f0ea3689SLuigi Rizzo 
284*f0ea3689SLuigi Rizzo 			/* A segment is complete or we have processed all the
285*f0ea3689SLuigi Rizzo 			   the GSO payload bytes. */
286*f0ea3689SLuigi Rizzo 			if (gso_bytes >= dst_na->mfs ||
287*f0ea3689SLuigi Rizzo 				(src_len == 0 && ft_p + 1 == ft_end)) {
288*f0ea3689SLuigi Rizzo 				/* After raw segmentation, we must fix some header
289*f0ea3689SLuigi Rizzo 				 * fields and compute checksums, in a protocol dependent
290*f0ea3689SLuigi Rizzo 				 * way. */
291*f0ea3689SLuigi Rizzo 				gso_fix_segment(dst, gso_bytes, gso_idx,
292*f0ea3689SLuigi Rizzo 						segmented_bytes,
293*f0ea3689SLuigi Rizzo 						src_len == 0 && ft_p + 1 == ft_end,
294*f0ea3689SLuigi Rizzo 						tcp, iphlen);
295*f0ea3689SLuigi Rizzo 
296*f0ea3689SLuigi Rizzo 				ND("frame %u completed with %d bytes", gso_idx, (int)gso_bytes);
297*f0ea3689SLuigi Rizzo 				slot->len = gso_bytes;
298*f0ea3689SLuigi Rizzo 				slot->flags = 0;
299*f0ea3689SLuigi Rizzo 				segmented_bytes += gso_bytes - gso_hdr_len;
300*f0ea3689SLuigi Rizzo 
301*f0ea3689SLuigi Rizzo 				dst_slots++;
302*f0ea3689SLuigi Rizzo 
303*f0ea3689SLuigi Rizzo 				/* Next destination slot. */
304*f0ea3689SLuigi Rizzo 				*j = nm_next(*j, lim);
305*f0ea3689SLuigi Rizzo 				slot = &ring->slot[*j];
306*f0ea3689SLuigi Rizzo 				dst = BDG_NMB(&dst_na->up, slot);
307*f0ea3689SLuigi Rizzo 
308*f0ea3689SLuigi Rizzo 				gso_bytes = 0;
309*f0ea3689SLuigi Rizzo 				gso_idx++;
310*f0ea3689SLuigi Rizzo 			}
311*f0ea3689SLuigi Rizzo 
312*f0ea3689SLuigi Rizzo 			/* Next input slot. */
313*f0ea3689SLuigi Rizzo 			if (src_len == 0) {
314*f0ea3689SLuigi Rizzo 				ft_p++;
315*f0ea3689SLuigi Rizzo 				if (ft_p == ft_end)
316*f0ea3689SLuigi Rizzo 					break;
317*f0ea3689SLuigi Rizzo 				src = ft_p->ft_buf;
318*f0ea3689SLuigi Rizzo 				src_len = ft_p->ft_len;
319*f0ea3689SLuigi Rizzo 			}
320*f0ea3689SLuigi Rizzo 		}
321*f0ea3689SLuigi Rizzo 		ND(3, "%d bytes segmented", segmented_bytes);
322*f0ea3689SLuigi Rizzo 
323*f0ea3689SLuigi Rizzo 	} else {
324*f0ea3689SLuigi Rizzo 		/* Address of a checksum field into a destination slot. */
325*f0ea3689SLuigi Rizzo 		uint16_t *check = NULL;
326*f0ea3689SLuigi Rizzo 		/* Accumulator for an unfolded checksum. */
327*f0ea3689SLuigi Rizzo 		rawsum_t csum = 0;
328*f0ea3689SLuigi Rizzo 
329*f0ea3689SLuigi Rizzo 		/* Process a non-GSO packet. */
330*f0ea3689SLuigi Rizzo 
331*f0ea3689SLuigi Rizzo 		/* Init 'check' if necessary. */
332*f0ea3689SLuigi Rizzo 		if (vh && (vh->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM)) {
333*f0ea3689SLuigi Rizzo 			if (unlikely(vh->csum_offset + vh->csum_start > src_len))
334*f0ea3689SLuigi Rizzo 				D("invalid checksum request");
335*f0ea3689SLuigi Rizzo 			else
336*f0ea3689SLuigi Rizzo 				check = (uint16_t *)(dst + vh->csum_start +
337*f0ea3689SLuigi Rizzo 						vh->csum_offset);
338*f0ea3689SLuigi Rizzo 		}
339*f0ea3689SLuigi Rizzo 
340*f0ea3689SLuigi Rizzo 		while (ft_p != ft_end) {
341*f0ea3689SLuigi Rizzo 			/* Init/update the packet checksum if needed. */
342*f0ea3689SLuigi Rizzo 			if (vh && (vh->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM)) {
343*f0ea3689SLuigi Rizzo 				if (!dst_slots)
344*f0ea3689SLuigi Rizzo 					csum = nm_csum_raw(src + vh->csum_start,
345*f0ea3689SLuigi Rizzo 								src_len - vh->csum_start, 0);
346*f0ea3689SLuigi Rizzo 				else
347*f0ea3689SLuigi Rizzo 					csum = nm_csum_raw(src, src_len, csum);
348*f0ea3689SLuigi Rizzo 			}
349*f0ea3689SLuigi Rizzo 
350*f0ea3689SLuigi Rizzo 			/* Round to a multiple of 64 */
351*f0ea3689SLuigi Rizzo 			src_len = (src_len + 63) & ~63;
352*f0ea3689SLuigi Rizzo 
353*f0ea3689SLuigi Rizzo 			if (ft_p->ft_flags & NS_INDIRECT) {
354*f0ea3689SLuigi Rizzo 				if (copyin(src, dst, src_len)) {
355*f0ea3689SLuigi Rizzo 					/* Invalid user pointer, pretend len is 0. */
356*f0ea3689SLuigi Rizzo 					dst_len = 0;
357*f0ea3689SLuigi Rizzo 				}
358*f0ea3689SLuigi Rizzo 			} else {
359*f0ea3689SLuigi Rizzo 				memcpy(dst, src, (int)src_len);
360*f0ea3689SLuigi Rizzo 			}
361*f0ea3689SLuigi Rizzo 			slot->len = dst_len;
362*f0ea3689SLuigi Rizzo 
363*f0ea3689SLuigi Rizzo 			dst_slots++;
364*f0ea3689SLuigi Rizzo 
365*f0ea3689SLuigi Rizzo 			/* Next destination slot. */
366*f0ea3689SLuigi Rizzo 			*j = nm_next(*j, lim);
367*f0ea3689SLuigi Rizzo 			slot = &ring->slot[*j];
368*f0ea3689SLuigi Rizzo 			dst = BDG_NMB(&dst_na->up, slot);
369*f0ea3689SLuigi Rizzo 
370*f0ea3689SLuigi Rizzo 			/* Next source slot. */
371*f0ea3689SLuigi Rizzo 			ft_p++;
372*f0ea3689SLuigi Rizzo 			src = ft_p->ft_buf;
373*f0ea3689SLuigi Rizzo 			dst_len = src_len = ft_p->ft_len;
374*f0ea3689SLuigi Rizzo 
375*f0ea3689SLuigi Rizzo 		}
376*f0ea3689SLuigi Rizzo 
377*f0ea3689SLuigi Rizzo 		/* Finalize (fold) the checksum if needed. */
378*f0ea3689SLuigi Rizzo 		if (check && vh && (vh->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM)) {
379*f0ea3689SLuigi Rizzo 			*check = nm_csum_fold(csum);
380*f0ea3689SLuigi Rizzo 		}
381*f0ea3689SLuigi Rizzo 		ND(3, "using %u dst_slots", dst_slots);
382*f0ea3689SLuigi Rizzo 
383*f0ea3689SLuigi Rizzo 		/* A second pass on the desitations slots to set the slot flags,
384*f0ea3689SLuigi Rizzo 		 * using the right number of destination slots.
385*f0ea3689SLuigi Rizzo 		 */
386*f0ea3689SLuigi Rizzo 		while (j_start != *j) {
387*f0ea3689SLuigi Rizzo 			slot = &ring->slot[j_start];
388*f0ea3689SLuigi Rizzo 			slot->flags = (dst_slots << 8)| NS_MOREFRAG;
389*f0ea3689SLuigi Rizzo 			j_start = nm_next(j_start, lim);
390*f0ea3689SLuigi Rizzo 		}
391*f0ea3689SLuigi Rizzo 		/* Clear NS_MOREFRAG flag on last entry. */
392*f0ea3689SLuigi Rizzo 		slot->flags = (dst_slots << 8);
393*f0ea3689SLuigi Rizzo 	}
394*f0ea3689SLuigi Rizzo 
395*f0ea3689SLuigi Rizzo 	/* Update howmany. */
396*f0ea3689SLuigi Rizzo 	if (unlikely(dst_slots > *howmany)) {
397*f0ea3689SLuigi Rizzo 		dst_slots = *howmany;
398*f0ea3689SLuigi Rizzo 		D("Slot allocation error: Should never happen");
399*f0ea3689SLuigi Rizzo 	}
400*f0ea3689SLuigi Rizzo 	*howmany -= dst_slots;
401*f0ea3689SLuigi Rizzo }
402