1718cf2ccSPedro F. Giffuni /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni *
437e3a6d3SLuigi Rizzo * Copyright (C) 2014-2015 Vincenzo Maffione
537e3a6d3SLuigi Rizzo * All rights reserved.
6f0ea3689SLuigi Rizzo *
7f0ea3689SLuigi Rizzo * Redistribution and use in source and binary forms, with or without
8f0ea3689SLuigi Rizzo * modification, are permitted provided that the following conditions
9f0ea3689SLuigi Rizzo * are met:
10f0ea3689SLuigi Rizzo * 1. Redistributions of source code must retain the above copyright
11f0ea3689SLuigi Rizzo * notice, this list of conditions and the following disclaimer.
12f0ea3689SLuigi Rizzo * 2. Redistributions in binary form must reproduce the above copyright
13f0ea3689SLuigi Rizzo * notice, this list of conditions and the following disclaimer in the
14f0ea3689SLuigi Rizzo * documentation and/or other materials provided with the distribution.
15f0ea3689SLuigi Rizzo *
16f0ea3689SLuigi Rizzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17f0ea3689SLuigi Rizzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18f0ea3689SLuigi Rizzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19f0ea3689SLuigi Rizzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20f0ea3689SLuigi Rizzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21f0ea3689SLuigi Rizzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22f0ea3689SLuigi Rizzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23f0ea3689SLuigi Rizzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24f0ea3689SLuigi Rizzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25f0ea3689SLuigi Rizzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26f0ea3689SLuigi Rizzo * SUCH DAMAGE.
27f0ea3689SLuigi Rizzo */
28f0ea3689SLuigi Rizzo
29f0ea3689SLuigi Rizzo
30f0ea3689SLuigi Rizzo #if defined(__FreeBSD__)
31f0ea3689SLuigi Rizzo #include <sys/cdefs.h> /* prerequisite */
32f0ea3689SLuigi Rizzo
33f0ea3689SLuigi Rizzo #include <sys/types.h>
34f0ea3689SLuigi Rizzo #include <sys/errno.h>
35f0ea3689SLuigi Rizzo #include <sys/param.h> /* defines used in kernel.h */
36f0ea3689SLuigi Rizzo #include <sys/kernel.h> /* types used in module initialization */
37f0ea3689SLuigi Rizzo #include <sys/sockio.h>
3837e3a6d3SLuigi Rizzo #include <sys/malloc.h>
39f0ea3689SLuigi Rizzo #include <sys/socketvar.h> /* struct socket */
40f0ea3689SLuigi Rizzo #include <sys/socket.h> /* sockaddrs */
41f0ea3689SLuigi Rizzo #include <net/if.h>
42f0ea3689SLuigi Rizzo #include <net/if_var.h>
43f0ea3689SLuigi Rizzo #include <machine/bus.h> /* bus_dmamap_* */
44f0ea3689SLuigi Rizzo #include <sys/endian.h>
45f0ea3689SLuigi Rizzo
46f0ea3689SLuigi Rizzo #elif defined(linux)
47f0ea3689SLuigi Rizzo
48f0ea3689SLuigi Rizzo #include "bsd_glue.h"
49f0ea3689SLuigi Rizzo
50f0ea3689SLuigi Rizzo #elif defined(__APPLE__)
51f0ea3689SLuigi Rizzo
52f0ea3689SLuigi Rizzo #warning OSX support is only partial
53f0ea3689SLuigi Rizzo #include "osx_glue.h"
54f0ea3689SLuigi Rizzo
55f0ea3689SLuigi Rizzo #else
56f0ea3689SLuigi Rizzo
57f0ea3689SLuigi Rizzo #error Unsupported platform
58f0ea3689SLuigi Rizzo
59f0ea3689SLuigi Rizzo #endif /* unsupported */
60f0ea3689SLuigi Rizzo
61f0ea3689SLuigi Rizzo #include <net/netmap.h>
62f0ea3689SLuigi Rizzo #include <dev/netmap/netmap_kern.h>
63f0ea3689SLuigi Rizzo
64f0ea3689SLuigi Rizzo
65f0ea3689SLuigi Rizzo
66f0ea3689SLuigi Rizzo /* This routine is called by bdg_mismatch_datapath() when it finishes
67f0ea3689SLuigi Rizzo * accumulating bytes for a segment, in order to fix some fields in the
68f0ea3689SLuigi Rizzo * segment headers (which still contain the same content as the header
6937e3a6d3SLuigi Rizzo * of the original GSO packet). 'pkt' points to the beginning of the IP
7037e3a6d3SLuigi Rizzo * header of the segment, while 'len' is the length of the IP packet.
71f0ea3689SLuigi Rizzo */
7237e3a6d3SLuigi Rizzo static void
gso_fix_segment(uint8_t * pkt,size_t len,u_int ipv4,u_int iphlen,u_int tcp,u_int idx,u_int segmented_bytes,u_int last_segment)7337e3a6d3SLuigi Rizzo gso_fix_segment(uint8_t *pkt, size_t len, u_int ipv4, u_int iphlen, u_int tcp,
7437e3a6d3SLuigi Rizzo u_int idx, u_int segmented_bytes, u_int last_segment)
75f0ea3689SLuigi Rizzo {
7637e3a6d3SLuigi Rizzo struct nm_iphdr *iph = (struct nm_iphdr *)(pkt);
7737e3a6d3SLuigi Rizzo struct nm_ipv6hdr *ip6h = (struct nm_ipv6hdr *)(pkt);
78f0ea3689SLuigi Rizzo uint16_t *check = NULL;
79f0ea3689SLuigi Rizzo uint8_t *check_data = NULL;
80f0ea3689SLuigi Rizzo
8137e3a6d3SLuigi Rizzo if (ipv4) {
82f0ea3689SLuigi Rizzo /* Set the IPv4 "Total Length" field. */
8337e3a6d3SLuigi Rizzo iph->tot_len = htobe16(len);
8475f4f3edSVincenzo Maffione nm_prdis("ip total length %u", be16toh(ip->tot_len));
85f0ea3689SLuigi Rizzo
86f0ea3689SLuigi Rizzo /* Set the IPv4 "Identification" field. */
87f0ea3689SLuigi Rizzo iph->id = htobe16(be16toh(iph->id) + idx);
8875f4f3edSVincenzo Maffione nm_prdis("ip identification %u", be16toh(iph->id));
89f0ea3689SLuigi Rizzo
90f0ea3689SLuigi Rizzo /* Compute and insert the IPv4 header checksum. */
91f0ea3689SLuigi Rizzo iph->check = 0;
9237e3a6d3SLuigi Rizzo iph->check = nm_os_csum_ipv4(iph);
9375f4f3edSVincenzo Maffione nm_prdis("IP csum %x", be16toh(iph->check));
9437e3a6d3SLuigi Rizzo } else {
95f0ea3689SLuigi Rizzo /* Set the IPv6 "Payload Len" field. */
9637e3a6d3SLuigi Rizzo ip6h->payload_len = htobe16(len-iphlen);
97f0ea3689SLuigi Rizzo }
98f0ea3689SLuigi Rizzo
99f0ea3689SLuigi Rizzo if (tcp) {
10037e3a6d3SLuigi Rizzo struct nm_tcphdr *tcph = (struct nm_tcphdr *)(pkt + iphlen);
101f0ea3689SLuigi Rizzo
102f0ea3689SLuigi Rizzo /* Set the TCP sequence number. */
103f0ea3689SLuigi Rizzo tcph->seq = htobe32(be32toh(tcph->seq) + segmented_bytes);
10475f4f3edSVincenzo Maffione nm_prdis("tcp seq %u", be32toh(tcph->seq));
105f0ea3689SLuigi Rizzo
106f0ea3689SLuigi Rizzo /* Zero the PSH and FIN TCP flags if this is not the last
107f0ea3689SLuigi Rizzo segment. */
108f0ea3689SLuigi Rizzo if (!last_segment)
109f0ea3689SLuigi Rizzo tcph->flags &= ~(0x8 | 0x1);
11075f4f3edSVincenzo Maffione nm_prdis("last_segment %u", last_segment);
111f0ea3689SLuigi Rizzo
112f0ea3689SLuigi Rizzo check = &tcph->check;
113f0ea3689SLuigi Rizzo check_data = (uint8_t *)tcph;
114f0ea3689SLuigi Rizzo } else { /* UDP */
11537e3a6d3SLuigi Rizzo struct nm_udphdr *udph = (struct nm_udphdr *)(pkt + iphlen);
116f0ea3689SLuigi Rizzo
117f0ea3689SLuigi Rizzo /* Set the UDP 'Length' field. */
11837e3a6d3SLuigi Rizzo udph->len = htobe16(len-iphlen);
119f0ea3689SLuigi Rizzo
120f0ea3689SLuigi Rizzo check = &udph->check;
121f0ea3689SLuigi Rizzo check_data = (uint8_t *)udph;
122f0ea3689SLuigi Rizzo }
123f0ea3689SLuigi Rizzo
124f0ea3689SLuigi Rizzo /* Compute and insert TCP/UDP checksum. */
125f0ea3689SLuigi Rizzo *check = 0;
12637e3a6d3SLuigi Rizzo if (ipv4)
12737e3a6d3SLuigi Rizzo nm_os_csum_tcpudp_ipv4(iph, check_data, len-iphlen, check);
128f0ea3689SLuigi Rizzo else
12937e3a6d3SLuigi Rizzo nm_os_csum_tcpudp_ipv6(ip6h, check_data, len-iphlen, check);
130f0ea3689SLuigi Rizzo
13175f4f3edSVincenzo Maffione nm_prdis("TCP/UDP csum %x", be16toh(*check));
132f0ea3689SLuigi Rizzo }
133f0ea3689SLuigi Rizzo
1344f80b14cSVincenzo Maffione static inline int
vnet_hdr_is_bad(struct nm_vnet_hdr * vh)13537e3a6d3SLuigi Rizzo vnet_hdr_is_bad(struct nm_vnet_hdr *vh)
13637e3a6d3SLuigi Rizzo {
13737e3a6d3SLuigi Rizzo uint8_t gso_type = vh->gso_type & ~VIRTIO_NET_HDR_GSO_ECN;
13837e3a6d3SLuigi Rizzo
13937e3a6d3SLuigi Rizzo return (
14037e3a6d3SLuigi Rizzo (gso_type != VIRTIO_NET_HDR_GSO_NONE &&
14137e3a6d3SLuigi Rizzo gso_type != VIRTIO_NET_HDR_GSO_TCPV4 &&
14237e3a6d3SLuigi Rizzo gso_type != VIRTIO_NET_HDR_GSO_UDP &&
14337e3a6d3SLuigi Rizzo gso_type != VIRTIO_NET_HDR_GSO_TCPV6)
14437e3a6d3SLuigi Rizzo ||
14537e3a6d3SLuigi Rizzo (vh->flags & ~(VIRTIO_NET_HDR_F_NEEDS_CSUM
14637e3a6d3SLuigi Rizzo | VIRTIO_NET_HDR_F_DATA_VALID))
14737e3a6d3SLuigi Rizzo );
14837e3a6d3SLuigi Rizzo }
149f0ea3689SLuigi Rizzo
150f0ea3689SLuigi Rizzo /* The VALE mismatch datapath implementation. */
15137e3a6d3SLuigi Rizzo void
bdg_mismatch_datapath(struct netmap_vp_adapter * na,struct netmap_vp_adapter * dst_na,const struct nm_bdg_fwd * ft_p,struct netmap_ring * dst_ring,u_int * j,u_int lim,u_int * howmany)15237e3a6d3SLuigi Rizzo bdg_mismatch_datapath(struct netmap_vp_adapter *na,
153f0ea3689SLuigi Rizzo struct netmap_vp_adapter *dst_na,
15437e3a6d3SLuigi Rizzo const struct nm_bdg_fwd *ft_p,
15537e3a6d3SLuigi Rizzo struct netmap_ring *dst_ring,
156f0ea3689SLuigi Rizzo u_int *j, u_int lim, u_int *howmany)
157f0ea3689SLuigi Rizzo {
15837e3a6d3SLuigi Rizzo struct netmap_slot *dst_slot = NULL;
159f0ea3689SLuigi Rizzo struct nm_vnet_hdr *vh = NULL;
16037e3a6d3SLuigi Rizzo const struct nm_bdg_fwd *ft_end = ft_p + ft_p->ft_frags;
161f0ea3689SLuigi Rizzo
162f0ea3689SLuigi Rizzo /* Source and destination pointers. */
163f0ea3689SLuigi Rizzo uint8_t *dst, *src;
164f0ea3689SLuigi Rizzo size_t src_len, dst_len;
165f0ea3689SLuigi Rizzo
16637e3a6d3SLuigi Rizzo /* Indices and counters for the destination ring. */
167f0ea3689SLuigi Rizzo u_int j_start = *j;
16837e3a6d3SLuigi Rizzo u_int j_cur = j_start;
169f0ea3689SLuigi Rizzo u_int dst_slots = 0;
170f0ea3689SLuigi Rizzo
17137e3a6d3SLuigi Rizzo if (unlikely(ft_p == ft_end)) {
17275f4f3edSVincenzo Maffione nm_prlim(1, "No source slots to process");
17337e3a6d3SLuigi Rizzo return;
174f0ea3689SLuigi Rizzo }
175f0ea3689SLuigi Rizzo
176f0ea3689SLuigi Rizzo /* Init source and dest pointers. */
177f0ea3689SLuigi Rizzo src = ft_p->ft_buf;
178f0ea3689SLuigi Rizzo src_len = ft_p->ft_len;
17937e3a6d3SLuigi Rizzo dst_slot = &dst_ring->slot[j_cur];
18037e3a6d3SLuigi Rizzo dst = NMB(&dst_na->up, dst_slot);
181f0ea3689SLuigi Rizzo dst_len = src_len;
182f0ea3689SLuigi Rizzo
18337e3a6d3SLuigi Rizzo /* If the source port uses the offloadings, while destination doesn't,
18437e3a6d3SLuigi Rizzo * we grab the source virtio-net header and do the offloadings here.
18537e3a6d3SLuigi Rizzo */
18637e3a6d3SLuigi Rizzo if (na->up.virt_hdr_len && !dst_na->up.virt_hdr_len) {
18737e3a6d3SLuigi Rizzo vh = (struct nm_vnet_hdr *)src;
18837e3a6d3SLuigi Rizzo /* Initial sanity check on the source virtio-net header. If
18937e3a6d3SLuigi Rizzo * something seems wrong, just drop the packet. */
19037e3a6d3SLuigi Rizzo if (src_len < na->up.virt_hdr_len) {
19175f4f3edSVincenzo Maffione nm_prlim(1, "Short src vnet header, dropping");
19237e3a6d3SLuigi Rizzo return;
19337e3a6d3SLuigi Rizzo }
1944f80b14cSVincenzo Maffione if (unlikely(vnet_hdr_is_bad(vh))) {
19575f4f3edSVincenzo Maffione nm_prlim(1, "Bad src vnet header, dropping");
19637e3a6d3SLuigi Rizzo return;
19737e3a6d3SLuigi Rizzo }
19837e3a6d3SLuigi Rizzo }
19937e3a6d3SLuigi Rizzo
200f0ea3689SLuigi Rizzo /* We are processing the first input slot and there is a mismatch
201f0ea3689SLuigi Rizzo * between source and destination virt_hdr_len (SHL and DHL).
202f0ea3689SLuigi Rizzo * When the a client is using virtio-net headers, the header length
203f0ea3689SLuigi Rizzo * can be:
204f0ea3689SLuigi Rizzo * - 10: the header corresponds to the struct nm_vnet_hdr
205f0ea3689SLuigi Rizzo * - 12: the first 10 bytes correspond to the struct
206f0ea3689SLuigi Rizzo * virtio_net_hdr, and the last 2 bytes store the
207f0ea3689SLuigi Rizzo * "mergeable buffers" info, which is an optional
208453130d9SPedro F. Giffuni * hint that can be zeroed for compatibility
209f0ea3689SLuigi Rizzo *
210f0ea3689SLuigi Rizzo * The destination header is therefore built according to the
211f0ea3689SLuigi Rizzo * following table:
212f0ea3689SLuigi Rizzo *
213f0ea3689SLuigi Rizzo * SHL | DHL | destination header
214f0ea3689SLuigi Rizzo * -----------------------------
215f0ea3689SLuigi Rizzo * 0 | 10 | zero
216f0ea3689SLuigi Rizzo * 0 | 12 | zero
217f0ea3689SLuigi Rizzo * 10 | 0 | doesn't exist
218f0ea3689SLuigi Rizzo * 10 | 12 | first 10 bytes are copied from source header, last 2 are zero
219f0ea3689SLuigi Rizzo * 12 | 0 | doesn't exist
220f0ea3689SLuigi Rizzo * 12 | 10 | copied from the first 10 bytes of source header
221f0ea3689SLuigi Rizzo */
22237e3a6d3SLuigi Rizzo bzero(dst, dst_na->up.virt_hdr_len);
22337e3a6d3SLuigi Rizzo if (na->up.virt_hdr_len && dst_na->up.virt_hdr_len)
224f0ea3689SLuigi Rizzo memcpy(dst, src, sizeof(struct nm_vnet_hdr));
225f0ea3689SLuigi Rizzo /* Skip the virtio-net headers. */
22637e3a6d3SLuigi Rizzo src += na->up.virt_hdr_len;
22737e3a6d3SLuigi Rizzo src_len -= na->up.virt_hdr_len;
22837e3a6d3SLuigi Rizzo dst += dst_na->up.virt_hdr_len;
22937e3a6d3SLuigi Rizzo dst_len = dst_na->up.virt_hdr_len + src_len;
230f0ea3689SLuigi Rizzo
231f0ea3689SLuigi Rizzo /* Here it could be dst_len == 0 (which implies src_len == 0),
232f0ea3689SLuigi Rizzo * so we avoid passing a zero length fragment.
233f0ea3689SLuigi Rizzo */
234f0ea3689SLuigi Rizzo if (dst_len == 0) {
235f0ea3689SLuigi Rizzo ft_p++;
236f0ea3689SLuigi Rizzo src = ft_p->ft_buf;
237f0ea3689SLuigi Rizzo src_len = ft_p->ft_len;
238f0ea3689SLuigi Rizzo dst_len = src_len;
239f0ea3689SLuigi Rizzo }
240f0ea3689SLuigi Rizzo
241f0ea3689SLuigi Rizzo if (vh && vh->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
242f0ea3689SLuigi Rizzo u_int gso_bytes = 0;
243f0ea3689SLuigi Rizzo /* Length of the GSO packet header. */
244f0ea3689SLuigi Rizzo u_int gso_hdr_len = 0;
245f0ea3689SLuigi Rizzo /* Pointer to the GSO packet header. Assume it is in a single fragment. */
246f0ea3689SLuigi Rizzo uint8_t *gso_hdr = NULL;
247f0ea3689SLuigi Rizzo /* Index of the current segment. */
248f0ea3689SLuigi Rizzo u_int gso_idx = 0;
249f0ea3689SLuigi Rizzo /* Payload data bytes segmented so far (e.g. TCP data bytes). */
250f0ea3689SLuigi Rizzo u_int segmented_bytes = 0;
25137e3a6d3SLuigi Rizzo /* Is this an IPv4 or IPv6 GSO packet? */
25237e3a6d3SLuigi Rizzo u_int ipv4 = 0;
253f0ea3689SLuigi Rizzo /* Length of the IP header (20 if IPv4, 40 if IPv6). */
254f0ea3689SLuigi Rizzo u_int iphlen = 0;
25537e3a6d3SLuigi Rizzo /* Length of the Ethernet header (18 if 802.1q, otherwise 14). */
25637e3a6d3SLuigi Rizzo u_int ethhlen = 14;
257f0ea3689SLuigi Rizzo /* Is this a TCP or an UDP GSO packet? */
258f0ea3689SLuigi Rizzo u_int tcp = ((vh->gso_type & ~VIRTIO_NET_HDR_GSO_ECN)
259f0ea3689SLuigi Rizzo == VIRTIO_NET_HDR_GSO_UDP) ? 0 : 1;
260f0ea3689SLuigi Rizzo
261f0ea3689SLuigi Rizzo /* Segment the GSO packet contained into the input slots (frags). */
26237e3a6d3SLuigi Rizzo for (;;) {
263f0ea3689SLuigi Rizzo size_t copy;
264f0ea3689SLuigi Rizzo
26537e3a6d3SLuigi Rizzo if (dst_slots >= *howmany) {
26637e3a6d3SLuigi Rizzo /* We still have work to do, but we've run out of
26737e3a6d3SLuigi Rizzo * dst slots, so we have to drop the packet. */
26875f4f3edSVincenzo Maffione nm_prdis(1, "Not enough slots, dropping GSO packet");
26937e3a6d3SLuigi Rizzo return;
27037e3a6d3SLuigi Rizzo }
27137e3a6d3SLuigi Rizzo
272f0ea3689SLuigi Rizzo /* Grab the GSO header if we don't have it. */
273f0ea3689SLuigi Rizzo if (!gso_hdr) {
274f0ea3689SLuigi Rizzo uint16_t ethertype;
275f0ea3689SLuigi Rizzo
276f0ea3689SLuigi Rizzo gso_hdr = src;
277f0ea3689SLuigi Rizzo
278f0ea3689SLuigi Rizzo /* Look at the 'Ethertype' field to see if this packet
27937e3a6d3SLuigi Rizzo * is IPv4 or IPv6, taking into account VLAN
28037e3a6d3SLuigi Rizzo * encapsulation. */
28137e3a6d3SLuigi Rizzo for (;;) {
28237e3a6d3SLuigi Rizzo if (src_len < ethhlen) {
28375f4f3edSVincenzo Maffione nm_prlim(1, "Short GSO fragment [eth], dropping");
28437e3a6d3SLuigi Rizzo return;
28537e3a6d3SLuigi Rizzo }
28637e3a6d3SLuigi Rizzo ethertype = be16toh(*((uint16_t *)
28737e3a6d3SLuigi Rizzo (gso_hdr + ethhlen - 2)));
28837e3a6d3SLuigi Rizzo if (ethertype != 0x8100) /* not 802.1q */
28937e3a6d3SLuigi Rizzo break;
29037e3a6d3SLuigi Rizzo ethhlen += 4;
29137e3a6d3SLuigi Rizzo }
29237e3a6d3SLuigi Rizzo switch (ethertype) {
29337e3a6d3SLuigi Rizzo case 0x0800: /* IPv4 */
29437e3a6d3SLuigi Rizzo {
29537e3a6d3SLuigi Rizzo struct nm_iphdr *iph = (struct nm_iphdr *)
29637e3a6d3SLuigi Rizzo (gso_hdr + ethhlen);
29737e3a6d3SLuigi Rizzo
29837e3a6d3SLuigi Rizzo if (src_len < ethhlen + 20) {
29975f4f3edSVincenzo Maffione nm_prlim(1, "Short GSO fragment "
30037e3a6d3SLuigi Rizzo "[IPv4], dropping");
30137e3a6d3SLuigi Rizzo return;
30237e3a6d3SLuigi Rizzo }
30337e3a6d3SLuigi Rizzo ipv4 = 1;
30437e3a6d3SLuigi Rizzo iphlen = 4 * (iph->version_ihl & 0x0F);
30537e3a6d3SLuigi Rizzo break;
30637e3a6d3SLuigi Rizzo }
30737e3a6d3SLuigi Rizzo case 0x86DD: /* IPv6 */
30837e3a6d3SLuigi Rizzo ipv4 = 0;
309f0ea3689SLuigi Rizzo iphlen = 40;
31037e3a6d3SLuigi Rizzo break;
31137e3a6d3SLuigi Rizzo default:
31275f4f3edSVincenzo Maffione nm_prlim(1, "Unsupported ethertype, "
31337e3a6d3SLuigi Rizzo "dropping GSO packet");
31437e3a6d3SLuigi Rizzo return;
31537e3a6d3SLuigi Rizzo }
31675f4f3edSVincenzo Maffione nm_prdis(3, "type=%04x", ethertype);
317f0ea3689SLuigi Rizzo
31837e3a6d3SLuigi Rizzo if (src_len < ethhlen + iphlen) {
31975f4f3edSVincenzo Maffione nm_prlim(1, "Short GSO fragment [IP], dropping");
32037e3a6d3SLuigi Rizzo return;
32137e3a6d3SLuigi Rizzo }
32237e3a6d3SLuigi Rizzo
323f0ea3689SLuigi Rizzo /* Compute gso_hdr_len. For TCP we need to read the
324f0ea3689SLuigi Rizzo * content of the 'Data Offset' field.
325f0ea3689SLuigi Rizzo */
326f0ea3689SLuigi Rizzo if (tcp) {
32737e3a6d3SLuigi Rizzo struct nm_tcphdr *tcph = (struct nm_tcphdr *)
32837e3a6d3SLuigi Rizzo (gso_hdr + ethhlen + iphlen);
329f0ea3689SLuigi Rizzo
33037e3a6d3SLuigi Rizzo if (src_len < ethhlen + iphlen + 20) {
33175f4f3edSVincenzo Maffione nm_prlim(1, "Short GSO fragment "
33237e3a6d3SLuigi Rizzo "[TCP], dropping");
33337e3a6d3SLuigi Rizzo return;
33437e3a6d3SLuigi Rizzo }
33537e3a6d3SLuigi Rizzo gso_hdr_len = ethhlen + iphlen +
33637e3a6d3SLuigi Rizzo 4 * (tcph->doff >> 4);
33737e3a6d3SLuigi Rizzo } else {
33837e3a6d3SLuigi Rizzo gso_hdr_len = ethhlen + iphlen + 8; /* UDP */
33937e3a6d3SLuigi Rizzo }
34037e3a6d3SLuigi Rizzo
34137e3a6d3SLuigi Rizzo if (src_len < gso_hdr_len) {
34275f4f3edSVincenzo Maffione nm_prlim(1, "Short GSO fragment [TCP/UDP], dropping");
34337e3a6d3SLuigi Rizzo return;
34437e3a6d3SLuigi Rizzo }
345f0ea3689SLuigi Rizzo
34675f4f3edSVincenzo Maffione nm_prdis(3, "gso_hdr_len %u gso_mtu %d", gso_hdr_len,
347f0ea3689SLuigi Rizzo dst_na->mfs);
348f0ea3689SLuigi Rizzo
349f0ea3689SLuigi Rizzo /* Advance source pointers. */
350f0ea3689SLuigi Rizzo src += gso_hdr_len;
351f0ea3689SLuigi Rizzo src_len -= gso_hdr_len;
352f0ea3689SLuigi Rizzo if (src_len == 0) {
353f0ea3689SLuigi Rizzo ft_p++;
354f0ea3689SLuigi Rizzo if (ft_p == ft_end)
355f0ea3689SLuigi Rizzo break;
356f0ea3689SLuigi Rizzo src = ft_p->ft_buf;
357f0ea3689SLuigi Rizzo src_len = ft_p->ft_len;
358f0ea3689SLuigi Rizzo }
359f0ea3689SLuigi Rizzo }
360f0ea3689SLuigi Rizzo
361f0ea3689SLuigi Rizzo /* Fill in the header of the current segment. */
362f0ea3689SLuigi Rizzo if (gso_bytes == 0) {
363f0ea3689SLuigi Rizzo memcpy(dst, gso_hdr, gso_hdr_len);
364f0ea3689SLuigi Rizzo gso_bytes = gso_hdr_len;
365f0ea3689SLuigi Rizzo }
366f0ea3689SLuigi Rizzo
367f0ea3689SLuigi Rizzo /* Fill in data and update source and dest pointers. */
368f0ea3689SLuigi Rizzo copy = src_len;
369f0ea3689SLuigi Rizzo if (gso_bytes + copy > dst_na->mfs)
370f0ea3689SLuigi Rizzo copy = dst_na->mfs - gso_bytes;
371f0ea3689SLuigi Rizzo memcpy(dst + gso_bytes, src, copy);
372f0ea3689SLuigi Rizzo gso_bytes += copy;
373f0ea3689SLuigi Rizzo src += copy;
374f0ea3689SLuigi Rizzo src_len -= copy;
375f0ea3689SLuigi Rizzo
376f0ea3689SLuigi Rizzo /* A segment is complete or we have processed all the
377f0ea3689SLuigi Rizzo the GSO payload bytes. */
378f0ea3689SLuigi Rizzo if (gso_bytes >= dst_na->mfs ||
379f0ea3689SLuigi Rizzo (src_len == 0 && ft_p + 1 == ft_end)) {
380f0ea3689SLuigi Rizzo /* After raw segmentation, we must fix some header
381f0ea3689SLuigi Rizzo * fields and compute checksums, in a protocol dependent
382f0ea3689SLuigi Rizzo * way. */
38337e3a6d3SLuigi Rizzo gso_fix_segment(dst + ethhlen, gso_bytes - ethhlen,
38437e3a6d3SLuigi Rizzo ipv4, iphlen, tcp,
38537e3a6d3SLuigi Rizzo gso_idx, segmented_bytes,
38637e3a6d3SLuigi Rizzo src_len == 0 && ft_p + 1 == ft_end);
387f0ea3689SLuigi Rizzo
38875f4f3edSVincenzo Maffione nm_prdis("frame %u completed with %d bytes", gso_idx, (int)gso_bytes);
38937e3a6d3SLuigi Rizzo dst_slot->len = gso_bytes;
39037e3a6d3SLuigi Rizzo dst_slot->flags = 0;
391f0ea3689SLuigi Rizzo dst_slots++;
39237e3a6d3SLuigi Rizzo segmented_bytes += gso_bytes - gso_hdr_len;
393f0ea3689SLuigi Rizzo
394f0ea3689SLuigi Rizzo gso_bytes = 0;
395f0ea3689SLuigi Rizzo gso_idx++;
39637e3a6d3SLuigi Rizzo
39737e3a6d3SLuigi Rizzo /* Next destination slot. */
39837e3a6d3SLuigi Rizzo j_cur = nm_next(j_cur, lim);
39937e3a6d3SLuigi Rizzo dst_slot = &dst_ring->slot[j_cur];
40037e3a6d3SLuigi Rizzo dst = NMB(&dst_na->up, dst_slot);
401f0ea3689SLuigi Rizzo }
402f0ea3689SLuigi Rizzo
403f0ea3689SLuigi Rizzo /* Next input slot. */
404f0ea3689SLuigi Rizzo if (src_len == 0) {
405f0ea3689SLuigi Rizzo ft_p++;
406f0ea3689SLuigi Rizzo if (ft_p == ft_end)
407f0ea3689SLuigi Rizzo break;
408f0ea3689SLuigi Rizzo src = ft_p->ft_buf;
409f0ea3689SLuigi Rizzo src_len = ft_p->ft_len;
410f0ea3689SLuigi Rizzo }
411f0ea3689SLuigi Rizzo }
41275f4f3edSVincenzo Maffione nm_prdis(3, "%d bytes segmented", segmented_bytes);
413f0ea3689SLuigi Rizzo
414f0ea3689SLuigi Rizzo } else {
415f0ea3689SLuigi Rizzo /* Address of a checksum field into a destination slot. */
416f0ea3689SLuigi Rizzo uint16_t *check = NULL;
417f0ea3689SLuigi Rizzo /* Accumulator for an unfolded checksum. */
418f0ea3689SLuigi Rizzo rawsum_t csum = 0;
419f0ea3689SLuigi Rizzo
420f0ea3689SLuigi Rizzo /* Process a non-GSO packet. */
421f0ea3689SLuigi Rizzo
422f0ea3689SLuigi Rizzo /* Init 'check' if necessary. */
423f0ea3689SLuigi Rizzo if (vh && (vh->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM)) {
424f0ea3689SLuigi Rizzo if (unlikely(vh->csum_offset + vh->csum_start > src_len))
42575f4f3edSVincenzo Maffione nm_prerr("invalid checksum request");
426f0ea3689SLuigi Rizzo else
427f0ea3689SLuigi Rizzo check = (uint16_t *)(dst + vh->csum_start +
428f0ea3689SLuigi Rizzo vh->csum_offset);
429f0ea3689SLuigi Rizzo }
430f0ea3689SLuigi Rizzo
431f0ea3689SLuigi Rizzo while (ft_p != ft_end) {
432f0ea3689SLuigi Rizzo /* Init/update the packet checksum if needed. */
433f0ea3689SLuigi Rizzo if (vh && (vh->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM)) {
434f0ea3689SLuigi Rizzo if (!dst_slots)
43537e3a6d3SLuigi Rizzo csum = nm_os_csum_raw(src + vh->csum_start,
436f0ea3689SLuigi Rizzo src_len - vh->csum_start, 0);
437f0ea3689SLuigi Rizzo else
43837e3a6d3SLuigi Rizzo csum = nm_os_csum_raw(src, src_len, csum);
439f0ea3689SLuigi Rizzo }
440f0ea3689SLuigi Rizzo
441f0ea3689SLuigi Rizzo /* Round to a multiple of 64 */
442f0ea3689SLuigi Rizzo src_len = (src_len + 63) & ~63;
443f0ea3689SLuigi Rizzo
444f0ea3689SLuigi Rizzo if (ft_p->ft_flags & NS_INDIRECT) {
445f0ea3689SLuigi Rizzo if (copyin(src, dst, src_len)) {
446f0ea3689SLuigi Rizzo /* Invalid user pointer, pretend len is 0. */
447f0ea3689SLuigi Rizzo dst_len = 0;
448f0ea3689SLuigi Rizzo }
449f0ea3689SLuigi Rizzo } else {
450f0ea3689SLuigi Rizzo memcpy(dst, src, (int)src_len);
451f0ea3689SLuigi Rizzo }
45237e3a6d3SLuigi Rizzo dst_slot->len = dst_len;
453f0ea3689SLuigi Rizzo dst_slots++;
454f0ea3689SLuigi Rizzo
455f0ea3689SLuigi Rizzo /* Next destination slot. */
45637e3a6d3SLuigi Rizzo j_cur = nm_next(j_cur, lim);
45737e3a6d3SLuigi Rizzo dst_slot = &dst_ring->slot[j_cur];
45837e3a6d3SLuigi Rizzo dst = NMB(&dst_na->up, dst_slot);
459f0ea3689SLuigi Rizzo
460f0ea3689SLuigi Rizzo /* Next source slot. */
461f0ea3689SLuigi Rizzo ft_p++;
462f0ea3689SLuigi Rizzo src = ft_p->ft_buf;
463f0ea3689SLuigi Rizzo dst_len = src_len = ft_p->ft_len;
464f0ea3689SLuigi Rizzo }
465f0ea3689SLuigi Rizzo
466f0ea3689SLuigi Rizzo /* Finalize (fold) the checksum if needed. */
467f0ea3689SLuigi Rizzo if (check && vh && (vh->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM)) {
46837e3a6d3SLuigi Rizzo *check = nm_os_csum_fold(csum);
469f0ea3689SLuigi Rizzo }
47075f4f3edSVincenzo Maffione nm_prdis(3, "using %u dst_slots", dst_slots);
471f0ea3689SLuigi Rizzo
47237e3a6d3SLuigi Rizzo /* A second pass on the destination slots to set the slot flags,
473f0ea3689SLuigi Rizzo * using the right number of destination slots.
474f0ea3689SLuigi Rizzo */
47537e3a6d3SLuigi Rizzo while (j_start != j_cur) {
47637e3a6d3SLuigi Rizzo dst_slot = &dst_ring->slot[j_start];
47737e3a6d3SLuigi Rizzo dst_slot->flags = (dst_slots << 8)| NS_MOREFRAG;
478f0ea3689SLuigi Rizzo j_start = nm_next(j_start, lim);
479f0ea3689SLuigi Rizzo }
480f0ea3689SLuigi Rizzo /* Clear NS_MOREFRAG flag on last entry. */
48137e3a6d3SLuigi Rizzo dst_slot->flags = (dst_slots << 8);
482f0ea3689SLuigi Rizzo }
483f0ea3689SLuigi Rizzo
48437e3a6d3SLuigi Rizzo /* Update howmany and j. This is to commit the use of
48537e3a6d3SLuigi Rizzo * those slots in the destination ring. */
486f0ea3689SLuigi Rizzo if (unlikely(dst_slots > *howmany)) {
48775f4f3edSVincenzo Maffione nm_prerr("bug: slot allocation error");
488f0ea3689SLuigi Rizzo }
48937e3a6d3SLuigi Rizzo *j = j_cur;
490f0ea3689SLuigi Rizzo *howmany -= dst_slots;
491f0ea3689SLuigi Rizzo }
492