diff mbox series

[net] ping: fix the dif and sdif check in ping_lookup

Message ID ea03066bc7256ab86df8d3501f3440819305be57.1644988852.git.lucien.xin@gmail.com (mailing list archive)
State Accepted
Commit 35a79e64de29e8d57a5989aac57611c0cd29e13e
Delegated to: Netdev Maintainers
Headers show
Series [net] ping: fix the dif and sdif check in ping_lookup | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net
netdev/fixes_present success Fixes tag present in non-next series
netdev/subject_prefix success Link
netdev/cover_letter success Single patches do not need cover letters
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 2 this patch: 2
netdev/cc_maintainers warning 2 maintainers not CCed: dsahern@kernel.org yoshfuji@linux-ipv6.org
netdev/build_clang success Errors and warnings before: 18 this patch: 18
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 7 this patch: 7
netdev/checkpatch warning WARNING: line length of 86 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Xin Long Feb. 16, 2022, 5:20 a.m. UTC
When 'ping' changes to use PING socket instead of RAW socket by:

   # sysctl -w net.ipv4.ping_group_range="0 100"

There is another regression caused when matching sk_bound_dev_if
and dif, RAW socket is using inet_iif() while PING socket lookup
is using skb->dev->ifindex, the cmd below fails due to this:

  # ip link add dummy0 type dummy
  # ip link set dummy0 up
  # ip addr add 192.168.111.1/24 dev dummy0
  # ping -I dummy0 192.168.111.1 -c1

The issue was also reported on:

  https://github.com/iputils/iputils/issues/104

But fixed in iputils in a wrong way by not binding to device when
destination IP is on device, and it will cause some of kselftests
to fail, as Jianlin noticed.

This patch is to use inet(6)_iif and inet(6)_sdif to get dif and
sdif for PING socket, and keep consistent with RAW socket.

Fixes: c319b4d76b9e ("net: ipv4: add IPPROTO_ICMP socket kind")
Reported-by: Jianlin Shi <jishi@redhat.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
---
 net/ipv4/ping.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

Comments

patchwork-bot+netdevbpf@kernel.org Feb. 17, 2022, 3:10 p.m. UTC | #1
Hello:

This patch was applied to netdev/net.git (master)
by David S. Miller <davem@davemloft.net>:

On Wed, 16 Feb 2022 00:20:52 -0500 you wrote:
> When 'ping' changes to use PING socket instead of RAW socket by:
> 
>    # sysctl -w net.ipv4.ping_group_range="0 100"
> 
> There is another regression caused when matching sk_bound_dev_if
> and dif, RAW socket is using inet_iif() while PING socket lookup
> is using skb->dev->ifindex, the cmd below fails due to this:
> 
> [...]

Here is the summary with links:
  - [net] ping: fix the dif and sdif check in ping_lookup
    https://git.kernel.org/netdev/net/c/35a79e64de29

You are awesome, thank you!
Jakub Kicinski Feb. 17, 2022, 3:57 p.m. UTC | #2
On Wed, 16 Feb 2022 00:20:52 -0500 Xin Long wrote:
>  	if (skb->protocol == htons(ETH_P_IP)) {
> +		dif = inet_iif(skb);
> +		sdif = inet_sdif(skb);
>  		pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n",
>  			 (int)ident, &ip_hdr(skb)->daddr, dif);
>  #if IS_ENABLED(CONFIG_IPV6)
>  	} else if (skb->protocol == htons(ETH_P_IPV6)) {
> +		dif = inet6_iif(skb);
> +		sdif = inet6_sdif(skb);
>  		pr_debug("try to find: num = %d, daddr = %pI6c, dif = %d\n",
>  			 (int)ident, &ipv6_hdr(skb)->daddr, dif);
>  #endif
> +	} else {
> +		pr_err("ping: protocol(%x) is not supported\n", ntohs(skb->protocol));
> +		return NULL;
>  	}

