diff -Naur linux/Documentation/Configure.help linux-2.2.17/Documentation/Configure.help
--- linux/Documentation/Configure.help Mon Sep 4 17:39:15 2000
+++ linux-2.2.17/Documentation/Configure.help Mon Oct 16 20:39:47 2000
@@ -3525,6 +3525,29 @@
in ftp://metalab.unc.edu/pub/Linux/docs/HOWTO. The Bridging code is
still in test. If unsure, say N.
+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
+
Packet socket
CONFIG_PACKET
The Packet protocol is used by applications which communicate
diff -Naur linux/drivers/net/Space.c linux-2.2.17/drivers/net/Space.c
--- linux/drivers/net/Space.c Mon Sep 4 17:39:18 2000
+++ linux-2.2.17/drivers/net/Space.c Mon Oct 16 20:39:48 2000
@@ -31,6 +31,7 @@
#include
#include
#include
+#include
#define NEXT_DEV NULL
@@ -171,13 +172,35 @@
struct devprobe *p = plist;
unsigned long base_addr = dev->base_addr;
+#ifdef CONFIG_NET_DIVERT
+ int ret;
+#endif
+
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
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
}
p++;
}
diff -Naur linux/include/linux/netdevice.h linux-2.2.17/include/linux/netdevice.h
--- linux/include/linux/netdevice.h Tue Jan 4 18:12:25 2000
+++ linux-2.2.17/include/linux/netdevice.h Mon Oct 23 23:38:30 2000
@@ -30,6 +30,7 @@
#include
#include
#include
+#include
#include
@@ -317,6 +318,11 @@
/* Semi-private data. Keep it at the end of device struct. */
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.2.17/include/linux/sockios.h
--- linux/include/linux/sockios.h Fri Aug 28 02:33:08 1998
+++ linux-2.2.17/include/linux/sockios.h Mon Oct 16 20:39:48 2000
@@ -68,6 +68,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.2.17/include/linux/sysctl.h
--- linux/include/linux/sysctl.h Wed Jun 7 21:26:44 2000
+++ linux-2.2.17/include/linux/sysctl.h Mon Oct 16 20:39:48 2000
@@ -166,7 +166,8 @@
NET_CORE_FASTROUTE=7,
NET_CORE_MSG_COST=8,
NET_CORE_MSG_BURST=9,
- NET_CORE_OPTMEM_MAX=10
+ NET_CORE_OPTMEM_MAX=10,
+ NET_CORE_DIVERT_VERSION=12
};
/* /proc/sys/net/ethernet */
diff -Naur linux/include/net/divert.h linux-2.2.17/include/net/divert.h
--- linux/include/net/divert.h Thu Jan 1 00:00:00 1970
+++ linux-2.2.17/include/net/divert.h Mon Oct 23 23:37:07 2000
@@ -0,0 +1,190 @@
+/*
+ * Frame Diversion, Benoit Locher
+ *
+ * Changes:
+ * 06/09/2000 BL: initial version
+ * 15/10/2000 BL: changed to a rule based structure
+ */
+
+#ifndef _LINUX_DIVERT_H
+#define _LINUX_DIVERT_H
+
+#include
+
+#ifdef __KERNEL__
+#include
+#endif /* __KERNEL__ */
+
+
+struct sk_buff;
+struct divert_rule;
+typedef int (*process_rule_func)(struct sk_buff *, struct divert_rule *);
+
+
+enum DIVERT_PROTO
+{
+ DIVERT_PROTO_IP=10,
+ DIVERT_PROTO_TCP=11,
+ DIVERT_PROTO_UDP=12,
+ DIVERT_PROTO_ICMP=13,
+ DIVERT_PROTO_ARP=20,
+ DIVERT_PROTO_IPX=30,
+};
+
+
+typedef struct _divert_cidr
+{
+ unsigned long ipnum,
+ ipmask;
+} divert_cidr;
+
+
+typedef struct _divert_icmp_blk
+{
+ divert_cidr scidr;
+ divert_cidr dcidr;
+ unsigned short type;
+} divert_icmp_blk;
+
+
+typedef struct _divert_tcp_blk
+{
+ divert_cidr scidr;
+ divert_cidr dcidr;
+ unsigned short sport_start,
+ sport_end,
+ dport_start,
+ dport_end;
+} divert_tcp_blk;
+
+
+typedef struct _divert_udp_blk
+{
+ divert_cidr scidr;
+ divert_cidr dcidr;
+ unsigned short sport_start,
+ sport_end,
+ dport_start,
+ dport_end;
+} divert_udp_blk;
+
+
+typedef struct _divert_ip_blk
+{
+ divert_cidr scidr;
+ divert_cidr dcidr;
+} divert_ip_blk;
+
+
+typedef struct _divert_arp_blk
+{
+
+} divert_arp_blk;
+
+
+typedef struct _divert_ipx_blk
+{
+
+} divert_ipx_blk;
+
+
+typedef struct _divert_spx_blk
+{
+
+} divert_spx_blk;
+
+
+typedef union _divert_proto
+{
+ divert_ip_blk ip;
+ divert_tcp_blk tcp;
+ divert_udp_blk udp;
+ divert_icmp_blk icmp;
+ divert_arp_blk arp;
+ divert_ipx_blk ipx;
+ divert_spx_blk spx;
+} divert_proto;
+
+
+#ifdef __KERNEL__
+
+struct divert_rule
+{
+ struct divert_rule *prev,
+ *next;
+ unsigned long proto_num;
+ divert_proto proto_blk;
+ int divert_locally;
+ unsigned char dest_mac[ETH_ALEN];
+ process_rule_func process_rule;
+};
+
+
+struct divert_blk
+{
+ int divert; /* diverter status on|off */
+ rwlock_t rwlock; /* for rule list interprocess locking */
+ struct divert_rule *rule; /* the rule doubly-linked list */
+};
+
+#endif /* __KERNEL__ */
+
+/*
+ * Diversion control block, for configuration with the userspace tool
+ * divert
+ */
+
+struct divert_cf
+{
+ int cmd; /* Command */
+ unsigned int rule_num; /* Used to retrieve a specific rule */
+ unsigned int proto_num; /* Protocol this rule works on */
+ divert_proto proto_blk; /* Rule protocol description */
+ int status; /* Used to get the status of the diverter */
+ int divert_locally; /* acts locally to the specified interface */
+ unsigned char dest_mac[ETH_ALEN]; /* If not divert_locally, mac address it should
+ be forwarded to */
+ char dev_name[32]; /* Device name (eth0...) */
+};
+
+
+/* Diversion commands */
+#define DIVERT_SET_STATE 1 /* Switch diversion on or off */
+#define DIVERT_APPEND_RULE 2 /* Add a rule */
+#define DIVERT_INSERT_RULE 3 /* Insert a rule */
+#define DIVERT_REPLACE_RULE 4 /* Replace a rule */
+#define DIVERT_DELETE_RULE 5 /* Delete a rule */
+#define DIVERT_FLUSH_RULES 6 /* Flush all the rules */
+#define DIVERT_GET_RULE 7 /* Retrieve a rule */
+#define DIVERT_GET_STATUS 8 /* retrieve the version + diverter status */
+
+
+#ifdef __KERNEL__
+
+/* diverter functions */
+#include
+void dv_init(void);
+int alloc_divert_blk(struct device *);
+void free_divert_blk(struct device *);
+int divert_ioctl(unsigned int, struct divert_cf *);
+void divert_frame(struct sk_buff *);
+
+/* Rule processing routines */
+int process_ip_rule(struct sk_buff *, struct divert_rule *);
+int process_ip_tcp_rule(struct sk_buff *, struct divert_rule *);
+int process_ip_udp_rule(struct sk_buff *, struct divert_rule *);
+int process_ip_icmp_rule(struct sk_buff *, struct divert_rule *);
+
+/* Rule manipulation routines */
+struct divert_rule *divert_alloc_rule(void);
+void divert_free_rule(struct divert_rule *);
+struct divert_rule *divert_get_rule(struct device *, int);
+int divert_append_rule(struct device *, struct divert_rule *);
+int divert_insert_rule(struct device *, struct divert_rule *, int);
+int divert_delete_rule(struct device *, int);
+int divert_replace_rule(struct device *, struct divert_rule *, int);
+int divert_flush_rules(struct device *);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_DIVERT_H */
diff -Naur linux/net/Config.in linux-2.2.17/net/Config.in
--- linux/net/Config.in Tue Jan 4 18:12:26 2000
+++ linux-2.2.17/net/Config.in Mon Oct 16 20:39:48 2000
@@ -42,6 +42,7 @@
if [ "$CONFIG_BRIDGE" != "n" ]; then
int ' Maximum number of bridged interfaces' CONFIG_BRIDGE_NUM_PORTS 8
fi
+ bool 'Frame Diverter (EXPERIMENTAL)' CONFIG_NET_DIVERT
bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC
# if [ "$CONFIG_LLC" = "y" ]; then
# bool 'Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI
diff -Naur linux/net/core/Makefile linux-2.2.17/net/core/Makefile
--- linux/net/core/Makefile Tue Dec 29 19:21:49 1998
+++ linux-2.2.17/net/core/Makefile Mon Oct 16 20:39:48 2000
@@ -29,6 +29,10 @@
OX_OBJS += firewall.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.2.17/net/core/dev.c
--- linux/net/core/dev.c Mon Sep 4 17:39:29 2000
+++ linux-2.2.17/net/core/dev.c Mon Oct 23 22:49:33 2000
@@ -59,6 +59,7 @@
* Paul Rusty Russel : SIOCSIFNAME
* Andrea Arcangeli : dev_clear_backlog() needs the
* skb_queue_lock held.
+ * Benoit Locher : Added the Frame Diversion code
*/
#include
@@ -84,6 +85,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -839,6 +841,17 @@
}
#endif
+
+#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 && skb->dev->divert->rule)
+ divert_frame(skb);
+ return;
+}
+#endif /* CONFIG_NET_DIVERT */
+
/*
* When we are called the queue is ready to grab, the interrupts are
* on and hardware can interrupt and queue to the receive queue as we
@@ -942,6 +955,15 @@
type = skb->protocol;
+#ifdef CONFIG_NET_DIVERT
+ /*
+ * Have the frame diverted ?
+ *
+ */
+ handle_diverter(skb);
+#endif /* CONFIG_NET_DIVERT */
+
+
#ifdef CONFIG_BRIDGE
/*
* If we are bridging then pass the frame up to the
@@ -1754,10 +1776,12 @@
static int dev_boot_phase = 1;
-
int register_netdevice(struct device *dev)
{
struct device *d, **dp;
+#ifdef CONFIG_NET_DIVERT
+ int ret;
+#endif
if (dev_boot_phase) {
/* This is NOT bug, but I am not sure, that all the
@@ -1780,6 +1804,11 @@
}
dev->next = NULL;
*dp = dev;
+#ifdef CONFIG_NET_DIVERT
+ ret=alloc_divert_blk(dev);
+ if (ret)
+ return ret;
+#endif /* CONFIG_NET_DIVERT */
return 0;
}
@@ -1803,7 +1832,11 @@
/* Notify protocols, that a new device appeared. */
notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
-
+#ifdef CONFIG_NET_DIVERT
+ ret=alloc_divert_blk(dev);
+ if (ret)
+ return ret;
+#endif
return 0;
}
@@ -1851,6 +1884,9 @@
if (dev->destructor)
dev->destructor(dev);
+#ifdef CONFIG_NET_DIVERT
+ free_divert_blk(dev);
+#endif /* CONFIG_NET_DIVERT */
return 0;
}
}
@@ -1926,6 +1962,13 @@
#ifdef CONFIG_BRIDGE
br_init();
#endif
+
+ /*
+ * Frame Diverter init
+ */
+#ifdef CONFIG_NET_DIVERT
+ dv_init();
+#endif /* CONFIG_NET_DIVERT */
/*
* This is Very Ugly(tm).
diff -Naur linux/net/core/dv.c linux-2.2.17/net/core/dv.c
--- linux/net/core/dv.c Thu Jan 1 00:00:00 1970
+++ linux-2.2.17/net/core/dv.c Mon Oct 23 23:38:14 2000
@@ -0,0 +1,693 @@
+/*
+ * 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.5"; /* Current version */
+
+
+__initfunc(void dv_init(void))
+{
+ printk(KERN_INFO "NET4: Frame Diverter %s\n", sysctl_divert_version);
+}
+
+
+/*
+ * Allocate a divert_blk for a device. This must be an ethernet nic.
+ */
+
+int alloc_divert_blk(struct 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
+ {
+ dev->divert->rwlock= RW_LOCK_UNLOCKED;
+ dev->divert->divert=0;
+ dev->divert->rule=NULL;
+ }
+ }
+ 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 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;
+}
+
+struct divert_rule *divert_alloc_rule(void)
+{
+ struct divert_rule *rule;
+ int alloc_size=(sizeof(struct divert_rule)+3)&~3;
+
+ rule=(struct divert_rule *)kmalloc(alloc_size, GFP_KERNEL);
+ if (rule==NULL)
+ return NULL;
+ memset(rule, 0, alloc_size);
+ rule->prev=NULL;
+ rule->next=NULL;
+ return rule;
+}
+
+
+void divert_free_rule(struct divert_rule *rule)
+{
+ kfree(rule);
+}
+
+
+int divert_append_rule(struct device *dev, struct divert_rule *new_rule)
+{
+ struct divert_rule *rule;
+ unsigned long flags;
+
+ if (dev->divert==NULL)
+ return -EFAULT;
+
+ /* write-lock the rules */
+ write_lock_irqsave(&dev->divert->rwlock, flags);
+
+ /* special case, 1st rule appended */
+ new_rule->next=NULL;
+ if (dev->divert->rule==NULL)
+ {
+ new_rule->prev=NULL;
+ dev->divert->rule=new_rule;
+ }
+ else
+ {
+ rule=dev->divert->rule;
+
+ while(rule->next!=NULL)
+ rule=rule->next;
+ rule->next=new_rule;
+ new_rule->prev=rule;
+
+ }
+
+ /* release the write-lock on the rules */
+ write_unlock_irqrestore(&dev->divert->rwlock, flags);
+
+ return 0;
+}
+
+
+int divert_insert_rule(struct device *dev, struct divert_rule *new_rule, int position)
+{
+ struct divert_rule *rule;
+ unsigned long flags;
+
+ if (dev->divert==NULL)
+ return -EFAULT;
+
+ /* write-lock the rules */
+ write_lock_irqsave(&dev->divert->rwlock, flags);
+
+ if (position==1 && dev->divert->rule==NULL)
+ {
+ dev->divert->rule=new_rule;
+ new_rule->prev=NULL;
+ new_rule->next=NULL;
+
+ }
+ else
+ {
+ rule=divert_get_rule(dev, position);
+ if (rule==NULL)
+ {
+ /* release the write-lock */
+ write_unlock_irqrestore(&dev->divert->rwlock, flags);
+
+ return -EINVAL;
+ }
+ new_rule->next=rule;
+ new_rule->prev=rule->prev;
+ rule->prev=new_rule;
+ }
+
+ /* release the write-lock */
+ write_unlock_irqrestore(&dev->divert->rwlock, flags);
+
+ return 0;
+}
+
+
+int divert_delete_rule(struct device *dev, int position)
+{
+ struct divert_rule *rule;
+ unsigned long flags;
+
+ /* write-lock the rules */
+ write_lock_irqsave(&dev->divert->rwlock, flags);
+
+ rule=divert_get_rule(dev, position);
+ if (rule==NULL)
+ {
+ write_unlock_irqrestore(&dev->divert->rwlock, flags);
+ return -EINVAL;
+ }
+
+ /* First rule in the chain */
+ if (rule->prev==NULL)
+ {
+ dev->divert->rule=rule->next;
+ dev->divert->rule->prev=NULL;
+ }
+ /* Last rule in the chain */
+ else if (rule->next==NULL)
+ {
+ rule->prev->next=NULL;
+ }
+ else
+ {
+ rule->prev->next=rule->next;
+ rule->next->prev=rule->prev;
+ }
+ divert_free_rule(rule);
+
+ /* release the write-lock on the rules */
+ write_unlock_irqrestore(&dev->divert->rwlock, flags);
+
+ return 0;
+}
+
+
+int divert_replace_rule(struct device *dev, struct divert_rule *new_rule, int position)
+{
+ struct divert_rule *rule;
+ unsigned long flags;
+
+ /* write-lock the rules */
+ write_lock_irqsave(&dev->divert->rwlock, flags);
+
+ rule=divert_get_rule(dev, position);
+ if (rule==NULL)
+ {
+ /* release the write-lock on the rules */
+ write_unlock_irqrestore(&dev->divert->rwlock, flags);
+
+ return -EINVAL;
+ }
+ rule->next->prev=new_rule;
+ rule->prev->next=new_rule;
+ divert_free_rule(rule);
+
+ /* release the write-lock */
+ write_unlock_irqrestore(&dev->divert->rwlock, flags);
+
+ return 0;
+}
+
+
+int divert_flush_rules(struct device *dev)
+{
+ struct divert_rule *rule,
+ *next_rule;
+ unsigned long flags;
+
+ /* No diversion possible on this interface ? */
+ if (!dev->divert)
+ return -EINVAL;
+
+ /* there are no rule set on this interface */
+ if (!dev->divert->rule)
+ return -EINVAL;
+
+ /* Before flushing the rules, diversion on this nic must be off */
+ if (dev->divert->divert)
+ return -EINVAL;
+
+ /* write-lock the rules */
+ write_lock_irqsave(&dev->divert->rwlock, flags);
+
+ rule=dev->divert->rule;
+ dev->divert->rule=NULL;
+ while(rule!=NULL)
+ {
+ next_rule=rule->next;
+ divert_free_rule(rule);
+ rule=next_rule;
+ }
+
+ /* release the write-lock */
+ write_unlock_irqrestore(&dev->divert->rwlock, flags);
+
+ return 0;
+}
+
+
+/*
+ *
+ * Must be called with divert->rwlock locked
+ *
+ */
+struct divert_rule *divert_get_rule(struct device *dev, int rule_num)
+{
+ struct divert_rule *rule;
+ int num=1;
+
+ if (dev==NULL)
+ return NULL;
+
+ if (dev->divert==NULL)
+ return NULL;
+
+ rule=dev->divert->rule;
+ while(rule!=NULL && rule_num!=num)
+ {
+ num++;
+ rule=rule->next;
+ }
+ return rule;
+}
+
+
+/* Some basic sanity checks on the arguments passed to divert_ioctl() */
+int check_args(struct divert_cf *div_cf, struct device **dev)
+{
+ char devname[32];
+
+ if (dev==NULL)
+ return -EFAULT;
+
+ /* Let's try to find the ifname */
+ *dev=dev_get(div_cf->dev_name);
+
+ /* 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;
+}
+
+
+int fill_rule_from_arg(struct divert_cf *div_cf, struct divert_rule *rule)
+{
+ rule->proto_num=div_cf->proto_num;
+ memcpy((void *)&rule->proto_blk, (void *)&div_cf->proto_blk, sizeof(divert_proto));
+ rule->divert_locally=div_cf->divert_locally;
+ memcpy(rule->dest_mac, div_cf->dest_mac, ETH_ALEN);
+ return 0;
+}
+
+
+int fill_arg_from_rule(struct divert_rule *rule, struct divert_cf *div_cf)
+{
+ div_cf->proto_num=rule->proto_num;
+ memcpy((void *)&div_cf->proto_blk, (void *)&rule->proto_blk, sizeof(divert_proto));
+ div_cf->divert_locally=rule->divert_locally;
+ memcpy(div_cf->dest_mac, rule->dest_mac, ETH_ALEN);
+ return 0;
+}
+
+
+int divert_ioctl(unsigned int cmd, struct divert_cf *arg)
+{
+ struct divert_cf div_cf;
+ struct device *dev;
+ int oper, ret;
+ struct divert_rule *rule;
+
+ if (copy_from_user(&div_cf, arg, sizeof(struct divert_cf)))
+ return -EFAULT;
+
+ ret=check_args(&div_cf, &dev);
+ if (ret)
+ return ret;
+
+ oper=div_cf.cmd;
+
+ switch(cmd)
+ {
+ case SIOCGIFDIVERT:
+ switch(oper)
+ {
+ case DIVERT_GET_RULE:
+ rule=divert_get_rule(dev, div_cf.rule_num);
+ if (rule==NULL)
+ return -EINVAL;
+ ret=fill_arg_from_rule(rule, &div_cf);
+ if (ret)
+ return ret;
+ break;
+
+ case DIVERT_GET_STATUS:
+ div_cf.status=dev->divert->divert;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ if (copy_to_user(arg, &div_cf ,sizeof(struct divert_cf)))
+ return -EFAULT;
+ break;
+
+ case SIOCSIFDIVERT:
+ switch(oper)
+ {
+ case DIVERT_SET_STATE:
+ if (div_cf.status && !dev->divert->divert)
+ {
+ dev->divert->divert=!dev->divert->divert;
+ return 0;
+ }
+ else
+ return -EALREADY;
+ break;
+
+ case DIVERT_APPEND_RULE:
+ rule=divert_alloc_rule();
+ if (rule==NULL)
+ return -EFAULT;
+ ret=fill_rule_from_arg(&div_cf, rule);
+ if (ret)
+ {
+ /* Cleanup allocated memory */
+ divert_free_rule(rule);
+ return ret;
+ }
+ ret=divert_append_rule(dev, rule);
+ if (ret)
+ {
+ /* Cleanup allocated memory */
+ divert_free_rule(rule);
+ return ret;
+ }
+ break;
+
+ case DIVERT_INSERT_RULE:
+ rule=divert_alloc_rule();
+ if (rule==NULL)
+ return -EFAULT;
+ ret=fill_rule_from_arg(&div_cf, rule);
+ if (ret)
+ {
+ /* Cleanup allocated memory */
+ divert_free_rule(rule);
+ return ret;
+ }
+ ret=divert_insert_rule(dev, rule, div_cf.rule_num);
+ if (ret)
+ {
+ /* Cleanup allocated memory */
+ divert_free_rule(rule);
+ return ret;
+ }
+ break;
+
+ case DIVERT_REPLACE_RULE:
+ rule=divert_alloc_rule();
+ if (rule==NULL)
+ return -EFAULT;
+ ret=fill_rule_from_arg(&div_cf, rule);
+ if (ret)
+ {
+ /* Cleanup allocated memory */
+ divert_free_rule(rule);
+ return ret;
+ }
+ ret=divert_replace_rule(dev, rule, div_cf.rule_num);
+ if (ret)
+ {
+ /* Cleanup allocated memory */
+ divert_free_rule(rule);
+ return ret;
+ }
+ break;
+
+ case DIVERT_DELETE_RULE:
+ ret=divert_delete_rule(dev, div_cf.rule_num);
+ if (ret)
+ return ret;
+ 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
+
+
+#define CHECK_CIDR(ip, cidr) (((ip)&(cidr.ipmask))==(cidr.ipnum))
+
+
+int process_ip_rule(struct sk_buff *skb, struct divert_rule *rule)
+{
+ struct iphdr *iph;
+ unsigned char *skb_data_end=skb->data+skb->len;
+
+ 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 0; /* don't eat the frame */
+ }
+ if (!CHECK_CIDR(iph->saddr, rule->proto_blk.ip.scidr) ||
+ !CHECK_CIDR(iph->daddr, rule->proto_blk.ip.dcidr))
+ return 0;
+ if (rule->divert_locally)
+ skb->pkt_type=PACKET_HOST;
+ else
+ memcpy(skb->mac.ethernet, rule->dest_mac, ETH_ALEN);
+ return 1; /* packet has been diverted */
+}
+
+
+int process_ip_tcp_rule(struct sk_buff *skb, struct divert_rule *rule)
+{
+ struct iphdr *iph;
+ struct tcphdr *tcph;
+ unsigned char *skb_data_end=skb->data+skb->len;
+ unsigned short sport, dport;
+
+ 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 0; /* don't eat the frame */
+ }
+ 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 0;
+ }
+ sport=ntohs(tcph->source);
+ dport=ntohs(tcph->dest);
+ if (!CHECK_CIDR(iph->saddr, rule->proto_blk.tcp.scidr) ||
+ !CHECK_CIDR(iph->daddr, rule->proto_blk.tcp.dcidr))
+ return 0; /* ip addresses do not match */
+ if (sportproto_blk.tcp.sport_start ||
+ sport>rule->proto_blk.tcp.sport_end ||
+ sportproto_blk.tcp.dport_start ||
+ sport>rule->proto_blk.tcp.sport_end)
+ return 0; /* ports do not match */
+ if (rule->divert_locally)
+ skb->pkt_type=PACKET_HOST;
+ else
+ memcpy(skb->mac.ethernet, rule->dest_mac, ETH_ALEN);
+ return 1; /* packet has been diverted */
+}
+
+
+int process_ip_udp_rule(struct sk_buff *skb, struct divert_rule *rule)
+{
+ struct iphdr *iph;
+ struct udphdr *udph;
+ unsigned char *skb_data_end=skb->data+skb->len;
+ unsigned short sport, dport;
+
+ 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 0; /* don't eat the frame */
+ }
+ 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 0; /* don't eat the frame */
+ }
+ sport=ntohs(udph->source);
+ dport=ntohs(udph->dest);
+ if (!CHECK_CIDR(iph->saddr, rule->proto_blk.udp.scidr) ||
+ !CHECK_CIDR(iph->daddr, rule->proto_blk.udp.dcidr))
+ return 0; /* ip addresses do not match */
+ if (sportproto_blk.udp.sport_start ||
+ sport>rule->proto_blk.udp.sport_end ||
+ sportproto_blk.udp.dport_start ||
+ sport>rule->proto_blk.udp.sport_end)
+ return 0; /* ports do not match */
+ if (rule->divert_locally)
+ skb->pkt_type=PACKET_HOST;
+ else
+ memcpy(skb->mac.ethernet, rule->dest_mac, ETH_ALEN);
+ return 1; /* packet has been diverted */
+}
+
+
+int process_ip_icmp_rule(struct sk_buff *skb, struct divert_rule *rule)
+{
+ struct iphdr *iph;
+ struct icmphdr *icmph;
+ unsigned char *skb_data_end=skb->data+skb->len;
+ unsigned short type;
+
+ 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 0; /* don't eat the packet */
+ }
+ icmph=(struct icmphdr *)(((unsigned char *)iph) + (iph->ihl<<2));
+ /* Check for possible (maliciously) malformed IP packet (thanks Dave) */
+ if (((unsigned char *)(icmph+1))>=skb_data_end)
+ {
+ printk(KERN_INFO "divert: malformed ICMP packet !\n");
+ return 0; /* don't eat the frame */
+ }
+ if (!CHECK_CIDR(iph->saddr, rule->proto_blk.icmp.scidr) ||
+ !CHECK_CIDR(iph->daddr, rule->proto_blk.icmp.dcidr))
+ return 0; /* ip addresses do not match */
+ type=icmph->type;
+ if (type!=rule->proto_blk.icmp.type)
+ return 0; /* icmp types do not match */
+ if (rule->divert_locally)
+ skb->pkt_type=PACKET_HOST;
+ else
+ memcpy(skb->mac.ethernet, rule->dest_mac, ETH_ALEN);
+ return 1;
+}
+
+
+void divert_frame(struct sk_buff *skb)
+{
+ struct ethhdr *eth=skb->mac.ethernet;
+ struct divert_rule *rule=skb->dev->divert->rule;
+ unsigned long flags;
+
+ /* read-lock the rule list for this interface */
+ read_lock_irqsave(&skb->dev->divert->rwlock, flags);
+
+ while(rule)
+ {
+ if (rule->proto_num==eth->h_proto)
+ {
+ /*
+ * if rule_process() returns !=0 then stop further
+ * diversion on this packet
+ */
+ if (rule->process_rule(skb, rule))
+ break;
+ }
+ rule=rule->next;
+
+ }
+
+ /* release the read lock on this interface */
+ write_unlock_irqrestore(&skb->dev->divert->rwlock, flags);
+}
+
diff -Naur linux/net/core/sysctl_net_core.c linux-2.2.17/net/core/sysctl_net_core.c
--- linux/net/core/sysctl_net_core.c Sat Nov 7 19:00:32 1998
+++ linux-2.2.17/net/core/sysctl_net_core.c Mon Oct 16 20:39:48 2000
@@ -23,6 +23,9 @@
extern int sysctl_core_destroy_delay;
extern int sysctl_optmem_max;
+#ifdef CONFIG_NET_DIVERT
+extern char sysctl_divert_version[];
+#endif /* CONFIG_NET_DIVERT */
ctl_table core_table[] = {
#ifdef CONFIG_NET
@@ -55,6 +58,11 @@
{NET_CORE_OPTMEM_MAX, "optmem_max",
&sysctl_optmem_max, 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.2.17/net/ipv4/af_inet.c
--- linux/net/ipv4/af_inet.c Mon Sep 4 17:39:29 2000
+++ linux-2.2.17/net/ipv4/af_inet.c Mon Oct 16 20:39:48 2000
@@ -109,6 +109,11 @@
#ifdef CONFIG_BRIDGE
#include
#endif
+
+#ifdef CONFIG_NET_DIVERT
+#include
+#endif /* CONFIG_NET_DIVERT */
+
#ifdef CONFIG_KMOD
#include
#endif
@@ -912,6 +917,14 @@
return -ENOPKG;
#endif
+ case SIOCGIFDIVERT:
+ case SIOCSIFDIVERT:
+#ifdef CONFIG_NET_DIVERT
+ return(divert_ioctl(cmd, (struct divert_cf *) arg));
+#else
+ return -ENOPKG;
+#endif /* CONFIG_NET_DIVERT */
+
case SIOCADDDLCI:
case SIOCDELDLCI:
#ifdef CONFIG_DLCI
diff -Naur linux/net/netsyms.c linux-2.2.17/net/netsyms.c
--- linux/net/netsyms.c Wed Jun 7 21:26:44 2000
+++ linux-2.2.17/net/netsyms.c Mon Oct 16 20:39:48 2000
@@ -33,6 +33,10 @@
#include
#endif
+#ifdef CONFIG_NET_DIVERT
+#include
+#endif /* CONFIG_NET_DIVERT */
+
#ifdef CONFIG_INET
#include
#include
@@ -224,6 +228,12 @@
#ifdef CONFIG_BRIDGE
EXPORT_SYMBOL(br_ioctl);
#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.2.17/net/packet/af_packet.c
--- linux/net/packet/af_packet.c Thu May 4 00:16:54 2000
+++ linux-2.2.17/net/packet/af_packet.c Mon Oct 16 20:39:48 2000
@@ -72,6 +72,10 @@
#include
#endif
+#ifdef CONFIG_NET_DIVERT
+#include
+#endif /* CONFIG_NET_DIVERT */
+
#ifdef CONFIG_DLCI
extern int dlci_ioctl(unsigned int, void*);
#endif
@@ -1132,6 +1136,14 @@
#else
return -ENOPKG;
#endif
+
+ case SIOCGIFDIVERT:
+ case SIOCSIFDIVERT:
+#ifdef CONFIG_NET_DIVERT
+ return(divert_ioctl(cmd, (struct divert_cf *) arg));
+ #else
+ return -ENOPKG;
+#endif /* CONFIG_NET_DIVERT */
#ifdef CONFIG_INET
case SIOCADDRT: