/* * Shortbridge - short-circuited bridge * Copyright (C) 2007 * Somebody * */ #define MAX_SHORTBRIDGES 20 #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct shortbridge_entry { struct list_head list; /* My bead in the rosary */ char first_dev[IFNAMSIZ]; struct net_device **dev1; struct net_device **dev2; }; static LIST_HEAD(shortbridges); int atoi(char *s) { int sum=0; while (*s) { int digit=(*s)-48; sum=10*sum+digit; s++; } return sum; } static int sb_shortcircuit(net_t net, const char *name0, const char *name1, char *number) { struct shortbridge_entry *ent; struct net_device *dev1, *dev2; int numentries = atoi(number); int i=0; printk(KERN_CRIT "Short circuiting %s objects in device classes %s and %s\n",number,name0,name1); ent = (struct shortbridge_entry *) kmalloc(sizeof(struct shortbridge_entry), GFP_KERNEL); ent->dev1 = (struct net_device ** ) kmalloc(numentries * sizeof(struct net_device *), GFP_KERNEL); ent->dev2 = (struct net_device ** ) kmalloc(numentries * sizeof(struct net_device *), GFP_KERNEL); if (ent == NULL) { printk(KERN_CRIT "kmalloc failed for shortbridge_entry"); return -ENOMEM; } for(i=0;i %s\n",name0i,name1i); dev1 = dev_get_by_name(net, name0i); if (dev1 == NULL) { printk(KERN_CRIT "Can't find device %s (first device)",name0i); return -ENOMEM; } dev2 = dev_get_by_name(net, name1i); if (dev2 == NULL) { printk(KERN_CRIT "Can't find device %s (second device)",name1i); return -ENOMEM; } rtnl_lock(); ent->dev1[i] = dev1; //dev1->hard_start_xmit = dev2->hard_start_xmit; ent->dev2[i] = dev2; rtnl_unlock(); } list_add(&ent->list,&shortbridges); return 0; } static int sb_newsb(const char *val, struct kernel_param *kp) { char name0[IFNAMSIZ], name1[IFNAMSIZ],number[4]; const char *mid,*last; int len, len0, len01,len1; if (!capable(CAP_NET_ADMIN)) return -EPERM; /* Avoid frustration by removing trailing whitespace */ len = strlen(val); while (isspace(val[len - 1])) len--; /* Split the string into 2 names */ mid = memchr(val, ',', len); if (!mid) { printk(KERN_CRIT "Error!\n"); return -EINVAL; } /* Get the first device name */ len0 = mid - val; if (len0 > sizeof(name0) - 1) len0 = sizeof(name0) - 1; strncpy(name0, val, len0); name0[len0] = '\0'; len-=(len0+1); last = memchr(mid+1, ',', len); if (!last) { printk(KERN_CRIT "Error!\n"); return -EINVAL; } /* Get the second device name */ len01 = last - mid - 1; if (len01 > sizeof(name0) - 1) len0 = sizeof(name0) - 1; strncpy(name1, mid+1, len01); name1[len01] = '\0'; /* And the number device name */ len1 = len - (len01 + 1); if (len1 > sizeof(name1) - 1) len1 = sizeof(name1) - 1; strncpy(number, last + 1, len1); number[len1] = '\0'; return sb_shortcircuit(current->nsproxy->net_ns, name0, name1, number); } static int sb_delsb(const char *val, struct kernel_param *kp) { char name[IFNAMSIZ]; int len; struct list_head *cur, *tmp; int err=0; if (!capable(CAP_NET_ADMIN)) return -EPERM; /* Avoid frustration by removing trailing whitespace */ len = strlen(val); while (isspace(val[len - 1])) len--; /* Get the device name */ if (len > sizeof(name) - 1) { printk(KERN_CRIT "shortbridge: Input string too long\n"); return -EINVAL; } strncpy(name, val, len); name[len] = '\0'; rtnl_lock(); list_for_each_safe(cur, tmp, &shortbridges) { struct shortbridge_entry *ent; ent = list_entry (cur, struct shortbridge_entry, list); if (!strcmp(name, ent->first_dev)) { struct net_device *dev1 = ent->dev1; dev1->hard_start_xmit = ent->dev1; list_del (cur); kfree (ent); } } rtnl_unlock(); return err; } unsigned int sb_hook (struct sk_buff *skb, struct packet_type *pt,struct net_device *orig_dev) { struct list_head *cur, *tmp; /* Let's find our device - change this to use a hashtable instead of a list */ list_for_each_safe(cur, tmp, &shortbridges) { struct shortbridge_entry *ent; ent = list_entry (cur, struct shortbridge_entry, list); if (skb->this_packet_is_stoned == 0){ unsigned int dev1,dev2; unsigned int devb; dev1=*((unsigned int *) (skb->dev->name)); dev2=*((unsigned int *) (ent->dev2[0]->name)); devb=*((unsigned int *) (ent->dev1[0]->name)); if (dev1 == dev2) { //printk(KERN_CRIT "Splice: %s-->%s\n",skb->dev->name, ent->dev1[0]->name); char *number=(char *) skb->dev->name+4; int num=atoi(number); skb->dev = ent->dev1[num]; skb->this_packet_is_stoned = 1; } else if (dev1 == devb) { //printk(KERN_CRIT "Splice: %s-->%s\n",skb->dev->name, ent->dev2[0]->name); // char *number=(char *) skb->dev->name+4; int num=atoi(number); skb->dev = ent->dev2[num]; skb->this_packet_is_stoned = 1; } } } return NET_RX_SUCCESS; } static struct packet_type sb_type = { .type = __constant_htons(ETH_P_ALL), .func = sb_hook }; static int __init sb_init(void) { dev_add_pack(&sb_type); printk(KERN_CRIT "Shortbridge device driver v0.1 loaded\n"); return 0; } static int sb_noget(char *buffer, struct kernel_param *kp) { return 0; } static void sb_cleanup(void) { struct list_head *cur, *tmp; rtnl_lock(); list_for_each_safe(cur, tmp, &shortbridges) { struct shortbridge_entry *ent; struct net_device *dev1; ent = list_entry (cur, struct shortbridge_entry, list); dev1 = ent->dev1; list_del (cur); kfree (ent); } rtnl_unlock(); dev_remove_pack(&sb_type); } module_param_call(newsb, sb_newsb, sb_noget, NULL, S_IWUSR); module_param_call(delsb, sb_delsb, sb_noget, NULL, S_IWUSR); module_init(sb_init); module_exit(sb_cleanup); MODULE_DESCRIPTION("Short bridge device driver"); MODULE_AUTHOR("Sapan Bhatia "); MODULE_LICENSE("GPL");