| From c3812651b522fe8437ebb7063b75ddb95b571643 Mon Sep 17 00:00:00 2001 |
| From: Andrea Mayer <andrea.mayer@uniroma2.it> |
| Date: Sat, 4 Apr 2026 02:44:04 +0200 |
| Subject: seg6: separate dst_cache for input and output paths in seg6 lwtunnel |
| |
| From: Andrea Mayer <andrea.mayer@uniroma2.it> |
| |
| commit c3812651b522fe8437ebb7063b75ddb95b571643 upstream. |
| |
| The seg6 lwtunnel uses a single dst_cache per encap route, shared |
| between seg6_input_core() and seg6_output_core(). These two paths |
| can perform the post-encap SID lookup in different routing contexts |
| (e.g., ip rules matching on the ingress interface, or VRF table |
| separation). Whichever path runs first populates the cache, and the |
| other reuses it blindly, bypassing its own lookup. |
| |
| Fix this by splitting the cache into cache_input and cache_output, |
| so each path maintains its own cached dst independently. |
| |
| Fixes: 6c8702c60b88 ("ipv6: sr: add support for SRH encapsulation and injection with lwtunnels") |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Andrea Mayer <andrea.mayer@uniroma2.it> |
| Reviewed-by: Nicolas Dichtel <nicolas.dichtel@6wind.com> |
| Reviewed-by: Justin Iurman <justin.iurman@gmail.com> |
| Link: https://patch.msgid.link/20260404004405.4057-2-andrea.mayer@uniroma2.it |
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/ipv6/seg6_iptunnel.c | 34 +++++++++++++++++++++++----------- |
| 1 file changed, 23 insertions(+), 11 deletions(-) |
| |
| --- a/net/ipv6/seg6_iptunnel.c |
| +++ b/net/ipv6/seg6_iptunnel.c |
| @@ -48,7 +48,8 @@ static size_t seg6_lwt_headroom(struct s |
| } |
| |
| struct seg6_lwt { |
| - struct dst_cache cache; |
| + struct dst_cache cache_input; |
| + struct dst_cache cache_output; |
| struct seg6_iptunnel_encap tuninfo[]; |
| }; |
| |
| @@ -488,7 +489,7 @@ static int seg6_input_core(struct net *n |
| slwt = seg6_lwt_lwtunnel(lwtst); |
| |
| local_bh_disable(); |
| - dst = dst_cache_get(&slwt->cache); |
| + dst = dst_cache_get(&slwt->cache_input); |
| local_bh_enable(); |
| |
| err = seg6_do_srh(skb, dst); |
| @@ -504,7 +505,7 @@ static int seg6_input_core(struct net *n |
| /* cache only if we don't create a dst reference loop */ |
| if (!dst->error && lwtst != dst->lwtstate) { |
| local_bh_disable(); |
| - dst_cache_set_ip6(&slwt->cache, dst, |
| + dst_cache_set_ip6(&slwt->cache_input, dst, |
| &ipv6_hdr(skb)->saddr); |
| local_bh_enable(); |
| } |
| @@ -564,7 +565,7 @@ static int seg6_output_core(struct net * |
| slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate); |
| |
| local_bh_disable(); |
| - dst = dst_cache_get(&slwt->cache); |
| + dst = dst_cache_get(&slwt->cache_output); |
| local_bh_enable(); |
| |
| err = seg6_do_srh(skb, dst); |
| @@ -591,7 +592,7 @@ static int seg6_output_core(struct net * |
| /* cache only if we don't create a dst reference loop */ |
| if (orig_dst->lwtstate != dst->lwtstate) { |
| local_bh_disable(); |
| - dst_cache_set_ip6(&slwt->cache, dst, &fl6.saddr); |
| + dst_cache_set_ip6(&slwt->cache_output, dst, &fl6.saddr); |
| local_bh_enable(); |
| } |
| |
| @@ -701,11 +702,13 @@ static int seg6_build_state(struct net * |
| |
| slwt = seg6_lwt_lwtunnel(newts); |
| |
| - err = dst_cache_init(&slwt->cache, GFP_ATOMIC); |
| - if (err) { |
| - kfree(newts); |
| - return err; |
| - } |
| + err = dst_cache_init(&slwt->cache_input, GFP_ATOMIC); |
| + if (err) |
| + goto err_free_newts; |
| + |
| + err = dst_cache_init(&slwt->cache_output, GFP_ATOMIC); |
| + if (err) |
| + goto err_destroy_input; |
| |
| memcpy(&slwt->tuninfo, tuninfo, tuninfo_len); |
| |
| @@ -720,11 +723,20 @@ static int seg6_build_state(struct net * |
| *ts = newts; |
| |
| return 0; |
| + |
| +err_destroy_input: |
| + dst_cache_destroy(&slwt->cache_input); |
| +err_free_newts: |
| + kfree(newts); |
| + return err; |
| } |
| |
| static void seg6_destroy_state(struct lwtunnel_state *lwt) |
| { |
| - dst_cache_destroy(&seg6_lwt_lwtunnel(lwt)->cache); |
| + struct seg6_lwt *slwt = seg6_lwt_lwtunnel(lwt); |
| + |
| + dst_cache_destroy(&slwt->cache_input); |
| + dst_cache_destroy(&slwt->cache_output); |
| } |
| |
| static int seg6_fill_encap_info(struct sk_buff *skb, |