1*a942fcd7SXu Du /* SPDX-License-Identifier: GPL-2.0-only */ 2*a942fcd7SXu Du 3*a942fcd7SXu Du #ifndef _TUNTAP_HELPERS_H 4*a942fcd7SXu Du #define _TUNTAP_HELPERS_H 5*a942fcd7SXu Du 6*a942fcd7SXu Du #include <errno.h> 7*a942fcd7SXu Du #include <linux/if_packet.h> 8*a942fcd7SXu Du #include <linux/ipv6.h> 9*a942fcd7SXu Du #include <linux/virtio_net.h> 10*a942fcd7SXu Du #include <netinet/in.h> 11*a942fcd7SXu Du #include <netinet/if_ether.h> 12*a942fcd7SXu Du #include <netinet/udp.h> 13*a942fcd7SXu Du #include <stdio.h> 14*a942fcd7SXu Du #include <stdlib.h> 15*a942fcd7SXu Du #include <string.h> 16*a942fcd7SXu Du #include <unistd.h> 17*a942fcd7SXu Du #include <ynl.h> 18*a942fcd7SXu Du 19*a942fcd7SXu Du #include "rt-route-user.h" 20*a942fcd7SXu Du #include "rt-addr-user.h" 21*a942fcd7SXu Du #include "rt-neigh-user.h" 22*a942fcd7SXu Du #include "rt-link-user.h" 23*a942fcd7SXu Du 24*a942fcd7SXu Du #define GENEVE_HLEN 8 25*a942fcd7SXu Du #define PKT_DATA 0xCB 26*a942fcd7SXu Du #define TUNTAP_DEFAULT_TTL 8 27*a942fcd7SXu Du #define TUNTAP_DEFAULT_IPID 1337 28*a942fcd7SXu Du 29*a942fcd7SXu Du unsigned int if_nametoindex(const char *ifname); 30*a942fcd7SXu Du 31*a942fcd7SXu Du static inline int ip_addr_len(int family) 32*a942fcd7SXu Du { 33*a942fcd7SXu Du return (family == AF_INET) ? sizeof(struct in_addr) : 34*a942fcd7SXu Du sizeof(struct in6_addr); 35*a942fcd7SXu Du } 36*a942fcd7SXu Du 37*a942fcd7SXu Du static inline void fill_ifaddr_msg(struct ifaddrmsg *ifam, int family, 38*a942fcd7SXu Du int prefix, int flags, const char *dev) 39*a942fcd7SXu Du { 40*a942fcd7SXu Du ifam->ifa_family = family; 41*a942fcd7SXu Du ifam->ifa_prefixlen = prefix; 42*a942fcd7SXu Du ifam->ifa_index = if_nametoindex(dev); 43*a942fcd7SXu Du ifam->ifa_flags = flags; 44*a942fcd7SXu Du ifam->ifa_scope = RT_SCOPE_UNIVERSE; 45*a942fcd7SXu Du } 46*a942fcd7SXu Du 47*a942fcd7SXu Du static inline int ip_addr_add(const char *dev, int family, void *addr, 48*a942fcd7SXu Du uint8_t prefix) 49*a942fcd7SXu Du { 50*a942fcd7SXu Du int nl_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; 51*a942fcd7SXu Du int ifa_flags = IFA_F_PERMANENT | IFA_F_NODAD; 52*a942fcd7SXu Du int ret = -1, ipalen = ip_addr_len(family); 53*a942fcd7SXu Du struct rt_addr_newaddr_req *req; 54*a942fcd7SXu Du struct ynl_sock *ys; 55*a942fcd7SXu Du 56*a942fcd7SXu Du ys = ynl_sock_create(&ynl_rt_addr_family, NULL); 57*a942fcd7SXu Du if (!ys) 58*a942fcd7SXu Du return -1; 59*a942fcd7SXu Du 60*a942fcd7SXu Du req = rt_addr_newaddr_req_alloc(); 61*a942fcd7SXu Du if (!req) 62*a942fcd7SXu Du goto err_req_alloc; 63*a942fcd7SXu Du 64*a942fcd7SXu Du fill_ifaddr_msg(&req->_hdr, family, prefix, ifa_flags, dev); 65*a942fcd7SXu Du rt_addr_newaddr_req_set_nlflags(req, nl_flags); 66*a942fcd7SXu Du rt_addr_newaddr_req_set_local(req, addr, ipalen); 67*a942fcd7SXu Du 68*a942fcd7SXu Du ret = rt_addr_newaddr(ys, req); 69*a942fcd7SXu Du rt_addr_newaddr_req_free(req); 70*a942fcd7SXu Du err_req_alloc: 71*a942fcd7SXu Du ynl_sock_destroy(ys); 72*a942fcd7SXu Du return ret; 73*a942fcd7SXu Du } 74*a942fcd7SXu Du 75*a942fcd7SXu Du static inline void fill_neigh_req_header(struct ndmsg *ndm, int family, 76*a942fcd7SXu Du int state, const char *dev) 77*a942fcd7SXu Du { 78*a942fcd7SXu Du ndm->ndm_family = family; 79*a942fcd7SXu Du ndm->ndm_ifindex = if_nametoindex(dev); 80*a942fcd7SXu Du ndm->ndm_state = state; 81*a942fcd7SXu Du ndm->ndm_flags = 0; 82*a942fcd7SXu Du ndm->ndm_type = RTN_UNICAST; 83*a942fcd7SXu Du } 84*a942fcd7SXu Du 85*a942fcd7SXu Du static inline int ip_neigh_add(const char *dev, int family, void *addr, 86*a942fcd7SXu Du unsigned char *lladdr) 87*a942fcd7SXu Du { 88*a942fcd7SXu Du int nl_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; 89*a942fcd7SXu Du int ret = -1, ipalen = ip_addr_len(family); 90*a942fcd7SXu Du struct rt_neigh_newneigh_req *req; 91*a942fcd7SXu Du struct ynl_sock *ys; 92*a942fcd7SXu Du 93*a942fcd7SXu Du ys = ynl_sock_create(&ynl_rt_neigh_family, NULL); 94*a942fcd7SXu Du if (!ys) 95*a942fcd7SXu Du return -1; 96*a942fcd7SXu Du 97*a942fcd7SXu Du req = rt_neigh_newneigh_req_alloc(); 98*a942fcd7SXu Du if (!req) 99*a942fcd7SXu Du goto err_req_alloc; 100*a942fcd7SXu Du 101*a942fcd7SXu Du fill_neigh_req_header(&req->_hdr, family, NUD_PERMANENT, dev); 102*a942fcd7SXu Du rt_neigh_newneigh_req_set_nlflags(req, nl_flags); 103*a942fcd7SXu Du rt_neigh_newneigh_req_set_dst(req, addr, ipalen); 104*a942fcd7SXu Du rt_neigh_newneigh_req_set_lladdr(req, lladdr, ETH_ALEN); 105*a942fcd7SXu Du rt_neigh_newneigh_req_set_ifindex(req, if_nametoindex(dev)); 106*a942fcd7SXu Du 107*a942fcd7SXu Du ret = rt_neigh_newneigh(ys, req); 108*a942fcd7SXu Du rt_neigh_newneigh_req_free(req); 109*a942fcd7SXu Du err_req_alloc: 110*a942fcd7SXu Du ynl_sock_destroy(ys); 111*a942fcd7SXu Du return ret; 112*a942fcd7SXu Du } 113*a942fcd7SXu Du 114*a942fcd7SXu Du static inline void fill_route_req_header(struct rtmsg *rtm, int family, 115*a942fcd7SXu Du int table) 116*a942fcd7SXu Du { 117*a942fcd7SXu Du rtm->rtm_family = family; 118*a942fcd7SXu Du rtm->rtm_table = table; 119*a942fcd7SXu Du } 120*a942fcd7SXu Du 121*a942fcd7SXu Du static inline int 122*a942fcd7SXu Du ip_route_get(const char *dev, int family, int table, void *dst, 123*a942fcd7SXu Du void (*parse_rsp)(struct rt_route_getroute_rsp *rsp, void *out), 124*a942fcd7SXu Du void *out) 125*a942fcd7SXu Du { 126*a942fcd7SXu Du int ret = -1, ipalen = ip_addr_len(family); 127*a942fcd7SXu Du struct rt_route_getroute_req *req; 128*a942fcd7SXu Du struct rt_route_getroute_rsp *rsp; 129*a942fcd7SXu Du struct ynl_sock *ys; 130*a942fcd7SXu Du 131*a942fcd7SXu Du ys = ynl_sock_create(&ynl_rt_route_family, NULL); 132*a942fcd7SXu Du if (!ys) 133*a942fcd7SXu Du return -1; 134*a942fcd7SXu Du 135*a942fcd7SXu Du req = rt_route_getroute_req_alloc(); 136*a942fcd7SXu Du if (!req) 137*a942fcd7SXu Du goto err_req_alloc; 138*a942fcd7SXu Du 139*a942fcd7SXu Du fill_route_req_header(&req->_hdr, family, table); 140*a942fcd7SXu Du rt_route_getroute_req_set_nlflags(req, NLM_F_REQUEST); 141*a942fcd7SXu Du rt_route_getroute_req_set_dst(req, dst, ipalen); 142*a942fcd7SXu Du rt_route_getroute_req_set_oif(req, if_nametoindex(dev)); 143*a942fcd7SXu Du 144*a942fcd7SXu Du rsp = rt_route_getroute(ys, req); 145*a942fcd7SXu Du if (!rsp) 146*a942fcd7SXu Du goto err_rsp_get; 147*a942fcd7SXu Du 148*a942fcd7SXu Du ret = 0; 149*a942fcd7SXu Du if (parse_rsp) 150*a942fcd7SXu Du parse_rsp(rsp, out); 151*a942fcd7SXu Du 152*a942fcd7SXu Du rt_route_getroute_rsp_free(rsp); 153*a942fcd7SXu Du err_rsp_get: 154*a942fcd7SXu Du rt_route_getroute_req_free(req); 155*a942fcd7SXu Du err_req_alloc: 156*a942fcd7SXu Du ynl_sock_destroy(ys); 157*a942fcd7SXu Du return ret; 158*a942fcd7SXu Du } 159*a942fcd7SXu Du 160*a942fcd7SXu Du static inline int 161*a942fcd7SXu Du ip_link_add(const char *dev, char *link_type, 162*a942fcd7SXu Du int (*fill_link_attr)(struct rt_link_newlink_req *req, void *data), 163*a942fcd7SXu Du void *data) 164*a942fcd7SXu Du { 165*a942fcd7SXu Du int nl_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; 166*a942fcd7SXu Du struct rt_link_newlink_req *req; 167*a942fcd7SXu Du struct ynl_sock *ys; 168*a942fcd7SXu Du int ret = -1; 169*a942fcd7SXu Du 170*a942fcd7SXu Du ys = ynl_sock_create(&ynl_rt_link_family, NULL); 171*a942fcd7SXu Du if (!ys) 172*a942fcd7SXu Du return -1; 173*a942fcd7SXu Du 174*a942fcd7SXu Du req = rt_link_newlink_req_alloc(); 175*a942fcd7SXu Du if (!req) 176*a942fcd7SXu Du goto err_req_alloc; 177*a942fcd7SXu Du 178*a942fcd7SXu Du req->_hdr.ifi_flags = IFF_UP; 179*a942fcd7SXu Du rt_link_newlink_req_set_nlflags(req, nl_flags); 180*a942fcd7SXu Du rt_link_newlink_req_set_ifname(req, dev); 181*a942fcd7SXu Du rt_link_newlink_req_set_linkinfo_kind(req, link_type); 182*a942fcd7SXu Du 183*a942fcd7SXu Du if (fill_link_attr && fill_link_attr(req, data) < 0) 184*a942fcd7SXu Du goto err_attr_fill; 185*a942fcd7SXu Du 186*a942fcd7SXu Du ret = rt_link_newlink(ys, req); 187*a942fcd7SXu Du err_attr_fill: 188*a942fcd7SXu Du rt_link_newlink_req_free(req); 189*a942fcd7SXu Du err_req_alloc: 190*a942fcd7SXu Du ynl_sock_destroy(ys); 191*a942fcd7SXu Du return ret; 192*a942fcd7SXu Du } 193*a942fcd7SXu Du 194*a942fcd7SXu Du static inline int ip_link_del(const char *dev) 195*a942fcd7SXu Du { 196*a942fcd7SXu Du struct rt_link_dellink_req *req; 197*a942fcd7SXu Du struct ynl_sock *ys; 198*a942fcd7SXu Du int ret = -1; 199*a942fcd7SXu Du 200*a942fcd7SXu Du ys = ynl_sock_create(&ynl_rt_link_family, NULL); 201*a942fcd7SXu Du if (!ys) 202*a942fcd7SXu Du return -1; 203*a942fcd7SXu Du 204*a942fcd7SXu Du req = rt_link_dellink_req_alloc(); 205*a942fcd7SXu Du if (!req) 206*a942fcd7SXu Du goto err_req_alloc; 207*a942fcd7SXu Du 208*a942fcd7SXu Du rt_link_dellink_req_set_nlflags(req, NLM_F_REQUEST); 209*a942fcd7SXu Du rt_link_dellink_req_set_ifname(req, dev); 210*a942fcd7SXu Du 211*a942fcd7SXu Du ret = rt_link_dellink(ys, req); 212*a942fcd7SXu Du rt_link_dellink_req_free(req); 213*a942fcd7SXu Du err_req_alloc: 214*a942fcd7SXu Du ynl_sock_destroy(ys); 215*a942fcd7SXu Du return ret; 216*a942fcd7SXu Du } 217*a942fcd7SXu Du 218*a942fcd7SXu Du static inline size_t build_eth(uint8_t *buf, uint16_t proto, unsigned char *src, 219*a942fcd7SXu Du unsigned char *dest) 220*a942fcd7SXu Du { 221*a942fcd7SXu Du struct ethhdr *eth = (struct ethhdr *)buf; 222*a942fcd7SXu Du 223*a942fcd7SXu Du eth->h_proto = htons(proto); 224*a942fcd7SXu Du memcpy(eth->h_source, src, ETH_ALEN); 225*a942fcd7SXu Du memcpy(eth->h_dest, dest, ETH_ALEN); 226*a942fcd7SXu Du 227*a942fcd7SXu Du return ETH_HLEN; 228*a942fcd7SXu Du } 229*a942fcd7SXu Du 230*a942fcd7SXu Du static inline uint32_t add_csum(const uint8_t *buf, int len) 231*a942fcd7SXu Du { 232*a942fcd7SXu Du uint16_t *sbuf = (uint16_t *)buf; 233*a942fcd7SXu Du uint32_t sum = 0; 234*a942fcd7SXu Du 235*a942fcd7SXu Du while (len > 1) { 236*a942fcd7SXu Du sum += *sbuf++; 237*a942fcd7SXu Du len -= 2; 238*a942fcd7SXu Du } 239*a942fcd7SXu Du 240*a942fcd7SXu Du if (len) 241*a942fcd7SXu Du sum += *(uint8_t *)sbuf; 242*a942fcd7SXu Du 243*a942fcd7SXu Du return sum; 244*a942fcd7SXu Du } 245*a942fcd7SXu Du 246*a942fcd7SXu Du static inline uint16_t finish_ip_csum(uint32_t sum) 247*a942fcd7SXu Du { 248*a942fcd7SXu Du while (sum >> 16) 249*a942fcd7SXu Du sum = (sum & 0xffff) + (sum >> 16); 250*a942fcd7SXu Du return ~((uint16_t)sum); 251*a942fcd7SXu Du } 252*a942fcd7SXu Du 253*a942fcd7SXu Du static inline uint16_t build_ip_csum(const uint8_t *buf, int len, uint32_t sum) 254*a942fcd7SXu Du { 255*a942fcd7SXu Du sum += add_csum(buf, len); 256*a942fcd7SXu Du return finish_ip_csum(sum); 257*a942fcd7SXu Du } 258*a942fcd7SXu Du 259*a942fcd7SXu Du static inline int build_ipv4_header(uint8_t *buf, uint8_t proto, 260*a942fcd7SXu Du int payload_len, struct in_addr *src, 261*a942fcd7SXu Du struct in_addr *dst) 262*a942fcd7SXu Du { 263*a942fcd7SXu Du struct iphdr *iph = (struct iphdr *)buf; 264*a942fcd7SXu Du 265*a942fcd7SXu Du iph->ihl = 5; 266*a942fcd7SXu Du iph->version = 4; 267*a942fcd7SXu Du iph->ttl = TUNTAP_DEFAULT_TTL; 268*a942fcd7SXu Du iph->tot_len = htons(sizeof(*iph) + payload_len); 269*a942fcd7SXu Du iph->id = htons(TUNTAP_DEFAULT_IPID); 270*a942fcd7SXu Du iph->protocol = proto; 271*a942fcd7SXu Du iph->saddr = src->s_addr; 272*a942fcd7SXu Du iph->daddr = dst->s_addr; 273*a942fcd7SXu Du iph->check = build_ip_csum(buf, iph->ihl << 2, 0); 274*a942fcd7SXu Du 275*a942fcd7SXu Du return iph->ihl << 2; 276*a942fcd7SXu Du } 277*a942fcd7SXu Du 278*a942fcd7SXu Du static inline void ipv6_set_dsfield(struct ipv6hdr *ip6h, uint8_t dsfield) 279*a942fcd7SXu Du { 280*a942fcd7SXu Du uint16_t val, *ptr = (uint16_t *)ip6h; 281*a942fcd7SXu Du 282*a942fcd7SXu Du val = ntohs(*ptr); 283*a942fcd7SXu Du val &= 0xF00F; 284*a942fcd7SXu Du val |= ((uint16_t)dsfield) << 4; 285*a942fcd7SXu Du *ptr = htons(val); 286*a942fcd7SXu Du } 287*a942fcd7SXu Du 288*a942fcd7SXu Du static inline int build_ipv6_header(uint8_t *buf, uint8_t proto, 289*a942fcd7SXu Du uint8_t dsfield, int payload_len, 290*a942fcd7SXu Du struct in6_addr *src, struct in6_addr *dst) 291*a942fcd7SXu Du { 292*a942fcd7SXu Du struct ipv6hdr *ip6h = (struct ipv6hdr *)buf; 293*a942fcd7SXu Du 294*a942fcd7SXu Du ip6h->version = 6; 295*a942fcd7SXu Du ip6h->payload_len = htons(payload_len); 296*a942fcd7SXu Du ip6h->nexthdr = proto; 297*a942fcd7SXu Du ip6h->hop_limit = TUNTAP_DEFAULT_TTL; 298*a942fcd7SXu Du ipv6_set_dsfield(ip6h, dsfield); 299*a942fcd7SXu Du memcpy(&ip6h->saddr, src, sizeof(ip6h->saddr)); 300*a942fcd7SXu Du memcpy(&ip6h->daddr, dst, sizeof(ip6h->daddr)); 301*a942fcd7SXu Du 302*a942fcd7SXu Du return sizeof(struct ipv6hdr); 303*a942fcd7SXu Du } 304*a942fcd7SXu Du 305*a942fcd7SXu Du static inline int build_geneve_header(uint8_t *buf, uint32_t vni) 306*a942fcd7SXu Du { 307*a942fcd7SXu Du uint16_t protocol = htons(ETH_P_TEB); 308*a942fcd7SXu Du uint32_t geneve_vni = htonl((vni << 8) & 0xffffff00); 309*a942fcd7SXu Du 310*a942fcd7SXu Du memcpy(buf + 2, &protocol, 2); 311*a942fcd7SXu Du memcpy(buf + 4, &geneve_vni, 4); 312*a942fcd7SXu Du return GENEVE_HLEN; 313*a942fcd7SXu Du } 314*a942fcd7SXu Du 315*a942fcd7SXu Du static inline int build_udp_header(uint8_t *buf, uint16_t sport, uint16_t dport, 316*a942fcd7SXu Du int payload_len) 317*a942fcd7SXu Du { 318*a942fcd7SXu Du struct udphdr *udph = (struct udphdr *)buf; 319*a942fcd7SXu Du 320*a942fcd7SXu Du udph->source = htons(sport); 321*a942fcd7SXu Du udph->dest = htons(dport); 322*a942fcd7SXu Du udph->len = htons(sizeof(*udph) + payload_len); 323*a942fcd7SXu Du return sizeof(*udph); 324*a942fcd7SXu Du } 325*a942fcd7SXu Du 326*a942fcd7SXu Du static inline void build_udp_packet_csum(uint8_t *buf, int family, 327*a942fcd7SXu Du bool csum_off) 328*a942fcd7SXu Du { 329*a942fcd7SXu Du struct udphdr *udph = (struct udphdr *)buf; 330*a942fcd7SXu Du size_t ipalen = ip_addr_len(family); 331*a942fcd7SXu Du uint32_t sum; 332*a942fcd7SXu Du 333*a942fcd7SXu Du /* No extension IPv4 and IPv6 headers addresses are the last fields */ 334*a942fcd7SXu Du sum = add_csum(buf - 2 * ipalen, 2 * ipalen); 335*a942fcd7SXu Du sum += htons(IPPROTO_UDP) + udph->len; 336*a942fcd7SXu Du 337*a942fcd7SXu Du if (!csum_off) 338*a942fcd7SXu Du sum += add_csum(buf, udph->len); 339*a942fcd7SXu Du 340*a942fcd7SXu Du udph->check = finish_ip_csum(sum); 341*a942fcd7SXu Du } 342*a942fcd7SXu Du 343*a942fcd7SXu Du static inline int build_udp_packet(uint8_t *buf, uint16_t sport, uint16_t dport, 344*a942fcd7SXu Du int payload_len, int family, bool csum_off) 345*a942fcd7SXu Du { 346*a942fcd7SXu Du struct udphdr *udph = (struct udphdr *)buf; 347*a942fcd7SXu Du 348*a942fcd7SXu Du build_udp_header(buf, sport, dport, payload_len); 349*a942fcd7SXu Du memset(buf + sizeof(*udph), PKT_DATA, payload_len); 350*a942fcd7SXu Du build_udp_packet_csum(buf, family, csum_off); 351*a942fcd7SXu Du 352*a942fcd7SXu Du return sizeof(*udph) + payload_len; 353*a942fcd7SXu Du } 354*a942fcd7SXu Du 355*a942fcd7SXu Du static inline int build_virtio_net_hdr_v1_hash_tunnel(uint8_t *buf, bool is_tap, 356*a942fcd7SXu Du int hdr_len, int gso_size, 357*a942fcd7SXu Du int outer_family, 358*a942fcd7SXu Du int inner_family) 359*a942fcd7SXu Du { 360*a942fcd7SXu Du struct virtio_net_hdr_v1_hash_tunnel *vh_tunnel = (void *)buf; 361*a942fcd7SXu Du struct virtio_net_hdr_v1 *vh = &vh_tunnel->hash_hdr.hdr; 362*a942fcd7SXu Du int outer_iphlen, inner_iphlen, eth_hlen, gso_type; 363*a942fcd7SXu Du 364*a942fcd7SXu Du eth_hlen = is_tap ? ETH_HLEN : 0; 365*a942fcd7SXu Du outer_iphlen = (outer_family == AF_INET) ? sizeof(struct iphdr) : 366*a942fcd7SXu Du sizeof(struct ipv6hdr); 367*a942fcd7SXu Du inner_iphlen = (inner_family == AF_INET) ? sizeof(struct iphdr) : 368*a942fcd7SXu Du sizeof(struct ipv6hdr); 369*a942fcd7SXu Du 370*a942fcd7SXu Du vh_tunnel->outer_th_offset = eth_hlen + outer_iphlen; 371*a942fcd7SXu Du vh_tunnel->inner_nh_offset = vh_tunnel->outer_th_offset + ETH_HLEN + 372*a942fcd7SXu Du GENEVE_HLEN + sizeof(struct udphdr); 373*a942fcd7SXu Du 374*a942fcd7SXu Du vh->csum_start = vh_tunnel->inner_nh_offset + inner_iphlen; 375*a942fcd7SXu Du vh->csum_offset = __builtin_offsetof(struct udphdr, check); 376*a942fcd7SXu Du vh->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; 377*a942fcd7SXu Du vh->hdr_len = hdr_len; 378*a942fcd7SXu Du vh->gso_size = gso_size; 379*a942fcd7SXu Du 380*a942fcd7SXu Du if (gso_size) { 381*a942fcd7SXu Du gso_type = outer_family == AF_INET ? 382*a942fcd7SXu Du VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV4 : 383*a942fcd7SXu Du VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6; 384*a942fcd7SXu Du vh->gso_type = VIRTIO_NET_HDR_GSO_UDP_L4 | gso_type; 385*a942fcd7SXu Du } 386*a942fcd7SXu Du 387*a942fcd7SXu Du return sizeof(struct virtio_net_hdr_v1_hash_tunnel); 388*a942fcd7SXu Du } 389*a942fcd7SXu Du 390*a942fcd7SXu Du #endif /* _TUNTAP_HELPERS_H */ 391