diff options
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/Config.in | 6 | ||||
-rw-r--r-- | net/ipv4/Makefile | 20 | ||||
-rw-r--r-- | net/ipv4/arp.c | 5 | ||||
-rw-r--r-- | net/ipv4/fib_frontend.c | 2 | ||||
-rw-r--r-- | net/ipv4/icmp.c | 12 | ||||
-rw-r--r-- | net/ipv4/ip_forward.c | 22 | ||||
-rw-r--r-- | net/ipv4/ip_masq.c | 1488 | ||||
-rw-r--r-- | net/ipv4/ip_masq_app.c | 62 | ||||
-rw-r--r-- | net/ipv4/ip_masq_autofw.c | 427 | ||||
-rw-r--r-- | net/ipv4/ip_masq_cuseeme.c | 261 | ||||
-rw-r--r-- | net/ipv4/ip_masq_ftp.c | 244 | ||||
-rw-r--r-- | net/ipv4/ip_masq_irc.c | 366 | ||||
-rw-r--r-- | net/ipv4/ip_masq_mod.c | 316 | ||||
-rw-r--r-- | net/ipv4/ip_masq_portfw.c | 461 | ||||
-rw-r--r-- | net/ipv4/ip_masq_quake.c | 10 | ||||
-rw-r--r-- | net/ipv4/ip_masq_raudio.c | 422 | ||||
-rw-r--r-- | net/ipv4/ip_masq_vdolive.c | 291 | ||||
-rw-r--r-- | net/ipv4/ip_output.c | 8 | ||||
-rw-r--r-- | net/ipv4/ip_sockglue.c | 21 | ||||
-rw-r--r-- | net/ipv4/route.c | 5 | ||||
-rw-r--r-- | net/ipv4/sysctl_net_ipv4.c | 14 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 95 | ||||
-rw-r--r-- | net/ipv4/tcp_timer.c | 103 | ||||
-rw-r--r-- | net/ipv4/udp.c | 117 |
24 files changed, 4030 insertions, 748 deletions
diff --git a/net/ipv4/Config.in b/net/ipv4/Config.in index ea50576ab..2f057ab4a 100644 --- a/net/ipv4/Config.in +++ b/net/ipv4/Config.in @@ -36,6 +36,12 @@ bool 'IP: accounting' CONFIG_IP_ACCT bool 'IP: masquerading' CONFIG_IP_MASQUERADE if [ "$CONFIG_IP_MASQUERADE" != "n" ]; then comment 'Protocol-specific masquerading support will be built as modules.' + bool 'IP: ICMP masquerading' CONFIG_IP_MASQUERADE_ICMP + comment 'Protocol-specific masquerading support will be built as modules.' + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'IP: ipautofw masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_IPAUTOFW + tristate 'IP: ipportfw masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_IPPORTFW + fi fi bool 'IP: optimize as router not host' CONFIG_IP_ROUTER tristate 'IP: tunneling' CONFIG_NET_IPIP diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 759def7ea..10f4c5e7c 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -56,8 +56,26 @@ else endif ifeq ($(CONFIG_IP_MASQUERADE),y) -IPV4X_OBJS += ip_masq.o ip_masq_app.o +IPV4X_OBJS += ip_masq.o ip_masq_mod.o ip_masq_app.o + +ifeq ($(CONFIG_IP_MASQUERADE_IPAUTOFW),y) +IPV4_OBJS += ip_masq_autofw.o +else + ifeq ($(CONFIG_IP_MASQUERADE_IPAUTOFW),m) + M_OBJS += ip_masq_autofw.o + endif +endif + +ifeq ($(CONFIG_IP_MASQUERADE_IPPORTFW),y) +IPV4_OBJS += ip_masq_portfw.o +else + ifeq ($(CONFIG_IP_MASQUERADE_IPPORTFW),m) + M_OBJS += ip_masq_portfw.o + endif +endif + M_OBJS += ip_masq_ftp.o ip_masq_irc.o ip_masq_raudio.o ip_masq_quake.o +M_OBJS += ip_masq_vdolive.o ip_masq_cuseeme.o endif ifeq ($(CONFIG_SYN_COOKIES),y) diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 26cc21977..58bb4174a 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -1,6 +1,6 @@ /* linux/net/inet/arp.c * - * Version: $Id: arp.c,v 1.56 1997/11/24 12:51:47 freitag Exp $ + * Version: $Id: arp.c,v 1.3 1997/12/16 05:37:34 ralf Exp $ * * Copyright (C) 1994 by Florian La Roche * @@ -1532,7 +1532,8 @@ int arp_req_set(struct arpreq *r, struct device * dev) int err; if ((r->arp_flags & ATF_PERM) && !(r->arp_flags & ATF_COM)) - return -EINVAL; + r->arp_flags |= ATF_COM; + err = ip_route_output(&rt, ip, 0, 1, dev ? dev->ifindex : 0); if (err) return err; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 16d72fcd2..8775c43bf 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -259,7 +259,7 @@ int ip_rt_ioctl(unsigned int cmd, void *arg) if (copy_from_user(&r, arg, sizeof(struct rtentry))) return -EFAULT; rtnl_lock(); - err = fib_convert_rtentry(cmd, &req.nlh, &req.rtm, &rta, arg); + err = fib_convert_rtentry(cmd, &req.nlh, &req.rtm, &rta, &r); if (err == 0) { if (cmd == SIOCDELRT) { struct fib_table *tb = fib_get_table(req.rtm.rtm_table); diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index e66efde90..77d96acf9 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -3,7 +3,7 @@ * * Alan Cox, <alan@cymru.net> * - * Version: $Id: icmp.c,v 1.35 1997/10/19 18:17:13 freitag Exp $ + * Version: $Id: icmp.c,v 1.3 1997/12/16 05:37:35 ralf Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -44,7 +44,7 @@ * and moved all kfree_skb() up to * icmp_rcv. * Andi Kleen : Move the rate limit bookkeeping - * into the dest entry and use a tocken + * into the dest entry and use a token * bucket filter (thanks to ANK). Make * the rates sysctl configurable. * @@ -549,7 +549,8 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, unsigned long info) /* XXX: use a more aggressive expire for routes created by * this call (not longer than the rate limit timeout). * It could be also worthwhile to not put them into ipv4 - * fast routing cache at first. + * fast routing cache at first. Otherwise an attacker can + * grow the routing table. */ if (ip_route_output(&rt, iph->saddr, saddr, RT_TOS(tos), 0)) return; @@ -1021,8 +1022,11 @@ static unsigned long dummy; /* * Configurable rate limits. - * Send at most one packets per time. * Someone should check if these default values are correct. + * Note that these values interact with the routing cache GC timeout. + * If you chose them too high they won't take effect, because the + * dst_entry gets expired too early. The same should happen when + * the cache grows too big. */ int sysctl_icmp_sourcequench_time = 1*HZ; int sysctl_icmp_destunreach_time = 1*HZ; diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 8f48894a4..7010e3a30 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -5,7 +5,7 @@ * * The IP forwarding functionality. * - * Version: $Id: ip_forward.c,v 1.32 1997/10/24 17:16:06 kuznet Exp $ + * Version: $Id: ip_forward.c,v 1.2 1997/12/16 05:37:36 ralf Exp $ * * Authors: see ip.c * @@ -175,7 +175,16 @@ int ip_forward(struct sk_buff *skb) * and skip the firewall checks */ if (iph->protocol == IPPROTO_ICMP) { - if ((fw_res = ip_fw_masq_icmp(&skb)) < 0) { + __u32 maddr; +#ifdef CONFIG_IP_MASQUERADE_ICMP +#define icmph ((struct icmphdr *)((char *)iph + (iph->ihl<<2))) + if ((icmph->type==ICMP_DEST_UNREACH)|| + (icmph->type==ICMP_SOURCE_QUENCH)|| + (icmph->type==ICMP_TIME_EXCEEDED)) + { +#endif + maddr = inet_select_addr(dev2, rt->rt_gateway, RT_SCOPE_UNIVERSE); + if (fw_res = ip_fw_masq_icmp(&skb, maddr) < 0) { kfree_skb(skb, FREE_READ); return -1; } @@ -183,6 +192,9 @@ int ip_forward(struct sk_buff *skb) if (fw_res) /* ICMP matched - skip firewall */ goto skip_call_fw_firewall; +#ifdef CONFIG_IP_MASQUERADE_ICMP + } +#endif } if (rt->rt_flags&RTCF_MASQ) goto skip_call_fw_firewall; @@ -225,6 +237,12 @@ skip_call_fw_firewall: if (ip_fw_masquerade(&skb, maddr) < 0) { kfree_skb(skb, FREE_READ); return -1; + } else { + /* + * Masquerader may have changed skb + */ + iph = skb->nh.iph; + opt = &(IPCB(skb)->opt); } } #endif diff --git a/net/ipv4/ip_masq.c b/net/ipv4/ip_masq.c index 8c300e155..dc367a289 100644 --- a/net/ipv4/ip_masq.c +++ b/net/ipv4/ip_masq.c @@ -4,6 +4,9 @@ * * Copyright (c) 1994 Pauline Middelink * + * Version: @(#)ip_masq.c 0.12 97/11/30 + * + * * See ip_fw.c for original log * * Fixes: @@ -12,10 +15,24 @@ * 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 + * * */ @@ -38,21 +55,252 @@ #include <net/udp.h> #include <net/checksum.h> #include <net/ip_masq.h> +#include <net/ip_masq_mod.h> +#include <linux/sysctl.h> +#include <linux/ip_fw.h> + +#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW +#include <net/ip_autofw.h> +#endif +#ifdef CONFIG_IP_MASQUERADE_IPPORTFW +#include <net/ip_portfw.h> +#endif + + +int sysctl_ip_masq_debug = 0; + +/* + * Exported wrapper + */ +int ip_masq_get_debug_level(void) +{ + return sysctl_ip_masq_debug; +} + +/* + * 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 */ + }, +}; + +#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_LISTEN + +struct masq_tcp_states_t { + int next_state[IP_MASQ_S_LAST]; /* should be _LAST_TCP */ +}; + +static const char * 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), + masq_state_name(ms->state), + 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) +{ + struct tcphdr *th = 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, th); + } + return -1; +} + +/* + * Moves tunnel to listen state + */ +int ip_masq_listen(struct ip_masq *ms) +{ + masq_set_state_timeout(ms, IP_MASQ_S_LISTEN); + return ms->timeout; +} #define IP_MASQ_TAB_SIZE 256 /* must be power of 2 */ +/* + * Dynamic address rewriting + */ +extern int sysctl_ip_dynaddr; + /* - * Implement IP packet masquerading + * Lookup lock */ +static struct wait_queue *masq_wait; +atomic_t __ip_masq_lock = ATOMIC_INIT(0); -static const char *strProt[] = {"UDP","TCP"}; -static __inline__ const char * masq_proto_name(unsigned proto) +/* + * Implement IP packet masquerading + */ + +/* + * Converts an ICMP reply code into the equivalent request code + */ +static __inline__ const __u8 icmp_type_request(__u8 type) { - return strProt[proto==IPPROTO_TCP]; + 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. */ @@ -68,18 +316,38 @@ static __u16 masq_port = PORT_MASQ_BEGIN; * 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 ... */ -int ip_masq_free_ports[2] = { - PORT_MASQ_END - PORT_MASQ_BEGIN, /* UDP */ - PORT_MASQ_END - PORT_MASQ_BEGIN /* TCP */ +#ifdef CONFIG_IP_MASQ_NREUSE +#define PORT_MASQ_MUL 1 +#else +#define PORT_MASQ_MUL 10 +#endif + +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 */ }; +EXPORT_SYMBOL(ip_masq_get_debug_level); EXPORT_SYMBOL(ip_masq_new); +EXPORT_SYMBOL(ip_masq_listen); +/* EXPORT_SYMBOL(ip_masq_set_expire); +*/ EXPORT_SYMBOL(ip_masq_free_ports); EXPORT_SYMBOL(ip_masq_expire); -EXPORT_SYMBOL(ip_masq_out_get_2); +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_lock); /* * 2 ip_masq hash tables: for input and output pkts lookups. @@ -100,12 +368,29 @@ static struct ip_fw_masq ip_masq_dummy = { struct ip_fw_masq *ip_masq_expire = &ip_masq_dummy; + /* - * Returns hash value + * Set masq expiration (deletion) and adds timer, + * if timeout==0 cancel expiration. + * Warning: it does not check/delete previous timer! */ -static __inline__ unsigned +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); @@ -117,13 +402,13 @@ ip_masq_hash_key(unsigned proto, __u32 addr, __u16 port) * returns bool success. */ -static __inline__ int -ip_masq_hash(struct ip_masq *ms) +static int ip_masq_hash(struct ip_masq *ms) { unsigned hash; if (ms->flags & IP_MASQ_F_HASHED) { - printk(KERN_INFO "ip_masq_hash(): request for already hashed\n"); + IP_MASQ_ERR( "ip_masq_hash(): request for already hashed, called from %p\n", + __builtin_return_address(0)); return 0; } /* @@ -131,6 +416,7 @@ ip_masq_hash(struct ip_masq *ms) */ hash = ip_masq_hash_key(ms->protocol, ms->maddr, ms->mport); ms->m_link = ip_masq_m_tab[hash]; + atomic_inc(&ms->refcnt); ip_masq_m_tab[hash] = ms; /* @@ -138,6 +424,7 @@ ip_masq_hash(struct ip_masq *ms) */ hash = ip_masq_hash_key(ms->protocol, ms->saddr, ms->sport); ms->s_link = ip_masq_s_tab[hash]; + atomic_inc(&ms->refcnt); ip_masq_s_tab[hash] = ms; @@ -151,12 +438,13 @@ ip_masq_hash(struct ip_masq *ms) * returns bool success. */ -static __inline__ int ip_masq_unhash(struct ip_masq *ms) +static int ip_masq_unhash(struct ip_masq *ms) { unsigned hash; struct ip_masq ** ms_p; if (!(ms->flags & IP_MASQ_F_HASHED)) { - printk(KERN_INFO "ip_masq_unhash(): request for unhash flagged\n"); + IP_MASQ_ERR( "ip_masq_unhash(): request for unhash flagged, called from %p\n", + __builtin_return_address(0)); return 0; } /* @@ -165,16 +453,19 @@ static __inline__ int ip_masq_unhash(struct ip_masq *ms) hash = ip_masq_hash_key(ms->protocol, ms->maddr, ms->mport); for (ms_p = &ip_masq_m_tab[hash]; *ms_p ; ms_p = &(*ms_p)->m_link) if (ms == (*ms_p)) { - *ms_p = ms->m_link; + atomic_dec(&ms->refcnt); + *ms_p = ms->m_link; break; } + /* * UNhash by s{addr,port} */ hash = ip_masq_hash_key(ms->protocol, ms->saddr, ms->sport); for (ms_p = &ip_masq_s_tab[hash]; *ms_p ; ms_p = &(*ms_p)->s_link) if (ms == (*ms_p)) { - *ms_p = ms->s_link; + atomic_dec(&ms->refcnt); + *ms_p = ms->s_link; break; } @@ -183,40 +474,14 @@ static __inline__ int ip_masq_unhash(struct ip_masq *ms) } /* - * Returns ip_masq associated with addresses found in iph. - * called for pkts coming from outside-to-INside the 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 - */ - -struct ip_masq * -ip_masq_in_get(struct iphdr *iph) -{ - __u16 *portptr; - int protocol; - __u32 s_addr, d_addr; - __u16 s_port, d_port; - - portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]); - protocol = iph->protocol; - s_addr = iph->saddr; - s_port = portptr[0]; - d_addr = iph->daddr; - d_port = portptr[1]; - - return ip_masq_in_get_2(protocol, s_addr, s_port, d_addr, d_port); -} - -/* * 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. + * 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 @@ -225,44 +490,39 @@ ip_masq_in_get(struct iphdr *iph) * [Only for UDP] - AC */ -struct ip_masq * -ip_masq_in_get_2(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port) +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; + struct ip_masq *ms = NULL; + + ip_masq_lock(&__ip_masq_lock, 0); hash = ip_masq_hash_key(protocol, d_addr, d_port); for(ms = ip_masq_m_tab[hash]; ms ; ms = ms->m_link) { - if ( protocol==ms->protocol && - (s_addr==ms->daddr || ms->flags & IP_MASQ_F_NO_DADDR) && - (s_port==ms->dport || ms->flags & IP_MASQ_F_NO_DPORT) && - (d_addr==ms->maddr && d_port==ms->mport)) - return ms; + if (protocol==ms->protocol && + ((s_addr==ms->daddr || ms->flags & IP_MASQ_F_NO_DADDR)) && + (s_port==ms->dport || ms->flags & IP_MASQ_F_NO_DPORT) && + (d_addr==ms->maddr && d_port==ms->mport)) { + 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; + } } - return NULL; -} - -/* - * Returns ip_masq associated with addresses found in iph. - * called for pkts coming from inside-to-OUTside the firewall. - */ - -struct ip_masq * -ip_masq_out_get(struct iphdr *iph) -{ - __u16 *portptr; - int protocol; - __u32 s_addr, d_addr; - __u16 s_port, d_port; - - portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]); - protocol = iph->protocol; - s_addr = iph->saddr; - s_port = portptr[0]; - d_addr = iph->daddr; - d_port = portptr[1]; - - return ip_masq_out_get_2(protocol, s_addr, s_port, d_addr, d_port); + IP_MASQ_DEBUG(2, "look/in %d %08X:%04hX->%08X:%04hX fail\n", + protocol, + s_addr, + s_port, + d_addr, + d_port); + +out: + ip_masq_unlock(&__ip_masq_lock, 0); + return ms; } /* @@ -271,66 +531,209 @@ ip_masq_out_get(struct iphdr *iph) * 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". */ -struct ip_masq * -ip_masq_out_get_2(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port) +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; + struct ip_masq *ms = NULL; + /* + * Check for "full" addressed entries + */ hash = ip_masq_hash_key(protocol, s_addr, s_port); + + ip_masq_lock(&__ip_masq_lock, 0); + for(ms = ip_masq_s_tab[hash]; ms ; ms = ms->s_link) { if (protocol == ms->protocol && s_addr == ms->saddr && s_port == ms->sport && - d_addr == ms->daddr && d_port == ms->dport ) - return ms; + d_addr == ms->daddr && d_port == ms->dport ) { + 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; + } + } - return NULL; + /* + * Check for NO_SPORT entries + */ + hash = ip_masq_hash_key(protocol, s_addr, 0); + for(ms = ip_masq_s_tab[hash]; ms ; ms = ms->s_link) { + if (ms->flags & IP_MASQ_F_NO_SPORT && + protocol == ms->protocol && + s_addr == ms->saddr && + d_addr == ms->daddr && d_port == ms->dport ) { + 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); + +out: + ip_masq_unlock(&__ip_masq_lock, 0); + return ms; } +#ifdef CONFIG_IP_MASQUERADE_NREUSE /* * Returns ip_masq for given proto,m_addr,m_port. * called by allocation routine to find an unused m_port. */ -struct ip_masq * -ip_masq_getbym(int protocol, __u32 m_addr, __u16 m_port) +static struct ip_masq * __ip_masq_getbym(int protocol, __u32 m_addr, __u16 m_port) { unsigned hash; - struct ip_masq *ms; + struct ip_masq *ms = NULL; hash = ip_masq_hash_key(protocol, m_addr, m_port); + + ip_masq_lock(&__ip_masq_lock, 0); + for(ms = ip_masq_m_tab[hash]; ms ; ms = ms->m_link) { if ( protocol==ms->protocol && - (m_addr==ms->maddr && m_port==ms->mport)) - return ms; + (m_addr==ms->maddr && m_port==ms->mport)) { + atomic_inc(&ms->refcnt); + goto out; + } } - return NULL; + +out: + ip_masq_unlock(&__ip_masq_lock, 0); + return ms; +} +#endif + +struct ip_masq * ip_masq_out_get(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port) +{ + struct ip_masq *ms; + ms = __ip_masq_out_get(protocol, s_addr, s_port, d_addr, d_port); + 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; + ms = __ip_masq_in_get(protocol, s_addr, s_port, d_addr, d_port); + 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==2 (2 hashes) + */ + if (atomic_read(&ms->refcnt)==2) { + __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)); + } } static void masq_expire(unsigned long data) { struct ip_masq *ms = (struct ip_masq *)data; - unsigned long flags; + ms->timeout = MASQUERADE_EXPIRE_RETRY; + + /* + * hey, I'm using it + */ + atomic_inc(&ms->refcnt); -#ifdef DEBUG_CONFIG_IP_MASQUERADE - printk("Masqueraded %s %lX:%X expired\n", + IP_MASQ_DEBUG(1, "Masqueraded %s %08lX:%04X expired\n", masq_proto_name(ms->protocol), ntohl(ms->saddr),ntohs(ms->sport)); -#endif - save_flags(flags); - cli(); + ip_masq_lock(&__ip_masq_lock, 1); + + /* + * Already locked, do bounce ... + */ + if (ip_masq_nlocks(&__ip_masq_lock) != 1) { + goto masq_expire_later; + } + + /* + * 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)) { - ip_masq_free_ports[ms->protocol==IPPROTO_TCP]++; - ip_masq_unbind_app(ms); - kfree_s(ms,sizeof(*ms)); + if (!(ms->flags&IP_MASQ_F_MPORT)) + atomic_inc(ip_masq_free_ports + masq_proto_num(ms->protocol)); + ip_masq_unbind_app(ms); } - restore_flags(flags); + /* + * refcnt==1 implies I'm the only one referrer + */ + if (atomic_read(&ms->refcnt) == 1) { + kfree_s(ms,sizeof(*ms)); + goto masq_expire_out; + } + +masq_expire_later: + IP_MASQ_DEBUG(0, "masq_expire delayed: %s %08lX:%04X->%08lX:%04X nlocks-1=%d 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), + ip_masq_nlocks(&__ip_masq_lock)-1, + atomic_read(&ms->refcnt)-1, + atomic_read(&ms->n_control)); + + ip_masq_put(ms); + +masq_expire_out: + ip_masq_unlock(&__ip_masq_lock, 1); } /* @@ -339,25 +742,28 @@ static void masq_expire(unsigned long data) * given boundaries MASQ_BEGIN and MASQ_END. */ -struct ip_masq * ip_masq_new(__u32 maddr, int proto, __u32 saddr, __u16 sport, __u32 daddr, __u16 dport, unsigned mflags) +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, *free_ports_p; - unsigned long flags; + int ports_tried; + atomic_t *free_ports_p = NULL; static int n_fails = 0; - free_ports_p = &ip_masq_free_ports[proto==IPPROTO_TCP]; - if (*free_ports_p == 0) { - if (++n_fails < 5) - printk(KERN_ERR "ip_masq_new(proto=%s): no free ports.\n", - masq_proto_name(proto)); - return NULL; - } + 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; + } + } ms = (struct ip_masq *) kmalloc(sizeof(struct ip_masq), GFP_ATOMIC); if (ms == NULL) { if (++n_fails < 5) - printk(KERN_ERR "ip_masq_new(proto=%s): no memory available.\n", + IP_MASQ_ERR("ip_masq_new(proto=%s): no memory available.\n", masq_proto_name(proto)); return NULL; } @@ -372,73 +778,115 @@ struct ip_masq * ip_masq_new(__u32 maddr, int proto, __u32 saddr, __u16 sport, _ 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) + if (proto == IPPROTO_UDP && !mport) ms->flags |= IP_MASQ_F_NO_DADDR; + /* get masq address from rif */ ms->maddr = maddr; - for (ports_tried = 0; ports_tried < *free_ports_p; ports_tried++){ - save_flags(flags); - cli(); + /* + * 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; - /* - * Try the next available port number - */ + /* + * Check 5-upla uniqueness + */ + ip_masq_lock(&__ip_masq_lock, 1); - ms->mport = htons(masq_port++); - if (masq_port==PORT_MASQ_END) masq_port = PORT_MASQ_BEGIN; + mst = __ip_masq_in_get(proto, daddr, dport, maddr, mport); + if (mst==NULL) { + ms->flags |= IP_MASQ_F_MPORT; - restore_flags(flags); + ip_masq_hash(ms); + ip_masq_unlock(&__ip_masq_lock, 1); - /* - * lookup to find out if this port is used. - */ + ip_masq_bind_app(ms); + atomic_inc(&ms->refcnt); + masq_set_state_timeout(ms, IP_MASQ_S_NONE); + return ms; + } - mst = ip_masq_getbym(proto, ms->maddr, ms->mport); - if (mst == NULL) { - save_flags(flags); - cli(); + ip_masq_unlock(&__ip_masq_lock, 1); + __ip_masq_put(mst); - if (*free_ports_p == 0) { - restore_flags(flags); - break; - } - (*free_ports_p)--; - ip_masq_hash(ms); + 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)); - restore_flags(flags); - ip_masq_bind_app(ms); - n_fails = 0; - return ms; - } + goto mport_nono; + } + + + for (ports_tried = 0; + (atomic_read(free_ports_p) && (ports_tried <= (PORT_MASQ_END - PORT_MASQ_BEGIN))); + ports_tried++){ + + cli(); + /* + * Try the next available port number + */ + mport = ms->mport = htons(masq_port++); + if (masq_port==PORT_MASQ_END) masq_port = PORT_MASQ_BEGIN; + + sti(); + + /* + * lookup to find out if this connection is used. + */ + + ip_masq_lock(&__ip_masq_lock, 1); + +#ifdef CONFIG_IP_MASQUERADE_NREUSE + mst = __ip_masq_getbym(proto, maddr, mport); +#else + mst = __ip_masq_in_get(proto, daddr, dport, maddr, mport); +#endif + if (mst == NULL) { + + if (atomic_read(free_ports_p) == 0) { + ip_masq_unlock(&__ip_masq_lock, 1); + break; + } + atomic_dec(free_ports_p); + ip_masq_hash(ms); + ip_masq_unlock(&__ip_masq_lock, 1); + + ip_masq_bind_app(ms); + n_fails = 0; + atomic_inc(&ms->refcnt); + masq_set_state_timeout(ms, IP_MASQ_S_NONE); + return ms; + } + ip_masq_unlock(&__ip_masq_lock, 1); + __ip_masq_put(mst); } if (++n_fails < 5) - printk(KERN_ERR "ip_masq_new(proto=%s): could not get free masq entry (free=%d).\n", - masq_proto_name(ms->protocol), *free_ports_p); + IP_MASQ_ERR( "ip_masq_new(proto=%s): could not get free masq entry (free=%d).\n", + masq_proto_name(ms->protocol), + atomic_read(free_ports_p)); +mport_nono: kfree_s(ms, sizeof(*ms)); return NULL; } -/* - * Set masq expiration (deletion) and adds timer, - * if timeout==0 cancel expiration. - * Warning: it does not check/delete previous timer! - */ - -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); - } -} - static void recalc_check(struct udphdr *uh, __u32 saddr, __u32 daddr, int len) { @@ -456,7 +904,6 @@ int ip_fw_masquerade(struct sk_buff **skb_ptr, __u32 maddr) __u16 *portptr; struct ip_masq *ms; int size; - unsigned long timeout; /* * We can only masquerade protocols with ports... @@ -464,6 +911,9 @@ int ip_fw_masquerade(struct sk_buff **skb_ptr, __u32 maddr) * We may need to consider masq-ing some ICMP related to masq-ed protocols */ + if (iph->protocol==IPPROTO_ICMP) + return (ip_fw_masq_icmp(skb_ptr, maddr)); + if (iph->protocol!=IPPROTO_UDP && iph->protocol!=IPPROTO_TCP) return -1; @@ -472,31 +922,72 @@ int ip_fw_masquerade(struct sk_buff **skb_ptr, __u32 maddr) */ portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]); -#ifdef DEBUG_CONFIG_IP_MASQUERADE - printk("Outgoing %s %lX:%X -> %lX:%X\n", - masq_proto_name(iph->protocol), - ntohl(iph->saddr), ntohs(portptr[0]), - ntohl(iph->daddr), ntohs(portptr[1])); -#endif + IP_MASQ_DEBUG(2, "Outgoing %s %08lX:%04X -> %08lX:%04X\n", + masq_proto_name(iph->protocol), + ntohl(iph->saddr), ntohs(portptr[0]), + ntohl(iph->daddr), ntohs(portptr[1])); - ms = ip_masq_out_get(iph); - if (ms!=NULL) - ip_masq_set_expire(ms,0); + ms = ip_masq_out_get_iph(iph); + if (ms!=NULL) { - /* - * Nope, not found, create a new entry for it - */ + /* + * If sysctl !=0 and no pkt has been received yet + * in this tunnel and routing iface address has changed... + * "You are welcome, diald". + */ + if ( sysctl_ip_dynaddr && ms->flags & IP_MASQ_F_NO_REPLY && maddr != ms->maddr) { - if (ms==NULL) - { - ms = ip_masq_new(maddr, iph->protocol, - iph->saddr, portptr[0], - iph->daddr, portptr[1], - 0); + if (sysctl_ip_dynaddr > 1) { + IP_MASQ_INFO( "ip_fw_masquerade(): change masq.addr from %d.%d.%d.%d to %d.%d.%d.%d\n", + NIPQUAD(ms->maddr),NIPQUAD(maddr)); + } + + ip_masq_lock(&__ip_masq_lock, 1); + + ip_masq_unhash(ms); + ms->maddr = maddr; + ip_masq_hash(ms); + + ip_masq_unlock(&__ip_masq_lock, 1); + } + + /* + * Set sport if not defined yet (e.g. ftp PASV). Because + * masq entries are hashed on sport, unhash with old value + * and hash with new. + */ + + if ( ms->flags & IP_MASQ_F_NO_SPORT && ms->protocol == IPPROTO_TCP ) { + ms->flags &= ~IP_MASQ_F_NO_SPORT; + + ip_masq_lock(&__ip_masq_lock, 1); + + ip_masq_unhash(ms); + ms->sport = portptr[0]; + ip_masq_hash(ms); /* hash on new sport */ + + ip_masq_unlock(&__ip_masq_lock, 1); + + IP_MASQ_DEBUG(1, "ip_fw_masquerade(): filled sport=%d\n", + ntohs(ms->sport)); + } + } else { + /* + * Nope, not found, create a new entry for it + */ + + if (!(ms = ip_masq_mod_out_create(iph, portptr, maddr))) + ms = ip_masq_new(iph->protocol, + maddr, 0, + iph->saddr, portptr[0], + iph->daddr, portptr[1], + 0); if (ms == NULL) return -1; } + ip_masq_mod_out_update(iph, portptr, ms); + /* * Change the fragments origin */ @@ -527,40 +1018,12 @@ int ip_fw_masquerade(struct sk_buff **skb_ptr, __u32 maddr) * Adjust packet accordingly to protocol */ - if (iph->protocol==IPPROTO_UDP) + if (iph->protocol == IPPROTO_UDP) { - timeout = ip_masq_expire->udp_timeout; recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,size); - } - else - { - struct tcphdr *th; - th = (struct tcphdr *)portptr; - - /* Set the flags up correctly... */ - if (th->fin) - { - ms->flags |= IP_MASQ_F_SAW_FIN_OUT; - } - - if (th->rst) - { - ms->flags |= IP_MASQ_F_SAW_RST; - } + } else { + struct tcphdr *th = (struct tcphdr *)portptr; - /* - * Timeout depends if FIN packet has been seen - * Very short timeout if RST packet seen. - */ - if (ms->flags & IP_MASQ_F_SAW_RST) - { - timeout = 1; - } - else if ((ms->flags & IP_MASQ_F_SAW_FIN) == IP_MASQ_F_SAW_FIN) - { - timeout = ip_masq_expire->tcp_fin_timeout; - } - else timeout = ip_masq_expire->tcp_timeout; skb->csum = csum_partial((void *)(th + 1), size - sizeof(*th), 0); th->check = 0; @@ -568,12 +1031,14 @@ int ip_fw_masquerade(struct sk_buff **skb_ptr, __u32 maddr) csum_partial((char *)th, sizeof(*th), skb->csum)); } - ip_masq_set_expire(ms, timeout); - ip_send_check(iph); - #ifdef DEBUG_CONFIG_IP_MASQUERADE - printk("O-routed from %lX:%X via %lX\n",ntohl(ms->maddr),ntohs(ms->mport),ntohl(maddr)); - #endif + ip_send_check(iph); + + IP_MASQ_DEBUG(2, "O-routed from %08lX:%04X with masq.addr %08lX\n", + ntohl(ms->maddr),ntohs(ms->mport),ntohl(maddr)); + + masq_set_state(ms, 1, iph, portptr); + ip_masq_put(ms); return 0; } @@ -586,7 +1051,7 @@ int ip_fw_masquerade(struct sk_buff **skb_ptr, __u32 maddr) * Currently handles error types - unreachable, quench, ttl exceeded */ -int ip_fw_masq_icmp(struct sk_buff **skb_p) +int ip_fw_masq_icmp(struct sk_buff **skb_p, __u32 maddr) { struct sk_buff *skb = *skb_p; struct iphdr *iph = skb->nh.iph; @@ -596,10 +1061,78 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p) struct ip_masq *ms; unsigned short len = ntohs(iph->tot_len) - (iph->ihl * 4); -#ifdef DEBUG_CONFIG_IP_MASQUERADE - printk("Incoming forward ICMP (%d) %lX -> %lX\n", - icmph->type, + IP_MASQ_DEBUG(2, "Incoming forward ICMP (%d,%d) %lX -> %lX\n", + icmph->type, ntohs(icmp_id(icmph)), ntohl(iph->saddr), ntohl(iph->daddr)); + +#ifdef CONFIG_IP_MASQUERADE_ICMP + if ((icmph->type == ICMP_ECHO ) || + (icmph->type == ICMP_TIMESTAMP ) || + (icmph->type == ICMP_INFO_REQUEST ) || + (icmph->type == ICMP_ADDRESS )) { + + IP_MASQ_DEBUG(2, "icmp request rcv %lX->%lX id %d type %d\n", + ntohl(iph->saddr), + ntohl(iph->daddr), + ntohs(icmp_id(icmph)), + icmph->type); + + ms = ip_masq_out_get(iph->protocol, + iph->saddr, + icmp_id(icmph), + iph->daddr, + icmp_hv_req(icmph)); + if (ms == NULL) { + ms = ip_masq_new(iph->protocol, + maddr, 0, + iph->saddr, icmp_id(icmph), + iph->daddr, icmp_hv_req(icmph), + 0); + if (ms == NULL) + return (-1); + IP_MASQ_DEBUG(1, "Created new icmp entry\n"); + } + /* Rewrite source address */ + + /* + * If sysctl !=0 and no pkt has been received yet + * in this tunnel and routing iface address has changed... + * "You are welcome, diald". + */ + if ( sysctl_ip_dynaddr && ms->flags & IP_MASQ_F_NO_REPLY && maddr != ms->maddr) { + + if (sysctl_ip_dynaddr > 1) { + IP_MASQ_INFO( "ip_fw_masq_icmp(): change masq.addr %d.%d.%d.%d to %d.%d.%d.%d", + NIPQUAD(ms->maddr), NIPQUAD(maddr)); + } + + ip_masq_lock(&__ip_masq_lock, 1); + + ip_masq_unhash(ms); + ms->maddr = maddr; + ip_masq_hash(ms); + + ip_masq_unlock(&__ip_masq_lock, 1); + } + + iph->saddr = ms->maddr; + ip_send_check(iph); + /* Rewrite port (id) */ + (icmph->un).echo.id = ms->mport; + icmph->checksum = 0; + icmph->checksum = ip_compute_csum((unsigned char *)icmph, len); + + IP_MASQ_DEBUG(2, "icmp request rwt %lX->%lX id %d type %d\n", + ntohl(iph->saddr), + ntohl(iph->daddr), + ntohs(icmp_id(icmph)), + icmph->type); + + masq_set_state(ms, 1, iph, icmph); + ip_masq_put(ms); + + return 1; + } #endif /* @@ -618,6 +1151,59 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p) /* Now find the contained IP header */ ciph = (struct iphdr *) (icmph + 1); +#ifdef CONFIG_IP_MASQUERADE_ICMP + if (ciph->protocol == IPPROTO_ICMP) { + /* + * This section handles ICMP errors for ICMP packets + */ + struct icmphdr *cicmph = (struct icmphdr *)((char *)ciph + + (ciph->ihl<<2)); + + + IP_MASQ_DEBUG(2, "fw icmp/icmp rcv %lX->%lX id %d type %d\n", + ntohl(ciph->saddr), + ntohl(ciph->daddr), + ntohs(icmp_id(cicmph)), + cicmph->type); + + ms = __ip_masq_out_get(ciph->protocol, + ciph->daddr, + icmp_id(cicmph), + ciph->saddr, + icmp_hv_rep(cicmph)); + + if (ms == NULL) + return 0; + + /* Now we do real damage to this packet...! */ + /* First change the source IP address, and recalc checksum */ + iph->saddr = ms->maddr; + ip_send_check(iph); + + /* Now change the *dest* address in the contained IP */ + ciph->daddr = ms->maddr; + __ip_masq_put(ms); + + ip_send_check(ciph); + + /* Change the ID to the masqed one! */ + (cicmph->un).echo.id = ms->mport; + + /* And finally the ICMP checksum */ + icmph->checksum = 0; + icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); + + + IP_MASQ_DEBUG(2, "fw icmp/icmp rwt %lX->%lX id %d type %d\n", + ntohl(ciph->saddr), + ntohl(ciph->daddr), + ntohs(icmp_id(cicmph)), + cicmph->type); + + return 1; + } +#endif /* CONFIG_IP_MASQUERADE_ICMP */ + /* We are only interested ICMPs generated from TCP or UDP packets */ if ((ciph->protocol != IPPROTO_UDP) && (ciph->protocol != IPPROTO_TCP)) return 0; @@ -628,27 +1214,36 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p) * (but reversed relative to outer IP header!) */ pptr = (__u16 *)&(((char *)ciph)[ciph->ihl*4]); +#if 0 if (ntohs(pptr[1]) < PORT_MASQ_BEGIN || ntohs(pptr[1]) > PORT_MASQ_END) return 0; +#endif /* Ensure the checksum is correct */ if (ip_compute_csum((unsigned char *) icmph, len)) { /* Failed checksum! */ - printk(KERN_INFO "MASQ: forward ICMP: failed checksum from %s!\n", - in_ntoa(iph->saddr)); + IP_MASQ_WARNING( "forward ICMP: failed checksum from %d.%d.%d.%d!\n", + NIPQUAD(iph->saddr)); return(-1); } -#ifdef DEBUG_CONFIG_IP_MASQUERADE - printk("Handling forward ICMP for %lX:%X -> %lX:%X\n", + + IP_MASQ_DEBUG(2, "Handling forward ICMP for %08lX:%04X -> %08lX:%04X\n", ntohl(ciph->saddr), ntohs(pptr[0]), ntohl(ciph->daddr), ntohs(pptr[1])); -#endif - /* This is pretty much what ip_masq_in_get() does */ - ms = ip_masq_in_get_2(ciph->protocol, ciph->saddr, pptr[0], ciph->daddr, pptr[1]); + +#if 0 + /* This is pretty much what __ip_masq_in_get_iph() does */ + ms = __ip_masq_in_get(ciph->protocol, ciph->saddr, pptr[0], ciph->daddr, pptr[1]); +#endif + ms = __ip_masq_out_get(ciph->protocol, + ciph->daddr, + pptr[1], + ciph->saddr, + pptr[0]); if (ms == NULL) return 0; @@ -664,16 +1259,17 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p) /* the TCP/UDP dest port - cannot redo check */ pptr[1] = ms->mport; + __ip_masq_put(ms); /* And finally the ICMP checksum */ icmph->checksum = 0; icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); -#ifdef DEBUG_CONFIG_IP_MASQUERADE - printk("Rewrote forward ICMP to %lX:%X -> %lX:%X\n", + + IP_MASQ_DEBUG(2, "Rewrote forward ICMP to %08lX:%04X -> %08lX:%04X\n", ntohl(ciph->saddr), ntohs(pptr[0]), ntohl(ciph->daddr), ntohs(pptr[1])); -#endif + return 1; } @@ -695,22 +1291,133 @@ int ip_fw_demasq_icmp(struct sk_buff **skb_p) struct ip_masq *ms; unsigned short len = ntohs(iph->tot_len) - (iph->ihl * 4); -#ifdef DEBUG_CONFIG_IP_MASQUERADE - printk("Incoming reverse ICMP (%d) %lX -> %lX\n", - icmph->type, + + IP_MASQ_DEBUG(2, "icmp in/rev (%d,%d) %lX -> %lX\n", + icmph->type, ntohs(icmp_id(icmph)), ntohl(iph->saddr), ntohl(iph->daddr)); -#endif - if ((icmph->type != ICMP_DEST_UNREACH) && - (icmph->type != ICMP_SOURCE_QUENCH) && - (icmph->type != ICMP_TIME_EXCEEDED)) - return 0; - /* Now find the contained IP header */ +#ifdef CONFIG_IP_MASQUERADE_ICMP + if ((icmph->type == ICMP_ECHOREPLY) || + (icmph->type == ICMP_TIMESTAMPREPLY) || + (icmph->type == ICMP_INFO_REPLY) || + (icmph->type == ICMP_ADDRESSREPLY)) { + + IP_MASQ_DEBUG(2, "icmp reply rcv %lX->%lX id %d type %d, req %d\n", + ntohl(iph->saddr), + ntohl(iph->daddr), + ntohs(icmp_id(icmph)), + icmph->type, + icmp_type_request(icmph->type)); + + ms = ip_masq_in_get(iph->protocol, + iph->saddr, + icmp_hv_rep(icmph), + iph->daddr, + icmp_id(icmph)); + if (ms == NULL) + return 0; + + /* + * got reply, so clear flag + */ + ms->flags &= ~IP_MASQ_F_NO_REPLY; + + /* Reset source address */ + iph->daddr = ms->saddr; + /* Redo IP header checksum */ + ip_send_check(iph); + /* Set ID to fake port number */ + (icmph->un).echo.id = ms->sport; + /* Reset ICMP checksum and set expiry */ + icmph->checksum=0; + icmph->checksum=ip_compute_csum((unsigned char *)icmph,len); + + + + IP_MASQ_DEBUG(2, "icmp reply rwt %lX->%lX id %d type %d\n", + ntohl(iph->saddr), + ntohl(iph->daddr), + ntohs(icmp_id(icmph)), + icmph->type); + + masq_set_state(ms, 0, iph, icmph); + ip_masq_put(ms); + + return 1; + } else { +#endif + if ((icmph->type != ICMP_DEST_UNREACH) && + (icmph->type != ICMP_SOURCE_QUENCH) && + (icmph->type != ICMP_TIME_EXCEEDED)) + return 0; +#ifdef CONFIG_IP_MASQUERADE_ICMP + } +#endif + /* + * If we get here we have an ICMP error of one of the above 3 types + * Now find the contained IP header + */ + ciph = (struct iphdr *) (icmph + 1); +#ifdef CONFIG_IP_MASQUERADE_ICMP + if (ciph->protocol == IPPROTO_ICMP) { + /* + * This section handles ICMP errors for ICMP packets + * + * First get a new ICMP header structure out of the IP packet + */ + struct icmphdr *cicmph = (struct icmphdr *)((char *)ciph + + (ciph->ihl<<2)); + + + IP_MASQ_DEBUG(2, "rv icmp/icmp rcv %lX->%lX id %d type %d\n", + ntohl(ciph->saddr), + ntohl(ciph->daddr), + ntohs(icmp_id(cicmph)), + cicmph->type); + + ms = __ip_masq_in_get(ciph->protocol, + ciph->daddr, + icmp_hv_req(cicmph), + ciph->saddr, + icmp_id(cicmph)); + + if (ms == NULL) + return 0; + + /* Now we do real damage to this packet...! */ + /* First change the dest IP address, and recalc checksum */ + iph->daddr = ms->saddr; + ip_send_check(iph); + + /* Now change the *source* address in the contained IP */ + ciph->saddr = ms->saddr; + ip_send_check(ciph); + + /* Change the ID to the original one! */ + (cicmph->un).echo.id = ms->sport; + __ip_masq_put(ms); + + /* And finally the ICMP checksum */ + icmph->checksum = 0; + icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); + + + IP_MASQ_DEBUG(2, "rv icmp/icmp rwt %lX->%lX id %d type %d\n", + ntohl(ciph->saddr), + ntohl(ciph->daddr), + ntohs(icmp_id(cicmph)), + cicmph->type); + + return 1; + } +#endif /* CONFIG_IP_MASQUERADE_ICMP */ + /* We are only interested ICMPs generated from TCP or UDP packets */ - if ((ciph->protocol != IPPROTO_UDP) && (ciph->protocol != IPPROTO_TCP)) + if ((ciph->protocol != IPPROTO_UDP) && + (ciph->protocol != IPPROTO_TCP)) return 0; /* @@ -726,19 +1433,23 @@ int ip_fw_demasq_icmp(struct sk_buff **skb_p) if (ip_compute_csum((unsigned char *) icmph, len)) { /* Failed checksum! */ - printk(KERN_INFO "MASQ: reverse ICMP: failed checksum from %s!\n", - in_ntoa(iph->saddr)); + IP_MASQ_ERR( "reverse ICMP: failed checksum from %d.%d.%d.%d!\n", + NIPQUAD(iph->saddr)); return(-1); } -#ifdef DEBUG_CONFIG_IP_MASQUERADE - printk("Handling reverse ICMP for %lX:%X -> %lX:%X\n", + + IP_MASQ_DEBUG(2, "Handling reverse ICMP for %08lX:%04X -> %08lX:%04X\n", ntohl(ciph->saddr), ntohs(pptr[0]), ntohl(ciph->daddr), ntohs(pptr[1])); -#endif - /* This is pretty much what ip_masq_in_get() does, except params are wrong way round */ - ms = ip_masq_in_get_2(ciph->protocol, ciph->daddr, pptr[1], ciph->saddr, pptr[0]); + + /* This is pretty much what __ip_masq_in_get_iph() does, except params are wrong way round */ + ms = __ip_masq_in_get(ciph->protocol, + ciph->daddr, + pptr[1], + ciph->saddr, + pptr[0]); if (ms == NULL) return 0; @@ -754,16 +1465,17 @@ int ip_fw_demasq_icmp(struct sk_buff **skb_p) /* the TCP/UDP source port - cannot redo check */ pptr[0] = ms->sport; + __ip_masq_put(ms); /* And finally the ICMP checksum */ icmph->checksum = 0; icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); -#ifdef DEBUG_CONFIG_IP_MASQUERADE - printk("Rewrote reverse ICMP to %lX:%X -> %lX:%X\n", + + IP_MASQ_DEBUG(2, "Rewrote reverse ICMP to %08lX:%04X -> %08lX:%04X\n", ntohl(ciph->saddr), ntohs(pptr[0]), ntohl(ciph->daddr), ntohs(pptr[1])); -#endif + return 1; } @@ -785,7 +1497,10 @@ int ip_fw_demasquerade(struct sk_buff **skb_p) __u16 *portptr; struct ip_masq *ms; unsigned short len; - unsigned long timeout; + + __u32 maddr; + + maddr = iph->daddr; switch (iph->protocol) { case IPPROTO_ICMP: @@ -794,9 +1509,11 @@ int ip_fw_demasquerade(struct sk_buff **skb_p) case IPPROTO_UDP: /* Make sure packet is in the masq range */ portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]); - if (ntohs(portptr[1]) < PORT_MASQ_BEGIN || - ntohs(portptr[1]) > PORT_MASQ_END) + if ((ntohs(portptr[1]) < PORT_MASQ_BEGIN + || ntohs(portptr[1]) > PORT_MASQ_END) + && (ip_masq_mod_in_rule(iph, portptr) != 1)) return 0; + /* Check that the checksum is OK */ len = ntohs(iph->tot_len) - (iph->ihl * 4); if ((iph->protocol == IPPROTO_UDP) && (portptr[3] == 0)) @@ -811,8 +1528,8 @@ int ip_fw_demasquerade(struct sk_buff **skb_p) if (csum_tcpudp_magic(iph->saddr, iph->daddr, len, iph->protocol, skb->csum)) { - printk(KERN_INFO "MASQ: failed TCP/UDP checksum from %s!\n", - in_ntoa(iph->saddr)); + IP_MASQ_WARNING( "failed TCP/UDP checksum from %d.%d.%d.%d!\n", + NIPQUAD(iph->saddr)); return -1; } default: @@ -824,42 +1541,50 @@ int ip_fw_demasquerade(struct sk_buff **skb_p) } -#ifdef DEBUG_CONFIG_IP_MASQUERADE - printk("Incoming %s %lX:%X -> %lX:%X\n", + + IP_MASQ_DEBUG(2, "Incoming %s %08lX:%04X -> %08lX:%04X\n", masq_proto_name(iph->protocol), ntohl(iph->saddr), ntohs(portptr[0]), ntohl(iph->daddr), ntohs(portptr[1])); -#endif + /* * reroute to original host:port if found... */ - ms = ip_masq_in_get(iph); + ms = ip_masq_in_get_iph(iph); + + if (!ms) + ms = ip_masq_mod_in_create(iph, portptr, maddr); + + ip_masq_mod_in_update(iph, portptr, ms); if (ms != NULL) { - /* Stop the timer ticking.... */ - ip_masq_set_expire(ms,0); /* + * got reply, so clear flag + */ + ms->flags &= ~IP_MASQ_F_NO_REPLY; + + /* * Set dport if not defined yet. */ - if ( ms->flags & IP_MASQ_F_NO_DPORT && ms->protocol == IPPROTO_TCP ) { + if ( ms->flags & IP_MASQ_F_NO_DPORT ) { /* && ms->protocol == IPPROTO_TCP ) { */ ms->flags &= ~IP_MASQ_F_NO_DPORT; ms->dport = portptr[0]; -#if DEBUG_CONFIG_IP_MASQUERADE - printk("ip_fw_demasquerade(): filled dport=%d\n", + + IP_MASQ_DEBUG(1, "ip_fw_demasquerade(): filled dport=%d\n", ntohs(ms->dport)); -#endif + } - if (ms->flags & IP_MASQ_F_NO_DADDR && ms->protocol == IPPROTO_TCP) { + if (ms->flags & IP_MASQ_F_NO_DADDR ) { /* && ms->protocol == IPPROTO_TCP) { */ ms->flags &= ~IP_MASQ_F_NO_DADDR; ms->daddr = iph->saddr; -#if DEBUG_CONFIG_IP_MASQUERADE - printk("ip_fw_demasquerade(): filled daddr=%X\n", + + IP_MASQ_DEBUG(1, "ip_fw_demasquerade(): filled daddr=%X\n", ntohs(ms->daddr)); -#endif + } iph->daddr = ms->saddr; portptr[1] = ms->sport; @@ -869,7 +1594,7 @@ int ip_fw_demasquerade(struct sk_buff **skb_p) * will fix ip_masq and iph ack_seq stuff */ - if (ip_masq_app_pkt_in(ms, skb_p) != 0) + if (ip_masq_app_pkt_in(ms, skb_p, maddr) != 0) { /* * skb has changed, update pointers. @@ -886,50 +1611,27 @@ int ip_fw_demasquerade(struct sk_buff **skb_p) * timeouts. * If a TCP RST is seen collapse the tunnel (by using short timeout)! */ - if (iph->protocol==IPPROTO_UDP) - { + if (iph->protocol == IPPROTO_UDP) { recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,len); - timeout = ip_masq_expire->udp_timeout; - } - else - { - struct tcphdr *th; - skb->csum = csum_partial((void *)(((struct tcphdr *)portptr) + 1), + } else { + struct tcphdr *th = (struct tcphdr *)portptr; + skb->csum = csum_partial((void *)(th + 1), len - sizeof(struct tcphdr), 0); - th = (struct tcphdr *) portptr; + th->check = 0; - th->check = tcp_v4_check(th, len, iph->saddr, - iph->daddr, + th->check = tcp_v4_check(th, len, iph->saddr, iph->daddr, csum_partial((char *)th, sizeof(*th), skb->csum)); - /* Check if TCP FIN or RST */ - if (th->fin) - { - ms->flags |= IP_MASQ_F_SAW_FIN_IN; - } - if (th->rst) - { - ms->flags |= IP_MASQ_F_SAW_RST; - } - - /* Now set the timeouts */ - if (ms->flags & IP_MASQ_F_SAW_RST) - { - timeout = 1; - } - else if ((ms->flags & IP_MASQ_F_SAW_FIN) == IP_MASQ_F_SAW_FIN) - { - timeout = ip_masq_expire->tcp_fin_timeout; - } - else timeout = ip_masq_expire->tcp_timeout; } - ip_masq_set_expire(ms, timeout); ip_send_check(iph); -#ifdef DEBUG_CONFIG_IP_MASQUERADE - printk("I-routed to %lX:%X\n",ntohl(iph->daddr),ntohs(portptr[1])); -#endif + + IP_MASQ_DEBUG(2, "I-routed to %08lX:%04X\n",ntohl(iph->daddr),ntohs(portptr[1])); + + masq_set_state (ms, 0, iph, portptr); + ip_masq_put(ms); + return 1; } @@ -937,43 +1639,89 @@ int ip_fw_demasquerade(struct sk_buff **skb_p) return 0; } + +void ip_masq_control_add(struct ip_masq *ms, struct ip_masq* ctl_ms) +{ + if (ms->control) { + IP_MASQ_ERR( "request control ADD for already controlled: %d.%d.%d.%d:%d to %d.%d.%d.%d:%d\n", + NIPQUAD(ms->saddr),ntohs(ms->sport), + NIPQUAD(ms->daddr),ntohs(ms->dport)); + ip_masq_control_del(ms); + } + IP_MASQ_DEBUG(1, "ADDing control for: ms.dst=%d.%d.%d.%d:%d ctl_ms.dst=%d.%d.%d.%d:%d\n", + NIPQUAD(ms->daddr),ntohs(ms->dport), + NIPQUAD(ctl_ms->daddr),ntohs(ctl_ms->dport)); + ms->control = ctl_ms; + atomic_inc(&ctl_ms->n_control); +} + +void ip_masq_control_del(struct ip_masq *ms) +{ + struct ip_masq *ctl_ms = ms->control; + if (!ctl_ms) { + IP_MASQ_ERR( "request control DEL for uncontrolled: %d.%d.%d.%d:%d to %d.%d.%d.%d:%d\n", + NIPQUAD(ms->saddr),ntohs(ms->sport), + NIPQUAD(ms->daddr),ntohs(ms->dport)); + return; + } + IP_MASQ_DEBUG(1, "DELeting control for: ms.dst=%d.%d.%d.%d:%d ctl_ms.dst=%d.%d.%d.%d:%d\n", + NIPQUAD(ms->daddr),ntohs(ms->dport), + NIPQUAD(ctl_ms->daddr),ntohs(ctl_ms->dport)); + ms->control = NULL; + if (atomic_read(&ctl_ms->n_control) == 0) { + IP_MASQ_ERR( "BUG control DEL with n=0 : %d.%d.%d.%d:%d to %d.%d.%d.%d:%d\n", + NIPQUAD(ms->saddr),ntohs(ms->sport), + NIPQUAD(ms->daddr),ntohs(ms->dport)); + return; + + } + atomic_dec(&ctl_ms->n_control); +} + +struct ip_masq * ip_masq_control_get(struct ip_masq *ms) +{ + return ms->control; +} + #ifdef CONFIG_PROC_FS /* - * /proc/net entry + * /proc/net entries + * From userspace */ - static int ip_msqhst_procinfo(char *buffer, char **start, off_t offset, int length, int unused) { off_t pos=0, begin; struct ip_masq *ms; - unsigned long flags; char temp[129]; int idx = 0; int len=0; + ip_masq_lockz(&__ip_masq_lock, &masq_wait, 0); + if (offset < 128) { sprintf(temp, - "Prc FromIP FPrt ToIP TPrt Masq Init-seq Delta PDelta Expires (free=%d,%d)", - ip_masq_free_ports[0], ip_masq_free_ports[1]); + "Prc FromIP FPrt ToIP TPrt Masq Init-seq Delta PDelta Expires (free=%d,%d,%d)", + atomic_read(ip_masq_free_ports), + atomic_read(ip_masq_free_ports+1), + atomic_read(ip_masq_free_ports+2)); len = sprintf(buffer, "%-127s\n", temp); } pos = 128; - save_flags(flags); - cli(); for(idx = 0; idx < IP_MASQ_TAB_SIZE; idx++) for(ms = ip_masq_m_tab[idx]; ms ; ms = ms->m_link) { - int timer_active; pos += 128; if (pos <= offset) continue; - timer_active = del_timer(&ms->timer); - if (!timer_active) - ms->timer.expires = jiffies; + /* + * We have locked the tables, no need to del/add timers + * nor cli() 8) + */ + sprintf(temp,"%s %08lX:%04X %08lX:%04X %04X %08X %6d %6d %7lu", masq_proto_name(ms->protocol), ntohl(ms->saddr), ntohs(ms->sport), @@ -983,15 +1731,75 @@ static int ip_msqhst_procinfo(char *buffer, char **start, off_t offset, ms->out_seq.delta, ms->out_seq.previous_delta, ms->timer.expires-jiffies); - if (timer_active) - add_timer(&ms->timer); len += sprintf(buffer+len, "%-127s\n", temp); if(len >= length) goto done; } done: - restore_flags(flags); + + ip_masq_unlockz(&__ip_masq_lock, &masq_wait, 0); + + begin = len - (pos - offset); + *start = buffer + begin; + len -= begin; + if(len>length) + len = length; + return len; +} + +static int ip_masq_procinfo(char *buffer, char **start, off_t offset, + int length, int unused) +{ + off_t pos=0, begin; + struct ip_masq *ms; + char temp[129]; + int idx = 0; + int len=0; + + ip_masq_lockz(&__ip_masq_lock, &masq_wait, 0); + + if (offset < 128) + { + sprintf(temp, + "Prot SrcIP SPrt DstIP DPrt MAddr MPrt State Ref Ctl Expires (free=%d,%d,%d)", + atomic_read(ip_masq_free_ports), + atomic_read(ip_masq_free_ports+1), + atomic_read(ip_masq_free_ports+2)); + len = sprintf(buffer, "%-127s\n", temp); + } + pos = 128; + + for(idx = 0; idx < IP_MASQ_TAB_SIZE; idx++) + for(ms = ip_masq_m_tab[idx]; ms ; ms = ms->m_link) + { + pos += 128; + if (pos <= offset) + continue; + + /* + * We have locked the tables, no need to del/add timers + * nor cli() 8) + */ + + sprintf(temp,"%-4s %08lX:%04X %08lX:%04X %08lX:%04X %-12s %3d %3d %7lu", + masq_proto_name(ms->protocol), + ntohl(ms->saddr), ntohs(ms->sport), + ntohl(ms->daddr), ntohs(ms->dport), + ntohl(ms->maddr), ntohs(ms->mport), + masq_state_name(ms->state), + atomic_read(&ms->refcnt), + atomic_read(&ms->n_control), + (ms->timer.expires-jiffies)/HZ); + len += sprintf(buffer+len, "%-127s\n", temp); + + if(len >= length) + goto done; + } +done: + + ip_masq_unlockz(&__ip_masq_lock, &masq_wait, 0); + begin = len - (pos - offset); *start = buffer + begin; len -= begin; @@ -1000,21 +1808,51 @@ done: return len; } -static struct proc_dir_entry proc_net_ipmsqhst = { - PROC_NET_IPMSQHST, 13, "ip_masquerade", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_net_inode_operations, - ip_msqhst_procinfo -}; #endif /* + * Control from ip_sockglue + * From userspace + */ +int ip_masq_ctl(int optname, void *arg, int arglen) +{ + struct ip_fw_masqctl *mctl = arg; + int ret = EINVAL; + + ip_masq_lockz(&__ip_masq_lock, &masq_wait, 0); + + if (1) /* (mctl->mctl_action == IP_MASQ_MOD_CTL) */ + ret = ip_masq_mod_ctl(optname, mctl, arglen); + + ip_masq_unlockz(&__ip_masq_lock, &masq_wait, 0); + + return ret; +} + +/* * Initialize ip masquerading */ __initfunc(int ip_masq_init(void)) { -#ifdef CONFIG_PROC_FS - proc_net_register(&proc_net_ipmsqhst); +#ifdef CONFIG_PROC_FS + proc_net_register(&(struct proc_dir_entry) { + PROC_NET_IPMSQHST, 13, "ip_masquerade", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + ip_msqhst_procinfo + }); + proc_net_register(&(struct proc_dir_entry) { + 0, 7, "ip_masq", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + ip_masq_procinfo + }); +#endif +#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW + ip_autofw_init(); +#endif +#ifdef CONFIG_IP_MASQUERADE_IPPORTFW + ip_portfw_init(); #endif ip_masq_app_init(); diff --git a/net/ipv4/ip_masq_app.c b/net/ipv4/ip_masq_app.c index f03aef04b..814da2aa8 100644 --- a/net/ipv4/ip_masq_app.c +++ b/net/ipv4/ip_masq_app.c @@ -39,13 +39,6 @@ #include <linux/proc_fs.h> #include <net/ip_masq.h> -static const char *strProt[] = {"UDP","TCP"}; - -static __inline__ const char * masq_proto_name(unsigned proto) -{ - return strProt[proto==IPPROTO_TCP]; -} - #define IP_MASQ_APP_TAB_SIZE 16 /* must be power of 2 */ #define IP_MASQ_APP_HASH(proto, port) ((port^proto) & (IP_MASQ_APP_TAB_SIZE-1)) @@ -74,7 +67,7 @@ int register_ip_masq_app(struct ip_masq_app *mapp, unsigned short proto, __u16 p unsigned long flags; unsigned hash; if (!mapp) { - printk(KERN_ERR "register_ip_masq_app(): NULL arg\n"); + IP_MASQ_ERR("register_ip_masq_app(): NULL arg\n"); return -EINVAL; } mapp->type = IP_MASQ_APP_TYPE(proto, port); @@ -100,14 +93,14 @@ int unregister_ip_masq_app(struct ip_masq_app *mapp) unsigned hash; unsigned long flags; if (!mapp) { - printk(KERN_ERR "unregister_ip_masq_app(): NULL arg\n"); + IP_MASQ_ERR("unregister_ip_masq_app(): NULL arg\n"); return -EINVAL; } /* * only allow unregistration if it has no attachments */ if (mapp->n_attach) { - printk(KERN_ERR "unregister_ip_masq_app(): has %d attachments. failed\n", + IP_MASQ_ERR("unregister_ip_masq_app(): has %d attachments. failed\n", mapp->n_attach); return -EINVAL; } @@ -123,7 +116,7 @@ int unregister_ip_masq_app(struct ip_masq_app *mapp) } restore_flags(flags); - printk(KERN_ERR "unregister_ip_masq_app(proto=%s,port=%u): not hashed!\n", + IP_MASQ_ERR("unregister_ip_masq_app(proto=%s,port=%u): not hashed!\n", masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)), IP_MASQ_APP_PORT(mapp->type)); return -EINVAL; } @@ -165,7 +158,7 @@ static __inline__ int ip_masq_app_bind_chg(struct ip_masq_app *mapp, int delta) n_at = mapp->n_attach + delta; if (n_at < 0) { restore_flags(flags); - printk(KERN_ERR "ip_masq_app: tried to set n_attach < 0 for (proto=%s,port==%d) ip_masq_app object.\n", + IP_MASQ_ERR("ip_masq_app: tried to set n_attach < 0 for (proto=%s,port==%d) ip_masq_app object.\n", masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)), IP_MASQ_APP_PORT(mapp->type)); return -1; @@ -183,14 +176,26 @@ static __inline__ int ip_masq_app_bind_chg(struct ip_masq_app *mapp, int delta) struct ip_masq_app * ip_masq_bind_app(struct ip_masq *ms) { struct ip_masq_app * mapp; + + if (ms->protocol != IPPROTO_TCP && ms->protocol != IPPROTO_UDP) + return NULL; + mapp = ip_masq_app_get(ms->protocol, ms->dport); + +#if 0000 +/* #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW */ + if (mapp == NULL) + mapp = ip_masq_app_get(ms->protocol, ms->sport); +/* #endif */ +#endif + if (mapp != NULL) { /* * don't allow binding if already bound */ if (ms->app != NULL) { - printk(KERN_ERR "ip_masq_bind_app() called for already bound object.\n"); + IP_MASQ_ERR("ip_masq_bind_app() called for already bound object.\n"); return ms->app; } @@ -209,6 +214,10 @@ int ip_masq_unbind_app(struct ip_masq *ms) { struct ip_masq_app * mapp; mapp = ms->app; + + if (ms->protocol != IPPROTO_TCP && ms->protocol != IPPROTO_UDP) + return 0; + if (mapp != NULL) { if (mapp->masq_done_1) mapp->masq_done_1(mapp, ms); ms->app = NULL; @@ -236,14 +245,10 @@ static __inline__ void masq_fix_seq(const struct ip_masq_seq *ms_seq, struct tcp if (ms_seq->delta || ms_seq->previous_delta) { if(after(seq,ms_seq->init_seq) ) { th->seq = htonl(seq + ms_seq->delta); -#if DEBUG_CONFIG_IP_MASQ_APP - printk("masq_fix_seq() : added delta (%d) to seq\n",ms_seq->delta); -#endif + IP_MASQ_DEBUG(1, "masq_fix_seq() : added delta (%d) to seq\n",ms_seq->delta); } else { th->seq = htonl(seq + ms_seq->previous_delta); -#if DEBUG_CONFIG_IP_MASQ_APP - printk("masq_fix_seq() : added previous_delta (%d) to seq\n",ms_seq->previous_delta); -#endif + IP_MASQ_DEBUG(1, "masq_fix_seq() : added previous_delta (%d) to seq\n",ms_seq->previous_delta); } } @@ -269,14 +274,11 @@ static __inline__ void masq_fix_ack_seq(const struct ip_masq_seq *ms_seq, struct if (ms_seq->delta || ms_seq->previous_delta) { if(after(ack_seq,ms_seq->init_seq)) { th->ack_seq = htonl(ack_seq-ms_seq->delta); -#if DEBUG_CONFIG_IP_MASQ_APP - printk("masq_fix_ack_seq() : subtracted delta (%d) from ack_seq\n",ms_seq->delta); -#endif + IP_MASQ_DEBUG(1, "masq_fix_ack_seq() : subtracted delta (%d) from ack_seq\n",ms_seq->delta); + } else { th->ack_seq = htonl(ack_seq-ms_seq->previous_delta); -#if DEBUG_CONFIG_IP_MASQ_APP - printk("masq_fix_ack_seq() : subtracted previous_delta (%d) from ack_seq\n",ms_seq->previous_delta); -#endif + IP_MASQ_DEBUG(1, "masq_fix_ack_seq() : subtracted previous_delta (%d) from ack_seq\n",ms_seq->previous_delta); } } @@ -369,7 +371,7 @@ int ip_masq_app_pkt_out(struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr) * returns (new - old) skb->len diff. */ -int ip_masq_app_pkt_in(struct ip_masq *ms, struct sk_buff **skb_p) +int ip_masq_app_pkt_in(struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr) { struct ip_masq_app * mapp; struct iphdr *iph; @@ -414,7 +416,7 @@ int ip_masq_app_pkt_in(struct ip_masq *ms, struct sk_buff **skb_p) if ( mapp->pkt_in == NULL ) return 0; - diff = mapp->pkt_in(mapp, ms, skb_p); + diff = mapp->pkt_in(mapp, ms, skb_p, maddr); /* * Update ip_masq seq stuff if len has changed. @@ -529,7 +531,7 @@ static struct sk_buff * skb_replace(struct sk_buff *skb, int pri, char *o_buf, i n_skb = alloc_skb(MAX_HEADER + skb->len + diff, pri); if (n_skb == NULL) { - printk(KERN_ERR "skb_replace(): no room left (from %p)\n", + IP_MASQ_ERR(KERN_ERR "skb_replace(): no room left (from %p)\n", return_address()); return skb; @@ -589,9 +591,7 @@ struct sk_buff * ip_masq_skb_replace(struct sk_buff *skb, int pri, char *o_buf, if (diff) { struct iphdr *iph; -#if DEBUG_CONFIG_IP_MASQ_APP - printk("masq_skb_replace(): pkt resized for %d bytes (len=%ld)\n", diff, skb->len); -#endif + IP_MASQ_DEBUG(1, "masq_skb_replace(): pkt resized for %d bytes (len=%d)\n", diff, skb->len); /* * update ip header */ diff --git a/net/ipv4/ip_masq_autofw.c b/net/ipv4/ip_masq_autofw.c new file mode 100644 index 000000000..30493d4cd --- /dev/null +++ b/net/ipv4/ip_masq_autofw.c @@ -0,0 +1,427 @@ +/* + * IP_MASQ_AUTOFW auto forwarding module + * + * + * Version: @(#)ip_masq_autofw.c 0.02 97/10/22 + * + * Author: Richard Lynch + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * + * Fixes: + * Juan Jose Ciarlante : created this new file from ip_masq.c and ip_fw.c + * Juan Jose Ciarlante : modularized + * Juan Jose Ciarlante : use GFP_KERNEL when creating entries + * Juan Jose Ciarlante : call del_timer() when freeing entries (!) + * FIXME: + * - implement refcnt + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <asm/system.h> +#include <linux/stat.h> +#include <linux/proc_fs.h> +#include <linux/if.h> +#include <linux/init.h> +#include <linux/ip_fw.h> +#include <net/ip_masq.h> +#include <net/ip_masq_mod.h> +#include <net/ip_autofw.h> + +/* + * Debug level + */ +static int debug=0; + +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i"); +MODULE_PARM(debug, "i"); + +/* + * Auto-forwarding table + */ + +static struct ip_autofw * ip_autofw_hosts = NULL; +static struct ip_masq_mod * mmod_self = NULL; + +/* + * Check if a masq entry should be created for a packet + */ + +static __inline__ struct ip_autofw * ip_autofw_check_range (__u32 where, __u16 port, __u16 protocol, int reqact) +{ + struct ip_autofw *af; + af=ip_autofw_hosts; + port=ntohs(port); + while (af) { + if (af->type==IP_FWD_RANGE && + port>=af->low && + port<=af->high && + protocol==af->protocol && + + /* + * It's ok to create masq entries after + * the timeout if we're in insecure mode + */ + (af->flags & IP_AUTOFW_ACTIVE || !reqact || !(af->flags & IP_AUTOFW_SECURE)) && + (!(af->flags & IP_AUTOFW_SECURE) || af->lastcontact==where || !reqact)) + return(af); + af=af->next; + } + return(NULL); +} + +static __inline__ struct ip_autofw * ip_autofw_check_port (__u16 port, __u16 protocol) +{ + struct ip_autofw *af; + af=ip_autofw_hosts; + port=ntohs(port); + while (af) + { + if (af->type==IP_FWD_PORT && port==af->visible && protocol==af->protocol) + return(af); + af=af->next; + } + return(NULL); +} + +static __inline__ struct ip_autofw * ip_autofw_check_direct (__u16 port, __u16 protocol) +{ + struct ip_autofw *af; + af=ip_autofw_hosts; + port=ntohs(port); + while (af) + { + if (af->type==IP_FWD_DIRECT && af->low<=port && af->high>=port) + return(af); + af=af->next; + } + return(NULL); +} + +static __inline__ void ip_autofw_update_out (__u32 who, __u32 where, __u16 port, __u16 protocol) +{ + struct ip_autofw *af; + af=ip_autofw_hosts; + port=ntohs(port); + while (af) + { + if (af->type==IP_FWD_RANGE && af->ctlport==port && af->ctlproto==protocol) + { + if (af->flags & IP_AUTOFW_USETIME) + { + if (af->timer.expires) + del_timer(&af->timer); + af->timer.expires=jiffies+IP_AUTOFW_EXPIRE; + add_timer(&af->timer); + } + af->flags|=IP_AUTOFW_ACTIVE; + af->lastcontact=where; + af->where=who; + } + af=af->next; + } +} + +#if 0 +static __inline__ void ip_autofw_update_in (__u32 where, __u16 port, __u16 protocol) +{ + struct ip_autofw *af; + af=ip_autofw_check_range(where, port,protocol); + if (af) + { + del_timer(&af->timer); + af->timer.expires=jiffies+IP_AUTOFW_EXPIRE; + add_timer(&af->timer); + } +} +#endif + + +static __inline__ void ip_autofw_expire(unsigned long data) +{ + struct ip_autofw * af; + af=(struct ip_autofw *) data; + af->flags &= ~IP_AUTOFW_ACTIVE; + af->timer.expires=0; + af->lastcontact=0; + if (af->flags & IP_AUTOFW_SECURE) + af->where=0; +} + + + +static __inline__ int ip_autofw_add(struct ip_autofw * af) +{ + struct ip_autofw * newaf; + init_timer(&af->timer); + newaf = kmalloc( sizeof(struct ip_autofw), GFP_KERNEL ); + if ( newaf == NULL ) + { + printk("ip_autofw_add: malloc said no\n"); + return( ENOMEM ); + } + + MOD_INC_USE_COUNT; + + memcpy(newaf, af, sizeof(struct ip_autofw)); + newaf->timer.data = (unsigned long) newaf; + newaf->timer.function = ip_autofw_expire; + newaf->timer.expires = 0; + newaf->lastcontact=0; + newaf->next=ip_autofw_hosts; + ip_autofw_hosts=newaf; + ip_masq_mod_inc_nent(mmod_self); + return(0); +} + +static __inline__ int ip_autofw_del(struct ip_autofw * af) +{ + struct ip_autofw ** af_p, *curr; + + for (af_p=&ip_autofw_hosts, curr=*af_p; (curr=*af_p); af_p = &(*af_p)->next) { + if (af->type == curr->type && + af->low == curr->low && + af->high == curr->high && + af->hidden == curr->hidden && + af->visible == curr->visible && + af->protocol == curr->protocol && + af->where == curr->where && + af->ctlproto == curr->ctlproto && + af->ctlport == curr->ctlport) + { + ip_masq_mod_dec_nent(mmod_self); + *af_p = curr->next; + if (af->flags&IP_AUTOFW_ACTIVE) + del_timer(&curr->timer); + kfree_s(curr,sizeof(struct ip_autofw)); + MOD_DEC_USE_COUNT; + return 0; + } + curr=curr->next; + } + return EINVAL; +} + +static __inline__ int ip_autofw_flush(void) +{ + struct ip_autofw * af; + + while (ip_autofw_hosts) + { + af=ip_autofw_hosts; + ip_masq_mod_dec_nent(mmod_self); + ip_autofw_hosts=ip_autofw_hosts->next; + if (af->flags&IP_AUTOFW_ACTIVE) + del_timer(&af->timer); + kfree_s(af,sizeof(struct ip_autofw)); + MOD_DEC_USE_COUNT; + } + return(0); +} + +/* + * Methods for registered object + */ + +static int autofw_ctl(int optname, struct ip_fw_masqctl *mctl, int optlen) +{ + struct ip_autofw *af = (struct ip_autofw*) mctl->u.mod.data; + + switch (optname) { + case IP_FW_MASQ_ADD: + if (optlen<sizeof(*af)) + return EINVAL; + return ip_autofw_add(af); + case IP_FW_MASQ_DEL: + if (optlen<sizeof(*af)) + return EINVAL; + return ip_autofw_del(af); + case IP_FW_MASQ_FLUSH: + return ip_autofw_flush(); + + } + return EINVAL; +} + + +static int autofw_out_update(struct iphdr *iph, __u16 *portp, struct ip_masq *ms) +{ + /* + * Update any ipautofw entries ... + */ + + ip_autofw_update_out(iph->saddr, iph->daddr, portp[1], iph->protocol); + return IP_MASQ_MOD_NOP; +} + +static struct ip_masq * autofw_out_create(struct iphdr *iph, __u16 * portp, __u32 maddr) +{ + /* + * If the source port is supposed to match the masq port, then + * make it so + */ + + if (ip_autofw_check_direct(portp[1],iph->protocol)) { + return ip_masq_new(iph->protocol, + maddr, portp[0], + iph->saddr, portp[0], + iph->daddr, portp[1], + 0); + } + return NULL; +} + +#if 0 +static int autofw_in_update(struct iphdr *iph, __u16 *portp, struct ip_masq *ms) +{ + ip_autofw_update_in(iph->saddr, portp[1], iph->protocol); + return IP_MASQ_MOD_NOP; +} +#endif + +static int autofw_in_rule(struct iphdr *iph, __u16 *portp) +{ + return (ip_autofw_check_range(iph->saddr, portp[1], iph->protocol, 0) + || ip_autofw_check_direct(portp[1], iph->protocol) + || ip_autofw_check_port(portp[1], iph->protocol)); +} + +static struct ip_masq * autofw_in_create(struct iphdr *iph, __u16 *portp, __u32 maddr) +{ + struct ip_autofw *af; + + if ((af=ip_autofw_check_range(iph->saddr, portp[1], iph->protocol, 0))) { + IP_MASQ_DEBUG(1-debug, "autofw_check_range HIT\n"); + return ip_masq_new(iph->protocol, + maddr, portp[1], + af->where, portp[1], + iph->saddr, portp[0], + 0); + } + if ((af=ip_autofw_check_port(portp[1], iph->protocol)) ) { + IP_MASQ_DEBUG(1-debug, "autofw_check_port HIT\n"); + return ip_masq_new(iph->protocol, + maddr, htons(af->visible), + af->where, htons(af->hidden), + iph->saddr, portp[0], + 0); + } + return NULL; +} + +#ifdef CONFIG_PROC_FS +static int autofw_procinfo(char *buffer, char **start, off_t offset, + int length, int unused) +{ + off_t pos=0, begin=0; + struct ip_autofw * af; + int len=0; + + len=sprintf(buffer,"Type Prot Low High Vis Hid Where Last CPto CPrt Timer Flags\n"); + + for(af = ip_autofw_hosts; af ; af = af->next) + { + len+=sprintf(buffer+len,"%4X %4X %04X-%04X/%04X %04X %08lX %08lX %04X %04X %6lu %4X\n", + af->type, + af->protocol, + af->low, + af->high, + af->visible, + af->hidden, + ntohl(af->where), + ntohl(af->lastcontact), + af->ctlproto, + af->ctlport, + (af->timer.expires<jiffies ? 0 : af->timer.expires-jiffies), + af->flags); + + pos=begin+len; + if(pos<offset) + { + len=0; + begin=pos; + } + if(pos>offset+length) + break; + } + *start=buffer+(offset-begin); + len-=(offset-begin); + if(len>length) + len=length; + return len; +} + +static struct proc_dir_entry autofw_proc_entry = { + 0, 0, NULL, + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + autofw_procinfo +}; + +#define proc_ent &autofw_proc_entry +#else /* !CONFIG_PROC_FS */ + +#define proc_ent NULL +#endif + + +#define autofw_in_update NULL +#define autofw_out_rule NULL +#define autofw_mod_init NULL +#define autofw_mod_done NULL + +static struct ip_masq_mod autofw_mod = { + NULL, /* next */ + NULL, /* next_reg */ + "autofw", /* name */ + ATOMIC_INIT(0), /* nent */ + ATOMIC_INIT(0), /* refcnt */ + proc_ent, + autofw_ctl, + autofw_mod_init, + autofw_mod_done, + autofw_in_rule, + autofw_in_update, + autofw_in_create, + autofw_out_rule, + autofw_out_update, + autofw_out_create, +}; + +__initfunc(int ip_autofw_init(void)) +{ + return register_ip_masq_mod ((mmod_self=&autofw_mod)); +} + +int ip_autofw_done(void) +{ + return unregister_ip_masq_mod(&autofw_mod); +} + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + if (ip_autofw_init() != 0) + return -EIO; + return 0; +} + +void cleanup_module(void) +{ + if (ip_autofw_done() != 0) + printk(KERN_INFO "ip_autofw_done(): can't remove module"); +} + +#endif /* MODULE */ diff --git a/net/ipv4/ip_masq_cuseeme.c b/net/ipv4/ip_masq_cuseeme.c new file mode 100644 index 000000000..a306b4f25 --- /dev/null +++ b/net/ipv4/ip_masq_cuseeme.c @@ -0,0 +1,261 @@ +/* + * IP_MASQ_FTP CUSeeMe masquerading module + * + * + * Version: @(#)$Id: ip_masq_cuseeme.c,v 1.2 1997/11/28 15:32:18 alan Exp $ + * + * Author: Richard Lynch + * + * + * Fixes: + * Richard Lynch : Updated patch to conform to new module + * specifications + * Nigel Metheringham : Multiple port support + * Michael Owings : Fixed broken init code + * Added code to update inbound + * packets with correct local addresses. + * Fixes audio and "chat" problems + * Thanx to the CU-SeeMe Consortium for + * technical docs + * Steven Clarke : Small changes for 2.1 + * + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Multiple Port Support + * The helper can be made to handle up to MAX_MASQ_APP_PORTS (normally 12) + * with the port numbers being defined at module load time. The module + * uses the symbol "ports" to define a list of monitored ports, which can + * be specified on the insmod command line as + * ports=x1,x2,x3... + * where x[n] are integer port numbers. This option can be put into + * /etc/conf.modules (or /etc/modules.conf depending on your config) + * where modload will pick it up should you use modload to load your + * modules. + * + */ + +#include <linux/module.h> +#include <asm/system.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/init.h> +#include <net/protocol.h> +#include <net/udp.h> + +/* #define IP_MASQ_NDEBUG */ +#include <net/ip_masq.h> + +#pragma pack(1) +/* CU-SeeMe Data Header */ +typedef struct { + u_short dest_family; + u_short dest_port; + u_long dest_addr; + short family; + u_short port; + u_long addr; + u_long seq; + u_short msg; + u_short data_type; + u_short packet_len; +} cu_header; + +/* Open Continue Header */ +typedef struct { + cu_header cu_head; + u_short client_count; /* Number of client info structs */ + u_long seq_no; + char user_name[20]; + char stuff[4]; /* flags, version stuff, etc */ +}oc_header; + +/* client info structures */ +typedef struct { + u_long address; /* Client address */ + char stuff[8]; /* Flags, pruning bitfield, packet counts etc */ +} client_info; +#pragma pack() + +/* + * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper + * First port is set to the default port. + */ +static int ports[MAX_MASQ_APP_PORTS] = {7648}; /* I rely on the trailing items being set to zero */ +struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS]; + +/* + * Debug level + */ +static int debug=0; + +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i"); +MODULE_PARM(debug, "i"); + +static int +masq_cuseeme_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms) +{ + MOD_INC_USE_COUNT; + return 0; +} + +static int +masq_cuseeme_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +int +masq_cuseeme_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr) +{ + struct sk_buff *skb = *skb_p; + struct iphdr *iph = skb->nh.iph; + struct udphdr *uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]); + cu_header *cu_head; + char *data=(char *)&uh[1]; + + if (skb->len - ((unsigned char *) data - skb->h.raw) >= sizeof(cu_header)) + { + cu_head = (cu_header *) data; + /* cu_head->port = ms->mport; */ + if( cu_head->addr ) + cu_head->addr = (u_long) maddr; + if(ntohs(cu_head->data_type) == 257) + IP_MASQ_DEBUG(1-debug, "Sending talk packet!\n"); + } + return 0; +} + +int +masq_cuseeme_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr) +{ + struct sk_buff *skb = *skb_p; + struct iphdr *iph = skb->nh.iph; + struct udphdr *uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]); + cu_header *cu_head; + oc_header *oc; + client_info *ci; + char *data=(char *)&uh[1]; + u_short len = skb->len - ((unsigned char *) data - skb->h.raw); + int i, off; + + if (len >= sizeof(cu_header)) + { + cu_head = (cu_header *) data; + if(cu_head->dest_addr) /* Correct destination address */ + cu_head->dest_addr = (u_long) ms->saddr; + if(ntohs(cu_head->data_type)==101 && len > sizeof(oc_header)) + { + oc = (oc_header * ) data; + /* Spin (grovel) thru client_info structs till we find our own */ + off=sizeof(oc_header); + for(i=0; + (i < oc->client_count && off+sizeof(client_info) <= len); + i++) + { + ci=(client_info *)(data+off); + if(ci->address==(u_long) maddr) + { + /* Update w/ our real ip address and exit */ + ci->address = (u_long) ms->saddr; + break; + } + else + off+=sizeof(client_info); + } + } + } + return 0; +} + +struct ip_masq_app ip_masq_cuseeme = { + NULL, /* next */ + "cuseeme", + 0, /* type */ + 0, /* n_attach */ + masq_cuseeme_init_1, /* ip_masq_init_1 */ + masq_cuseeme_done_1, /* ip_masq_done_1 */ + masq_cuseeme_out, /* pkt_out */ + masq_cuseeme_in /* pkt_in */ +}; + + +/* + * ip_masq_cuseeme initialization + */ + +__initfunc(int ip_masq_cuseeme_init(void)) +{ + int i, j; + + for (i=0; (i<MAX_MASQ_APP_PORTS); i++) { + if (ports[i]) { + if ((masq_incarnations[i] = kmalloc(sizeof(struct ip_masq_app), + GFP_KERNEL)) == NULL) + return -ENOMEM; + memcpy(masq_incarnations[i], &ip_masq_cuseeme, sizeof(struct ip_masq_app)); + if ((j = register_ip_masq_app(masq_incarnations[i], + IPPROTO_UDP, + ports[i]))) { + return j; + } +#if DEBUG_CONFIG_IP_MASQ_CUSEEME + IP_MASQ_DEBUG(1-debug, "CuSeeMe: loaded support on port[%d] = %d\n", + i, ports[i]); +#endif + } else { + /* To be safe, force the incarnation table entry to NULL */ + masq_incarnations[i] = NULL; + } + } + return 0; +} + +/* + * ip_masq_cuseeme fin. + */ + +int ip_masq_cuseeme_done(void) +{ + int i, j, k; + + k=0; + for (i=0; (i<MAX_MASQ_APP_PORTS); i++) { + if (masq_incarnations[i]) { + if ((j = unregister_ip_masq_app(masq_incarnations[i]))) { + k = j; + } else { + kfree(masq_incarnations[i]); + masq_incarnations[i] = NULL; + IP_MASQ_DEBUG(1-debug, "CuSeeMe: unloaded support on port[%d] = %d\n", i, ports[i]); + } + } + } + return k; +} + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + if (ip_masq_cuseeme_init() != 0) + return -EIO; + return 0; +} + +void cleanup_module(void) +{ + if (ip_masq_cuseeme_done() != 0) + IP_MASQ_DEBUG(1-debug, "ip_masq_cuseeme: can't remove module"); +} + +#endif /* MODULE */ diff --git a/net/ipv4/ip_masq_ftp.c b/net/ipv4/ip_masq_ftp.c index 4cb88d925..5313f4429 100644 --- a/net/ipv4/ip_masq_ftp.c +++ b/net/ipv4/ip_masq_ftp.c @@ -2,14 +2,20 @@ * IP_MASQ_FTP ftp masquerading module * * - * Version: @(#)ip_masq_ftp.c 0.01 02/05/96 + * Version: @(#)ip_masq_ftp.c 0.04 02/05/96 * * Author: Wouter Gadeyne - * + * * * Fixes: * Wouter Gadeyne : Fixed masquerading support of ftp PORT commands * Juan Jose Ciarlante : Code moved and adapted from ip_fw.c + * Keith Owens : Add keep alive for ftp control channel + * Nigel Metheringham : Added multiple port support + * Juan Jose Ciarlante : Use control_add() for ftp control chan + * Juan Jose Ciarlante : Litl bits for 2.1 + * Juan Jose Ciarlante : use ip_masq_listen() + * Juan Jose Ciarlante : use private app_data for own flag(s) * * * @@ -17,7 +23,18 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. - * + * + * Multiple Port Support + * The helper can be made to handle up to MAX_MASQ_APP_PORTS (normally 12) + * with the port numbers being defined at module load time. The module + * uses the symbol "ports" to define a list of monitored ports, which can + * be specified on the insmod command line as + * ports=x1,x2,x3... + * where x[n] are integer port numbers. This option can be put into + * /etc/conf.modules (or /etc/modules.conf depending on your config) + * where modload will pick it up should you use modload to load your + * modules. + * */ #include <linux/config.h> @@ -31,9 +48,28 @@ #include <linux/init.h> #include <net/protocol.h> #include <net/tcp.h> + +/* #define IP_MASQ_NDEBUG */ #include <net/ip_masq.h> -#define DEBUG_CONFIG_IP_MASQ_FTP 0 + +/* + * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper + * First port is set to the default port. + */ +static int ports[MAX_MASQ_APP_PORTS] = {21}; /* I rely on the trailing items being set to zero */ +struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS]; + +/* + * Debug level + */ +static int debug=0; + +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i"); +MODULE_PARM(debug, "i"); + +/* Dummy variable */ +static int masq_ftp_pasv; static int masq_ftp_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms) @@ -70,6 +106,8 @@ masq_ftp_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb data = (char *)&th[1]; data_limit = skb->h.raw + skb->len - 18; + if (skb->len >= 6 && (memcmp(data, "PASV\r\n", 6) == 0 || memcmp(data, "pasv\r\n", 6) == 0)) + ms->app_data = &masq_ftp_pasv; while (data < data_limit) { @@ -100,39 +138,30 @@ masq_ftp_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb from = (p1<<24) | (p2<<16) | (p3<<8) | p4; port = (p5<<8) | p6; -#if DEBUG_CONFIG_IP_MASQ_FTP - printk("PORT %X:%X detected\n",from,port); -#endif + + IP_MASQ_DEBUG(1-debug, "PORT %X:%X detected\n",from,port); + /* * Now update or create an masquerade entry for it */ -#if DEBUG_CONFIG_IP_MASQ_FTP - printk("protocol %d %lX:%X %X:%X\n", iph->protocol, htonl(from), htons(port), iph->daddr, 0); -#endif - n_ms = ip_masq_out_get_2(iph->protocol, + IP_MASQ_DEBUG(1-debug, "protocol %d %lX:%X %X:%X\n", iph->protocol, htonl(from), htons(port), iph->daddr, 0); + + n_ms = ip_masq_out_get(iph->protocol, htonl(from), htons(port), iph->daddr, 0); - if (n_ms) { - /* existing masquerade, clear timer */ - ip_masq_set_expire(n_ms,0); - } - else { - n_ms = ip_masq_new(maddr, IPPROTO_TCP, + if (!n_ms) { + n_ms = ip_masq_new(IPPROTO_TCP, + maddr, 0, htonl(from), htons(port), iph->daddr, 0, IP_MASQ_F_NO_DPORT); if (n_ms==NULL) return 0; + ip_masq_control_add(n_ms, ms); } - /* - * keep for a bit longer than tcp_fin, caller may not reissue - * PORT before tcp_fin_timeout. - */ - ip_masq_set_expire(n_ms, ip_masq_expire->tcp_fin_timeout*3); - /* * Replace the old PORT with the new one */ @@ -142,30 +171,34 @@ masq_ftp_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb from>>24&255,from>>16&255,from>>8&255,from&255, port>>8&255,port&255); buf_len = strlen(buf); -#if DEBUG_CONFIG_IP_MASQ_FTP - printk("new PORT %X:%X\n",from,port); -#endif + + IP_MASQ_DEBUG(1-debug, "new PORT %X:%X\n",from,port); /* * Calculate required delta-offset to keep TCP happy */ - + diff = buf_len - (data-p); - + /* * No shift. */ - - if (diff==0) - { + + if (diff==0) { /* * simple case, just replace the old PORT cmd */ memcpy(p,buf,buf_len); - return 0; - } + } else { + + *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, p, data-p, buf, buf_len); + } + /* + * Move tunnel to listen state + */ + ip_masq_listen(n_ms); + ip_masq_put(n_ms); - *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, p, data-p, buf, buf_len); return diff; } @@ -173,6 +206,108 @@ masq_ftp_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb } +/* + * Look at incoming ftp packets to catch the response to a PASV command. When + * we see one we build a masquerading entry for the client address, client port + * 0 (unknown at the moment), the server address and the server port. Mark the + * current masquerade entry as a control channel and point the new entry at the + * control entry. All this work just for ftp keepalive across masquerading. + * + * The incoming packet should be something like + * "227 Entering Passive Mode (xxx,xxx,xxx,xxx,ppp,ppp)". + * xxx,xxx,xxx,xxx is the server address, ppp,ppp is the server port number. + * ncftp 2.3.0 cheats by skipping the leading number then going 22 bytes into + * the data so we do the same. If it's good enough for ncftp then it's good + * enough for me. + * + * In this case, the client is the source machine being masqueraded, the server + * is the destination for ftp requests. It all depends on your point of view ... + */ + +int +masq_ftp_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr) +{ + struct sk_buff *skb; + struct iphdr *iph; + struct tcphdr *th; + char *data, *data_limit; + unsigned char p1,p2,p3,p4,p5,p6; + __u32 to; + __u16 port; + struct ip_masq *n_ms; + + if (ms->app_data != &masq_ftp_pasv) + return 0; /* quick exit if no outstanding PASV */ + + skb = *skb_p; + iph = skb->nh.iph; + th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); + data = (char *)&th[1]; + data_limit = skb->h.raw + skb->len; + + while (data < data_limit && *data != ' ') + ++data; + while (data < data_limit && *data == ' ') + ++data; + data += 22; + if (data >= data_limit || *data != '(') + return 0; + p1 = simple_strtoul(data+1, &data, 10); + if (data >= data_limit || *data != ',') + return 0; + p2 = simple_strtoul(data+1, &data, 10); + if (data >= data_limit || *data != ',') + return 0; + p3 = simple_strtoul(data+1, &data, 10); + if (data >= data_limit || *data != ',') + return 0; + p4 = simple_strtoul(data+1, &data, 10); + if (data >= data_limit || *data != ',') + return 0; + p5 = simple_strtoul(data+1, &data, 10); + if (data >= data_limit || *data != ',') + return 0; + p6 = simple_strtoul(data+1, &data, 10); + if (data >= data_limit || *data != ')') + return 0; + + to = (p1<<24) | (p2<<16) | (p3<<8) | p4; + port = (p5<<8) | p6; + + /* + * Now update or create an masquerade entry for it + */ + IP_MASQ_DEBUG(1-debug, "PASV response %lX:%X %X:%X detected\n", ntohl(ms->saddr), 0, to, port); + + n_ms = ip_masq_out_get(iph->protocol, + ms->saddr, 0, + htonl(to), htons(port)); + if (!n_ms) { + n_ms = ip_masq_new(IPPROTO_TCP, + maddr, 0, + ms->saddr, 0, + htonl(to), htons(port), + IP_MASQ_F_NO_SPORT); + + if (n_ms==NULL) + return 0; + ip_masq_control_add(n_ms, ms); + } + +#if 0 /* v0.12 state processing */ + + /* + * keep for a bit longer than tcp_fin, client may not issue open + * to server port before tcp_fin_timeout. + */ + n_ms->timeout = ip_masq_expire->tcp_fin_timeout*3; +#endif + ms->app_data = NULL; + ip_masq_put(n_ms); + + return 0; /* no diff required for incoming packets, thank goodness */ +} + struct ip_masq_app ip_masq_ftp = { NULL, /* next */ "ftp", /* name */ @@ -181,7 +316,7 @@ struct ip_masq_app ip_masq_ftp = { masq_ftp_init_1, /* ip_masq_init_1 */ masq_ftp_done_1, /* ip_masq_done_1 */ masq_ftp_out, /* pkt_out */ - NULL /* pkt_in */ + masq_ftp_in, /* pkt_in */ }; /* @@ -190,7 +325,27 @@ struct ip_masq_app ip_masq_ftp = { __initfunc(int ip_masq_ftp_init(void)) { - return register_ip_masq_app(&ip_masq_ftp, IPPROTO_TCP, 21); + int i, j; + + for (i=0; (i<MAX_MASQ_APP_PORTS); i++) { + if (ports[i]) { + if ((masq_incarnations[i] = kmalloc(sizeof(struct ip_masq_app), + GFP_KERNEL)) == NULL) + return -ENOMEM; + memcpy(masq_incarnations[i], &ip_masq_ftp, sizeof(struct ip_masq_app)); + if ((j = register_ip_masq_app(masq_incarnations[i], + IPPROTO_TCP, + ports[i]))) { + return j; + } + IP_MASQ_DEBUG(1-debug, "Ftp: loaded support on port[%d] = %d\n", + i, ports[i]); + } else { + /* To be safe, force the incarnation table entry to NULL */ + masq_incarnations[i] = NULL; + } + } + return 0; } /* @@ -199,7 +354,22 @@ __initfunc(int ip_masq_ftp_init(void)) int ip_masq_ftp_done(void) { - return unregister_ip_masq_app(&ip_masq_ftp); + int i, j, k; + + k=0; + for (i=0; (i<MAX_MASQ_APP_PORTS); i++) { + if (masq_incarnations[i]) { + if ((j = unregister_ip_masq_app(masq_incarnations[i]))) { + k = j; + } else { + kfree(masq_incarnations[i]); + masq_incarnations[i] = NULL; + IP_MASQ_DEBUG(1-debug, "Ftp: unloaded support on port[%d] = %d\n", + i, ports[i]); + } + } + } + return k; } #ifdef MODULE diff --git a/net/ipv4/ip_masq_irc.c b/net/ipv4/ip_masq_irc.c index b2e325ce6..6668efdaf 100644 --- a/net/ipv4/ip_masq_irc.c +++ b/net/ipv4/ip_masq_irc.c @@ -2,13 +2,22 @@ * IP_MASQ_IRC irc masquerading module * * - * Version: @(#)ip_masq_irc.c 0.01 03/20/96 + * Version: @(#)ip_masq_irc.c 0.03 97/11/30 * * Author: Juan Jose Ciarlante - * - * + * + * Additions: + * - recognize a few non-irc-II DCC requests (Oliver Wagner) + * DCC MOVE (AmIRC/DCC.MOVE; SEND with resuming) + * DCC SCHAT (AmIRC IDEA encrypted CHAT) + * DCC TSEND (AmIRC/PIRCH SEND without ACKs) * Fixes: - * - set NO_DADDR flag in ip_masq_new(). + * Juan Jose Ciarlante : set NO_DADDR flag in ip_masq_new() + * Nigel Metheringham : Added multiple port support + * Juan Jose Ciarlante : litl bits for 2.1 + * Oliver Wagner : more IRC cmds processing + * <winmute@lucifer.gv.kotnet.org> + * Juan Jose Ciarlante : put new ms entry to listen() * * FIXME: * - detect also previous "PRIVMSG" string ?. @@ -17,7 +26,18 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. - * + * + * Multiple Port Support + * The helper can be made to handle up to MAX_MASQ_APP_PORTS (normally 12) + * with the port numbers being defined at module load time. The module + * uses the symbol "ports" to define a list of monitored ports, which can + * be specified on the insmod command line as + * ports=x1,x2,x3... + * where x[n] are integer port numbers. This option can be put into + * /etc/conf.modules (or /etc/modules.conf depending on your config) + * where modload will pick it up should you use modload to load your + * modules. + * */ #include <linux/config.h> @@ -34,7 +54,43 @@ #include <net/tcp.h> #include <net/ip_masq.h> -#define DEBUG_CONFIG_IP_MASQ_IRC 0 + +/* + * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper + * First port is set to the default port. + */ +int ports[MAX_MASQ_APP_PORTS] = {6667}; /* I rely on the trailing items being set to zero */ +struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS]; +/* + * Debug level + */ +static int debug=0; + +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i"); +MODULE_PARM(debug, "i"); + + +/* + * List of supported DCC protocols + */ + +#define NUM_DCCPROTO 5 + +struct dccproto +{ + char *match; + int matchlen; + int xtra_args; +}; + +struct dccproto dccprotos[NUM_DCCPROTO] = { + { "SEND ", 5, 1 }, + { "CHAT ", 5, 0, }, + { "MOVE ", 5, 1 }, + { "TSEND ", 6, 1, }, + { "SCHAT ", 6, 0, } +}; +#define MAXMATCHLEN 6 static int masq_irc_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms) @@ -72,145 +128,148 @@ masq_irc_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb data = (char *)&th[1]; /* - * Hunt irc DCC string, the _shortest_: - * - * strlen("DCC CHAT chat AAAAAAAA P\x01\n")=26 - * strlen("DCC SEND F AAAAAAAA P S\x01\n")=25 - * AAAAAAAAA: bound addr (1.0.0.0==16777216, min 8 digits) - * P: bound port (min 1 d ) - * F: filename (min 1 d ) - * S: size (min 1 d ) - * 0x01, \n: terminators + * Hunt irc DCC string, the _shortest_: + * + * strlen("DCC CHAT chat AAAAAAAA P\x01\n")=26 + * strlen("DCC SCHAT chat AAAAAAAA P\x01\n")=27 + * strlen("DCC SEND F AAAAAAAA P S\x01\n")=25 + * strlen("DCC MOVE F AAAAAAAA P S\x01\n")=25 + * strlen("DCC TSEND F AAAAAAAA P S\x01\n")=26 + * strlen("DCC MOVE F AAAAAAAA P S\x01\n")=25 + * AAAAAAAAA: bound addr (1.0.0.0==16777216, min 8 digits) + * P: bound port (min 1 d ) + * F: filename (min 1 d ) + * S: size (min 1 d ) + * 0x01, \n: terminators */ data_limit = skb->h.raw + skb->len; - - while (data < (data_limit - 25) ) + + while (data < (data_limit - ( 21 + MAXMATCHLEN ) ) ) { + int i; if (memcmp(data,"DCC ",4)) { data ++; continue; } - dcc_p = data; + dcc_p = data; data += 4; /* point to DCC cmd */ - if (memcmp(data, "CHAT ", 5) == 0 || - memcmp(data, "SEND ", 5) == 0) - { - /* - * extra arg (file_size) req. for "SEND" - */ - - if (*data == 'S') xtra_args++; - data += 5; - } - else - continue; - - /* - * skip next string. - */ - - while( *data++ != ' ') - - /* - * must still parse, at least, "AAAAAAAA P\x01\n", - * 12 bytes left. - */ - if (data > (data_limit-12)) return 0; - - - addr_beg_p = data; - - /* - * client bound address in dec base - */ - - s_addr = simple_strtoul(data,&data,10); - if (*data++ !=' ') - continue; - - /* - * client bound port in dec base - */ - - s_port = simple_strtoul(data,&data,10); - addr_end_p = data; - - /* - * should check args consistency? - */ - - while(xtra_args) { - if (*data != ' ') - break; - data++; - simple_strtoul(data,&data,10); - xtra_args--; - } - - if (xtra_args != 0) continue; - - /* - * terminators. - */ - - if (data[0] != 0x01) - continue; - if (data[1]!='\r' && data[1]!='\n') - continue; - - /* - * Now create an masquerade entry for it - * must set NO_DPORT and NO_DADDR because - * connection is requested by another client. - */ - - n_ms = ip_masq_new(maddr, IPPROTO_TCP, - htonl(s_addr),htons(s_port), - 0, 0, - IP_MASQ_F_NO_DPORT|IP_MASQ_F_NO_DADDR - ); - if (n_ms==NULL) - return 0; - - ip_masq_set_expire(n_ms, ip_masq_expire->tcp_fin_timeout); - - /* - * Replace the old "address port" with the new one - */ - - buf_len = sprintf(buf,"%lu %u", - ntohl(n_ms->maddr),ntohs(n_ms->mport)); - - /* - * Calculate required delta-offset to keep TCP happy - */ - - diff = buf_len - (addr_end_p-addr_beg_p); - -#if DEBUG_CONFIG_IP_MASQ_IRC - *addr_beg_p = '\0'; - printk("masq_irc_out(): '%s' %X:%X detected (diff=%d)\n", dcc_p, s_addr,s_port, diff); -#endif - /* - * No shift. - */ - - if (diff==0) + for(i=0; i<NUM_DCCPROTO; i++) { /* - * simple case, just copy. - */ - memcpy(addr_beg_p,buf,buf_len); - return 0; - } - - *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, - addr_beg_p, addr_end_p-addr_beg_p, - buf, buf_len); - return diff; + * go through the table and hunt a match string + */ + + if( memcmp(data, dccprotos[i].match, dccprotos[i].matchlen ) == 0 ) + { + xtra_args = dccprotos[i].xtra_args; + data += dccprotos[i].matchlen; + + /* + * skip next string. + */ + + while( *data++ != ' ') + + /* + * must still parse, at least, "AAAAAAAA P\x01\n", + * 12 bytes left. + */ + if (data > (data_limit-12)) return 0; + + + addr_beg_p = data; + + /* + * client bound address in dec base + */ + + s_addr = simple_strtoul(data,&data,10); + if (*data++ !=' ') + continue; + + /* + * client bound port in dec base + */ + + s_port = simple_strtoul(data,&data,10); + addr_end_p = data; + + /* + * should check args consistency? + */ + + while(xtra_args) { + if (*data != ' ') + break; + data++; + simple_strtoul(data,&data,10); + xtra_args--; + } + + if (xtra_args != 0) continue; + + /* + * terminators. + */ + + if (data[0] != 0x01) + continue; + if (data[1]!='\r' && data[1]!='\n') + continue; + + /* + * Now create an masquerade entry for it + * must set NO_DPORT and NO_DADDR because + * connection is requested by another client. + */ + + n_ms = ip_masq_new(IPPROTO_TCP, + maddr, 0, + htonl(s_addr),htons(s_port), + 0, 0, + IP_MASQ_F_NO_DPORT|IP_MASQ_F_NO_DADDR); + if (n_ms==NULL) + return 0; + + /* + * Replace the old "address port" with the new one + */ + + buf_len = sprintf(buf,"%lu %u", + ntohl(n_ms->maddr),ntohs(n_ms->mport)); + + /* + * Calculate required delta-offset to keep TCP happy + */ + + diff = buf_len - (addr_end_p-addr_beg_p); + + *addr_beg_p = '\0'; + IP_MASQ_DEBUG(1-debug, "masq_irc_out(): '%s' %X:%X detected (diff=%d)\n", dcc_p, s_addr,s_port, diff); + + /* + * No shift. + */ + + if (diff==0) { + /* + * simple case, just copy. + */ + memcpy(addr_beg_p,buf,buf_len); + } else { + + *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, + addr_beg_p, addr_end_p-addr_beg_p, + buf, buf_len); + } + ip_masq_listen(n_ms); + ip_masq_put(n_ms); + return diff; + } + } } return 0; @@ -221,7 +280,7 @@ masq_irc_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb * You need 1 object per port in case you need * to offer also other used irc ports (6665,6666,etc), * they will share methods but they need own space for - * data. + * data. */ struct ip_masq_app ip_masq_irc = { @@ -241,7 +300,28 @@ struct ip_masq_app ip_masq_irc = { __initfunc(int ip_masq_irc_init(void)) { - return register_ip_masq_app(&ip_masq_irc, IPPROTO_TCP, 6667); + int i, j; + + for (i=0; (i<MAX_MASQ_APP_PORTS); i++) { + if (ports[i]) { + if ((masq_incarnations[i] = kmalloc(sizeof(struct ip_masq_app), + GFP_KERNEL)) == NULL) + return -ENOMEM; + memcpy(masq_incarnations[i], &ip_masq_irc, sizeof(struct ip_masq_app)); + if ((j = register_ip_masq_app(masq_incarnations[i], + IPPROTO_TCP, + ports[i]))) { + return j; + } + IP_MASQ_DEBUG(1-debug, + "Irc: loaded support on port[%d] = %d\n", + i, ports[i]); + } else { + /* To be safe, force the incarnation table entry to NULL */ + masq_incarnations[i] = NULL; + } + } + return 0; } /* @@ -250,9 +330,25 @@ __initfunc(int ip_masq_irc_init(void)) int ip_masq_irc_done(void) { - return unregister_ip_masq_app(&ip_masq_irc); + int i, j, k; + + k=0; + for (i=0; (i<MAX_MASQ_APP_PORTS); i++) { + if (masq_incarnations[i]) { + if ((j = unregister_ip_masq_app(masq_incarnations[i]))) { + k = j; + } else { + kfree(masq_incarnations[i]); + masq_incarnations[i] = NULL; + IP_MASQ_DEBUG(1-debug, "Irc: unloaded support on port[%d] = %d\n", + i, ports[i]); + } + } + } + return k; } + #ifdef MODULE EXPORT_NO_SYMBOLS; diff --git a/net/ipv4/ip_masq_mod.c b/net/ipv4/ip_masq_mod.c new file mode 100644 index 000000000..797f9112f --- /dev/null +++ b/net/ipv4/ip_masq_mod.c @@ -0,0 +1,316 @@ +/* + * IP_MASQ_MOD masq modules support + * + * + * Version: @(#)ip_masq_mod.c 0.02 97/10/30 + * + * Author: Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar> + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <net/ip_masq.h> +#include <net/ip_masq_mod.h> +#ifdef CONFIG_KERNELD +#include <linux/kerneld.h> +#endif + +EXPORT_SYMBOL(register_ip_masq_mod); +EXPORT_SYMBOL(unregister_ip_masq_mod); +EXPORT_SYMBOL(ip_masq_mod_lkp_link); +EXPORT_SYMBOL(ip_masq_mod_lkp_unlink); + +/* + * Base pointer for registered modules + */ +struct ip_masq_mod * ip_masq_mod_reg_base = NULL; + +/* + * Base pointer for lookup (subset of above, a module could be + * registered, but it could have no active rule); will avoid + * unnecessary lookups. + */ +struct ip_masq_mod * ip_masq_mod_lkp_base = NULL; + +int ip_masq_mod_register_proc(struct ip_masq_mod *mmod) +{ +#ifdef CONFIG_PROC_FS + int ret; + + struct proc_dir_entry *ent = mmod->mmod_proc_ent; + + if (!ent) + return 0; + if (!ent->name) { + ent->name = mmod->mmod_name; + ent->namelen = strlen (mmod->mmod_name); + } + ret = proc_net_register(ent); + if (ret) mmod->mmod_proc_ent = NULL; + + return ret; +#else + return 0; +#endif +} + +void ip_masq_mod_unregister_proc(struct ip_masq_mod *mmod) +{ +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *ent = mmod->mmod_proc_ent; + if (!ent) + return; + proc_unregister(proc_net, ent->low_ino); +#endif +} + +/* + * Link/unlink object for lookups + */ + +int ip_masq_mod_lkp_unlink(struct ip_masq_mod *mmod) +{ + struct ip_masq_mod **mmod_p; + + start_bh_atomic(); + + for (mmod_p = &ip_masq_mod_lkp_base; *mmod_p ; mmod_p = &(*mmod_p)->next) + if (mmod == (*mmod_p)) { + *mmod_p = mmod->next; + mmod->next = NULL; + end_bh_atomic(); + return 0; + } + + end_bh_atomic(); + return -EINVAL; +} + +int ip_masq_mod_lkp_link(struct ip_masq_mod *mmod) +{ + start_bh_atomic(); + + mmod->next = ip_masq_mod_lkp_base; + ip_masq_mod_lkp_base=mmod; + + end_bh_atomic(); + return 0; +} + +int register_ip_masq_mod(struct ip_masq_mod *mmod) +{ + if (!mmod) { + IP_MASQ_ERR("register_ip_masq_mod(): NULL arg\n"); + return -EINVAL; + } + if (!mmod->mmod_name) { + IP_MASQ_ERR("register_ip_masq_mod(): NULL mmod_name\n"); + return -EINVAL; + } + ip_masq_mod_register_proc(mmod); + + mmod->next_reg = ip_masq_mod_reg_base; + ip_masq_mod_reg_base=mmod; + + return 0; +} + +int unregister_ip_masq_mod(struct ip_masq_mod *mmod) +{ + struct ip_masq_mod **mmod_p; + + if (!mmod) { + IP_MASQ_ERR( "unregister_ip_masq_mod(): NULL arg\n"); + return -EINVAL; + } + + /* + * Only allow unregistration if it is not referenced + */ + if (atomic_read(&mmod->refcnt)) { + IP_MASQ_ERR( "unregister_ip_masq_mod(): is in use by %d guys. failed\n", + atomic_read(&mmod->refcnt)); + return -EINVAL; + } + + /* + * Must be already unlinked from lookup list + */ + if (mmod->next) { + IP_MASQ_WARNING("MASQ: unregistering \"%s\" while in lookup list.fixed.", + mmod->mmod_name); + ip_masq_mod_lkp_unlink(mmod); + } + + for (mmod_p = &ip_masq_mod_reg_base; *mmod_p ; mmod_p = &(*mmod_p)->next_reg) + if (mmod == (*mmod_p)) { + ip_masq_mod_unregister_proc(mmod); + *mmod_p = mmod->next_reg; + return 0; + } + + IP_MASQ_ERR("unregister_ip_masq_mod(%s): not linked \n", mmod->mmod_name); + return -EINVAL; +} + +int ip_masq_mod_in_rule(struct iphdr *iph, __u16 *portp) +{ + struct ip_masq_mod *mmod; + int ret; + + for (mmod=ip_masq_mod_lkp_base;mmod;mmod=mmod->next) { + if (!mmod->mmod_in_rule) continue; + switch (ret=mmod->mmod_in_rule(iph, portp)) { + case IP_MASQ_MOD_NOP: + continue; + case IP_MASQ_MOD_ACCEPT: + return 1; + case IP_MASQ_MOD_REJECT: + return -1; + } + } + return 0; +} + +int ip_masq_mod_out_rule(struct iphdr *iph, __u16 *portp) +{ + struct ip_masq_mod *mmod; + int ret; + + for (mmod=ip_masq_mod_lkp_base;mmod;mmod=mmod->next) { + if (!mmod->mmod_out_rule) continue; + switch (ret=mmod->mmod_out_rule(iph, portp)) { + case IP_MASQ_MOD_NOP: + continue; + case IP_MASQ_MOD_ACCEPT: + return 1; + case IP_MASQ_MOD_REJECT: + return -1; + } + } + return 0; +} + +struct ip_masq * ip_masq_mod_in_create(struct iphdr *iph, __u16 *portp, __u32 maddr) +{ + struct ip_masq_mod *mmod; + struct ip_masq *ms; + + for (mmod=ip_masq_mod_lkp_base;mmod;mmod=mmod->next) { + if (!mmod->mmod_in_create) continue; + if ((ms=mmod->mmod_in_create(iph, portp, maddr))) { + return ms; + } + } + return NULL; +} + +struct ip_masq * ip_masq_mod_out_create(struct iphdr *iph, __u16 *portp, __u32 maddr) +{ + struct ip_masq_mod *mmod; + struct ip_masq *ms; + + for (mmod=ip_masq_mod_lkp_base;mmod;mmod=mmod->next) { + if (!mmod->mmod_out_create) continue; + if ((ms=mmod->mmod_out_create(iph, portp, maddr))) { + return ms; + } + } + return NULL; +} + +int ip_masq_mod_in_update(struct iphdr *iph, __u16 *portp, struct ip_masq *ms) +{ + struct ip_masq_mod *mmod; + int ret; + + for (mmod=ip_masq_mod_lkp_base;mmod;mmod=mmod->next) { + if (!mmod->mmod_in_update) continue; + switch (ret=mmod->mmod_in_update(iph, ms)) { + case IP_MASQ_MOD_NOP: + continue; + case IP_MASQ_MOD_ACCEPT: + return 1; + case IP_MASQ_MOD_REJECT: + return -1; + } + } + return 0; +} + +int ip_masq_mod_out_update(struct iphdr *iph, __u16 *portp, struct ip_masq *ms) +{ + struct ip_masq_mod *mmod; + int ret; + + for (mmod=ip_masq_mod_lkp_base;mmod;mmod=mmod->next) { + if (!mmod->mmod_out_update) continue; + switch (ret=mmod->mmod_out_update(iph, portp, ms)) { + case IP_MASQ_MOD_NOP: + continue; + case IP_MASQ_MOD_ACCEPT: + return 1; + case IP_MASQ_MOD_REJECT: + return -1; + } + } + return 0; +} + +struct ip_masq_mod * ip_masq_mod_getbyname(const char *mmod_name) +{ + struct ip_masq_mod * mmod; + + IP_MASQ_DEBUG(1, "searching mmod_name \"%s\"\n", mmod_name); + + for (mmod=ip_masq_mod_reg_base; mmod ; mmod=mmod->next) { + if (mmod->mmod_ctl && *(mmod_name) + && (strcmp(mmod_name, mmod->mmod_name)==0)) { + /* HIT */ + return mmod; + } + } + return NULL; +} + +/* + * Module control entry + * no need to lock (already locked in ip_masq.c) + */ +int ip_masq_mod_ctl(int optname, struct ip_fw_masqctl *mctl, int optlen) +{ + struct ip_masq_mod * mmod; +#ifdef CONFIG_KERNELD + char kmod_name[IP_MASQ_MOD_NMAX+8]; +#endif + /* tappo */ + mctl->u.mod.name[IP_MASQ_MOD_NMAX-1] = 0; + + mmod = ip_masq_mod_getbyname(mctl->u.mod.name); + if (mmod) + return mmod->mmod_ctl(optname, mctl, optlen); +#ifdef CONFIG_KERNELD + sprintf(kmod_name,"ip_masq_%s", mctl->u.mod.name); + + IP_MASQ_DEBUG(1, "About to request \"%s\" module\n", kmod_name); + + /* + * Let sleep for a while ... + */ + request_module(kmod_name); + mmod = ip_masq_mod_getbyname(mctl->u.mod.name); + if (mmod) + return mmod->mmod_ctl(optname, mctl, optlen); +#endif + return ESRCH; +} diff --git a/net/ipv4/ip_masq_portfw.c b/net/ipv4/ip_masq_portfw.c new file mode 100644 index 000000000..862742a21 --- /dev/null +++ b/net/ipv4/ip_masq_portfw.c @@ -0,0 +1,461 @@ +/* + * IP_MASQ_PORTFW masquerading module + * + * + * Version: @(#)ip_masq_portfw.c 0.02 97/10/30 + * + * Author: Steven Clarke <steven.clarke@monmouth.demon.co.uk> + * + * Fixes: + * Juan Jose Ciarlante : created this new file from ip_masq.c and ip_fw.c + * Juan Jose Ciarlante : modularized + * Juan Jose Ciarlante : use GFP_KERNEL + * + * FIXME + * - after creating /proc/net/ip_masq/ direct, put portfw underneath + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/list.h> +#include <net/ip.h> +#include <linux/ip_fw.h> +#include <net/ip_masq.h> +#include <net/ip_masq_mod.h> +#include <net/ip_portfw.h> +#include <linux/proc_fs.h> +#include <linux/init.h> + +static struct ip_masq_mod *mmod_self = NULL; + +/* + * Lock + */ +static atomic_t portfw_lock = ATOMIC_INIT(0); +static struct wait_queue *portfw_wait; + +static struct list_head portfw_list[2]; +static __inline__ int portfw_idx(int protocol) +{ + return (protocol==IPPROTO_TCP); +} + +/* + * + * Delete forwarding entry(s): + * called from _DEL, u-space. + * . "relaxed" match, except for lport + * + */ + +static __inline__ int ip_portfw_del(__u16 protocol, __u16 lport, __u32 laddr, __u16 rport, __u32 raddr) +{ + int prot = portfw_idx(protocol); + struct ip_portfw *n; + struct list_head *entry; + struct list_head *list = &portfw_list[prot]; + int nent; + + nent = atomic_read(&mmod_self->mmod_nent); + + ip_masq_lockz(&portfw_lock, &portfw_wait, 1); + + for (entry=list->next;entry != list;entry = entry->next) { + n = list_entry(entry, struct ip_portfw, list); + if (n->lport == lport && + (!laddr || n->laddr == laddr) && + (!raddr || n->raddr == raddr) && + (!rport || n->rport == rport)) { + list_del(entry); + ip_masq_mod_dec_nent(mmod_self); + kfree_s(n, sizeof(struct ip_portfw)); + MOD_DEC_USE_COUNT; + } + } + ip_masq_unlockz(&portfw_lock, &portfw_wait, 1); + + return nent==atomic_read(&mmod_self->mmod_nent)? ESRCH : 0; +} + +/* + * Flush tables + * called from _FLUSH, u-space. + */ +static __inline__ void ip_portfw_flush(void) +{ + int prot; + struct list_head *l; + struct list_head *e; + struct ip_portfw *n; + + ip_masq_lockz(&portfw_lock, &portfw_wait, 1); + + for (prot = 0; prot < 2;prot++) { + l = &portfw_list[prot]; + while((e=l->next) != l) { + ip_masq_mod_dec_nent(mmod_self); + n = list_entry (e, struct ip_portfw, list); + list_del(e); + kfree_s(n, sizeof (*n)); + MOD_DEC_USE_COUNT; + } + } + + ip_masq_unlockz(&portfw_lock, &portfw_wait, 1); +} + +/* + * Lookup routine for lport,laddr match + * called from ip_masq module (via registered obj) + */ +static __inline__ struct ip_portfw *ip_portfw_lookup(__u16 protocol, __u16 lport, __u32 laddr, __u32 *daddr_p, __u16 *dport_p) +{ + int prot = portfw_idx(protocol); + + struct ip_portfw *n = NULL; + struct list_head *l, *e; + + ip_masq_lock(&portfw_lock, 0); + + l = &portfw_list[prot]; + + for (e=l->next;e!=l;e=e->next) { + n = list_entry(e, struct ip_portfw, list); + if (lport == n->lport && laddr == n->laddr) { + /* Please be nice, don't pass only a NULL dport */ + if (daddr_p) { + *daddr_p = n->raddr; + *dport_p = n->rport; + } + + goto out; + } + } + n = NULL; +out: + ip_masq_unlock(&portfw_lock, 0); + return n; +} + +/* + * Edit routine for lport,[laddr], [raddr], [rport] match + * By now, only called from u-space + */ +static __inline__ int ip_portfw_edit(__u16 protocol, __u16 lport, __u32 laddr, __u16 rport, __u32 raddr, int pref) +{ + int prot = portfw_idx(protocol); + + struct ip_portfw *n = NULL; + struct list_head *l, *e; + int count = 0; + + + ip_masq_lockz(&portfw_lock, &portfw_wait, 0); + + l = &portfw_list[prot]; + + for (e=l->next;e!=l;e=e->next) { + n = list_entry(e, struct ip_portfw, list); + if (lport == n->lport && + (!laddr || laddr == n->laddr) && + (!rport || rport == n->rport) && + (!raddr || raddr == n->raddr)) { + n->pref = pref; + atomic_set(&n->pref_cnt, pref); + count++; + } + } + + ip_masq_unlockz(&portfw_lock, &portfw_wait, 0); + + return count; +} + +/* + * Add/edit en entry + * called from _ADD, u-space. + * must return 0 or +errno + */ +static __inline__ int ip_portfw_add(__u16 protocol, __u16 lport, __u32 laddr, __u16 rport, __u32 raddr, int pref) +{ + struct ip_portfw *npf; + int prot = portfw_idx(protocol); + + if (pref <= 0) + return EINVAL; + + if (ip_portfw_edit(protocol, lport, laddr, rport, raddr, pref)) { + /* + * Edit ok ... + */ + return 0; + } + + /* may block ... */ + npf = (struct ip_portfw*) kmalloc(sizeof(struct ip_portfw), GFP_KERNEL); + + if (!npf) + return ENOMEM; + + MOD_INC_USE_COUNT; + memset(npf, 0, sizeof(*npf)); + + npf->laddr = laddr; + npf->lport = lport; + npf->rport = rport; + npf->raddr = raddr; + npf->pref = pref; + + atomic_set(&npf->pref_cnt, npf->pref); + INIT_LIST_HEAD(&npf->list); + + ip_masq_lockz(&portfw_lock, &portfw_wait, 1); + + /* + * Add at head + */ + list_add(&npf->list, &portfw_list[prot]); + + ip_masq_unlockz(&portfw_lock, &portfw_wait, 1); + + ip_masq_mod_inc_nent(mmod_self); + return 0; +} + + + +static __inline__ int portfw_ctl(int cmd, struct ip_fw_masqctl *mctl, int optlen) +{ + struct ip_portfw_edits *mm = (struct ip_portfw_edits *) mctl->u.mod.data; + int ret = EINVAL; + + /* + * Don't trust the lusers - plenty of error checking! + */ + if (optlen<sizeof(*mm)) + return EINVAL; + + if (cmd != IP_FW_MASQ_FLUSH) { + if (htons(mm->lport) < IP_PORTFW_PORT_MIN + || htons(mm->lport) > IP_PORTFW_PORT_MAX) + return EINVAL; + + if (mm->protocol!=IPPROTO_TCP && mm->protocol!=IPPROTO_UDP) + return EINVAL; + } + + + switch(cmd) { + case IP_FW_MASQ_ADD: + ret = ip_portfw_add(mm->protocol, + mm->lport, mm->laddr, + mm->rport, mm->raddr, + mm->pref); + break; + + case IP_FW_MASQ_DEL: + ret = ip_portfw_del(mm->protocol, + mm->lport, mm->laddr, + mm->rport, mm->raddr); + break; + case IP_FW_MASQ_FLUSH: + ip_portfw_flush(); + ret = 0; + break; + } + + + return ret; +} + + + + +#ifdef CONFIG_PROC_FS + +static int portfw_procinfo(char *buffer, char **start, off_t offset, + int length, int unused) +{ + off_t pos=0, begin; + struct ip_portfw *pf; + struct list_head *l, *e; + char temp[65]; + int ind; + int len=0; + + ip_masq_lockz(&portfw_lock, &portfw_wait, 0); + + if (offset < 64) + { + sprintf(temp, "Prot LAddr LPort > RAddr RPort PrCnt Pref"); + len = sprintf(buffer, "%-63s\n", temp); + } + pos = 64; + + for(ind = 0; ind < 2; ind++) + { + l = &portfw_list[ind]; + for (e=l->next; e!=l; e=e->next) + { + pf = list_entry(e, struct ip_portfw, list); + pos += 64; + if (pos <= offset) + continue; + + sprintf(temp,"%s %08lX %5u > %08lX %5u %5d %5d", + ind ? "TCP" : "UDP", + ntohl(pf->laddr), ntohs(pf->lport), + ntohl(pf->raddr), ntohs(pf->rport), + atomic_read(&pf->pref_cnt), pf->pref); + len += sprintf(buffer+len, "%-63s\n", temp); + + if (len >= length) + goto done; + } + } +done: + ip_masq_unlockz(&portfw_lock, &portfw_wait, 0); + + begin = len - (pos - offset); + *start = buffer + begin; + len -= begin; + if(len>length) + len = length; + return len; +} + +static struct proc_dir_entry portfw_proc_entry = { +/* 0, 0, NULL", */ + 0, 9, "ip_portfw", /* Just for compatibility, for now ... */ + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + portfw_procinfo +}; + +#define proc_ent &portfw_proc_entry +#else /* !CONFIG_PROC_FS */ + +#define proc_ent NULL +#endif + +static int portfw_in_rule(struct iphdr *iph, __u16 *portp) +{ + + return (ip_portfw_lookup(iph->protocol, portp[1], iph->daddr, NULL, NULL)!=0); +} + +static struct ip_masq * portfw_in_create(struct iphdr *iph, __u16 *portp, __u32 maddr) +{ + /* + * If no entry exists in the masquerading table + * and the port is involved + * in port forwarding, create a new masq entry + */ + + __u32 raddr; + __u16 rport; + struct ip_masq *ms = NULL; + struct ip_portfw *pf; + + /* + * Lock for reading only, by now... + */ + ip_masq_lock(&portfw_lock, 0); + + if ((pf=ip_portfw_lookup(iph->protocol, + portp[1], iph->daddr, + &raddr, &rport))) { + ms = ip_masq_new(iph->protocol, + iph->daddr, portp[1], + raddr, rport, + iph->saddr, portp[0], + 0); + ip_masq_listen(ms); + + if (!ms || atomic_read(&mmod_self->mmod_nent) <= 1 || + ip_masq_nlocks(&portfw_lock) != 1) + /* + * Maybe later... + */ + goto out; + + /* + * Entry created, lock==1. + * if pref_cnt == 0, move + * entry at _tail_. + * This is a simple load balance scheduling + */ + + if (atomic_dec_and_test(&pf->pref_cnt)) { + start_bh_atomic(); + + atomic_set(&pf->pref_cnt, pf->pref); + list_del(&pf->list); + list_add(&pf->list, + portfw_list[portfw_idx(iph->protocol)].prev); + + end_bh_atomic(); + } + } +out: + ip_masq_unlock(&portfw_lock, 0); + return ms; +} + +#define portfw_in_update NULL +#define portfw_out_rule NULL +#define portfw_out_create NULL +#define portfw_out_update NULL + +static struct ip_masq_mod portfw_mod = { + NULL, /* next */ + NULL, /* next_reg */ + "portfw", /* name */ + ATOMIC_INIT(0), /* nent */ + ATOMIC_INIT(0), /* refcnt */ + proc_ent, + portfw_ctl, + NULL, /* masq_mod_init */ + NULL, /* masq_mod_done */ + portfw_in_rule, + portfw_in_update, + portfw_in_create, + portfw_out_rule, + portfw_out_update, + portfw_out_create, +}; + + + +__initfunc(int ip_portfw_init(void)) +{ + INIT_LIST_HEAD(&portfw_list[0]); + INIT_LIST_HEAD(&portfw_list[1]); + return register_ip_masq_mod ((mmod_self=&portfw_mod)); +} + +int ip_portfw_done(void) +{ + return unregister_ip_masq_mod(&portfw_mod); +} + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + if (ip_portfw_init() != 0) + return -EIO; + return 0; +} + +void cleanup_module(void) +{ + if (ip_portfw_done() != 0) + printk(KERN_INFO "ip_portfw_done(): can't remove module"); +} + +#endif /* MODULE */ diff --git a/net/ipv4/ip_masq_quake.c b/net/ipv4/ip_masq_quake.c index 482096f2b..fb0978175 100644 --- a/net/ipv4/ip_masq_quake.c +++ b/net/ipv4/ip_masq_quake.c @@ -11,6 +11,7 @@ * Harald Hoyer : Unofficial Quake Specs found at * http://www.gamers.org/dEngine/quake/spec/ * Harald Hoyer : Check for QUAKE-STRING + * Juan Jose Ciarlante : litl bits for 2.1 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -73,7 +74,7 @@ masq_quake_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms) } int -masq_quake_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p) +masq_quake_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr) { struct sk_buff *skb; struct iphdr *iph; @@ -234,7 +235,8 @@ masq_quake_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **s memcpy(&udp_port, data, 2); - n_ms = ip_masq_new(maddr, IPPROTO_UDP, + n_ms = ip_masq_new(IPPROTO_UDP, + maddr, 0, ms->saddr, htons(udp_port), ms->daddr, ms->dport, 0); @@ -249,6 +251,10 @@ masq_quake_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **s udp_port = ntohs(n_ms->mport); memcpy(data, &udp_port, 2); + ip_masq_listen(n_ms); + ip_masq_control_add(n_ms, ms); + ip_masq_put(n_ms); + break; } diff --git a/net/ipv4/ip_masq_raudio.c b/net/ipv4/ip_masq_raudio.c index 26b5cd4da..d68be7555 100644 --- a/net/ipv4/ip_masq_raudio.c +++ b/net/ipv4/ip_masq_raudio.c @@ -2,11 +2,13 @@ * IP_MASQ_RAUDIO - Real Audio masquerading module * * - * Version: @(#)$Id: ip_masq_raudio.c,v 1.7 1997/09/16 18:43:40 kuznet Exp $ + * Version: @(#)$Id: ip_masq_raudio.c,v 1.8 1997/11/28 15:32:32 alan Exp $ * * Author: Nigel Metheringham + * Real Time Streaming code by Progressive Networks * [strongly based on ftp module by Juan Jose Ciarlante & Wouter Gadeyne] * [Real Audio information taken from Progressive Networks firewall docs] + * [Kudos to Progressive Networks for making the protocol specs available] * * * @@ -31,16 +33,40 @@ * * At present the "first packet" is defined as a packet starting with * the protocol ID string - "PNA". - * When the link is up there appears to be enough control data + * When the link is up there appears to be enough control data * crossing the control link to keep it open even if a long audio * piece is playing. * + * The Robust UDP support added in RealAudio 3.0 is supported, but due + * to servers/clients not making great use of this has not been greatly + * tested. RealVideo (as used in the Real client version 4.0beta1) is + * supported but again is not greatly tested (bandwidth requirements + * appear to exceed that available at the sites supporting the protocol). + * + * Multiple Port Support + * The helper can be made to handle up to MAX_MASQ_APP_PORTS (normally 12) + * with the port numbers being defined at module load time. The module + * uses the symbol "ports" to define a list of monitored ports, which can + * be specified on the insmod command line as + * ports=x1,x2,x3... + * where x[n] are integer port numbers. This option can be put into + * /etc/conf.modules (or /etc/modules.conf depending on your config) + * where modload will pick it up should you use modload to load your + * modules. + * + * Fixes: + * Juan Jose Ciarlante : Use control_add() for control chan + * 10/15/97 - Modifications to allow masquerading of RTSP connections as + * well as PNA, which can potentially exist on the same port. + * Joe Rumsey <ogre@real.com> + * */ #include <linux/config.h> #include <linux/module.h> #include <asm/system.h> #include <linux/types.h> +#include <linux/ctype.h> #include <linux/kernel.h> #include <linux/skbuff.h> #include <linux/in.h> @@ -50,30 +76,62 @@ #include <net/tcp.h> #include <net/ip_masq.h> +/* #ifndef DEBUG_CONFIG_IP_MASQ_RAUDIO #define DEBUG_CONFIG_IP_MASQ_RAUDIO 0 #endif +*/ + +#define TOLOWER(c) (((c) >= 'A' && (c) <= 'Z') ? ((c) - 'A' + 'a') : (c)) +#define ISDIGIT(c) (((c) >= '0') && ((c) <= '9')) struct raudio_priv_data { /* Associated data connection - setup but not used at present */ struct ip_masq *data_conn; + /* UDP Error correction connection - setup but not used at present */ + struct ip_masq *error_conn; /* Have we seen and performed setup */ short seen_start; + short is_rtsp; }; +int +masq_rtsp_out (struct ip_masq_app *mapp, + struct ip_masq *ms, + struct sk_buff **skb_p, + __u32 maddr); + +/* + * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper + * First port is set to the default port. + */ +int ports[MAX_MASQ_APP_PORTS] = {554, 7070, 0}; /* I rely on the trailing items being set to zero */ +struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS]; + +/* + * Debug level + */ +static int debug=0; + +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i"); +MODULE_PARM(debug, "i"); + + static int masq_raudio_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms) { MOD_INC_USE_COUNT; if ((ms->app_data = kmalloc(sizeof(struct raudio_priv_data), - GFP_ATOMIC)) == NULL) + GFP_ATOMIC)) == NULL) printk(KERN_INFO "RealAudio: No memory for application data\n"); - else + else { - struct raudio_priv_data *priv = + struct raudio_priv_data *priv = (struct raudio_priv_data *)ms->app_data; priv->seen_start = 0; priv->data_conn = NULL; + priv->error_conn = NULL; + priv->is_rtsp = 0; } return 0; } @@ -96,34 +154,43 @@ masq_raudio_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff ** char *p, *data, *data_limit; struct ip_masq *n_ms; unsigned short version, msg_id, msg_len, udp_port; - struct raudio_priv_data *priv = + struct raudio_priv_data *priv = (struct raudio_priv_data *)ms->app_data; /* Everything running correctly already */ if (priv && priv->seen_start) return 0; + if(priv && priv->is_rtsp) + return masq_rtsp_out(mapp, ms, skb_p, maddr); + skb = *skb_p; iph = skb->nh.iph; th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); data = (char *)&th[1]; - data_limit = skb->h.raw + skb->len - 18; + data_limit = skb->h.raw + skb->len; + + if(memcmp(data, "OPTIONS", 7) == 0 || + memcmp(data, "DESCRIBE", 8) == 0) + { + IP_MASQ_DEBUG(1-debug, "RealAudio: Detected RTSP connection\n"); + /* This is an RTSP client */ + if(priv) + priv->is_rtsp = 1; + return masq_rtsp_out(mapp, ms, skb_p, maddr); + } /* Check to see if this is the first packet with protocol ID */ if (memcmp(data, "PNA", 3)) { -#if DEBUG_CONFIG_IP_MASQ_RAUDIO - printk("RealAudio: not initial protocol packet - ignored\n"); -#endif + IP_MASQ_DEBUG(1-debug, "RealAudio: not initial protocol packet - ignored\n"); return(0); } data += 3; memcpy(&version, data, 2); -#if DEBUG_CONFIG_IP_MASQ_RAUDIO - printk("RealAudio: initial seen - protocol version %d\n", + IP_MASQ_DEBUG(1-debug, "RealAudio: initial seen - protocol version %d\n", ntohs(version)); -#endif if (priv) priv->seen_start = 1; @@ -135,15 +202,22 @@ masq_raudio_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff ** } data += 2; - while (data < data_limit) { + while (data+4 < data_limit) { memcpy(&msg_id, data, 2); data += 2; memcpy(&msg_len, data, 2); data += 2; -#if DEBUG_CONFIG_IP_MASQ_RAUDIO - printk("RealAudio: msg %d - %d byte\n", + if (ntohs(msg_id) == 0) { + /* The zero tag indicates the end of options */ + IP_MASQ_DEBUG(1-debug, "RealAudio: packet end tag seen\n"); + return 0; + } + IP_MASQ_DEBUG(1-debug, "RealAudio: msg %d - %d byte\n", ntohs(msg_id), ntohs(msg_len)); -#endif + if (ntohs(msg_id) == 0) { + /* The zero tag indicates the end of options */ + return 0; + } p = data; data += ntohs(msg_len); if (data > data_limit) @@ -151,39 +225,272 @@ masq_raudio_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff ** printk(KERN_INFO "RealAudio: Packet too short for data\n"); return 0; } - if (ntohs(msg_id) == 1) { - /* This is a message detailing the UDP port to be used */ + if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) { + /* + * MsgId == 1 + * Audio UDP data port on client + * + * MsgId == 7 + * Robust UDP error correction port number on client + * + * Since these messages are treated just the same, they + * are bundled together here.... + */ memcpy(&udp_port, p, 2); - n_ms = ip_masq_new(maddr, IPPROTO_UDP, - ms->saddr, udp_port, - ms->daddr, 0, - IP_MASQ_F_NO_DPORT); + + /* + * Sometimes a server sends a message 7 with a zero UDP port + * Rather than do anything with this, just ignore it! + */ + if (udp_port == 0) + continue; + + + n_ms = ip_masq_new(IPPROTO_UDP, + maddr, 0, + ms->saddr, udp_port, + ms->daddr, 0, + IP_MASQ_F_NO_DPORT); if (n_ms==NULL) return 0; + ip_masq_listen(n_ms); + ip_masq_control_add(n_ms, ms); + memcpy(p, &(n_ms->mport), 2); -#if DEBUG_CONFIG_IP_MASQ_RAUDIO - printk("RealAudio: rewrote UDP port %d -> %d\n", - ntohs(udp_port), ntohs(n_ms->mport)); -#endif - ip_masq_set_expire(n_ms, ip_masq_expire->udp_timeout); + IP_MASQ_DEBUG(1-debug, "RealAudio: rewrote UDP port %d -> %d in msg %d\n", + ntohs(udp_port), ntohs(n_ms->mport), ntohs(msg_id)); /* Make ref in application data to data connection */ - if (priv) - priv->data_conn = n_ms; + if (priv) { + if (ntohs(msg_id) == 1) + priv->data_conn = n_ms; + else + priv->error_conn = n_ms; + } + + ip_masq_put(n_ms); + } + } + return 0; +} - /* - * There is nothing else useful we can do - * Maybe a development could do more, but for now - * we exit gracefully! - */ - return 0; +/* + * masq_rtsp_out + * + * + */ +int +masq_rtsp_out (struct ip_masq_app *mapp, + struct ip_masq *ms, + struct sk_buff **skb_p, + __u32 maddr) +{ + struct sk_buff *skb; + struct iphdr *iph; + struct tcphdr *th; + char *data, *data_limit; + struct ip_masq *n_ms, *n_ms2; + unsigned short udp_port; + struct raudio_priv_data *priv = + (struct raudio_priv_data *)ms->app_data; + const char* srch = "transport:"; + const char* srchpos = srch; + const char* srchend = srch + strlen(srch); + int state = 0; + char firstport[6]; + int firstportpos = 0; + char secondport[6]; + int secondportpos = 0; + char *portstart = NULL, *portend = NULL; + int diff; - } else if (ntohs(msg_id) == 0) + /* Everything running correctly already */ + if (priv && priv->seen_start) + return 0; + + skb = *skb_p; + iph = skb->nh.iph; + th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); + data = (char *)&th[1]; + + data_limit = skb->h.raw + skb->len; + + firstport[0] = 0; + secondport[0] = 0; + + while(data < data_limit && state >= 0) + { + switch(state) + { + case 0: + case 1: + if(TOLOWER(*data) == *srchpos) + { + srchpos++; + if(srchpos == srchend) + { + IP_MASQ_DEBUG(1-debug, "Found string %s in message\n", + srch); + state++; + if(state == 1) + { + srch = "client_port"; + srchpos = srch; + srchend = srch + strlen(srch); + } + } + } + else + { + srchpos = srch; + } + break; + case 2: + if(*data == '=') + state = 3; + break; + case 3: + if(ISDIGIT(*data)) + { + portstart = data; + firstportpos = 0; + firstport[firstportpos++] = *data; + state = 4; + } + break; + case 4: + if(*data == '-') + { + state = 5; + } + else if(*data == ';') + { + portend = data - 1; + firstport[firstportpos] = 0; + state = -1; + } + else if(ISDIGIT(*data)) + { + firstport[firstportpos++] = *data; + } + else if(*data != ' ' && *data != '\t') + { + /* This is a badly formed RTSP message, let's bail out */ + IP_MASQ_DEBUG(1-debug, "Badly formed RTSP Message\n"); return 0; + } + break; + case 5: + if(ISDIGIT(*data)) + { + secondportpos = 0; + secondport[secondportpos++] = *data; + state = 6; + } + else if(*data == ';') + { + portend = data - 1; + secondport[secondportpos] = 0; + state = -1; + } + break; + case 6: + if(*data == ';') + { + portend = data - 1; + secondport[secondportpos] = 0; + state = -1; + } + else if(ISDIGIT(*data)) + { + secondport[secondportpos++] = *data; + } + else if(*data != ' ' && *data != '\t') + { + /* This is a badly formed RTSP message, let's bail out */ + IP_MASQ_DEBUG(1-debug, "Badly formed RTSP Message\n"); + return 0; + } + break; + } + data++; } - return 0; + + if(state >= 0) + return 0; + + if(firstportpos > 0) + { + char newbuf[12]; /* xxxxx-xxxxx\0 */ + char* tmpptr; + + udp_port = htons(simple_strtoul(firstport, &tmpptr, 10)); + n_ms = ip_masq_new(IPPROTO_UDP, + maddr, 0, + ms->saddr, udp_port, + ms->daddr, 0, + IP_MASQ_F_NO_DPORT); + if (n_ms==NULL) + return 0; + + ip_masq_listen(n_ms); + ip_masq_control_add(n_ms, ms); + + if(secondportpos > 0) + { + udp_port = htons(simple_strtoul(secondport, &tmpptr, 10)); + n_ms2 = ip_masq_new(IPPROTO_UDP, + maddr, 0, + ms->saddr, udp_port, + ms->daddr, 0, + IP_MASQ_F_NO_DPORT); + if (n_ms2==NULL) { + ip_masq_put(n_ms); + return 0; + } + + ip_masq_listen(n_ms2); + ip_masq_control_add(n_ms2, ms); + sprintf(newbuf, "%d-%d", ntohs(n_ms->mport), + ntohs(n_ms2->mport)); + } + else + { + sprintf(newbuf, "%d", ntohs(n_ms->mport)); + n_ms2 = NULL; + } + *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, + portstart, portend - portstart + 1, + newbuf, strlen(newbuf)); + IP_MASQ_DEBUG(1-debug, "RTSP: rewrote client_port to %s\n", newbuf); + diff = strlen(newbuf) - (portend - portstart); + } + else + { + return 0; + } + + if(priv) + { + priv->seen_start = 1; + if(n_ms) + priv->data_conn = n_ms; + if(n_ms2) + priv->error_conn = n_ms2; + } + /* + * Release tunnels + */ + + if (n_ms) + ip_masq_put(n_ms); + + if (n_ms2) + ip_masq_put(n_ms2); + + return diff; } struct ip_masq_app ip_masq_raudio = { @@ -203,7 +510,27 @@ struct ip_masq_app ip_masq_raudio = { __initfunc(int ip_masq_raudio_init(void)) { - return register_ip_masq_app(&ip_masq_raudio, IPPROTO_TCP, 7070); + int i, j; + + for (i=0; (i<MAX_MASQ_APP_PORTS); i++) { + if (ports[i]) { + if ((masq_incarnations[i] = kmalloc(sizeof(struct ip_masq_app), + GFP_KERNEL)) == NULL) + return -ENOMEM; + memcpy(masq_incarnations[i], &ip_masq_raudio, sizeof(struct ip_masq_app)); + if ((j = register_ip_masq_app(masq_incarnations[i], + IPPROTO_TCP, + ports[i]))) { + return j; + } + IP_MASQ_DEBUG(1-debug, "RealAudio: loaded support on port[%d] = %d\n", + i, ports[i]); + } else { + /* To be safe, force the incarnation table entry to NULL */ + masq_incarnations[i] = NULL; + } + } + return 0; } /* @@ -212,7 +539,22 @@ __initfunc(int ip_masq_raudio_init(void)) int ip_masq_raudio_done(void) { - return unregister_ip_masq_app(&ip_masq_raudio); + int i, j, k; + + k=0; + for (i=0; (i<MAX_MASQ_APP_PORTS); i++) { + if (masq_incarnations[i]) { + if ((j = unregister_ip_masq_app(masq_incarnations[i]))) { + k = j; + } else { + kfree(masq_incarnations[i]); + masq_incarnations[i] = NULL; + IP_MASQ_DEBUG(1-debug, "RealAudio: unloaded support on port[%d] = %d\n", + i, ports[i]); + } + } + } + return k; } #ifdef MODULE diff --git a/net/ipv4/ip_masq_vdolive.c b/net/ipv4/ip_masq_vdolive.c new file mode 100644 index 000000000..3b74d5f6f --- /dev/null +++ b/net/ipv4/ip_masq_vdolive.c @@ -0,0 +1,291 @@ +/* + * IP_MASQ_VDOLIVE - VDO Live masquerading module + * + * + * Version: @(#)$Id: ip_masq_vdolive.c,v 1.2 1997/11/28 15:32:35 alan Exp $ + * + * Author: Nigel Metheringham <Nigel.Metheringham@ThePLAnet.net> + * PLAnet Online Ltd + * + * Fixes: Minor changes for 2.1 by + * Steven Clarke <Steven.Clarke@ThePlanet.Net>, Planet Online Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Thanks: + * Thank you to VDOnet Corporation for allowing me access to + * a protocol description without an NDA. This means that + * this module can be distributed as source - a great help! + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <asm/system.h> +#include <linux/skbuff.h> +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/init.h> +#include <net/protocol.h> +#include <net/tcp.h> +#include <net/ip_masq.h> + +struct vdolive_priv_data { + /* Ports used */ + unsigned short origport; + unsigned short masqport; + /* State of decode */ + unsigned short state; +}; + +/* + * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper + * First port is set to the default port. + */ +static int ports[MAX_MASQ_APP_PORTS] = {7000}; /* I rely on the trailing items being set to zero */ +struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS]; + +/* + * Debug level + */ +static int debug=0; + +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i"); +MODULE_PARM(debug, "i"); + +static int +masq_vdolive_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms) +{ + MOD_INC_USE_COUNT; + if ((ms->app_data = kmalloc(sizeof(struct vdolive_priv_data), + GFP_ATOMIC)) == NULL) + IP_MASQ_DEBUG(1-debug, "VDOlive: No memory for application data\n"); + else + { + struct vdolive_priv_data *priv = + (struct vdolive_priv_data *)ms->app_data; + priv->origport = 0; + priv->masqport = 0; + priv->state = 0; + } + return 0; +} + +static int +masq_vdolive_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms) +{ + MOD_DEC_USE_COUNT; + if (ms->app_data) + kfree_s(ms->app_data, sizeof(struct vdolive_priv_data)); + return 0; +} + +int +masq_vdolive_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr) +{ + struct sk_buff *skb; + struct iphdr *iph; + struct tcphdr *th; + char *data, *data_limit; + unsigned int tagval; /* This should be a 32 bit quantity */ + struct ip_masq *n_ms; + struct vdolive_priv_data *priv = + (struct vdolive_priv_data *)ms->app_data; + + /* This doesn't work at all if no priv data was allocated on startup */ + if (!priv) + return 0; + + /* Everything running correctly already */ + if (priv->state == 3) + return 0; + + skb = *skb_p; + iph = skb->nh.iph; + th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); + data = (char *)&th[1]; + + data_limit = skb->h.raw + skb->len; + + if (data+8 > data_limit) { + IP_MASQ_DEBUG(1-debug, "VDOlive: packet too short for ID %p %p\n", data, data_limit); + return 0; + } + memcpy(&tagval, data+4, 4); + IP_MASQ_DEBUG(1-debug, "VDOlive: packet seen, tag %ld, in initial state %d\n", ntohl(tagval), priv->state); + + /* Check for leading packet ID */ + if ((ntohl(tagval) != 6) && (ntohl(tagval) != 1)) { + IP_MASQ_DEBUG(1-debug, "VDOlive: unrecognised tag %ld, in initial state %d\n", ntohl(tagval), priv->state); + return 0; + } + + + /* Check packet is long enough for data - ignore if not */ + if ((ntohl(tagval) == 6) && (data+36 > data_limit)) { + IP_MASQ_DEBUG(1-debug, "VDOlive: initial packet too short %p %p\n", data, data_limit); + return 0; + } else if ((ntohl(tagval) == 1) && (data+20 > data_limit)) { + IP_MASQ_DEBUG(1-debug,"VDOlive: secondary packet too short %p %p\n", data, data_limit); + return 0; + } + + /* Adjust data pointers */ + /* + * I could check the complete protocol version tag + * in here however I am just going to look for the + * "VDO Live" tag in the hope that this part will + * remain constant even if the version changes + */ + if (ntohl(tagval) == 6) { + data += 24; + IP_MASQ_DEBUG(1-debug, "VDOlive: initial packet found\n"); + } else { + data += 8; + IP_MASQ_DEBUG(1-debug, "VDOlive: secondary packet found\n"); + } + + if (memcmp(data, "VDO Live", 8) != 0) { + IP_MASQ_DEBUG(1-debug,"VDOlive: did not find tag\n"); + return 0; + } + /* + * The port number is the next word after the tag. + * VDOlive encodes all of these values + * in 32 bit words, so in this case I am + * skipping the first 2 bytes of the next + * word to get to the relevant 16 bits + */ + data += 10; + + /* + * If we have not seen the port already, + * set the masquerading tunnel up + */ + if (!priv->origport) { + memcpy(&priv->origport, data, 2); + IP_MASQ_DEBUG(1-debug, "VDOlive: found port %d\n", ntohs(priv->origport)); + + /* Open up a tunnel */ + n_ms = ip_masq_new(IPPROTO_UDP, + maddr, 0, + ms->saddr, priv->origport, + ms->daddr, 0, + IP_MASQ_F_NO_DPORT); + + if (n_ms==NULL) { + ip_masq_put(n_ms); + IP_MASQ_DEBUG(1-debug, "VDOlive: unable to build UDP tunnel for %x:%x\n", ms->saddr, priv->origport); + /* Leave state as unset */ + priv->origport = 0; + return 0; + } + ip_masq_listen(n_ms); + + ip_masq_put(ms); + priv->masqport = n_ms->mport; + } else if (memcmp(data, &(priv->origport), 2)) { + IP_MASQ_DEBUG(1-debug, "VDOlive: ports do not match\n"); + /* Write the port in anyhow!!! */ + } + + /* + * Write masq port into packet + */ + memcpy(data, &(priv->masqport), 2); + IP_MASQ_DEBUG(1-debug, "VDOlive: rewrote port %d to %d, server %08X\n", ntohs(priv->origport), ntohs(priv->masqport), ms->saddr); + + /* + * Set state bit to make which bit has been done + */ + + priv->state |= (ntohl(tagval) == 6) ? 1 : 2; + + return 0; +} + + +struct ip_masq_app ip_masq_vdolive = { + NULL, /* next */ + "VDOlive", /* name */ + 0, /* type */ + 0, /* n_attach */ + masq_vdolive_init_1, /* ip_masq_init_1 */ + masq_vdolive_done_1, /* ip_masq_done_1 */ + masq_vdolive_out, /* pkt_out */ + NULL /* pkt_in */ +}; + +/* + * ip_masq_vdolive initialization + */ + +__initfunc(int ip_masq_vdolive_init(void)) +{ + int i, j; + + for (i=0; (i<MAX_MASQ_APP_PORTS); i++) { + if (ports[i]) { + if ((masq_incarnations[i] = kmalloc(sizeof(struct ip_masq_app), + GFP_KERNEL)) == NULL) + return -ENOMEM; + memcpy(masq_incarnations[i], &ip_masq_vdolive, sizeof(struct ip_masq_app)); + if ((j = register_ip_masq_app(masq_incarnations[i], + IPPROTO_TCP, + ports[i]))) { + return j; + } + IP_MASQ_DEBUG(1-debug, "RealAudio: loaded support on port[%d] = %d\n", i, ports[i]); + } else { + /* To be safe, force the incarnation table entry to NULL */ + masq_incarnations[i] = NULL; + } + } + return 0; +} + +/* + * ip_masq_vdolive fin. + */ + +int ip_masq_vdolive_done(void) +{ + int i, j, k; + + k=0; + for (i=0; (i<MAX_MASQ_APP_PORTS); i++) { + if (masq_incarnations[i]) { + if ((j = unregister_ip_masq_app(masq_incarnations[i]))) { + k = j; + } else { + kfree(masq_incarnations[i]); + masq_incarnations[i] = NULL; + IP_MASQ_DEBUG(1-debug,"VDOlive: unloaded support on port[%d] = %d\n", i, ports[i]); + } + } + } + return k; +} + + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + if (ip_masq_vdolive_init() != 0) + return -EIO; + return 0; +} + +void cleanup_module(void) +{ + if (ip_masq_vdolive_done() != 0) + IP_MASQ_DEBUG(1-debug, "ip_masq_vdolive: can't remove module"); +} + +#endif /* MODULE */ diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 106236c93..4ed7f7638 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -5,7 +5,7 @@ * * The Internet Protocol (IP) output module. * - * Version: $Id: ip_output.c,v 1.40 1997/10/12 17:01:48 kuznet Exp $ + * Version: $Id: ip_output.c,v 1.3 1997/12/16 05:37:41 ralf Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -70,6 +70,12 @@ #include <linux/netlink.h> #include <linux/ipsec.h> +/* + * Shall we try to damage output packets if routing dev changes? + */ + +int sysctl_ip_dynaddr = 0; + static void __inline__ ip_ll_header_reserve(struct sk_buff *skb) { struct rtable *rt = (struct rtable*)skb->dst; diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 080452dd3..2fd2b16ab 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -5,7 +5,7 @@ * * The IP to API glue. * - * Version: $Id: ip_sockglue.c,v 1.28 1997/11/17 17:36:08 kuznet Exp $ + * Version: $Id: ip_sockglue.c,v 1.3 1997/12/16 05:37:41 ralf Exp $ * * Authors: see ip.c * @@ -229,7 +229,10 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt int val=0,err; #if defined(CONFIG_IP_FIREWALL) || defined(CONFIG_IP_ACCT) struct ip_fw tmp_fw; -#endif +#endif +#ifdef CONFIG_IP_MASQUERADE + char masq_ctl[IP_FW_MASQCTL_MAX]; +#endif if(optlen>=sizeof(int)) { if(get_user(val, (int *) optval)) @@ -465,6 +468,20 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt return -err; /* -0 is 0 after all */ #endif +#ifdef CONFIG_IP_MASQUERADE + case IP_FW_MASQ_ADD: + case IP_FW_MASQ_DEL: + case IP_FW_MASQ_FLUSH: + if(!suser()) + return -EPERM; + if(optlen>sizeof(masq_ctl) || optlen<1) + return -EINVAL; + if(copy_from_user(masq_ctl,optval,optlen)) + return -EFAULT; + err=ip_masq_ctl(optname, masq_ctl,optlen); + return -err; /* -0 is 0 after all */ + +#endif #ifdef CONFIG_IP_ACCT case IP_ACCT_INSERT: case IP_ACCT_APPEND: diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 046c60beb..552b83664 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -5,7 +5,7 @@ * * ROUTE - implementation of the IP router. * - * Version: $Id: route.c,v 1.33 1997/10/24 17:16:08 kuznet Exp $ + * Version: $Id: route.c,v 1.3 1997/12/16 05:37:45 ralf Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -1000,6 +1000,8 @@ int ip_route_input_slow(struct sk_buff *skb, u32 daddr, u32 saddr, rth->u.dst.pmtu = res.fi->fib_mtu ? : out_dev->dev->mtu; rth->u.dst.window=res.fi->fib_window ? : 0; rth->u.dst.rtt = res.fi->fib_rtt ? : TCP_TIMEOUT_INIT; + rth->u.dst.rate_last = rth->u.dst.rate_tokens = 0; + if (FIB_RES_GW(res) && FIB_RES_NH(res).nh_scope == RT_SCOPE_LINK) rth->rt_gateway = FIB_RES_GW(res); @@ -1370,6 +1372,7 @@ make_route: rth->u.dst.window=0; rth->u.dst.rtt = TCP_TIMEOUT_INIT; } + rth->u.dst.rate_last = rth->u.dst.rate_tokens = 0; rth->rt_flags = flags; rth->rt_type = res.type; hash = rt_hash_code(daddr, saddr^(oif<<5), tos); diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index f49514171..637f2f933 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -1,7 +1,7 @@ /* * sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem. * - * $Id: sysctl_net_ipv4.c,v 1.21 1997/10/17 01:21:18 davem Exp $ + * $Id: sysctl_net_ipv4.c,v 1.5 1997/12/16 05:37:46 ralf Exp $ * * Begun April 1, 1996, Mike Shaver. * Added /proc/sys/net/ipv4 directory entry (empty =) ). [MS] @@ -47,6 +47,12 @@ extern int sysctl_ipfrag_low_thresh; extern int sysctl_ipfrag_high_thresh; extern int sysctl_ipfrag_time; +/* From ip_output.c */ +extern int sysctl_ip_dynaddr; + +/* From ip_masq.c */ +extern int sysctl_ip_masq_debug; + extern int sysctl_tcp_cong_avoidance; extern int sysctl_tcp_hoe_retransmits; extern int sysctl_tcp_sack; @@ -198,6 +204,12 @@ ctl_table ipv4_table[] = { &sysctl_ipfrag_high_thresh, sizeof(int), 0644, NULL, &proc_dointvec}, {NET_IPV4_IPFRAG_LOW_THRESH, "ipfrag_low_thresh", &sysctl_ipfrag_low_thresh, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_IPV4_IP_DYNADDR, "ip_dynaddr", + &sysctl_ip_dynaddr, sizeof(int), 0644, NULL, &proc_dointvec}, +#ifdef CONFIG_IP_MASQUERADE + {NET_IPV4_IP_MASQ_DEBUG, "ip_masq_debug", + &sysctl_ip_masq_debug, sizeof(int), 0644, NULL, &proc_dointvec}, +#endif {NET_IPV4_IPFRAG_TIME, "ipfrag_time", &sysctl_ipfrag_time, sizeof(int), 0644, NULL, &proc_dointvec_jiffies}, {NET_IPV4_TCP_MAX_KA_PROBES, "tcp_max_ka_probes", diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 10c7cd4f4..8c75bce3e 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_ipv4.c,v 1.74 1997/10/30 23:52:27 davem Exp $ + * Version: $Id: tcp_ipv4.c,v 1.76 1997/12/07 04:44:19 freitag Exp $ * * IPv4 specific functions * @@ -40,6 +40,7 @@ * Added tail drop and some other bugfixes. * Added new listen sematics (ifdefed by * NEW_LISTEN for now) + * Juan Jose Ciarlante: ip_dynaddr bits */ #include <linux/config.h> @@ -47,6 +48,7 @@ #include <linux/fcntl.h> #include <linux/random.h> #include <linux/ipsec.h> +#include <linux/inet.h> #include <net/icmp.h> #include <net/tcp.h> @@ -59,6 +61,7 @@ extern int sysctl_tcp_tsack; extern int sysctl_tcp_timestamps; extern int sysctl_tcp_window_scaling; extern int sysctl_tcp_syncookies; +extern int sysctl_ip_dynaddr; /* Check TCP sequence numbers in ICMP packets. */ #define ICMP_PARANOIA 1 @@ -846,31 +849,6 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len) return; } - /* pointless, because we have no way to retry when sk is locked. - But the socket should be really locked here for better interaction - with the socket layer. This needs to be solved for SMP - (I would prefer an "ICMP backlog"). - - tcp_v4_err is called only from bh, so that lock_sock is pointless, - even in commented form :-) --ANK - - Note "for SMP" ;) -AK - - Couple of notes about backlogging: - - error_queue could be used for it. - - could, but MUST NOT :-), because: - a) it is not clear, - who will process deferred messages. - b) ICMP is not reliable by design, so that you can safely - drop ICMP messages. Besides that, if ICMP really arrived - it is very unlikely, that socket is locked. --ANK - - I don't think it's unlikely that sk is locked. With the - open_request stuff there is much more stress on the main - LISTEN socket. I just want to make sure that all ICMP unreachables - destroy unneeded open_requests as reliable as possible (for - syn flood protection) -AK - */ tp = &sk->tp_pinfo.af_tcp; #ifdef ICMP_PARANOIA seq = ntohl(th->seq); @@ -1617,10 +1595,31 @@ int tcp_v4_rebuild_header(struct sock *sk, struct sk_buff *skb) struct iphdr *iph; struct tcphdr *th; int size; + int want_rewrite = sysctl_ip_dynaddr && sk->state == TCP_SYN_SENT; /* Check route */ rt = (struct rtable*)skb->dst; + + /* Force route checking if want_rewrite */ + if (want_rewrite) { + int tmp; + __u32 old_saddr = rt->rt_src; + + /* Query new route */ + tmp = ip_route_connect(&rt, rt->rt_dst, 0, + RT_TOS(sk->ip_tos)|(sk->localroute||0), + sk->bound_dev_if); + + /* Only useful if different source addrs */ + if (tmp == 0 || rt->rt_src != old_saddr ) { + dst_release(skb->dst); + skb->dst = &rt->u.dst; + } else { + want_rewrite = 0; + dst_release(&rt->u.dst); + } + } else if (rt->u.dst.obsolete) { int err; err = ip_route_output(&rt, rt->rt_dst, rt->rt_src, rt->key.tos, rt->key.oif); @@ -1640,6 +1639,50 @@ int tcp_v4_rebuild_header(struct sock *sk, struct sk_buff *skb) th = skb->h.th; size = skb->tail - skb->h.raw; + if (want_rewrite) { + __u32 new_saddr = rt->rt_src; + + /* + * Ouch!, this should not happen. + */ + if (!sk->saddr || !sk->rcv_saddr) { + printk(KERN_WARNING "tcp_v4_rebuild_header(): not valid sock addrs: saddr=%08lX rcv_saddr=%08lX\n", + ntohl(sk->saddr), + ntohl(sk->rcv_saddr)); + return 0; + } + + /* + * Maybe whe are in a skb chain loop and socket address has + * yet been 'damaged'. + */ + + if (new_saddr != sk->saddr) { + if (sysctl_ip_dynaddr > 1) { + printk(KERN_INFO "tcp_v4_rebuild_header(): shifting sk->saddr from %d.%d.%d.%d to %d.%d.%d.%d\n", + NIPQUAD(sk->saddr), + NIPQUAD(new_saddr)); + } + + sk->saddr = new_saddr; + sk->rcv_saddr = new_saddr; + /* sk->prot->rehash(sk); */ + tcp_v4_rehash(sk); + } + + if (new_saddr != iph->saddr) { + if (sysctl_ip_dynaddr > 1) { + printk(KERN_INFO "tcp_v4_rebuild_header(): shifting iph->saddr from %d.%d.%d.%d to %d.%d.%d.%d\n", + NIPQUAD(iph->saddr), + NIPQUAD(new_saddr)); + } + + iph->saddr = new_saddr; + ip_send_check(iph); + } + + } + return 0; } diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 5cb05d55b..1d804a864 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_timer.c,v 1.31 1997/11/05 08:14:01 freitag Exp $ + * Version: $Id: tcp_timer.c,v 1.4 1997/12/16 05:37:48 ralf Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -44,6 +44,8 @@ struct tcp_sl_timer tcp_slt_array[TCP_SLT_MAX] = { {ATOMIC_INIT(0), TCP_KEEPALIVE_PERIOD, 0, tcp_keepalive} /* KEEPALIVE */ }; +const char timer_bug_msg[] = KERN_DEBUG "tcpbug: unknown timer value\n"; + /* * Using different timers for retransmit, delayed acks and probes * We may wish use just one timer maintaining a list of expire jiffies @@ -112,45 +114,6 @@ void tcp_reset_xmit_timer(struct sock *sk, int what, unsigned long when) }; } -void tcp_clear_xmit_timer(struct sock *sk, int what) -{ - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; - - switch (what) { - case TIME_RETRANS: - del_timer(&tp->retransmit_timer); - break; - case TIME_DACK: - del_timer(&tp->delack_timer); - break; - case TIME_PROBE0: - del_timer(&tp->probe_timer); - break; - default: - printk(KERN_DEBUG "bug: unknown timer value\n"); - }; -} - -int tcp_timer_is_set(struct sock *sk, int what) -{ - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; - - switch (what) { - case TIME_RETRANS: - return tp->retransmit_timer.next != NULL; - break; - case TIME_DACK: - return tp->delack_timer.next != NULL; - break; - case TIME_PROBE0: - return tp->probe_timer.next != NULL; - break; - default: - printk(KERN_DEBUG "bug: unknown timer value\n"); - }; - return 0; -} - void tcp_clear_xmit_timers(struct sock *sk) { struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; @@ -160,6 +123,25 @@ void tcp_clear_xmit_timers(struct sock *sk) del_timer(&tp->probe_timer); } +static int tcp_write_err(struct sock *sk, int force) +{ + sk->err = sk->err_soft ? sk->err_soft : ETIMEDOUT; + sk->error_report(sk); + + tcp_clear_xmit_timers(sk); + + /* Time wait the socket. */ + if (!force && (1<<sk->state) & (TCPF_FIN_WAIT1|TCPF_FIN_WAIT2|TCPF_CLOSING)) { + tcp_set_state(sk,TCP_TIME_WAIT); + tcp_reset_msl_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + } else { + /* Clean up time. */ + tcp_set_state(sk, TCP_CLOSE); + return 0; + } + return 1; +} + /* * A write timeout has occurred. Process the after effects. BROKEN (badly) */ @@ -172,10 +154,7 @@ static int tcp_write_timeout(struct sock *sk) * Look for a 'soft' timeout. */ if ((sk->state == TCP_ESTABLISHED && - - /* Eric, what the heck is this doing?!?! */ - tp->retransmits && !(tp->retransmits & 7)) || - + tp->retransmits && (tp->retransmits % TCP_QUICK_TRIES) == 0) || (sk->state != TCP_ESTABLISHED && tp->retransmits > sysctl_tcp_retries1)) { /* Attempt to recover if arp has changed (unlikely!) or * a route has shifted (not supported prior to 1.3). @@ -185,42 +164,15 @@ static int tcp_write_timeout(struct sock *sk) /* Have we tried to SYN too many times (repent repent 8)) */ if(tp->retransmits > sysctl_tcp_syn_retries && sk->state==TCP_SYN_SENT) { - if(sk->err_soft) - sk->err=sk->err_soft; - else - sk->err=ETIMEDOUT; -#ifdef TCP_DEBUG - printk(KERN_DEBUG "syn timeout\n"); -#endif - - sk->error_report(sk); - tcp_clear_xmit_timers(sk); - tcp_statistics.TcpAttemptFails++; /* Is this right ??? - FIXME - */ - tcp_set_state(sk,TCP_CLOSE); + tcp_write_err(sk, 1); /* Don't FIN, we got nothing back */ return 0; } /* Has it gone just too far? */ - if (tp->retransmits > sysctl_tcp_retries2) { - if(sk->err_soft) - sk->err = sk->err_soft; - else - sk->err = ETIMEDOUT; - sk->error_report(sk); - - tcp_clear_xmit_timers(sk); + if (tp->retransmits > sysctl_tcp_retries2) + return tcp_write_err(sk, 0); - /* Time wait the socket. */ - if ((1<<sk->state) & (TCPF_FIN_WAIT1|TCPF_FIN_WAIT2|TCPF_CLOSING)) { - tcp_set_state(sk,TCP_TIME_WAIT); - tcp_reset_msl_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); - } else { - /* Clean up time. */ - tcp_set_state(sk, TCP_CLOSE); - return 0; - } - } return 1; } @@ -251,7 +203,8 @@ void tcp_probe_timer(unsigned long data) { } /* - * *WARNING* RFC 1122 forbids this + * *WARNING* RFC 1122 forbids this + * It doesn't AFAIK, because we kill the retransmit timer -AK * FIXME: We ought not to do it, Solaris 2.5 actually has fixing * this behaviour in Solaris down as a bug fix. [AC] */ diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 42a3df7ca..84586867f 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -5,7 +5,7 @@ * * The User Datagram Protocol (UDP). * - * Version: $Id: udp.c,v 1.44 1997/10/15 19:56:35 freitag Exp $ + * Version: $Id: udp.c,v 1.2 1997/12/16 05:37:48 ralf Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -53,6 +53,8 @@ * Last socket cache retained as it * does have a high hit rate. * Olaf Kirch : Don't linearise iovec on sendmsg. + * Andi Kleen : Some cleanups, cache destination entry + * for connect. * * * This program is free software; you can redistribute it and/or @@ -69,7 +71,7 @@ MUST pass IP options from IP -> application (OK) MUST allow application to specify IP options (OK) 4.1.3.3 (ICMP Messages) - MUST pass ICMP error messages to application (OK) + MUST pass ICMP error messages to application (OK -- except when SO_BSDCOMPAT is set) 4.1.3.4 (UDP Checksums) MUST provide facility for checksumming (OK) MAY allow application to control checksumming (OK) @@ -78,12 +80,10 @@ 4.1.3.5 (UDP Multihoming) MUST allow application to specify source address (OK) SHOULD be able to communicate the chosen src addr up to application - when application doesn't choose (NOT YET - doesn't seem to be in the BSD API) - [Does opening a SOCK_PACKET and snooping your output count 8)] + when application doesn't choose (DOES - use recvmsg cmsgs) 4.1.3.6 (Invalid Addresses) MUST discard invalid source addresses (OK -- done in the new routing code) - MUST only send datagrams with one of our addresses (NOT YET - ought to be OK ) - 950728 -- MS + MUST only send datagrams with one of our addresses (OK) */ #include <asm/system.h> @@ -459,8 +459,6 @@ static inline struct sock *udp_v4_mcast_next(struct sock *sk, return s; } -#define min(a,b) ((a)<(b)?(a):(b)) - /* * This routine is called by the ICMP module when it gets some * sort of error condition. If err < 0 then the socket should @@ -497,33 +495,27 @@ void udp_err(struct sk_buff *skb, unsigned char *dp, int len) kfree_skb(skb2, FREE_READ); } - if (type == ICMP_SOURCE_QUENCH) { -#if 0 /* FIXME: If you check the rest of the code, this is a NOP! - * Someone figure out what we were trying to be doing - * here. Besides, cong_window is a TCP thing and thus - * I moved it out of normal sock and into tcp_opt. - */ - /* Slow down! */ - if (sk->cong_window > 1) - sk->cong_window = sk->cong_window/2; -#endif + switch (type) { + case ICMP_SOURCE_QUENCH: return; - } - - if (type == ICMP_PARAMETERPROB) - { + case ICMP_PARAMETERPROB: sk->err = EPROTO; sk->error_report(sk); return; - } - - if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) - { - if (sk->ip_pmtudisc != IP_PMTUDISC_DONT) { - sk->err = EMSGSIZE; - sk->error_report(sk); + case ICMP_DEST_UNREACH: + if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */ + if (sk->ip_pmtudisc != IP_PMTUDISC_DONT) { + /* + * There should be really a way to pass the + * discovered MTU value back to the user (the + * ICMP layer did all the work for us) + */ + sk->err = EMSGSIZE; + sk->error_report(sk); + } + return; } - return; + break; } /* @@ -622,8 +614,8 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len) int ulen = len + sizeof(struct udphdr); struct ipcm_cookie ipc; struct udpfakehdr ufh; - struct rtable *rt; - int free = 0; + struct rtable *rt = NULL; + int free = 0, localroute = 0; u32 daddr; u8 tos; int err; @@ -635,12 +627,13 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len) * Check the flags. */ - if (msg->msg_flags&MSG_OOB) /* Mirror BSD error message compatibility */ + if (msg->msg_flags&MSG_OOB) /* Mirror BSD error message compatibility */ return -EOPNOTSUPP; if (msg->msg_flags&~(MSG_DONTROUTE|MSG_DONTWAIT)) return -EINVAL; + /* * Get and verify the address. */ @@ -660,11 +653,13 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len) ufh.uh.dest = usin->sin_port; if (ufh.uh.dest == 0) return -EINVAL; + /* XXX: is a one-behind cache for the dst_entry worth it? */ } else { if (sk->state != TCP_ESTABLISHED) return -EINVAL; ufh.daddr = sk->daddr; ufh.uh.dest = sk->dummy_th.dest; + rt = (struct rtable *)sk->dst_cache; } ipc.addr = sk->saddr; @@ -688,9 +683,13 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len) return -EINVAL; daddr = ipc.opt->faddr; } - tos = RT_TOS(sk->ip_tos) | (sk->localroute || (msg->msg_flags&MSG_DONTROUTE) || - (ipc.opt && ipc.opt->is_strictroute)); - + tos = RT_TOS(sk->ip_tos); + if (sk->localroute || (msg->msg_flags&MSG_DONTROUTE) || + (ipc.opt && ipc.opt->is_strictroute)) { + tos |= 1; + rt = NULL; /* sorry */ + } + if (MULTICAST(daddr)) { if (!ipc.oif) ipc.oif = sk->ip_mc_index; @@ -698,17 +697,15 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len) ufh.saddr = sk->ip_mc_addr; } - err = ip_route_output(&rt, daddr, ufh.saddr, tos, ipc.oif); - - if (err) { - if (free) kfree(ipc.opt); - return err; - } + if (rt == NULL) { + err = ip_route_output(&rt, daddr, ufh.saddr, tos, ipc.oif); + if (err) + goto out; + localroute = 1; - if (rt->rt_flags&RTCF_BROADCAST && !sk->broadcast) { - if (free) kfree(ipc.opt); - ip_rt_put(rt); - return -EACCES; + err = -EACCES; + if (rt->rt_flags&RTCF_BROADCAST && !sk->broadcast) + goto out; } ufh.saddr = rt->rt_src; @@ -727,15 +724,13 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len) /* violation above. -- MS */ lock_sock(sk); - if (sk->no_check) - err = ip_build_xmit(sk, udp_getfrag_nosum, &ufh, ulen, - &ipc, rt, msg->msg_flags); - else - err = ip_build_xmit(sk, udp_getfrag, &ufh, ulen, - &ipc, rt, msg->msg_flags); - ip_rt_put(rt); + err = ip_build_xmit(sk,sk->no_check ? udp_getfrag_nosum : udp_getfrag, + &ufh, ulen, &ipc, rt, msg->msg_flags); release_sock(sk); +out: + if (localroute) + ip_rt_put(rt); if (free) kfree(ipc.opt); if (!err) { @@ -776,7 +771,7 @@ int udp_ioctl(struct sock *sk, int cmd, unsigned long arg) * of this packet since that is all * that will be read. */ - amount = skb->len-sizeof(struct udphdr); + amount = skb->tail - skb->h.raw; } return put_user(amount, (int *)arg); } @@ -905,6 +900,9 @@ int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (usin->sin_family && usin->sin_family != AF_INET) return(-EAFNOSUPPORT); + dst_release(sk->dst_cache); + sk->dst_cache = NULL; + err = ip_route_connect(&rt, usin->sin_addr.s_addr, sk->saddr, sk->ip_tos|sk->localroute, sk->bound_dev_if); if (err) @@ -920,9 +918,11 @@ int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) sk->daddr = rt->rt_dst; sk->dummy_th.dest = usin->sin_port; sk->state = TCP_ESTABLISHED; + if(uh_cache_sk == sk) uh_cache_sk = NULL; - ip_rt_put(rt); + + sk->dst_cache = &rt->u.dst; return(0); } @@ -1074,13 +1074,6 @@ int udp_rcv(struct sk_buff *skb, unsigned short len) return(0); } - /* RFC1122 warning: According to 4.1.3.6, we MUST discard any */ - /* datagram which has an invalid source address, either here or */ - /* in IP. */ - /* Right now, IP isn't doing it, and neither is UDP. It's on the */ - /* FIXME list for IP, though, so I wouldn't worry about it. */ - /* (That's the Right Place to do it, IMHO.) -- MS */ - if (uh->check && (((skb->ip_summed==CHECKSUM_HW)&&udp_check(uh,len,saddr,daddr,skb->csum)) || ((skb->ip_summed==CHECKSUM_NONE) && |