Branch data 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/virtio_net.h> 7 : : 8 : 0 : static inline int virtio_net_hdr_set_proto(struct sk_buff *skb, 9 : : const struct virtio_net_hdr *hdr) 10 : : { 11 [ # # # # : 0 : switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { # # ] 12 : 0 : case VIRTIO_NET_HDR_GSO_TCPV4: 13 : : case VIRTIO_NET_HDR_GSO_UDP: 14 : 0 : skb->protocol = cpu_to_be16(ETH_P_IP); 15 : 0 : break; 16 : 0 : case VIRTIO_NET_HDR_GSO_TCPV6: 17 : 0 : skb->protocol = cpu_to_be16(ETH_P_IPV6); 18 : 0 : break; 19 : : default: 20 : : return -EINVAL; 21 : : } 22 : : 23 : : return 0; 24 : : } 25 : : 26 : 0 : static inline int virtio_net_hdr_to_skb(struct sk_buff *skb, 27 : : const struct virtio_net_hdr *hdr, 28 : : bool little_endian) 29 : : { 30 : 0 : unsigned int gso_type = 0; 31 : : 32 [ # # ]: 0 : if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { 33 [ # # # # ]: 0 : switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { 34 : : case VIRTIO_NET_HDR_GSO_TCPV4: 35 : : gso_type = SKB_GSO_TCPV4; 36 : : break; 37 : 0 : case VIRTIO_NET_HDR_GSO_TCPV6: 38 : 0 : gso_type = SKB_GSO_TCPV6; 39 : 0 : break; 40 : 0 : case VIRTIO_NET_HDR_GSO_UDP: 41 : 0 : gso_type = SKB_GSO_UDP; 42 : 0 : break; 43 : : default: 44 : : return -EINVAL; 45 : : } 46 : : 47 [ # # ]: 0 : if (hdr->gso_type & VIRTIO_NET_HDR_GSO_ECN) 48 : 0 : gso_type |= SKB_GSO_TCP_ECN; 49 : : 50 [ # # ]: 0 : if (hdr->gso_size == 0) 51 : : return -EINVAL; 52 : : } 53 : : 54 [ # # ]: 0 : if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { 55 [ # # ]: 0 : u16 start = __virtio16_to_cpu(little_endian, hdr->csum_start); 56 [ # # ]: 0 : u16 off = __virtio16_to_cpu(little_endian, hdr->csum_offset); 57 : : 58 [ # # ]: 0 : if (!skb_partial_csum_set(skb, start, off)) 59 : : return -EINVAL; 60 : : } else { 61 : : /* gso packets without NEEDS_CSUM do not set transport_offset. 62 : : * probe and drop if does not match one of the above types. 63 : : */ 64 [ # # # # ]: 0 : if (gso_type && skb->network_header) { 65 [ # # ]: 0 : if (!skb->protocol) 66 [ # # # ]: 0 : virtio_net_hdr_set_proto(skb, hdr); 67 : 0 : retry: 68 : 0 : skb_probe_transport_header(skb); 69 [ # # ]: 0 : if (!skb_transport_header_was_set(skb)) { 70 : : /* UFO does not specify ipv4 or 6: try both */ 71 [ # # ]: 0 : if (gso_type & SKB_GSO_UDP && 72 [ # # ]: 0 : skb->protocol == htons(ETH_P_IP)) { 73 : 0 : skb->protocol = htons(ETH_P_IPV6); 74 : 0 : goto retry; 75 : : } 76 : : return -EINVAL; 77 : : } 78 : : } 79 : : } 80 : : 81 [ # # ]: 0 : if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { 82 [ # # ]: 0 : u16 gso_size = __virtio16_to_cpu(little_endian, hdr->gso_size); 83 : : 84 : 0 : skb_shinfo(skb)->gso_size = gso_size; 85 : 0 : skb_shinfo(skb)->gso_type = gso_type; 86 : : 87 : : /* Header must be checked, and gso_segs computed. */ 88 : 0 : skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY; 89 : 0 : skb_shinfo(skb)->gso_segs = 0; 90 : : } 91 : : 92 : : return 0; 93 : : } 94 : : 95 : 0 : static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb, 96 : : struct virtio_net_hdr *hdr, 97 : : bool little_endian, 98 : : bool has_data_valid, 99 : : int vlan_hlen) 100 : : { 101 : 0 : memset(hdr, 0, sizeof(*hdr)); /* no info leak */ 102 : : 103 [ # # ]: 0 : if (skb_is_gso(skb)) { 104 [ # # ]: 0 : struct skb_shared_info *sinfo = skb_shinfo(skb); 105 : : 106 : : /* This is a hint as to how much should be linear. */ 107 : 0 : hdr->hdr_len = __cpu_to_virtio16(little_endian, 108 [ # # ]: 0 : skb_headlen(skb)); 109 : 0 : hdr->gso_size = __cpu_to_virtio16(little_endian, 110 [ # # ]: 0 : sinfo->gso_size); 111 [ # # ]: 0 : if (sinfo->gso_type & SKB_GSO_TCPV4) 112 : 0 : hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; 113 [ # # ]: 0 : else if (sinfo->gso_type & SKB_GSO_TCPV6) 114 : 0 : hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; 115 : : else 116 : : return -EINVAL; 117 [ # # ]: 0 : if (sinfo->gso_type & SKB_GSO_TCP_ECN) 118 : 0 : hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN; 119 : : } else 120 : 0 : hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE; 121 : : 122 [ # # ]: 0 : if (skb->ip_summed == CHECKSUM_PARTIAL) { 123 : 0 : hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; 124 [ # # ]: 0 : hdr->csum_start = __cpu_to_virtio16(little_endian, 125 [ # # ]: 0 : skb_checksum_start_offset(skb) + vlan_hlen); 126 : 0 : hdr->csum_offset = __cpu_to_virtio16(little_endian, 127 [ # # ]: 0 : skb->csum_offset); 128 [ # # # # ]: 0 : } else if (has_data_valid && 129 : : skb->ip_summed == CHECKSUM_UNNECESSARY) { 130 : 0 : hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID; 131 : : } /* else everything is zero */ 132 : : 133 : : return 0; 134 : : } 135 : : 136 : : #endif /* _LINUX_VIRTIO_NET_H */