1dbddf429SAlex Dewar // SPDX-License-Identifier: GPL-2.0
249da7e64SAnton Ivanov /*
349da7e64SAnton Ivanov  * Copyright (C) 2017 - Cambridge Greys Limited
449da7e64SAnton Ivanov  * Copyright (C) 2011 - 2014 Cisco Systems Inc
549da7e64SAnton Ivanov  */
649da7e64SAnton Ivanov 
749da7e64SAnton Ivanov #include <linux/etherdevice.h>
849da7e64SAnton Ivanov #include <linux/netdevice.h>
949da7e64SAnton Ivanov #include <linux/skbuff.h>
1049da7e64SAnton Ivanov #include <linux/slab.h>
1149da7e64SAnton Ivanov #include <asm/byteorder.h>
1249da7e64SAnton Ivanov #include <uapi/linux/ip.h>
1349da7e64SAnton Ivanov #include <uapi/linux/virtio_net.h>
1449da7e64SAnton Ivanov #include <linux/virtio_net.h>
1549da7e64SAnton Ivanov #include <linux/virtio_byteorder.h>
1649da7e64SAnton Ivanov #include <linux/netdev_features.h>
1749da7e64SAnton Ivanov #include "vector_user.h"
1849da7e64SAnton Ivanov #include "vector_kern.h"
1949da7e64SAnton Ivanov 
2049da7e64SAnton Ivanov #define GOOD_LINEAR 512
2149da7e64SAnton Ivanov #define GSO_ERROR "Incoming GSO frames and GRO disabled on the interface"
2249da7e64SAnton Ivanov 
2349da7e64SAnton Ivanov struct gre_minimal_header {
2449da7e64SAnton Ivanov 	uint16_t header;
2549da7e64SAnton Ivanov 	uint16_t arptype;
2649da7e64SAnton Ivanov };
2749da7e64SAnton Ivanov 
2849da7e64SAnton Ivanov 
2949da7e64SAnton Ivanov struct uml_gre_data {
3049da7e64SAnton Ivanov 	uint32_t rx_key;
3149da7e64SAnton Ivanov 	uint32_t tx_key;
3249da7e64SAnton Ivanov 	uint32_t sequence;
3349da7e64SAnton Ivanov 
3449da7e64SAnton Ivanov 	bool ipv6;
3549da7e64SAnton Ivanov 	bool has_sequence;
3649da7e64SAnton Ivanov 	bool pin_sequence;
3749da7e64SAnton Ivanov 	bool checksum;
3849da7e64SAnton Ivanov 	bool key;
3949da7e64SAnton Ivanov 	struct gre_minimal_header expected_header;
4049da7e64SAnton Ivanov 
4149da7e64SAnton Ivanov 	uint32_t checksum_offset;
4249da7e64SAnton Ivanov 	uint32_t key_offset;
4349da7e64SAnton Ivanov 	uint32_t sequence_offset;
4449da7e64SAnton Ivanov 
4549da7e64SAnton Ivanov };
4649da7e64SAnton Ivanov 
4749da7e64SAnton Ivanov struct uml_l2tpv3_data {
4849da7e64SAnton Ivanov 	uint64_t rx_cookie;
4949da7e64SAnton Ivanov 	uint64_t tx_cookie;
5049da7e64SAnton Ivanov 	uint64_t rx_session;
5149da7e64SAnton Ivanov 	uint64_t tx_session;
5249da7e64SAnton Ivanov 	uint32_t counter;
5349da7e64SAnton Ivanov 
5449da7e64SAnton Ivanov 	bool udp;
5549da7e64SAnton Ivanov 	bool ipv6;
5649da7e64SAnton Ivanov 	bool has_counter;
5749da7e64SAnton Ivanov 	bool pin_counter;
5849da7e64SAnton Ivanov 	bool cookie;
5949da7e64SAnton Ivanov 	bool cookie_is_64;
6049da7e64SAnton Ivanov 
6149da7e64SAnton Ivanov 	uint32_t cookie_offset;
6249da7e64SAnton Ivanov 	uint32_t session_offset;
6349da7e64SAnton Ivanov 	uint32_t counter_offset;
6449da7e64SAnton Ivanov };
6549da7e64SAnton Ivanov 
l2tpv3_form_header(uint8_t * header,struct sk_buff * skb,struct vector_private * vp)6649da7e64SAnton Ivanov static int l2tpv3_form_header(uint8_t *header,
6749da7e64SAnton Ivanov 	struct sk_buff *skb, struct vector_private *vp)
6849da7e64SAnton Ivanov {
6949da7e64SAnton Ivanov 	struct uml_l2tpv3_data *td = vp->transport_data;
7049da7e64SAnton Ivanov 	uint32_t *counter;
7149da7e64SAnton Ivanov 
7249da7e64SAnton Ivanov 	if (td->udp)
7349da7e64SAnton Ivanov 		*(uint32_t *) header = cpu_to_be32(L2TPV3_DATA_PACKET);
7449da7e64SAnton Ivanov 	(*(uint32_t *) (header + td->session_offset)) = td->tx_session;
7549da7e64SAnton Ivanov 
7649da7e64SAnton Ivanov 	if (td->cookie) {
7749da7e64SAnton Ivanov 		if (td->cookie_is_64)
7849da7e64SAnton Ivanov 			(*(uint64_t *)(header + td->cookie_offset)) =
7949da7e64SAnton Ivanov 				td->tx_cookie;
8049da7e64SAnton Ivanov 		else
8149da7e64SAnton Ivanov 			(*(uint32_t *)(header + td->cookie_offset)) =
8249da7e64SAnton Ivanov 				td->tx_cookie;
8349da7e64SAnton Ivanov 	}
8449da7e64SAnton Ivanov 	if (td->has_counter) {
8549da7e64SAnton Ivanov 		counter = (uint32_t *)(header + td->counter_offset);
8649da7e64SAnton Ivanov 		if (td->pin_counter) {
8749da7e64SAnton Ivanov 			*counter = 0;
8849da7e64SAnton Ivanov 		} else {
8949da7e64SAnton Ivanov 			td->counter++;
9049da7e64SAnton Ivanov 			*counter = cpu_to_be32(td->counter);
9149da7e64SAnton Ivanov 		}
9249da7e64SAnton Ivanov 	}
9349da7e64SAnton Ivanov 	return 0;
9449da7e64SAnton Ivanov }
9549da7e64SAnton Ivanov 
gre_form_header(uint8_t * header,struct sk_buff * skb,struct vector_private * vp)9649da7e64SAnton Ivanov static int gre_form_header(uint8_t *header,
9749da7e64SAnton Ivanov 		struct sk_buff *skb, struct vector_private *vp)
9849da7e64SAnton Ivanov {
9949da7e64SAnton Ivanov 	struct uml_gre_data *td = vp->transport_data;
10049da7e64SAnton Ivanov 	uint32_t *sequence;
10149da7e64SAnton Ivanov 	*((uint32_t *) header) = *((uint32_t *) &td->expected_header);
10249da7e64SAnton Ivanov 	if (td->key)
10349da7e64SAnton Ivanov 		(*(uint32_t *) (header + td->key_offset)) = td->tx_key;
10449da7e64SAnton Ivanov 	if (td->has_sequence) {
10549da7e64SAnton Ivanov 		sequence = (uint32_t *)(header + td->sequence_offset);
10649da7e64SAnton Ivanov 		if (td->pin_sequence)
10749da7e64SAnton Ivanov 			*sequence = 0;
10849da7e64SAnton Ivanov 		else
10949da7e64SAnton Ivanov 			*sequence = cpu_to_be32(++td->sequence);
11049da7e64SAnton Ivanov 	}
11149da7e64SAnton Ivanov 	return 0;
11249da7e64SAnton Ivanov }
11349da7e64SAnton Ivanov 
raw_form_header(uint8_t * header,struct sk_buff * skb,struct vector_private * vp)11449da7e64SAnton Ivanov static int raw_form_header(uint8_t *header,
11549da7e64SAnton Ivanov 		struct sk_buff *skb, struct vector_private *vp)
11649da7e64SAnton Ivanov {
11749da7e64SAnton Ivanov 	struct virtio_net_hdr *vheader = (struct virtio_net_hdr *) header;
11849da7e64SAnton Ivanov 
11949da7e64SAnton Ivanov 	virtio_net_hdr_from_skb(
12049da7e64SAnton Ivanov 		skb,
12149da7e64SAnton Ivanov 		vheader,
12249da7e64SAnton Ivanov 		virtio_legacy_is_little_endian(),
123fd3a8862SWillem de Bruijn 		false,
124fd3a8862SWillem de Bruijn 		0
12549da7e64SAnton Ivanov 	);
12649da7e64SAnton Ivanov 
12749da7e64SAnton Ivanov 	return 0;
12849da7e64SAnton Ivanov }
12949da7e64SAnton Ivanov 
l2tpv3_verify_header(uint8_t * header,struct sk_buff * skb,struct vector_private * vp)13049da7e64SAnton Ivanov static int l2tpv3_verify_header(
13149da7e64SAnton Ivanov 	uint8_t *header, struct sk_buff *skb, struct vector_private *vp)
13249da7e64SAnton Ivanov {
13349da7e64SAnton Ivanov 	struct uml_l2tpv3_data *td = vp->transport_data;
13449da7e64SAnton Ivanov 	uint32_t *session;
13549da7e64SAnton Ivanov 	uint64_t cookie;
13649da7e64SAnton Ivanov 
13749da7e64SAnton Ivanov 	if ((!td->udp) && (!td->ipv6))
13849da7e64SAnton Ivanov 		header += sizeof(struct iphdr) /* fix for ipv4 raw */;
13949da7e64SAnton Ivanov 
14049da7e64SAnton Ivanov 	/* we do not do a strict check for "data" packets as per
14149da7e64SAnton Ivanov 	 * the RFC spec because the pure IP spec does not have
14249da7e64SAnton Ivanov 	 * that anyway.
14349da7e64SAnton Ivanov 	 */
14449da7e64SAnton Ivanov 
14549da7e64SAnton Ivanov 	if (td->cookie) {
14649da7e64SAnton Ivanov 		if (td->cookie_is_64)
14749da7e64SAnton Ivanov 			cookie = *(uint64_t *)(header + td->cookie_offset);
14849da7e64SAnton Ivanov 		else
14949da7e64SAnton Ivanov 			cookie = *(uint32_t *)(header + td->cookie_offset);
15049da7e64SAnton Ivanov 		if (cookie != td->rx_cookie) {
15149da7e64SAnton Ivanov 			if (net_ratelimit())
15249da7e64SAnton Ivanov 				netdev_err(vp->dev, "uml_l2tpv3: unknown cookie id");
15349da7e64SAnton Ivanov 			return -1;
15449da7e64SAnton Ivanov 		}
15549da7e64SAnton Ivanov 	}
15649da7e64SAnton Ivanov 	session = (uint32_t *) (header + td->session_offset);
15749da7e64SAnton Ivanov 	if (*session != td->rx_session) {
15849da7e64SAnton Ivanov 		if (net_ratelimit())
15949da7e64SAnton Ivanov 			netdev_err(vp->dev, "uml_l2tpv3: session mismatch");
16049da7e64SAnton Ivanov 		return -1;
16149da7e64SAnton Ivanov 	}
16249da7e64SAnton Ivanov 	return 0;
16349da7e64SAnton Ivanov }
16449da7e64SAnton Ivanov 
gre_verify_header(uint8_t * header,struct sk_buff * skb,struct vector_private * vp)16549da7e64SAnton Ivanov static int gre_verify_header(
16649da7e64SAnton Ivanov 	uint8_t *header, struct sk_buff *skb, struct vector_private *vp)
16749da7e64SAnton Ivanov {
16849da7e64SAnton Ivanov 
16949da7e64SAnton Ivanov 	uint32_t key;
17049da7e64SAnton Ivanov 	struct uml_gre_data *td = vp->transport_data;
17149da7e64SAnton Ivanov 
17249da7e64SAnton Ivanov 	if (!td->ipv6)
17349da7e64SAnton Ivanov 		header += sizeof(struct iphdr) /* fix for ipv4 raw */;
17449da7e64SAnton Ivanov 
17549da7e64SAnton Ivanov 	if (*((uint32_t *) header) != *((uint32_t *) &td->expected_header)) {
17649da7e64SAnton Ivanov 		if (net_ratelimit())
17749da7e64SAnton Ivanov 			netdev_err(vp->dev, "header type disagreement, expecting %0x, got %0x",
17849da7e64SAnton Ivanov 				*((uint32_t *) &td->expected_header),
17949da7e64SAnton Ivanov 				*((uint32_t *) header)
18049da7e64SAnton Ivanov 			);
18149da7e64SAnton Ivanov 		return -1;
18249da7e64SAnton Ivanov 	}
18349da7e64SAnton Ivanov 
18449da7e64SAnton Ivanov 	if (td->key) {
18549da7e64SAnton Ivanov 		key = (*(uint32_t *)(header + td->key_offset));
18649da7e64SAnton Ivanov 		if (key != td->rx_key) {
18749da7e64SAnton Ivanov 			if (net_ratelimit())
18849da7e64SAnton Ivanov 				netdev_err(vp->dev, "unknown key id %0x, expecting %0x",
18949da7e64SAnton Ivanov 						key, td->rx_key);
19049da7e64SAnton Ivanov 			return -1;
19149da7e64SAnton Ivanov 		}
19249da7e64SAnton Ivanov 	}
19349da7e64SAnton Ivanov 	return 0;
19449da7e64SAnton Ivanov }
19549da7e64SAnton Ivanov 
raw_verify_header(uint8_t * header,struct sk_buff * skb,struct vector_private * vp)19649da7e64SAnton Ivanov static int raw_verify_header(
19749da7e64SAnton Ivanov 	uint8_t *header, struct sk_buff *skb, struct vector_private *vp)
19849da7e64SAnton Ivanov {
19949da7e64SAnton Ivanov 	struct virtio_net_hdr *vheader = (struct virtio_net_hdr *) header;
20049da7e64SAnton Ivanov 
20149da7e64SAnton Ivanov 	if ((vheader->gso_type != VIRTIO_NET_HDR_GSO_NONE) &&
20249da7e64SAnton Ivanov 		(vp->req_size != 65536)) {
20349da7e64SAnton Ivanov 		if (net_ratelimit())
20449da7e64SAnton Ivanov 			netdev_err(
20549da7e64SAnton Ivanov 				vp->dev,
20649da7e64SAnton Ivanov 				GSO_ERROR
20749da7e64SAnton Ivanov 		);
20849da7e64SAnton Ivanov 	}
20949da7e64SAnton Ivanov 	if ((vheader->flags & VIRTIO_NET_HDR_F_DATA_VALID) > 0)
21049da7e64SAnton Ivanov 		return 1;
21149da7e64SAnton Ivanov 
21249da7e64SAnton Ivanov 	virtio_net_hdr_to_skb(skb, vheader, virtio_legacy_is_little_endian());
21349da7e64SAnton Ivanov 	return 0;
21449da7e64SAnton Ivanov }
21549da7e64SAnton Ivanov 
get_uint_param(struct arglist * def,char * param,unsigned int * result)21649da7e64SAnton Ivanov static bool get_uint_param(
21749da7e64SAnton Ivanov 	struct arglist *def, char *param, unsigned int *result)
21849da7e64SAnton Ivanov {
21949da7e64SAnton Ivanov 	char *arg = uml_vector_fetch_arg(def, param);
22049da7e64SAnton Ivanov 
22149da7e64SAnton Ivanov 	if (arg != NULL) {
22249da7e64SAnton Ivanov 		if (kstrtoint(arg, 0, result) == 0)
22349da7e64SAnton Ivanov 			return true;
22449da7e64SAnton Ivanov 	}
22549da7e64SAnton Ivanov 	return false;
22649da7e64SAnton Ivanov }
22749da7e64SAnton Ivanov 
get_ulong_param(struct arglist * def,char * param,unsigned long * result)22849da7e64SAnton Ivanov static bool get_ulong_param(
22949da7e64SAnton Ivanov 	struct arglist *def, char *param, unsigned long *result)
23049da7e64SAnton Ivanov {
23149da7e64SAnton Ivanov 	char *arg = uml_vector_fetch_arg(def, param);
23249da7e64SAnton Ivanov 
23349da7e64SAnton Ivanov 	if (arg != NULL) {
23449da7e64SAnton Ivanov 		if (kstrtoul(arg, 0, result) == 0)
23549da7e64SAnton Ivanov 			return true;
23649da7e64SAnton Ivanov 		return true;
23749da7e64SAnton Ivanov 	}
23849da7e64SAnton Ivanov 	return false;
23949da7e64SAnton Ivanov }
24049da7e64SAnton Ivanov 
build_gre_transport_data(struct vector_private * vp)24149da7e64SAnton Ivanov static int build_gre_transport_data(struct vector_private *vp)
24249da7e64SAnton Ivanov {
24349da7e64SAnton Ivanov 	struct uml_gre_data *td;
24449da7e64SAnton Ivanov 	int temp_int;
24549da7e64SAnton Ivanov 	int temp_rx;
24649da7e64SAnton Ivanov 	int temp_tx;
24749da7e64SAnton Ivanov 
24849da7e64SAnton Ivanov 	vp->transport_data = kmalloc(sizeof(struct uml_gre_data), GFP_KERNEL);
24949da7e64SAnton Ivanov 	if (vp->transport_data == NULL)
25049da7e64SAnton Ivanov 		return -ENOMEM;
25149da7e64SAnton Ivanov 	td = vp->transport_data;
25249da7e64SAnton Ivanov 	td->sequence = 0;
25349da7e64SAnton Ivanov 
25449da7e64SAnton Ivanov 	td->expected_header.arptype = GRE_IRB;
25549da7e64SAnton Ivanov 	td->expected_header.header = 0;
25649da7e64SAnton Ivanov 
25749da7e64SAnton Ivanov 	vp->form_header = &gre_form_header;
25849da7e64SAnton Ivanov 	vp->verify_header = &gre_verify_header;
25949da7e64SAnton Ivanov 	vp->header_size = 4;
26049da7e64SAnton Ivanov 	td->key_offset = 4;
26149da7e64SAnton Ivanov 	td->sequence_offset = 4;
26249da7e64SAnton Ivanov 	td->checksum_offset = 4;
26349da7e64SAnton Ivanov 
26449da7e64SAnton Ivanov 	td->ipv6 = false;
26549da7e64SAnton Ivanov 	if (get_uint_param(vp->parsed, "v6", &temp_int)) {
26649da7e64SAnton Ivanov 		if (temp_int > 0)
26749da7e64SAnton Ivanov 			td->ipv6 = true;
26849da7e64SAnton Ivanov 	}
26949da7e64SAnton Ivanov 	td->key = false;
27049da7e64SAnton Ivanov 	if (get_uint_param(vp->parsed, "rx_key", &temp_rx)) {
27149da7e64SAnton Ivanov 		if (get_uint_param(vp->parsed, "tx_key", &temp_tx)) {
27249da7e64SAnton Ivanov 			td->key = true;
27349da7e64SAnton Ivanov 			td->expected_header.header |= GRE_MODE_KEY;
27449da7e64SAnton Ivanov 			td->rx_key = cpu_to_be32(temp_rx);
27549da7e64SAnton Ivanov 			td->tx_key = cpu_to_be32(temp_tx);
27649da7e64SAnton Ivanov 			vp->header_size += 4;
27749da7e64SAnton Ivanov 			td->sequence_offset += 4;
27849da7e64SAnton Ivanov 		} else {
27949da7e64SAnton Ivanov 			return -EINVAL;
28049da7e64SAnton Ivanov 		}
28149da7e64SAnton Ivanov 	}
28249da7e64SAnton Ivanov 
28349da7e64SAnton Ivanov 	td->sequence = false;
28449da7e64SAnton Ivanov 	if (get_uint_param(vp->parsed, "sequence", &temp_int)) {
28549da7e64SAnton Ivanov 		if (temp_int > 0) {
28649da7e64SAnton Ivanov 			vp->header_size += 4;
28749da7e64SAnton Ivanov 			td->has_sequence = true;
28849da7e64SAnton Ivanov 			td->expected_header.header |= GRE_MODE_SEQUENCE;
28949da7e64SAnton Ivanov 			if (get_uint_param(
29049da7e64SAnton Ivanov 				vp->parsed, "pin_sequence", &temp_int)) {
29149da7e64SAnton Ivanov 				if (temp_int > 0)
29249da7e64SAnton Ivanov 					td->pin_sequence = true;
29349da7e64SAnton Ivanov 			}
29449da7e64SAnton Ivanov 		}
29549da7e64SAnton Ivanov 	}
29649da7e64SAnton Ivanov 	vp->rx_header_size = vp->header_size;
29749da7e64SAnton Ivanov 	if (!td->ipv6)
29849da7e64SAnton Ivanov 		vp->rx_header_size += sizeof(struct iphdr);
29949da7e64SAnton Ivanov 	return 0;
30049da7e64SAnton Ivanov }
30149da7e64SAnton Ivanov 
build_l2tpv3_transport_data(struct vector_private * vp)30249da7e64SAnton Ivanov static int build_l2tpv3_transport_data(struct vector_private *vp)
30349da7e64SAnton Ivanov {
30449da7e64SAnton Ivanov 
30549da7e64SAnton Ivanov 	struct uml_l2tpv3_data *td;
30649da7e64SAnton Ivanov 	int temp_int, temp_rxs, temp_txs;
30749da7e64SAnton Ivanov 	unsigned long temp_rx;
30849da7e64SAnton Ivanov 	unsigned long temp_tx;
30949da7e64SAnton Ivanov 
31049da7e64SAnton Ivanov 	vp->transport_data = kmalloc(
31149da7e64SAnton Ivanov 		sizeof(struct uml_l2tpv3_data), GFP_KERNEL);
31249da7e64SAnton Ivanov 
31349da7e64SAnton Ivanov 	if (vp->transport_data == NULL)
31449da7e64SAnton Ivanov 		return -ENOMEM;
31549da7e64SAnton Ivanov 
31649da7e64SAnton Ivanov 	td = vp->transport_data;
31749da7e64SAnton Ivanov 
31849da7e64SAnton Ivanov 	vp->form_header = &l2tpv3_form_header;
31949da7e64SAnton Ivanov 	vp->verify_header = &l2tpv3_verify_header;
32049da7e64SAnton Ivanov 	td->counter = 0;
32149da7e64SAnton Ivanov 
32249da7e64SAnton Ivanov 	vp->header_size = 4;
32349da7e64SAnton Ivanov 	td->session_offset = 0;
32449da7e64SAnton Ivanov 	td->cookie_offset = 4;
32549da7e64SAnton Ivanov 	td->counter_offset = 4;
32649da7e64SAnton Ivanov 
32749da7e64SAnton Ivanov 
32849da7e64SAnton Ivanov 	td->ipv6 = false;
32949da7e64SAnton Ivanov 	if (get_uint_param(vp->parsed, "v6", &temp_int)) {
33049da7e64SAnton Ivanov 		if (temp_int > 0)
33149da7e64SAnton Ivanov 			td->ipv6 = true;
33249da7e64SAnton Ivanov 	}
33349da7e64SAnton Ivanov 
33449da7e64SAnton Ivanov 	if (get_uint_param(vp->parsed, "rx_session", &temp_rxs)) {
33549da7e64SAnton Ivanov 		if (get_uint_param(vp->parsed, "tx_session", &temp_txs)) {
33649da7e64SAnton Ivanov 			td->tx_session = cpu_to_be32(temp_txs);
33749da7e64SAnton Ivanov 			td->rx_session = cpu_to_be32(temp_rxs);
33849da7e64SAnton Ivanov 		} else {
33949da7e64SAnton Ivanov 			return -EINVAL;
34049da7e64SAnton Ivanov 		}
34149da7e64SAnton Ivanov 	} else {
34249da7e64SAnton Ivanov 		return -EINVAL;
34349da7e64SAnton Ivanov 	}
34449da7e64SAnton Ivanov 
34549da7e64SAnton Ivanov 	td->cookie_is_64  = false;
34649da7e64SAnton Ivanov 	if (get_uint_param(vp->parsed, "cookie64", &temp_int)) {
34749da7e64SAnton Ivanov 		if (temp_int > 0)
34849da7e64SAnton Ivanov 			td->cookie_is_64  = true;
34949da7e64SAnton Ivanov 	}
35049da7e64SAnton Ivanov 	td->cookie = false;
35149da7e64SAnton Ivanov 	if (get_ulong_param(vp->parsed, "rx_cookie", &temp_rx)) {
35249da7e64SAnton Ivanov 		if (get_ulong_param(vp->parsed, "tx_cookie", &temp_tx)) {
35349da7e64SAnton Ivanov 			td->cookie = true;
35449da7e64SAnton Ivanov 			if (td->cookie_is_64) {
35549da7e64SAnton Ivanov 				td->rx_cookie = cpu_to_be64(temp_rx);
35649da7e64SAnton Ivanov 				td->tx_cookie = cpu_to_be64(temp_tx);
35749da7e64SAnton Ivanov 				vp->header_size += 8;
35849da7e64SAnton Ivanov 				td->counter_offset += 8;
35949da7e64SAnton Ivanov 			} else {
36049da7e64SAnton Ivanov 				td->rx_cookie = cpu_to_be32(temp_rx);
36149da7e64SAnton Ivanov 				td->tx_cookie = cpu_to_be32(temp_tx);
36249da7e64SAnton Ivanov 				vp->header_size += 4;
36349da7e64SAnton Ivanov 				td->counter_offset += 4;
36449da7e64SAnton Ivanov 			}
36549da7e64SAnton Ivanov 		} else {
36649da7e64SAnton Ivanov 			return -EINVAL;
36749da7e64SAnton Ivanov 		}
36849da7e64SAnton Ivanov 	}
36949da7e64SAnton Ivanov 
37049da7e64SAnton Ivanov 	td->has_counter = false;
37149da7e64SAnton Ivanov 	if (get_uint_param(vp->parsed, "counter", &temp_int)) {
37249da7e64SAnton Ivanov 		if (temp_int > 0) {
37349da7e64SAnton Ivanov 			td->has_counter = true;
37449da7e64SAnton Ivanov 			vp->header_size += 4;
37549da7e64SAnton Ivanov 			if (get_uint_param(
37649da7e64SAnton Ivanov 				vp->parsed, "pin_counter", &temp_int)) {
37749da7e64SAnton Ivanov 				if (temp_int > 0)
37849da7e64SAnton Ivanov 					td->pin_counter = true;
37949da7e64SAnton Ivanov 			}
38049da7e64SAnton Ivanov 		}
38149da7e64SAnton Ivanov 	}
38249da7e64SAnton Ivanov 
38349da7e64SAnton Ivanov 	if (get_uint_param(vp->parsed, "udp", &temp_int)) {
38449da7e64SAnton Ivanov 		if (temp_int > 0) {
38549da7e64SAnton Ivanov 			td->udp = true;
38649da7e64SAnton Ivanov 			vp->header_size += 4;
38749da7e64SAnton Ivanov 			td->counter_offset += 4;
38849da7e64SAnton Ivanov 			td->session_offset += 4;
38949da7e64SAnton Ivanov 			td->cookie_offset += 4;
39049da7e64SAnton Ivanov 		}
39149da7e64SAnton Ivanov 	}
39249da7e64SAnton Ivanov 
39349da7e64SAnton Ivanov 	vp->rx_header_size = vp->header_size;
39449da7e64SAnton Ivanov 	if ((!td->ipv6) && (!td->udp))
39549da7e64SAnton Ivanov 		vp->rx_header_size += sizeof(struct iphdr);
39649da7e64SAnton Ivanov 
39749da7e64SAnton Ivanov 	return 0;
39849da7e64SAnton Ivanov }
39949da7e64SAnton Ivanov 
build_raw_transport_data(struct vector_private * vp)40049da7e64SAnton Ivanov static int build_raw_transport_data(struct vector_private *vp)
40149da7e64SAnton Ivanov {
40249da7e64SAnton Ivanov 	if (uml_raw_enable_vnet_headers(vp->fds->rx_fd)) {
40349da7e64SAnton Ivanov 		if (!uml_raw_enable_vnet_headers(vp->fds->tx_fd))
40449da7e64SAnton Ivanov 			return -1;
40549da7e64SAnton Ivanov 		vp->form_header = &raw_form_header;
40649da7e64SAnton Ivanov 		vp->verify_header = &raw_verify_header;
40749da7e64SAnton Ivanov 		vp->header_size = sizeof(struct virtio_net_hdr);
40849da7e64SAnton Ivanov 		vp->rx_header_size = sizeof(struct virtio_net_hdr);
40949da7e64SAnton Ivanov 		vp->dev->hw_features |= (NETIF_F_TSO | NETIF_F_GRO);
41049da7e64SAnton Ivanov 		vp->dev->features |=
41149da7e64SAnton Ivanov 			(NETIF_F_RXCSUM | NETIF_F_HW_CSUM |
41249da7e64SAnton Ivanov 				NETIF_F_TSO | NETIF_F_GRO);
41349da7e64SAnton Ivanov 		netdev_info(
41449da7e64SAnton Ivanov 			vp->dev,
41549da7e64SAnton Ivanov 			"raw: using vnet headers for tso and tx/rx checksum"
41649da7e64SAnton Ivanov 		);
41749da7e64SAnton Ivanov 	}
41849da7e64SAnton Ivanov 	return 0;
41949da7e64SAnton Ivanov }
42049da7e64SAnton Ivanov 
build_hybrid_transport_data(struct vector_private * vp)421b3b8ca2aSAnton Ivanov static int build_hybrid_transport_data(struct vector_private *vp)
42249da7e64SAnton Ivanov {
42349da7e64SAnton Ivanov 	if (uml_raw_enable_vnet_headers(vp->fds->rx_fd)) {
42449da7e64SAnton Ivanov 		vp->form_header = &raw_form_header;
42549da7e64SAnton Ivanov 		vp->verify_header = &raw_verify_header;
42649da7e64SAnton Ivanov 		vp->header_size = sizeof(struct virtio_net_hdr);
42749da7e64SAnton Ivanov 		vp->rx_header_size = sizeof(struct virtio_net_hdr);
42849da7e64SAnton Ivanov 		vp->dev->hw_features |=
42949da7e64SAnton Ivanov 			(NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GRO);
43049da7e64SAnton Ivanov 		vp->dev->features |=
43149da7e64SAnton Ivanov 			(NETIF_F_RXCSUM | NETIF_F_HW_CSUM |
43249da7e64SAnton Ivanov 				NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GRO);
43349da7e64SAnton Ivanov 		netdev_info(
43449da7e64SAnton Ivanov 			vp->dev,
435b3b8ca2aSAnton Ivanov 			"tap/raw hybrid: using vnet headers for tso and tx/rx checksum"
43649da7e64SAnton Ivanov 		);
43749da7e64SAnton Ivanov 	} else {
43849da7e64SAnton Ivanov 		return 0; /* do not try to enable tap too if raw failed */
43949da7e64SAnton Ivanov 	}
44049da7e64SAnton Ivanov 	if (uml_tap_enable_vnet_headers(vp->fds->tx_fd))
44149da7e64SAnton Ivanov 		return 0;
44249da7e64SAnton Ivanov 	return -1;
44349da7e64SAnton Ivanov }
44449da7e64SAnton Ivanov 
build_tap_transport_data(struct vector_private * vp)445b3b8ca2aSAnton Ivanov static int build_tap_transport_data(struct vector_private *vp)
446b3b8ca2aSAnton Ivanov {
447b3b8ca2aSAnton Ivanov 	/* "Pure" tap uses the same fd for rx and tx */
448b3b8ca2aSAnton Ivanov 	if (uml_tap_enable_vnet_headers(vp->fds->tx_fd)) {
449b3b8ca2aSAnton Ivanov 		vp->form_header = &raw_form_header;
450b3b8ca2aSAnton Ivanov 		vp->verify_header = &raw_verify_header;
451b3b8ca2aSAnton Ivanov 		vp->header_size = sizeof(struct virtio_net_hdr);
452b3b8ca2aSAnton Ivanov 		vp->rx_header_size = sizeof(struct virtio_net_hdr);
453b3b8ca2aSAnton Ivanov 		vp->dev->hw_features |=
454b3b8ca2aSAnton Ivanov 			(NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GRO);
455b3b8ca2aSAnton Ivanov 		vp->dev->features |=
456b3b8ca2aSAnton Ivanov 			(NETIF_F_RXCSUM | NETIF_F_HW_CSUM |
457b3b8ca2aSAnton Ivanov 				NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GRO);
458b3b8ca2aSAnton Ivanov 		netdev_info(
459b3b8ca2aSAnton Ivanov 			vp->dev,
460b3b8ca2aSAnton Ivanov 			"tap: using vnet headers for tso and tx/rx checksum"
461b3b8ca2aSAnton Ivanov 		);
462b3b8ca2aSAnton Ivanov 		return 0;
463b3b8ca2aSAnton Ivanov 	}
464b3b8ca2aSAnton Ivanov 	return -1;
465b3b8ca2aSAnton Ivanov }
466b3b8ca2aSAnton Ivanov 
467b3b8ca2aSAnton Ivanov 
build_bess_transport_data(struct vector_private * vp)46877f1073cSAnton Ivanov static int build_bess_transport_data(struct vector_private *vp)
46977f1073cSAnton Ivanov {
47077f1073cSAnton Ivanov 	vp->form_header = NULL;
47177f1073cSAnton Ivanov 	vp->verify_header = NULL;
47277f1073cSAnton Ivanov 	vp->header_size = 0;
47377f1073cSAnton Ivanov 	vp->rx_header_size = 0;
47477f1073cSAnton Ivanov 	return 0;
47577f1073cSAnton Ivanov }
47677f1073cSAnton Ivanov 
build_transport_data(struct vector_private * vp)47749da7e64SAnton Ivanov int build_transport_data(struct vector_private *vp)
47849da7e64SAnton Ivanov {
47949da7e64SAnton Ivanov 	char *transport = uml_vector_fetch_arg(vp->parsed, "transport");
48049da7e64SAnton Ivanov 
48149da7e64SAnton Ivanov 	if (strncmp(transport, TRANS_GRE, TRANS_GRE_LEN) == 0)
48249da7e64SAnton Ivanov 		return build_gre_transport_data(vp);
48349da7e64SAnton Ivanov 	if (strncmp(transport, TRANS_L2TPV3, TRANS_L2TPV3_LEN) == 0)
48449da7e64SAnton Ivanov 		return build_l2tpv3_transport_data(vp);
48549da7e64SAnton Ivanov 	if (strncmp(transport, TRANS_RAW, TRANS_RAW_LEN) == 0)
48649da7e64SAnton Ivanov 		return build_raw_transport_data(vp);
48749da7e64SAnton Ivanov 	if (strncmp(transport, TRANS_TAP, TRANS_TAP_LEN) == 0)
48849da7e64SAnton Ivanov 		return build_tap_transport_data(vp);
489b3b8ca2aSAnton Ivanov 	if (strncmp(transport, TRANS_HYBRID, TRANS_HYBRID_LEN) == 0)
490b3b8ca2aSAnton Ivanov 		return build_hybrid_transport_data(vp);
49177f1073cSAnton Ivanov 	if (strncmp(transport, TRANS_BESS, TRANS_BESS_LEN) == 0)
49277f1073cSAnton Ivanov 		return build_bess_transport_data(vp);
49349da7e64SAnton Ivanov 	return 0;
49449da7e64SAnton Ivanov }
49549da7e64SAnton Ivanov 
496