xref: /OK3568_Linux_fs/kernel/include/linux/virtio_net.h (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /* SPDX-License-Identifier: GPL-2.0 */
2*4882a593Smuzhiyun #ifndef _LINUX_VIRTIO_NET_H
3*4882a593Smuzhiyun #define _LINUX_VIRTIO_NET_H
4*4882a593Smuzhiyun 
5*4882a593Smuzhiyun #include <linux/if_vlan.h>
6*4882a593Smuzhiyun #include <uapi/linux/tcp.h>
7*4882a593Smuzhiyun #include <uapi/linux/udp.h>
8*4882a593Smuzhiyun #include <uapi/linux/virtio_net.h>
9*4882a593Smuzhiyun 
virtio_net_hdr_match_proto(__be16 protocol,__u8 gso_type)10*4882a593Smuzhiyun static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type)
11*4882a593Smuzhiyun {
12*4882a593Smuzhiyun 	switch (gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
13*4882a593Smuzhiyun 	case VIRTIO_NET_HDR_GSO_TCPV4:
14*4882a593Smuzhiyun 		return protocol == cpu_to_be16(ETH_P_IP);
15*4882a593Smuzhiyun 	case VIRTIO_NET_HDR_GSO_TCPV6:
16*4882a593Smuzhiyun 		return protocol == cpu_to_be16(ETH_P_IPV6);
17*4882a593Smuzhiyun 	case VIRTIO_NET_HDR_GSO_UDP:
18*4882a593Smuzhiyun 		return protocol == cpu_to_be16(ETH_P_IP) ||
19*4882a593Smuzhiyun 		       protocol == cpu_to_be16(ETH_P_IPV6);
20*4882a593Smuzhiyun 	default:
21*4882a593Smuzhiyun 		return false;
22*4882a593Smuzhiyun 	}
23*4882a593Smuzhiyun }
24*4882a593Smuzhiyun 
virtio_net_hdr_set_proto(struct sk_buff * skb,const struct virtio_net_hdr * hdr)25*4882a593Smuzhiyun static inline int virtio_net_hdr_set_proto(struct sk_buff *skb,
26*4882a593Smuzhiyun 					   const struct virtio_net_hdr *hdr)
27*4882a593Smuzhiyun {
28*4882a593Smuzhiyun 	if (skb->protocol)
29*4882a593Smuzhiyun 		return 0;
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun 	switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
32*4882a593Smuzhiyun 	case VIRTIO_NET_HDR_GSO_TCPV4:
33*4882a593Smuzhiyun 	case VIRTIO_NET_HDR_GSO_UDP:
34*4882a593Smuzhiyun 		skb->protocol = cpu_to_be16(ETH_P_IP);
35*4882a593Smuzhiyun 		break;
36*4882a593Smuzhiyun 	case VIRTIO_NET_HDR_GSO_TCPV6:
37*4882a593Smuzhiyun 		skb->protocol = cpu_to_be16(ETH_P_IPV6);
38*4882a593Smuzhiyun 		break;
39*4882a593Smuzhiyun 	default:
40*4882a593Smuzhiyun 		return -EINVAL;
41*4882a593Smuzhiyun 	}
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun 	return 0;
44*4882a593Smuzhiyun }
45*4882a593Smuzhiyun 
virtio_net_hdr_to_skb(struct sk_buff * skb,const struct virtio_net_hdr * hdr,bool little_endian)46*4882a593Smuzhiyun static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
47*4882a593Smuzhiyun 					const struct virtio_net_hdr *hdr,
48*4882a593Smuzhiyun 					bool little_endian)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun 	unsigned int gso_type = 0;
51*4882a593Smuzhiyun 	unsigned int thlen = 0;
52*4882a593Smuzhiyun 	unsigned int p_off = 0;
53*4882a593Smuzhiyun 	unsigned int ip_proto;
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
56*4882a593Smuzhiyun 		switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
57*4882a593Smuzhiyun 		case VIRTIO_NET_HDR_GSO_TCPV4:
58*4882a593Smuzhiyun 			gso_type = SKB_GSO_TCPV4;
59*4882a593Smuzhiyun 			ip_proto = IPPROTO_TCP;
60*4882a593Smuzhiyun 			thlen = sizeof(struct tcphdr);
61*4882a593Smuzhiyun 			break;
62*4882a593Smuzhiyun 		case VIRTIO_NET_HDR_GSO_TCPV6:
63*4882a593Smuzhiyun 			gso_type = SKB_GSO_TCPV6;
64*4882a593Smuzhiyun 			ip_proto = IPPROTO_TCP;
65*4882a593Smuzhiyun 			thlen = sizeof(struct tcphdr);
66*4882a593Smuzhiyun 			break;
67*4882a593Smuzhiyun 		case VIRTIO_NET_HDR_GSO_UDP:
68*4882a593Smuzhiyun 			gso_type = SKB_GSO_UDP;
69*4882a593Smuzhiyun 			ip_proto = IPPROTO_UDP;
70*4882a593Smuzhiyun 			thlen = sizeof(struct udphdr);
71*4882a593Smuzhiyun 			break;
72*4882a593Smuzhiyun 		default:
73*4882a593Smuzhiyun 			return -EINVAL;
74*4882a593Smuzhiyun 		}
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 		if (hdr->gso_type & VIRTIO_NET_HDR_GSO_ECN)
77*4882a593Smuzhiyun 			gso_type |= SKB_GSO_TCP_ECN;
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 		if (hdr->gso_size == 0)
80*4882a593Smuzhiyun 			return -EINVAL;
81*4882a593Smuzhiyun 	}
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	skb_reset_mac_header(skb);
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
86*4882a593Smuzhiyun 		u32 start = __virtio16_to_cpu(little_endian, hdr->csum_start);
87*4882a593Smuzhiyun 		u32 off = __virtio16_to_cpu(little_endian, hdr->csum_offset);
88*4882a593Smuzhiyun 		u32 needed = start + max_t(u32, thlen, off + sizeof(__sum16));
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 		if (!pskb_may_pull(skb, needed))
91*4882a593Smuzhiyun 			return -EINVAL;
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun 		if (!skb_partial_csum_set(skb, start, off))
94*4882a593Smuzhiyun 			return -EINVAL;
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 		p_off = skb_transport_offset(skb) + thlen;
97*4882a593Smuzhiyun 		if (!pskb_may_pull(skb, p_off))
98*4882a593Smuzhiyun 			return -EINVAL;
99*4882a593Smuzhiyun 	} else {
100*4882a593Smuzhiyun 		/* gso packets without NEEDS_CSUM do not set transport_offset.
101*4882a593Smuzhiyun 		 * probe and drop if does not match one of the above types.
102*4882a593Smuzhiyun 		 */
103*4882a593Smuzhiyun 		if (gso_type && skb->network_header) {
104*4882a593Smuzhiyun 			struct flow_keys_basic keys;
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 			if (!skb->protocol) {
107*4882a593Smuzhiyun 				__be16 protocol = dev_parse_header_protocol(skb);
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 				if (!protocol)
110*4882a593Smuzhiyun 					virtio_net_hdr_set_proto(skb, hdr);
111*4882a593Smuzhiyun 				else if (!virtio_net_hdr_match_proto(protocol, hdr->gso_type))
112*4882a593Smuzhiyun 					return -EINVAL;
113*4882a593Smuzhiyun 				else
114*4882a593Smuzhiyun 					skb->protocol = protocol;
115*4882a593Smuzhiyun 			}
116*4882a593Smuzhiyun retry:
117*4882a593Smuzhiyun 			if (!skb_flow_dissect_flow_keys_basic(NULL, skb, &keys,
118*4882a593Smuzhiyun 							      NULL, 0, 0, 0,
119*4882a593Smuzhiyun 							      0)) {
120*4882a593Smuzhiyun 				/* UFO does not specify ipv4 or 6: try both */
121*4882a593Smuzhiyun 				if (gso_type & SKB_GSO_UDP &&
122*4882a593Smuzhiyun 				    skb->protocol == htons(ETH_P_IP)) {
123*4882a593Smuzhiyun 					skb->protocol = htons(ETH_P_IPV6);
124*4882a593Smuzhiyun 					goto retry;
125*4882a593Smuzhiyun 				}
126*4882a593Smuzhiyun 				return -EINVAL;
127*4882a593Smuzhiyun 			}
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 			p_off = keys.control.thoff + thlen;
130*4882a593Smuzhiyun 			if (!pskb_may_pull(skb, p_off) ||
131*4882a593Smuzhiyun 			    keys.basic.ip_proto != ip_proto)
132*4882a593Smuzhiyun 				return -EINVAL;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 			skb_set_transport_header(skb, keys.control.thoff);
135*4882a593Smuzhiyun 		} else if (gso_type) {
136*4882a593Smuzhiyun 			p_off = thlen;
137*4882a593Smuzhiyun 			if (!pskb_may_pull(skb, p_off))
138*4882a593Smuzhiyun 				return -EINVAL;
139*4882a593Smuzhiyun 		}
140*4882a593Smuzhiyun 	}
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
143*4882a593Smuzhiyun 		u16 gso_size = __virtio16_to_cpu(little_endian, hdr->gso_size);
144*4882a593Smuzhiyun 		unsigned int nh_off = p_off;
145*4882a593Smuzhiyun 		struct skb_shared_info *shinfo = skb_shinfo(skb);
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 		/* UFO may not include transport header in gso_size. */
148*4882a593Smuzhiyun 		if (gso_type & SKB_GSO_UDP)
149*4882a593Smuzhiyun 			nh_off -= thlen;
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 		/* Too small packets are not really GSO ones. */
152*4882a593Smuzhiyun 		if (skb->len - nh_off > gso_size) {
153*4882a593Smuzhiyun 			shinfo->gso_size = gso_size;
154*4882a593Smuzhiyun 			shinfo->gso_type = gso_type;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 			/* Header must be checked, and gso_segs computed. */
157*4882a593Smuzhiyun 			shinfo->gso_type |= SKB_GSO_DODGY;
158*4882a593Smuzhiyun 			shinfo->gso_segs = 0;
159*4882a593Smuzhiyun 		}
160*4882a593Smuzhiyun 	}
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	return 0;
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun 
virtio_net_hdr_from_skb(const struct sk_buff * skb,struct virtio_net_hdr * hdr,bool little_endian,bool has_data_valid,int vlan_hlen)165*4882a593Smuzhiyun static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb,
166*4882a593Smuzhiyun 					  struct virtio_net_hdr *hdr,
167*4882a593Smuzhiyun 					  bool little_endian,
168*4882a593Smuzhiyun 					  bool has_data_valid,
169*4882a593Smuzhiyun 					  int vlan_hlen)
170*4882a593Smuzhiyun {
171*4882a593Smuzhiyun 	memset(hdr, 0, sizeof(*hdr));   /* no info leak */
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	if (skb_is_gso(skb)) {
174*4882a593Smuzhiyun 		struct skb_shared_info *sinfo = skb_shinfo(skb);
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 		/* This is a hint as to how much should be linear. */
177*4882a593Smuzhiyun 		hdr->hdr_len = __cpu_to_virtio16(little_endian,
178*4882a593Smuzhiyun 						 skb_headlen(skb));
179*4882a593Smuzhiyun 		hdr->gso_size = __cpu_to_virtio16(little_endian,
180*4882a593Smuzhiyun 						  sinfo->gso_size);
181*4882a593Smuzhiyun 		if (sinfo->gso_type & SKB_GSO_TCPV4)
182*4882a593Smuzhiyun 			hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
183*4882a593Smuzhiyun 		else if (sinfo->gso_type & SKB_GSO_TCPV6)
184*4882a593Smuzhiyun 			hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
185*4882a593Smuzhiyun 		else
186*4882a593Smuzhiyun 			return -EINVAL;
187*4882a593Smuzhiyun 		if (sinfo->gso_type & SKB_GSO_TCP_ECN)
188*4882a593Smuzhiyun 			hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN;
189*4882a593Smuzhiyun 	} else
190*4882a593Smuzhiyun 		hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	if (skb->ip_summed == CHECKSUM_PARTIAL) {
193*4882a593Smuzhiyun 		hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
194*4882a593Smuzhiyun 		hdr->csum_start = __cpu_to_virtio16(little_endian,
195*4882a593Smuzhiyun 			skb_checksum_start_offset(skb) + vlan_hlen);
196*4882a593Smuzhiyun 		hdr->csum_offset = __cpu_to_virtio16(little_endian,
197*4882a593Smuzhiyun 				skb->csum_offset);
198*4882a593Smuzhiyun 	} else if (has_data_valid &&
199*4882a593Smuzhiyun 		   skb->ip_summed == CHECKSUM_UNNECESSARY) {
200*4882a593Smuzhiyun 		hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID;
201*4882a593Smuzhiyun 	} /* else everything is zero */
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	return 0;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun #endif /* _LINUX_VIRTIO_NET_H */
207