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 <linux/ip.h> 7 #include <linux/ipv6.h> 8 #include <linux/udp.h> 9 #include <uapi/linux/tcp.h> 10 #include <uapi/linux/virtio_net.h> 11 12 static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type) 13 { 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); 23 default: 24 return false; 25 } 26 } 27 28 static inline int virtio_net_hdr_set_proto(struct sk_buff *skb, 29 const struct virtio_net_hdr *hdr) 30 { 31 if (skb->protocol) 32 return 0; 33 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); 39 break; 40 case VIRTIO_NET_HDR_GSO_TCPV6: 41 skb->protocol = cpu_to_be16(ETH_P_IPV6); 42 break; 43 default: 44 return -EINVAL; 45 } 46 47 return 0; 48 } 49 50 static inline int virtio_net_hdr_to_skb(struct sk_buff *skb, 51 const struct virtio_net_hdr *hdr, 52 bool little_endian) 53 { 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; 59 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); 66 break; 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); 72 break; 73 case VIRTIO_NET_HDR_GSO_UDP: 74 gso_type = SKB_GSO_UDP; 75 ip_proto = IPPROTO_UDP; 76 thlen = sizeof(struct udphdr); 77 break; 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); 82 break; 83 default: 84 return -EINVAL; 85 } 86 87 if (hdr->gso_type & VIRTIO_NET_HDR_GSO_ECN) 88 gso_type |= SKB_GSO_TCP_ECN; 89 90 if (hdr->gso_size == 0) 91 return -EINVAL; 92 } 93 94 skb_reset_mac_header(skb); 95 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)); 100 101 if (!pskb_may_pull(skb, needed)) 102 return -EINVAL; 103 104 if (!skb_partial_csum_set(skb, start, off)) 105 return -EINVAL; 106 if (skb_transport_offset(skb) < nh_min_len) 107 return -EINVAL; 108 109 nh_min_len = skb_transport_offset(skb); 110 p_off = nh_min_len + thlen; 111 if (!pskb_may_pull(skb, p_off)) 112 return -EINVAL; 113 } else { 114 /* gso packets without NEEDS_CSUM do not set transport_offset. 115 * probe and drop if does not match one of the above types. 116 */ 117 if (gso_type && skb->network_header) { 118 struct flow_keys_basic keys; 119 120 if (!skb->protocol) { 121 __be16 protocol = dev_parse_header_protocol(skb); 122 123 if (!protocol) 124 virtio_net_hdr_set_proto(skb, hdr); 125 else if (!virtio_net_hdr_match_proto(protocol, hdr->gso_type)) 126 return -EINVAL; 127 else 128 skb->protocol = protocol; 129 } 130 retry: 131 if (!skb_flow_dissect_flow_keys_basic(NULL, skb, &keys, 132 NULL, 0, 0, 0, 133 0)) { 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); 138 goto retry; 139 } 140 return -EINVAL; 141 } 142 143 p_off = keys.control.thoff + thlen; 144 if (!pskb_may_pull(skb, p_off) || 145 keys.basic.ip_proto != ip_proto) 146 return -EINVAL; 147 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)) 152 return -EINVAL; 153 } 154 } 155 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); 160 161 switch (gso_type & ~SKB_GSO_TCP_ECN) { 162 case SKB_GSO_UDP: 163 /* UFO may not include transport header in gso_size. */ 164 nh_off -= thlen; 165 break; 166 case SKB_GSO_UDP_L4: 167 if (!(hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM)) 168 return -EINVAL; 169 if (skb->csum_offset != offsetof(struct udphdr, check)) 170 return -EINVAL; 171 if (skb->len - p_off > gso_size * UDP_MAX_SEGMENTS) 172 return -EINVAL; 173 if (gso_type != SKB_GSO_UDP_L4) 174 return -EINVAL; 175 break; 176 case SKB_GSO_TCPV4: 177 case SKB_GSO_TCPV6: 178 if (skb->ip_summed == CHECKSUM_PARTIAL && 179 skb->csum_offset != offsetof(struct tcphdr, check)) 180 return -EINVAL; 181 break; 182 } 183 184 /* Kernel has a special handling for GSO_BY_FRAGS. */ 185 if (gso_size == GSO_BY_FRAGS) 186 return -EINVAL; 187 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; 192 193 /* Header must be checked, and gso_segs computed. */ 194 shinfo->gso_type |= SKB_GSO_DODGY; 195 shinfo->gso_segs = 0; 196 } 197 } 198 199 return 0; 200 } 201 202 static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb, 203 struct virtio_net_hdr *hdr, 204 bool little_endian, 205 bool has_data_valid, 206 int vlan_hlen) 207 { 208 memset(hdr, 0, sizeof(*hdr)); /* no info leak */ 209 210 if (skb_is_gso(skb)) { 211 struct skb_shared_info *sinfo = skb_shinfo(skb); 212 213 /* This is a hint as to how much should be linear. */ 214 hdr->hdr_len = __cpu_to_virtio16(little_endian, 215 skb_headlen(skb)); 216 hdr->gso_size = __cpu_to_virtio16(little_endian, 217 sinfo->gso_size); 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; 224 else 225 return -EINVAL; 226 if (sinfo->gso_type & SKB_GSO_TCP_ECN) 227 hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN; 228 } else 229 hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE; 230 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, 236 skb->csum_offset); 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 */ 241 242 return 0; 243 } 244 245 #endif /* _LINUX_VIRTIO_NET_H */ 246
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.