Introduce nf_flow_offload_check_mtu() helper and use the encapsulated protocol (tun.encap_proto) to compute the tunnel overhead instead of relying on tun_num, so the correct inner header size (IPv4 vs IPv6) is accounted for. Use it in both the IPv4 and IPv6 forward paths. This is a preliminary patch to support IPv4 over IPv6 and SIT flowtable tunnel offload. Signed-off-by: Lorenzo Bianconi --- net/netfilter/nf_flow_table_ip.c | 47 ++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index 3ecc47b57854..0aaf1e07f320 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -470,6 +470,37 @@ nf_flow_offload_lookup(struct nf_flowtable_ctx *ctx, return flow_offload_lookup(flow_table, &tuple); } +static int nf_flow_offload_check_mtu(struct nf_flowtable_ctx *ctx, + struct flow_offload_tuple_rhash *tuplehash, + struct sk_buff *skb) +{ + enum flow_offload_tuple_dir dir; + struct flow_offload *flow; + unsigned int mtu; + + dir = tuplehash->tuple.dir; + flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]); + + mtu = flow->tuplehash[dir].tuple.mtu + ctx->offset; + if (unlikely(nf_flow_is_tunnel_ip(ctx))) { + switch (ctx->ether_type) { + case htons(ETH_P_IP): + mtu -= sizeof(struct iphdr); + break; + case htons(ETH_P_IPV6): + mtu -= sizeof(struct ipv6hdr); + break; + default: + break; + } + } + + if (unlikely(nf_flow_exceeds_mtu(skb, mtu))) + return -EINVAL; + + return 0; +} + static int nf_flow_offload_forward(struct nf_flowtable_ctx *ctx, struct nf_flowtable *flow_table, struct flow_offload_tuple_rhash *tuplehash, @@ -477,17 +508,13 @@ static int nf_flow_offload_forward(struct nf_flowtable_ctx *ctx, { enum flow_offload_tuple_dir dir; struct flow_offload *flow; - unsigned int thoff, mtu; + unsigned int thoff; struct iphdr *iph; dir = tuplehash->tuple.dir; flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]); - mtu = flow->tuplehash[dir].tuple.mtu + ctx->offset; - if (flow->tuplehash[!dir].tuple.tun_num) - mtu -= sizeof(*iph); - - if (unlikely(nf_flow_exceeds_mtu(skb, mtu))) + if (nf_flow_offload_check_mtu(ctx, tuplehash, skb)) return 0; iph = (struct iphdr *)(skb_network_header(skb) + ctx->offset); @@ -1073,17 +1100,13 @@ static int nf_flow_offload_ipv6_forward(struct nf_flowtable_ctx *ctx, { enum flow_offload_tuple_dir dir; struct flow_offload *flow; - unsigned int thoff, mtu; struct ipv6hdr *ip6h; + unsigned int thoff; dir = tuplehash->tuple.dir; flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]); - mtu = flow->tuplehash[dir].tuple.mtu + ctx->offset; - if (flow->tuplehash[!dir].tuple.tun_num) - mtu -= sizeof(*ip6h); - - if (unlikely(nf_flow_exceeds_mtu(skb, mtu))) + if (nf_flow_offload_check_mtu(ctx, tuplehash, skb)) return 0; ip6h = (struct ipv6hdr *)(skb_network_header(skb) + ctx->offset); -- 2.55.0