?? ip_conntrack_core.c
字號:
ip_conntrack_tuple_taken(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); READ_UNLOCK(&ip_conntrack_lock); return h != NULL;}/* Returns conntrack if it dealt with ICMP, and filled in skb fields */struct ip_conntrack *icmp_error_track(struct sk_buff *skb, enum ip_conntrack_info *ctinfo, unsigned int hooknum){ struct ip_conntrack_tuple innertuple, origtuple; struct { struct icmphdr icmp; struct iphdr ip; } inside; struct ip_conntrack_protocol *innerproto; struct ip_conntrack_tuple_hash *h; int dataoff; IP_NF_ASSERT(skb->nfct == NULL); /* Not enough header? */ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &inside, sizeof(inside))!=0) return NULL; if (inside.icmp.type != ICMP_DEST_UNREACH && inside.icmp.type != ICMP_SOURCE_QUENCH && inside.icmp.type != ICMP_TIME_EXCEEDED && inside.icmp.type != ICMP_PARAMETERPROB && inside.icmp.type != ICMP_REDIRECT) return NULL; /* Ignore ICMP's containing fragments (shouldn't happen) */ if (inside.ip.frag_off & htons(IP_OFFSET)) { DEBUGP("icmp_error_track: fragment of proto %u\n", inside.ip.protocol); return NULL; } innerproto = ip_ct_find_proto(inside.ip.protocol); dataoff = skb->nh.iph->ihl*4 + sizeof(inside.icmp) + inside.ip.ihl*4; /* Are they talking about one of our connections? */ if (!get_tuple(&inside.ip, skb, dataoff, &origtuple, innerproto)) { DEBUGP("icmp_error: ! get_tuple p=%u", inside.ip.protocol); return NULL; } /* Ordinarily, we'd expect the inverted tupleproto, but it's been preserved inside the ICMP. */ if (!invert_tuple(&innertuple, &origtuple, innerproto)) { DEBUGP("icmp_error_track: Can't invert tuple\n"); return NULL; } *ctinfo = IP_CT_RELATED; h = ip_conntrack_find_get(&innertuple, NULL); if (!h) { /* Locally generated ICMPs will match inverted if they haven't been SNAT'ed yet */ /* FIXME: NAT code has to handle half-done double NAT --RR */ if (hooknum == NF_IP_LOCAL_OUT) h = ip_conntrack_find_get(&origtuple, NULL); if (!h) { DEBUGP("icmp_error_track: no match\n"); return NULL; } /* Reverse direction from that found */ if (DIRECTION(h) != IP_CT_DIR_REPLY) *ctinfo += IP_CT_IS_REPLY; } else { if (DIRECTION(h) == IP_CT_DIR_REPLY) *ctinfo += IP_CT_IS_REPLY; } /* Update skb to refer to this connection */ skb->nfct = &h->ctrack->infos[*ctinfo]; return h->ctrack;}/* There's a small race here where we may free a just-assured connection. Too bad: we're in trouble anyway. */static inline int unreplied(const struct ip_conntrack_tuple_hash *i){ return !(test_bit(IPS_ASSURED_BIT, &i->ctrack->status));}static int early_drop(struct list_head *chain){ /* Traverse backwards: gives us oldest, which is roughly LRU */ struct ip_conntrack_tuple_hash *h; int dropped = 0; READ_LOCK(&ip_conntrack_lock); h = LIST_FIND_B(chain, unreplied, struct ip_conntrack_tuple_hash *); if (h) atomic_inc(&h->ctrack->ct_general.use); READ_UNLOCK(&ip_conntrack_lock); if (!h) return dropped; if (del_timer(&h->ctrack->timeout)) { death_by_timeout((unsigned long)h->ctrack); dropped = 1; } ip_conntrack_put(h->ctrack); return dropped;}static inline int helper_cmp(const struct ip_conntrack_helper *i, const struct ip_conntrack_tuple *rtuple){ return ip_ct_tuple_mask_cmp(rtuple, &i->tuple, &i->mask);}struct ip_conntrack_helper *ip_ct_find_helper(const struct ip_conntrack_tuple *tuple){ return LIST_FIND(&helpers, helper_cmp, struct ip_conntrack_helper *, tuple);}/* Allocate a new conntrack: we return -ENOMEM if classification failed due to stress. Otherwise it really is unclassifiable. */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 *expected; int i; static unsigned int drop_next; if (!ip_conntrack_hash_rnd_initted) { get_random_bytes(&ip_conntrack_hash_rnd, 4); ip_conntrack_hash_rnd_initted = 1; } hash = hash_conntrack(tuple); if (ip_conntrack_max && atomic_read(&ip_conntrack_count) >= ip_conntrack_max) { /* Try dropping from random chain, or else from the chain about to put into (in case they're trying to bomb one hash chain). */ unsigned int next = (drop_next++)%ip_conntrack_htable_size; if (!early_drop(&ip_conntrack_hash[next]) && !early_drop(&ip_conntrack_hash[hash])) { if (net_ratelimit()) printk(KERN_WARNING "ip_conntrack: table full, dropping" " packet.\n"); return ERR_PTR(-ENOMEM); } } if (!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); conntrack->ct_general.destroy = destroy_conntrack; conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *tuple; conntrack->tuplehash[IP_CT_DIR_ORIGINAL].ctrack = conntrack; conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = repl_tuple; conntrack->tuplehash[IP_CT_DIR_REPLY].ctrack = conntrack; for (i=0; i < IP_CT_NUMBER; i++) conntrack->infos[i].master = &conntrack->ct_general; if (!protocol->new(conntrack, skb)) { kmem_cache_free(ip_conntrack_cachep, conntrack); return NULL; } /* Don't set timer yet: wait for confirmation */ init_timer(&conntrack->timeout); conntrack->timeout.data = (unsigned long)conntrack; conntrack->timeout.function = death_by_timeout; INIT_LIST_HEAD(&conntrack->sibling_list); WRITE_LOCK(&ip_conntrack_lock); /* Need finding and deleting of expected ONLY if we win race */ READ_LOCK(&ip_conntrack_expect_tuple_lock); expected = LIST_FIND(&ip_conntrack_expect_list, expect_cmp, struct ip_conntrack_expect *, tuple); READ_UNLOCK(&ip_conntrack_expect_tuple_lock); /* If master is not in hash table yet (ie. packet hasn't left this machine yet), how can other end know about expected? Hence these are not the droids you are looking for (if master ct never got confirmed, we'd hold a reference to it and weird things would happen to future packets). */ if (expected && !is_confirmed(expected->expectant)) expected = NULL; /* Look up the conntrack helper for master connections only */ if (!expected) conntrack->helper = ip_ct_find_helper(&repl_tuple); /* If the expectation is dying, then this is a loser. */ if (expected && expected->expectant->helper->timeout && ! del_timer(&expected->timeout)) expected = NULL; if (expected) { DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n", conntrack, expected); /* Welcome, Mr. Bond. We've been expecting you... */ __set_bit(IPS_EXPECTED_BIT, &conntrack->status); conntrack->master = expected; expected->sibling = conntrack; LIST_DELETE(&ip_conntrack_expect_list, expected); expected->expectant->expecting--; nf_conntrack_get(&master_ct(conntrack)->infos[0]); } atomic_inc(&ip_conntrack_count); WRITE_UNLOCK(&ip_conntrack_lock); if (expected && expected->expectfn) expected->expectfn(conntrack); return &conntrack->tuplehash[IP_CT_DIR_ORIGINAL];}/* 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; IP_NF_ASSERT((skb->nh.iph->frag_off & htons(IP_OFFSET)) == 0); if (!get_tuple(skb->nh.iph, skb, skb->nh.iph->ihl*4, &tuple, proto)) return NULL; /* look for tuple match */ 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; } /* It exists; we have (non-exclusive) reference. */ 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, &h->ctrack->status)) { DEBUGP("ip_conntrack_in: normal packet for %p\n", h->ctrack); *ctinfo = IP_CT_ESTABLISHED; } else if (test_bit(IPS_EXPECTED_BIT, &h->ctrack->status)) { DEBUGP("ip_conntrack_in: related packet for %p\n", h->ctrack); *ctinfo = IP_CT_RELATED; } else { DEBUGP("ip_conntrack_in: new packet for %p\n", h->ctrack); *ctinfo = IP_CT_NEW; } *set_reply = 0; } skb->nfct = &h->ctrack->infos[*ctinfo]; return h->ctrack;}/* 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; /* Never happen */ 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; } /* FIXME: Do this right please. --RR */ (*pskb)->nfcache |= NFC_UNKNOWN;/* Doesn't cover locally-generated broadcast, so not worth it. */#if 0 /* Ignore broadcast: no `connection'. */ if ((*pskb)->pkt_type == PACKET_BROADCAST) { printk("Broadcast packet!\n"); return NF_ACCEPT; } else if (((*pskb)->nh.iph->daddr & htonl(0x000000FF)) == htonl(0x000000FF)) { printk("Should bcast: %u.%u.%u.%u->%u.%u.%u.%u (sk=%p, ptype=%u)\n", NIPQUAD((*pskb)->nh.iph->saddr), NIPQUAD((*pskb)->nh.iph->daddr), (*pskb)->sk, (*pskb)->pkt_type); }#endif /* Previously seen (loopback or untracked)? Ignore. */ if ((*pskb)->nfct) return NF_ACCEPT; proto = ip_ct_find_proto((*pskb)->nh.iph->protocol); /* It may be an icmp error... */ if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP && icmp_error_track(*pskb, &ctinfo, hooknum)) return NF_ACCEPT; if (!(ct = resolve_normal_ct(*pskb, proto,&set_reply,hooknum,&ctinfo))) /* Not valid part of a connection */ return NF_ACCEPT; if (IS_ERR(ct)) /* Too stressed to deal. */ return NF_DROP; IP_NF_ASSERT((*pskb)->nfct); ret = proto->packet(ct, *pskb, ctinfo); if (ret == -1) { /* Invalid */ nf_conntrack_put((*pskb)->nfct); (*pskb)->nfct = NULL; return NF_ACCEPT; } if (ret != NF_DROP && ct->helper) { ret = ct->helper->help(*pskb, ct, ctinfo); if (ret == -1) { /* Invalid */ nf_conntrack_put((*pskb)->nfct); (*pskb)->nfct = NULL; return NF_ACCEPT; } } if (set_reply) set_bit(IPS_SEEN_REPLY_BIT, &ct->status); return ret;}int invert_tuplepr(struct ip_conntrack_tuple *inverse, const struct ip_conntrack_tuple *orig){ return invert_tuple(inverse, orig, ip_ct_find_proto(orig->dst.protonum));}static inline int resent_expect(const struct ip_conntrack_expect *i, const struct ip_conntrack_tuple *tuple, const struct ip_conntrack_tuple *mask){ DEBUGP("resent_expect\n"); DEBUGP(" tuple: "); DUMP_TUPLE(&i->tuple); DEBUGP("ct_tuple: "); DUMP_TUPLE(&i->ct_tuple); DEBUGP("test tuple: "); DUMP_TUPLE(tuple); return (((i->ct_tuple.dst.protonum == 0 && ip_ct_tuple_equal(&i->tuple, tuple)) || (i->ct_tuple.dst.protonum && ip_ct_tuple_equal(&i->ct_tuple, tuple))) && ip_ct_tuple_equal(&i->mask, mask));}/* Would two expected things clash? */static inline int expect_clash(const struct ip_conntrack_expect *i, const struct ip_conntrack_tuple *tuple, const struct ip_conntrack_tuple *mask){ /* Part covered by intersection of masks must be unequal, otherwise they clash */ struct ip_conntrack_tuple intersect_mask = { { i->mask.src.ip & mask->src.ip, { i->mask.src.u.all & mask->src.u.all } }, { i->mask.dst.ip & mask->dst.ip, { i->mask.dst.u.all & mask->dst.u.all }, i->mask.dst.protonum & mask->dst.protonum } }; return ip_ct_tuple_mask_cmp(&i->tuple, tuple, &intersect_mask);}inline void ip_conntrack_unexpect_related(struct ip_conntrack_expect *expect){ WRITE_LOCK(&ip_conntrack_lock); unexpect_related(expect); WRITE_UNLOCK(&ip_conntrack_lock);} static void expectation_timed_out(unsigned long ul_expect){ struct ip_conntrack_expect *expect = (void *) ul_expect; DEBUGP("expectation %p timed out\n", expect); WRITE_LOCK(&ip_conntrack_lock); __unexpect_related(expect); WRITE_UNLOCK(&ip_conntrack_lock);}struct ip_conntrack_expect *ip_conntrack_expect_alloc(void){ struct ip_conntrack_expect *new; new = (struct ip_conntrack_expect *) kmalloc(sizeof(struct ip_conntrack_expect), GFP_ATOMIC); if (!new) { DEBUGP("expect_related: OOM allocating expect\n"); return NULL; } /* tuple_cmp compares whole union, we have to initialized cleanly */ memset(new, 0, sizeof(struct ip_conntrack_expect)); return new;}static voidip_conntrack_expect_insert(struct ip_conntrack_expect *new, struct ip_conntrack *related_to){ DEBUGP("new expectation %p of conntrack %p\n", new, related_to); new->expectant = related_to; new->sibling = NULL; atomic_set(&new->use, 1); /* add to expected list for this connection */ list_add_tail(&new->expected_list, &related_to->sibling_list); /* add to global list of expectations */ list_prepend(&ip_conntrack_expect_list, &new->list); /* add and start timer if required */ if (related_to->helper->timeout) { init_timer(&new->timeout); new->timeout.data = (unsigned long)new; new->timeout.function = expectation_timed_out; new->timeout.expires = jiffies + related_to->helper->timeout * HZ; add_timer(&new->timeout); } related_to->expecting++;}/* Add a related connection. */int ip_conntrack_expect_related(struct ip_conntrack_expect *expect, struct ip_conntrack *related_to){ struct ip_conntrack_expect *old; int ret = 0; WRITE_LOCK(&ip_conntrack_lock);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -