skbuff.c

来自「xen虚拟机源代码安装包」· C语言 代码 · 共 531 行

C
531
字号
/* *	Routines having to do with the 'struct sk_buff' memory handlers. * *	Authors:	Alan Cox <iiitac@pyr.swan.ac.uk> *			Florian La Roche <rzsfl@rz.uni-sb.de> * *	Fixes: *		Alan Cox	:	Fixed the worst of the load *					balancer bugs. *		Dave Platt	:	Interrupt stacking fix. *	Richard Kooijman	:	Timestamp fixes. *		Alan Cox	:	Changed buffer format. *		Alan Cox	:	destructor hook for AF_UNIX etc. *		Linus Torvalds	:	Better skb_clone. *		Alan Cox	:	Added skb_copy. *		Alan Cox	:	Added all the changed routines Linus *					only put in the headers *		Ray VanTassle	:	Fixed --skb->lock in free *		Alan Cox	:	skb_copy copy arp field *		Andi Kleen	:	slabified it. *		Robert Olsson	:	Removed skb_head_pool * *	This program is free software; you can redistribute it and/or *	modify it under the terms of the GNU General Public License *	as published by the Free Software Foundation; either version *	2 of the License, or (at your option) any later version. */#include <stddef.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include "allocate.h"#include "debug.h"#include "skbuff.h"#define SKB_DATA_ALIGN(size) ((((size) + 7) >> 3) << 3)/** *	skb_over_panic	- 	private function *	@skb: buffer *	@sz: size *	@here: address * *	Out of line support code for skb_put(). Not user callable. */void skb_over_panic(struct sk_buff *skb, int sz, void *here){        eprintf("skput:over: %p:%d put:%d\n", here, skb->len, sz);    	BUG();}/** *	skb_under_panic	- 	private function *	@skb: buffer *	@sz: size *	@here: address * *	Out of line support code for skb_push(). Not user callable. */void skb_under_panic(struct sk_buff *skb, int sz, void *here){        eprintf("skput:under: %p:%d put:%d\n", here, skb->len, sz);        BUG();}/** *	alloc_skb	-	allocate a network buffer *	@size: size to allocate *	@gfp_mask: allocation mask * *	Allocate a new &sk_buff. The returned buffer has no headroom and a *	tail room of size bytes. The object has a reference count of one. *	The return is the buffer. On a failure the return is %NULL. */struct sk_buff *alloc_skb(unsigned int size, int gfp_mask){	struct sk_buff *skb;	u8 *data;	/* Get the HEAD */	skb = ALLOCATE(struct sk_buff);	if (!skb)		goto out;	/* Get the DATA. Size must match skb_add_mtu(). */	size = SKB_DATA_ALIGN(size);	data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);	if (!data)		goto nodata;	memset(skb, 0, offsetof(struct sk_buff, truesize));	skb->truesize = size + sizeof(struct sk_buff);	atomic_set(&skb->users, 1);	skb->head = data;	skb->data = data;	skb->tail = data;	skb->end  = data + size;        skb->list = NULL;	atomic_set(&(skb_shinfo(skb)->dataref), 1);	skb_shinfo(skb)->nr_frags  = 0;	skb_shinfo(skb)->tso_size = 0;	skb_shinfo(skb)->tso_segs = 0;	skb_shinfo(skb)->frag_list = NULL;out:	return skb;nodata:	kfree(skb);	skb = NULL;	goto out;}void skb_release_data(struct sk_buff *skb){        kfree(skb->head);}/* *	Free an skbuff by memory without cleaning the state. */void kfree_skbmem(struct sk_buff *skb){	skb_release_data(skb);	kfree(skb);}/** *	__kfree_skb - private function *	@skb: buffer * *	Free an sk_buff. Release anything attached to the buffer. *	Clean the state. This is an internal helper function. Users should *	always call kfree_skb */void __kfree_skb(struct sk_buff *skb){	if (skb->list) {	 	wprintf("Warning: kfree_skb passed an skb still "                        "on a list.\n");		//BUG();	}	if(skb->destructor) {		skb->destructor(skb);	}	kfree_skbmem(skb);}/** *	skb_clone	-	duplicate an sk_buff *	@skb: buffer to clone *	@gfp_mask: allocation priority * *	Duplicate an &sk_buff. The new one is not owned by a socket. Both *	copies share the same packet data but not structure. The new *	buffer has a reference count of 1. If the allocation fails the *	function returns %NULL otherwise the new buffer is returned. * *	If this function is called from an interrupt gfp_mask() must be *	%GFP_ATOMIC. */struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask){    return pskb_copy(skb, gfp_mask);}static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old){	/*	 *	Shift between the two data areas in bytes	 */	unsigned long offset = new->data - old->data;	new->list	= NULL;	new->protocol	= old->protocol;	new->h.raw	= old->h.raw + offset;	new->nh.raw	= old->nh.raw + offset;	new->mac.raw	= old->mac.raw + offset;	new->pkt_type	= old->pkt_type;	new->destructor = NULL;	atomic_set(&new->users, 1);}/** *	pskb_expand_head - reallocate header of &sk_buff *	@skb: buffer to reallocate *	@nhead: room to add at head *	@ntail: room to add at tail *	@gfp_mask: allocation priority * *	Expands (or creates identical copy, if &nhead and &ntail are zero) *	header of skb. &sk_buff itself is not changed. &sk_buff MUST have *	reference count of 1. Returns zero in the case of success or error, *	if expansion failed. In the last case, &sk_buff is not changed. * *	All the pointers pointing into skb header may change and must be *	reloaded after call to this function. */int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, int gfp_mask){	u8 *data;	int size = nhead + (skb->end - skb->head) + ntail;	long off;	if (skb_shared(skb))		BUG();	size = SKB_DATA_ALIGN(size);	data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);	if (!data)		goto nodata;	/* Copy only real data... and, alas, header. This should be	 * optimized for the cases when header is void. */	memcpy(data + nhead, skb->head, skb->tail - skb->head);	memcpy(data + size, skb->end, sizeof(struct skb_shared_info));	skb_release_data(skb);	off = (data + nhead) - skb->head;	skb->head     = data;	skb->end      = data + size;	skb->data    += off;	skb->tail    += off;	skb->mac.raw += off;	skb->h.raw   += off;	skb->nh.raw  += off;	return 0;nodata:	return -ENOMEM;}struct sk_buff *pskb_copy(struct sk_buff *skb, int gfp_mask){	/*	 *	Allocate the copy buffer	 */	struct sk_buff *n = alloc_skb(skb->end - skb->head, gfp_mask);	if (!n)		goto out;	/* Set the data pointer */	skb_reserve(n, skb->data - skb->head);	/* Set the tail pointer and length */	skb_put(n, skb_headlen(skb));	/* Copy the bytes */	memcpy(n->data, skb->data, n->len);	n->data_len  = skb->data_len;	n->len	     = skb->len;	copy_skb_header(n, skb);out:	return n;}/* Make private copy of skb with writable head and some headroom */struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom){	struct sk_buff *skb2;	int delta = headroom - skb_headroom(skb);	if (delta <= 0)		skb2 = pskb_copy(skb, GFP_ATOMIC);	else {            skb2 = skb_copy_expand(skb, headroom, 0, GFP_ATOMIC);	}	return skb2;}/** *	skb_copy_expand	-	copy and expand sk_buff *	@skb: buffer to copy *	@newheadroom: new free bytes at head *	@newtailroom: new free bytes at tail *	@gfp_mask: allocation priority * *	Make a copy of both an &sk_buff and its data and while doing so *	allocate additional space. * *	This is used when the caller wishes to modify the data and needs a *	private copy of the data to alter as well as more space for new fields. *	Returns %NULL on failure or the pointer to the buffer *	on success. The returned buffer has a reference count of 1. * *	You must pass %GFP_ATOMIC as the allocation priority if this function *	is called from an interrupt. * *	BUG ALERT: ip_summed is not copied. Why does this work? Is it used *	only by netfilter in the cases when checksum is recalculated? --ANK */struct sk_buff *skb_copy_expand(const struct sk_buff *skb,				int newheadroom, int newtailroom, int gfp_mask){	/*	 *	Allocate the copy buffer	 */	struct sk_buff *n = alloc_skb(newheadroom + skb->len + newtailroom,				      gfp_mask);	int head_copy_len, head_copy_off;	if (!n)		return NULL;	skb_reserve(n, newheadroom);	/* Set the tail pointer and length */	skb_put(n, skb->len);	head_copy_len = skb_headroom(skb);	head_copy_off = 0;	if (newheadroom <= head_copy_len)		head_copy_len = newheadroom;	else		head_copy_off = newheadroom - head_copy_len;	/* Copy the linear header and data. */	if (skb_copy_bits(skb, -head_copy_len, n->head + head_copy_off,			  skb->len + head_copy_len))		BUG();	copy_skb_header(n, skb);	return n;}/* Copy some data bits from skb to kernel buffer. */int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len){	int copy;	int start = skb_headlen(skb);	if (offset > (int)skb->len - len)		goto fault;	/* Copy header. */	if ((copy = start - offset) > 0) {		if (copy > len)			copy = len;		memcpy(to, skb->data + offset, copy);		if ((len -= copy) == 0)			return 0;		offset += copy;		to     += copy;	}	if (!len)		return 0;fault:	return -EFAULT;}/** *	skb_dequeue - remove from the head of the queue *	@list: list to dequeue from * *	Remove the head of the list. The list lock is taken so the function *	may be used safely with other locking list functions. The head item is *	returned or %NULL if the list is empty. */struct sk_buff *skb_dequeue(struct sk_buff_head *list){	unsigned long flags;	struct sk_buff *result;	spin_lock_irqsave(&list->lock, flags);	result = __skb_dequeue(list);	spin_unlock_irqrestore(&list->lock, flags);	return result;}/** *	skb_dequeue_tail - remove from the tail of the queue *	@list: list to dequeue from * *	Remove the tail of the list. The list lock is taken so the function *	may be used safely with other locking list functions. The tail item is *	returned or %NULL if the list is empty. */struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list){	unsigned long flags;	struct sk_buff *result;	spin_lock_irqsave(&list->lock, flags);	result = __skb_dequeue_tail(list);	spin_unlock_irqrestore(&list->lock, flags);	return result;}/** *	skb_queue_purge - empty a list *	@list: list to empty * *	Delete all buffers on an &sk_buff list. Each buffer is removed from *	the list and one reference dropped. This function takes the list *	lock and is atomic with respect to other list locking functions. */void skb_queue_purge(struct sk_buff_head *list){	struct sk_buff *skb;	while ((skb = skb_dequeue(list)) != NULL)		kfree_skb(skb);}/** *	skb_queue_head - queue a buffer at the list head *	@list: list to use *	@newsk: buffer to queue * *	Queue a buffer at the start of the list. This function takes the *	list lock and can be used safely with other locking &sk_buff functions *	safely. * *	A buffer cannot be placed on two lists at the same time. */void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk){	unsigned long flags;	spin_lock_irqsave(&list->lock, flags);	__skb_queue_head(list, newsk);	spin_unlock_irqrestore(&list->lock, flags);}/** *	skb_queue_tail - queue a buffer at the list tail *	@list: list to use *	@newsk: buffer to queue * *	Queue a buffer at the tail of the list. This function takes the *	list lock and can be used safely with other locking &sk_buff functions *	safely. * *	A buffer cannot be placed on two lists at the same time. */void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk){	unsigned long flags;	spin_lock_irqsave(&list->lock, flags);	__skb_queue_tail(list, newsk);	spin_unlock_irqrestore(&list->lock, flags);}/** *	skb_unlink	-	remove a buffer from a list *	@skb: buffer to remove * *	Place a packet after a given packet in a list. The list locks are taken *	and this function is atomic with respect to other list locked calls * *	Works even without knowing the list it is sitting on, which can be *	handy at times. It also means that THE LIST MUST EXIST when you *	unlink. Thus a list must have its contents unlinked before it is *	destroyed. */void skb_unlink(struct sk_buff *skb){	struct sk_buff_head *list = skb->list;	if (list) {		unsigned long flags;		spin_lock_irqsave(&list->lock, flags);		if (skb->list == list)			__skb_unlink(skb, skb->list);		spin_unlock_irqrestore(&list->lock, flags);	}}/** *	skb_append	-	append a buffer *	@old: buffer to insert after *	@newsk: buffer to insert * *	Place a packet after a given packet in a list. The list locks are taken *	and this function is atomic with respect to other list locked calls. *	A buffer cannot be placed on two lists at the same time. */void skb_append(struct sk_buff *old, struct sk_buff *newsk){	unsigned long flags;	spin_lock_irqsave(&old->list->lock, flags);	__skb_append(old, newsk);	spin_unlock_irqrestore(&old->list->lock, flags);}/** *	skb_insert	-	insert a buffer *	@old: buffer to insert before *	@newsk: buffer to insert * *	Place a packet before a given packet in a list. The list locks are taken *	and this function is atomic with respect to other list locked calls *	A buffer cannot be placed on two lists at the same time. */void skb_insert(struct sk_buff *old, struct sk_buff *newsk){	unsigned long flags;	spin_lock_irqsave(&old->list->lock, flags);	__skb_insert(newsk, old->prev, old, old->list);	spin_unlock_irqrestore(&old->list->lock, flags);}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?