LCOV - code coverage report
Current view: top level - include/linux - virtio_net.h (source / functions) Hit Total Coverage
Test: landlock.info Lines: 15 84 17.9 %
Date: 2021-04-22 12:43:58 Functions: 2 3 66.7 %

          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 */

Generated by: LCOV version 1.14