Modify an sk_buff from an NF_HOOK

Ian Norton bredroll at darkspace.org.uk
Wed Mar 2 05:30:52 CET 2005


On Tue, Mar 01, 2005 at 08:58:18PM -0500, Tobias DiPasquale wrote:
> On Wed, 2 Mar 2005 01:50:34 +0000, Ian Norton <bredroll at darkspace.org.uk> wrote:
> > Hi everyone,
> > 
> > I've been faffing about for well over two weeks now, and, have come almost to
> > the conculsion (as I hauntingly suspected) that 'any' changes to an skb that
> > involve changes outside of the original values of skb->head and skb->tail are
> > impossible to do safely.
> 
> No its not. How about some code, buddy? What exactly are you doing and
> where are you doing it?

Im aware Im probably doing it all wrong :-).

here goes,

my hook functions is registered at the NF_IP_LOCAL_IN with NF_IP_PRI_FIRST
after some testing to see if a packet contains eg IPCOMP going from some
specific ip, i pass the skb (via the original pointer i get from the hook) 
to a function to decompress the contents of the IPCOMP data.

Im doing this in 2.4.2x .

// registered with netfilter as my hook func 
unsigned int
compressed_rx (unsigned int nfhook, struct sk_buff **skb,
	      const struct net_device *in, const struct net_device *our,
	      int (*okfn) (struct sk_buff *))
{

	struct sk_buff *skbuff = skb;
	int rx_retval = NF_ACCEPT;

	/* ignore anything that isnt a packet with an IPComp payload */
	if (skbuff->nh.iph->protocol != PROTO_IPCOMP)
		return NF_DROP;

	/* stuff removed here for clarity, nothing to do with the skb */	

	return sk_buff_data_decompress (skbuff, ACTION_INFLATE_MANUAL);
}



/* decompress the data of an sk_buff according to it's cpi */
int
sk_buff_data_decompress (struct sk_buff *_skb, int action)
{
	void *zlib_output_start;	// we will put the decompressed payload here
	u8 zlib_output_buffer[MAX_PAYLOAD_SIZE];
	size_t zlib_output_size;	// the amount of memory requested at *zlib_output_start

	int output_bytes;	// set to the length of output from zlib
	size_t payload_size;	// the total size of the payload + ipcomp hdr
	size_t new_payload_size; // decompressed payload size
	size_t ipcomp_size;	    // same as payload_size
	size_t ipcomp_payload_size;	// the length of the ipcomp data
	size_t payload_size_delta; // absolute diff between payload_size and new_payload_size
	
	u8 payload_protocol;	// save the payload value from the ipcomp header

	struct iphdr *ip_head;	// the start of the IP (nh) in the sk_buff
	struct iphdr *orig_ip_head;
	size_t iphead_size;	
	
	pneumatic_header *ipcomp_head;

	void *payload_start;	// the start of the ip payload in the skb_buff
	void *ipcomp_payload_start;	// the start of the compressed data

	struct sk_buff *skb = _skb; // do work here	
	struct sk_buff *newskb;		
	
	ip_head = skb->nh.iph;
	payload_start = skb->h.raw;
	ipcomp_head = (pneumatic_header *) payload_start;
	ipcomp_payload_start =
			(void *) ipcomp_head + sizeof (pneumatic_header);


	payload_size = ntohs (ip_head->tot_len) - (ip_head->ihl * 4);
	ipcomp_size = payload_size;
	ipcomp_payload_size = ipcomp_size - sizeof (pneumatic_header);


	/* the proto number of the compressed data */
	payload_protocol = ipcomp_head->payload_proto;

	/* set space for decompressed output */
	zlib_output_size = (size_t) MAX_PAYLOAD_SIZE;
	zlib_output_start = &zlib_output_buffer;

    /* this works without any problems, i get correct decompressed data here */
	output_bytes = pneu_inflate_decompress (ipcomp_payload_start,
						 ipcomp_payload_size, zlib_output_start, zlib_output_size);

	// compression didnt work?!     
	if (output_bytes <= 0)
		return NF_DROP;


	// packet seemed to decompress ok,
	
	new_payload_size = (size_t)output_bytes;	// length of decompressed payload
	payload_size_delta = new_payload_size - payload_size; // increased by this length 
	
	if (skb_tailroom(skb) < payload_size_delta)
		return NF_DROP; // buffer didnt have enough space in. 
	

	// the payload decompressed ok here,
	// now expand the skb's data and put the result in the payload
	// of the ip packet.

	iphead_size = sizeof(struct iphdr);
	orig_ip_head = kmalloc( iphead_size, GFP_ATOMIC );
	if (! orig_ip_head )
		return NF_DROP;

	memcpy( orig_ip_head, skb->nh.raw, iphead_size);
		
	// adjust the data area to fit the new payload
	skb_put(skb, payload_size_delta);
		
	// now copy back the old ip header
	memcpy(skb->nh.raw , orig_ip_head ,iphead_size);
	kfree(orig_ip_head);

	skb->nh.iph->protocol = payload_protocol;	// set the old protocol's value
	skb->nh.iph->tot_len = htons (ntohs(skb->nh.iph->tot_len) + payload_size_delta);
		
	// copy the stuff we got from zlib into the skb's data area
	memcpy (skb->h.raw, zlib_output_start, (unsigned int) output_bytes);

	// update ip header csum
	ip_send_check(skb->nh.iph);

	// right now, i can see the entirety of the buffer, if i copy the skb
    // and send it to myself with netif_rx() i can see the whole buffer.

    // when netfilter releases the skb back to the application/linux,
    // I see the decompressed data but it is truncated to the length of the
    // original recieved skb.
			
	return NF_ACCEPT;
}

I hope this makes sense, Ive chopped quite a fair chunk out to do with some
conditions about when to ignore or when to decompress.

Thanks for your help :-)


-- 
Ian Norton-Badrul




More information about the netfilter-devel mailing list