[2954] in linux-net channel archive
IP Masquerading patches
daemon@ATHENA.MIT.EDU (Nigel Metheringham)
Mon May 20 10:52:11 1996
From: Nigel Metheringham <Nigel.Metheringham@theplanet.net>
To: alan@lxorguk.ukuu.org.uk (Alan Cox)
Cc: linux-net@vger.rutgers.edu, masq@lists.indyramp.com
Date: Mon, 20 May 1996 15:00:46 +0100
This is a multipart MIME message.
--===_0_Mon_May_20_15:00:06_BST_1996
Content-Type: text/plain; charset=us-ascii
[Original message was missing the patch :-( ]
Alan,
} Can you generate me a single clean patch against .5. I've tried sorting the
} bits you gave me out with little success.
}
} Alan
}
Here is the merged patch against pre2.0.6 (I wasn't available over
w/e so never saw .5). I am posting it additionally to the linux-net
and masquerading lists because I want it beaten harder to make sure
it doesn't break too badly!
The included changes are:-
1. Real Audio proxy (OK least important is listed first!).
2. Checking of TCP/UDP data when demasquerading to ensure that
the rewritten packet is not corrupt when we receive it. The code
would previously have "fixed" a corrupt packet by fixing the
checksums
so making the final destination receive undetectable corrupt data
3. Handling of ICMP errors through the demasquerade path so that
MTU problems etc can be detected. This was partially in
pre2.0.6,
this version fixes all problems I know with it.
Bugs
~~~~
There are going to be bugs, I have made a number of changes and so
something may well have been broken. As time is short please can all
bugs be funnelled through me and I'll pass new patches to Alan for
kernel inclusion. If you can give me a patch obviously things can be
fixed faster!
TODO
~~~~
ICMP handling for forward direction through masquerade. Not sure if
this is needed - most internal networks are rather more simple in
configuration.
Checksumming in forward direction. Not done at present.
COmments
~~~~~~~~
RealAudio stuff has worked on all working sites I have tried. It did
fail on some sites, but in all cases these didn't work with a direct
net connection either - there just seem to be a lot of broken sites
out there!
I really would appreciate it if people can test this under conditions
that broke earlier masq code - especially fragmenting links with odd
MTUs. I have tested this on a test rig but a test config isn't real
life!
Unlike previous patches, this one is a unidiff.
Nigel.
--===_0_Mon_May_20_15:00:06_BST_1996
Content-Type: application/x-patch
Content-Description: masq_merged_206.patch
Index: linux/net/ipv4/Makefile
diff -u linux/net/ipv4/Makefile:1.1.1.1 linux/net/ipv4/Makefile:1.2
--- linux/net/ipv4/Makefile:1.1.1.1 Wed May 15 10:20:23 1996
+++ linux/net/ipv4/Makefile Thu May 16 18:39:31 1996
@@ -40,7 +40,7 @@
ifeq ($(CONFIG_IP_MASQUERADE),y)
IPV4_OBJS += ip_masq.o ip_masq_app.o
-M_OBJS += ip_masq_ftp.o ip_masq_irc.o
+M_OBJS += ip_masq_ftp.o ip_masq_irc.o ip_masq_raudio.o
endif
ifeq ($(CONFIG_IP_ALIAS),y)
Index: linux/net/ipv4/ip_input.c
diff -u linux/net/ipv4/ip_input.c:1.1.1.1 linux/net/ipv4/ip_input.c:1.3
--- linux/net/ipv4/ip_input.c:1.1.1.1 Wed May 15 10:20:28 1996
+++ linux/net/ipv4/ip_input.c Thu May 16 13:45:01 1996
@@ -208,6 +208,7 @@
int brd=IS_MYADDR;
struct options * opt = NULL;
int is_frag=0;
+ int ret;
__u32 daddr;
#ifdef CONFIG_FIREWALL
@@ -422,19 +423,6 @@
}
#endif
-#ifdef CONFIG_IP_MASQUERADE
- /*
- * Do we need to de-masquerade this fragment?
- */
- if (ip_fw_demasquerade(&skb,dev))
- {
- struct iphdr *iph=skb->h.iph;
- if (ip_forward(skb, dev, is_frag|IPFWD_MASQUERADED, iph->daddr))
- kfree_skb(skb, FREE_WRITE);
- return(0);
- }
-#endif
-
/*
* Reassemble IP fragments.
*/
@@ -447,16 +435,27 @@
return 0;
skb->dev = dev;
iph=skb->h.iph;
+ }
+
#ifdef CONFIG_IP_MASQUERADE
- if (ip_fw_demasquerade(&skb,dev))
+ /*
+ * Do we need to de-masquerade this packet?
+ */
+ if ((ret = ip_fw_demasquerade(&skb,dev)) != 0) {
+ if (ret < 0)
+ {
+ kfree_skb(skb, FREE_WRITE);
+ return(0);
+ }
+ else
{
struct iphdr *iph=skb->h.iph;
if (ip_forward(skb, dev, IPFWD_MASQUERADED, iph->daddr))
kfree_skb(skb, FREE_WRITE);
- return 0;
+ return(0);
}
-#endif
}
+#endif
/*
* Point into the IP datagram, just past the header.
Index: linux/net/ipv4/ip_masq.c
diff -u linux/net/ipv4/ip_masq.c:1.1.1.2 linux/net/ipv4/ip_masq.c:1.17
--- linux/net/ipv4/ip_masq.c:1.1.1.2 Mon May 20 10:13:10 1996
+++ linux/net/ipv4/ip_masq.c Thu May 16 18:34:08 1996
@@ -12,7 +12,9 @@
* 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.
- * Nigel Metheringham : ICMP handling.
+ * Nigel Metheringham : Added ICMP handling for demasquerade
+ * Nigel Metheringham : Checksum checking of masqueraded data
+ *
*
*/
@@ -26,6 +28,7 @@
#include <linux/proc_fs.h>
#include <linux/in.h>
#include <linux/ip.h>
+#include <linux/inet.h>
#include <net/protocol.h>
#include <net/icmp.h>
#include <net/tcp.h>
@@ -458,6 +461,8 @@
/*
* We can only masquerade protocols with ports...
+ * [TODO]
+ * We may need to consider masq-ing some ICMP related to masq-ed protocols
*/
if (iph->protocol!=IPPROTO_UDP && iph->protocol!=IPPROTO_TCP)
@@ -575,8 +580,9 @@
struct iphdr *iph = skb->h.iph;
struct icmphdr *icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
struct iphdr *ciph; /* The ip header contained within the ICMP */
- __u16 *portptr; /* port numbers from TCP/UDP contained header */
+ __u16 *pptr; /* port numbers from TCP/UDP contained header */
struct ip_masq *ms;
+ unsigned short len = ntohs(iph->tot_len) - (iph->ihl * 4);
#ifdef DEBUG_CONFIG_IP_MASQUERADE
printk("Incoming ICMP (%d) %lX -> %lX\n",
@@ -600,19 +606,27 @@
* Find the ports involved - remember this packet was
* *outgoing* so the ports are reversed (and addresses)
*/
- portptr = (__u16 *)&(((char *)ciph)[ciph->ihl*4]);
- if (ntohs(portptr[0]) < PORT_MASQ_BEGIN ||
- ntohs(portptr[0]) > PORT_MASQ_END)
+ pptr = (__u16 *)&(((char *)ciph)[ciph->ihl*4]);
+ if (ntohs(pptr[0]) < PORT_MASQ_BEGIN ||
+ ntohs(pptr[0]) > PORT_MASQ_END)
return 0;
+ /* Ensure the checksum is correct */
+ if (ip_compute_csum((unsigned char *) icmph, len))
+ {
+ /* Failed checksum! */
+ printk(KERN_INFO "MASQ: ICMP: failed checksum from %s!\n", in_ntoa(iph->saddr));
+ return(-1);
+ }
+
#ifdef DEBUG_CONFIG_IP_MASQUERADE
printk("Handling ICMP for %lX:%X -> %lX:%X\n",
- ntohl(ciph->saddr), ntohs(portptr[0]),
- ntohl(ciph->daddr), ntohs(portptr[1]));
+ 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, portptr[1], ciph->saddr, portptr[0]);
+ ms = ip_masq_in_get_2(ciph->protocol, ciph->daddr, pptr[1], ciph->saddr, pptr[0]);
if (ms == NULL)
return 0;
@@ -627,17 +641,16 @@
ip_send_check(ciph);
/* the TCP/UDP source port - cannot redo check */
- portptr[0] = ms->sport;
+ pptr[0] = ms->sport;
/* And finally the ICMP checksum */
icmph->checksum = 0;
- icmph->checksum = ip_compute_csum((unsigned char *) icmph,
- skb->len - sizeof(struct iphdr));
+ icmph->checksum = ip_compute_csum((unsigned char *) icmph, len);
#ifdef DEBUG_CONFIG_IP_MASQUERADE
printk("Rewrote ICMP to %lX:%X -> %lX:%X\n",
- ntohl(ciph->saddr), ntohs(portptr[0]),
- ntohl(ciph->daddr), ntohs(portptr[1]));
+ ntohl(ciph->saddr), ntohs(pptr[0]),
+ ntohl(ciph->daddr), ntohs(pptr[1]));
#endif
return 1;
@@ -659,30 +672,44 @@
struct iphdr *iph = skb->h.iph;
__u16 *portptr;
struct ip_masq *ms;
- unsigned short frag;
-
- if (iph->protocol!=IPPROTO_UDP && iph->protocol!=IPPROTO_TCP
- && iph->protocol!=IPPROTO_ICMP)
- return 0;
-
- /*
- * Toss fragments, since we handle them in ip_rcv()
- */
+ unsigned short len;
- frag = ntohs(iph->frag_off);
+ switch (iph->protocol) {
+ case IPPROTO_ICMP:
+ return(ip_fw_demasq_icmp(skb_p, dev));
+ case IPPROTO_TCP:
+ 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)
+ return 0;
+ /* Check that the checksum is OK */
+ len = ntohs(iph->tot_len) - (iph->ihl * 4);
+ if ((iph->protocol == IPPROTO_UDP) && (portptr[3] == 0))
+ /* No UDP checksum */
+ break;
- if ((frag & IP_MF) != 0 || (frag & IP_OFFSET) != 0)
- {
+ switch (skb->ip_summed)
+ {
+ case CHECKSUM_NONE:
+ skb->csum = csum_partial((char *)portptr, len, 0);
+ case CHECKSUM_HW:
+ 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));
+ return -1;
+ }
+ default:
+ /* CHECKSUM_UNNECESSARY */
+ }
+ break;
+ default:
return 0;
}
- if (iph->protocol == IPPROTO_ICMP)
- return ip_fw_demasq_icmp(skb_p, dev);
-
- portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]);
- if (ntohs(portptr[1]) < PORT_MASQ_BEGIN ||
- ntohs(portptr[1]) > PORT_MASQ_END)
- return 0;
#ifdef DEBUG_CONFIG_IP_MASQUERADE
printk("Incoming %s %lX:%X -> %lX:%X\n",
@@ -698,8 +725,6 @@
if (ms != NULL)
{
- int size;
-
/*
* Set dport if not defined yet.
*/
@@ -720,7 +745,6 @@
ntohs(ms->daddr));
#endif
}
- size = skb->len - ((unsigned char *)portptr - skb->h.raw);
iph->daddr = ms->saddr;
portptr[1] = ms->sport;
@@ -738,7 +762,7 @@
skb = *skb_p;
iph = skb->h.iph;
portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]);
- size = skb->len - ((unsigned char *)portptr-skb->h.raw);
+ len = ntohs(iph->tot_len) - (iph->ihl * 4);
}
/*
@@ -750,7 +774,7 @@
*/
if (iph->protocol==IPPROTO_UDP)
{
- recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,size);
+ recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,len);
ip_masq_set_expire(ms, 0);
ip_masq_set_expire(ms, ip_masq_expire->udp_timeout);
}
@@ -758,8 +782,8 @@
{
struct tcphdr *th;
skb->csum = csum_partial((void *)(((struct tcphdr *)portptr) + 1),
- size - sizeof(struct tcphdr), 0);
- tcp_send_check((struct tcphdr *)portptr,iph->saddr,iph->daddr,size,skb);
+ len - sizeof(struct tcphdr), 0);
+ tcp_send_check((struct tcphdr *)portptr,iph->saddr,iph->daddr,len,skb);
/* Check if TCP RST */
th = (struct tcphdr *)portptr;
if (th->rst)
Index: linux/net/ipv4/ip_masq_app.c
diff -u linux/net/ipv4/ip_masq_app.c:1.1.1.1 linux/net/ipv4/ip_masq_app.c:1.2
--- linux/net/ipv4/ip_masq_app.c:1.1.1.1 Wed May 15 10:20:29 1996
+++ linux/net/ipv4/ip_masq_app.c Thu May 16 14:37:10 1996
@@ -438,24 +438,25 @@
struct ip_masq_app * mapp;
unsigned idx;
- if (offset < 22)
- len=sprintf(buffer,"%-21s\n", "prot port n_attach");
- pos = 22;
+ if (offset < 40)
+ len=sprintf(buffer,"%-39s\n", "prot port n_attach name");
+ pos = 40;
for (idx=0 ; idx < IP_MASQ_APP_TAB_SIZE; idx++)
for (mapp = ip_masq_app_base[idx]; mapp ; mapp = mapp->next) {
/*
* If you change the length of this sprintf, then all
* the length calculations need fixing too!
- * Line length = 22 (3 + 2 + 7 + 1 + 7 + 1 + 1)
+ * Line length = 40 (3 + 2 + 7 + 1 + 7 + 1 + 2 + 17)
*/
- pos += 22;
+ pos += 40;
if (pos < offset)
continue;
- len += sprintf(buffer+len, "%-3s %-7u %-7d \n",
+ len += sprintf(buffer+len, "%-3s %-7u %-7d %-17s\n",
masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)),
- IP_MASQ_APP_PORT(mapp->type), mapp->n_attach);
+ IP_MASQ_APP_PORT(mapp->type), mapp->n_attach,
+ mapp->name);
if(len >= length)
goto done;
Index: linux/net/ipv4/ip_masq_raudio.c
diff -u /dev/null linux/net/ipv4/ip_masq_raudio.c:1.3
--- /dev/null Mon May 20 14:53:48 1996
+++ linux/net/ipv4/ip_masq_raudio.c Mon May 20 14:24:26 1996
@@ -0,0 +1,232 @@
+/*
+ * IP_MASQ_RAUDIO - Real Audio masquerading module
+ *
+ *
+ * Version: @(#)$Id: ip_masq_raudio.c,v 1.3 1996/05/20 13:24:26 nigel Exp $
+ *
+ * Author: Nigel Metheringham
+ * [strongly based on ftp module by Juan Jose Ciarlante & Wouter Gadeyne]
+ * [Real Audio information taken from Progressive Networks firewall docs]
+ *
+ *
+ *
+ *
+ * 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.
+ *
+ *
+ * Limitations
+ * The IP Masquerading proxies at present do not have access to a processed
+ * data stream. Hence for a protocol like the Real Audio control protocol,
+ * which depends on knowing where you are in the data stream, you either
+ * to keep a *lot* of state in your proxy, or you cheat and simplify the
+ * problem [needless to say I did the latter].
+ *
+ * This proxy only handles data in the first packet. Everything else is
+ * passed transparently. This means it should work under all normal
+ * circumstances, but it could be fooled by new data formats or a
+ * malicious application!
+ *
+ * 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
+ * crossing the control link to keep it open even if a long audio
+ * piece is playing.
+ *
+ */
+
+#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 <net/protocol.h>
+#include <net/tcp.h>
+#include <net/ip_masq.h>
+
+#ifndef DEBUG_CONFIG_IP_MASQ_RAUDIO
+#define DEBUG_CONFIG_IP_MASQ_RAUDIO 0
+#endif
+
+struct raudio_priv_data {
+ /* Associated data connection - setup but not used at present */
+ struct ip_masq *data_conn;
+ /* Have we seen and performed setup */
+ short seen_start;
+};
+
+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)
+ printk(KERN_INFO "RealAudio: No memory for application data\n");
+ else
+ {
+ struct raudio_priv_data *priv =
+ (struct raudio_priv_data *)ms->app_data;
+ priv->seen_start = 0;
+ priv->data_conn = NULL;
+ }
+ return 0;
+}
+
+static int
+masq_raudio_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 raudio_priv_data));
+ return 0;
+}
+
+int
+masq_raudio_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, struct device *dev)
+{
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ struct tcphdr *th;
+ 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 *)ms->app_data;
+
+ /* Everything running correctly already */
+ if (priv && priv->seen_start)
+ return 0;
+
+ skb = *skb_p;
+ iph = skb->h.iph;
+ th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
+ data = (char *)&th[1];
+
+ data_limit = skb->h.raw + skb->len - 18;
+
+ /* 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
+ return(0);
+ }
+ data += 3;
+ memcpy(&version, data, 2);
+
+#if DEBUG_CONFIG_IP_MASQ_RAUDIO
+ printk("RealAudio: initial seen - protocol version %d\n",
+ ntohs(version));
+#endif
+ if (priv)
+ priv->seen_start = 1;
+
+ if (ntohs(version) >= 256)
+ {
+ printk(KERN_INFO "RealAudio: version (%d) not supported\n",
+ ntohs(version));
+ return 0;
+ }
+
+ data += 2;
+ while (data < 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",
+ ntohs(msg_id), ntohs(msg_len));
+#endif
+ p = data;
+ data += ntohs(msg_len);
+ if (data > data_limit)
+ {
+ 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 */
+ memcpy(&udp_port, p, 2);
+ n_ms = ip_masq_new(dev, IPPROTO_UDP,
+ ms->saddr, udp_port,
+ ms->daddr, 0,
+ IP_MASQ_F_NO_DPORT);
+
+ if (n_ms==NULL)
+ return 0;
+
+ 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);
+
+ /* Make ref in application data to data connection */
+ if (priv)
+ priv->data_conn = n_ms;
+
+ /*
+ * There is nothing else useful we can do
+ * Maybe a development could do more, but for now
+ * we exit gracefully!
+ */
+ return 0;
+
+ } else if (ntohs(msg_id) == 0)
+ return 0;
+ }
+ return 0;
+}
+
+struct ip_masq_app ip_masq_raudio = {
+ NULL, /* next */
+ "RealAudio", /* name */
+ 0, /* type */
+ 0, /* n_attach */
+ masq_raudio_init_1, /* ip_masq_init_1 */
+ masq_raudio_done_1, /* ip_masq_done_1 */
+ masq_raudio_out, /* pkt_out */
+ NULL /* pkt_in */
+};
+
+/*
+ * ip_masq_raudio initialization
+ */
+
+int ip_masq_raudio_init(void)
+{
+ return register_ip_masq_app(&ip_masq_raudio, IPPROTO_TCP, 7070);
+}
+
+/*
+ * ip_masq_raudio fin.
+ */
+
+int ip_masq_raudio_done(void)
+{
+ return unregister_ip_masq_app(&ip_masq_raudio);
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+ if (ip_masq_raudio_init() != 0)
+ return -EIO;
+ register_symtab(0);
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ if (ip_masq_raudio_done() != 0)
+ printk("ip_masq_raudio: can't remove module");
+}
+
+#endif /* MODULE */
--===_0_Mon_May_20_15:00:06_BST_1996
Content-Type: text/plain; charset=us-ascii
[ Nigel.Metheringham@theplanet.net - Unix Applications Engineer ]
[ *Views expressed here are personal and not supported by PLAnet* ]
[ PLAnet Online : The White House Tel : +44 113 2345566 x 612 ]
[ Melbourne Street, Leeds LS2 7PS UK. Fax : +44 113 2345656 ]
--===_0_Mon_May_20_15:00:06_BST_1996
Content-Type: text/plain; charset=us-ascii
[ Nigel.Metheringham@theplanet.net - Unix Applications Engineer ]
[ *Views expressed here are personal and not supported by PLAnet* ]
[ PLAnet Online : The White House Tel : +44 113 2345566 x 612 ]
[ Melbourne Street, Leeds LS2 7PS UK. Fax : +44 113 2345656 ]
--===_0_Mon_May_20_15:00:06_BST_1996--