diff -Naur linux/Documentation/Configure.help linux-2.4.0-test8/Documentation/Configure.help
--- linux/Documentation/Configure.help Tue Sep 5 19:57:51 2000
+++ linux-2.4.0-test8/Documentation/Configure.help Sun Sep 17 15:34:59 2000
@@ -4557,6 +4557,30 @@
This is a Logical Link Layer protocol used for X.25 connections over
Ethernet, using ordinary Ethernet cards.
+
+Frame Diverter (EXPERIMENTAL)
+CONFIG_NET_DIVERT
+ The Frame Diverter allows you to divert packets from the
+ network, that are not aimed at the interface receiving it (in
+ promisc. mode). Typically, a Linux box setup as an ethernet bridge
+ with the Frames Diverter on, can do some *really* transparent www
+ caching using a Squid proxy for example.
+
+ This is very usefull when you don't want to change your router's
+ config (or if you simply don't have access to it).
+
+ The other possible usages of diverting Ethernet Frames are numberous:
+ - reroute smtp traffic to another interface
+ - traffic-shape certain network streams
+ - transparently proxy smtp connections
+ - etc...
+
+ For more informations, please refer to:
+ http://www.freshmeat.net/projects/etherdivert
+ http://perso.wanadoo.fr/magpie/EtherDivert.html
+
+ If unsure, say N
+
802.1d Ethernet Bridging
CONFIG_BRIDGE
If you say Y here, then your Linux box will be able to act as an
diff -Naur linux/drivers/net/Space.c linux-2.4.0-test8/drivers/net/Space.c
--- linux/drivers/net/Space.c Wed Aug 23 16:30:13 2000
+++ linux-2.4.0-test8/drivers/net/Space.c Sun Sep 17 15:39:25 2000
@@ -33,6 +33,7 @@
#include
#include
#include
+#include
#define NEXT_DEV NULL
@@ -136,14 +137,35 @@
{
struct devprobe *p = plist;
unsigned long base_addr = dev->base_addr;
+#ifdef CONFIG_NET_DIVERT
+ int ret;
+#endif /* CONFIG_NET_DIVERT */
while (p->probe != NULL) {
if (base_addr && p->probe(dev) == 0) /* probe given addr */
+#ifdef CONFIG_NET_DIVERT
+ {
+ ret=alloc_divert_blk(dev);
+ if (ret)
+ return ret;
return 0;
+ }
+#else
+ return 0;
+#endif /* CONFIG_NET_DIVERT */
else if (p->status == 0) { /* has autoprobe failed yet? */
p->status = p->probe(dev); /* no, try autoprobe */
if (p->status == 0)
+#ifdef CONFIG_NET_DIVERT
+ {
+ ret=alloc_divert_blk(dev);
+ if (ret)
+ return ret;
+ return 0;
+ }
+#else
return 0;
+#endif /* CONFIG_NET_DIVERT */
}
p++;
}
diff -Naur linux/go linux-2.4.0-test8/go
--- linux/go Thu Jan 1 00:00:00 1970
+++ linux-2.4.0-test8/go Wed Sep 20 17:23:59 2000
@@ -0,0 +1,5 @@
+#!/bin/bash
+make bzImage && make modules && make modules_install
+cp System.map /boot/System.map-2.4.0-test8
+cp arch/i386/boot/bzImage /boot/vmlinuz-2.4.0-test8
+lilo
diff -Naur linux/include/linux/netdevice.h linux-2.4.0-test8/include/linux/netdevice.h
--- linux/include/linux/netdevice.h Fri Sep 8 19:53:53 2000
+++ linux-2.4.0-test8/include/linux/netdevice.h Wed Sep 20 17:28:02 2000
@@ -28,6 +28,7 @@
#include
#include
#include
+#include
#include
#include
@@ -382,6 +383,10 @@
rwlock_t fastpath_lock;
struct dst_entry *fastpath[NETDEV_FASTROUTE_HMASK+1];
#endif
+#ifdef CONFIG_NET_DIVERT
+ /* this will get initialized at each interface type init routine */
+ struct divert_blk *divert;
+#endif /* CONFIG_NET_DIVERT */
};
diff -Naur linux/include/linux/sockios.h linux-2.4.0-test8/include/linux/sockios.h
--- linux/include/linux/sockios.h Sat Jan 22 19:54:57 2000
+++ linux-2.4.0-test8/include/linux/sockios.h Sun Sep 17 15:49:53 2000
@@ -72,6 +72,9 @@
#define SIOCGIFTXQLEN 0x8942 /* Get the tx queue length */
#define SIOCSIFTXQLEN 0x8943 /* Set the tx queue length */
+#define SIOCGIFDIVERT 0x8944 /* Frame diversion support */
+#define SIOCSIFDIVERT 0x8945 /* Set frame diversion options */
+
/* ARP cache control calls. */
/* 0x8950 - 0x8952 * obsolete calls, don't re-use */
diff -Naur linux/include/linux/sysctl.h linux-2.4.0-test8/include/linux/sysctl.h
--- linux/include/linux/sysctl.h Thu Aug 10 20:01:26 2000
+++ linux-2.4.0-test8/include/linux/sysctl.h Sun Sep 17 15:50:50 2000
@@ -186,7 +186,8 @@
NET_CORE_MSG_COST=8,
NET_CORE_MSG_BURST=9,
NET_CORE_OPTMEM_MAX=10,
- NET_CORE_HOT_LIST_LENGTH=11
+ NET_CORE_HOT_LIST_LENGTH=11,
+ NET_CORE_DIVERT_VERSION=12
};
/* /proc/sys/net/ethernet */
diff -Naur linux/include/net/divert.h linux-2.4.0-test8/include/net/divert.h
--- linux/include/net/divert.h Thu Jan 1 00:00:00 1970
+++ linux-2.4.0-test8/include/net/divert.h Wed Sep 20 17:28:02 2000
@@ -0,0 +1,131 @@
+/*
+ * Frame Diversion, Benoit Locher
+ *
+ * Changes:
+ * 06/09/2000 BL: initial version
+ *
+ */
+
+#ifndef _LINUX_DIVERT_H
+#define _LINUX_DIVERT_H
+
+#define MAX_DIVERT_PORTS 8 /* Max number of ports to divert (tcp, udp) */
+
+/* Divertable protocols */
+#define DIVERT_PROTO_NONE 0x0000
+#define DIVERT_PROTO_IP 0x0001
+#define DIVERT_PROTO_ICMP 0x0002
+#define DIVERT_PROTO_TCP 0x0004
+#define DIVERT_PROTO_UDP 0x0008
+
+#ifdef __KERNEL__
+ #define S16 s16
+ #define U16 u16
+ #define S32 s32
+ #define U32 u32
+ #define S64 s64
+ #define U64 u64
+#else
+ #define S16 __s16
+ #define U16 __u16
+ #define S32 __s32
+ #define U32 __u32
+ #define S64 __s64
+ #define U64 __u64
+#endif
+
+/*
+ * This is an Ethernet Frame Diverter option block
+ */
+struct divert_blk
+{
+ int divert; /* are we active */
+ unsigned int protos; /* protocols */
+ U16 tcp_dst[MAX_DIVERT_PORTS]; /* specific tcp dst ports to divert */
+ U16 tcp_src[MAX_DIVERT_PORTS]; /* specific tcp src ports to divert */
+ U16 udp_dst[MAX_DIVERT_PORTS]; /* specific udp dst ports to divert */
+ U16 udp_src[MAX_DIVERT_PORTS]; /* specific udp src ports to divert */
+};
+
+/*
+ * Diversion control block, for configuration with the userspace tool
+ * divert
+ */
+
+typedef union _divert_cf_arg
+{
+ S16 int16;
+ U16 uint16;
+ S32 int32;
+ U32 uint32;
+ S64 int64;
+ U64 uint64;
+ void *ptr;
+} divert_cf_arg;
+
+
+struct divert_cf
+{
+ int cmd; /* Command */
+ divert_cf_arg arg1,
+ arg2,
+ arg3;
+ int dev_index; /* device index (eth0=0, etc...) */
+};
+
+
+/* Diversion commands */
+#define DIVCMD_DIVERT 1 /* ENABLE/DISABLE diversion */
+#define DIVCMD_IP 2 /* ENABLE/DISABLE whold IP diversion */
+#define DIVCMD_TCP 3 /* ENABLE/DISABLE whold TCP diversion */
+#define DIVCMD_TCPDST 4 /* ADD/REMOVE TCP DST port for diversion */
+#define DIVCMD_TCPSRC 5 /* ADD/REMOVE TCP SRC port for diversion */
+#define DIVCMD_UDP 6 /* ENABLE/DISABLE whole UDP diversion */
+#define DIVCMD_UDPDST 7 /* ADD/REMOVE UDP DST port for diversion */
+#define DIVCMD_UDPSRC 8 /* ADD/REMOVE UDP SRC port for diversion */
+#define DIVCMD_ICMP 9 /* ENABLE/DISABLE whole ICMP diversion */
+#define DIVCMD_GETSTATUS 10 /* GET the status of the diverter */
+#define DIVCMD_RESET 11 /* Reset the diverter on the specified dev */
+#define DIVCMD_GETVERSION 12 /* Retrieve the diverter code version (char[32]) */
+
+/* General syntax of the commands:
+ *
+ * DIVCMD_xxxxxx(arg1, arg2, arg3, dev_index)
+ *
+ * SIOCSIFDIVERT:
+ * DIVCMD_DIVERT(DIVARG1_ENABLE|DIVARG1_DISABLE, , ,ifindex)
+ * DIVCMD_IP(DIVARG1_ENABLE|DIVARG1_DISABLE, , , ifindex)
+ * DIVCMD_TCP(DIVARG1_ENABLE|DIVARG1_DISABLE, , , ifindex)
+ * DIVCMD_TCPDST(DIVARG1_ADD|DIVARG1_REMOVE, port, , ifindex)
+ * DIVCMD_TCPSRC(DIVARG1_ADD|DIVARG1_REMOVE, port, , ifindex)
+ * DIVCMD_UDP(DIVARG1_ENABLE|DIVARG1_DISABLE, , , ifindex)
+ * DIVCMD_UDPDST(DIVARG1_ADD|DIVARG1_REMOVE, port, , ifindex)
+ * DIVCMD_UDPSRC(DIVARG1_ADD|DIVARG1_REMOVE, port, , ifindex)
+ * DIVCMD_ICMP(DIVARG1_ENABLE|DIVARG1_DISABLE, , , ifindex)
+ * DIVCMD_RESET(, , , ifindex)
+ *
+ * SIOGIFDIVERT:
+ * DIVCMD_GETSTATUS(divert_blk, , , ifindex)
+ * DIVCMD_GETVERSION(string[3])
+ */
+
+
+/* Possible values for arg1 */
+#define DIVARG1_ENABLE 0 /* ENABLE something */
+#define DIVARG1_DISABLE 1 /* DISABLE something */
+#define DIVARG1_ADD 2 /* ADD something */
+#define DIVARG1_REMOVE 3 /* REMOVE something */
+
+
+#ifdef __KERNEL__
+
+/* diverter functions */
+#include
+int alloc_divert_blk(struct net_device *);
+void free_divert_blk(struct net_device *);
+int divert_ioctl(unsigned int cmd, struct divert_cf *arg);
+void divert_frame(struct sk_buff *skb);
+
+#endif __KERNEL__
+
+#endif /* _LINUX_DIVERT_H */
diff -Naur linux/net/Config.in linux-2.4.0-test8/net/Config.in
--- linux/net/Config.in Mon Jun 19 20:45:51 2000
+++ linux-2.4.0-test8/net/Config.in Sun Sep 17 15:52:16 2000
@@ -63,6 +63,7 @@
tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC
+ bool 'Frame Diverter (EXPERIMENTAL)' CONFIG_NET_DIVERT
# if [ "$CONFIG_LLC" = "y" ]; then
# bool ' Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI
# fi
diff -Naur linux/net/core/Makefile linux-2.4.0-test8/net/core/Makefile
--- linux/net/core/Makefile Mon Aug 23 16:56:58 1999
+++ linux-2.4.0-test8/net/core/Makefile Sun Sep 17 15:54:16 2000
@@ -29,6 +29,10 @@
OX_OBJS += netfilter.o
endif
+ifeq ($(CONFIG_NET_DIVERT),y)
+O_OBJS += dv.o
+endif
+
endif
ifdef CONFIG_NET_PROFILE
diff -Naur linux/net/core/dev.c linux-2.4.0-test8/net/core/dev.c
--- linux/net/core/dev.c Thu Sep 7 15:32:01 2000
+++ linux-2.4.0-test8/net/core/dev.c Sun Sep 17 18:16:36 2000
@@ -85,6 +85,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -1206,6 +1207,17 @@
}
+#ifdef CONFIG_NET_DIVERT
+static inline void handle_diverter(struct sk_buff *skb)
+{
+ /* if diversion is supported on device, then divert */
+ if (skb->dev->divert && skb->dev->divert->divert)
+ divert_frame(skb);
+ return;
+}
+#endif /* CONFIG_NET_DIVERT */
+
+
static void net_rx_action(struct softirq_action *h)
{
int this_cpu = smp_processor_id();
@@ -1256,6 +1268,15 @@
}
}
+#ifdef CONFIG_NET_DIVERT
+ if (skb->dev->divert && skb->dev->divert->divert)
+ {
+ handle_diverter(skb);
+ /*continue;*/
+ }
+#endif /* CONFIG_NET_DIVERT */
+
+
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
if (skb->dev->br_port != NULL &&
br_handle_frame_hook != NULL) {
@@ -2166,6 +2187,10 @@
{
struct net_device *d, **dp;
+#ifdef CONFIG_NET_DIVERT
+ int ret;
+#endif
+
spin_lock_init(&dev->queue_lock);
spin_lock_init(&dev->xmit_lock);
dev->xmit_lock_owner = -1;
@@ -2199,6 +2224,12 @@
dev_hold(dev);
write_unlock_bh(&dev_base_lock);
+#ifdef CONFIG_NET_DIVERT
+ ret=alloc_divert_blk(dev);
+ if (ret)
+ return ret;
+#endif /* CONFIG_NET_DIVERT */
+
/*
* Default initial state at registry is that the
* device is present.
@@ -2248,6 +2279,12 @@
dev->deadbeaf = 0;
write_unlock_bh(&dev_base_lock);
+#ifdef CONFIG_NET_DIVERT
+ ret=alloc_divert_blk(dev);
+ if (ret)
+ return ret;
+#endif /* CONFIG_NET_DIVERT */
+
/* Notify protocols, that a new device appeared. */
notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
@@ -2342,6 +2379,10 @@
/* Notifier chain MUST detach us from master device. */
BUG_TRAP(dev->master==NULL);
+#ifdef CONFIG_NET_DIVERT
+ free_divert_blk(dev);
+#endif
+
if (dev->new_style) {
#ifdef NET_REFCNT_DEBUG
if (atomic_read(&dev->refcnt) != 1)
@@ -2414,6 +2455,9 @@
extern void net_device_init(void);
extern void ip_auto_config(void);
+#ifdef CONFIG_NET_DIVERT
+extern void dv_init(void);
+#endif /* CONFIG_NET_DIVERT */
int __init net_dev_init(void)
{
@@ -2424,6 +2468,10 @@
pktsched_init();
#endif
+#ifdef CONFIG_NET_DIVERT
+ dv_init();
+#endif /* CONFIG_NET_DIVERT */
+
/*
* Initialise the packet receive queues.
*/
diff -Naur linux/net/core/dv.c linux-2.4.0-test8/net/core/dv.c
--- linux/net/core/dv.c Thu Jan 1 00:00:00 1970
+++ linux-2.4.0-test8/net/core/dv.c Thu Sep 21 22:13:42 2000
@@ -0,0 +1,561 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Generic frame diversion
+ *
+ * Version: @(#)eth.c 0.41 09/09/2000
+ *
+ * Authors:
+ * Benoit LOCHER: initial integration within the kernel with support for ethernet
+ * Dave Miller: improvement on the code (correctness, performance and source files)
+ *
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+const char sysctl_divert_version[32]="0.46"; /* Current version */
+
+int __init dv_init(void)
+{
+ printk(KERN_INFO "NET4: Frame Diverter %s\n", sysctl_divert_version);
+ return 0;
+}
+
+/*
+ * Allocate a divert_blk for a device. This must be an ethernet nic.
+ *
+ */
+
+int alloc_divert_blk(struct net_device *dev)
+{
+ int alloc_size=(sizeof(struct divert_blk)+3)&~3;
+
+ if (!strncmp(dev->name, "eth", 3))
+ {
+ printk(KERN_DEBUG "divert: allocating divert_blk for %s\n",
+ dev->name);
+ dev->divert=(struct divert_blk *)kmalloc(alloc_size, GFP_KERNEL);
+ if (dev->divert==NULL)
+ {
+ printk(KERN_DEBUG "divert: unable to allocate divert_blk for %s\n",
+ dev->name);
+ return -EFAULT;
+ }
+ else
+ memset(dev->divert, 0, sizeof(struct divert_blk));
+ }
+ else
+ {
+ printk(KERN_DEBUG "divert: not allocating divert_blk for non-ethernet device %s\n",
+ dev->name);
+ dev->divert=NULL;
+ }
+ return 0;
+}
+
+
+/*
+ * Free a divert_blk allocated by the above function, if it was
+ * allocated on that device.
+ *
+ */
+
+
+void free_divert_blk(struct net_device *dev)
+{
+ if (dev->divert)
+ {
+ kfree(dev->divert);
+ dev->divert=NULL;
+ printk(KERN_DEBUG "divert: freeing divert_blk for %s\n",
+ dev->name);
+ }
+ else
+ {
+ printk(KERN_DEBUG "divert: no divert_blk to free, %s not ethernet\n",
+ dev->name);
+ }
+ return;
+}
+
+
+
+/*
+ * Adds a tcp/udp (source or dest) port to an array
+ */
+int add_port(u16 ports[], u16 port)
+{
+ int i;
+
+ if (port==0)
+ return -EINVAL;
+
+ /* Storing directly in network format for performance, thanks Dave :) */
+ port=htons(port);
+
+ for (i=0; icmd==DIVCMD_GETVERSION)
+ return 0;
+
+ /* Network device index should reasonably be between 0 and 1000 :) */
+ if (div_cf->dev_index<0 || div_cf->dev_index>1000)
+ return -EINVAL;
+
+ /* Let's try to find the ifname */
+ sprintf(devname, "eth%d", div_cf->dev_index);
+ *dev=dev_get_by_name(devname);
+
+ /* dev should NOT be null */
+ if (*dev==NULL)
+ return -EINVAL;
+
+ /* user issuing the ioctl must be a super one :) */
+ if (!suser())
+ return -EPERM;
+
+ /* Device must have a divert_blk member NOT null */
+ if ((*dev)->divert==NULL)
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * control function of the diverter
+ */
+#define DVDBG(a) printk(KERN_DEBUG "divert_ioctl() line %d %s\n", __LINE__, (a))
+
+int divert_ioctl(unsigned int cmd, struct divert_cf *arg)
+{
+ struct divert_cf div_cf;
+ struct divert_blk *div_blk;
+ struct net_device *dev;
+ int ret;
+
+
+ switch(cmd)
+ {
+ case SIOCGIFDIVERT:
+ DVDBG("SIOCGIFDIVERT, copy_from_user");
+ if (copy_from_user(&div_cf, arg, sizeof(struct divert_cf)))
+ return -EFAULT;
+ DVDBG("before check_args");
+ ret=check_args(&div_cf, &dev);
+ if (ret)
+ return ret;
+ DVDBG("after checkargs");
+ div_blk=dev->divert;
+
+ DVDBG("befre switch()");
+ switch(div_cf.cmd)
+ {
+ case DIVCMD_GETSTATUS:
+ /* Now, just give the user the raw divert block for him to play with :) */
+ if(copy_to_user(div_cf.arg1.ptr, dev->divert, sizeof(struct divert_blk)))
+ return -EFAULT;
+ break;
+
+ case DIVCMD_GETVERSION:
+ DVDBG("GETVERSION: checking ptr");
+ if (div_cf.arg1.ptr == NULL)
+ return -EINVAL;
+ DVDBG("GETVERSION: copying data to userland");
+ if(copy_to_user(div_cf.arg1.ptr, sysctl_divert_version, 32))
+ return -EFAULT;
+ DVDBG("GETVERSION: data copied");
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case SIOCSIFDIVERT:
+ if(copy_from_user(&div_cf, arg, sizeof(struct divert_cf)))
+ return -EFAULT;
+
+ ret=check_args(&div_cf, &dev);
+ if (ret)
+ return ret;
+
+ div_blk=dev->divert;
+
+ switch(div_cf.cmd)
+ {
+ case DIVCMD_RESET:
+ div_blk->divert=0;
+ div_blk->protos=DIVERT_PROTO_NONE;
+ memset(div_blk->tcp_dst, 0, MAX_DIVERT_PORTS*sizeof(u16));
+ memset(div_blk->tcp_src, 0, MAX_DIVERT_PORTS*sizeof(u16));
+ memset(div_blk->udp_dst, 0, MAX_DIVERT_PORTS*sizeof(u16));
+ memset(div_blk->udp_src, 0, MAX_DIVERT_PORTS*sizeof(u16));
+ return 0;
+ break;
+
+
+ case DIVCMD_DIVERT:
+ switch(div_cf.arg1.int32)
+ {
+ case DIVARG1_ENABLE:
+ if (div_blk->divert)
+ return -EALREADY;
+ div_blk->divert=1;
+ break;
+
+ case DIVARG1_DISABLE:
+ if (!div_blk->divert)
+ return -EALREADY;
+ div_blk->divert=0;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ break;
+
+
+ case DIVCMD_IP:
+ switch(div_cf.arg1.int32)
+ {
+ case DIVARG1_ENABLE:
+ if (div_blk->protos&DIVERT_PROTO_IP)
+ return -EALREADY;
+ div_blk->protos|=DIVERT_PROTO_IP;
+ break;
+
+ case DIVARG1_DISABLE:
+ if (!(div_blk->protos&DIVERT_PROTO_IP))
+ return -EALREADY;
+ div_blk->protos&=~DIVERT_PROTO_IP;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ break;
+
+
+ case DIVCMD_TCP:
+ switch(div_cf.arg1.int32)
+ {
+ case DIVARG1_ENABLE:
+ if (div_blk->protos&DIVERT_PROTO_TCP)
+ return -EALREADY;
+ div_blk->protos|=DIVERT_PROTO_TCP;
+ break;
+
+ case DIVARG1_DISABLE:
+ if (!(div_blk->protos&DIVERT_PROTO_TCP))
+ return -EALREADY;
+ div_blk->protos&=~DIVERT_PROTO_TCP;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ break;
+
+
+ case DIVCMD_TCPDST:
+ switch(div_cf.arg1.int32)
+ {
+ case DIVARG1_ADD:
+ return add_port(div_blk->tcp_dst, div_cf.arg2.uint16);
+ break;
+
+ case DIVARG1_REMOVE:
+ return remove_port(div_blk->tcp_dst, div_cf.arg2.uint16);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ break;
+
+
+ case DIVCMD_TCPSRC:
+ switch(div_cf.arg1.int32)
+ {
+ case DIVARG1_ADD:
+ return add_port(div_blk->tcp_src, div_cf.arg2.uint16);
+ break;
+
+ case DIVARG1_REMOVE:
+ return remove_port(div_blk->tcp_src, div_cf.arg2.uint16);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ break;
+
+
+ case DIVCMD_UDP:
+ switch(div_cf.arg1.int32)
+ {
+ case DIVARG1_ENABLE:
+ if (div_blk->protos&DIVERT_PROTO_UDP)
+ return -EALREADY;
+ div_blk->protos|=DIVERT_PROTO_UDP;
+ break;
+
+ case DIVARG1_DISABLE:
+ if (!(div_blk->protos&DIVERT_PROTO_UDP))
+ return -EALREADY;
+ div_blk->protos&=~DIVERT_PROTO_UDP;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ break;
+
+
+ case DIVCMD_UDPDST:
+ switch(div_cf.arg1.int32)
+ {
+ case DIVARG1_ADD:
+ return add_port(div_blk->udp_dst, div_cf.arg2.uint16);
+ break;
+
+ case DIVARG1_REMOVE:
+ return remove_port(div_blk->udp_dst, div_cf.arg2.uint16);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ break;
+
+
+ case DIVCMD_UDPSRC:
+ switch(div_cf.arg1.int32)
+ {
+ case DIVARG1_ADD:
+ return add_port(div_blk->udp_src, div_cf.arg2.uint16);
+ break;
+
+ case DIVARG1_REMOVE:
+ return remove_port(div_blk->udp_src, div_cf.arg2.uint16);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ break;
+
+
+ case DIVCMD_ICMP:
+ switch(div_cf.arg1.int32)
+ {
+ case DIVARG1_ENABLE:
+ if (div_blk->protos&DIVERT_PROTO_ICMP)
+ return -EALREADY;
+ div_blk->protos|=DIVERT_PROTO_ICMP;
+ break;
+
+ case DIVARG1_DISABLE:
+ if (!(div_blk->protos&DIVERT_PROTO_ICMP))
+ return -EALREADY;
+ div_blk->protos&=~DIVERT_PROTO_ICMP;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ break;
+
+
+ default:
+ return -EINVAL;
+ }
+ break;
+
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+/*
+ * Check if packet should have its dest mac address set to the box itself
+ * for diversion
+ */
+
+#define ETH_DIVERT_FRAME(skb) \
+ memcpy(skb->mac.ethernet, skb->dev->dev_addr, ETH_ALEN); \
+ skb->pkt_type=PACKET_HOST
+
+void divert_frame(struct sk_buff *skb)
+{
+ struct ethhdr *eth=skb->mac.ethernet;
+ struct iphdr *iph;
+ struct tcphdr *tcph;
+ struct udphdr *udph;
+ struct divert_blk *divert=skb->dev->divert;
+ int i, src, dst;
+ unsigned char *skb_data_end=skb->data+skb->len;
+
+ /* Packet is already aimed at us, return */
+ if (!memcmp(eth, skb->dev->dev_addr, ETH_ALEN))
+ return;
+
+ /* proto is not IP, do nothing */
+ if (eth->h_proto != htons(ETH_P_IP))
+ return;
+
+ /* Divert all IP frames ? */
+ if (divert->protos&DIVERT_PROTO_IP)
+ {
+ ETH_DIVERT_FRAME(skb);
+ return;
+ }
+
+ iph=(struct iphdr *)(skb->data);
+ /* Check for possible (maliciously) malformed IP frame (thanks Dave) */
+ if (((iph->ihl<<2)+(unsigned char*)(iph))>=skb_data_end)
+ {
+ printk(KERN_INFO "divert: malformed IP packet !\n");
+ return;
+ }
+ switch(iph->protocol)
+ {
+ /* Divert all ICMP frames ? */
+ case IPPROTO_ICMP:
+ if (divert->protos&DIVERT_PROTO_ICMP)
+ {
+ ETH_DIVERT_FRAME(skb);
+ return;
+ }
+ break;
+
+ /* Divert all TCP frames ? */
+ case IPPROTO_TCP:
+ if (divert->protos&DIVERT_PROTO_TCP)
+ {
+ ETH_DIVERT_FRAME(skb);
+ return;
+ }
+ tcph=(struct tcphdr *)(((unsigned char *)iph) + (iph->ihl<<2));
+ /* Check for possible (maliciously) malformed IP frame (thanx Dave) */
+ if (((unsigned char *)(tcph+1))>=skb_data_end)
+ {
+ printk(KERN_INFO "divert: malformed TCP packet !\n");
+ return;
+ }
+ /* Divert some tcp dst/src ports only ?*/
+ for (i=0; itcp_dst[i];
+ src=divert->tcp_src[i];
+ if ((dst && dst==tcph->dest) ||
+ (src && src==tcph->source))
+ {
+ ETH_DIVERT_FRAME(skb);
+ return;
+ }
+ }
+ break;
+
+ /* Divert all UDP frames ? */
+ case IPPROTO_UDP:
+ if (divert->protos&DIVERT_PROTO_UDP)
+ {
+ ETH_DIVERT_FRAME(skb);
+ return;
+ }
+ udph=(struct udphdr *)(((unsigned char *)iph) + (iph->ihl<<2));
+ /* Check for possible (maliciously) malformed IP packet (thanks Dave) */
+ if (((unsigned char *)(udph+1))>=skb_data_end)
+ {
+ printk(KERN_INFO "divert: malformed UDP packet !\n");
+ return;
+ }
+ /* Divert some udp dst/src ports only ? */
+ for (i=0; iudp_dst[i];
+ src=divert->udp_src[i];
+ if ((dst && dst==udph->dest) ||
+ (src && src==udph->source))
+ {
+ ETH_DIVERT_FRAME(skb);
+ return;
+ }
+ }
+ break;
+ }
+ return;
+}
+
diff -Naur linux/net/core/sysctl_net_core.c linux-2.4.0-test8/net/core/sysctl_net_core.c
--- linux/net/core/sysctl_net_core.c Thu Feb 10 04:08:09 2000
+++ linux-2.4.0-test8/net/core/sysctl_net_core.c Sun Sep 17 16:15:51 2000
@@ -25,6 +25,10 @@
extern int sysctl_optmem_max;
extern int sysctl_hot_list_len;
+#ifdef CONFIG_NET_DIVERT
+extern char sysctl_divert_version[];
+#endif /* CONFIG_NET_DIVERT */
+
ctl_table core_table[] = {
#ifdef CONFIG_NET
{NET_CORE_WMEM_MAX, "wmem_max",
@@ -59,6 +63,11 @@
{NET_CORE_HOT_LIST_LENGTH, "hot_list_length",
&sysctl_hot_list_len, sizeof(int), 0644, NULL,
&proc_dointvec},
+#ifdef CONFIG_NET_DIVERT
+ {NET_CORE_DIVERT_VERSION, "divert_version",
+ (void *)sysctl_divert_version, 32, 0444, NULL,
+ &proc_dostring},
+#endif /* CONFIG_NET_DIVERT */
#endif /* CONFIG_NET */
{ 0 }
};
diff -Naur linux/net/ipv4/af_inet.c linux-2.4.0-test8/net/ipv4/af_inet.c
--- linux/net/ipv4/af_inet.c Fri Aug 18 17:26:25 2000
+++ linux-2.4.0-test8/net/ipv4/af_inet.c Sun Sep 17 16:19:47 2000
@@ -107,6 +107,9 @@
#ifdef CONFIG_KMOD
#include
#endif
+#ifdef CONFIG_NET_DIVERT
+#include
+#endif /* CONFIG_NET_DIVERT */
#if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO)
#include /* Note : will define WIRELESS_EXT */
#endif /* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */
@@ -848,6 +851,13 @@
if (br_ioctl_hook != NULL)
return br_ioctl_hook(arg);
#endif
+ case SIOCGIFDIVERT:
+ case SIOCSIFDIVERT:
+#ifdef CONFIG_NET_DIVERT
+ return(divert_ioctl(cmd, (struct divert_cf *) arg));
+#else
+ return -ENOPKG;
+#endif /* CONFIG_NET_DIVERT */
return -ENOPKG;
case SIOCADDDLCI:
diff -Naur linux/net/netsyms.c linux-2.4.0-test8/net/netsyms.c
--- linux/net/netsyms.c Thu Sep 7 15:32:01 2000
+++ linux-2.4.0-test8/net/netsyms.c Sun Sep 17 17:01:23 2000
@@ -30,6 +30,9 @@
#include
#include
#include
+#ifdef CONFIG_NET_DIVERT
+#include
+#endif /* CONFIG_NET_DIVERT */
#ifdef CONFIG_NET
extern __u32 sysctl_wmem_max;
@@ -206,6 +209,12 @@
EXPORT_SYMBOL(br_ioctl_hook);
#endif
#endif
+
+#ifdef CONFIG_NET_DIVERT
+EXPORT_SYMBOL(alloc_divert_blk);
+EXPORT_SYMBOL(free_divert_blk);
+EXPORT_SYMBOL(divert_ioctl);
+#endif /* CONFIG_NET_DIVERT */
#ifdef CONFIG_INET
/* Internet layer registration */
diff -Naur linux/net/packet/af_packet.c linux-2.4.0-test8/net/packet/af_packet.c
--- linux/net/packet/af_packet.c Tue Aug 29 04:16:05 2000
+++ linux-2.4.0-test8/net/packet/af_packet.c Sun Sep 17 16:22:55 2000
@@ -67,6 +67,10 @@
#include
#include
+#ifdef CONFIG_NET_DIVERT
+#include
+#endif /* CONFIG_NET_DIVERT */
+
#ifdef CONFIG_INET
#include
#endif
@@ -1483,6 +1487,14 @@
return br_ioctl_hook(arg);
#endif
#endif
+
+ case SIOCGIFDIVERT:
+ case SIOCSIFDIVERT:
+#ifdef CONFIG_NET_DIVERT
+ return(divert_ioctl(cmd, (struct divert_cf *) arg));
+#else
+ return -ENOPKG;
+#endif /* CONFIG_NET_DIVERT */
return -ENOPKG;