📄 ip_conntrack_core.c
字号:
ret = 0;out: WRITE_UNLOCK(&ip_conntrack_lock); return ret;}/* Alter reply tuple (maybe alter helper). This is for NAT, and is implicitly racy: see __ip_conntrack_confirm */void ip_conntrack_alter_reply(struct ip_conntrack *conntrack, const struct ip_conntrack_tuple *newreply){ WRITE_LOCK(&ip_conntrack_lock); /* Should be unconfirmed, so not in hash table yet */ IP_NF_ASSERT(!is_confirmed(conntrack)); DEBUGP("Altering reply tuple of %p to ", conntrack); DUMP_TUPLE(newreply); conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply; if (!conntrack->master && conntrack->expecting == 0) conntrack->helper = ip_ct_find_helper(newreply); WRITE_UNLOCK(&ip_conntrack_lock);}int ip_conntrack_helper_register(struct ip_conntrack_helper *me){ BUG_ON(me->timeout == 0); WRITE_LOCK(&ip_conntrack_lock); list_prepend(&helpers, me); WRITE_UNLOCK(&ip_conntrack_lock); return 0;}static inline int unhelp(struct ip_conntrack_tuple_hash *i, const struct ip_conntrack_helper *me){ if (tuplehash_to_ctrack(i)->helper == me) tuplehash_to_ctrack(i)->helper = NULL; return 0;}void ip_conntrack_helper_unregister(struct ip_conntrack_helper *me){ unsigned int i; struct ip_conntrack_expect *exp, *tmp; /* Need write lock here, to delete helper. */ WRITE_LOCK(&ip_conntrack_lock); LIST_DELETE(&helpers, me); /* Get rid of expectations */ list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, list) { if (exp->master->helper == me && del_timer(&exp->timeout)) { unlink_expect(exp); destroy_expect(exp); } } /* Get rid of expecteds, set helpers to NULL. */ LIST_FIND_W(&unconfirmed, unhelp, struct ip_conntrack_tuple_hash*, me); for (i = 0; i < ip_conntrack_htable_size; i++) LIST_FIND_W(&ip_conntrack_hash[i], unhelp, struct ip_conntrack_tuple_hash *, me); WRITE_UNLOCK(&ip_conntrack_lock); /* Someone could be still looking at the helper in a bh. */ synchronize_net();}static inline void ct_add_counters(struct ip_conntrack *ct, enum ip_conntrack_info ctinfo, const struct sk_buff *skb){#ifdef CONFIG_IP_NF_CT_ACCT if (skb) { ct->counters[CTINFO2DIR(ctinfo)].packets++; ct->counters[CTINFO2DIR(ctinfo)].bytes += ntohs(skb->nh.iph->tot_len); }#endif}/* Refresh conntrack for this many jiffies and do accounting (if skb != NULL) */void ip_ct_refresh_acct(struct ip_conntrack *ct, enum ip_conntrack_info ctinfo, const struct sk_buff *skb, unsigned long extra_jiffies){ IP_NF_ASSERT(ct->timeout.data == (unsigned long)ct); /* If not in hash table, timer will not be active yet */ if (!is_confirmed(ct)) { ct->timeout.expires = extra_jiffies; ct_add_counters(ct, ctinfo, skb); } else { WRITE_LOCK(&ip_conntrack_lock); /* Need del_timer for race avoidance (may already be dying). */ if (del_timer(&ct->timeout)) { ct->timeout.expires = jiffies + extra_jiffies; add_timer(&ct->timeout); } ct_add_counters(ct, ctinfo, skb); WRITE_UNLOCK(&ip_conntrack_lock); }}/* Returns new sk_buff, or NULL */struct sk_buff *ip_ct_gather_frags(struct sk_buff *skb, u_int32_t user){ struct sock *sk = skb->sk;#ifdef CONFIG_NETFILTER_DEBUG unsigned int olddebug = skb->nf_debug;#endif if (sk) { sock_hold(sk); skb_orphan(skb); } local_bh_disable(); skb = ip_defrag(skb, user); local_bh_enable(); if (!skb) { if (sk) sock_put(sk); return skb; } if (sk) { skb_set_owner_w(skb, sk); sock_put(sk); } ip_send_check(skb->nh.iph); skb->nfcache |= NFC_ALTERED;#ifdef CONFIG_NETFILTER_DEBUG /* Packet path as if nothing had happened. */ skb->nf_debug = olddebug;#endif return skb;}/* Used by ipt_REJECT. */static void ip_conntrack_attach(struct sk_buff *nskb, struct sk_buff *skb){ struct ip_conntrack *ct; enum ip_conntrack_info ctinfo; /* This ICMP is in reverse direction to the packet which caused it */ ct = ip_conntrack_get(skb, &ctinfo); if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) ctinfo = IP_CT_RELATED + IP_CT_IS_REPLY; else ctinfo = IP_CT_RELATED; /* Attach to new skbuff, and increment count */ nskb->nfct = &ct->ct_general; nskb->nfctinfo = ctinfo; nf_conntrack_get(nskb->nfct);}static inline intdo_iter(const struct ip_conntrack_tuple_hash *i, int (*iter)(struct ip_conntrack *i, void *data), void *data){ return iter(tuplehash_to_ctrack(i), data);}/* Bring out ya dead! */static struct ip_conntrack_tuple_hash *get_next_corpse(int (*iter)(struct ip_conntrack *i, void *data), void *data, unsigned int *bucket){ struct ip_conntrack_tuple_hash *h = NULL; WRITE_LOCK(&ip_conntrack_lock); for (; *bucket < ip_conntrack_htable_size; (*bucket)++) { h = LIST_FIND_W(&ip_conntrack_hash[*bucket], do_iter, struct ip_conntrack_tuple_hash *, iter, data); if (h) break; } if (!h) h = LIST_FIND_W(&unconfirmed, do_iter, struct ip_conntrack_tuple_hash *, iter, data); if (h) atomic_inc(&tuplehash_to_ctrack(h)->ct_general.use); WRITE_UNLOCK(&ip_conntrack_lock); return h;}voidip_ct_iterate_cleanup(int (*iter)(struct ip_conntrack *i, void *), void *data){ struct ip_conntrack_tuple_hash *h; unsigned int bucket = 0; while ((h = get_next_corpse(iter, data, &bucket)) != NULL) { struct ip_conntrack *ct = tuplehash_to_ctrack(h); /* Time to push up daises... */ if (del_timer(&ct->timeout)) death_by_timeout((unsigned long)ct); /* ... else the timer will get him soon. */ ip_conntrack_put(ct); }}/* Fast function for those who don't want to parse /proc (and I don't blame them). *//* Reversing the socket's dst/src point of view gives us the reply mapping. */static intgetorigdst(struct sock *sk, int optval, void __user *user, int *len){ struct inet_sock *inet = inet_sk(sk); struct ip_conntrack_tuple_hash *h; struct ip_conntrack_tuple tuple; IP_CT_TUPLE_U_BLANK(&tuple); tuple.src.ip = inet->rcv_saddr; tuple.src.u.tcp.port = inet->sport; tuple.dst.ip = inet->daddr; tuple.dst.u.tcp.port = inet->dport; tuple.dst.protonum = IPPROTO_TCP; /* We only do TCP at the moment: is there a better way? */ if (strcmp(sk->sk_prot->name, "TCP")) { DEBUGP("SO_ORIGINAL_DST: Not a TCP socket\n"); return -ENOPROTOOPT; } if ((unsigned int) *len < sizeof(struct sockaddr_in)) { DEBUGP("SO_ORIGINAL_DST: len %u not %u\n", *len, sizeof(struct sockaddr_in)); return -EINVAL; } h = ip_conntrack_find_get(&tuple, NULL); if (h) { struct sockaddr_in sin; struct ip_conntrack *ct = tuplehash_to_ctrack(h); sin.sin_family = AF_INET; sin.sin_port = ct->tuplehash[IP_CT_DIR_ORIGINAL] .tuple.dst.u.tcp.port; sin.sin_addr.s_addr = ct->tuplehash[IP_CT_DIR_ORIGINAL] .tuple.dst.ip; DEBUGP("SO_ORIGINAL_DST: %u.%u.%u.%u %u\n", NIPQUAD(sin.sin_addr.s_addr), ntohs(sin.sin_port)); ip_conntrack_put(ct); if (copy_to_user(user, &sin, sizeof(sin)) != 0) return -EFAULT; else return 0; } DEBUGP("SO_ORIGINAL_DST: Can't find %u.%u.%u.%u/%u-%u.%u.%u.%u/%u.\n", NIPQUAD(tuple.src.ip), ntohs(tuple.src.u.tcp.port), NIPQUAD(tuple.dst.ip), ntohs(tuple.dst.u.tcp.port)); return -ENOENT;}static struct nf_sockopt_ops so_getorigdst = { .pf = PF_INET, .get_optmin = SO_ORIGINAL_DST, .get_optmax = SO_ORIGINAL_DST+1, .get = &getorigdst,};static int kill_all(struct ip_conntrack *i, void *data){ return 1;}static void free_conntrack_hash(void){ if (ip_conntrack_vmalloc) vfree(ip_conntrack_hash); else free_pages((unsigned long)ip_conntrack_hash, get_order(sizeof(struct list_head) * ip_conntrack_htable_size));}/* Mishearing the voices in his head, our hero wonders how he's supposed to kill the mall. */void ip_conntrack_cleanup(void){ ip_ct_attach = NULL; /* This makes sure all current packets have passed through netfilter framework. Roll on, two-stage module delete... */ synchronize_net(); i_see_dead_people: ip_ct_iterate_cleanup(kill_all, NULL); if (atomic_read(&ip_conntrack_count) != 0) { schedule(); goto i_see_dead_people; } kmem_cache_destroy(ip_conntrack_cachep); kmem_cache_destroy(ip_conntrack_expect_cachep); free_conntrack_hash(); nf_unregister_sockopt(&so_getorigdst);}static int hashsize;module_param(hashsize, int, 0400);int __init ip_conntrack_init(void){ unsigned int i; int ret; /* Idea from tcp.c: use 1/16384 of memory. On i386: 32MB * machine has 256 buckets. >= 1GB machines have 8192 buckets. */ 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; } 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)); ret = nf_register_sockopt(&so_getorigdst); if (ret != 0) { printk(KERN_ERR "Unable to register netfilter socket option\n"); return ret; } /* AK: the hash table is twice as big than needed because it uses list_head. it would be much nicer to caches to use a single pointer list head here. */ ip_conntrack_vmalloc = 0; ip_conntrack_hash =(void*)__get_free_pages(GFP_KERNEL, get_order(sizeof(struct list_head) *ip_conntrack_htable_size)); 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); for (i = 0; i < MAX_IP_CT_PROTO; i++) ip_ct_protos[i] = &ip_conntrack_generic_protocol; /* Sew in builtin protocols. */ 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); for (i = 0; i < ip_conntrack_htable_size; i++) INIT_LIST_HEAD(&ip_conntrack_hash[i]); /* 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;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -