diff mbox series

[net] net: Fix icmp host relookup triggering ip_rt_bug

Message ID 20241111123915.3879488-1-dongchenchen2@huawei.com (mailing list archive)
State New
Delegated to: Netdev Maintainers
Headers show
Series [net] net: Fix icmp host relookup triggering ip_rt_bug | expand

Checks

Context Check Description
netdev/series_format success Single patches do not need cover letters
netdev/tree_selection success Clearly marked for net
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag present in non-next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 3 this patch: 3
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers warning 1 maintainers not CCed: kuba@kernel.org
netdev/build_clang success Errors and warnings before: 3 this patch: 3
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 6 this patch: 6
netdev/checkpatch warning WARNING: line length of 83 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 1 this patch: 1
netdev/source_inline success Was 0 now: 0

Commit Message

dongchenchen (A) Nov. 11, 2024, 12:39 p.m. UTC
arp link failure may trigger ip_rt_bug while xfrm enabled, call trace is:

WARNING: CPU: 0 PID: 0 at net/ipv4/route.c:1241 ip_rt_bug+0x14/0x20
Modules linked in:
CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Not tainted 6.12.0-rc6-00077-g2e1b3cc9d7f7
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996),
BIOS rel-1.13.0-0-gf21b5a4aeb02-prebuilt.qemu.org 04/01/2014
RIP: 0010:ip_rt_bug+0x14/0x20
Call Trace:
 <IRQ>
 ip_send_skb+0x14/0x40
 __icmp_send+0x42d/0x6a0
 ipv4_link_failure+0xe2/0x1d0
 arp_error_report+0x3c/0x50
 neigh_invalidate+0x8d/0x100
 neigh_timer_handler+0x2e1/0x330
 call_timer_fn+0x21/0x120
 __run_timer_base.part.0+0x1c9/0x270
 run_timer_softirq+0x4c/0x80
 handle_softirqs+0xac/0x280
 irq_exit_rcu+0x62/0x80
 sysvec_apic_timer_interrupt+0x77/0x90

The script below reproduces this scenario:
ip xfrm policy add src 0.0.0.0/0 dst 0.0.0.0/0 \
	dir out priority 0 ptype main flag localok icmp
ip l a veth1 type veth
ip a a 192.168.141.111/24 dev veth0
ip l s veth0 up
ping 192.168.141.155 -c 1

icmp_route_lookup() create input routes for locally generated packets
while xfrm relookup ICMP traffic.Then it will set input route
(dst->out = ip_rt_bug) to skb for DESTUNREACH.

