Extending packet header from within a Netfilter hook

Tag: network-programming , linux-kernel , ip , netfilter Author: sunlangqiao Date: 2011-03-29

I want to prepend IP header on an existing IP packet while inside NF_HOOK_LOCAL_OUT. The issue I face is that the skb expansion functions (such as copy/clone/expand/reallocate header) allocate a new sk_buff. We can not return this newly allocated pointer since netfilter hook function no longer (kernel version 2.6.31) passes the skb pointer's address (passes by value). How I solved the issue is as follows: 1. I got a new skb using skb_header_realloc(). This copies all the data from skb. 2. I modified the new skb (call it skb2) to prepend the new IP header, set appropriate values in the new IP header. 3. Replace the contents of the original skb (passed in the Netfilter hook function) with the contents of the skb2 using skb_morph(). Returned NF_ACCEPT.

Is this the only way of achieving what I intended to? Is there a more efficient solution? Are there other use cases of skb_morph (besides the IP reassembly code)?

Best Answer

This works for me, in 2.6 kernels:

struct iphdr* iph;
if (skb_headroom(skb) < sizeof(struct iphdr))
  if (0 != pskb_expand_head(skb, sizeof(struct iphdr) - skb_headroom(skb), 0, GFP_ATOMIC)) {
    return NF_STOLEN;
iph = (struct iphdr*) skb_push(skb, sizeof(struct iphdr));
//Fill ip packet
return NF_ACCEPT;

Hope it helps.


Thank you Fred. It works with a minor correction: pskb_expand_head retruns 0 on success. So the check should be changed to if (0 != pskb_expand_head(skb, sizeof(struct iphdr) - skb_headroom(skb), 0, GFP_ATOMIC)).
I forgot to add something to this post. I used kfree_skb because I am returning NF_STOLEN. You can return NF_DROP, but you cannot free skb. NF_DROP will tell the application that the packet failed to be sent. However NF_STOLEN does not.