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