diff options
Diffstat (limited to 'drivers/net/geneve.c')
| -rw-r--r-- | drivers/net/geneve.c | 557 |
1 files changed, 523 insertions, 34 deletions
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 77b0c3d52041..0949d4579171 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -38,6 +38,26 @@ MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); #define GENEVE_IPV4_HLEN (ETH_HLEN + sizeof(struct iphdr) + GENEVE_BASE_HLEN) #define GENEVE_IPV6_HLEN (ETH_HLEN + sizeof(struct ipv6hdr) + GENEVE_BASE_HLEN) +#define GENEVE_OPT_NETDEV_CLASS 0x100 +#define GENEVE_OPT_GRO_HINT_SIZE 8 +#define GENEVE_OPT_GRO_HINT_TYPE 1 +#define GENEVE_OPT_GRO_HINT_LEN 1 + +struct geneve_opt_gro_hint { + u8 inner_proto_id:2, + nested_is_v6:1; + u8 nested_nh_offset; + u8 nested_tp_offset; + u8 nested_hdr_len; +}; + +struct geneve_skb_cb { + unsigned int gro_hint_len; + struct geneve_opt_gro_hint gro_hint; +}; + +#define GENEVE_SKB_CB(__skb) ((struct geneve_skb_cb *)&((__skb)->cb[0])) + /* per-network namespace private data for this module */ struct geneve_net { struct list_head geneve_list; @@ -56,6 +76,7 @@ struct geneve_config { bool collect_md; bool use_udp6_rx_checksums; bool ttl_inherit; + bool gro_hint; enum ifla_geneve_df df; bool inner_proto_inherit; u16 port_min; @@ -84,6 +105,7 @@ struct geneve_dev { struct geneve_sock { bool collect_md; + bool gro_hint; struct list_head list; struct socket *sock; struct rcu_head rcu; @@ -91,6 +113,21 @@ struct geneve_sock { struct hlist_head vni_list[VNI_HASH_SIZE]; }; +static const __be16 proto_id_map[] = { htons(ETH_P_TEB), + htons(ETH_P_IPV6), + htons(ETH_P_IP) }; + +static int proto_to_id(__be16 proto) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(proto_id_map); i++) + if (proto_id_map[i] == proto) + return i; + + return -1; +} + static inline __u32 geneve_net_vni_hash(u8 vni[3]) { __u32 vnid; @@ -222,9 +259,8 @@ static struct geneve_dev *geneve_lookup_skb(struct geneve_sock *gs, /* geneve receive/decap routine */ static void geneve_rx(struct geneve_dev *geneve, struct geneve_sock *gs, - struct sk_buff *skb) + struct sk_buff *skb, const struct genevehdr *gnvh) { - struct genevehdr *gnvh = geneve_hdr(skb); struct metadata_dst *tun_dst = NULL; unsigned int len; int nh, err = 0; @@ -325,8 +361,12 @@ static void geneve_rx(struct geneve_dev *geneve, struct geneve_sock *gs, } } + /* Skip the additional GRO stage when hints are in use. */ len = skb->len; - err = gro_cells_receive(&geneve->gro_cells, skb); + if (skb->encapsulation) + err = netif_rx(skb); + else + err = gro_cells_receive(&geneve->gro_cells, skb); if (likely(err == NET_RX_SUCCESS)) dev_dstats_rx_add(geneve->dev, len); @@ -363,6 +403,250 @@ static void geneve_uninit(struct net_device *dev) gro_cells_destroy(&geneve->gro_cells); } +static int geneve_hlen(const struct genevehdr *gh) +{ + return sizeof(*gh) + gh->opt_len * 4; +} + +/* + * Look for GRO hint in the genenve options; if not found or does not pass basic + * sanitization return 0, otherwise the offset WRT the geneve hdr start. + */ +static unsigned int +geneve_opt_gro_hint_off(const struct genevehdr *gh, __be16 *type, + unsigned int *gh_len) +{ + struct geneve_opt *opt = (void *)(gh + 1); + unsigned int id, opt_len = gh->opt_len; + struct geneve_opt_gro_hint *gro_hint; + + while (opt_len >= (GENEVE_OPT_GRO_HINT_SIZE >> 2)) { + if (opt->opt_class == htons(GENEVE_OPT_NETDEV_CLASS) && + opt->type == GENEVE_OPT_GRO_HINT_TYPE && + opt->length == GENEVE_OPT_GRO_HINT_LEN) + goto found; + + /* check for bad opt len */ + if (opt->length + 1 >= opt_len) + return 0; + + /* next opt */ + opt_len -= opt->length + 1; + opt = ((void *)opt) + ((opt->length + 1) << 2); + } + return 0; + +found: + gro_hint = (struct geneve_opt_gro_hint *)opt->opt_data; + + /* + * Sanitize the hinted hdrs: the nested transport is UDP and must fit + * the overall hinted hdr size. + */ + if (gro_hint->nested_tp_offset + sizeof(struct udphdr) > + gro_hint->nested_hdr_len) + return 0; + + if (gro_hint->nested_nh_offset + + (gro_hint->nested_is_v6 ? sizeof(struct ipv6hdr) : + sizeof(struct iphdr)) > + gro_hint->nested_tp_offset) + return 0; + + /* Allow only supported L2. */ + id = gro_hint->inner_proto_id; + if (id >= ARRAY_SIZE(proto_id_map)) + return 0; + + *type = proto_id_map[id]; + *gh_len += gro_hint->nested_hdr_len; + + return (void *)gro_hint - (void *)gh; +} + +static const struct geneve_opt_gro_hint * +geneve_opt_gro_hint(const struct genevehdr *gh, unsigned int hint_off) +{ + return (const struct geneve_opt_gro_hint *)((void *)gh + hint_off); +} + +static unsigned int +geneve_sk_gro_hint_off(const struct sock *sk, const struct genevehdr *gh, + __be16 *type, unsigned int *gh_len) +{ + const struct geneve_sock *gs = rcu_dereference_sk_user_data(sk); + + if (!gs || !gs->gro_hint) + return 0; + return geneve_opt_gro_hint_off(gh, type, gh_len); +} + +/* Validate the packet headers pointed by data WRT the provided hint */ +static bool +geneve_opt_gro_hint_validate(void *data, + const struct geneve_opt_gro_hint *gro_hint) +{ + void *nested_nh = data + gro_hint->nested_nh_offset; + struct iphdr *iph; + + if (gro_hint->nested_is_v6) { + struct ipv6hdr *ipv6h = nested_nh; + struct ipv6_opt_hdr *opth; + int offset, len; + + if (ipv6h->nexthdr == IPPROTO_UDP) + return true; + + offset = sizeof(*ipv6h) + gro_hint->nested_nh_offset; + while (offset + sizeof(*opth) <= gro_hint->nested_tp_offset) { + opth = data + offset; + + len = ipv6_optlen(opth); + if (len + offset > gro_hint->nested_tp_offset) + return false; + if (opth->nexthdr == IPPROTO_UDP) + return true; + + offset += len; + } + return false; + } + + iph = nested_nh; + if (*(u8 *)iph != 0x45 || ip_is_fragment(iph) || + iph->protocol != IPPROTO_UDP || ip_fast_csum((u8 *)iph, 5)) + return false; + + return true; +} + +/* + * Validate the skb headers following the specified geneve hdr vs the + * provided hint, including nested L4 checksum. + * The caller already ensured that the relevant amount of data is available + * in the linear part. + */ +static bool +geneve_opt_gro_hint_validate_csum(const struct sk_buff *skb, + const struct genevehdr *gh, + const struct geneve_opt_gro_hint *gro_hint) +{ + unsigned int plen, gh_len = geneve_hlen(gh); + void *nested = (void *)gh + gh_len; + struct udphdr *nested_uh; + unsigned int nested_len; + struct ipv6hdr *ipv6h; + struct iphdr *iph; + __wsum csum, psum; + + if (!geneve_opt_gro_hint_validate(nested, gro_hint)) + return false; + + /* Use GRO hints with nested csum only if the outer header has csum. */ + nested_uh = nested + gro_hint->nested_tp_offset; + if (!nested_uh->check || skb->ip_summed == CHECKSUM_PARTIAL) + return true; + + if (!NAPI_GRO_CB(skb)->csum_valid) + return false; + + /* Compute the complete checksum up to the nested transport. */ + plen = gh_len + gro_hint->nested_tp_offset; + csum = csum_sub(NAPI_GRO_CB(skb)->csum, csum_partial(gh, plen, 0)); + nested_len = skb_gro_len(skb) - plen; + + /* Compute the nested pseudo header csum. */ + ipv6h = nested + gro_hint->nested_nh_offset; + iph = (struct iphdr *)ipv6h; + psum = gro_hint->nested_is_v6 ? + ~csum_unfold(csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, + nested_len, IPPROTO_UDP, 0)) : + csum_tcpudp_nofold(iph->saddr, iph->daddr, + nested_len, IPPROTO_UDP, 0); + + return !csum_fold(csum_add(psum, csum)); +} + +static int geneve_post_decap_hint(const struct sock *sk, struct sk_buff *skb, + unsigned int gh_len, + struct genevehdr **geneveh) +{ + const struct geneve_opt_gro_hint *gro_hint; + unsigned int len, total_len, hint_off; + struct ipv6hdr *ipv6h; + struct iphdr *iph; + struct udphdr *uh; + __be16 p; + + hint_off = geneve_sk_gro_hint_off(sk, *geneveh, &p, &len); + if (!hint_off) + return 0; + + if (!skb_is_gso(skb)) + return 0; + + gro_hint = geneve_opt_gro_hint(*geneveh, hint_off); + if (unlikely(!pskb_may_pull(skb, gro_hint->nested_hdr_len))) + return -ENOMEM; + + *geneveh = geneve_hdr(skb); + gro_hint = geneve_opt_gro_hint(*geneveh, hint_off); + + /* + * Validate hints from untrusted source before accessing + * the headers; csum will be checked later by the nested + * protocol rx path. + */ + if (unlikely(skb_shinfo(skb)->gso_type & SKB_GSO_DODGY && + !geneve_opt_gro_hint_validate(skb->data, gro_hint))) + return -EINVAL; + + ipv6h = (void *)skb->data + gro_hint->nested_nh_offset; + iph = (struct iphdr *)ipv6h; + total_len = skb->len - gro_hint->nested_nh_offset; + if (total_len > GRO_LEGACY_MAX_SIZE) + return -E2BIG; + + /* + * After stripping the outer encap, the packet still carries a + * tunnel encapsulation: the nested one. + */ + skb->encapsulation = 1; + + /* GSO expect a valid transpor header, move it to the current one. */ + skb_set_transport_header(skb, gro_hint->nested_tp_offset); + + /* Adjust the nested IP{6} hdr to actual GSO len. */ + if (gro_hint->nested_is_v6) { + ipv6h->payload_len = htons(total_len - sizeof(*ipv6h)); + } else { + __be16 old_len = iph->tot_len; + + iph->tot_len = htons(total_len); + + /* For IPv4 additionally adjust the nested csum. */ + csum_replace2(&iph->check, old_len, iph->tot_len); + ip_send_check(iph); + } + + /* Adjust the nested UDP header len and checksum. */ + uh = udp_hdr(skb); + uh->len = htons(skb->len - gro_hint->nested_tp_offset); + if (uh->check) { + len = skb->len - gro_hint->nested_nh_offset; + skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL_CSUM; + if (gro_hint->nested_is_v6) + uh->check = ~udp_v6_check(len, &ipv6h->saddr, + &ipv6h->daddr, 0); + else + uh->check = ~udp_v4_check(len, iph->saddr, + iph->daddr, 0); + } else { + skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL; + } + return 0; +} + /* Callback from net/ipv4/udp.c to receive packets */ static int geneve_udp_encap_recv(struct sock *sk, struct sk_buff *skb) { @@ -404,7 +688,18 @@ static int geneve_udp_encap_recv(struct sock *sk, struct sk_buff *skb) goto drop; } - geneve_rx(geneve, gs, skb); + /* + * After hint processing, the transport header points to the inner one + * and we can't use anymore on geneve_hdr(). + */ + geneveh = geneve_hdr(skb); + if (geneve_post_decap_hint(sk, skb, sizeof(struct genevehdr) + + opts_len, &geneveh)) { + DEV_STATS_INC(geneve->dev, rx_errors); + goto drop; + } + + geneve_rx(geneve, gs, skb, geneveh); return 0; drop: @@ -495,22 +790,93 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6, return sock; } -static int geneve_hlen(struct genevehdr *gh) +static bool geneve_hdr_match(struct sk_buff *skb, + const struct genevehdr *gh, + const struct genevehdr *gh2, + unsigned int hint_off) { - return sizeof(*gh) + gh->opt_len * 4; + const struct geneve_opt_gro_hint *gro_hint; + void *nested, *nested2, *nh, *nh2; + struct udphdr *udp, *udp2; + unsigned int gh_len; + + /* Match the geneve hdr and options */ + if (gh->opt_len != gh2->opt_len) + return false; + + gh_len = geneve_hlen(gh); + if (memcmp(gh, gh2, gh_len)) + return false; + + if (!hint_off) + return true; + + /* + * When gro is present consider the nested headers as part + * of the geneve options + */ + nested = (void *)gh + gh_len; + nested2 = (void *)gh2 + gh_len; + gro_hint = geneve_opt_gro_hint(gh, hint_off); + if (!memcmp(nested, nested2, gro_hint->nested_hdr_len)) + return true; + + /* + * The nested headers differ; the packets can still belong to + * the same flow when IPs/proto/ports match; if so flushing is + * required. + */ + nh = nested + gro_hint->nested_nh_offset; + nh2 = nested2 + gro_hint->nested_nh_offset; + if (gro_hint->nested_is_v6) { + struct ipv6hdr *iph = nh, *iph2 = nh2; + unsigned int nested_nlen; + __be32 first_word; + + first_word = *(__be32 *)iph ^ *(__be32 *)iph2; + if ((first_word & htonl(0xF00FFFFF)) || + !ipv6_addr_equal(&iph->saddr, &iph2->saddr) || + !ipv6_addr_equal(&iph->daddr, &iph2->daddr) || + iph->nexthdr != iph2->nexthdr) + return false; + + nested_nlen = gro_hint->nested_tp_offset - + gro_hint->nested_nh_offset; + if (nested_nlen > sizeof(struct ipv6hdr) && + (memcmp(iph + 1, iph2 + 1, + nested_nlen - sizeof(struct ipv6hdr)))) + return false; + } else { + struct iphdr *iph = nh, *iph2 = nh2; + + if ((iph->protocol ^ iph2->protocol) | + ((__force u32)iph->saddr ^ (__force u32)iph2->saddr) | + ((__force u32)iph->daddr ^ (__force u32)iph2->daddr)) + return false; + } + + udp = nested + gro_hint->nested_tp_offset; + udp2 = nested2 + gro_hint->nested_tp_offset; + if (udp->source != udp2->source || udp->dest != udp2->dest || + udp->check != udp2->check) + return false; + + NAPI_GRO_CB(skb)->flush = 1; + return true; } static struct sk_buff *geneve_gro_receive(struct sock *sk, struct list_head *head, struct sk_buff *skb) { + unsigned int hlen, gh_len, off_gnv, hint_off; + const struct geneve_opt_gro_hint *gro_hint; + const struct packet_offload *ptype; + struct genevehdr *gh, *gh2; struct sk_buff *pp = NULL; struct sk_buff *p; - struct genevehdr *gh, *gh2; - unsigned int hlen, gh_len, off_gnv; - const struct packet_offload *ptype; - __be16 type; int flush = 1; + __be16 type; off_gnv = skb_gro_offset(skb); hlen = off_gnv + sizeof(*gh); @@ -521,6 +887,7 @@ static struct sk_buff *geneve_gro_receive(struct sock *sk, if (gh->ver != GENEVE_VER || gh->oam) goto out; gh_len = geneve_hlen(gh); + type = gh->proto_type; hlen = off_gnv + gh_len; if (!skb_gro_may_pull(skb, hlen)) { @@ -529,13 +896,30 @@ static struct sk_buff *geneve_gro_receive(struct sock *sk, goto out; } + /* The GRO hint/nested hdr could use a different ethernet type. */ + hint_off = geneve_sk_gro_hint_off(sk, gh, &type, &gh_len); + if (hint_off) { + + /* + * If the hint is present, and nested hdr validation fails, do + * not attempt plain GRO: it will ignore inner hdrs and cause + * OoO. + */ + gh = skb_gro_header(skb, off_gnv + gh_len, off_gnv); + if (unlikely(!gh)) + goto out; + + gro_hint = geneve_opt_gro_hint(gh, hint_off); + if (!geneve_opt_gro_hint_validate_csum(skb, gh, gro_hint)) + goto out; + } + list_for_each_entry(p, head, list) { if (!NAPI_GRO_CB(p)->same_flow) continue; gh2 = (struct genevehdr *)(p->data + off_gnv); - if (gh->opt_len != gh2->opt_len || - memcmp(gh, gh2, gh_len)) { + if (!geneve_hdr_match(skb, gh, gh2, hint_off)) { NAPI_GRO_CB(p)->same_flow = 0; continue; } @@ -543,7 +927,6 @@ static struct sk_buff *geneve_gro_receive(struct sock *sk, skb_gro_pull(skb, gh_len); skb_gro_postpull_rcsum(skb, gh, gh_len); - type = gh->proto_type; if (likely(type == htons(ETH_P_TEB))) return call_gro_receive(eth_gro_receive, head, skb); @@ -572,6 +955,7 @@ static int geneve_gro_complete(struct sock *sk, struct sk_buff *skb, gh = (struct genevehdr *)(skb->data + nhoff); gh_len = geneve_hlen(gh); type = gh->proto_type; + geneve_opt_gro_hint_off(gh, &type, &gh_len); /* since skb->encapsulation is set, eth_gro_complete() sets the inner mac header */ if (likely(type == htons(ETH_P_TEB))) @@ -659,13 +1043,15 @@ static void geneve_sock_release(struct geneve_dev *geneve) static struct geneve_sock *geneve_find_sock(struct geneve_net *gn, sa_family_t family, - __be16 dst_port) + __be16 dst_port, + bool gro_hint) { struct geneve_sock *gs; list_for_each_entry(gs, &gn->sock_list, list) { if (inet_sk(gs->sock->sk)->inet_sport == dst_port && - geneve_get_sk_family(gs) == family) { + geneve_get_sk_family(gs) == family && + gs->gro_hint == gro_hint) { return gs; } } @@ -676,12 +1062,14 @@ static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6) { struct net *net = geneve->net; struct geneve_net *gn = net_generic(net, geneve_net_id); + bool gro_hint = geneve->cfg.gro_hint; struct geneve_dev_node *node; struct geneve_sock *gs; __u8 vni[3]; __u32 hash; - gs = geneve_find_sock(gn, ipv6 ? AF_INET6 : AF_INET, geneve->cfg.info.key.tp_dst); + gs = geneve_find_sock(gn, ipv6 ? AF_INET6 : AF_INET, + geneve->cfg.info.key.tp_dst, gro_hint); if (gs) { gs->refcnt++; goto out; @@ -694,6 +1082,7 @@ static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6) out: gs->collect_md = geneve->cfg.collect_md; + gs->gro_hint = gro_hint; #if IS_ENABLED(CONFIG_IPV6) if (ipv6) { rcu_assign_pointer(geneve->sock6, gs); @@ -766,34 +1155,116 @@ static void geneve_build_header(struct genevehdr *geneveh, ip_tunnel_info_opts_get(geneveh->options, info); } +static int geneve_build_gro_hint_opt(const struct geneve_dev *geneve, + struct sk_buff *skb) +{ + struct geneve_skb_cb *cb = GENEVE_SKB_CB(skb); + struct geneve_opt_gro_hint *hint; + unsigned int nhlen; + bool nested_is_v6; + int id; + + BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct geneve_skb_cb)); + cb->gro_hint_len = 0; + + /* Try to add the GRO hint only in case of double encap. */ + if (!geneve->cfg.gro_hint || !skb->encapsulation) + return 0; + + /* + * The nested headers must fit the geneve opt len fields and the + * nested encap must carry a nested transport (UDP) header. + */ + nhlen = skb_inner_mac_header(skb) - skb->data; + if (nhlen > 255 || !skb_transport_header_was_set(skb) || + skb->inner_protocol_type != ENCAP_TYPE_ETHER || + (skb_transport_offset(skb) + sizeof(struct udphdr) > nhlen)) + return 0; + + id = proto_to_id(skb->inner_protocol); + if (id < 0) + return 0; + + nested_is_v6 = skb->protocol == htons(ETH_P_IPV6); + if (nested_is_v6) { + int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); + u8 proto = ipv6_hdr(skb)->nexthdr; + __be16 foff; + + if (ipv6_skip_exthdr(skb, start, &proto, &foff) < 0 || + proto != IPPROTO_UDP) + return 0; + } else { + if (ip_hdr(skb)->protocol != IPPROTO_UDP) + return 0; + } + + hint = &cb->gro_hint; + memset(hint, 0, sizeof(*hint)); + hint->inner_proto_id = id; + hint->nested_is_v6 = skb->protocol == htons(ETH_P_IPV6); + hint->nested_nh_offset = skb_network_offset(skb); + hint->nested_tp_offset = skb_transport_offset(skb); + hint->nested_hdr_len = nhlen; + cb->gro_hint_len = GENEVE_OPT_GRO_HINT_SIZE; + return GENEVE_OPT_GRO_HINT_SIZE; +} + +static void geneve_put_gro_hint_opt(struct genevehdr *gnvh, int opt_size, + const struct geneve_opt_gro_hint *hint) +{ + struct geneve_opt *gro_opt; + + /* geneve_build_header() did not took in account the GRO hint. */ + gnvh->opt_len = (opt_size + GENEVE_OPT_GRO_HINT_SIZE) >> 2; + + gro_opt = (void *)(gnvh + 1) + opt_size; + memset(gro_opt, 0, sizeof(*gro_opt)); + + gro_opt->opt_class = htons(GENEVE_OPT_NETDEV_CLASS); + gro_opt->type = GENEVE_OPT_GRO_HINT_TYPE; + gro_opt->length = GENEVE_OPT_GRO_HINT_LEN; + memcpy(gro_opt + 1, hint, sizeof(*hint)); +} + static int geneve_build_skb(struct dst_entry *dst, struct sk_buff *skb, const struct ip_tunnel_info *info, - bool xnet, int ip_hdr_len, - bool inner_proto_inherit) + const struct geneve_dev *geneve, int ip_hdr_len) { bool udp_sum = test_bit(IP_TUNNEL_CSUM_BIT, info->key.tun_flags); + bool inner_proto_inherit = geneve->cfg.inner_proto_inherit; + bool xnet = !net_eq(geneve->net, dev_net(geneve->dev)); + struct geneve_skb_cb *cb = GENEVE_SKB_CB(skb); struct genevehdr *gnvh; __be16 inner_proto; + bool double_encap; int min_headroom; + int opt_size; int err; skb_reset_mac_header(skb); skb_scrub_packet(skb, xnet); + opt_size = info->options_len + cb->gro_hint_len; min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len + - GENEVE_BASE_HLEN + info->options_len + ip_hdr_len; + GENEVE_BASE_HLEN + opt_size + ip_hdr_len; err = skb_cow_head(skb, min_headroom); if (unlikely(err)) goto free_dst; + double_encap = udp_tunnel_handle_partial(skb); err = udp_tunnel_handle_offloads(skb, udp_sum); if (err) goto free_dst; - gnvh = __skb_push(skb, sizeof(*gnvh) + info->options_len); + gnvh = __skb_push(skb, sizeof(*gnvh) + opt_size); inner_proto = inner_proto_inherit ? skb->protocol : htons(ETH_P_TEB); geneve_build_header(gnvh, info, inner_proto); - skb_set_inner_protocol(skb, inner_proto); + + if (cb->gro_hint_len) + geneve_put_gro_hint_opt(gnvh, info->options_len, &cb->gro_hint); + + udp_tunnel_set_inner_protocol(skb, double_encap, inner_proto); return 0; free_dst: @@ -821,8 +1292,6 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, struct geneve_dev *geneve, const struct ip_tunnel_info *info) { - bool inner_proto_inherit = geneve->cfg.inner_proto_inherit; - bool xnet = !net_eq(geneve->net, dev_net(geneve->dev)); struct geneve_sock *gs4 = rcu_dereference(geneve->sock4); const struct ip_tunnel_key *key = &info->key; struct rtable *rt; @@ -833,7 +1302,7 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, __be16 sport; int err; - if (skb_vlan_inet_prepare(skb, inner_proto_inherit)) + if (skb_vlan_inet_prepare(skb, geneve->cfg.inner_proto_inherit)) return -EINVAL; if (!gs4) @@ -854,7 +1323,8 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, return PTR_ERR(rt); err = skb_tunnel_check_pmtu(skb, &rt->dst, - GENEVE_IPV4_HLEN + info->options_len, + GENEVE_IPV4_HLEN + info->options_len + + geneve_build_gro_hint_opt(geneve, skb), netif_is_any_bridge_port(dev)); if (err < 0) { dst_release(&rt->dst); @@ -916,8 +1386,8 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, } } - err = geneve_build_skb(&rt->dst, skb, info, xnet, sizeof(struct iphdr), - inner_proto_inherit); + err = geneve_build_skb(&rt->dst, skb, info, geneve, + sizeof(struct iphdr)); if (unlikely(err)) return err; @@ -934,8 +1404,6 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, struct geneve_dev *geneve, const struct ip_tunnel_info *info) { - bool inner_proto_inherit = geneve->cfg.inner_proto_inherit; - bool xnet = !net_eq(geneve->net, dev_net(geneve->dev)); struct geneve_sock *gs6 = rcu_dereference(geneve->sock6); const struct ip_tunnel_key *key = &info->key; struct dst_entry *dst = NULL; @@ -945,7 +1413,7 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, __be16 sport; int err; - if (skb_vlan_inet_prepare(skb, inner_proto_inherit)) + if (skb_vlan_inet_prepare(skb, geneve->cfg.inner_proto_inherit)) return -EINVAL; if (!gs6) @@ -966,7 +1434,8 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, return PTR_ERR(dst); err = skb_tunnel_check_pmtu(skb, dst, - GENEVE_IPV6_HLEN + info->options_len, + GENEVE_IPV6_HLEN + info->options_len + + geneve_build_gro_hint_opt(geneve, skb), netif_is_any_bridge_port(dev)); if (err < 0) { dst_release(dst); @@ -1008,8 +1477,7 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, ttl = key->ttl; ttl = ttl ? : ip6_dst_hoplimit(dst); } - err = geneve_build_skb(dst, skb, info, xnet, sizeof(struct ipv6hdr), - inner_proto_inherit); + err = geneve_build_skb(dst, skb, info, geneve, sizeof(struct ipv6hdr)); if (unlikely(err)) return err; @@ -1211,9 +1679,16 @@ static void geneve_setup(struct net_device *dev) dev->features |= NETIF_F_RXCSUM; dev->features |= NETIF_F_GSO_SOFTWARE; + /* Partial features are disabled by default. */ dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST; dev->hw_features |= NETIF_F_RXCSUM; dev->hw_features |= NETIF_F_GSO_SOFTWARE; + dev->hw_features |= UDP_TUNNEL_PARTIAL_FEATURES; + dev->hw_features |= NETIF_F_GSO_PARTIAL; + + dev->hw_enc_features = dev->hw_features; + dev->gso_partial_features = UDP_TUNNEL_PARTIAL_FEATURES; + dev->mangleid_features = NETIF_F_GSO_PARTIAL; dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS; /* MTU range: 68 - (something less than 65535) */ @@ -1248,6 +1723,7 @@ static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = { [IFLA_GENEVE_DF] = { .type = NLA_U8 }, [IFLA_GENEVE_INNER_PROTO_INHERIT] = { .type = NLA_FLAG }, [IFLA_GENEVE_PORT_RANGE] = NLA_POLICY_EXACT_LEN(sizeof(struct ifla_geneve_port_range)), + [IFLA_GENEVE_GRO_HINT] = { .type = NLA_FLAG }, }; static int geneve_validate(struct nlattr *tb[], struct nlattr *data[], @@ -1598,10 +2074,18 @@ static int geneve_nl2info(struct nlattr *tb[], struct nlattr *data[], cfg->inner_proto_inherit = true; } + if (data[IFLA_GENEVE_GRO_HINT]) { + if (changelink) { + attrtype = IFLA_GENEVE_GRO_HINT; + goto change_notsup; + } + cfg->gro_hint = true; + } + return 0; change_notsup: NL_SET_ERR_MSG_ATTR(extack, data[attrtype], - "Changing VNI, Port, endpoint IP address family, external, inner_proto_inherit, and UDP checksum attributes are not supported"); + "Changing VNI, Port, endpoint IP address family, external, inner_proto_inherit, gro_hint and UDP checksum attributes are not supported"); return -EOPNOTSUPP; } @@ -1784,6 +2268,7 @@ static size_t geneve_get_size(const struct net_device *dev) nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_TTL_INHERIT */ nla_total_size(0) + /* IFLA_GENEVE_INNER_PROTO_INHERIT */ nla_total_size(sizeof(struct ifla_geneve_port_range)) + /* IFLA_GENEVE_PORT_RANGE */ + nla_total_size(0) + /* IFLA_GENEVE_GRO_HINT */ 0; } @@ -1856,6 +2341,10 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev) if (nla_put(skb, IFLA_GENEVE_PORT_RANGE, sizeof(ports), &ports)) goto nla_put_failure; + if (geneve->cfg.gro_hint && + nla_put_flag(skb, IFLA_GENEVE_GRO_HINT)) + goto nla_put_failure; + return 0; nla_put_failure: |
