📄 ip_nat_core.c
字号:
return ret;}static inline inthelper_cmp(const struct ip_nat_helper *helper, const struct ip_conntrack_tuple *tuple){ return ip_ct_tuple_mask_cmp(tuple, &helper->tuple, &helper->mask);}/* Where to manip the reply packets (will be reverse manip). */static unsigned int opposite_hook[NF_IP_NUMHOOKS]= { [NF_IP_PRE_ROUTING] = NF_IP_POST_ROUTING, [NF_IP_POST_ROUTING] = NF_IP_PRE_ROUTING, [NF_IP_LOCAL_OUT] = NF_IP_POST_ROUTING};unsigned intip_nat_setup_info(struct ip_conntrack *conntrack, const struct ip_nat_multi_range *mr, unsigned int hooknum){ struct ip_conntrack_tuple new_tuple, inv_tuple, reply; struct ip_conntrack_tuple orig_tp; struct ip_nat_info *info = &conntrack->nat.info; MUST_BE_WRITE_LOCKED(&ip_nat_lock); IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING || hooknum == NF_IP_POST_ROUTING || hooknum == NF_IP_LOCAL_OUT); IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS); /* What we've got will look like inverse of reply. Normally this is what is in the conntrack, except for prior manipulations (future optimization: if num_manips == 0, orig_tp = conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple) */ invert_tuplepr(&orig_tp, &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple);#if 0 { unsigned int i; DEBUGP("Hook %u (%s), ", hooknum, HOOK2MANIP(hooknum)==IP_NAT_MANIP_SRC ? "SRC" : "DST"); DUMP_TUPLE(&orig_tp); DEBUGP("Range %p: ", mr); for (i = 0; i < mr->rangesize; i++) { DEBUGP("%u:%s%s%s %u.%u.%u.%u - %u.%u.%u.%u %u - %u\n", i, (mr->range[i].flags & IP_NAT_RANGE_MAP_IPS) ? " MAP_IPS" : "", (mr->range[i].flags & IP_NAT_RANGE_PROTO_SPECIFIED) ? " PROTO_SPECIFIED" : "", (mr->range[i].flags & IP_NAT_RANGE_FULL) ? " FULL" : "", NIPQUAD(mr->range[i].min_ip), NIPQUAD(mr->range[i].max_ip), mr->range[i].min.all, mr->range[i].max.all); } }#endif do { if (!get_unique_tuple(&new_tuple, &orig_tp, mr, conntrack, hooknum)) { DEBUGP("ip_nat_setup_info: Can't get unique for %p.\n", conntrack); return NF_DROP; }#if 0 DEBUGP("Hook %u (%s) %p\n", hooknum, HOOK2MANIP(hooknum)==IP_NAT_MANIP_SRC ? "SRC" : "DST", conntrack); DEBUGP("Original: "); DUMP_TUPLE(&orig_tp); DEBUGP("New: "); DUMP_TUPLE(&new_tuple);#endif /* We now have two tuples (SRCIP/SRCPT/DSTIP/DSTPT): the original (A/B/C/D') and the mangled one (E/F/G/H'). We're only allowed to work with the SRC per-proto part, so we create inverses of both to start, then derive the other fields we need. */ /* Reply connection: simply invert the new tuple (G/H/E/F') */ invert_tuplepr(&reply, &new_tuple); /* Alter conntrack table so it recognizes replies. If fail this race (reply tuple now used), repeat. */ } while (!ip_conntrack_alter_reply(conntrack, &reply)); /* FIXME: We can simply used existing conntrack reply tuple here --RR */ /* Create inverse of original: C/D/A/B' */ invert_tuplepr(&inv_tuple, &orig_tp); /* Has source changed?. */ if (!ip_ct_tuple_src_equal(&new_tuple, &orig_tp)) { /* In this direction, a source manip. */ info->manips[info->num_manips++] = ((struct ip_nat_info_manip) { IP_CT_DIR_ORIGINAL, hooknum, IP_NAT_MANIP_SRC, new_tuple.src }); IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS); /* In the reverse direction, a destination manip. */ info->manips[info->num_manips++] = ((struct ip_nat_info_manip) { IP_CT_DIR_REPLY, opposite_hook[hooknum], IP_NAT_MANIP_DST, orig_tp.src }); IP_NF_ASSERT(info->num_manips <= IP_NAT_MAX_MANIPS); } /* Has destination changed? */ if (!ip_ct_tuple_dst_equal(&new_tuple, &orig_tp)) { /* In this direction, a destination manip */ info->manips[info->num_manips++] = ((struct ip_nat_info_manip) { IP_CT_DIR_ORIGINAL, hooknum, IP_NAT_MANIP_DST, reply.src }); IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS); /* In the reverse direction, a source manip. */ info->manips[info->num_manips++] = ((struct ip_nat_info_manip) { IP_CT_DIR_REPLY, opposite_hook[hooknum], IP_NAT_MANIP_SRC, inv_tuple.src }); IP_NF_ASSERT(info->num_manips <= IP_NAT_MAX_MANIPS); } /* If there's a helper, assign it; based on new tuple. */ info->helper = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *, &reply); /* It's done. */ info->initialized |= (1 << HOOK2MANIP(hooknum)); return NF_ACCEPT;}void replace_in_hashes(struct ip_conntrack *conntrack, struct ip_nat_info *info){ /* Source has changed, so replace in hashes. */ unsigned int srchash = hash_by_src(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL] .tuple.src, conntrack->tuplehash[IP_CT_DIR_ORIGINAL] .tuple.dst.protonum); /* We place packet as seen OUTGOUNG in byips_proto hash (ie. reverse dst and src of reply packet. */ unsigned int ipsprotohash = hash_by_ipsproto(conntrack->tuplehash[IP_CT_DIR_REPLY] .tuple.dst.ip, conntrack->tuplehash[IP_CT_DIR_REPLY] .tuple.src.ip, conntrack->tuplehash[IP_CT_DIR_REPLY] .tuple.dst.protonum); IP_NF_ASSERT(info->bysource.conntrack == conntrack); MUST_BE_WRITE_LOCKED(&ip_nat_lock); list_del(&info->bysource.list); list_del(&info->byipsproto.list); list_prepend(&bysource[srchash], &info->bysource); list_prepend(&byipsproto[ipsprotohash], &info->byipsproto);}void place_in_hashes(struct ip_conntrack *conntrack, struct ip_nat_info *info){ unsigned int srchash = hash_by_src(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL] .tuple.src, conntrack->tuplehash[IP_CT_DIR_ORIGINAL] .tuple.dst.protonum); /* We place packet as seen OUTGOUNG in byips_proto hash (ie. reverse dst and src of reply packet. */ unsigned int ipsprotohash = hash_by_ipsproto(conntrack->tuplehash[IP_CT_DIR_REPLY] .tuple.dst.ip, conntrack->tuplehash[IP_CT_DIR_REPLY] .tuple.src.ip, conntrack->tuplehash[IP_CT_DIR_REPLY] .tuple.dst.protonum); IP_NF_ASSERT(!info->bysource.conntrack); MUST_BE_WRITE_LOCKED(&ip_nat_lock); info->byipsproto.conntrack = conntrack; info->bysource.conntrack = conntrack; list_prepend(&bysource[srchash], &info->bysource); list_prepend(&byipsproto[ipsprotohash], &info->byipsproto);}static voidmanip_pkt(u_int16_t proto, struct iphdr *iph, size_t len, const struct ip_conntrack_manip *manip, enum ip_nat_manip_type maniptype, __u32 *nfcache){ *nfcache |= NFC_ALTERED; find_nat_proto(proto)->manip_pkt(iph, len, manip, maniptype); if (maniptype == IP_NAT_MANIP_SRC) { iph->check = ip_nat_cheat_check(~iph->saddr, manip->ip, iph->check); iph->saddr = manip->ip; } else { iph->check = ip_nat_cheat_check(~iph->daddr, manip->ip, iph->check); iph->daddr = manip->ip; }#if 0 if (ip_fast_csum((u8 *)iph, iph->ihl) != 0) DEBUGP("IP: checksum on packet bad.\n"); if (proto == IPPROTO_TCP) { void *th = (u_int32_t *)iph + iph->ihl; if (tcp_v4_check(th, len - 4*iph->ihl, iph->saddr, iph->daddr, csum_partial((char *)th, len-4*iph->ihl, 0))) DEBUGP("TCP: checksum on packet bad\n"); }#endif}/* Do packet manipulations according to binding. */unsigned intdo_bindings(struct ip_conntrack *ct, enum ip_conntrack_info ctinfo, struct ip_nat_info *info, unsigned int hooknum, struct sk_buff **pskb){ unsigned int i; struct ip_nat_helper *helper; enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); /* Need nat lock to protect against modification, but neither conntrack (referenced) and helper (deleted with synchronize_bh()) can vanish. */ READ_LOCK(&ip_nat_lock); for (i = 0; i < info->num_manips; i++) { if (info->manips[i].direction == dir && info->manips[i].hooknum == hooknum) { DEBUGP("Mangling %p: %s to %u.%u.%u.%u %u\n", *pskb, info->manips[i].maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST", NIPQUAD(info->manips[i].manip.ip), htons(info->manips[i].manip.u.all)); manip_pkt((*pskb)->nh.iph->protocol, (*pskb)->nh.iph, (*pskb)->len, &info->manips[i].manip, info->manips[i].maniptype, &(*pskb)->nfcache); } } helper = info->helper; READ_UNLOCK(&ip_nat_lock); if (helper) { /* Always defragged for helpers */ IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off & __constant_htons(IP_MF|IP_OFFSET))); return helper->help(ct, info, ctinfo, hooknum, pskb); } else return NF_ACCEPT;}unsigned inticmp_reply_translation(struct sk_buff *skb, struct ip_conntrack *conntrack, unsigned int hooknum, int dir){ struct iphdr *iph = skb->nh.iph; struct icmphdr *hdr = (struct icmphdr *)((u_int32_t *)iph + iph->ihl); struct iphdr *inner = (struct iphdr *)(hdr + 1); size_t datalen = skb->len - ((void *)inner - (void *)iph); unsigned int i; struct ip_nat_info *info = &conntrack->nat.info; IP_NF_ASSERT(skb->len >= iph->ihl*4 + sizeof(struct icmphdr)); /* Must be RELATED */ IP_NF_ASSERT(skb->nfct - (struct ip_conntrack *)skb->nfct->master == IP_CT_RELATED || skb->nfct - (struct ip_conntrack *)skb->nfct->master == IP_CT_RELATED+IP_CT_IS_REPLY); /* Redirects on non-null nats must be dropped, else they'll start talking to each other without our translation, and be confused... --RR */ if (hdr->type == ICMP_REDIRECT) { /* Don't care about races here. */ if (info->initialized != ((1 << IP_NAT_MANIP_SRC) | (1 << IP_NAT_MANIP_DST)) || info->num_manips != 0) return NF_DROP; } DEBUGP("icmp_reply_translation: translating error %p hook %u dir %s\n", skb, hooknum, dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY"); /* Note: May not be from a NAT'd host, but probably safest to do translation always as if it came from the host itself (even though a "host unreachable" coming from the host itself is a bit wierd). More explanation: some people use NAT for anonymizing. Also, CERT recommends dropping all packets from private IP addresses (although ICMP errors from internal links with such addresses are not too uncommon, as Alan Cox points out) */ READ_LOCK(&ip_nat_lock); for (i = 0; i < info->num_manips; i++) { DEBUGP("icmp_reply: manip %u dir %s hook %u\n", i, info->manips[i].direction == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", info->manips[i].hooknum); if (info->manips[i].direction != dir) continue; /* Mapping the inner packet is just like a normal packet, except it was never src/dst reversed, so where we would normally apply a dst manip, we apply a src, and vice versa. */ if (info->manips[i].hooknum == opposite_hook[hooknum]) { DEBUGP("icmp_reply: inner %s -> %u.%u.%u.%u %u\n", info->manips[i].maniptype == IP_NAT_MANIP_SRC ? "DST" : "SRC", NIPQUAD(info->manips[i].manip.ip), ntohs(info->manips[i].manip.u.udp.port)); manip_pkt(inner->protocol, inner, skb->len - ((void *)inner - (void *)iph), &info->manips[i].manip, !info->manips[i].maniptype, &skb->nfcache); /* Outer packet needs to have IP header NATed like it's a reply. */ } else if (info->manips[i].hooknum == hooknum) { /* Use mapping to map outer packet: 0 give no per-proto mapping */ DEBUGP("icmp_reply: outer %s -> %u.%u.%u.%u\n", info->manips[i].maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST", NIPQUAD(info->manips[i].manip.ip)); manip_pkt(0, iph, skb->len, &info->manips[i].manip, info->manips[i].maniptype, &skb->nfcache); } } READ_UNLOCK(&ip_nat_lock); /* Since we mangled inside ICMP packet, recalculate its checksum from scratch. (Hence the handling of incorrect checksums in conntrack, so we don't accidentally fix one.) */ hdr->checksum = 0; hdr->checksum = ip_compute_csum((unsigned char *)hdr, sizeof(*hdr) + datalen); return NF_ACCEPT;}int ip_nat_helper_register(struct ip_nat_helper *me){ int ret = 0; WRITE_LOCK(&ip_nat_lock); if (LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,&me->tuple)) ret = -EBUSY; else { list_prepend(&helpers, me); MOD_INC_USE_COUNT; } WRITE_UNLOCK(&ip_nat_lock); return ret;}static intkill_helper(const struct ip_conntrack *i, void *helper){ int ret; READ_LOCK(&ip_nat_lock); ret = (i->nat.info.helper == helper); READ_UNLOCK(&ip_nat_lock); return ret;}void ip_nat_helper_unregister(struct ip_nat_helper *me){ WRITE_LOCK(&ip_nat_lock); LIST_DELETE(&helpers, me); WRITE_UNLOCK(&ip_nat_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); /* Find anything using it, and umm, kill them. We can't turn them into normal connections: if we've adjusted SYNs, then they'll ackstorm. So we just drop it. We used to just bump module count when a connection existed, but that forces admins to gen fake RSTs or bounce box, either of which is just a long-winded way of making things worse. --RR */ ip_ct_selective_cleanup(kill_helper, me); MOD_DEC_USE_COUNT;}int __init ip_nat_init(void){ size_t i; /* Sew in builtin protocols. */ WRITE_LOCK(&ip_nat_lock); list_append(&protos, &ip_nat_protocol_tcp); list_append(&protos, &ip_nat_protocol_udp); list_append(&protos, &ip_nat_protocol_icmp); WRITE_UNLOCK(&ip_nat_lock); for (i = 0; i < IP_NAT_HTABLE_SIZE; i++) { INIT_LIST_HEAD(&bysource[i]); INIT_LIST_HEAD(&byipsproto[i]); } /* FIXME: Man, this is a hack. <SIGH> */ IP_NF_ASSERT(ip_conntrack_destroyed == NULL); ip_conntrack_destroyed = &ip_nat_cleanup_conntrack; return 0;}void ip_nat_cleanup(void){ ip_conntrack_destroyed = NULL;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -