netdev
[Top] [All Lists]

Re: [BUG] Kernel oops with slip+dnat

To: Sami Ponkanen <sami.ponkanen@xxxxxx>
Subject: Re: [BUG] Kernel oops with slip+dnat
From: Harald Welte <laforge@xxxxxxxxxxxx>
Date: Sat, 12 Jan 2002 21:23:30 +0100
Cc: netdev@xxxxxxxxxxx, Netfilter Development Mailinglist <netfilter-devel@xxxxxxxxxxxxxxx>
In-reply-to: <200201080948.LAA15338@xxxxxxxxxxxxxx>; from sami.ponkanen@xxxxxx on Tue, Jan 08, 2002 at 11:40:54AM +0200
Mail-followup-to: Harald Welte <laforge@xxxxxxxxxxxx>, Sami Ponkanen <sami.ponkanen@xxxxxx>, netdev@xxxxxxxxxxx, Netfilter Development Mailinglist <netfilter-devel@xxxxxxxxxxxxxxx>
References: <200201080948.LAA15338@xxxxxxxxxxxxxx>
Sender: owner-netdev@xxxxxxxxxxx
User-agent: Mutt/1.3.17i
On Tue, Jan 08, 2002 at 11:40:54AM +0200, Sami Ponkanen wrote:
> I posted this yesterday to netfilter-devel and linux networking lists, but I 
> was instructed that this list might suit better.

Well, from my perspective it's not sure.

> 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.
[...]

 
> 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.

mh. I'm not sure why we append an ethernet header, but in any case I think
netfilter is expected to do some more work.

So if we have a NAT rule in the OUTPUT chain, and we call route_me_harder()
from ip_nat_local_fn() we need to check if the hh_len of the output device
has changed.  If it has, we need to check if skb has enough headroom and
potentially re-allocate the skb headroom.

Question to the networking gurus:

Is it true that the core networking code expects the skb to have enough
headroom for the hardware header at the time we return from the netfilter
NF_IP_LOCAL_OUT hook?


> 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.

sure. As stated above, I think we need to re-allocate headroom inside the
netfilter hook.

> 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?

no idea. But as loopback is a physical device, it should have at least some
information about which l3 protocol the packet is... and using ethernet 
seems convenient. 

> Regards,
> Sami Pönkänen

-- 
Live long and prosper
- Harald Welte / laforge@xxxxxxxxxxxx               http://www.gnumonks.org/
============================================================================
GCS/E/IT d- s-: a-- C+++ UL++++$ P+++ L++++$ E--- W- N++ o? K- w--- O- M- 
V-- PS+ PE-- Y+ PGP++ t++ 5-- !X !R tv-- b+++ DI? !D G+ e* h+ r% y+(*)

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