点击上方蓝字【囧囧妹】一起学习,一起成长!
一、开篇
最近两年一直在学习密码安全,感谢我的职场老师带我入门,我才非常快速的进入这个行业。接上一节网络链路层加密实现01-skb最近整理了之前做的一套网络链路层加密的软件。这里主要涉及netfilter、skb、linux内核的一些东西。
我将我总结的资料整理成pdf版本放在了百度云盘上,感兴趣的可以关注囧囧妹公众号,回复“密码学常识”来获取。
二、skb相关函数
【skb初始化函数】
void __init skb_init(void){skbuff_head_cache = kmem_cache_create("skbuff_head_cache",sizeof(struct sk_buff),0,SLAB_HWCACHE_ALIGN|SLAB_PANIC,NULL);skbuff_fclone_cache = kmem_cache_create("skbuff_fclone_cache",sizeof(struct sk_buff_fclones),0,SLAB_HWCACHE_ALIGN|SLAB_PANIC,NULL);}//struct sk_buff_fclones结构体如下:/* Layout of fast clones : [skb1][skb2][fclone_ref] */struct sk_buff_fclones {struct sk_buff skb1;struct sk_buff skb2;atomic_t fclone_ref;};
创建skbuff_head_cache高速缓存,skbuff_fclone_cache创建两倍skb描述符长度的高速缓存,主要为解决在克隆时再次分配skb的问题,所以申请两倍skb的空间,克隆时直接使用后备,提高效率。
【dev_alloc_skb()分配skb】
alloc_skb()分配skb。数据缓冲区和skb描述符是两个不同的实体,在分配skb时需要分配两块内存,一块是数据缓冲区,一块是skb描述符。
static inline struct sk_buff *alloc_skb(unsigned int size, gfp_t priority){//size - 待分配skb的线性存储区的长度//priority – 分配内存的方式//0 – 预测是否会被克隆,用于确定是从哪个高速缓存中分配//NUMA_NO_NODE – 当支持NUMA时用于确定何种区域中分配SKBreturn __alloc_skb(size, priority, 0, NUMA_NO_NODE);}
dev_alloc_skb()分配一个缓冲区,通常被设备驱动用在中断上下文中。
/* legacy helper around netdev_alloc_skb() */static inline struct sk_buff *dev_alloc_skb(unsigned int length){return netdev_alloc_skb(NULL, length);}*/static inline struct sk_buff *netdev_alloc_skb(struct net_device *dev,unsigned int length){//因为在中断函数中被调用,所以要求原子操作GFP_ATOMICreturn __netdev_alloc_skb(dev, length, GFP_ATOMIC);}
#define dev_kfree_skb(a) consume_skb(a)/*** consume_skb - free an skbuff* @skb: buffer to free** Drop a ref to the buffer and free it if the usage count has hit zero* Functions identically to kfree_skb, but kfree_skb assumes that the frame* is being dropped after a failure and notes that*/void consume_skb(struct sk_buff *skb){if (unlikely(!skb))return;if (likely(atomic_read(&skb->users) == 1))smp_rmb();else if (likely(!atomic_dec_and_test(&skb->users)))return;trace_consume_skb(skb);__kfree_skb(skb);}
【skb_reserve()为缓冲区预留空间】
skb_reserve()在数据缓冲区头部预留一定的空间用来在数据缓冲区中插入协议首部或者在某个边界对齐。注意该函数只能用于空的skb,只是简单的更新了数据缓冲区的data和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, int len){skb->data += len;skb->tail += len;}
【skb_push()为数据缓冲区头部加入一块数据】
skb_push()在数据缓冲区的前头加入一块数据,只是移动数据缓冲区的data和tail指针。适用于空的skb,拷贝进tcp payload然后上移data指针和下移tail和end指针,拷贝tcp首部,拷贝IP首部,以太网帧首部,分别上移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.*/unsigned char *skb_push(struct sk_buff *skb, unsigned int len){skb->data -= len;skb->len += len;if (unlikely(skb->data<skb->head))skb_under_panic(skb, len, __builtin_return_address(0));return skb->data;}
【skb_pull()下移data指针】
skb_pull()通过将data指针下移,在数据区首部忽略len字节长度的数据,通常用于接收到数据包后在各层间由下往上传递时上层忽略下层的首部,向下移动data指针。
/*** 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.*/unsigned char *skb_pull(struct sk_buff *skb, unsigned int len){return skb_pull_inline(skb, len);}
【skb_add_data()】
skb_add_data()将指定用户空间的数据添加到skb的数据缓冲区尾部。
static inline int skb_add_data(struct sk_buff *skb,struct iov_iter *from, int copy){const int off = skb->len;if (skb->ip_summed == CHECKSUM_NONE) {__wsum csum = 0;if (csum_and_copy_from_iter(skb_put(skb, copy), copy,&csum, from) == copy) {skb->csum = csum_block_add(skb->csum, csum, off);return 0;}} else if (copy_from_iter(skb_put(skb, copy), copy, from) == copy)return 0;__skb_trim(skb, off);return -EFAULT;}struct kvec {void *iov_base; /* and that should *never* hold a userland pointer */size_t iov_len;};enum {ITER_IOVEC = 0,ITER_KVEC = 2,ITER_BVEC = 4,};struct iov_iter {int type;size_t iov_offset;size_t count;union {const struct iovec *iov;const struct kvec *kvec;const struct bio_vec *bvec;};unsigned long nr_segs;};
skb_trim()根据指定长度删除skb的数据缓冲区尾部数据。待操作skb的数据必须是线性存储,如果长度大于当前长度则不作处理。
/*** 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.* The skb must be linear.*/void skb_trim(struct sk_buff *skb, unsigned int len){if (skb->len > len)__skb_trim(skb, len);}static inline int pskb_trim(struct sk_buff *skb, unsigned int len){return (len < skb->len) ? __pskb_trim(skb, len) : 0;}
pskb_trim()与skb_trim()类型,但是pskb_trim()不仅可以处理线性数据还可以处理非线性数据。
总结一下,关于skb的几个操作函数简单说一下,给自己留个作业,假期我要通过skb来实现一个网络链路层加密的ko文件。其实原理很简单就是操作skb,难度在于如何实现类似于ssl的握手和密钥协商。努力ing。
如果有不对的或者问题大家可以后台私信我们一起学习!一起进步!




