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 + -
显示快捷键?