[BUG] Kernel oops with slip+dnat

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I posted this yesterday to netfilter-devel list, but perhaps this list is 
better.

Sami

I wrote on netfilter-devel:

Hello,

As I wrote earlier on netfilter list and more recently on linux networking 
list, there is a bug that results in a kernel oops when using DNAT or 
REDIRECT rule in the OUTPUT chain on a host with SLIP interfaces.

The bug is reproducible on atleast 2.4.7, 2.4.16 and 2.4.17. 
Here's how to do it:
1. modprobe slip
2. slattach -p slip -s 1200 /dev/ttyS0
3. ifconfig sl0 192.168.1.2 pointopoint 192.168.1.1
4. iptables -t nat -A OUTPUT -d 192.168.1.1 -j REDIRECT or
iptables -t nat -A OUTPUT -d 192.168.1.1 -j DNAT --to-destination 192.168.1.2
5. ping 192.168.1.1 or send a UDP packet to 192.168.1.1
6. Oops!

I've traced the problem and it seems that the problem is following:

A buffer for the packet is reserved in ip_build_xmit() 
(net/ipv4/ip_output.c:627) and the correct size for the buffer is calculated 
on line 667:

int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;

Now here (I think, correct me if I'm wrong) the hard_header_len is just 1 
byte, the SLIP header byte.

Later on the control goes through nf_hook_slow() (net/core/netfilter.c:445) 
where the packet is put on another output device (skb->dev changes). The 
new device has a different hard_header_len, but skb has only space for the 
1-byte SLIP header! Am I on the right tracks here?

Ok, again few steps forward and the control reaches neigh_resolve_output() 
(net/core/neighbour.c:950). Here the function dev->hard_header() is called 
and consequently ether_header() (net/ethernet/eth.c:75) is called (why?). 
Right in the beginning of the function the call to skb_push(skb, ETH_HLEN) 
results in skb_under_panic() and BUG() and consequently the system crashes.

A quick fix is to reserve few extra bytes in ip_build_xmit(). I tried 
changing line 676 in ip_output.c from this: 
int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;

into this:
int hh_len = (rt->u.dst.dev->hard_header_len + 31)&~15;

and voila, no more oopses. Well, this is definitely not the correct way to 
fix the problem, but it works for now.

Now, a few questions came to my mind while debugging the problem. Firstly, 
why do you put an ethernet header on a packet that is sent via the loopback 
device?

Secondly why call skb_under_panic() in skb_push()? Shouldn't the packet 
rather be just silently discarded? Anyway I think we all agree that it should 
not crash the whole kernel, right?


Regards,

Sami Pönkänen
-
: send the line "unsubscribe linux-net" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux 802.1Q VLAN]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Git]     [Bugtraq]     [Yosemite News and Information]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux PCI]     [Linux Admin]     [Samba]

  Powered by Linux