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