cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

virtio_net.h (5512B)


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