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 <uapi/linux/tcp.h> 7 #include <uapi/linux/udp.h> 8 #include <uapi/linux/virtio_net.h> 9 10 static inline int virtio_net_hdr_set_proto(struct sk_buff *skb, 11 const struct virtio_net_hdr *hdr) 12 { 13 switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { 14 case VIRTIO_NET_HDR_GSO_TCPV4: 15 case VIRTIO_NET_HDR_GSO_UDP: 16 skb->protocol = cpu_to_be16(ETH_P_IP); 17 break; 18 case VIRTIO_NET_HDR_GSO_TCPV6: 19 skb->protocol = cpu_to_be16(ETH_P_IPV6); 20 break; 21 default: 22 return -EINVAL; 23 } 24 25 return 0; 26 } 27 28 static inline int virtio_net_hdr_to_skb(struct sk_buff *skb, 29 const struct virtio_net_hdr *hdr, 30 bool little_endian) 31 { 32 unsigned int gso_type = 0; 33 unsigned int thlen = 0; 34 unsigned int p_off = 0; 35 unsigned int ip_proto; 36 37 if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { 38 switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { 39 case VIRTIO_NET_HDR_GSO_TCPV4: 40 gso_type = SKB_GSO_TCPV4; 41 ip_proto = IPPROTO_TCP; 42 thlen = sizeof(struct tcphdr); 43 break; 44 case VIRTIO_NET_HDR_GSO_TCPV6: 45 gso_type = SKB_GSO_TCPV6; 46 ip_proto = IPPROTO_TCP; 47 thlen = sizeof(struct tcphdr); 48 break; 49 case VIRTIO_NET_HDR_GSO_UDP: 50 gso_type = SKB_GSO_UDP; 51 ip_proto = IPPROTO_UDP; 52 thlen = sizeof(struct udphdr); 53 break; 54 default: 55 return -EINVAL; 56 } 57 58 if (hdr->gso_type & VIRTIO_NET_HDR_GSO_ECN) 59 gso_type |= SKB_GSO_TCP_ECN; 60 61 if (hdr->gso_size == 0) 62 return -EINVAL; 63 } 64 65 skb_reset_mac_header(skb); 66 67 if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { 68 u32 start = __virtio16_to_cpu(little_endian, hdr->csum_start); 69 u32 off = __virtio16_to_cpu(little_endian, hdr->csum_offset); 70 u32 needed = start + max_t(u32, thlen, off + sizeof(__sum16)); 71 72 if (!pskb_may_pull(skb, needed)) 73 return -EINVAL; 74 75 if (!skb_partial_csum_set(skb, start, off)) 76 return -EINVAL; 77 78 p_off = skb_transport_offset(skb) + thlen; 79 if (!pskb_may_pull(skb, p_off)) 80 return -EINVAL; 81 } else { 82 /* gso packets without NEEDS_CSUM do not set transport_offset. 83 * probe and drop if does not match one of the above types. 84 */ 85 if (gso_type && skb->network_header) { 86 struct flow_keys_basic keys; 87 88 if (!skb->protocol) { 89 __be16 protocol = dev_parse_header_protocol(skb); 90 91 virtio_net_hdr_set_proto(skb, hdr); 92 if (protocol && protocol != skb->protocol) 93 return -EINVAL; 94 } 95 retry: 96 if (!skb_flow_dissect_flow_keys_basic(NULL, skb, &keys, 97 NULL, 0, 0, 0, 98 0)) { 99 /* UFO does not specify ipv4 or 6: try both */ 100 if (gso_type & SKB_GSO_UDP && 101 skb->protocol == htons(ETH_P_IP)) { 102 skb->protocol = htons(ETH_P_IPV6); 103 goto retry; 104 } 105 return -EINVAL; 106 } 107 108 p_off = keys.control.thoff + thlen; 109 if (!pskb_may_pull(skb, p_off) || 110 keys.basic.ip_proto != ip_proto) 111 return -EINVAL; 112 113 skb_set_transport_header(skb, keys.control.thoff); 114 } else if (gso_type) { 115 p_off = thlen; 116 if (!pskb_may_pull(skb, p_off)) 117 return -EINVAL; 118 } 119 } 120 121 if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { 122 u16 gso_size = __virtio16_to_cpu(little_endian, hdr->gso_size); 123 struct skb_shared_info *shinfo = skb_shinfo(skb); 124 125 /* Too small packets are not really GSO ones. */ 126 if (skb->len - p_off > gso_size) { 127 shinfo->gso_size = gso_size; 128 shinfo->gso_type = gso_type; 129 130 /* Header must be checked, and gso_segs computed. */ 131 shinfo->gso_type |= SKB_GSO_DODGY; 132 shinfo->gso_segs = 0; 133 } 134 } 135 136 return 0; 137 } 138 139 static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb, 140 struct virtio_net_hdr *hdr, 141 bool little_endian, 142 bool has_data_valid, 143 int vlan_hlen) 144 { 145 memset(hdr, 0, sizeof(*hdr)); /* no info leak */ 146 147 if (skb_is_gso(skb)) { 148 struct skb_shared_info *sinfo = skb_shinfo(skb); 149 150 /* This is a hint as to how much should be linear. */ 151 hdr->hdr_len = __cpu_to_virtio16(little_endian, 152 skb_headlen(skb)); 153 hdr->gso_size = __cpu_to_virtio16(little_endian, 154 sinfo->gso_size); 155 if (sinfo->gso_type & SKB_GSO_TCPV4) 156 hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; 157 else if (sinfo->gso_type & SKB_GSO_TCPV6) 158 hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; 159 else 160 return -EINVAL; 161 if (sinfo->gso_type & SKB_GSO_TCP_ECN) 162 hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN; 163 } else 164 hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE; 165 166 if (skb->ip_summed == CHECKSUM_PARTIAL) { 167 hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; 168 hdr->csum_start = __cpu_to_virtio16(little_endian, 169 skb_checksum_start_offset(skb) + vlan_hlen); 170 hdr->csum_offset = __cpu_to_virtio16(little_endian, 171 skb->csum_offset); 172 } else if (has_data_valid && 173 skb->ip_summed == CHECKSUM_UNNECESSARY) { 174 hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID; 175 } /* else everything is zero */ 176 177 return 0; 178 } 179 180 #endif /* _LINUX_VIRTIO_NET_H */ 181