Similar to commit ed6e4ef836d4("netfilter: Fix ip_route_me_harder
triggering ip_rt_bug"), avoid creating input routes with
icmp_route_lookup() to fix it.

Fixes: 8b7817f3a959 ("[IPSEC]: Add ICMP host relookup support")
Signed-off-by: Dong Chenchen <dongchenchen2@huawei.com>
---
 net/ipv4/icmp.c | 35 ++++++++++-------------------------
 1 file changed, 10 insertions(+), 25 deletions(-)

Comments

Herbert Xu Nov. 12, 2024, 2:10 a.m. UTC | #1
On Mon, Nov 11, 2024 at 08:39:15PM +0800, Dong Chenchen wrote:
> arp link failure may trigger ip_rt_bug while xfrm enabled, call trace is:
> 
> WARNING: CPU: 0 PID: 0 at net/ipv4/route.c:1241 ip_rt_bug+0x14/0x20
> Modules linked in:
> CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Not tainted 6.12.0-rc6-00077-g2e1b3cc9d7f7
> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996),
> BIOS rel-1.13.0-0-gf21b5a4aeb02-prebuilt.qemu.org 04/01/2014
> RIP: 0010:ip_rt_bug+0x14/0x20
> Call Trace:
>  <IRQ>
>  ip_send_skb+0x14/0x40
>  __icmp_send+0x42d/0x6a0
>  ipv4_link_failure+0xe2/0x1d0
>  arp_error_report+0x3c/0x50
>  neigh_invalidate+0x8d/0x100
>  neigh_timer_handler+0x2e1/0x330
>  call_timer_fn+0x21/0x120
>  __run_timer_base.part.0+0x1c9/0x270
>  run_timer_softirq+0x4c/0x80
>  handle_softirqs+0xac/0x280
>  irq_exit_rcu+0x62/0x80
>  sysvec_apic_timer_interrupt+0x77/0x90
> 
> The script below reproduces this scenario:
> ip xfrm policy add src 0.0.0.0/0 dst 0.0.0.0/0 \
> 	dir out priority 0 ptype main flag localok icmp
> ip l a veth1 type veth
> ip a a 192.168.141.111/24 dev veth0
> ip l s veth0 up
> ping 192.168.141.155 -c 1
> 
> icmp_route_lookup() create input routes for locally generated packets
> while xfrm relookup ICMP traffic.Then it will set input route
> (dst->out = ip_rt_bug) to skb for DESTUNREACH.
> 
> Similar to commit ed6e4ef836d4("netfilter: Fix ip_route_me_harder
> triggering ip_rt_bug"), avoid creating input routes with
> icmp_route_lookup() to fix it.
> 
> Fixes: 8b7817f3a959 ("[IPSEC]: Add ICMP host relookup support")
> Signed-off-by: Dong Chenchen <dongchenchen2@huawei.com>
> ---
>  net/ipv4/icmp.c | 35 ++++++++++-------------------------
>  1 file changed, 10 insertions(+), 25 deletions(-)
> 
> diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
> index e1384e7331d8..11ef4eb5b659 100644
> --- a/net/ipv4/icmp.c
> +++ b/net/ipv4/icmp.c
> @@ -490,6 +490,7 @@ static struct rtable *icmp_route_lookup(struct net *net,
>  	struct dst_entry *dst, *dst2;
>  	struct rtable *rt, *rt2;
>  	struct flowi4 fl4_dec;
> +	unsigned int addr_type;
>  	int err;
>  
>  	memset(fl4, 0, sizeof(*fl4));
> @@ -528,31 +529,15 @@ static struct rtable *icmp_route_lookup(struct net *net,
>  	if (err)
>  		goto relookup_failed;
>  
> -	if (inet_addr_type_dev_table(net, route_lookup_dev,
> -				     fl4_dec.saddr) == RTN_LOCAL) {
> -		rt2 = __ip_route_output_key(net, &fl4_dec);
> -		if (IS_ERR(rt2))
> -			err = PTR_ERR(rt2);
> -	} else {

So you're saying that your packet triggered the else branch?

That I think is the bug here, not the input route lookup which
is the whole purpose of the original patch (simulate an input route
lookup in order to determine the correct policy).

AFAIK the packet that triggered the crash has a source address
of 192.168.141.111, which should definitely be local.  So why
is the RTN_LOCAL check failing?

Cheers,
dongchenchen (A) Nov. 12, 2024, 3:42 a.m. UTC | #2
On 2024/11/12 10:10, Herbert Xu wrote:
> On Mon, Nov 11, 2024 at 08:39:15PM +0800, Dong Chenchen wrote:
>> arp link failure may trigger ip_rt_bug while xfrm enabled, call trace is:
>>
>> WARNING: CPU: 0 PID: 0 at net/ipv4/route.c:1241 ip_rt_bug+0x14/0x20
>> Modules linked in:
>> CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Not tainted 6.12.0-rc6-00077-g2e1b3cc9d7f7
>> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996),
>> BIOS rel-1.13.0-0-gf21b5a4aeb02-prebuilt.qemu.org 04/01/2014
>> RIP: 0010:ip_rt_bug+0x14/0x20
>> Call Trace:
>>   <IRQ>
>>   ip_send_skb+0x14/0x40
>>   __icmp_send+0x42d/0x6a0
>>   ipv4_link_failure+0xe2/0x1d0
>>   arp_error_report+0x3c/0x50
>>   neigh_invalidate+0x8d/0x100
>>   neigh_timer_handler+0x2e1/0x330
>>   call_timer_fn+0x21/0x120
>>   __run_timer_base.part.0+0x1c9/0x270
>>   run_timer_softirq+0x4c/0x80
>>   handle_softirqs+0xac/0x280
>>   irq_exit_rcu+0x62/0x80
>>   sysvec_apic_timer_interrupt+0x77/0x90
>>
>> The script below reproduces this scenario:
>> ip xfrm policy add src 0.0.0.0/0 dst 0.0.0.0/0 \
>> 	dir out priority 0 ptype main flag localok icmp
>> ip l a veth1 type veth
>> ip a a 192.168.141.111/24 dev veth0
>> ip l s veth0 up
>> ping 192.168.141.155 -c 1
>>
>> icmp_route_lookup() create input routes for locally generated packets
>> while xfrm relookup ICMP traffic.Then it will set input route
>> (dst->out = ip_rt_bug) to skb for DESTUNREACH.
>>
>> Similar to commit ed6e4ef836d4("netfilter: Fix ip_route_me_harder
>> triggering ip_rt_bug"), avoid creating input routes with
>> icmp_route_lookup() to fix it.
>>
>> Fixes: 8b7817f3a959 ("[IPSEC]: Add ICMP host relookup support")
>> Signed-off-by: Dong Chenchen <dongchenchen2@huawei.com>
>> ---
>>   net/ipv4/icmp.c | 35 ++++++++++-------------------------
>>   1 file changed, 10 insertions(+), 25 deletions(-)
>>
>> diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
>> index e1384e7331d8..11ef4eb5b659 100644
>> --- a/net/ipv4/icmp.c
>> +++ b/net/ipv4/icmp.c
>> @@ -490,6 +490,7 @@ static struct rtable *icmp_route_lookup(struct net *net,
>>   	struct dst_entry *dst, *dst2;
>>   	struct rtable *rt, *rt2;
>>   	struct flowi4 fl4_dec;
>> +	unsigned int addr_type;
>>   	int err;
>>   
>>   	memset(fl4, 0, sizeof(*fl4));
>> @@ -528,31 +529,15 @@ static struct rtable *icmp_route_lookup(struct net *net,
>>   	if (err)
>>   		goto relookup_failed;
>>   
>> -	if (inet_addr_type_dev_table(net, route_lookup_dev,
>> -				     fl4_dec.saddr) == RTN_LOCAL) {
>> -		rt2 = __ip_route_output_key(net, &fl4_dec);
>> -		if (IS_ERR(rt2))
>> -			err = PTR_ERR(rt2);
>> -	} else {
> So you're saying that your packet triggered the else branch?
>
> That I think is the bug here, not the input route lookup which
> is the whole purpose of the original patch (simulate an input route
> lookup in order to determine the correct policy).
>
> AFAIK the packet that triggered the crash has a source address
> of 192.168.141.111, which should definitely be local.  So why
> is the RTN_LOCAL check failing?
>
> Cheers,

Hi,Herbert. Thanks for your review! As described in rfc4301:
"The implementation extracts the header for the
packet that triggered the error (from the ICMP message payload),
reverses the source and destination IP address fields, extracts the
protocol field, and reverses the port fields (if accessible).  It
then uses this extracted information to locate an appropriate, active
outbound SA, and transmits the error message via this SA" icmp_route_lookup reverse IP address fields of skb_in

skb_in: A -> B

icmp_err: B -> A

icmp_route_lookup

     xfrm_decode_session_reverse(net, skb_in, flowi4_to_flowi(&fl4_dec), 
AF_INET);

     //fl4_dec: saddr=B, daddr=A

If skb_in is outbound, fl4_dec.saddr is not nolocal. It may be no input 
route from B to A for

first communication.

If skb_in is inbound, fl4_dec.saddr is rtn_local.


cheers!
Herbert Xu Nov. 12, 2024, 4:15 a.m. UTC | #3
On Tue, Nov 12, 2024 at 11:42:47AM +0800, dongchenchen (A) wrote:
>
> If skb_in is outbound, fl4_dec.saddr is not nolocal. It may be no input
> route from B to A for
> 
> first communication.

You're right.  So the problem here is that for the case of A
being local, we should not be taking the ip_route_input code
path (this is intended for forwarded packets).

In fact if A is local, and we're sending an ICMP message to A,
then perhaps we could skip the IPsec lookup completely and just
do normal routing?

Steffen, what do you think?

So I think it boils down to two choices:

1) We could simply drop IPsec processing if we notice that A
(fl.fl4_dst) is local;
2) Or we could take the __ip_route_output_key code path and
continue to do the xfrm lookup when A is local.

Thanks,
dongchenchen (A) Nov. 13, 2024, 2:13 p.m. UTC | #4
>> If skb_in is outbound, fl4_dec.saddr is not nolocal. It may be no input
>> route from B to A for
>>
>> first communication.
> You're right.  So the problem here is that for the case of A
> being local, we should not be taking the ip_route_input code
> path (this is intended for forwarded packets).
>
> In fact if A is local, and we're sending an ICMP message to A,
> then perhaps we could skip the IPsec lookup completely and just
> do normal routing?
>
> Steffen, what do you think?
>
> So I think it boils down to two choices:
>
> 1) We could simply drop IPsec processing if we notice that A
> (fl.fl4_dst) is local;

Hi, Herbert! Thanks for your suggestions very much.

maybe we can fix it as below:
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index e1384e7331d8..5f63c4dde4e9 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -517,7 +517,9 @@ static struct rtable *icmp_route_lookup(struct net *net,
                           flowi4_to_flowi(fl4), NULL, 0);
         rt = dst_rtable(dst);
         if (!IS_ERR(dst)) {
-               if (rt != rt2)
+               unsigned int addr_type = inet_addr_type_dev_table(net, 
route_lookup_dev, fl4->daddr);
+               if (rt != rt2 || addr_type == RTN_LOCAL)
                         return rt;
         } else if (PTR_ERR(dst) == -EPERM) {
                 rt = NULL;
> 2) Or we could take the __ip_route_output_key code path and
> continue to do the xfrm lookup when A is local.
If only skip input route lookup as below, xfrm_lookup check may return 
ENOENT
for no xfrm pol or dst nofrm flag.

@@ -543,6 +544,10 @@ static struct rtable *icmp_route_lookup(struct net 
*net, err = PTR_ERR(rt2); goto relookup_failed; } + + unsigned int 
addr_type = inet_addr_type_dev_table(net, route_lookup_dev, fl4->daddr); 
+ if (addr_type == RTN_LOCAL) + goto relookup; /* Ugh! */ orefdst = 
skb_in->_skb_refdst; /* save old refdst */

xfrm_lookup
xfrm_lookup_with_ifid
       if (!if_id && ((dst_orig->flags & DST_NOXFRM) ||
!net->xfrm.policy_count[XFRM_POLICY_OUT]))
         goto nopol;

Thanks a lot!
Dong Chenchen

> Thanks,
dongchenchen (A) Nov. 13, 2024, 2:19 p.m. UTC | #5
On 2024/11/13 22:13, dongchenchen (A) wrote:
>>> If skb_in is outbound, fl4_dec.saddr is not nolocal. It may be no input
>>> route from B to A for
>>>
>>> first communication.
>> You're right.  So the problem here is that for the case of A
>> being local, we should not be taking the ip_route_input code
>> path (this is intended for forwarded packets).
>>
>> In fact if A is local, and we're sending an ICMP message to A,
>> then perhaps we could skip the IPsec lookup completely and just
>> do normal routing?
>>
>> Steffen, what do you think?
>>
>> So I think it boils down to two choices:
>>
>> 1) We could simply drop IPsec processing if we notice that A
>> (fl.fl4_dst) is local;
>
> Hi, Herbert! Thanks for your suggestions very much.
>
> maybe we can fix it as below:
> diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
> index e1384e7331d8..5f63c4dde4e9 100644
> --- a/net/ipv4/icmp.c
> +++ b/net/ipv4/icmp.c
> @@ -517,7 +517,9 @@ static struct rtable *icmp_route_lookup(struct net 
> *net,
>                           flowi4_to_flowi(fl4), NULL, 0);
>         rt = dst_rtable(dst);
>         if (!IS_ERR(dst)) {
> -               if (rt != rt2)
> +               unsigned int addr_type = inet_addr_type_dev_table(net, 
> route_lookup_dev, fl4->daddr);
> +               if (rt != rt2 || addr_type == RTN_LOCAL)
>                         return rt;
>         } else if (PTR_ERR(dst) == -EPERM) {
>                 rt = NULL;
>> 2) Or we could take the __ip_route_output_key code path and
>> continue to do the xfrm lookup when A is local.

sorry,  resend it due to format problems

If only skip input route lookup as below, xfrm_lookup check may return 
ENOENT
for no xfrm pol or dst nofrm flag.

@@ -543,6 +544,10 @@ static struct rtable *icmp_route_lookup(struct net 
*net,
                         err = PTR_ERR(rt2);
                         goto relookup_failed;
                 }
+
+               unsigned int addr_type = inet_addr_type_dev_table(net, 
route_lookup_dev, fl4->daddr);
+               if (addr_type == RTN_LOCAL)
+                       goto relookup;
                 /* Ugh! */
                 orefdst = skb_in->_skb_refdst; /* save old refdst */

xfrm_lookup
     xfrm_lookup_with_ifid
         if (!if_id && ((dst_orig->flags & DST_NOXFRM) ||
             !net->xfrm.policy_count[XFRM_POLICY_OUT]))
             goto nopol;

Thanks a lot!
Dong Chenchen

> If only skip input route lookup as below, xfrm_lookup check may return 
> ENOENT
> for no xfrm pol or dst nofrm flag.
>
> @@ -543,6 +544,10 @@ static struct rtable *icmp_route_lookup(struct 
> net *net, err = PTR_ERR(rt2); goto relookup_failed; } + + unsigned int 
> addr_type = inet_addr_type_dev_table(net, route_lookup_dev, 
> fl4->daddr); + if (addr_type == RTN_LOCAL) + goto relookup; /* Ugh! */ 
> orefdst = skb_in->_skb_refdst; /* save old refdst */
>
> xfrm_lookup
> xfrm_lookup_with_ifid
>       if (!if_id && ((dst_orig->flags & DST_NOXFRM) ||
> !net->xfrm.policy_count[XFRM_POLICY_OUT]))
>         goto nopol;
>
> Thanks a lot!
> Dong Chenchen
>
>> Thanks,
dongchenchen (A) Nov. 14, 2024, 1:41 a.m. UTC | #6
On 2024/11/13 22:19, dongchenchen (A) wrote:
>
> On 2024/11/13 22:13, dongchenchen (A) wrote:
>>>> If skb_in is outbound, fl4_dec.saddr is not nolocal. It may be no 
>>>> input
>>>> route from B to A for
>>>>
>>>> first communication.
>>> You're right.  So the problem here is that for the case of A
>>> being local, we should not be taking the ip_route_input code
>>> path (this is intended for forwarded packets).
>>>
>>> In fact if A is local, and we're sending an ICMP message to A,
>>> then perhaps we could skip the IPsec lookup completely and just
>>> do normal routing?
>>>
>>> Steffen, what do you think?
>>>
>>> So I think it boils down to two choices:
>>>
>>> 1) We could simply drop IPsec processing if we notice that A
>>> (fl.fl4_dst) is local;
>>
>> Hi, Herbert! Thanks for your suggestions very much.
>>
>> maybe we can fix it as below:
>> diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
>> index e1384e7331d8..5f63c4dde4e9 100644
>> --- a/net/ipv4/icmp.c
>> +++ b/net/ipv4/icmp.c
>> @@ -517,7 +517,9 @@ static struct rtable *icmp_route_lookup(struct 
>> net *net,
>>                           flowi4_to_flowi(fl4), NULL, 0);
>>         rt = dst_rtable(dst);
>>         if (!IS_ERR(dst)) {
>> -               if (rt != rt2)
>> +               unsigned int addr_type = 
>> inet_addr_type_dev_table(net, route_lookup_dev, fl4->daddr);
>> +               if (rt != rt2 || addr_type == RTN_LOCAL)
>>                         return rt;
>>         } else if (PTR_ERR(dst) == -EPERM) {
>>                 rt = NULL;
>>> 2) Or we could take the __ip_route_output_key code path and
>>> continue to do the xfrm lookup when A is local.
>
> sorry,  resend it due to format problems
>
> If only skip input route lookup as below, xfrm_lookup check may return 
> ENOENT
> for no xfrm pol or dst nofrm flag.

Sorry, I misunderstood earlier.
it's correct to return ENOENT here.
The difference from the above is:
If A is local, rt1->dst->dev is lo (fl4->dst is A),
rt2->dst->dev is actual dev selected by daddr B(fl4_2->dst is B)

>
> @@ -543,6 +544,10 @@ static struct rtable *icmp_route_lookup(struct 
> net *net,
>                         err = PTR_ERR(rt2);
>                         goto relookup_failed;
>                 }
> +
> +               unsigned int addr_type = inet_addr_type_dev_table(net, 
> route_lookup_dev, fl4->daddr);
> +               if (addr_type == RTN_LOCAL)
> +                       goto relookup;
>                 /* Ugh! */
>                 orefdst = skb_in->_skb_refdst; /* save old refdst */
>
> xfrm_lookup
>     xfrm_lookup_with_ifid
>         if (!if_id && ((dst_orig->flags & DST_NOXFRM) ||
>             !net->xfrm.policy_count[XFRM_POLICY_OUT]))
>             goto nopol;
>
diff mbox series

Patch

diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index e1384e7331d8..11ef4eb5b659 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -490,6 +490,7 @@  static struct rtable *icmp_route_lookup(struct net *net,
 	struct dst_entry *dst, *dst2;
 	struct rtable *rt, *rt2;
 	struct flowi4 fl4_dec;
+	unsigned int addr_type;
 	int err;
 
 	memset(fl4, 0, sizeof(*fl4));
@@ -528,31 +529,15 @@  static struct rtable *icmp_route_lookup(struct net *net,
 	if (err)
 		goto relookup_failed;
 
-	if (inet_addr_type_dev_table(net, route_lookup_dev,
-				     fl4_dec.saddr) == RTN_LOCAL) {
-		rt2 = __ip_route_output_key(net, &fl4_dec);
-		if (IS_ERR(rt2))
-			err = PTR_ERR(rt2);
-	} else {
-		struct flowi4 fl4_2 = {};
-		unsigned long orefdst;
-
-		fl4_2.daddr = fl4_dec.saddr;
-		rt2 = ip_route_output_key(net, &fl4_2);
-		if (IS_ERR(rt2)) {
-			err = PTR_ERR(rt2);
-			goto relookup_failed;
-		}
-		/* Ugh! */
-		orefdst = skb_in->_skb_refdst; /* save old refdst */
-		skb_dst_set(skb_in, NULL);
-		err = ip_route_input(skb_in, fl4_dec.daddr, fl4_dec.saddr,
-				     tos, rt2->dst.dev);
-
-		dst_release(&rt2->dst);
-		rt2 = skb_rtable(skb_in);
-		skb_in->_skb_refdst = orefdst; /* restore old refdst */
-	}
+	addr_type = inet_addr_type_dev_table(net, route_lookup_dev, fl4_dec.saddr);
+	if (addr_type == RTN_LOCAL || addr_type == RTN_UNICAST)
+		fl4_dec.flowi4_flags |= FLOWI_FLAG_ANYSRC;
+	else
+		fl4_dec.saddr = 0;
+
+	rt2 = __ip_route_output_key(net, &fl4_dec);
+	if (IS_ERR(rt2))
+		err = PTR_ERR(rt2);
 
 	if (err)
 		goto relookup_failed;