2011年12月20日 星期二

Packet Buffer 操作的幾個內嵌函數

Packet buffer 由sk_buff結構描述, Packet buffer Data由其head和end成員界定, 而Packet Data則由Packet Data內data和tail界定的子區域來描述, 採用這種結構可以使添加或去除Packet 頭的操作變得非常方便.

skb_put(skb,len)  在Packet 尾部擴展長度為len的Data block, 返回擴展塊的地址, __skb_put()為未校驗版本
skb_push(skb,len) 在Packet 前部擴展長度為len的Data block, 返回擴展塊的地址, __skb_push()為未校驗版本
skb_pull(skb,len) 去除Packet 前部長度為len的Data block, 返回新Packet 的起始地址, __skb_pull()為未校驗版本
skb_headroom(skb) 返回Packet 前部距離Packet 區開始的長度
skb_tailroom(skb) 返回Packet 尾部距離Packet 區結束的長度
skb_reserve(skb,len) 設置Packet 起始位置為Packet 區開始的len字節
skb_trim(skb,len) 將Packet 截斷為len字節, __skb_trim()為未校驗版本

; include/linux/skbuff.h:

struct sk_buff {
...
unsigned int len; /* Length of actual data */
unsigned char *head; /* Head of buffer */
unsigned char *data; /* Data head pointer */
unsigned char *tail; /* Tail pointer */
unsigned char *end; /* End pointer */
...
}

/*
 * Add data to an sk_buff
 */

static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len)
{
unsigned char *tmp=skb->tail;
skb->tail+=len;
skb->len+=len;
return tmp;
}

/**
 * skb_put - add data to a buffer
 * @skb: buffer to use
 * @len: amount of data to add
 *
 * This function extends the used data area of the buffer. If this would
 * exceed the total buffer size the kernel will panic. A pointer to the
 * first byte of the extra data is returned.
 */

static inline unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
{
unsigned char *tmp=skb->tail;
skb->tail+=len;
skb->len+=len;
if(skb->tail>skb->end) {
skb_over_panic(skb, len, current_text_addr());
}
return tmp;
}

static inline unsigned char *__skb_push(struct sk_buff *skb, unsigned int len)
{
skb->data-=len;
skb->len+=len;
return skb->data;
}

/**
 * skb_push - add data to the start of a buffer
 * @skb: buffer to use
 * @len: amount of data to add
 *
 * This function extends the used data area of the buffer at the buffer
 * start. If this would exceed the total buffer headroom the kernel will
 * panic. A pointer to the first byte of the extra data is returned.
 */

static inline unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
{
skb->data-=len;
skb->len+=len;
if(skb->datahead) {
skb_under_panic(skb, len, current_text_addr());
}
return skb->data;
}

static inline char *__skb_pull(struct sk_buff *skb, unsigned int len)
{
skb->len-=len;
return skb->data+=len;
}

/**
 * skb_pull - remove data from the start of a buffer
 * @skb: buffer to use
 * @len: amount of data to remove
 *
 * This function removes data from the start of a buffer, returning
 * the memory to the headroom. A pointer to the next data in the buffer
 * is returned. Once the data has been pulled future pushes will overwrite
 * the old data.
 */

static inline unsigned char * skb_pull(struct sk_buff *skb, unsigned int len)
{
if (len > skb->len)
return NULL;
return __skb_pull(skb,len);
}

/**
 * skb_headroom - bytes at buffer head
 * @skb: buffer to check
 *
 * Return the number of bytes of free space at the head of an &sk_buff.
 */

static inline int skb_headroom(const struct sk_buff *skb)
{
return skb->data-skb->head;
}

/**
 * skb_tailroom - bytes at buffer end
 * @skb: buffer to check
 *
 * Return the number of bytes of free space at the tail of an sk_buff
 */

static inline int skb_tailroom(const struct sk_buff *skb)
{
return skb->end-skb->tail;
}

/**
 * skb_reserve - adjust headroom
 * @skb: buffer to alter
 * @len: bytes to move
 *
 * Increase the headroom of an empty &sk_buff by reducing the tail
 * room. This is only allowed for an empty buffer.
 */

static inline void skb_reserve(struct sk_buff *skb, unsigned int len)
{
skb->data+=len;
skb->tail+=len;
}


static inline void __skb_trim(struct sk_buff *skb, unsigned int len)
{
skb->len = len;
skb->tail = skb->data+len;
}

/**
 * skb_trim - remove end from a buffer
 * @skb: buffer to alter
 * @len: new length
 *
 * Cut the length of a buffer down by removing data from the tail. If
 * the buffer is already under the length specified it is not modified.
 */

static inline void skb_trim(struct sk_buff *skb, unsigned int len)
{
if (skb->len > len) {
__skb_trim(skb, len);
}
}






1. socket buffer : struct sk_buff
    (1) 宣告在 include/linux/skbuff.h
    (2) 重要欄位有


  unsigned char *head;  // 配置空間起點
  unsigned char *data;   // 有效資料起點
  unsigned char *tail;     // 有效資料的最後一個 Octet
  unsigned char *end;    // 配置空間終點

2. 配置 sk_buff



(1) struct sk_buff *alloc_skb(unsigned int len, int priority);
比較原始. 配置完成會將 skb-> data 和 skb->tail 指向 skb->head

(2) struct sk_buff *dev_alloc_skb(unsigned int len);
供ISR使用而設計, 以GFP_ATOMIC優先度來配置, 並在 skb->head 和 skb->data 之間保留空間, 用以存放協定標頭.





3. void skb_reserve(struct sk_buff *skb, int len);
    保留前頭空間.
    大多數的Ethernet 介面在封包之前會保留 2-bytes空間,
    這樣可使得 IP header 對齊 16-bytes 邊緣
    ( 因為 IP header 之前的 Ethernet header 有 14-bytes )





4. unsigned char * skb_put(struct sk_buff *skb, int len);
    unsigned char * __skb_put(struct sk_buff *skb, int len);
    將資料加到tail room 區, 並將 skb->tail 往後移
    skb_put() : 會檢查是否有足夠空間
    __skb_put() : 會省略檢查

How to repair and clone disk with ddrescue

  ddrescue  is a tool that can be used to repair and clone disks on a  Linux system . This includes hard drives, partitions, DVD discs, flas...