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
|