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;