1b2441318SGreg Kroah-Hartman /* SPDX-License-Identifier: GPL-2.0 */
2fd2a0437SMike Rapoport #ifndef _LINUX_VIRTIO_NET_H
3fd2a0437SMike Rapoport #define _LINUX_VIRTIO_NET_H
4fd2a0437SMike Rapoport
5fd2a0437SMike Rapoport #include <linux/if_vlan.h>
6*342c88f4SEric Dumazet #include <linux/ip.h>
7*342c88f4SEric Dumazet #include <linux/ipv6.h>
8fc8b2a61SWillem de Bruijn #include <linux/udp.h>
99274124fSWillem de Bruijn #include <uapi/linux/tcp.h>
10fd2a0437SMike Rapoport #include <uapi/linux/virtio_net.h>
11fd2a0437SMike Rapoport
virtio_net_hdr_match_proto(__be16 protocol,__u8 gso_type)127e5cced9SWillem de Bruijn static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type)
137e5cced9SWillem de Bruijn {
147e5cced9SWillem de Bruijn switch (gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
157e5cced9SWillem de Bruijn case VIRTIO_NET_HDR_GSO_TCPV4:
167e5cced9SWillem de Bruijn return protocol == cpu_to_be16(ETH_P_IP);
177e5cced9SWillem de Bruijn case VIRTIO_NET_HDR_GSO_TCPV6:
187e5cced9SWillem de Bruijn return protocol == cpu_to_be16(ETH_P_IPV6);
197e5cced9SWillem de Bruijn case VIRTIO_NET_HDR_GSO_UDP:
20860b7f27SAndrew Melnychenko case VIRTIO_NET_HDR_GSO_UDP_L4:
217e5cced9SWillem de Bruijn return protocol == cpu_to_be16(ETH_P_IP) ||
227e5cced9SWillem de Bruijn protocol == cpu_to_be16(ETH_P_IPV6);
237e5cced9SWillem de Bruijn default:
247e5cced9SWillem de Bruijn return false;
257e5cced9SWillem de Bruijn }
267e5cced9SWillem de Bruijn }
277e5cced9SWillem de Bruijn
virtio_net_hdr_set_proto(struct sk_buff * skb,const struct virtio_net_hdr * hdr)289d2f67e4SJianfeng Tan static inline int virtio_net_hdr_set_proto(struct sk_buff *skb,
299d2f67e4SJianfeng Tan const struct virtio_net_hdr *hdr)
309d2f67e4SJianfeng Tan {
311ed1d592SWillem de Bruijn if (skb->protocol)
321ed1d592SWillem de Bruijn return 0;
331ed1d592SWillem de Bruijn
349d2f67e4SJianfeng Tan switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
359d2f67e4SJianfeng Tan case VIRTIO_NET_HDR_GSO_TCPV4:
369d2f67e4SJianfeng Tan case VIRTIO_NET_HDR_GSO_UDP:
37860b7f27SAndrew Melnychenko case VIRTIO_NET_HDR_GSO_UDP_L4:
389d2f67e4SJianfeng Tan skb->protocol = cpu_to_be16(ETH_P_IP);
399d2f67e4SJianfeng Tan break;
409d2f67e4SJianfeng Tan case VIRTIO_NET_HDR_GSO_TCPV6:
419d2f67e4SJianfeng Tan skb->protocol = cpu_to_be16(ETH_P_IPV6);
429d2f67e4SJianfeng Tan break;
439d2f67e4SJianfeng Tan default:
449d2f67e4SJianfeng Tan return -EINVAL;
459d2f67e4SJianfeng Tan }
469d2f67e4SJianfeng Tan
479d2f67e4SJianfeng Tan return 0;
489d2f67e4SJianfeng Tan }
499d2f67e4SJianfeng Tan
virtio_net_hdr_to_skb(struct sk_buff * skb,const struct virtio_net_hdr * hdr,bool little_endian)50fd2a0437SMike Rapoport static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
51fd2a0437SMike Rapoport const struct virtio_net_hdr *hdr,
52fd2a0437SMike Rapoport bool little_endian)
53fd2a0437SMike Rapoport {
54*342c88f4SEric Dumazet unsigned int nh_min_len = sizeof(struct iphdr);
550c19f846SWillem de Bruijn unsigned int gso_type = 0;
569274124fSWillem de Bruijn unsigned int thlen = 0;
576dd912f8SWillem de Bruijn unsigned int p_off = 0;
589274124fSWillem de Bruijn unsigned int ip_proto;
59fd2a0437SMike Rapoport
60fd2a0437SMike Rapoport if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
61fd2a0437SMike Rapoport switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
62fd2a0437SMike Rapoport case VIRTIO_NET_HDR_GSO_TCPV4:
63fd2a0437SMike Rapoport gso_type = SKB_GSO_TCPV4;
649274124fSWillem de Bruijn ip_proto = IPPROTO_TCP;
659274124fSWillem de Bruijn thlen = sizeof(struct tcphdr);
66fd2a0437SMike Rapoport break;
67fd2a0437SMike Rapoport case VIRTIO_NET_HDR_GSO_TCPV6:
68fd2a0437SMike Rapoport gso_type = SKB_GSO_TCPV6;
699274124fSWillem de Bruijn ip_proto = IPPROTO_TCP;
709274124fSWillem de Bruijn thlen = sizeof(struct tcphdr);
71*342c88f4SEric Dumazet nh_min_len = sizeof(struct ipv6hdr);
72fd2a0437SMike Rapoport break;
730c19f846SWillem de Bruijn case VIRTIO_NET_HDR_GSO_UDP:
740c19f846SWillem de Bruijn gso_type = SKB_GSO_UDP;
759274124fSWillem de Bruijn ip_proto = IPPROTO_UDP;
769274124fSWillem de Bruijn thlen = sizeof(struct udphdr);
770c19f846SWillem de Bruijn break;
78860b7f27SAndrew Melnychenko case VIRTIO_NET_HDR_GSO_UDP_L4:
79860b7f27SAndrew Melnychenko gso_type = SKB_GSO_UDP_L4;
80860b7f27SAndrew Melnychenko ip_proto = IPPROTO_UDP;
81860b7f27SAndrew Melnychenko thlen = sizeof(struct udphdr);
82860b7f27SAndrew Melnychenko break;
83fd2a0437SMike Rapoport default:
84fd2a0437SMike Rapoport return -EINVAL;
85fd2a0437SMike Rapoport }
86fd2a0437SMike Rapoport
87fd2a0437SMike Rapoport if (hdr->gso_type & VIRTIO_NET_HDR_GSO_ECN)
88fd2a0437SMike Rapoport gso_type |= SKB_GSO_TCP_ECN;
89fd2a0437SMike Rapoport
90fd2a0437SMike Rapoport if (hdr->gso_size == 0)
91fd2a0437SMike Rapoport return -EINVAL;
92fd2a0437SMike Rapoport }
93fd2a0437SMike Rapoport
9461431a59SEric Dumazet skb_reset_mac_header(skb);
9561431a59SEric Dumazet
96fd2a0437SMike Rapoport if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
970f6925b3SEric Dumazet u32 start = __virtio16_to_cpu(little_endian, hdr->csum_start);
980f6925b3SEric Dumazet u32 off = __virtio16_to_cpu(little_endian, hdr->csum_offset);
990f6925b3SEric Dumazet u32 needed = start + max_t(u32, thlen, off + sizeof(__sum16));
1000f6925b3SEric Dumazet
1010f6925b3SEric Dumazet if (!pskb_may_pull(skb, needed))
1020f6925b3SEric Dumazet return -EINVAL;
103fd2a0437SMike Rapoport
104fd2a0437SMike Rapoport if (!skb_partial_csum_set(skb, start, off))
105fd2a0437SMike Rapoport return -EINVAL;
1069274124fSWillem de Bruijn
107*342c88f4SEric Dumazet nh_min_len = max_t(u32, nh_min_len, skb_transport_offset(skb));
108*342c88f4SEric Dumazet p_off = nh_min_len + thlen;
1090f6925b3SEric Dumazet if (!pskb_may_pull(skb, p_off))
1109274124fSWillem de Bruijn return -EINVAL;
111d5be7f63SWillem de Bruijn } else {
112d5be7f63SWillem de Bruijn /* gso packets without NEEDS_CSUM do not set transport_offset.
113d5be7f63SWillem de Bruijn * probe and drop if does not match one of the above types.
114d5be7f63SWillem de Bruijn */
1159e8db591SWillem de Bruijn if (gso_type && skb->network_header) {
1169274124fSWillem de Bruijn struct flow_keys_basic keys;
1179274124fSWillem de Bruijn
118924a9bc3SBalazs Nemeth if (!skb->protocol) {
119924a9bc3SBalazs Nemeth __be16 protocol = dev_parse_header_protocol(skb);
120924a9bc3SBalazs Nemeth
1217e5cced9SWillem de Bruijn if (!protocol)
1229e8db591SWillem de Bruijn virtio_net_hdr_set_proto(skb, hdr);
1237e5cced9SWillem de Bruijn else if (!virtio_net_hdr_match_proto(protocol, hdr->gso_type))
124924a9bc3SBalazs Nemeth return -EINVAL;
1257e5cced9SWillem de Bruijn else
1267e5cced9SWillem de Bruijn skb->protocol = protocol;
127924a9bc3SBalazs Nemeth }
1289e8db591SWillem de Bruijn retry:
1299274124fSWillem de Bruijn if (!skb_flow_dissect_flow_keys_basic(NULL, skb, &keys,
1309274124fSWillem de Bruijn NULL, 0, 0, 0,
1319274124fSWillem de Bruijn 0)) {
1329e8db591SWillem de Bruijn /* UFO does not specify ipv4 or 6: try both */
1339e8db591SWillem de Bruijn if (gso_type & SKB_GSO_UDP &&
1349e8db591SWillem de Bruijn skb->protocol == htons(ETH_P_IP)) {
1359e8db591SWillem de Bruijn skb->protocol = htons(ETH_P_IPV6);
1369e8db591SWillem de Bruijn goto retry;
1379e8db591SWillem de Bruijn }
138d5be7f63SWillem de Bruijn return -EINVAL;
139d5be7f63SWillem de Bruijn }
1409274124fSWillem de Bruijn
1416dd912f8SWillem de Bruijn p_off = keys.control.thoff + thlen;
1420f6925b3SEric Dumazet if (!pskb_may_pull(skb, p_off) ||
1439274124fSWillem de Bruijn keys.basic.ip_proto != ip_proto)
1449274124fSWillem de Bruijn return -EINVAL;
1459274124fSWillem de Bruijn
1469274124fSWillem de Bruijn skb_set_transport_header(skb, keys.control.thoff);
1476dd912f8SWillem de Bruijn } else if (gso_type) {
148*342c88f4SEric Dumazet p_off = nh_min_len + thlen;
1490f6925b3SEric Dumazet if (!pskb_may_pull(skb, p_off))
1506dd912f8SWillem de Bruijn return -EINVAL;
151fd2a0437SMike Rapoport }
1529e8db591SWillem de Bruijn }
153fd2a0437SMike Rapoport
154fd2a0437SMike Rapoport if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
155fd2a0437SMike Rapoport u16 gso_size = __virtio16_to_cpu(little_endian, hdr->gso_size);
156cf9acc90SJonathan Davies unsigned int nh_off = p_off;
1577c6d2ecbSEric Dumazet struct skb_shared_info *shinfo = skb_shinfo(skb);
158fd2a0437SMike Rapoport
159fc8b2a61SWillem de Bruijn switch (gso_type & ~SKB_GSO_TCP_ECN) {
160fc8b2a61SWillem de Bruijn case SKB_GSO_UDP:
161cf9acc90SJonathan Davies /* UFO may not include transport header in gso_size. */
162cf9acc90SJonathan Davies nh_off -= thlen;
163fc8b2a61SWillem de Bruijn break;
164fc8b2a61SWillem de Bruijn case SKB_GSO_UDP_L4:
165fc8b2a61SWillem de Bruijn if (!(hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM))
166fc8b2a61SWillem de Bruijn return -EINVAL;
167fc8b2a61SWillem de Bruijn if (skb->csum_offset != offsetof(struct udphdr, check))
168fc8b2a61SWillem de Bruijn return -EINVAL;
169fc8b2a61SWillem de Bruijn if (skb->len - p_off > gso_size * UDP_MAX_SEGMENTS)
170fc8b2a61SWillem de Bruijn return -EINVAL;
171fc8b2a61SWillem de Bruijn if (gso_type != SKB_GSO_UDP_L4)
172fc8b2a61SWillem de Bruijn return -EINVAL;
173fc8b2a61SWillem de Bruijn break;
174fc8b2a61SWillem de Bruijn }
175cf9acc90SJonathan Davies
176b616be6bSEric Dumazet /* Kernel has a special handling for GSO_BY_FRAGS. */
177b616be6bSEric Dumazet if (gso_size == GSO_BY_FRAGS)
178b616be6bSEric Dumazet return -EINVAL;
179b616be6bSEric Dumazet
1807c6d2ecbSEric Dumazet /* Too small packets are not really GSO ones. */
181cf9acc90SJonathan Davies if (skb->len - nh_off > gso_size) {
1827c6d2ecbSEric Dumazet shinfo->gso_size = gso_size;
1837c6d2ecbSEric Dumazet shinfo->gso_type = gso_type;
184fd2a0437SMike Rapoport
185fd2a0437SMike Rapoport /* Header must be checked, and gso_segs computed. */
1867c6d2ecbSEric Dumazet shinfo->gso_type |= SKB_GSO_DODGY;
1877c6d2ecbSEric Dumazet shinfo->gso_segs = 0;
1887c6d2ecbSEric Dumazet }
189fd2a0437SMike Rapoport }
190fd2a0437SMike Rapoport
191fd2a0437SMike Rapoport return 0;
192fd2a0437SMike Rapoport }
193fd2a0437SMike Rapoport
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)194fd2a0437SMike Rapoport static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb,
195fd2a0437SMike Rapoport struct virtio_net_hdr *hdr,
1966391a448SJason Wang bool little_endian,
197fd3a8862SWillem de Bruijn bool has_data_valid,
198fd3a8862SWillem de Bruijn int vlan_hlen)
199fd2a0437SMike Rapoport {
2009403cd7cSJarno Rajahalme memset(hdr, 0, sizeof(*hdr)); /* no info leak */
201fd2a0437SMike Rapoport
202fd2a0437SMike Rapoport if (skb_is_gso(skb)) {
203fd2a0437SMike Rapoport struct skb_shared_info *sinfo = skb_shinfo(skb);
204fd2a0437SMike Rapoport
205fd2a0437SMike Rapoport /* This is a hint as to how much should be linear. */
206fd2a0437SMike Rapoport hdr->hdr_len = __cpu_to_virtio16(little_endian,
207fd2a0437SMike Rapoport skb_headlen(skb));
208fd2a0437SMike Rapoport hdr->gso_size = __cpu_to_virtio16(little_endian,
209fd2a0437SMike Rapoport sinfo->gso_size);
210fd2a0437SMike Rapoport if (sinfo->gso_type & SKB_GSO_TCPV4)
211fd2a0437SMike Rapoport hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
212fd2a0437SMike Rapoport else if (sinfo->gso_type & SKB_GSO_TCPV6)
213fd2a0437SMike Rapoport hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
214860b7f27SAndrew Melnychenko else if (sinfo->gso_type & SKB_GSO_UDP_L4)
215860b7f27SAndrew Melnychenko hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP_L4;
216fd2a0437SMike Rapoport else
217fd2a0437SMike Rapoport return -EINVAL;
218fd2a0437SMike Rapoport if (sinfo->gso_type & SKB_GSO_TCP_ECN)
219fd2a0437SMike Rapoport hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN;
220fd2a0437SMike Rapoport } else
221fd2a0437SMike Rapoport hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
222fd2a0437SMike Rapoport
223fd2a0437SMike Rapoport if (skb->ip_summed == CHECKSUM_PARTIAL) {
224fd2a0437SMike Rapoport hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
225fd2a0437SMike Rapoport hdr->csum_start = __cpu_to_virtio16(little_endian,
226fd3a8862SWillem de Bruijn skb_checksum_start_offset(skb) + vlan_hlen);
227fd2a0437SMike Rapoport hdr->csum_offset = __cpu_to_virtio16(little_endian,
228fd2a0437SMike Rapoport skb->csum_offset);
2296391a448SJason Wang } else if (has_data_valid &&
2306391a448SJason Wang skb->ip_summed == CHECKSUM_UNNECESSARY) {
2316391a448SJason Wang hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID;
232fd2a0437SMike Rapoport } /* else everything is zero */
233fd2a0437SMike Rapoport
234fd2a0437SMike Rapoport return 0;
235fd2a0437SMike Rapoport }
236fd2a0437SMike Rapoport
237d66016a7SJarno Rajahalme #endif /* _LINUX_VIRTIO_NET_H */
238