0001
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
0101
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
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
0148 if (gso_type & SKB_GSO_UDP)
0149 nh_off -= thlen;
0150
0151
0152 if (skb->len - nh_off > gso_size) {
0153 shinfo->gso_size = gso_size;
0154 shinfo->gso_type = gso_type;
0155
0156
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));
0172
0173 if (skb_is_gso(skb)) {
0174 struct skb_shared_info *sinfo = skb_shinfo(skb);
0175
0176
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 }
0202
0203 return 0;
0204 }
0205
0206 #endif