📄 ip_conntrack_core.c
字号:
unexpect_related(related_to); related_to->expected.tuple = *tuple; related_to->expected.mask = *mask; related_to->expected.expectfn = expectfn; if (LIST_FIND(&expect_list, expect_clash, struct ip_conntrack_expect *, &related_to->expected)) { WRITE_UNLOCK(&ip_conntrack_lock); return -EBUSY; } list_prepend(&expect_list, &related_to->expected); related_to->expected.expectant = related_to; WRITE_UNLOCK(&ip_conntrack_lock); return 0;}void ip_conntrack_unexpect_related(struct ip_conntrack *related_to){ WRITE_LOCK(&ip_conntrack_lock); unexpect_related(related_to); WRITE_UNLOCK(&ip_conntrack_lock);} /* Alter reply tuple (maybe alter helper). If it's already taken, return 0 and don't do alteration. */int ip_conntrack_alter_reply(struct ip_conntrack *conntrack, const struct ip_conntrack_tuple *newreply){ unsigned int newindex = hash_conntrack(newreply); WRITE_LOCK(&ip_conntrack_lock); if (__ip_conntrack_find(newreply, conntrack)) { WRITE_UNLOCK(&ip_conntrack_lock); return 0; } DEBUGP("Altering reply tuple of %p to ", conntrack); DUMP_TUPLE(newreply); LIST_DELETE(&ip_conntrack_hash [hash_conntrack(&conntrack->tuplehash[IP_CT_DIR_REPLY] .tuple)], &conntrack->tuplehash[IP_CT_DIR_REPLY]); conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply; list_prepend(&ip_conntrack_hash[newindex], &conntrack->tuplehash[IP_CT_DIR_REPLY]); conntrack->helper = LIST_FIND(&helpers, helper_cmp, struct ip_conntrack_helper *, newreply); WRITE_UNLOCK(&ip_conntrack_lock); return 1;}int ip_conntrack_helper_register(struct ip_conntrack_helper *me){ MOD_INC_USE_COUNT; 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 (i->ctrack->helper == me) { i->ctrack->helper = NULL; /* Get rid of any expected. */ if (i->ctrack->expected.expectant) { IP_NF_ASSERT(i->ctrack->expected.expectant == i->ctrack); LIST_DELETE(&expect_list, &i->ctrack->expected); i->ctrack->expected.expectant = NULL; } } return 0;}void ip_conntrack_helper_unregister(struct ip_conntrack_helper *me){ unsigned int i; /* Need write lock here, to delete helper. */ WRITE_LOCK(&ip_conntrack_lock); LIST_DELETE(&helpers, me); /* Get rid of expecteds, set helpers to NULL. */ 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. */ br_write_lock_bh(BR_NETPROTO_LOCK); br_write_unlock_bh(BR_NETPROTO_LOCK); MOD_DEC_USE_COUNT;}/* Refresh conntrack for this many jiffies. */void ip_ct_refresh(struct ip_conntrack *ct, unsigned long extra_jiffies){ IP_NF_ASSERT(ct->timeout.data == (unsigned long)ct); WRITE_LOCK(&ip_conntrack_lock); /* Timer may not be active yet */ if (!(ct->status & IPS_CONFIRMED)) ct->timeout.expires = extra_jiffies; else { /* 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); } } WRITE_UNLOCK(&ip_conntrack_lock);}/* Returns new sk_buff, or NULL */struct sk_buff *ip_ct_gather_frags(struct sk_buff *skb){ struct sock *sk = skb->sk;#ifdef CONFIG_NETFILTER_DEBUG unsigned int olddebug = skb->nf_debug;#endif if (sk) sock_hold(sk); local_bh_disable(); skb = ip_defrag(skb); 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;}static inline intdo_kill(const struct ip_conntrack_tuple_hash *i, int (*kill)(const struct ip_conntrack *i, void *data), void *data){ return kill(i->ctrack, data);}/* Bring out ya dead! */static struct ip_conntrack_tuple_hash *get_next_corpse(int (*kill)(const struct ip_conntrack *i, void *data), void *data){ struct ip_conntrack_tuple_hash *h = NULL; unsigned int i; READ_LOCK(&ip_conntrack_lock); for (i = 0; !h && i < ip_conntrack_htable_size; i++) { h = LIST_FIND(&ip_conntrack_hash[i], do_kill, struct ip_conntrack_tuple_hash *, kill, data); } if (h) atomic_inc(&h->ctrack->ct_general.use); READ_UNLOCK(&ip_conntrack_lock); return h;}voidip_ct_selective_cleanup(int (*kill)(const struct ip_conntrack *i, void *data), void *data){ struct ip_conntrack_tuple_hash *h; /* This is order n^2, by the way. */ while ((h = get_next_corpse(kill, data)) != NULL) { /* Time to push up daises... */ if (del_timer(&h->ctrack->timeout)) death_by_timeout((unsigned long)h->ctrack); else if (!(h->ctrack->status & IPS_CONFIRMED)) { /* Unconfirmed connection. Clean from lists, mark confirmed so it gets cleaned as soon as skb freed. */ WRITE_LOCK(&ip_conntrack_lock); /* Lock protects race against another setting of confirmed bit. set_bit isolates this bit from the others. */ if (!(h->ctrack->status & IPS_CONFIRMED)) { clean_from_lists(h->ctrack); set_bit(IPS_CONFIRMED_BIT, &h->ctrack->status); } WRITE_UNLOCK(&ip_conntrack_lock); } /* ... else the timer will get him soon. */ ip_conntrack_put(h->ctrack); }}/* 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, int *len){ struct ip_conntrack_tuple_hash *h; struct ip_conntrack_tuple tuple = { { sk->rcv_saddr, { sk->sport } }, { sk->daddr, { sk->dport }, IPPROTO_TCP } }; /* We only do TCP at the moment: is there a better way? */ if (strcmp(sk->prot->name, "TCP") != 0) { DEBUGP("SO_ORIGINAL_DST: Not a TCP socket\n"); return -ENOPROTOOPT; } if (*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; sin.sin_family = AF_INET; sin.sin_port = h->ctrack->tuplehash[IP_CT_DIR_ORIGINAL] .tuple.dst.u.tcp.port; sin.sin_addr.s_addr = h->ctrack->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(h->ctrack); 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= { { NULL, NULL }, PF_INET, 0, 0, NULL, /* Setsockopts */ SO_ORIGINAL_DST, SO_ORIGINAL_DST+1, &getorigdst, 0, NULL };#define NET_IP_CONNTRACK_MAX 2089#define NET_IP_CONNTRACK_MAX_NAME "ip_conntrack_max"#ifdef CONFIG_SYSCTLstatic struct ctl_table_header *ip_conntrack_sysctl_header;static ctl_table ip_conntrack_table[] = { { NET_IP_CONNTRACK_MAX, NET_IP_CONNTRACK_MAX_NAME, &ip_conntrack_max, sizeof(ip_conntrack_max), 0644, NULL, proc_dointvec }, { 0 }};static ctl_table ip_conntrack_dir_table[] = { {NET_IPV4, "ipv4", NULL, 0, 0555, ip_conntrack_table, 0, 0, 0, 0, 0}, { 0 }};static ctl_table ip_conntrack_root_table[] = { {CTL_NET, "net", NULL, 0, 0555, ip_conntrack_dir_table, 0, 0, 0, 0, 0}, { 0 }};#endif /*CONFIG_SYSCTL*/static int kill_all(const struct ip_conntrack *i, void *data){ return 1;}/* Mishearing the voices in his head, our hero wonders how he's supposed to kill the mall. */void ip_conntrack_cleanup(void){#ifdef CONFIG_SYSCTL unregister_sysctl_table(ip_conntrack_sysctl_header);#endif i_see_dead_people: ip_ct_selective_cleanup(kill_all, NULL); if (atomic_read(&ip_conntrack_count) != 0) { schedule(); goto i_see_dead_people; } kmem_cache_destroy(ip_conntrack_cachep); vfree(ip_conntrack_hash); nf_unregister_sockopt(&so_getorigdst);}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 machine has 8192 buckets. */ ip_conntrack_htable_size = (((num_physpages << PAGE_SHIFT) / 16384) / sizeof(struct list_head)); ip_conntrack_max = 8 * ip_conntrack_htable_size; printk("ip_conntrack (%u buckets, %d max)\n", ip_conntrack_htable_size, ip_conntrack_max); ret = nf_register_sockopt(&so_getorigdst); if (ret != 0) return ret; ip_conntrack_hash = vmalloc(sizeof(struct list_head) * ip_conntrack_htable_size); if (!ip_conntrack_hash) { nf_unregister_sockopt(&so_getorigdst); return -ENOMEM; } ip_conntrack_cachep = kmem_cache_create("ip_conntrack", sizeof(struct ip_conntrack), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); if (!ip_conntrack_cachep) { printk(KERN_ERR "Unable to create ip_conntrack slab cache\n"); vfree(ip_conntrack_hash); nf_unregister_sockopt(&so_getorigdst); return -ENOMEM; } /* Don't NEED lock here, but good form anyway. */ WRITE_LOCK(&ip_conntrack_lock); /* Sew in builtin protocols. */ list_append(&protocol_list, &ip_conntrack_protocol_tcp); list_append(&protocol_list, &ip_conntrack_protocol_udp); list_append(&protocol_list, &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]);/* This is fucking braindead. There is NO WAY of doing this without the CONFIG_SYSCTL unless you don't want to detect errors. Grrr... --RR */#ifdef CONFIG_SYSCTL ip_conntrack_sysctl_header = register_sysctl_table(ip_conntrack_root_table, 0); if (ip_conntrack_sysctl_header == NULL) { kmem_cache_destroy(ip_conntrack_cachep); vfree(ip_conntrack_hash); nf_unregister_sockopt(&so_getorigdst); return -ENOMEM; }#endif /*CONFIG_SYSCTL*/ return ret;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -