struct file { .... /* needed for tty driver, and maybe others */ void *private_data; .... };更一般的,该字段作为结构体的最后一个字段存在,这已经很接近OO的思想了,但是却永远无法达到,因为,一个private字段是作为结构体内部的扩展,即对象的属性扩展,而不是类型本身的扩展。怎么说呢?如果我建立了一个对象,即:
struct file *f = ....; f->private_data = ...;
struct txtfile_extends_file { struct file f; void *private; };这么做和直接在file加入private_data相比,更能体现OO的思想。如果直接在file结构体加入private字段,那么任何人都可以对任何file实例的private字段进行替换和解释,只要它拿到file实例即可,但是如果扩展的是struct file类型,那么只有类型的定义者和理解类型定义的人才能对其进行操作。举例如下。如果直接在file结构体加入private,那么下面的赋值就是合法的:
f1->private = a; ... f1->private = b;这样一来,除非频繁加锁,否则一个file的扩展就是不稳定的,它可以在任何地点被重定义。反之,如果是对struct file的定义进行扩展的话,那么
struct txtfile1_extends_file { struct file f; void *private1; };和
struct txtfile2_extends_file { struct file f; void *private2; };就是两个类型,任何时候,通过txtfile1的对象引用private2字段都是非法的,当然上述情况下你完全可以通过强制类型转换达到目的,但是请注意,C语言本身对类型检查就不严格。你也可以通过内存操作来达到任何不可告人的目的,但是你要知道,计算机世界的任何事情都可以通过内存操作来进行,如果你为了炫技巧非要采用那么原始的操作,那也无妨。
void __init skb_init(void) { skbuff_head_cache = kmem_cache_create("skbuff_head_cache", sizeof(struct sk_buff) + sizeof(char *), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); skbuff_fclone_cache = kmem_cache_create("skbuff_fclone_cache", (2*(sizeof(struct sk_buff) + sizeof(char *))) + sizeof(atomic_t), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); }这样你就可以在其它代码不知情的情况下扩展skb了,比如定义以下结构体:
struct data_extends_skb { struct sk_buff skb; char *info; };
在PREROUTING的地方调用:
struct data_extends_skb *des = (struct data_extends_skb *)skb; strcpy(des->info, "abcdefg", 6);然后可以在任何后续的地方将info取出来。但是这有一个问题,那就是如果出现第二个执行绪,它完全可以将info的赋值修改掉,即便它不明白结构体data_extends_skb的定义,也不知道info字段的具体名字,它如果这么做:
memcpy(skb + 1, 0, sizeof(char *));也会取消掉代码作者的本意。当然,这在C语言中是没有办法的。
#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh) \ ({int __ret; \ if ((__ret=nf_hook_thresh(pf, hook, (skb), indev, outdev, okfn, thresh, 1)) == 1)\ __ret = (okfn)(skb); \ __ret;})完全可以定义成:
#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh) \ ({struct sk_buff *__skb2, __ret; \ if ((__skb2=nf_hook_thresh(pf, hook, (skb), indev, outdev, okfn, thresh, 1)) != NULL)\ __ret = (okfn)(__skb2); \ __ret;}) ....// nf_hook_thresh的修改如此一来,就可以在Netfilter中实现skb的重定义(我还没有勇气对整个协议栈开刀,要改的地方太多太多了,还是只对Netfilter开刀吧),这是意义非凡的,迎合了OO思想的向上转型,即任何一个特殊的类的对象都可以转型为更加一般的其基类对象。看似操作的是sk_buff对象,实际上它只是一个基类对象。在Netfilter的hook中,你完全可以这样:
struct sub_extends_skb { struct sb_buff skb; type1 v1; type2 v2; type3 v3; ... }; struct sub_extends_skb *sub_construct (struct sb_buff *skb) { struct sub_extends_skb = _k_malloc_and_skb_copy(sizeof(sub_extends_skb), skb); ses->v1 = ...; ... return ses; } struct sub_extends_skb *ss; if (...) { ss = construct(skb); } else { ss = skb; } ... // process return ss;只要可以修改skb的指针了,那就意味着可以重新定义skb的类型了,这也意味着扩展skb成了可能。 需要注意的是,等到需要确定一个skb到底是什么类型的时候,怎么办呢?OO语言内置的有类型检查,反射等机制,比如JAVA的instance of宏等,可在Linux内核这种底层C写就的代码中,如何来做呢?C语言没有高级OO语言的种种内置特性,这完全需要程序员自己来解决这个问题,比如你不能对一个原生的skb取v2的值,而这个是不能保证的,正如你可以写下以下的代码一样:
char *p = (char *)0; *p = 'a'; ...越是高级的语言总是让人不关注语言以及计算机机制本身,而专注于自己的业务逻辑,但是以计算机为本业的系统程序员,或者网络安全系统程序员,却最好不要去用什么高级的语言,因为你的业务逻辑就是要搞定计算机或者网络所呈现的缤纷复杂,而不是和一群穿西服的人论战...这也是一个思想!