Are you sure this is not reachable from some networking path allowing
attacker (or local user) to DoS the kernel logs?
Xin Long Feb. 23, 2022, 6:08 a.m. UTC | #3
On Thu, Feb 17, 2022 at 11:57 PM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Wed, 16 Feb 2022 00:20:52 -0500 Xin Long wrote:
> >       if (skb->protocol == htons(ETH_P_IP)) {
> > +             dif = inet_iif(skb);
> > +             sdif = inet_sdif(skb);
> >               pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n",
> >                        (int)ident, &ip_hdr(skb)->daddr, dif);
> >  #if IS_ENABLED(CONFIG_IPV6)
> >       } else if (skb->protocol == htons(ETH_P_IPV6)) {
> > +             dif = inet6_iif(skb);
> > +             sdif = inet6_sdif(skb);
> >               pr_debug("try to find: num = %d, daddr = %pI6c, dif = %d\n",
> >                        (int)ident, &ipv6_hdr(skb)->daddr, dif);
> >  #endif
> > +     } else {
> > +             pr_err("ping: protocol(%x) is not supported\n", ntohs(skb->protocol));
> > +             return NULL;
> >       }
>
> Are you sure this is not reachable from some networking path allowing
> attacker (or local user) to DoS the kernel logs?
Hi, Jakub, sorry for late.

I actually didn't see how a skb with protocol != IP/IPV6 could reach here.
ping_err() is using "BUG()" for this kind of case.
I added this 'else' branch mostly to avoid the possible compiling
warning for "Use of uninitialized value dif/sdif".

Thanks.
Jakub Kicinski Feb. 23, 2022, 3:52 p.m. UTC | #4
On Wed, 23 Feb 2022 14:08:06 +0800 Xin Long wrote:
> On Thu, Feb 17, 2022 at 11:57 PM Jakub Kicinski <kuba@kernel.org> wrote:
> > Are you sure this is not reachable from some networking path allowing
> > attacker (or local user) to DoS the kernel logs?  
> Hi, Jakub, sorry for late.
> 
> I actually didn't see how a skb with protocol != IP/IPV6 could reach here.
> ping_err() is using "BUG()" for this kind of case.
> I added this 'else' branch mostly to avoid the possible compiling
> warning for "Use of uninitialized value dif/sdif".

Understood but it's best practice to avoid prints on the datapath.
We should either drop the print completely (given it's impossible)
or make it _once.
diff mbox series

Patch

diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index bcf7bc71cb56..3a5994b50571 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -172,16 +172,23 @@  static struct sock *ping_lookup(struct net *net, struct sk_buff *skb, u16 ident)
 	struct sock *sk = NULL;
 	struct inet_sock *isk;
 	struct hlist_nulls_node *hnode;
-	int dif = skb->dev->ifindex;
+	int dif, sdif;
 
 	if (skb->protocol == htons(ETH_P_IP)) {
+		dif = inet_iif(skb);
+		sdif = inet_sdif(skb);
 		pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n",
 			 (int)ident, &ip_hdr(skb)->daddr, dif);
 #if IS_ENABLED(CONFIG_IPV6)
 	} else if (skb->protocol == htons(ETH_P_IPV6)) {
+		dif = inet6_iif(skb);
+		sdif = inet6_sdif(skb);
 		pr_debug("try to find: num = %d, daddr = %pI6c, dif = %d\n",
 			 (int)ident, &ipv6_hdr(skb)->daddr, dif);
 #endif
+	} else {
+		pr_err("ping: protocol(%x) is not supported\n", ntohs(skb->protocol));
+		return NULL;
 	}
 
 	read_lock_bh(&ping_table.lock);
@@ -221,7 +228,7 @@  static struct sock *ping_lookup(struct net *net, struct sk_buff *skb, u16 ident)
 		}
 
 		if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif &&
-		    sk->sk_bound_dev_if != inet_sdif(skb))
+		    sk->sk_bound_dev_if != sdif)
 			continue;
 
 		sock_hold(sk);