summaryrefslogtreecommitdiffstats
path: root/net/ipv4
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4')
-rw-r--r--net/ipv4/Config.in6
-rw-r--r--net/ipv4/Makefile20
-rw-r--r--net/ipv4/arp.c5
-rw-r--r--net/ipv4/fib_frontend.c2
-rw-r--r--net/ipv4/icmp.c12
-rw-r--r--net/ipv4/ip_forward.c22
-rw-r--r--net/ipv4/ip_masq.c1488
-rw-r--r--net/ipv4/ip_masq_app.c62
-rw-r--r--net/ipv4/ip_masq_autofw.c427
-rw-r--r--net/ipv4/ip_masq_cuseeme.c261
-rw-r--r--net/ipv4/ip_masq_ftp.c244
-rw-r--r--net/ipv4/ip_masq_irc.c366
-rw-r--r--net/ipv4/ip_masq_mod.c316
-rw-r--r--net/ipv4/ip_masq_portfw.c461
-rw-r--r--net/ipv4/ip_masq_quake.c10
-rw-r--r--net/ipv4/ip_masq_raudio.c422
-rw-r--r--net/ipv4/ip_masq_vdolive.c291
-rw-r--r--net/ipv4/ip_output.c8
-rw-r--r--net/ipv4/ip_sockglue.c21
-rw-r--r--net/ipv4/route.c5
-rw-r--r--net/ipv4/sysctl_net_ipv4.c14
-rw-r--r--net/ipv4/tcp_ipv4.c95
-rw-r--r--net/ipv4/tcp_timer.c103
-rw-r--r--net/ipv4/udp.c117
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) &&