Introduce sw flowtable acceleration for the TX/RX paths of SIT tunnels, relying on the netfilter flowtable infrastructure. Only plain SIT tunnels are offloaded: NBMA tunnels (ISATAP, 6to4, 6rd), FOU/GUE encapsulation, inherited TOS, and packets whose inner protocol does not match the configured SIT mode are rejected. Tunnel parameters are read from the RCU-protected snapshot and the outer route is resolved in the tunnel netns using its fwmark, mirroring the slow path. The feature can be tested with a forwarding scenario between two NICs (eth0 and eth1), where a SIT tunnel is used to reach a remote site via eth1 as the underlay device: ETH0 -- TUN0 <==> ETH1 -- [IP network] -- TUN1 (192.168.2.2) [IP configuration] 6: eth0: mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether 00:00:22:33:11:55 brd ff:ff:ff:ff:ff:ff inet6 2001:db8:1::2/64 scope global nodad valid_lft forever preferred_lft forever 7: eth1: mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether 00:11:22:33:11:55 brd ff:ff:ff:ff:ff:ff inet 192.168.2.1/24 scope global eth1 valid_lft forever preferred_lft forever 8: tun0@NONE: mtu 1480 qdisc noqueue state UNKNOWN group default qlen 1000 link/sit 192.168.2.1 peer 192.168.2.2 inet6 2001:db8:200::1/64 scope global nodad valid_lft forever preferred_lft forever $ ip route show 192.168.2.0/24 dev eth1 proto kernel scope link src 192.168.2.1 $ ip -6 route show 2001:db8:1::/64 dev eth0 proto kernel metric 256 pref medium 2001:db8:200::/64 dev tun0 proto kernel metric 256 pref medium default via 2001:db8:200::2 dev tun0 metric 1024 pref medium $ nft list ruleset table inet filter { flowtable ft { hook ingress priority filter devices = { eth0, eth1 } } chain forward { type filter hook forward priority filter; policy accept; meta l4proto { tcp, udp } flow add @ft } } When reproducing this scenario using veth interfaces, the following results were observed: - TCP stream received from SIT tunnel: - net-next (baseline): ~118 Gbps - net-next + SIT flowtable support: ~148 Gbps - TCP stream transmitted to SIT tunnel: - net-next (baseline): ~131 Gbps - net-next + SIT flowtable support: ~147 Gbps Signed-off-by: Lorenzo Bianconi --- net/ipv6/sit.c | 72 ++++++++++++++++++++++++++++++++++++++++ net/netfilter/nf_flow_table_ip.c | 22 ++++++++++-- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index a7ec800216ad..9e6ef3b72cfc 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -1483,6 +1483,77 @@ ipip6_tunnel_ctl(struct net_device *dev, struct ip_tunnel_parm_kern *p, } } +static int ipip6_tunnel_fill_forward_path(struct net_device_path_ctx *ctx, + struct net_device_path *path) +{ + struct ip_tunnel *tunnel = netdev_priv(ctx->dev); + const struct ip_tunnel_parm_kern *parms; + const struct iphdr *tiph; + struct flowi4 fl4; + struct rtable *rt; + + /* NBMA tunnels (ISATAP, 6to4, 6rd) resolve the outer destination + * per-packet from the inner IPv6 address; not offloadable. + */ + if (ctx->dev->priv_flags & IFF_ISATAP) + return -EOPNOTSUPP; + + parms = rcu_dereference(tunnel->sit_parms); + tiph = &parms->iph; + if (!tiph->daddr) + return -EOPNOTSUPP; + + if (tiph->tos & 0x1) + return -EOPNOTSUPP; + + /* FOU/GUE encapsulation is handled in the slow path by + * ip_tunnel_encap(); the fast path only pushes a plain + * IPv4 header and would skip the UDP encapsulation. + */ + if (READ_ONCE(tunnel->encap.type) != TUNNEL_ENCAP_NONE) + return -EOPNOTSUPP; + + switch (ctx->ether_type) { + case cpu_to_be16(ETH_P_IP): + path->tun.inner_proto = IPPROTO_IPIP; + break; + case cpu_to_be16(ETH_P_IPV6): + path->tun.inner_proto = IPPROTO_IPV6; + break; + default: + return -EOPNOTSUPP; + } + + if (parms->iph.protocol && + parms->iph.protocol != path->tun.inner_proto) + return -EOPNOTSUPP; + + flowi4_init_output(&fl4, parms->link, READ_ONCE(tunnel->fwmark), + tiph->tos & INET_DSCP_MASK, RT_SCOPE_UNIVERSE, + path->tun.inner_proto, 0, tiph->daddr, + tiph->saddr, 0, 0, + sock_net_uid(tunnel->net, NULL)); + rt = ip_route_output_key(tunnel->net, &fl4); + if (IS_ERR(rt)) + return PTR_ERR(rt); + + if (dst_xfrm(&rt->dst)) { + ip_rt_put(rt); + return -EOPNOTSUPP; + } + + path->type = DEV_PATH_TUN; + path->tun.src_v4.s_addr = fl4.saddr; + path->tun.dst_v4.s_addr = tiph->daddr; + path->tun.encap_proto = AF_INET; + path->dev = ctx->dev; + path->tun.dst = &rt->dst; + + ctx->dev = rt->dst.dev; + + return 0; +} + static int ipip6_tunnel_siocdevprivate(struct net_device *dev, struct ifreq *ifr, void __user *data, int cmd) @@ -1533,6 +1604,7 @@ static const struct net_device_ops ipip6_netdev_ops = { .ndo_siocdevprivate = ipip6_tunnel_siocdevprivate, .ndo_get_iflink = ipip6_get_iflink, .ndo_tunnel_ctl = ipip6_tunnel_ctl, + .ndo_fill_forward_path = ipip6_tunnel_fill_forward_path, }; static void ipip6_dev_free(struct net_device *dev) diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index ea2cd9df33f8..3a7c5318ed15 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -158,6 +158,16 @@ struct nf_flowtable_ctx { } tun; }; +static int nf_flow_tuple_ipv6(struct nf_flowtable_ctx *ctx, + struct sk_buff *skb, + struct flow_offload_tuple *tuple); + +static int +nf_flow_offload_ipv6_forward(struct nf_flowtable_ctx *ctx, + struct nf_flowtable *flow_table, + struct flow_offload_tuple_rhash *tuplehash, + struct sk_buff *skb); + static void nf_flow_tuple_encap(struct nf_flowtable_ctx *ctx, struct sk_buff *skb, struct flow_offload_tuple *tuple) @@ -466,8 +476,12 @@ nf_flow_offload_lookup(struct nf_flowtable_ctx *ctx, { struct flow_offload_tuple tuple = {}; - if (nf_flow_tuple_ip(ctx, skb, &tuple) < 0) + if (ctx->tun.inner_proto == IPPROTO_IPV6) { + if (nf_flow_tuple_ipv6(ctx, skb, &tuple) < 0) + return NULL; + } else if (nf_flow_tuple_ip(ctx, skb, &tuple) < 0) { return NULL; + } return flow_offload_lookup(flow_table, &tuple); } @@ -969,7 +983,11 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, if (!tuplehash) return NF_ACCEPT; - ret = nf_flow_offload_forward(&ctx, flow_table, tuplehash, skb); + if (ctx.tun.inner_proto == IPPROTO_IPV6) + ret = nf_flow_offload_ipv6_forward(&ctx, flow_table, tuplehash, + skb); + else + ret = nf_flow_offload_forward(&ctx, flow_table, tuplehash, skb); if (ret < 0) return NF_DROP; else if (ret == 0) -- 2.56.0