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