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. Signed-off-by: Lorenzo Bianconi --- net/netfilter/nf_flow_table_ip.c | 121 +++++++++++++++++++++++---------------- 1 file changed, 73 insertions(+), 48 deletions(-) diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index 5cbc3cf10ec2..dc38dcc28773 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -614,23 +614,37 @@ 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; + if (tuple->tun.inner_proto == 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); + } else { + iph = (struct iphdr *)skb_network_header(skb); + frag_off = iph->frag_off; + tos = iph->tos; + ttl = iph->ttl; + } + 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; @@ -641,11 +655,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; @@ -660,57 +675,57 @@ 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; + if (tuple->tun.inner_proto == IPPROTO_IPIP) { + struct iphdr *iph = (struct iphdr *)skb_network_header(skb); + + dsfield = ipv4_get_dsfield(iph); + hop_limit = iph->ttl; + } else { + ip6h = (struct ipv6hdr *)skb_network_header(skb); + dsfield = ipv6_get_dsfield(ip6h); + hop_limit = ip6h->hop_limit; + } + 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)); @@ -721,15 +736,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, @@ -836,6 +856,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; @@ -853,9 +874,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) return NF_DROP; switch (tuplehash->tuple.xmit_type) { @@ -1164,6 +1187,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); @@ -1176,11 +1200,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; - 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) return NF_DROP; switch (tuplehash->tuple.xmit_type) { -- 2.55.0