📄 ip_masq.c
字号:
/* * * Masquerading functionality * * Copyright (c) 1994 Pauline Middelink * * $Id: ip_masq.c,v 1.34.2.2 1999/08/07 10:56:28 davem Exp $ * * * See ip_fw.c for original log * * Fixes: * Joseph Gooch : Modified ip_fw_masquerade() to do a ip_route_output() * (help by Dan Drown) : to choose the proper local address. * (and Alexey) : * Juan Jose Ciarlante : Modularized application masquerading (see ip_masq_app.c) * Juan Jose Ciarlante : New struct ip_masq_seq that holds output/input delta seq. * Juan Jose Ciarlante : Added hashed lookup by proto,maddr,mport and proto,saddr,sport * Juan Jose Ciarlante : Fixed deadlock if free ports get exhausted * Juan Jose Ciarlante : Added NO_ADDR status flag. * Richard Lynch : Added IP Autoforward * Nigel Metheringham : Added ICMP handling for demasquerade * Nigel Metheringham : Checksum checking of masqueraded data * Nigel Metheringham : Better handling of timeouts of TCP conns * Delian Delchev : Added support for ICMP requests and replys * Nigel Metheringham : ICMP in ICMP handling, tidy ups, bug fixes, made ICMP optional * Juan Jose Ciarlante : re-assign maddr if no packet received from outside * Juan Jose Ciarlante : ported to 2.1 tree * Juan Jose Ciarlante : reworked control connections * Steven Clarke : Added Port Forwarding * Juan Jose Ciarlante : Just ONE ip_masq_new (!) * Juan Jose Ciarlante : IP masq modules support * Juan Jose Ciarlante : don't go into search loop if mport specified * Juan Jose Ciarlante : locking * Steven Clarke : IP_MASQ_S_xx state design * Juan Jose Ciarlante : IP_MASQ_S state implementation * Juan Jose Ciarlante : xx_get() clears timer, _put() inserts it * Juan Jose Ciarlante : create /proc/net/ip_masq/ * Juan Jose Ciarlante : reworked checksums (save payload csum if possible) * Juan Jose Ciarlante : added missing ip_fw_masquerade checksum * Juan Jose Ciarlante : csum savings * Juan Jose Ciarlante : added user-space tunnel creation/del, etc * Juan Jose Ciarlante : (last) moved to ip_masq_user runtime module * Juan Jose Ciarlante : user timeout handling again * Juan Jose Ciarlante : make new modules support optional * Juan Jose Ciarlante : u-space context => locks reworked * Juan Jose Ciarlante : fixed stupid SMP locking bug * Juan Jose Ciarlante : fixed "tap"ing in demasq path by copy-on-w * Juan Jose Ciarlante : make masq_proto_doff() robust against fake sized/corrupted packets * Kai Bankett : do not toss other IP protos in proto_doff() * Dan Kegel : pointed correct NAT behavior for UDP streams * Julian Anastasov : use daddr and dport as hash keys * */#include <linux/config.h>#include <linux/module.h>#ifdef CONFIG_KMOD#include <linux/kmod.h>#endif#include <linux/types.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/skbuff.h>#include <asm/system.h>#include <linux/stat.h>#include <linux/proc_fs.h>#include <linux/in.h>#include <linux/ip.h>#include <linux/inet.h>#include <linux/init.h>#include <net/protocol.h>#include <net/icmp.h>#include <net/tcp.h>#include <net/udp.h>#include <net/checksum.h>#include <net/ip_masq.h>#ifdef CONFIG_IP_MASQUERADE_MOD#include <net/ip_masq_mod.h>#endif#include <linux/sysctl.h>#include <linux/ip_fw.h>#include <linux/ip_masq.h>int sysctl_ip_masq_debug = 0;/* * Exported wrapper */int ip_masq_get_debug_level(void){ return sysctl_ip_masq_debug;}struct ip_masq_hook *ip_masq_user_hook = NULL;/* * Timeout table[state] *//* static int masq_timeout_table[IP_MASQ_S_LAST+1] = { */static struct ip_masq_timeout_table masq_timeout_table = { ATOMIC_INIT(0), /* refcnt */ 0, /* scale */ { 30*60*HZ, /* IP_MASQ_S_NONE, */ 15*60*HZ, /* IP_MASQ_S_ESTABLISHED, */ 2*60*HZ, /* IP_MASQ_S_SYN_SENT, */ 1*60*HZ, /* IP_MASQ_S_SYN_RECV, */ 2*60*HZ, /* IP_MASQ_S_FIN_WAIT, */ 2*60*HZ, /* IP_MASQ_S_TIME_WAIT, */ 10*HZ, /* IP_MASQ_S_CLOSE, */ 60*HZ, /* IP_MASQ_S_CLOSE_WAIT, */ 30*HZ, /* IP_MASQ_S_LAST_ACK, */ 2*60*HZ, /* IP_MASQ_S_LISTEN, */ 5*60*HZ, /* IP_MASQ_S_UDP, */ 1*60*HZ, /* IP_MASQ_S_ICMP, */ 2*HZ,/* IP_MASQ_S_LAST */ }, /* timeout */};#define MASQUERADE_EXPIRE_RETRY masq_timeout_table.timeout[IP_MASQ_S_TIME_WAIT]static const char * state_name_table[IP_MASQ_S_LAST+1] = { "NONE", /* IP_MASQ_S_NONE, */ "ESTABLISHED", /* IP_MASQ_S_ESTABLISHED, */ "SYN_SENT", /* IP_MASQ_S_SYN_SENT, */ "SYN_RECV", /* IP_MASQ_S_SYN_RECV, */ "FIN_WAIT", /* IP_MASQ_S_FIN_WAIT, */ "TIME_WAIT", /* IP_MASQ_S_TIME_WAIT, */ "CLOSE", /* IP_MASQ_S_CLOSE, */ "CLOSE_WAIT", /* IP_MASQ_S_CLOSE_WAIT, */ "LAST_ACK", /* IP_MASQ_S_LAST_ACK, */ "LISTEN", /* IP_MASQ_S_LISTEN, */ "UDP", /* IP_MASQ_S_UDP, */ "ICMP", /* IP_MASQ_S_ICMP, */ "BUG!", /* IP_MASQ_S_LAST */};#define mNO IP_MASQ_S_NONE#define mES IP_MASQ_S_ESTABLISHED#define mSS IP_MASQ_S_SYN_SENT#define mSR IP_MASQ_S_SYN_RECV#define mFW IP_MASQ_S_FIN_WAIT#define mTW IP_MASQ_S_TIME_WAIT#define mCL IP_MASQ_S_CLOSE#define mCW IP_MASQ_S_CLOSE_WAIT#define mLA IP_MASQ_S_LAST_ACK#define mLI IP_MASQ_S_LISTENstruct masq_tcp_states_t { int next_state[IP_MASQ_S_LAST]; /* should be _LAST_TCP */};const char * ip_masq_state_name(int state){ if (state >= IP_MASQ_S_LAST) return "ERR!"; return state_name_table[state];}struct masq_tcp_states_t masq_tcp_states [] = {/* INPUT *//* mNO, mES, mSS, mSR, mFW, mTW, mCL, mCW, mLA, mLI *//*syn*/ {{mSR, mES, mES, mSR, mSR, mSR, mSR, mSR, mSR, mSR }},/*fin*/ {{mCL, mCW, mSS, mTW, mTW, mTW, mCL, mCW, mLA, mLI }},/*ack*/ {{mCL, mES, mSS, mSR, mFW, mTW, mCL, mCW, mCL, mLI }},/*rst*/ {{mCL, mCL, mCL, mSR, mCL, mCL, mCL, mCL, mLA, mLI }},/* OUTPUT *//* mNO, mES, mSS, mSR, mFW, mTW, mCL, mCW, mLA, mLI *//*syn*/ {{mSS, mES, mSS, mES, mSS, mSS, mSS, mSS, mSS, mLI }},/*fin*/ {{mTW, mFW, mSS, mTW, mFW, mTW, mCL, mTW, mLA, mLI }},/*ack*/ {{mES, mES, mSS, mSR, mFW, mTW, mCL, mCW, mLA, mES }},/*rst*/ {{mCL, mCL, mSS, mCL, mCL, mTW, mCL, mCL, mCL, mCL }},};static __inline__ int masq_tcp_state_idx(struct tcphdr *th, int output) { /* * [0-3]: input states, [4-7]: output. */ if (output) output=4; if (th->rst) return output+3; if (th->syn) return output+0; if (th->fin) return output+1; if (th->ack) return output+2; return -1;}static int masq_set_state_timeout(struct ip_masq *ms, int state){ struct ip_masq_timeout_table *mstim = ms->timeout_table; int scale; /* * Use default timeout table if no specific for this entry */ if (!mstim) mstim = &masq_timeout_table; ms->timeout = mstim->timeout[ms->state=state]; scale = mstim->scale; if (scale<0) ms->timeout >>= -scale; else if (scale > 0) ms->timeout <<= scale; return state;}static int masq_tcp_state(struct ip_masq *ms, int output, struct tcphdr *th){ int state_idx; int new_state = IP_MASQ_S_CLOSE; if ((state_idx = masq_tcp_state_idx(th, output)) < 0) { IP_MASQ_DEBUG(1, "masq_state_idx(%d)=%d!!!\n", output, state_idx); goto tcp_state_out; } new_state = masq_tcp_states[state_idx].next_state[ms->state]; tcp_state_out: if (new_state!=ms->state) IP_MASQ_DEBUG(1, "%s %s [%c%c%c%c] %08lX:%04X-%08lX:%04X state: %s->%s\n", masq_proto_name(ms->protocol), output? "output" : "input ", th->syn? 'S' : '.', th->fin? 'F' : '.', th->ack? 'A' : '.', th->rst? 'R' : '.', ntohl(ms->saddr), ntohs(ms->sport), ntohl(ms->daddr), ntohs(ms->dport), ip_masq_state_name(ms->state), ip_masq_state_name(new_state)); return masq_set_state_timeout(ms, new_state);}/* * Handle state transitions */static int masq_set_state(struct ip_masq *ms, int output, struct iphdr *iph, void *tp){ switch (iph->protocol) { case IPPROTO_ICMP: return masq_set_state_timeout(ms, IP_MASQ_S_ICMP); case IPPROTO_UDP: return masq_set_state_timeout(ms, IP_MASQ_S_UDP); case IPPROTO_TCP: return masq_tcp_state(ms, output, tp); } return -1;}/* * Set LISTEN timeout. (ip_masq_put will setup timer) */int ip_masq_listen(struct ip_masq *ms){ masq_set_state_timeout(ms, IP_MASQ_S_LISTEN); return ms->timeout;}/* * Dynamic address rewriting */extern int sysctl_ip_dynaddr;/* * Lookup lock */rwlock_t __ip_masq_lock = RW_LOCK_UNLOCKED;/* * Implement IP packet masquerading *//* * Converts an ICMP reply code into the equivalent request code */static __inline__ const __u8 icmp_type_request(__u8 type){ switch (type) { case ICMP_ECHOREPLY: return ICMP_ECHO; break; case ICMP_TIMESTAMPREPLY: return ICMP_TIMESTAMP; break; case ICMP_INFO_REPLY: return ICMP_INFO_REQUEST; break; case ICMP_ADDRESSREPLY: return ICMP_ADDRESS; break; default: return (255); break; }}/* * Helper macros - attempt to make code clearer! *//* ID used in ICMP lookups */#define icmp_id(icmph) ((icmph->un).echo.id)/* (port) hash value using in ICMP lookups for requests */#define icmp_hv_req(icmph) ((__u16)(icmph->code+(__u16)(icmph->type<<8)))/* (port) hash value using in ICMP lookups for replies */#define icmp_hv_rep(icmph) ((__u16)(icmph->code+(__u16)(icmp_type_request(icmph->type)<<8)))/* * Last masq_port number in use. * Will cycle in MASQ_PORT boundaries. */static __u16 masq_port = PORT_MASQ_BEGIN;static spinlock_t masq_port_lock = SPIN_LOCK_UNLOCKED;/* * free ports counters (UDP & TCP) * * Their value is _less_ or _equal_ to actual free ports: * same masq port, diff masq addr (firewall iface address) allocated * entries are accounted but their actually don't eat a more than 1 port. * * Greater values could lower MASQ_EXPIRATION setting as a way to * manage 'masq_entries resource'. * * By default we will reuse masq.port iff (output) connection * (5-upla) if not duplicated. * This may break midentd and others ... */#ifdef CONFIG_IP_MASQ_NREUSE#define PORT_MASQ_MUL 1#else#define PORT_MASQ_MUL 10#endif/* * At the moment, hardcore in sync with masq_proto_num */atomic_t ip_masq_free_ports[3] = { ATOMIC_INIT((PORT_MASQ_END-PORT_MASQ_BEGIN) * PORT_MASQ_MUL),/* UDP */ ATOMIC_INIT((PORT_MASQ_END-PORT_MASQ_BEGIN) * PORT_MASQ_MUL),/* TCP */ ATOMIC_INIT((PORT_MASQ_END-PORT_MASQ_BEGIN) * PORT_MASQ_MUL),/* ICMP */};/* * Counts entries that have been requested with specific mport. * Used for incoming packets to "relax" input rule (port in MASQ range). */atomic_t mport_count = ATOMIC_INIT(0);EXPORT_SYMBOL(ip_masq_get_debug_level);EXPORT_SYMBOL(ip_masq_new);EXPORT_SYMBOL(ip_masq_listen);EXPORT_SYMBOL(ip_masq_free_ports);EXPORT_SYMBOL(ip_masq_out_get);EXPORT_SYMBOL(ip_masq_in_get);EXPORT_SYMBOL(ip_masq_put);EXPORT_SYMBOL(ip_masq_control_add);EXPORT_SYMBOL(ip_masq_control_del);EXPORT_SYMBOL(ip_masq_control_get);EXPORT_SYMBOL(ip_masq_user_hook);EXPORT_SYMBOL(ip_masq_state_name);EXPORT_SYMBOL(ip_masq_select_addr);EXPORT_SYMBOL(__ip_masq_lock);EXPORT_SYMBOL(ip_masq_m_table);EXPORT_SYMBOL(ip_masq_s_table);EXPORT_SYMBOL(ip_masq_d_table);/* * 3 ip_masq hash double linked tables: * 2 for input m{addr,port} and output s{addr,port} pkts lookups. * 1 for extra modules support (daddr) */ #define IP_MASQ_NTABLES 3struct list_head ip_masq_m_table[IP_MASQ_TAB_SIZE];struct list_head ip_masq_s_table[IP_MASQ_TAB_SIZE];struct list_head ip_masq_d_table[IP_MASQ_TAB_SIZE];/* * timeouts */#if 000 /* FIXED timeout handling */static struct ip_fw_masq ip_masq_dummy = { MASQUERADE_EXPIRE_TCP, MASQUERADE_EXPIRE_TCP_FIN, MASQUERADE_EXPIRE_UDP};EXPORT_SYMBOL(ip_masq_expire);struct ip_fw_masq *ip_masq_expire = &ip_masq_dummy;#endif/* * These flags enable non-strict d{addr,port} checks * Given that both (in/out) lookup tables are hashed * by m{addr,port} and s{addr,port} this is quite easy */#define MASQ_DADDR_PASS (IP_MASQ_F_NO_DADDR|IP_MASQ_F_DLOOSE)#define MASQ_DPORT_PASS (IP_MASQ_F_NO_DPORT|IP_MASQ_F_DLOOSE)/* * By default enable dest loose semantics */#define CONFIG_IP_MASQ_LOOSE_DEFAULT 1/* * Set masq expiration (deletion) and adds timer, * if timeout==0 cancel expiration. * Warning: it does not check/delete previous timer! */static void __ip_masq_set_expire(struct ip_masq *ms, unsigned long tout){ if (tout) { ms->timer.expires = jiffies+tout; add_timer(&ms->timer); } else { del_timer(&ms->timer); }}/* * Returns hash value */static __inline__ unsigned ip_masq_hash_key(unsigned proto, __u32 addr, __u16 port){ return (proto^ntohl(addr)^ntohs(port)) & (IP_MASQ_TAB_SIZE-1);}/* * Hashes ip_masq by its proto,addrs,ports. * should be called with locked tables. * returns bool success. */static int ip_masq_hash(struct ip_masq *ms){ unsigned hash; if (ms->flags & IP_MASQ_F_HASHED) { IP_MASQ_ERR( "ip_masq_hash(): request for already hashed, called from %p\n", __builtin_return_address(0)); return 0; } atomic_add(IP_MASQ_NTABLES, &ms->refcnt); if ((ms->flags & (MASQ_DADDR_PASS | MASQ_DPORT_PASS | IP_MASQ_F_SIMPLE_HASH)) == 0) /* * Hash by proto,m{addr,port},d{addr,port} */ hash = ip_masq_hash_key(ms->protocol, ms->maddr^ms->daddr, ms->mport^ms->dport); else /* * Hash by proto,m{addr,port} */ hash = ip_masq_hash_key(ms->protocol, ms->maddr, ms->mport); list_add(&ms->m_list, &ip_masq_m_table[hash]); if ((ms->flags & (MASQ_DADDR_PASS | MASQ_DPORT_PASS | IP_MASQ_F_NO_SADDR | IP_MASQ_F_NO_SPORT | IP_MASQ_F_SIMPLE_HASH)) == 0) /* * Hash by proto,s{addr,port},d{addr,port} */ hash = ip_masq_hash_key(ms->protocol, ms->saddr^ms->daddr, ms->sport^ms->dport); else /* * Hash by proto,s{addr,port} */ hash = ip_masq_hash_key(ms->protocol, ms->saddr, ms->sport); list_add(&ms->s_list, &ip_masq_s_table[hash]); /* * Hash by proto,d{addr,port} */ hash = ip_masq_hash_key(ms->protocol, ms->daddr, ms->dport); list_add(&ms->d_list, &ip_masq_d_table[hash]); ms->flags |= IP_MASQ_F_HASHED; return 1;}/* * UNhashes ip_masq from ip_masq_[ms]_tables. * should be called with locked tables. * returns bool success. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -