netdev
[Top] [All Lists]

[BUG] Kernel oops with slip+dnat

To: netdev@xxxxxxxxxxx
Subject: [BUG] Kernel oops with slip+dnat
From: Sami Ponkanen <sami.ponkanen@xxxxxx>
Date: Tue, 8 Jan 2002 11:40:54 +0200
Organization: VTT Information Technology
Sender: owner-netdev@xxxxxxxxxxx
I posted this yesterday to netfilter-devel and linux networking lists, but I 
was instructed that this list might suit 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

<Prev in Thread] Current Thread [Next in Thread>