Skip to contentSkip to conversation

icmp: Fixed bug in raw sockets causing incorrect ICMP SNMP counter values

PATCH In review Ben Cartwright-Cox 2 participants 8 messages Oct 11, 2015

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 icmp: Fixed bug in raw sockets causing incorrect ICMP SNMP counter values

Sending ICMP packets with raw sockets ends up in the SNMP counters logging the type as the first byte of the IPv4 header rather than the ICMP header (in nearly all cases this is seen as "OutType69". This is fixed by adding the IP Header Length to the casting into a icmphdr struct.

---
 net/ipv4/raw.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv4/raw.c +1 -1
index 561cd4b..1ad8bae 100644
@@ -409,7 +409,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
 	}
 	if (iph->protocol == IPPROTO_ICMP)
 		icmp_out_count(net, ((struct icmphdr *)
-			skb_transport_header(skb))->type);
+			skb_transport_header(skb) + iphlen)->type);
 
 	err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, sk, skb,
 		      NULL, rt->dst.dev, dst_output_sk);
Signature · 1 line
1.9.1
#1 lore raw
Quoted 16 lines · Ben Cartwright-Cox

Sending ICMP packets with raw sockets ends up in the SNMP counters logging the type as the first byte of the IPv4 header rather than the ICMP header (in nearly all cases this is seen as "OutType69". This is fixed by adding the IP Header Length to the casting into a icmphdr struct.

---
 net/ipv4/raw.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv4/raw.c +0 -0
index 561cd4b..1ad8bae 100644

net/ipv4/raw.c static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, · line 415

Quoted 8 lines
net/ipv4/raw.c +1 -1
@@ -409,7 +409,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
 	}
 	if (iph->protocol == IPPROTO_ICMP)
 		icmp_out_count(net, ((struct icmphdr *)
-			skb_transport_header(skb))->type);
+			skb_transport_header(skb) + iphlen)->type);
 
 	err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, sk, skb,
 		      NULL, rt->dst.dev, dst_output_sk);

Hmm... This seems to lack checks against a malicious user ?

The only guarantee you have here is that iphlen < length.

It is not enough.

Make sure you do not access not initialized memory or even non existent one.

author
#2 lore raw

Forgive me for possibly being a little stupid here (This is my first patch to Linux so I am slightly over my head)

Is this issue not addressed above the file where the following check is done?

if (iphlen > length)

    goto error_free;
Quoted 38 lines · Eric Dumazet
Quoted 16 lines · Ben Cartwright-Cox

Sending ICMP packets with raw sockets ends up in the SNMP counters logging the type as the first byte of the IPv4 header rather than the ICMP header (in nearly all cases this is seen as "OutType69". This is fixed by adding the IP Header Length to the casting into a icmphdr struct.

---
 net/ipv4/raw.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv4/raw.c +0 -0
index 561cd4b..1ad8bae 100644

net/ipv4/raw.c static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, · line 415

Quoted 8 lines
net/ipv4/raw.c +1 -1
@@ -409,7 +409,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
      }
      if (iph->protocol == IPPROTO_ICMP)
              icmp_out_count(net, ((struct icmphdr *)
-                     skb_transport_header(skb))->type);
+                     skb_transport_header(skb) + iphlen)->type);
 
      err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, sk, skb,
                    NULL, rt->dst.dev, dst_output_sk);

Hmm... This seems to lack checks against a malicious user ?

The only guarantee you have here is that iphlen < length.

It is not enough.

Make sure you do not access not initialized memory or even non existent one.

#3 lore raw
Quoted 8 lines · Ben Cox

Forgive me for possibly being a little stupid here (This is my first patch to Linux so I am slightly over my head)

Is this issue not addressed above the file where the following check is done?

if (iphlen > length)

    goto error_free;

Imagine someone sends a frame, pretending it is ICMP, but containing only the IPv4 header. And not a _single_ byte more.

length = 20

iphlen = 20 (if say ihl == 5)

We copied 20 bytes from user land.

But your code reads 21th byte.

#4 lore raw
Quoted 1 line · Eric Dumazet

But your code reads 21th byte.

BTW, nice catch !

Your patch only need a small addition.

Thanks.

#5 lore raw
Quoted 5 lines · Eric Dumazet
Quoted 1 line · Eric Dumazet

But your code reads 21th byte.

BTW, nice catch !

Maybe the following one.

1) We properly set transport header
2) We use icmp_hdr() helper.

net/ipv4/raw.c +4 -4
index 561cd4b8fc6e..ffe25cd1f0e0 100644
@@ -406,11 +406,11 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
 			ip_select_ident(net, skb, NULL);
 
 		iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+		skb->transport_header += iphlen;
+		if (iph->protocol == IPPROTO_ICMP &&
+		    length >= iphlen + sizeof(struct icmphdr))
+			icmp_out_count(net, icmp_hdr(skb)->type);
 	}
-	if (iph->protocol == IPPROTO_ICMP)
-		icmp_out_count(net, ((struct icmphdr *)
-			skb_transport_header(skb))->type);
-
 	err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, sk, skb,
 		      NULL, rt->dst.dev, dst_output_sk);
 	if (err > 0)
author
#6 lore raw

Nice!

That works in my head at least, Sorry about not seeing that fairly glaring memory issue there.

Are you sure " skb->transport_header += iphlen; " won't have a knock on affect when it's given to NF_HOOK ( as in, would a potential userspace program get something it does not expect anymore ) ?

How does submission work at this point if the above is not a issue (apologies if this is already in a FAQ somewhere I missed)?

Quoted 34 lines · Eric Dumazet
Quoted 5 lines · Eric Dumazet
Quoted 1 line · Eric Dumazet

But your code reads 21th byte.

BTW, nice catch !

Maybe the following one.

1) We properly set transport header
2) We use icmp_hdr() helper.

net/ipv4/raw.c +4 -4
index 561cd4b8fc6e..ffe25cd1f0e0 100644
@@ -406,11 +406,11 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
                        ip_select_ident(net, skb, NULL);
 
                iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+               skb->transport_header += iphlen;
+               if (iph->protocol == IPPROTO_ICMP &&
+                   length >= iphlen + sizeof(struct icmphdr))
+                       icmp_out_count(net, icmp_hdr(skb)->type);
        }
-       if (iph->protocol == IPPROTO_ICMP)
-               icmp_out_count(net, ((struct icmphdr *)
-                       skb_transport_header(skb))->type);
-
        err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, sk, skb,
                      NULL, rt->dst.dev, dst_output_sk);
        if (err > 0)
#7 lore raw

Please do not top-post on netdev and/or lkml

Quoted 4 lines · Ben Cox

Nice!

That works in my head at least, Sorry about not seeing that fairly glaring memory issue there.

No problem, this is why we review patches ;)

Quoted 4 lines

Are you sure " skb->transport_header += iphlen; " won't have a knock on affect when it's given to NF_HOOK ( as in, would a potential userspace program get something it does not expect anymore ) ?

netfilter does not depend on transport_header being set (otherwise as you can see RAW packets would have problems with netfilter since the very beginning.

Quoted 3 lines

How does submission work at this point if the above is not a issue (apologies if this is already in a FAQ somewhere I missed)?

Just send a V2 of the patch, using following title :

[PATCH v2 net] raw: increment correct SNMP counters for ICMP messages

I'll add my 'Acked-by:' after your 'Signed-off-by:'

Thanks !