分类: LINUX
2009-03-21 21:38:08
/* Connections have two entries in the hash table: one for each way */
struct ip_conntrack_tuple_hash
{
struct list_head list;
struct ip_conntrack_tuple tuple;
};
/* The protocol-specific manipulable parts of the tuple: always in
network order! */
union ip_conntrack_manip_proto
{
/* Add other protocols here. */
u_int16_t all;
struct {
u_int16_t port;
} tcp;
struct {
u_int16_t port;
} udp;
struct {
u_int16_t id;
} icmp;
struct {
u_int16_t port;
} sctp;
};
/* The manipulable part of the tuple. */
struct ip_conntrack_manip
{
u_int32_t ip;
union ip_conntrack_manip_proto u;
};
/* This contains the information to distinguish a connection. */
struct ip_conntrack_tuple
{
struct ip_conntrack_manip src;
/* These are the parts of the tuple which are fixed. */
struct {
u_int32_t ip;
union {
/* Add other protocols here. */
u_int16_t all;
struct {
u_int16_t port;
} tcp;
struct {
u_int16_t port;
} udp;
struct {
u_int8_t type, code;
} icmp;
struct {
u_int16_t port;
} sctp;
} u;
/* The protocol. */
u_int8_t protonum;
/* The direction (for tuplehash) */
u_int8_t dir;
} dst;
};
struct ip_conntrack
{
……
/* These are my tuples; original and reply */
struct ip_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];
};
static int __init init(void)
{
return init_or_cleanup(1);
}
static int init_or_cleanup(int init)
{
/*初始化连接跟踪的一些变量、数据结构,如初始化连接跟踪表的大小,Hash表的大小等*/
ret = ip_conntrack_init();
if (ret < 0)
goto cleanup_nothing;
/*创建proc 文件系统的对应节点*/
#ifdef CONFIG_PROC_FS
……
#endif
/*为连接跟踪注册Hook */
ret = nf_register_hook(&ip_conntrack_defrag_ops);
if (ret < 0) {
printk("ip_conntrack: can't register pre-routing defrag hook.\n");
goto cleanup_proc_stat;
}
……
}
/*用户态可以在模块插入的时候,可以使用hashsize参数,指明hash 表的大小*/
static int hashsize;
module_param(hashsize, int, 0400);
int __init ip_conntrack_init(void)
{
unsigned int i;
int ret;
/* 如果模块指明了hash表的大小,则使用指定值,否则,根据内存的大小,来计算一个默认值. ,hash表的大小,是使用全局变量ip_conntrack_htable_size 来描述*/
if (hashsize) {
ip_conntrack_htable_size = hashsize;
} else {
ip_conntrack_htable_size
= (((num_physpages << PAGE_SHIFT) / 16384)
/ sizeof(struct list_head));
if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE))
ip_conntrack_htable_size = 8192;
if (ip_conntrack_htable_size < 16)
ip_conntrack_htable_size = 16;
}
/*根据hash表的大小,计算最大的连接跟踪表数*/
ip_conntrack_max = 8 * ip_conntrack_htable_size;
printk("ip_conntrack version %s (%u buckets, %d max)"
" - %Zd bytes per conntrack\n", IP_CONNTRACK_VERSION,
ip_conntrack_htable_size, ip_conntrack_max,
sizeof(struct ip_conntrack));
/*注册socket选项*/
ret = nf_register_sockopt(&so_getorigdst);
if (ret != 0) {
printk(KERN_ERR "Unable to register netfilter socket option\n");
return ret;
}
/* 初始化内存分配标识变量 */
ip_conntrack_vmalloc = 0;
/*为hash表分配连续内存页*/
ip_conntrack_hash
=(void*)__get_free_pages(GFP_KERNEL,
get_order(sizeof(struct list_head)
*ip_conntrack_htable_size));
/*分配失败,尝试调用vmalloc重新分配*/
if (!ip_conntrack_hash) {
ip_conntrack_vmalloc = 1;
printk(KERN_WARNING "ip_conntrack: falling back to vmalloc.\n");
ip_conntrack_hash = vmalloc(sizeof(struct list_head)
* ip_conntrack_htable_size);
}
/*仍然分配失败*/
if (!ip_conntrack_hash) {
printk(KERN_ERR "Unable to create ip_conntrack_hash\n");
goto err_unreg_sockopt;
}
ip_conntrack_cachep = kmem_cache_create("ip_conntrack",
sizeof(struct ip_conntrack), 0,
0, NULL, NULL);
if (!ip_conntrack_cachep) {
printk(KERN_ERR "Unable to create ip_conntrack slab cache\n");
goto err_free_hash;
}
ip_conntrack_expect_cachep = kmem_cache_create("ip_conntrack_expect",
sizeof(struct ip_conntrack_expect),
0, 0, NULL, NULL);
if (!ip_conntrack_expect_cachep) {
printk(KERN_ERR "Unable to create ip_expect slab cache\n");
goto err_free_conntrack_slab;
}
/* Don't NEED lock here, but good form anyway. */
WRITE_LOCK(&ip_conntrack_lock);
/* 注册协议。对不同协议,连接跟踪记录的参数不同,所以不同的协议定义了不同的 ip_conntrack_protocol结构来处理与协议相关的内容。这些结构被注册到一个全局的链表中,在使用时根据协议去查找,并调用相应的处理函数来完成相应的动作。*/
for (i = 0; i < MAX_IP_CT_PROTO; i++)
ip_ct_protos = &ip_conntrack_generic_protocol;
ip_ct_protos[IPPROTO_TCP] = &ip_conntrack_protocol_tcp;
ip_ct_protos[IPPROTO_UDP] = &ip_conntrack_protocol_udp;
ip_ct_protos[IPPROTO_ICMP] = &ip_conntrack_protocol_icmp;
WRITE_UNLOCK(&ip_conntrack_lock);
/*初始化hash表*/
for (i = 0; i < ip_conntrack_htable_size; i++)
INIT_LIST_HEAD(&ip_conntrack_hash);
/* For use by ipt_REJECT */
ip_ct_attach = ip_conntrack_attach;
/* Set up fake conntrack:
- to never be deleted, not in any hashes */
atomic_set(&ip_conntrack_untracked.ct_general.use, 1);
/* - and look it like as a confirmed connection */
set_bit(IPS_CONFIRMED_BIT, &ip_conntrack_untracked.status);
return ret;
err_free_conntrack_slab:
kmem_cache_destroy(ip_conntrack_cachep);
err_free_hash:
free_conntrack_hash();
err_unreg_sockopt:
nf_unregister_sockopt(&so_getorigdst);
return -ENOMEM;
}
ret = nf_register_hook(&ip_conntrack_defrag_ops);
if (ret < 0) {
printk("ip_conntrack: can't register pre-routing defrag hook.\n");
goto cleanup_proc_stat;
}
ret = nf_register_hook(&ip_conntrack_defrag_local_out_ops);
if (ret < 0) {
printk("ip_conntrack: can't register local_out defrag hook.\n");
goto cleanup_defragops;
}
……
static unsigned int ip_conntrack_defrag(unsigned int hooknum,
struct sk_buff **pskb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
/* Gather fragments. */
if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
*pskb = ip_ct_gather_frags(*pskb,
hooknum == NF_IP_PRE_ROUTING ?
IP_DEFRAG_CONNTRACK_IN :
IP_DEFRAG_CONNTRACK_OUT);
if (!*pskb)
return NF_STOLEN;
}
return NF_ACCEPT;
}
/* Netfilter hook itself. */
unsigned int ip_conntrack_in(unsigned int hooknum,
struct sk_buff **pskb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct ip_conntrack *ct;
enum ip_conntrack_info ctinfo;
struct ip_conntrack_protocol *proto;
int set_reply;
int ret;
/* 判断当前数据包是否已被检查过了 */
if ((*pskb)->nfct) {
CONNTRACK_STAT_INC(ignore);
return NF_ACCEPT;
}
/* 分片包当会在前一个Hook中被处理,事实上,并不会触发该条件 */
if ((*pskb)->nh.iph->frag_off & htons(IP_OFFSET)) {
if (net_ratelimit()) {
printk(KERN_ERR "ip_conntrack_in: Frag of proto %u (hook=%u)\n",
(*pskb)->nh.iph->protocol, hooknum);
}
return NF_DROP;
}
/* 将当前数据包设置为未修改 */
(*pskb)->nfcache |= NFC_UNKNOWN;
/*根据当前数据包的协议,查找与之相应的struct ip_conntrack_protocol结构*/
proto = ip_ct_find_proto((*pskb)->nh.iph->protocol);
/* 没有找到对应的协议. */
if (proto->error != NULL
&& (ret = proto->error(*pskb, &ctinfo, hooknum)) <= 0) {
CONNTRACK_STAT_INC(error);
CONNTRACK_STAT_INC(invalid);
return -ret;
}
/*在全局的连接表中,查找与当前包相匹配的连接结构,返回的是struct ip_conntrack *类型指针,它用于描述一个数据包的连接状态*/
if (!(ct = resolve_normal_ct(*pskb, proto,&set_reply,hooknum,&ctinfo))) {
/* Not valid part of a connection */
CONNTRACK_STAT_INC(invalid);
return NF_ACCEPT;
}
if (IS_ERR(ct)) {
/* Too stressed to deal. */
CONNTRACK_STAT_INC(drop);
return NF_DROP;
}
IP_NF_ASSERT((*pskb)->nfct);
/*Packet函数指针,为数据包返回一个判断,如果数据包不是连接中有效的部分,返回-1,否则返回NF_ACCEPT。*/
ret = proto->packet(ct, *pskb, ctinfo);
if (ret < 0) {
/* Invalid: inverse of the return code tells
* the netfilter core what to do*/
nf_conntrack_put((*pskb)->nfct);
(*pskb)->nfct = NULL;
CONNTRACK_STAT_INC(invalid);
return -ret;
}
/*设置应答状态标志位*/
if (set_reply)
set_bit(IPS_SEEN_REPLY_BIT, &ct->status);
return ret;
}
/* On success, returns conntrack ptr, sets skb->nfct and ctinfo */
static inline struct ip_conntrack *
resolve_normal_ct(struct sk_buff *skb,
struct ip_conntrack_protocol *proto,
int *set_reply,
unsigned int hooknum,
enum ip_conntrack_info *ctinfo)
{
struct ip_conntrack_tuple tuple;
struct ip_conntrack_tuple_hash *h;
struct ip_conntrack *ct;
IP_NF_ASSERT((skb->nh.iph->frag_off & htons(IP_OFFSET)) == 0);
/*前面提到过,需要将一个数据包转换成tuple,这个转换,就是通过ip_ct_get_tuple函数实现的*/
if (!ip_ct_get_tuple(skb->nh.iph, skb, skb->nh.iph->ihl*4,
&tuple,proto))
return NULL;
/*查看数据包对应的tuple在连接跟踪表中是否存在 */
h = ip_conntrack_find_get(&tuple, NULL);
if (!h) {
/*如果不存在,初始化之*/
h = init_conntrack(&tuple, proto, skb);
if (!h)
return NULL;
if (IS_ERR(h))
return (void *)h;
}
/*根据hash表节点,取得数据包对应的连接跟踪结构*/
ct = tuplehash_to_ctrack(h);
/* 判断连接的方向 */
if (DIRECTION(h) == IP_CT_DIR_REPLY) {
*ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY;
/* Please set reply bit if this packet OK */
*set_reply = 1;
} else {
/* Once we've had two way comms, always ESTABLISHED. */
if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
DEBUGP("ip_conntrack_in: normal packet for %p\n",
ct);
*ctinfo = IP_CT_ESTABLISHED;
} else if (test_bit(IPS_EXPECTED_BIT, &ct->status)) {
DEBUGP("ip_conntrack_in: related packet for %p\n",
ct);
*ctinfo = IP_CT_RELATED;
} else {
DEBUGP("ip_conntrack_in: new packet for %p\n",
ct);
*ctinfo = IP_CT_NEW;
}
*set_reply = 0;
}
/*设置skb的对应成员,如使用计数器、数据包状态标记*/
skb->nfct = &ct->ct_general;
skb->nfctinfo = *ctinfo;
return ct;
}
int ip_ct_get_tuple(const struct iphdr *iph,
const struct sk_buff *skb,
unsigned int dataoff,
struct ip_conntrack_tuple *tuple,
const struct ip_conntrack_protocol *protocol)
{
/* Never happen */
if (iph->frag_off & htons(IP_OFFSET)) {
printk("ip_conntrack_core: Frag of proto %u.\n",
iph->protocol);
return 0;
}
/*设置来源、目的地址*/
tuple->src.ip = iph->saddr;
tuple->dst.ip = iph->daddr;
tuple->dst.protonum = iph->protocol;
tuple->dst.dir = IP_CT_DIR_ORIGINAL;
return protocol->pkt_to_tuple(skb, dataoff, tuple);
}
static int tcp_pkt_to_tuple(const struct sk_buff *skb,
unsigned int dataoff,
struct ip_conntrack_tuple *tuple)
{
struct tcphdr _hdr, *hp;
/* 获取TCP报头*/
hp = skb_header_pointer(skb, dataoff, 8, &_hdr);
if (hp == NULL)
return 0;
/*根据报头的端口信息,设置tuple对应成员*/
tuple->src.u.tcp.port = hp->source;
tuple->dst.u.tcp.port = hp->dest;
return 1;
}
unsigned int hash = hash_conntrack(tuple);
这样,tuple对应的hash表入口即为ip_conntrack_hash[hash],也就是链表的首节点,然后调用ip_conntrack_find_get函数进行查找:
struct ip_conntrack_tuple_hash *
ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple,
const struct ip_conntrack *ignored_conntrack)
{
struct ip_conntrack_tuple_hash *h;
READ_LOCK(&ip_conntrack_lock);
/*搜索链表*/
h = __ip_conntrack_find(tuple, ignored_conntrack);
if (h) /*查找到了,使用计数器累加*/
atomic_inc(&tuplehash_to_ctrack(h)->ct_general.use);
READ_UNLOCK(&ip_conntrack_lock);
return h;
}
static struct ip_conntrack_tuple_hash *
__ip_conntrack_find(const struct ip_conntrack_tuple *tuple,
const struct ip_conntrack *ignored_conntrack)
{
struct ip_conntrack_tuple_hash *h;
unsigned int hash = hash_conntrack(tuple);
MUST_BE_READ_LOCKED(&ip_conntrack_lock);
list_for_each_entry(h, &ip_conntrack_hash[hash], list) {
if (conntrack_tuple_cmp(h, tuple, ignored_conntrack)) {
CONNTRACK_STAT_INC(found);
return h;
}
CONNTRACK_STAT_INC(searched);
}
return NULL;
}
conntrack_tuple_cmp(const struct ip_conntrack_tuple_hash *i,
const struct ip_conntrack_tuple *tuple,
const struct ip_conntrack *ignored_conntrack)
{
MUST_BE_READ_LOCKED(&ip_conntrack_lock);
return tuplehash_to_ctrack(i) != ignored_conntrack
&& ip_ct_tuple_equal(tuple, &i->tuple);
}
static inline int ip_ct_tuple_src_equal(const struct ip_conntrack_tuple *t1,
const struct ip_conntrack_tuple *t2)
{
return t1->src.ip == t2->src.ip
&& t1->src.u.all == t2->src.u.all;
}
static inline int ip_ct_tuple_dst_equal(const struct ip_conntrack_tuple *t1,
const struct ip_conntrack_tuple *t2)
{
return t1->dst.ip == t2->dst.ip
&& t1->dst.u.all == t2->dst.u.all
&& t1->dst.protonum == t2->dst.protonum;
}
static inline int ip_ct_tuple_equal(const struct ip_conntrack_tuple *t1,
const struct ip_conntrack_tuple *t2)
{
return ip_ct_tuple_src_equal(t1, t2) && ip_ct_tuple_dst_equal(t1, t2);
}
struct ip_conntrack
{
/* 包含了使用计数器和指向删除连接的函数的指针 */
struct nf_conntrack ct_general;
/* 连接状态位,它通常是一个ip_conntrack_status类型的枚举变量,如IPS_SEEN_REPLY_BIT等*/
unsigned long status;
/* 内核的定时器,用于处理连接超时 */
struct timer_list timeout;
#ifdef CONFIG_IP_NF_CT_ACCT
/* Accounting Information (same cache line as other written members) */
struct ip_conntrack_counter counters[IP_CT_DIR_MAX];
#endif
/* If we were expected by an expectation, this will be it */
struct ip_conntrack *master;
/* Current number of expected connections */
unsigned int expecting;
/* Helper, if any. */
struct ip_conntrack_helper *helper;
/* Storage reserved for other modules: */
union ip_conntrack_proto proto;
union ip_conntrack_help help;
#ifdef CONFIG_IP_NF_NAT_NEEDED
struct {
struct ip_nat_info info;
#if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \
defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE)
int masq_index;
#endif
} nat;
#endif /* CONFIG_IP_NF_NAT_NEEDED */
#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
unsigned long mark;
#endif
struct ip_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];
};
static struct ip_conntrack_tuple_hash *
init_conntrack(const struct ip_conntrack_tuple *tuple,
struct ip_conntrack_protocol *protocol,
struct sk_buff *skb)
{
struct ip_conntrack *conntrack;
struct ip_conntrack_tuple repl_tuple;
size_t hash;
struct ip_conntrack_expect *exp;
/*如果计算hash值的随机数种子没有被初始化,则初始化之*/
if (!ip_conntrack_hash_rnd_initted) {
get_random_bytes(&ip_conntrack_hash_rnd, 4);
ip_conntrack_hash_rnd_initted = 1;
}
/*计算hash值*/
hash = hash_conntrack(tuple);
/*判断连接跟踪表是否已满*/
if (ip_conntrack_max
&& atomic_read(&ip_conntrack_count) >= ip_conntrack_max) {
/* Try dropping from this hash chain. */
if (!early_drop(&ip_conntrack_hash[hash])) {
if (net_ratelimit())
printk(KERN_WARNING
"ip_conntrack: table full, dropping"
" packet.\n");
return ERR_PTR(-ENOMEM);
}
}
/*根据当前的tuple取反,计算该数据包的“应答”的tuple*/
if (!ip_ct_invert_tuple(&repl_tuple, tuple, protocol)) {
DEBUGP("Can't invert tuple.\n");
return NULL;
}
/*为数据包对应的连接分配空间*/
conntrack = kmem_cache_alloc(ip_conntrack_cachep, GFP_ATOMIC);
if (!conntrack) {
DEBUGP("Can't allocate conntrack.\n");
return ERR_PTR(-ENOMEM);
}
/*初始化该结构*/
memset(conntrack, 0, sizeof(*conntrack));
/*使用计数器累加*/
atomic_set(&conntrack->ct_general.use, 1);
/*设置destroy函数指针*/
conntrack->ct_general.destroy = destroy_conntrack;
/*设置正反两个方向的tuple*/
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *tuple;
conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = repl_tuple;
if (!protocol->new(conntrack, skb)) {
kmem_cache_free(ip_conntrack_cachep, conntrack);
return NULL;
}
/* 初始化时间计数器,并设置超时初始函数 */
init_timer(&conntrack->timeout);
conntrack->timeout.data = (unsigned long)conntrack;
conntrack->timeout.function = death_by_timeout;
WRITE_LOCK(&ip_conntrack_lock);
exp = find_expectation(tuple);
if (exp) {
DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n",
conntrack, exp);
/* Welcome, Mr. Bond. We've been expecting you... */
__set_bit(IPS_EXPECTED_BIT, &conntrack->status);
conntrack->master = exp->master;
#if CONFIG_IP_NF_CONNTRACK_MARK
conntrack->mark = exp->master->mark;
#endif
nf_conntrack_get(&conntrack->master->ct_general);
CONNTRACK_STAT_INC(expect_new);
} else {
conntrack->helper = ip_ct_find_helper(&repl_tuple);
CONNTRACK_STAT_INC(new);
}
/* 这里,并没有直接就把该连接加入hash表,而是先加入到unconfirmed链表中. */
list_add(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL].list, &unconfirmed);
atomic_inc(&ip_conntrack_count);
WRITE_UNLOCK(&ip_conntrack_lock);
if (exp) {
if (exp->expectfn)
exp->expectfn(conntrack, exp);
destroy_expect(exp);
}
/*返回的是初始方向的hash节点*/
return &conntrack->tuplehash[IP_CT_DIR_ORIGINAL];
}
int
ip_ct_invert_tuple(struct ip_conntrack_tuple *inverse,
const struct ip_conntrack_tuple *orig,
const struct ip_conntrack_protocol *protocol)
{
inverse->src.ip = orig->dst.ip;
inverse->dst.ip = orig->src.ip;
inverse->dst.protonum = orig->dst.protonum;
inverse->dst.dir = !orig->dst.dir;
return protocol->invert_tuple(inverse, orig);
}
/* Called when a new connection for this protocol found. */
static int icmp_new(struct ip_conntrack *conntrack,
const struct sk_buff *skb)
{
static u_int8_t valid_new[]
= { [ICMP_ECHO] = 1,
[ICMP_TIMESTAMP] = 1,
[ICMP_INFO_REQUEST] = 1,
[ICMP_ADDRESS] = 1 };
if (conntrack->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new)
|| !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmp.type]) {
/* Can't create a new ICMP `conn' with this. */
DEBUGP("icmp: can't create new conn with type %u\n",
conntrack->tuplehash[0].tuple.dst.u.icmp.type);
DUMP_TUPLE(&conntrack->tuplehash[0].tuple);
return 0;
}
atomic_set(&conntrack->proto.icmp.count, 0);
return 1;
}
enum ip_conntrack_dir
{
IP_CT_DIR_ORIGINAL,
IP_CT_DIR_REPLY,
IP_CT_DIR_MAX
};
/* 如果是一个应答包 ,设置状态为已建+应答*/
if (DIRECTION(h) == IP_CT_DIR_REPLY) {
*ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY;
/* 设置应答标志变量 */
*set_reply = 1;
} else {
/* 新建连接方过来的数据包,对面向连接的协议而言,可能是一个已建连接,判断其标志位*/
if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
DEBUGP("ip_conntrack_in: normal packet for %p\n",
ct);
*ctinfo = IP_CT_ESTABLISHED;
} else if (test_bit(IPS_EXPECTED_BIT, &ct->status)) {
DEBUGP("ip_conntrack_in: related packet for %p\n",
ct);
*ctinfo = IP_CT_RELATED; //关联连接
} else {
DEBUGP("ip_conntrack_in: new packet for %p\n",
ct);
*ctinfo = IP_CT_NEW; //否则,则为一个新建连接
}
*set_reply = 0;
}
/*设置数据包skb与连接状态的关联*/
skb->nfct = &ct->ct_general;
/*每个sk_buff都将与ip_conntrack的一个状态关联,所以从sk_buff可以得到相应ip_conntrack的状态,即数据包的状态*/
skb->nfctinfo = *ctinfo;
return ct;
static unsigned int ip_refrag(unsigned int hooknum,
struct sk_buff **pskb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct rtable *rt = (struct rtable *)(*pskb)->dst;
/* ip_confirm函数用于处理将tuple加入hash表等重要的后续处理 */
if (ip_confirm(hooknum, pskb, in, out, okfn) != NF_ACCEPT)
return NF_DROP;
/* 在连接跟踪开始之前,对分片包进行了重组,这里判断数据包是否需要分片,如果要分片,就调用ip_fragment分片函数将数据包分片发送出去,因为数据包已经被发送走了,所以,在它之后的任何Hook函数已经没有意思了 */
if ((*pskb)->len > dst_mtu(&rt->u.dst) &&
!skb_shinfo(*pskb)->tso_size) {
/* No hook can be after us, so this should be OK. */
ip_fragment(*pskb, okfn);
return NF_STOLEN;
}
return NF_ACCEPT;
}
static unsigned int ip_confirm(unsigned int hooknum,
struct sk_buff **pskb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
return ip_conntrack_confirm(pskb);
}
static inline int ip_conntrack_confirm(struct sk_buff **pskb)
{
if ((*pskb)->nfct
&& !is_confirmed((struct ip_conntrack *)(*pskb)->nfct))
return __ip_conntrack_confirm(pskb);
return NF_ACCEPT;
}