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