📄 ip_nat_core.c
字号:
invert_tuplepr(&reply, &new_tuple); ip_conntrack_alter_reply(conntrack, &reply); /* Non-atomic: we own this at the moment. */ if (maniptype == IP_NAT_MANIP_SRC) conntrack->status |= IPS_SRC_NAT; else conntrack->status |= IPS_DST_NAT; } /* Place in source hash if this is the first time. */ if (have_to_hash) { unsigned int srchash = hash_by_src(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL] .tuple); write_lock_bh(&ip_nat_lock); list_add(&info->bysource, &bysource[srchash]); write_unlock_bh(&ip_nat_lock); } /* It's done. */ if (maniptype == IP_NAT_MANIP_DST) set_bit(IPS_DST_NAT_DONE_BIT, &conntrack->status); else set_bit(IPS_SRC_NAT_DONE_BIT, &conntrack->status); return NF_ACCEPT;}EXPORT_SYMBOL(ip_nat_setup_info);/* Returns true if succeeded. */static intmanip_pkt(u_int16_t proto, struct sk_buff **pskb, unsigned int iphdroff, const struct ip_conntrack_tuple *target, enum ip_nat_manip_type maniptype){ struct iphdr *iph; struct ip_nat_protocol *p; if (!skb_make_writable(pskb, iphdroff + sizeof(*iph))) return 0; iph = (void *)(*pskb)->data + iphdroff; /* Manipulate protcol part. */ p = ip_nat_proto_find_get(proto); if (!p->manip_pkt(pskb, iphdroff, target, maniptype)) { ip_nat_proto_put(p); return 0; } ip_nat_proto_put(p); iph = (void *)(*pskb)->data + iphdroff; if (maniptype == IP_NAT_MANIP_SRC) { iph->check = ip_nat_cheat_check(~iph->saddr, target->src.ip, iph->check); iph->saddr = target->src.ip; } else { iph->check = ip_nat_cheat_check(~iph->daddr, target->dst.ip, iph->check); iph->daddr = target->dst.ip; } return 1;}/* Do packet manipulations according to ip_nat_setup_info. */unsigned int ip_nat_packet(struct ip_conntrack *ct, enum ip_conntrack_info ctinfo, unsigned int hooknum, struct sk_buff **pskb){ enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); unsigned long statusbit; enum ip_nat_manip_type mtype = HOOK2MANIP(hooknum); if (mtype == IP_NAT_MANIP_SRC) statusbit = IPS_SRC_NAT; else statusbit = IPS_DST_NAT; /* Invert if this is reply dir. */ if (dir == IP_CT_DIR_REPLY) statusbit ^= IPS_NAT_MASK; /* Non-atomic: these bits don't change. */ if (ct->status & statusbit) { struct ip_conntrack_tuple target; /* We are aiming to look like inverse of other direction. */ invert_tuplepr(&target, &ct->tuplehash[!dir].tuple); if (!manip_pkt(target.dst.protonum, pskb, 0, &target, mtype)) return NF_DROP; } return NF_ACCEPT;}EXPORT_SYMBOL_GPL(ip_nat_packet);/* Dir is direction ICMP is coming from (opposite to packet it contains) */int ip_nat_icmp_reply_translation(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_nat_manip_type manip, enum ip_conntrack_dir dir){ struct { struct icmphdr icmp; struct iphdr ip; } *inside; struct ip_conntrack_tuple inner, target; int hdrlen = (*pskb)->nh.iph->ihl * 4; if (!skb_make_writable(pskb, hdrlen + sizeof(*inside))) return 0; inside = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4; /* We're actually going to mangle it beyond trivial checksum adjustment, so make sure the current checksum is correct. */ if ((*pskb)->ip_summed != CHECKSUM_UNNECESSARY) { hdrlen = (*pskb)->nh.iph->ihl * 4; if ((u16)csum_fold(skb_checksum(*pskb, hdrlen, (*pskb)->len - hdrlen, 0))) return 0; } /* Must be RELATED */ IP_NF_ASSERT((*pskb)->nfctinfo == IP_CT_RELATED || (*pskb)->nfctinfo == 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 (inside->icmp.type == ICMP_REDIRECT) { /* If NAT isn't finished, assume it and drop. */ if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK) return 0; if (ct->status & IPS_NAT_MASK) return 0; } DEBUGP("icmp_reply_translation: translating error %p manp %u dir %s\n", *pskb, manip, dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY"); if (!ip_ct_get_tuple(&inside->ip, *pskb, (*pskb)->nh.iph->ihl*4 + sizeof(struct icmphdr) + inside->ip.ihl*4, &inner, __ip_conntrack_proto_find(inside->ip.protocol))) return 0; /* Change inner back to look like incoming packet. We do the opposite manip on this hook to normal, because it might not pass all hooks (locally-generated ICMP). Consider incoming packet: PREROUTING (DST manip), routing produces ICMP, goes through POSTROUTING (which must correct the DST manip). */ if (!manip_pkt(inside->ip.protocol, pskb, (*pskb)->nh.iph->ihl*4 + sizeof(inside->icmp), &ct->tuplehash[!dir].tuple, !manip)) return 0; /* Reloading "inside" here since manip_pkt inner. */ inside = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4; inside->icmp.checksum = 0; inside->icmp.checksum = csum_fold(skb_checksum(*pskb, hdrlen, (*pskb)->len - hdrlen, 0)); /* Change outer to look the reply to an incoming packet * (proto 0 means don't invert per-proto part). */ /* Obviously, we need to NAT destination IP, but source IP should be NAT'ed only if it is from a NAT'd host. 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) */ if (manip != IP_NAT_MANIP_SRC || ((*pskb)->nh.iph->saddr == ct->tuplehash[dir].tuple.src.ip)) { invert_tuplepr(&target, &ct->tuplehash[!dir].tuple); if (!manip_pkt(0, pskb, 0, &target, manip)) return 0; } return 1;}EXPORT_SYMBOL_GPL(ip_nat_icmp_reply_translation);/* Protocol registration. */int ip_nat_protocol_register(struct ip_nat_protocol *proto){ int ret = 0; write_lock_bh(&ip_nat_lock); if (ip_nat_protos[proto->protonum] != &ip_nat_unknown_protocol) { ret = -EBUSY; goto out; } ip_nat_protos[proto->protonum] = proto; out: write_unlock_bh(&ip_nat_lock); return ret;}EXPORT_SYMBOL(ip_nat_protocol_register);/* Noone stores the protocol anywhere; simply delete it. */void ip_nat_protocol_unregister(struct ip_nat_protocol *proto){ write_lock_bh(&ip_nat_lock); ip_nat_protos[proto->protonum] = &ip_nat_unknown_protocol; write_unlock_bh(&ip_nat_lock); /* Someone could be still looking at the proto in a bh. */ synchronize_net();}EXPORT_SYMBOL(ip_nat_protocol_unregister);#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)intip_nat_port_range_to_nfattr(struct sk_buff *skb, const struct ip_nat_range *range){ NFA_PUT(skb, CTA_PROTONAT_PORT_MIN, sizeof(u_int16_t), &range->min.tcp.port); NFA_PUT(skb, CTA_PROTONAT_PORT_MAX, sizeof(u_int16_t), &range->max.tcp.port); return 0;nfattr_failure: return -1;}intip_nat_port_nfattr_to_range(struct nfattr *tb[], struct ip_nat_range *range){ int ret = 0; /* we have to return whether we actually parsed something or not */ if (tb[CTA_PROTONAT_PORT_MIN-1]) { ret = 1; range->min.tcp.port = *(u_int16_t *)NFA_DATA(tb[CTA_PROTONAT_PORT_MIN-1]); } if (!tb[CTA_PROTONAT_PORT_MAX-1]) { if (ret) range->max.tcp.port = range->min.tcp.port; } else { ret = 1; range->max.tcp.port = *(u_int16_t *)NFA_DATA(tb[CTA_PROTONAT_PORT_MAX-1]); } return ret;}EXPORT_SYMBOL_GPL(ip_nat_port_nfattr_to_range);EXPORT_SYMBOL_GPL(ip_nat_port_range_to_nfattr);#endifstatic int __init ip_nat_init(void){ size_t i; /* Leave them the same for the moment. */ ip_nat_htable_size = ip_conntrack_htable_size; /* One vmalloc for both hash tables */ bysource = vmalloc(sizeof(struct list_head) * ip_nat_htable_size); if (!bysource) return -ENOMEM; /* Sew in builtin protocols. */ write_lock_bh(&ip_nat_lock); for (i = 0; i < MAX_IP_NAT_PROTO; i++) ip_nat_protos[i] = &ip_nat_unknown_protocol; ip_nat_protos[IPPROTO_TCP] = &ip_nat_protocol_tcp; ip_nat_protos[IPPROTO_UDP] = &ip_nat_protocol_udp; ip_nat_protos[IPPROTO_ICMP] = &ip_nat_protocol_icmp; write_unlock_bh(&ip_nat_lock); for (i = 0; i < ip_nat_htable_size; i++) { INIT_LIST_HEAD(&bysource[i]); } /* FIXME: Man, this is a hack. <SIGH> */ IP_NF_ASSERT(ip_conntrack_destroyed == NULL); ip_conntrack_destroyed = &ip_nat_cleanup_conntrack; /* Initialize fake conntrack so that NAT will skip it */ ip_conntrack_untracked.status |= IPS_NAT_DONE_MASK; return 0;}/* Clear NAT section of all conntracks, in case we're loaded again. */static int clean_nat(struct ip_conntrack *i, void *data){ memset(&i->nat, 0, sizeof(i->nat)); i->status &= ~(IPS_NAT_MASK | IPS_NAT_DONE_MASK | IPS_SEQ_ADJUST); return 0;}static void __exit ip_nat_cleanup(void){ ip_ct_iterate_cleanup(&clean_nat, NULL); ip_conntrack_destroyed = NULL; vfree(bysource);}MODULE_LICENSE("GPL");module_init(ip_nat_init);module_exit(ip_nat_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -