📄 ip_masq.c
字号:
static int ip_masq_unhash(struct ip_masq *ms){ if (!(ms->flags & IP_MASQ_F_HASHED)) { IP_MASQ_ERR( "ip_masq_unhash(): request for unhash flagged, called from %p\n", __builtin_return_address(0)); return 0; } list_del(&ms->m_list); list_del(&ms->s_list); list_del(&ms->d_list); atomic_sub(IP_MASQ_NTABLES, &ms->refcnt); ms->flags &= ~IP_MASQ_F_HASHED; return 1;}/* * Returns ip_masq associated with supplied parameters, either * broken out of the ip/tcp headers or directly supplied for those * pathological protocols with address/port in the data stream * (ftp, irc). addresses and ports are in network order. * called for pkts coming from OUTside-to-INside the firewall. * * s_addr, s_port: pkt source address (foreign host) * d_addr, d_port: pkt dest address (firewall) * * NB. Cannot check destination address, just for the incoming port. * reason: archie.doc.ac.uk has 6 interfaces, you send to * phoenix and get a reply from any other interface(==dst)! * * [Only for UDP] - AC * * Caller must lock tables */static struct ip_masq * __ip_masq_in_get(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port){ unsigned hash; struct ip_masq *ms = NULL; struct list_head *l,*e; hash = ip_masq_hash_key(protocol, d_addr^s_addr, d_port^s_port); l = &ip_masq_m_table[hash]; for (e=l->next; e!=l; e=e->next) { ms = list_entry(e, struct ip_masq, m_list); if (s_port==ms->dport && s_addr==ms->daddr && d_port==ms->mport && protocol==ms->protocol && d_addr==ms->maddr && ((ms->flags & (MASQ_DADDR_PASS | MASQ_DPORT_PASS)) == 0) ) { IP_MASQ_DEBUG(2, "look/in %d %08X:%04hX->%08X:%04hX OK\n", protocol, s_addr, s_port, d_addr, d_port); atomic_inc(&ms->refcnt); goto out; } } hash = ip_masq_hash_key(protocol, d_addr, d_port); l = &ip_masq_m_table[hash]; for (e=l->next; e!=l; e=e->next) { ms = list_entry(e, struct ip_masq, m_list); if (protocol==ms->protocol && (d_addr==ms->maddr && d_port==ms->mport) && (s_addr==ms->daddr || ms->flags & MASQ_DADDR_PASS) && (s_port==ms->dport || ms->flags & MASQ_DPORT_PASS) ) { IP_MASQ_DEBUG(2, "look/in %d %08X:%04hX->%08X:%04hX OK\n", protocol, s_addr, s_port, d_addr, d_port); atomic_inc(&ms->refcnt); goto out; } } IP_MASQ_DEBUG(2, "look/in %d %08X:%04hX->%08X:%04hX fail\n", protocol, s_addr, s_port, d_addr, d_port); ms = NULL;out: return ms;}/* * Returns ip_masq associated with supplied parameters, either * broken out of the ip/tcp headers or directly supplied for those * pathological protocols with address/port in the data stream * (ftp, irc). addresses and ports are in network order. * called for pkts coming from inside-to-OUTside the firewall. * * Normally we know the source address and port but for some protocols * (e.g. ftp PASV) we do not know the source port initially. Alas the * hash is keyed on source port so if the first lookup fails then try again * with a zero port, this time only looking at entries marked "no source * port". * * Caller must lock tables */static struct ip_masq * __ip_masq_out_get(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port){ unsigned hash; struct ip_masq *ms = NULL; struct list_head *l,*e; /* * Check for "full" addressed entries */ hash = ip_masq_hash_key(protocol, s_addr^d_addr, s_port^d_port); l = &ip_masq_s_table[hash]; for (e=l->next; e!=l; e=e->next) { ms = list_entry(e, struct ip_masq, s_list); if (d_addr==ms->daddr && d_port==ms->dport && s_addr==ms->saddr && s_port==ms->sport && protocol==ms->protocol && ((ms->flags & (MASQ_DADDR_PASS | MASQ_DPORT_PASS | IP_MASQ_F_NO_SADDR | IP_MASQ_F_NO_SPORT)) == 0) ) { IP_MASQ_DEBUG(2, "lk/out0 %d %08X:%04hX->%08X:%04hX OK\n", protocol, s_addr, s_port, d_addr, d_port); atomic_inc(&ms->refcnt); goto out; } } hash = ip_masq_hash_key(protocol, s_addr, s_port); l = &ip_masq_s_table[hash]; for (e=l->next; e!=l; e=e->next) { ms = list_entry(e, struct ip_masq, s_list); if (protocol == ms->protocol && s_addr == ms->saddr && s_port == ms->sport && (d_addr==ms->daddr || ms->flags & MASQ_DADDR_PASS) && (d_port==ms->dport || ms->flags & MASQ_DPORT_PASS) ) { IP_MASQ_DEBUG(2, "lk/out1 %d %08X:%04hX->%08X:%04hX OK\n", protocol, s_addr, s_port, d_addr, d_port); atomic_inc(&ms->refcnt); goto out; } } /* * Check for NO_SPORT entries */ hash = ip_masq_hash_key(protocol, s_addr, 0); l = &ip_masq_s_table[hash]; for (e=l->next; e!=l; e=e->next) { ms = list_entry(e, struct ip_masq, s_list); if (ms->flags & IP_MASQ_F_NO_SPORT && protocol == ms->protocol && s_addr == ms->saddr && (d_addr==ms->daddr || ms->flags & MASQ_DADDR_PASS) && (d_port==ms->dport || ms->flags & MASQ_DPORT_PASS) ) { IP_MASQ_DEBUG(2, "lk/out2 %d %08X:%04hX->%08X:%04hX OK\n", protocol, s_addr, s_port, d_addr, d_port); atomic_inc(&ms->refcnt); goto out; } } IP_MASQ_DEBUG(2, "lk/out1 %d %08X:%04hX->%08X:%04hX fail\n", protocol, s_addr, s_port, d_addr, d_port); ms = NULL;out: return ms;}#ifdef CONFIG_IP_MASQ_NREUSE/* * Returns ip_masq for given proto,m_addr,m_port. * called by allocation routine to find an unused m_port. * * Caller must lock tables */static struct ip_masq * __ip_masq_getbym(int protocol, __u32 m_addr, __u16 m_port){ unsigned hash; struct ip_masq *ms = NULL; hash = ip_masq_hash_key(protocol, m_addr, m_port); for(ms = ip_masq_m_tab[hash]; ms ; ms = ms->m_link) { if ( protocol==ms->protocol && (m_addr==ms->maddr && m_port==ms->mport)) { atomic_inc(&ms->refcnt); goto out; } }out: return ms;}#endifstruct ip_masq * ip_masq_out_get(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port) { struct ip_masq *ms; read_lock(&__ip_masq_lock); ms = __ip_masq_out_get(protocol, s_addr, s_port, d_addr, d_port); read_unlock(&__ip_masq_lock); if (ms) __ip_masq_set_expire(ms, 0); return ms;}struct ip_masq * ip_masq_in_get(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port){ struct ip_masq *ms; read_lock(&__ip_masq_lock); ms = __ip_masq_in_get(protocol, s_addr, s_port, d_addr, d_port); read_unlock(&__ip_masq_lock); if (ms) __ip_masq_set_expire(ms, 0); return ms;}static __inline__ void __ip_masq_put(struct ip_masq *ms) { atomic_dec(&ms->refcnt);}void ip_masq_put(struct ip_masq *ms){ /* * Decrement refcnt */ __ip_masq_put(ms); /* * if refcnt==IP_MASQ_NTABLES */ if (atomic_read(&ms->refcnt)==IP_MASQ_NTABLES) { __ip_masq_set_expire(ms, ms->timeout); } else { IP_MASQ_DEBUG(0, "did not set timer with refcnt=%d, called from %p\n", atomic_read(&ms->refcnt), __builtin_return_address(0)); }}extern int sysctl_ip_always_defrag;static void masq_expire(unsigned long data){ struct ip_masq *ms = (struct ip_masq *)data; ms->timeout = MASQUERADE_EXPIRE_RETRY; /* * hey, I'm using it */ atomic_inc(&ms->refcnt); IP_MASQ_DEBUG(1, "Masqueraded %s %08lX:%04X expired\n", masq_proto_name(ms->protocol), ntohl(ms->saddr),ntohs(ms->sport)); write_lock(&__ip_masq_lock);#if 0000 /* * Already locked, do bounce ... */ if (ip_masq_nlocks(&__ip_masq_lock) != 1) { goto masq_expire_later; }#endif /* * do I control anybody? */ if (atomic_read(&ms->n_control)) goto masq_expire_later; /* * does anybody controls me? */ if (ms->control) ip_masq_control_del(ms); if (ip_masq_unhash(ms)) { if (ms->flags&IP_MASQ_F_MPORT) { atomic_dec(&mport_count); } else { atomic_inc(ip_masq_free_ports + masq_proto_num(ms->protocol)); } ip_masq_unbind_app(ms); } /* * refcnt==1 implies I'm the only one referrer */ if (atomic_read(&ms->refcnt) == 1) { kfree_s(ms,sizeof(*ms)); sysctl_ip_always_defrag--; MOD_DEC_USE_COUNT; goto masq_expire_out; }masq_expire_later: IP_MASQ_DEBUG(0, "masq_expire delayed: %s %08lX:%04X->%08lX:%04X masq.refcnt-1=%d masq.n_control=%d\n", masq_proto_name(ms->protocol), ntohl(ms->saddr), ntohs(ms->sport), ntohl(ms->daddr), ntohs(ms->dport), atomic_read(&ms->refcnt)-1, atomic_read(&ms->n_control)); ip_masq_put(ms);masq_expire_out: write_unlock(&__ip_masq_lock);}static __u16 get_next_mport(void){ __u16 mport; spin_lock_irq(&masq_port_lock); /* * Try the next available port number */ mport = htons(masq_port++); if (masq_port==PORT_MASQ_END) masq_port = PORT_MASQ_BEGIN; spin_unlock_irq(&masq_port_lock); return mport;}/* * Create a new masquerade list entry, also allocate an * unused mport, keeping the portnumber between the * given boundaries MASQ_BEGIN and MASQ_END. * * Be careful, it can be called from u-space */struct ip_masq * ip_masq_new(int proto, __u32 maddr, __u16 mport, __u32 saddr, __u16 sport, __u32 daddr, __u16 dport, unsigned mflags){ struct ip_masq *ms, *mst; int ports_tried; atomic_t *free_ports_p = NULL; static int n_fails = 0; int prio; if (masq_proto_num(proto)!=-1 && mport == 0) { free_ports_p = ip_masq_free_ports + masq_proto_num(proto); if (atomic_read(free_ports_p) == 0) { if (++n_fails < 5) IP_MASQ_ERR( "ip_masq_new(proto=%s): no free ports.\n", masq_proto_name(proto)); return NULL; } } prio = (mflags&IP_MASQ_F_USER) ? GFP_KERNEL : GFP_ATOMIC; ms = (struct ip_masq *) kmalloc(sizeof(struct ip_masq), prio); if (ms == NULL) { if (++n_fails < 5) IP_MASQ_ERR("ip_masq_new(proto=%s): no memory available.\n", masq_proto_name(proto)); return NULL; } MOD_INC_USE_COUNT; sysctl_ip_always_defrag++; memset(ms, 0, sizeof(*ms)); INIT_LIST_HEAD(&ms->s_list); INIT_LIST_HEAD(&ms->m_list); INIT_LIST_HEAD(&ms->d_list); init_timer(&ms->timer); ms->timer.data = (unsigned long)ms; ms->timer.function = masq_expire; ms->protocol = proto; ms->saddr = saddr; ms->sport = sport; ms->daddr = daddr; ms->dport = dport; ms->flags = mflags; ms->app_data = NULL; ms->control = NULL; atomic_set(&ms->n_control,0); atomic_set(&ms->refcnt,0); if (proto == IPPROTO_UDP && !mport)#ifdef CONFIG_IP_MASQ_LOOSE_DEFAULT /* * Flag this tunnel as "dest loose" * */ ms->flags |= IP_MASQ_F_DLOOSE;#else ms->flags |= IP_MASQ_F_NO_DADDR;#endif /* get masq address from rif */ ms->maddr = maddr; /* * This flag will allow masq. addr (ms->maddr) * to follow forwarding interface address. */ ms->flags |= IP_MASQ_F_NO_REPLY; /* * We want a specific mport. Be careful. */ if (masq_proto_num(proto) == -1 || mport) { ms->mport = mport; /* * Check 5-upla uniqueness */ if (mflags & IP_MASQ_F_USER) write_lock_bh(&__ip_masq_lock); else write_lock(&__ip_masq_lock); mst = __ip_masq_in_get(proto, daddr, dport, maddr, mport); if (mst==NULL) { ms->flags |= IP_MASQ_F_MPORT; atomic_inc(&mport_count); ip_masq_hash(ms); if (mflags & IP_MASQ_F_USER) write_unlock_bh(&__ip_masq_lock); else write_unlock(&__ip_masq_lock); ip_masq_bind_app(ms); atomic_inc(&ms->refcnt); masq_set_state_timeout(ms, IP_MASQ_S_NONE); return ms; } if (mflags & IP_MASQ_F_USER) write_unlock_bh(&__ip_masq_lock); else write_unlock(&__ip_masq_lock); __ip_masq_put(mst); IP_MASQ_ERR( "Already used connection: %s, %d.%d.%d.%d:%d => %d.%d.%d.%d:%d, called from %p\n", masq_proto_name(proto), NIPQUAD(maddr), ntohs(mport), NIPQUAD(daddr), ntohs(dport), __builtin_return_address(0)); goto mport_nono; } for (ports_tried = 0; (atomic_read(free_ports_p) && (ports_tried <= (PORT_MASQ_END - PORT_MASQ_BEGIN))); ports_tried++){ mport = ms->mport = get_next_mport(); /* * lookup to find out if this connection is used. */ if (mflags & IP_MASQ_F_USER) write_lock_bh(&__ip_masq_lock); else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -