> On Mon, Sep 07, 2026 at 09:33:41AM +0200, Lorenzo Bianconi wrote: > > Refactor nf_flow_tunnel_ipip_push() and nf_flow_tunnel_ip6ip6_push() > > into nf_flow_tunnel_ip_push() and nf_flow_tunnel_ip6_push(), keying the > > inner header handling off tuple->tun.inner_proto so both IP-in-IP and > > IPv6-in-IPv6 inner protocols are supported regardless of the outer > > address family. Replace nf_flow_tunnel_v4_push() and > > nf_flow_tunnel_v6_push() with a single nf_flow_tunnel_push() that > > dispatches on tuple->tun.encap_proto, and set skb->protocol explicitly > > after pushing the outer header. > > This is a preliminary patch to support IPv4 over IPv6 and SIT tunnel > > flowtable offload. > > Please note IPv4 over IPv6 and SIT tunnel flowtable offloading is not > > enabled yet. > > > > Signed-off-by: Lorenzo Bianconi > > --- > > net/netfilter/nf_flow_table_ip.c | 129 ++++++++++++++++++++++++--------------- > > 1 file changed, 81 insertions(+), 48 deletions(-) > > > > diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c > > index 96dbdadba4e7..25875cc97bda 100644 > > --- a/net/netfilter/nf_flow_table_ip.c > > +++ b/net/netfilter/nf_flow_table_ip.c > > @@ -608,23 +608,41 @@ static int nf_flow_pppoe_push(struct sk_buff *skb, u16 id, > > return 0; > > } > > > > -static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb, > > - struct flow_offload_tuple *tuple, > > - struct dst_entry *dst, __be32 *ip_daddr) > > +static int nf_flow_tunnel_ip_push(struct net *net, struct sk_buff *skb, > > + struct flow_offload_tuple *tuple, > > + struct dst_entry *dst, __be32 *ip_daddr) > > { > > - struct iphdr *iph = (struct iphdr *)skb_network_header(skb); > > - struct rtable *rt = dst_rtable(dst); > > - u8 tos = iph->tos, ttl = iph->ttl; > > - __be16 frag_off = iph->frag_off; > > - u32 headroom = sizeof(*iph); > > + __be16 frag_off = 0; > > + struct iphdr *iph; > > + u8 tos = 0, ttl; > > + u32 headroom; > > int err; > > > > + switch (tuple->tun.inner_proto) { > > + case IPPROTO_IPV6: { > > + struct ipv6hdr *ip6h; > > + > > + ip6h = (struct ipv6hdr *)skb_network_header(skb); > > + tos = ipv6_get_dsfield(ip6h); > > + ttl = ip6h->hop_limit; > > + frag_off = htons(IP_DF); > > + break; > > + } > > + default: > > Please add an explicit case to check for IPv4 here, ie. no default: ack, I will fix it in v3. > > > + iph = (struct iphdr *)skb_network_header(skb); > > + frag_off = iph->frag_off; > > + tos = iph->tos; > > + ttl = iph->ttl; > > + break; > > + } > > + > > err = iptunnel_handle_offloads(skb, SKB_GSO_IPXIP4); > > if (err) > > return err; > > > > - skb_set_inner_ipproto(skb, IPPROTO_IPIP); > > - headroom += LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len; > > + skb_set_inner_ipproto(skb, tuple->tun.inner_proto); > > + headroom = sizeof(*iph) + LL_RESERVED_SPACE(dst->dev) + > > + dst->header_len; > > err = skb_cow_head(skb, headroom); > > if (err) > > return err; > > @@ -635,11 +653,12 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb, > > /* Push down and install the IP header. */ > > skb_push(skb, sizeof(*iph)); > > skb_reset_network_header(skb); > > + skb->protocol = htons(ETH_P_IP); > > > > iph = ip_hdr(skb); > > iph->version = 4; > > iph->ihl = sizeof(*iph) >> 2; > > - iph->frag_off = ip_mtu_locked(&rt->dst) ? 0 : frag_off; > > + iph->frag_off = ip_mtu_locked(dst) ? 0 : frag_off; > > iph->protocol = tuple->tun.inner_proto; > > iph->tos = tos; > > iph->daddr = tuple->tun.src_v4.s_addr; > > @@ -654,57 +673,61 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb, > > return 0; > > } > > > > -static int nf_flow_tunnel_v4_push(struct net *net, struct sk_buff *skb, > > - struct flow_offload_tuple *tuple, > > - struct dst_entry *dst, __be32 *ip_daddr) > > +static int nf_flow_tunnel_ip6_push(struct net *net, struct sk_buff *skb, > > + struct flow_offload_tuple *tuple, > > + struct dst_entry *dst, > > + struct in6_addr **ip6_daddr) > > { > > - if (tuple->tun_num) > > - return nf_flow_tunnel_ipip_push(net, skb, tuple, dst, ip_daddr); > > - > > - return 0; > > -} > > - > > -static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb, > > - struct flow_offload_tuple *tuple, > > - struct dst_entry *dst, > > - struct in6_addr **ip6_daddr) > > -{ > > - struct ipv6hdr *ip6h = (struct ipv6hdr *)skb_network_header(skb); > > - __u8 dsfield = ipv6_get_dsfield(ip6h); > > - struct rtable *rt = dst_rtable(dst); > > struct flowi6 fl6 = { > > .daddr = tuple->tun.src_v6, > > .saddr = tuple->tun.dst_v6, > > - .flowi6_proto = IPPROTO_IPV6, > > + .flowi6_proto = tuple->tun.inner_proto, > > }; > > - u8 hop_limit = ip6h->hop_limit; > > + u8 hop_limit, dsfield; > > + struct ipv6hdr *ip6h; > > int err, mtu; > > u32 headroom; > > > > + switch (tuple->tun.inner_proto) { > > + case IPPROTO_IPIP: { > > + struct iphdr *iph = (struct iphdr *)skb_network_header(skb); > > + > > + dsfield = ipv4_get_dsfield(iph); > > + hop_limit = iph->ttl; > > + break; > > + } > > + default: > > Same here. ack, I will fix it in v3. > > > + ip6h = (struct ipv6hdr *)skb_network_header(skb); > > + dsfield = ipv6_get_dsfield(ip6h); > > + hop_limit = ip6h->hop_limit; > > + break; > > + } > > + > > err = iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6); > > if (err) > > return err; > > > > - skb_set_inner_ipproto(skb, IPPROTO_IPV6); > > - headroom = sizeof(*ip6h) + LL_RESERVED_SPACE(rt->dst.dev) + > > - rt->dst.header_len; > > + skb_set_inner_ipproto(skb, tuple->tun.inner_proto); > > + headroom = sizeof(*ip6h) + LL_RESERVED_SPACE(dst->dev) + > > + dst->header_len; > > err = skb_cow_head(skb, headroom); > > if (err) > > return err; > > > > skb_scrub_packet(skb, true); > > - mtu = dst_mtu(&rt->dst) - sizeof(*ip6h); > > + mtu = dst_mtu(dst) - sizeof(*ip6h); > > mtu = max(mtu, IPV6_MIN_MTU); > > skb_dst_update_pmtu_no_confirm(skb, mtu); > > > > skb_push(skb, sizeof(*ip6h)); > > skb_reset_network_header(skb); > > + skb->protocol = htons(ETH_P_IPV6); > > > > ip6h = ipv6_hdr(skb); > > ip6_flow_hdr(ip6h, dsfield, > > ip6_make_flowlabel(net, skb, fl6.flowlabel, true, &fl6)); > > ip6h->hop_limit = hop_limit; > > - ip6h->nexthdr = IPPROTO_IPV6; > > + ip6h->nexthdr = tuple->tun.inner_proto; > > ip6h->daddr = tuple->tun.src_v6; > > ip6h->saddr = tuple->tun.dst_v6; > > ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(*ip6h)); > > @@ -715,15 +738,20 @@ static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb, > > return 0; > > } > > > > -static int nf_flow_tunnel_v6_push(struct net *net, struct sk_buff *skb, > > - struct flow_offload_tuple *tuple, > > - struct dst_entry *dst, > > - struct in6_addr **ip6_daddr) > > +static int nf_flow_tunnel_push(struct net *net, struct sk_buff *skb, > > + struct flow_offload_tuple *tuple, > > + struct dst_entry *dst, __be32 *ip_daddr, > > + struct in6_addr **ip6_daddr) > > { > > - if (tuple->tun_num) > > - return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, dst, ip6_daddr); > > - > > - return 0; > > + switch (tuple->tun.encap_proto) { > > + case AF_INET: > > + return nf_flow_tunnel_ip_push(net, skb, tuple, dst, ip_daddr); > > + case AF_INET6: > > + return nf_flow_tunnel_ip6_push(net, skb, tuple, dst, > > + ip6_daddr); > > + default: > > + return 0; > > + } > > } > > > > static int nf_flow_encap_push(struct sk_buff *skb, > > @@ -830,6 +858,7 @@ static int nf_flow_queue_xmit4(struct sk_buff *skb, > > struct flow_offload_tuple *other_tuple; > > enum flow_offload_tuple_dir dir; > > struct nf_flow_xmit xmit = {}; > > + struct in6_addr *ip6_daddr; > > struct flow_offload *flow; > > struct neighbour *neigh; > > struct rtable *rt; > > @@ -847,9 +876,11 @@ static int nf_flow_queue_xmit4(struct sk_buff *skb, > > flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]); > > other_tuple = &flow->tuplehash[!dir].tuple; > > ip_daddr = other_tuple->src_v4.s_addr; > > + ip6_daddr = &other_tuple->src_v6; > > > > - if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple, > > - tuplehash->tuple.dst_cache, &ip_daddr) < 0) > > + if (nf_flow_tunnel_push(state->net, skb, other_tuple, > > + tuplehash->tuple.dst_cache, > > + &ip_daddr, &ip6_daddr) < 0) > > See comment below regarding this. > > > return NF_DROP; > > > > switch (tuplehash->tuple.xmit_type) { > > @@ -1158,6 +1189,7 @@ static int nf_flow_queue_xmit6(struct sk_buff *skb, > > struct flow_offload *flow; > > struct neighbour *neigh; > > struct rt6_info *rt; > > + __be32 ip_daddr; > > > > if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) { > > rt = dst_rt6_info(tuplehash->tuple.dst_cache); > > @@ -1170,11 +1202,12 @@ static int nf_flow_queue_xmit6(struct sk_buff *skb, > > dir = tuplehash->tuple.dir; > > flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]); > > other_tuple = &flow->tuplehash[!dir].tuple; > > + ip_daddr = other_tuple->src_v4.s_addr; > > ip6_daddr = &other_tuple->src_v6; > > IIRC this is pointing to the same address, it is a double fetch of the > same pointer? See below: right. > > > > > - if (nf_flow_tunnel_v6_push(state->net, skb, other_tuple, > > - tuplehash->tuple.dst_cache, > > - &ip6_daddr) < 0) > > + if (nf_flow_tunnel_push(state->net, skb, other_tuple, > > + tuplehash->tuple.dst_cache, > > + &ip_daddr, &ip6_daddr) < 0) > > ... time to use union nf_inet_addr here instead of these two ip_daddr > and ip6_daddr? ack, I will fix it in v3. Regards, Lorenzo > > > return NF_DROP; > > > > switch (tuplehash->tuple.xmit_type) { > > > > -- > > 2.55.0 > > > >