1 /* SPDX-License-Identifier: GPL-2.0 */
2 #ifndef _LINUX_VIRTIO_NET_H
3 #define _LINUX_VIRTIO_NET_H
5 #include <linux/if_vlan.h>
7 #include <linux/ipv6.h>
9 #include <uapi/linux/tcp.h>
10 #include <uapi/linux/virtio_net.h>
12 static inline bool virtio_net_hdr_match_proto(__be16 protocol
, __u8 gso_type
)
14 switch (gso_type
& ~VIRTIO_NET_HDR_GSO_ECN
) {
15 case VIRTIO_NET_HDR_GSO_TCPV4
:
16 return protocol
== cpu_to_be16(ETH_P_IP
);
17 case VIRTIO_NET_HDR_GSO_TCPV6
:
18 return protocol
== cpu_to_be16(ETH_P_IPV6
);
19 case VIRTIO_NET_HDR_GSO_UDP
:
20 case VIRTIO_NET_HDR_GSO_UDP_L4
:
21 return protocol
== cpu_to_be16(ETH_P_IP
) ||
22 protocol
== cpu_to_be16(ETH_P_IPV6
);
28 static inline int virtio_net_hdr_set_proto(struct sk_buff
*skb
,
29 const struct virtio_net_hdr
*hdr
)
34 switch (hdr
->gso_type
& ~VIRTIO_NET_HDR_GSO_ECN
) {
35 case VIRTIO_NET_HDR_GSO_TCPV4
:
36 case VIRTIO_NET_HDR_GSO_UDP
:
37 case VIRTIO_NET_HDR_GSO_UDP_L4
:
38 skb
->protocol
= cpu_to_be16(ETH_P_IP
);
40 case VIRTIO_NET_HDR_GSO_TCPV6
:
41 skb
->protocol
= cpu_to_be16(ETH_P_IPV6
);
50 static inline int virtio_net_hdr_to_skb(struct sk_buff
*skb
,
51 const struct virtio_net_hdr
*hdr
,
54 unsigned int nh_min_len
= sizeof(struct iphdr
);
55 unsigned int gso_type
= 0;
56 unsigned int thlen
= 0;
57 unsigned int p_off
= 0;
58 unsigned int ip_proto
;
60 if (hdr
->gso_type
!= VIRTIO_NET_HDR_GSO_NONE
) {
61 switch (hdr
->gso_type
& ~VIRTIO_NET_HDR_GSO_ECN
) {
62 case VIRTIO_NET_HDR_GSO_TCPV4
:
63 gso_type
= SKB_GSO_TCPV4
;
64 ip_proto
= IPPROTO_TCP
;
65 thlen
= sizeof(struct tcphdr
);
67 case VIRTIO_NET_HDR_GSO_TCPV6
:
68 gso_type
= SKB_GSO_TCPV6
;
69 ip_proto
= IPPROTO_TCP
;
70 thlen
= sizeof(struct tcphdr
);
71 nh_min_len
= sizeof(struct ipv6hdr
);
73 case VIRTIO_NET_HDR_GSO_UDP
:
74 gso_type
= SKB_GSO_UDP
;
75 ip_proto
= IPPROTO_UDP
;
76 thlen
= sizeof(struct udphdr
);
78 case VIRTIO_NET_HDR_GSO_UDP_L4
:
79 gso_type
= SKB_GSO_UDP_L4
;
80 ip_proto
= IPPROTO_UDP
;
81 thlen
= sizeof(struct udphdr
);
87 if (hdr
->gso_type
& VIRTIO_NET_HDR_GSO_ECN
)
88 gso_type
|= SKB_GSO_TCP_ECN
;
90 if (hdr
->gso_size
== 0)
94 skb_reset_mac_header(skb
);
96 if (hdr
->flags
& VIRTIO_NET_HDR_F_NEEDS_CSUM
) {
97 u32 start
= __virtio16_to_cpu(little_endian
, hdr
->csum_start
);
98 u32 off
= __virtio16_to_cpu(little_endian
, hdr
->csum_offset
);
99 u32 needed
= start
+ max_t(u32
, thlen
, off
+ sizeof(__sum16
));
101 if (!pskb_may_pull(skb
, needed
))
104 if (!skb_partial_csum_set(skb
, start
, off
))
106 if (skb_transport_offset(skb
) < nh_min_len
)
109 nh_min_len
= skb_transport_offset(skb
);
110 p_off
= nh_min_len
+ thlen
;
111 if (!pskb_may_pull(skb
, p_off
))
114 /* gso packets without NEEDS_CSUM do not set transport_offset.
115 * probe and drop if does not match one of the above types.
117 if (gso_type
&& skb
->network_header
) {
118 struct flow_keys_basic keys
;
120 if (!skb
->protocol
) {
121 __be16 protocol
= dev_parse_header_protocol(skb
);
124 virtio_net_hdr_set_proto(skb
, hdr
);
125 else if (!virtio_net_hdr_match_proto(protocol
, hdr
->gso_type
))
128 skb
->protocol
= protocol
;
131 if (!skb_flow_dissect_flow_keys_basic(NULL
, skb
, &keys
,
134 /* UFO does not specify ipv4 or 6: try both */
135 if (gso_type
& SKB_GSO_UDP
&&
136 skb
->protocol
== htons(ETH_P_IP
)) {
137 skb
->protocol
= htons(ETH_P_IPV6
);
143 p_off
= keys
.control
.thoff
+ thlen
;
144 if (!pskb_may_pull(skb
, p_off
) ||
145 keys
.basic
.ip_proto
!= ip_proto
)
148 skb_set_transport_header(skb
, keys
.control
.thoff
);
149 } else if (gso_type
) {
150 p_off
= nh_min_len
+ thlen
;
151 if (!pskb_may_pull(skb
, p_off
))
156 if (hdr
->gso_type
!= VIRTIO_NET_HDR_GSO_NONE
) {
157 u16 gso_size
= __virtio16_to_cpu(little_endian
, hdr
->gso_size
);
158 unsigned int nh_off
= p_off
;
159 struct skb_shared_info
*shinfo
= skb_shinfo(skb
);
161 switch (gso_type
& ~SKB_GSO_TCP_ECN
) {
163 /* UFO may not include transport header in gso_size. */
167 if (!(hdr
->flags
& VIRTIO_NET_HDR_F_NEEDS_CSUM
))
169 if (skb
->csum_offset
!= offsetof(struct udphdr
, check
))
171 if (skb
->len
- p_off
> gso_size
* UDP_MAX_SEGMENTS
)
173 if (gso_type
!= SKB_GSO_UDP_L4
)
178 if (skb
->ip_summed
== CHECKSUM_PARTIAL
&&
179 skb
->csum_offset
!= offsetof(struct tcphdr
, check
))
184 /* Kernel has a special handling for GSO_BY_FRAGS. */
185 if (gso_size
== GSO_BY_FRAGS
)
188 /* Too small packets are not really GSO ones. */
189 if (skb
->len
- nh_off
> gso_size
) {
190 shinfo
->gso_size
= gso_size
;
191 shinfo
->gso_type
= gso_type
;
193 /* Header must be checked, and gso_segs computed. */
194 shinfo
->gso_type
|= SKB_GSO_DODGY
;
195 shinfo
->gso_segs
= 0;
202 static inline int virtio_net_hdr_from_skb(const struct sk_buff
*skb
,
203 struct virtio_net_hdr
*hdr
,
208 memset(hdr
, 0, sizeof(*hdr
)); /* no info leak */
210 if (skb_is_gso(skb
)) {
211 struct skb_shared_info
*sinfo
= skb_shinfo(skb
);
213 /* This is a hint as to how much should be linear. */
214 hdr
->hdr_len
= __cpu_to_virtio16(little_endian
,
216 hdr
->gso_size
= __cpu_to_virtio16(little_endian
,
218 if (sinfo
->gso_type
& SKB_GSO_TCPV4
)
219 hdr
->gso_type
= VIRTIO_NET_HDR_GSO_TCPV4
;
220 else if (sinfo
->gso_type
& SKB_GSO_TCPV6
)
221 hdr
->gso_type
= VIRTIO_NET_HDR_GSO_TCPV6
;
222 else if (sinfo
->gso_type
& SKB_GSO_UDP_L4
)
223 hdr
->gso_type
= VIRTIO_NET_HDR_GSO_UDP_L4
;
226 if (sinfo
->gso_type
& SKB_GSO_TCP_ECN
)
227 hdr
->gso_type
|= VIRTIO_NET_HDR_GSO_ECN
;
229 hdr
->gso_type
= VIRTIO_NET_HDR_GSO_NONE
;
231 if (skb
->ip_summed
== CHECKSUM_PARTIAL
) {
232 hdr
->flags
= VIRTIO_NET_HDR_F_NEEDS_CSUM
;
233 hdr
->csum_start
= __cpu_to_virtio16(little_endian
,
234 skb_checksum_start_offset(skb
) + vlan_hlen
);
235 hdr
->csum_offset
= __cpu_to_virtio16(little_endian
,
237 } else if (has_data_valid
&&
238 skb
->ip_summed
== CHECKSUM_UNNECESSARY
) {
239 hdr
->flags
= VIRTIO_NET_HDR_F_DATA_VALID
;
240 } /* else everything is zero */
245 #endif /* _LINUX_VIRTIO_NET_H */