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: