Back to home page

OSCL-LXR

 
 

    


0001 /* SPDX-License-Identifier: GPL-2.0 */
0002 #ifndef _LINUX_VIRTIO_NET_H
0003 #define _LINUX_VIRTIO_NET_H
0004 
0005 #include <linux/if_vlan.h>
0006 #include <uapi/linux/tcp.h>
0007 #include <uapi/linux/udp.h>
0008 #include <uapi/linux/virtio_net.h>
0009 
0010 static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type)
0011 {
0012     switch (gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
0013     case VIRTIO_NET_HDR_GSO_TCPV4:
0014         return protocol == cpu_to_be16(ETH_P_IP);
0015     case VIRTIO_NET_HDR_GSO_TCPV6:
0016         return protocol == cpu_to_be16(ETH_P_IPV6);
0017     case VIRTIO_NET_HDR_GSO_UDP:
0018         return protocol == cpu_to_be16(ETH_P_IP) ||
0019                protocol == cpu_to_be16(ETH_P_IPV6);
0020     default:
0021         return false;
0022     }
0023 }
0024 
0025 static inline int virtio_net_hdr_set_proto(struct sk_buff *skb,
0026                        const struct virtio_net_hdr *hdr)
0027 {
0028     if (skb->protocol)
0029         return 0;
0030 
0031     switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
0032     case VIRTIO_NET_HDR_GSO_TCPV4:
0033     case VIRTIO_NET_HDR_GSO_UDP:
0034         skb->protocol = cpu_to_be16(ETH_P_IP);
0035         break;
0036     case VIRTIO_NET_HDR_GSO_TCPV6:
0037         skb->protocol = cpu_to_be16(ETH_P_IPV6);
0038         break;
0039     default:
0040         return -EINVAL;
0041     }
0042 
0043     return 0;
0044 }
0045 
0046 static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
0047                     const struct virtio_net_hdr *hdr,
0048                     bool little_endian)
0049 {
0050     unsigned int gso_type = 0;
0051     unsigned int thlen = 0;
0052     unsigned int p_off = 0;
0053     unsigned int ip_proto;
0054 
0055     if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
0056         switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
0057         case VIRTIO_NET_HDR_GSO_TCPV4:
0058             gso_type = SKB_GSO_TCPV4;
0059             ip_proto = IPPROTO_TCP;
0060             thlen = sizeof(struct tcphdr);
0061             break;
0062         case VIRTIO_NET_HDR_GSO_TCPV6:
0063             gso_type = SKB_GSO_TCPV6;
0064             ip_proto = IPPROTO_TCP;
0065             thlen = sizeof(struct tcphdr);
0066             break;
0067         case VIRTIO_NET_HDR_GSO_UDP:
0068             gso_type = SKB_GSO_UDP;
0069             ip_proto = IPPROTO_UDP;
0070             thlen = sizeof(struct udphdr);
0071             break;
0072         default:
0073             return -EINVAL;
0074         }
0075 
0076         if (hdr->gso_type & VIRTIO_NET_HDR_GSO_ECN)
0077             gso_type |= SKB_GSO_TCP_ECN;
0078 
0079         if (hdr->gso_size == 0)
0080             return -EINVAL;
0081     }
0082 
0083     skb_reset_mac_header(skb);
0084 
0085     if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
0086         u32 start = __virtio16_to_cpu(little_endian, hdr->csum_start);
0087         u32 off = __virtio16_to_cpu(little_endian, hdr->csum_offset);
0088         u32 needed = start + max_t(u32, thlen, off + sizeof(__sum16));
0089 
0090         if (!pskb_may_pull(skb, needed))
0091             return -EINVAL;
0092 
0093         if (!skb_partial_csum_set(skb, start, off))
0094             return -EINVAL;
0095 
0096         p_off = skb_transport_offset(skb) + thlen;
0097         if (!pskb_may_pull(skb, p_off))
0098             return -EINVAL;
0099     } else {
0100         /* gso packets without NEEDS_CSUM do not set transport_offset.
0101          * probe and drop if does not match one of the above types.
0102          */
0103         if (gso_type && skb->network_header) {
0104             struct flow_keys_basic keys;
0105 
0106             if (!skb->protocol) {
0107                 __be16 protocol = dev_parse_header_protocol(skb);
0108 
0109                 if (!protocol)
0110                     virtio_net_hdr_set_proto(skb, hdr);
0111                 else if (!virtio_net_hdr_match_proto(protocol, hdr->gso_type))
0112                     return -EINVAL;
0113                 else
0114                     skb->protocol = protocol;
0115             }
0116 retry:
0117             if (!skb_flow_dissect_flow_keys_basic(NULL, skb, &keys,
0118                                   NULL, 0, 0, 0,
0119                                   0)) {
0120                 /* UFO does not specify ipv4 or 6: try both */
0121                 if (gso_type & SKB_GSO_UDP &&
0122                     skb->protocol == htons(ETH_P_IP)) {
0123                     skb->protocol = htons(ETH_P_IPV6);
0124                     goto retry;
0125                 }
0126                 return -EINVAL;
0127             }
0128 
0129             p_off = keys.control.thoff + thlen;
0130             if (!pskb_may_pull(skb, p_off) ||
0131                 keys.basic.ip_proto != ip_proto)
0132                 return -EINVAL;
0133 
0134             skb_set_transport_header(skb, keys.control.thoff);
0135         } else if (gso_type) {
0136             p_off = thlen;
0137             if (!pskb_may_pull(skb, p_off))
0138                 return -EINVAL;
0139         }
0140     }
0141 
0142     if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
0143         u16 gso_size = __virtio16_to_cpu(little_endian, hdr->gso_size);
0144         unsigned int nh_off = p_off;
0145         struct skb_shared_info *shinfo = skb_shinfo(skb);
0146 
0147         /* UFO may not include transport header in gso_size. */
0148         if (gso_type & SKB_GSO_UDP)
0149             nh_off -= thlen;
0150 
0151         /* Too small packets are not really GSO ones. */
0152         if (skb->len - nh_off > gso_size) {
0153             shinfo->gso_size = gso_size;
0154             shinfo->gso_type = gso_type;
0155 
0156             /* Header must be checked, and gso_segs computed. */
0157             shinfo->gso_type |= SKB_GSO_DODGY;
0158             shinfo->gso_segs = 0;
0159         }
0160     }
0161 
0162     return 0;
0163 }
0164 
0165 static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb,
0166                       struct virtio_net_hdr *hdr,
0167                       bool little_endian,
0168                       bool has_data_valid,
0169                       int vlan_hlen)
0170 {
0171     memset(hdr, 0, sizeof(*hdr));   /* no info leak */
0172 
0173     if (skb_is_gso(skb)) {
0174         struct skb_shared_info *sinfo = skb_shinfo(skb);
0175 
0176         /* This is a hint as to how much should be linear. */
0177         hdr->hdr_len = __cpu_to_virtio16(little_endian,
0178                          skb_headlen(skb));
0179         hdr->gso_size = __cpu_to_virtio16(little_endian,
0180                           sinfo->gso_size);
0181         if (sinfo->gso_type & SKB_GSO_TCPV4)
0182             hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
0183         else if (sinfo->gso_type & SKB_GSO_TCPV6)
0184             hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
0185         else
0186             return -EINVAL;
0187         if (sinfo->gso_type & SKB_GSO_TCP_ECN)
0188             hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN;
0189     } else
0190         hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
0191 
0192     if (skb->ip_summed == CHECKSUM_PARTIAL) {
0193         hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
0194         hdr->csum_start = __cpu_to_virtio16(little_endian,
0195             skb_checksum_start_offset(skb) + vlan_hlen);
0196         hdr->csum_offset = __cpu_to_virtio16(little_endian,
0197                 skb->csum_offset);
0198     } else if (has_data_valid &&
0199            skb->ip_summed == CHECKSUM_UNNECESSARY) {
0200         hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID;
0201     } /* else everything is zero */
0202 
0203     return 0;
0204 }
0205 
0206 #endif /* _LINUX_VIRTIO_NET_H */