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 107 nh_min_len = max_t(u32, nh_min_len, skb_transport_offset(skb)); 108 p_off = nh_min_len + thlen; 109 if (!pskb_may_pull(skb, p_off)) 110 return -EINVAL; 111 } else { 112 /* gso packets without NEEDS_CSUM do not set transport_offset. 113 * probe and drop if does not match one of the above types. 114 */ 115 if (gso_type && skb->network_header) { 116 struct flow_keys_basic keys; 117 118 if (!skb->protocol) { 119 __be16 protocol = dev_parse_header_protocol(skb); 120 121 if (!protocol) 122 virtio_net_hdr_set_proto(skb, hdr); 123 else if (!virtio_net_hdr_match_proto(protocol, hdr->gso_type)) 124 return -EINVAL; 125 else 126 skb->protocol = protocol; 127 } 128 retry: 129 if (!skb_flow_dissect_flow_keys_basic(NULL, skb, &keys, 130 NULL, 0, 0, 0, 131 0)) { 132 /* UFO does not specify ipv4 or 6: try both */ 133 if (gso_type & SKB_GSO_UDP && 134 skb->protocol == htons(ETH_P_IP)) { 135 skb->protocol = htons(ETH_P_IPV6); 136 goto retry; 137 } 138 return -EINVAL; 139 } 140 141 p_off = keys.control.thoff + thlen; 142 if (!pskb_may_pull(skb, p_off) || 143 keys.basic.ip_proto != ip_proto) 144 return -EINVAL; 145 146 skb_set_transport_header(skb, keys.control.thoff); 147 } else if (gso_type) { 148 p_off = nh_min_len + thlen; 149 if (!pskb_may_pull(skb, p_off)) 150 return -EINVAL; 151 } 152 } 153 154 if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { 155 u16 gso_size = __virtio16_to_cpu(little_endian, hdr->gso_size); 156 unsigned int nh_off = p_off; 157 struct skb_shared_info *shinfo = skb_shinfo(skb); 158 159 switch (gso_type & ~SKB_GSO_TCP_ECN) { 160 case SKB_GSO_UDP: 161 /* UFO may not include transport header in gso_size. */ 162 nh_off -= thlen; 163 break; 164 case SKB_GSO_UDP_L4: 165 if (!(hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM)) 166 return -EINVAL; 167 if (skb->csum_offset != offsetof(struct udphdr, check)) 168 return -EINVAL; 169 if (skb->len - p_off > gso_size * UDP_MAX_SEGMENTS) 170 return -EINVAL; 171 if (gso_type != SKB_GSO_UDP_L4) 172 return -EINVAL; 173 break; 174 } 175 176 /* Kernel has a special handling for GSO_BY_FRAGS. */ 177 if (gso_size == GSO_BY_FRAGS) 178 return -EINVAL; 179 180 /* Too small packets are not really GSO ones. */ 181 if (skb->len - nh_off > gso_size) { 182 shinfo->gso_size = gso_size; 183 shinfo->gso_type = gso_type; 184 185 /* Header must be checked, and gso_segs computed. */ 186 shinfo->gso_type |= SKB_GSO_DODGY; 187 shinfo->gso_segs = 0; 188 } 189 } 190 191 return 0; 192 } 193 194 static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb, 195 struct virtio_net_hdr *hdr, 196 bool little_endian, 197 bool has_data_valid, 198 int vlan_hlen) 199 { 200 memset(hdr, 0, sizeof(*hdr)); /* no info leak */ 201 202 if (skb_is_gso(skb)) { 203 struct skb_shared_info *sinfo = skb_shinfo(skb); 204 205 /* This is a hint as to how much should be linear. */ 206 hdr->hdr_len = __cpu_to_virtio16(little_endian, 207 skb_headlen(skb)); 208 hdr->gso_size = __cpu_to_virtio16(little_endian, 209 sinfo->gso_size); 210 if (sinfo->gso_type & SKB_GSO_TCPV4) 211 hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; 212 else if (sinfo->gso_type & SKB_GSO_TCPV6) 213 hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; 214 else if (sinfo->gso_type & SKB_GSO_UDP_L4) 215 hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP_L4; 216 else 217 return -EINVAL; 218 if (sinfo->gso_type & SKB_GSO_TCP_ECN) 219 hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN; 220 } else 221 hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE; 222 223 if (skb->ip_summed == CHECKSUM_PARTIAL) { 224 hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; 225 hdr->csum_start = __cpu_to_virtio16(little_endian, 226 skb_checksum_start_offset(skb) + vlan_hlen); 227 hdr->csum_offset = __cpu_to_virtio16(little_endian, 228 skb->csum_offset); 229 } else if (has_data_valid && 230 skb->ip_summed == CHECKSUM_UNNECESSARY) { 231 hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID; 232 } /* else everything is zero */ 233 234 return 0; 235 } 236 237 #endif /* _LINUX_VIRTIO_NET_H */ 238