]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
fd2a0437 MR |
2 | #ifndef _LINUX_VIRTIO_NET_H |
3 | #define _LINUX_VIRTIO_NET_H | |
4 | ||
5 | #include <linux/if_vlan.h> | |
9274124f WB |
6 | #include <uapi/linux/tcp.h> |
7 | #include <uapi/linux/udp.h> | |
fd2a0437 MR |
8 | #include <uapi/linux/virtio_net.h> |
9 | ||
72ad9cbd WB |
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 | ||
9d2f67e4 JT |
25 | static inline int virtio_net_hdr_set_proto(struct sk_buff *skb, |
26 | const struct virtio_net_hdr *hdr) | |
27 | { | |
1c3f4f4d WB |
28 | if (skb->protocol) |
29 | return 0; | |
30 | ||
9d2f67e4 JT |
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 | ||
fd2a0437 MR |
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 | { | |
0c19f846 | 50 | unsigned int gso_type = 0; |
9274124f | 51 | unsigned int thlen = 0; |
6dd912f8 | 52 | unsigned int p_off = 0; |
9274124f | 53 | unsigned int ip_proto; |
fd2a0437 MR |
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; | |
9274124f WB |
59 | ip_proto = IPPROTO_TCP; |
60 | thlen = sizeof(struct tcphdr); | |
fd2a0437 MR |
61 | break; |
62 | case VIRTIO_NET_HDR_GSO_TCPV6: | |
63 | gso_type = SKB_GSO_TCPV6; | |
9274124f WB |
64 | ip_proto = IPPROTO_TCP; |
65 | thlen = sizeof(struct tcphdr); | |
fd2a0437 | 66 | break; |
0c19f846 WB |
67 | case VIRTIO_NET_HDR_GSO_UDP: |
68 | gso_type = SKB_GSO_UDP; | |
9274124f WB |
69 | ip_proto = IPPROTO_UDP; |
70 | thlen = sizeof(struct udphdr); | |
0c19f846 | 71 | break; |
fd2a0437 MR |
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 | ||
61431a59 ED |
83 | skb_reset_mac_header(skb); |
84 | ||
fd2a0437 | 85 | if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { |
0f6925b3 ED |
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; | |
fd2a0437 MR |
92 | |
93 | if (!skb_partial_csum_set(skb, start, off)) | |
94 | return -EINVAL; | |
9274124f | 95 | |
6dd912f8 | 96 | p_off = skb_transport_offset(skb) + thlen; |
0f6925b3 | 97 | if (!pskb_may_pull(skb, p_off)) |
9274124f | 98 | return -EINVAL; |
d5be7f63 WB |
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 | */ | |
9e8db591 | 103 | if (gso_type && skb->network_header) { |
9274124f WB |
104 | struct flow_keys_basic keys; |
105 | ||
924a9bc3 BN |
106 | if (!skb->protocol) { |
107 | __be16 protocol = dev_parse_header_protocol(skb); | |
108 | ||
72ad9cbd WB |
109 | if (!protocol) |
110 | virtio_net_hdr_set_proto(skb, hdr); | |
111 | else if (!virtio_net_hdr_match_proto(protocol, hdr->gso_type)) | |
924a9bc3 | 112 | return -EINVAL; |
72ad9cbd WB |
113 | else |
114 | skb->protocol = protocol; | |
924a9bc3 | 115 | } |
9e8db591 | 116 | retry: |
9274124f WB |
117 | if (!skb_flow_dissect_flow_keys_basic(NULL, skb, &keys, |
118 | NULL, 0, 0, 0, | |
119 | 0)) { | |
9e8db591 WB |
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 | } | |
d5be7f63 | 126 | return -EINVAL; |
9e8db591 | 127 | } |
9274124f | 128 | |
6dd912f8 | 129 | p_off = keys.control.thoff + thlen; |
0f6925b3 | 130 | if (!pskb_may_pull(skb, p_off) || |
9274124f WB |
131 | keys.basic.ip_proto != ip_proto) |
132 | return -EINVAL; | |
133 | ||
134 | skb_set_transport_header(skb, keys.control.thoff); | |
6dd912f8 WB |
135 | } else if (gso_type) { |
136 | p_off = thlen; | |
0f6925b3 | 137 | if (!pskb_may_pull(skb, p_off)) |
6dd912f8 | 138 | return -EINVAL; |
d5be7f63 | 139 | } |
fd2a0437 MR |
140 | } |
141 | ||
142 | if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { | |
143 | u16 gso_size = __virtio16_to_cpu(little_endian, hdr->gso_size); | |
5245357a | 144 | unsigned int nh_off = p_off; |
7c6d2ecb | 145 | struct skb_shared_info *shinfo = skb_shinfo(skb); |
fd2a0437 | 146 | |
5245357a JD |
147 | /* UFO may not include transport header in gso_size. */ |
148 | if (gso_type & SKB_GSO_UDP) | |
149 | nh_off -= thlen; | |
150 | ||
7c6d2ecb | 151 | /* Too small packets are not really GSO ones. */ |
5245357a | 152 | if (skb->len - nh_off > gso_size) { |
7c6d2ecb ED |
153 | shinfo->gso_size = gso_size; |
154 | shinfo->gso_type = gso_type; | |
fd2a0437 | 155 | |
7c6d2ecb ED |
156 | /* Header must be checked, and gso_segs computed. */ |
157 | shinfo->gso_type |= SKB_GSO_DODGY; | |
158 | shinfo->gso_segs = 0; | |
159 | } | |
fd2a0437 MR |
160 | } |
161 | ||
162 | return 0; | |
163 | } | |
164 | ||
165 | static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb, | |
166 | struct virtio_net_hdr *hdr, | |
6391a448 | 167 | bool little_endian, |
fd3a8862 WB |
168 | bool has_data_valid, |
169 | int vlan_hlen) | |
fd2a0437 | 170 | { |
9403cd7c | 171 | memset(hdr, 0, sizeof(*hdr)); /* no info leak */ |
fd2a0437 MR |
172 | |
173 | if (skb_is_gso(skb)) { | |
174 | struct skb_shared_info *sinfo = skb_shinfo(skb); | |
175 | ||
176 | /* This is a hint as to how much should be linear. */ | |
177 | hdr->hdr_len = __cpu_to_virtio16(little_endian, | |
178 | skb_headlen(skb)); | |
179 | hdr->gso_size = __cpu_to_virtio16(little_endian, | |
180 | sinfo->gso_size); | |
181 | if (sinfo->gso_type & SKB_GSO_TCPV4) | |
182 | hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; | |
183 | else if (sinfo->gso_type & SKB_GSO_TCPV6) | |
184 | hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; | |
fd2a0437 MR |
185 | else |
186 | return -EINVAL; | |
187 | if (sinfo->gso_type & SKB_GSO_TCP_ECN) | |
188 | hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN; | |
189 | } else | |
190 | hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE; | |
191 | ||
192 | if (skb->ip_summed == CHECKSUM_PARTIAL) { | |
193 | hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; | |
fd3a8862 WB |
194 | hdr->csum_start = __cpu_to_virtio16(little_endian, |
195 | skb_checksum_start_offset(skb) + vlan_hlen); | |
fd2a0437 MR |
196 | hdr->csum_offset = __cpu_to_virtio16(little_endian, |
197 | skb->csum_offset); | |
6391a448 JW |
198 | } else if (has_data_valid && |
199 | skb->ip_summed == CHECKSUM_UNNECESSARY) { | |
200 | hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID; | |
fd2a0437 MR |
201 | } /* else everything is zero */ |
202 | ||
203 | return 0; | |
204 | } | |
205 | ||
d66016a7 | 206 | #endif /* _LINUX_VIRTIO_NET_H */ |