URL
https://opencores.org/ocsvn/or1k_old/or1k_old/trunk
Subversion Repositories or1k_old
[/] [or1k_old/] [trunk/] [uclinux/] [uClinux-2.0.x/] [net/] [core/] [net_alias.c] - Rev 1765
Go to most recent revision | Compare with Previous | Blame | View Log
/* * NET_ALIAS network device aliasing module. * * * Version: @(#)net_alias.c 0.50 4/20/97 * * * Authors: Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar> * Marcelo Fabian Roccasalva, <mfroccas@raiz.uncu.edu.ar> * * Features: * - AF_ independent: net_alias_type objects * - AF_INET optimized * - ACTUAL alias devices inserted in dev chain * - fast hashed alias address lookup * - net_alias_type objs registration/unreg., module-ables. * - /proc/net/aliases & /proc/net/alias_types entries * - tx and rx stats * - /proc/sys/net/core/net_alias_max entry * Fixes: * Juan Jose Ciarlante : several net_alias_type func. renamed. * Juan Jose Ciarlante : net_alias_type object methods now pass * *this. * Juan Jose Ciarlante : xxx_rcv device selection based on <src,dst> addrs * Andreas Schultz : Kerneld support. * Juan Jose Ciarlante : Added tx/rx stats for aliases. * Juan Jose Ciarlante : Added sysctl interface for max aliases per device * * FIXME: * - User calls sleep/wake_up locking. * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * */ #include <linux/config.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/netdevice.h> #include <linux/notifier.h> #include <linux/if.h> #include <linux/if_ether.h> #include <linux/inet.h> #include <linux/in.h> #include <linux/proc_fs.h> #include <linux/stat.h> #include <linux/sysctl.h> #ifdef ALIAS_USER_LAND_DEBUG #include "net_alias.h" #include "user_stubs.h" #endif #include <linux/net_alias.h> #ifdef CONFIG_KERNELD #include <linux/kerneld.h> #endif /* * NET_ALIAS_MAX_DEFAULT: max. alias slot number allowed by default, * can be changed by sysctl * NET_ALIAS_HASH_TAB_SIZE: hash table size (addr lookup), 1 per aliased * device, due to hash optimizations, MUST be 16 or 256 only. */ #define NET_ALIAS_MAX_DEFAULT 256 /* DO NOT CHANGE the line below ! */ #define NET_ALIAS_HASH_TAB_SIZE(n) ( ((n)>=NET_ALIAS_MAX_DEFAULT) ? 256 : 16 ) /* * set default max_aliases per device */ int sysctl_net_alias_max = NET_ALIAS_MAX_DEFAULT; /* * Only allow the following flags to pass from main device to aliases * Note that IFF_BROADCAST is not passed by default, this make sense * because: * a) if same-net alias: broadcasts are already handled by main device * b) if diff-net alias: bcasts will be set by 'broadcast' ifconfig opt. * I prefer this approach instead of setting '-broadcast' for each * same-net alias device --JJC. */ #define NET_ALIAS_IFF_MASK (IFF_SOFTHEADERS|IFF_NOARP|IFF_LOOPBACK|IFF_POINTOPOINT) static struct net_alias_type * nat_getbytype(int type); static int nat_attach_chg(struct net_alias_type *nat, int delta); static int nat_bind(struct net_alias_type *nat,struct net_alias *alias, struct sockaddr *sa); static int nat_unbind(struct net_alias_type *nat, struct net_alias *alias); static int net_alias_devinit(struct device *dev); static struct enet_statistics *net_alias_dev_stats(struct device *dev); static int net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev); static int net_alias_devsetup(struct net_alias *alias, struct net_alias_type *nat, struct sockaddr *sa); static struct net_alias **net_alias_slow_findp(struct net_alias_info *alias_info, struct net_alias *alias); static struct device *net_alias_dev_create(struct device *main_dev, int slot, int *err, struct sockaddr *sa, void *data); static struct device *net_alias_dev_delete(struct device *main_dev, int slot, int *err); static void net_alias_free(struct device *dev); /* * net_alias_type base array, will hold net_alias_type obj hashed list heads. */ struct net_alias_type *net_alias_type_base[16]; /* * get_stats function: return rx/tx pkts based on lookups */ static struct enet_statistics *net_alias_dev_stats(struct device *dev) { static struct enet_statistics alias_stats; struct net_alias *alias = dev->my_alias; memset (&alias_stats, 0, sizeof (alias_stats)); alias_stats.rx_packets = alias->rx_lookups; alias_stats.tx_packets = alias->tx_lookups; return &alias_stats; } /* * get net_alias_type ptr by type */ static __inline__ struct net_alias_type * nat_getbytype(int type) { struct net_alias_type *nat; for(nat = net_alias_type_base[type & 0x0f]; nat ; nat = nat->next) { if (nat->type == type) return nat; } return NULL; } /* * get addr32 representation (pre-hashing) of address. * if NULL nat->get_addr32, assume sockaddr_in struct (IP-ish). */ static __inline__ __u32 nat_addr32(struct net_alias_type *nat, struct sockaddr *sa) { if (nat->get_addr32) return nat->get_addr32(nat, sa); else return (*(struct sockaddr_in *)sa).sin_addr.s_addr; } /* * hashing code for alias_info->hash_tab entries * 4 bytes -> 1/2 byte using xor complemented by af */ static __inline__ unsigned hash_key(unsigned hsize, __u32 addr) { unsigned tmp = addr ^ (addr>>16); /* 4 -> 2 */ tmp ^= (tmp>>8); /* 2 -> 1 */ if (hsize == 256) return (tmp & 0xff); else return (tmp^(tmp>>4)) & 0x0f; /* 1 -> 1/2 */ } /* * get hash key for supplied net alias type and address * nat must be !NULL * the purpose here is to map a net_alias_type and a generic * address to a hash code. */ static __inline__ int nat_hash_key(struct net_alias_type *nat, unsigned hsize, struct sockaddr *sa) { return hash_key(hsize, nat_addr32(nat,sa)); } /* * change net_alias_type number of attachments (bindings) */ static int nat_attach_chg(struct net_alias_type *nat, int delta) { unsigned long flags; int n_at; if (!nat) return -1; save_flags(flags); cli(); n_at = nat->n_attach + delta; if (n_at < 0) { restore_flags(flags); printk(KERN_WARNING "net_alias: tried to set n_attach < 0 for (family==%d) nat object.\n", nat->type); return -1; } nat->n_attach = n_at; restore_flags(flags); return 0; } /* * bind alias to its type (family) object and call initialization hook */ static __inline__ int nat_bind(struct net_alias_type *nat,struct net_alias *alias, struct sockaddr *sa) { if (nat->alias_init_1) nat->alias_init_1(nat, alias, sa); return nat_attach_chg(nat, +1); } /* * unbind alias from type object and call alias destructor */ static __inline__ int nat_unbind(struct net_alias_type *nat, struct net_alias *alias) { if (nat->alias_done_1) nat->alias_done_1(nat, alias); return nat_attach_chg(nat, -1); } /* * compare device address with given. if NULL nat->dev_addr_chk, * compare dev->pa_addr with (sockaddr_in) 32 bits address (IP-ish) */ static __inline__ int nat_dev_addr_chk_1(struct net_alias_type *nat, struct device *dev, struct sockaddr *sa) { if (nat->dev_addr_chk) return nat->dev_addr_chk(nat, dev, sa); else return (dev->pa_addr == (*(struct sockaddr_in *)sa).sin_addr.s_addr); } /* * alias device init() * do nothing. */ static int net_alias_devinit(struct device *dev) { #ifdef ALIAS_USER_LAND_DEBUG printk("net_alias_devinit(%s) called.\n", dev->name); #endif return 0; } /* * hard_start_xmit() should not be called. * ignore ... but shout!. */ static int net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev) { printk(KERN_WARNING "net_alias: net_alias_hard_start_xmit() for %s called (ignored)!!\n", dev->name); dev_kfree_skb(skb, FREE_WRITE); return 0; } static int net_alias_dev_open(struct device * dev) { return 0; } static int net_alias_dev_close(struct device * dev) { return 0; } /* * setups a new (alias) device */ static int net_alias_devsetup(struct net_alias *alias, struct net_alias_type *nat, struct sockaddr *sa) { struct device *main_dev; struct device *dev; int family; int i; /* * * generic device setup based on main_dev info * * FIXME: is NULL bitwise 0 for all Linux platforms? */ main_dev = alias->main_dev; dev = &alias->dev; memset(dev, '\0', sizeof(struct device)); family = (sa)? sa->sa_family : main_dev->family; alias->rx_lookups = 0; alias->tx_lookups = 0; dev->alias_info = NULL; /* no aliasing recursion */ dev->my_alias = alias; /* point to alias */ dev->name = alias->name; dev->type = main_dev->type; dev->open = net_alias_dev_open; dev->stop = net_alias_dev_close; dev->get_stats = net_alias_dev_stats; dev->hard_header_len = main_dev->hard_header_len; memcpy(dev->broadcast, main_dev->broadcast, MAX_ADDR_LEN); memcpy(dev->dev_addr, main_dev->dev_addr, MAX_ADDR_LEN); dev->addr_len = main_dev->addr_len; dev->init = net_alias_devinit; dev->hard_start_xmit = net_alias_hard_start_xmit; dev->flags = main_dev->flags & NET_ALIAS_IFF_MASK & ~IFF_UP; /* * only makes sense if same family */ if (family == main_dev->family) { dev->metric = main_dev->metric; dev->mtu = main_dev->mtu; dev->pa_alen = main_dev->pa_alen; dev->hard_header = main_dev->hard_header; dev->rebuild_header = main_dev->rebuild_header; } /* * Fill in the generic fields of the device structure. * not actually used, avoids some dev.c #ifdef's */ for (i = 0; i < DEV_NUMBUFFS; i++) skb_queue_head_init(&dev->buffs[i]); dev->family = family; return 0; } /* * slow alias find (parse the whole hash_tab) * returns: alias' pointer address */ static struct net_alias ** net_alias_slow_findp(struct net_alias_info *alias_info, struct net_alias *alias) { unsigned idx, n_aliases; struct net_alias **aliasp; /* * for each alias_info's hash_tab entry, for every alias ... */ n_aliases = alias_info->n_aliases; for (idx=0; idx < alias_info->hash_tab_size ; idx++) for (aliasp = &alias_info->hash_tab[idx];*aliasp;aliasp = &(*aliasp)->next) if (*aliasp == alias) return aliasp; else if (--n_aliases == 0) break; /* faster give up */ return NULL; } /* * create alias device for main_dev with given slot num. * if sa==NULL will create a same_family alias device */ static struct device * net_alias_dev_create(struct device *main_dev, int slot, int *err, struct sockaddr *sa, void *data) { struct net_alias_info *alias_info; struct net_alias *alias, **aliasp; struct net_alias_type *nat; struct device *dev; unsigned long flags; int family; __u32 addr32; int max_aliases; /* FIXME: lock */ alias_info = main_dev->alias_info; /* * if NULL address given, take family from main_dev */ family = (sa)? sa->sa_family : main_dev->family; /* * check if wanted family has a net_alias_type object registered */ nat = nat_getbytype(family); if (!nat) { #ifdef CONFIG_KERNELD char modname[20]; sprintf (modname,"netalias-%d", family); request_module(modname); nat = nat_getbytype(family); if (!nat) { #endif printk(KERN_WARNING "net_alias_dev_create(%s:%d): unregistered family==%d\n", main_dev->name, slot, family); /* *err = -EAFNOSUPPORT; */ *err = -EINVAL; return NULL; #ifdef CONFIG_KERNELD } #endif } *err = -EINVAL; if (!alias_info) /* * At this point we take the sysctl value(s) */ max_aliases = sysctl_net_alias_max; else max_aliases = alias_info->max_aliases; if (slot >= max_aliases ) /* 0-based (eth0:0 _IS_ valid) */ return NULL; /* * do not allow creation over downed devices */ *err = -EIO; if (! (main_dev->flags & IFF_UP) ) return NULL; /* * if first alias, must also create alias_info */ *err = -ENOMEM; if (!alias_info) { /* * Allocate space for struct net_alias_info plus hash table */ int truesize; /* net_alias_info size */ truesize = sizeof(struct net_alias_info); /* add hash_tab size * sizeof(elem) */ truesize += NET_ALIAS_HASH_TAB_SIZE(max_aliases) * sizeof (struct net_alias *); alias_info = kmalloc( truesize , GFP_KERNEL); if (!alias_info) return NULL; /* ENOMEM */ memset(alias_info, 0, truesize); alias_info->truesize = truesize; alias_info->max_aliases = max_aliases; alias_info->hash_tab_size = NET_ALIAS_HASH_TAB_SIZE(max_aliases); } if (!(alias = kmalloc(sizeof(struct net_alias), GFP_KERNEL))) return NULL; /* ENOMEM */ /* * FIXME: is NULL bitwise 0 for all Linux platforms? */ memset(alias, 0, sizeof(struct net_alias)); alias->slot = slot; alias->main_dev = main_dev; alias->nat = nat; alias->next = NULL; alias->data = data; sprintf(alias->name, "%s:%d", main_dev->name, slot); /* * initialise alias' device structure */ net_alias_devsetup(alias, nat, sa); dev = &alias->dev; save_flags(flags); cli(); /* * bind alias to its object type * nat_bind calls nat->alias_init_1 */ nat_bind(nat, alias, sa); /* * if no address passed, take from device (could have been * set by nat->alias_init_1) */ addr32 = (sa)? nat_addr32(nat, sa) : alias->dev.pa_addr; /* * store hash key in alias: will speed-up rehashing and deletion */ alias->hash = hash_key(alias_info->hash_tab_size, addr32); /* * insert alias in hashed linked list */ aliasp = &alias_info->hash_tab[alias->hash]; alias->next = *aliasp; *aliasp = alias; /* * if first alias ... */ if (!alias_info->n_aliases++) { alias_info->taildev = main_dev; main_dev->alias_info = alias_info; } /* * add device at tail (just after last main_dev alias) */ dev->next = alias_info->taildev->next; alias_info->taildev->next = dev; alias_info->taildev = dev; restore_flags(flags); return dev; } /* * delete one main_dev alias (referred by its slot num) */ static struct device * net_alias_dev_delete(struct device *main_dev, int slot, int *err) { struct net_alias_info *alias_info; struct net_alias *alias, **aliasp; struct device *dev; unsigned n_aliases; unsigned long flags; struct net_alias_type *nat; struct device *prevdev; /* FIXME: lock */ *err = -ENODEV; if (main_dev == NULL) return NULL; /* * does main_dev have aliases? */ alias_info = main_dev->alias_info; if (!alias_info) return NULL; /* ENODEV */ n_aliases = alias_info->n_aliases; /* * find device that holds the same slot number (could also * be strcmp() ala dev_get). */ for (prevdev=main_dev, alias = NULL;prevdev->next && n_aliases; prevdev = prevdev->next) { if (!(alias = prevdev->next->my_alias)) { printk(KERN_ERR "net_alias_dev_delete(): incorrect non-alias device after maindev\n"); continue; /* or should give up? */ } if (alias->slot == slot) break; alias = NULL; n_aliases--; } if (!alias) return NULL; /* ENODEV */ dev = &alias->dev; /* * find alias hashed entry */ for(aliasp = &alias_info->hash_tab[alias->hash]; *aliasp; aliasp = &(*aliasp)->next) if(*aliasp == alias) break; /* * if not found (???), try a full search */ if (*aliasp != alias) if ((aliasp = net_alias_slow_findp(alias_info, alias))) printk(KERN_WARNING "net_alias_dev_delete(%s): bad hashing recovered\n", alias->name); else { printk(KERN_ERR "net_alias_dev_delete(%s): unhashed alias!\n",alias->name); return NULL; /* ENODEV */ } nat = alias->nat; save_flags(flags); cli(); /* * unbind alias from alias_type obj. */ nat_unbind(nat, alias); /* * is alias at tail? */ if ( dev == alias_info->taildev ) alias_info->taildev = prevdev; /* * unlink and close device */ prevdev->next = dev->next; dev_close(dev); /* * unlink alias */ *aliasp = (*aliasp)->next; if (--alias_info->n_aliases == 0) /* last alias */ main_dev->alias_info = NULL; restore_flags(flags); /* * now free structures */ kfree_s(alias, sizeof(struct net_alias)); if (main_dev->alias_info == NULL) kfree_s(alias_info, alias_info->truesize); /* * deletion ok (*err=0), NULL device returned. */ *err = 0; return NULL; } /* * free all main device aliasing stuff * will be called on dev_close(main_dev) */ static void net_alias_free(struct device *main_dev) { struct net_alias_info *alias_info; struct net_alias *alias; struct net_alias_type *nat; struct device *dev; unsigned long flags; /* * do I really have aliases? */ if (!(alias_info = main_dev->alias_info)) return; /* * fast device link "short-circuit": set main_dev->next to * device after last alias */ save_flags(flags); cli(); dev = main_dev->next; main_dev->next = alias_info->taildev->next; main_dev->alias_info = NULL; alias_info->taildev->next = NULL; restore_flags(flags); /* * loop over alias devices, free and dev_close() */ while (dev) { if (net_alias_is(dev)) { alias = dev->my_alias; if (alias->main_dev == main_dev) { /* * unbind alias from alias_type object */ nat = alias->nat; if (nat) { nat_unbind(nat, alias); } /* else error/printk ??? */ dev_close(dev); dev = dev->next; kfree_s(alias, sizeof(struct net_alias)); continue; } else printk(KERN_ERR "net_alias_free(%s): '%s' is not my alias\n", main_dev->name, alias->name); } else printk(KERN_ERR "net_alias_free(%s): found a non-alias after device!\n", main_dev->name); dev = dev->next; } kfree_s(alias_info, sizeof(alias_info)); return; } /* * dev_get() with added alias naming magic. */ struct device * net_alias_dev_get(char *dev_name, int aliasing_ok, int *err, struct sockaddr *sa, void *data) { struct device *dev; char *sptr,*eptr; int slot = 0; int delete = 0; *err = -ENODEV; if ((dev=dev_get(dev_name))) return dev; /* * want alias naming magic? */ if (!aliasing_ok) return NULL; if (!dev_name || !*dev_name) return NULL; /* * find the first ':' , must be followed by, at least, 1 char */ for (sptr=dev_name ; *sptr ; sptr++) if(*sptr==':') break; if (!*sptr || !*(sptr+1)) return NULL; /* * seems to be an alias name, fetch main device */ *sptr='\0'; if (!(dev=dev_get(dev_name))) return NULL; *sptr++=':'; /* * fetch slot number */ slot = simple_strtoul(sptr,&eptr,10); /* * if last char is '-', it is a deletion request */ if (eptr[0] == '-' && !eptr[1] ) delete++; else if (eptr[0]) return NULL; /* * well... let's work. */ if (delete) return net_alias_dev_delete(dev, slot, err); else return net_alias_dev_create(dev, slot, err, sa, data); } /* * rehash alias device with address supplied. */ int net_alias_dev_rehash(struct device *dev, struct sockaddr *sa) { struct net_alias_info *alias_info; struct net_alias *alias, **aliasp; struct device *main_dev; unsigned long flags; struct net_alias_type *o_nat, *n_nat; unsigned n_hash; /* * defensive ... */ if (dev == NULL) return -1; if ( (alias = dev->my_alias) == NULL ) return -1; if (!sa) { printk(KERN_ERR "net_alias_rehash(): NULL sockaddr passed\n"); return -1; } /* * defensive. should not happen. */ if ( (main_dev = alias->main_dev) == NULL ) { printk(KERN_ERR "net_alias_rehash for %s: NULL maindev\n", alias->name); return -1; } /* * defensive. should not happen. */ if (!(alias_info=main_dev->alias_info)) { printk(KERN_ERR "net_alias_rehash for %s: NULL alias_info\n", alias->name); return -1; } /* * will the request also change device family? */ o_nat = alias->nat; if (!o_nat) { printk(KERN_ERR "net_alias_rehash(%s): unbound alias.\n", alias->name); return -1; } /* * point to new alias_type obj. */ if (o_nat->type == sa->sa_family) n_nat = o_nat; else { n_nat = nat_getbytype(sa->sa_family); if (!n_nat) { printk(KERN_ERR "net_alias_rehash(%s): unreg family==%d.\n", alias->name, sa->sa_family); return -1; } } /* * new hash key. if same as old AND same type (family) return; */ n_hash = nat_hash_key(n_nat, alias_info->hash_tab_size, sa); if (n_hash == alias->hash && o_nat == n_nat ) return 0; /* * find alias in hashed list */ for (aliasp = &alias_info->hash_tab[alias->hash]; *aliasp; aliasp = &(*aliasp)->next) if (*aliasp == alias) break; /* * not found (???). try a full search */ if(!*aliasp) if ((aliasp = net_alias_slow_findp(alias_info, alias))) printk(KERN_WARNING "net_alias_rehash(%s): bad hashing recovered\n", alias->name); else { printk(KERN_ERR "net_alias_rehash(%s): unhashed alias!\n", alias->name); return -1; } save_flags(flags); cli(); /* * if type (family) changed, unlink from old type object (o_nat) * will call o_nat->alias_done_1() */ if (o_nat != n_nat) nat_unbind(o_nat, alias); /* * if diff hash key, change alias position in hashed list */ if (n_hash != alias->hash) { *aliasp = (*aliasp)->next; alias->hash = n_hash; aliasp = &alias_info->hash_tab[n_hash]; alias->next = *aliasp; *aliasp = alias; } /* * if type (family) changed link to new type object (n_nat) * will call n_nat->alias_init_1() */ if (o_nat != n_nat) nat_bind(n_nat, alias, sa); restore_flags(flags); return 0; } /* * implements /proc/net/alias_types entry * shows net_alias_type objects registered. */ int net_alias_types_getinfo(char *buffer, char **start, off_t offset, int length, int dummy) { off_t pos=0, begin=0; int len=0; struct net_alias_type *nat; unsigned idx; len=sprintf(buffer,"type name n_attach\n"); for (idx=0 ; idx < 16 ; idx++) for (nat = net_alias_type_base[idx]; nat ; nat = nat->next) { len += sprintf(buffer+len, "%-7d %-15s %-7d\n", nat->type, nat->name,nat->n_attach); pos=begin+len; if(pos<offset) { len=0; begin=pos; } if(pos>offset+length) break; } *start=buffer+(offset-begin); len-=(offset-begin); if(len>length) len=length; return len; } /* * implements /proc/net/aliases entry, shows alias devices. * calls alias nat->alias_print_1 if not NULL and formats everything * to a fixed rec. size without using local (stack) buffers * */ #define NET_ALIASES_RECSIZ 64 int net_alias_getinfo(char *buffer, char **start, off_t offset, int length, int dummy) { off_t pos=0, begin=0; int len=0; int dlen; struct net_alias_type *nat; struct net_alias *alias; struct device *dev; len=sprintf(buffer,"%-*s\n",NET_ALIASES_RECSIZ-1,"device family address"); for (dev = dev_base; dev ; dev = dev->next) if (net_alias_is(dev)) { alias = dev->my_alias; nat = alias->nat; dlen=sprintf(buffer+len, "%-16s %-6d ", alias->name, alias->dev.family); /* * call alias_type specific print function. */ if (nat->alias_print_1) dlen += nat->alias_print_1(nat, alias, buffer+len+dlen, NET_ALIASES_RECSIZ - dlen); else dlen += sprintf(buffer+len+dlen, "-"); /* * fill with spaces if needed */ if (dlen < NET_ALIASES_RECSIZ) memset(buffer+len+dlen, ' ', NET_ALIASES_RECSIZ - dlen); /* * truncate to NET_ALIASES_RECSIZ */ len += NET_ALIASES_RECSIZ; buffer[len-1] = '\n'; pos=begin+len; if(pos<offset) { len=0; begin=pos; } if(pos>offset+length) break; } *start=buffer+(offset-begin); len-=(offset-begin); if(len>length) len=length; return len; } /* * notifier for devices events */ int net_alias_device_event(struct notifier_block *this, unsigned long event, void *ptr) { struct device *dev = ptr; if (event == NETDEV_DOWN) { #ifdef ALIAS_USER_LAND_DEBUG printk("net_alias: NETDEV_DOWN for %s received\n", dev->name); #endif if (net_alias_has(dev)) net_alias_free(dev); } if (event == NETDEV_UP) { #ifdef ALIAS_USER_LAND_DEBUG printk("net_alias: NETDEV_UP for %s received\n", dev->name); #endif dev->alias_info = 0; } return NOTIFY_DONE; } /* * device aliases address comparison workhorse * no checks for nat and alias_info, must be !NULL */ static __inline__ struct device * nat_addr_chk(struct net_alias_type *nat, struct net_alias_info *alias_info, struct sockaddr *sa, int flags_on, int flags_off) { struct net_alias *alias; unsigned hsize = alias_info->hash_tab_size; for(alias = alias_info->hash_tab[nat_hash_key(nat,hsize,sa)]; alias; alias = alias->next) { if (alias->dev.family != sa->sa_family) continue; /* * nat_dev_addr_chk_1 will call type specific address cmp function. */ if (alias->dev.flags & flags_on && !(alias->dev.flags & flags_off) && nat_dev_addr_chk_1(nat,&alias->dev,sa)) return &alias->dev; } return NULL; } /* * nat_addr_chk enough for protocols whose addr is (fully) stored at pa_addr. * note that nat pointer is ignored because of static comparison. */ static __inline__ struct device * nat_addr_chk32(struct net_alias_type *nat, struct net_alias_info *alias_info, int family, __u32 addr32, int flags_on, int flags_off) { struct net_alias *alias; unsigned hsize = alias_info->hash_tab_size; for (alias=alias_info->hash_tab[hash_key(hsize, addr32)]; alias; alias=alias->next) { if (alias->dev.family != family) continue; /* * "hard" (static) comparison between addr32 and pa_addr. */ if (alias->dev.flags & flags_on && !(alias->dev.flags & flags_off) && addr32 == alias->dev.pa_addr) return &alias->dev; } return NULL; } /* * returns alias device with specified address AND flags_on AND flags_off, * else NULL. * intended for main devices. */ struct device * net_alias_dev_chk(struct device *main_dev, struct sockaddr *sa,int flags_on, int flags_off) { struct net_alias_info *alias_info = main_dev->alias_info; struct net_alias_type *nat; /* * only if main_dev has aliases */ if (!alias_info) return NULL; /* * get alias_type object for sa->sa_family. */ nat = nat_getbytype(sa->sa_family); if (!nat) return NULL; return nat_addr_chk(nat, alias_info, sa, flags_on, flags_off); } /* * net_alias_dev_chk enough for protocols whose addr is (fully) stored * at pa_addr. */ struct device * net_alias_dev_chk32(struct device *main_dev, int family, __u32 addr32, int flags_on, int flags_off) { struct net_alias_info *alias_info = main_dev->alias_info; /* * only if main_dev has aliases */ if (!alias_info) return NULL; return nat_addr_chk32(NULL, alias_info, family, addr32, flags_on, flags_off); } /* * select closest (main or alias) device to <src,dst> addresses given. if no * further info is available, return main_dev (for easier calling arrangement). * * Should be called early at xxx_rcv() time for device selection */ struct device * net_alias_dev_rx(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst) { int family; struct net_alias_type *nat; struct net_alias_info *alias_info; struct device *dev; if (main_dev == NULL) return NULL; /* * if not aliased, don't bother any more */ if ((alias_info = main_dev->alias_info) == NULL) return main_dev; /* * find out family */ family = (sa_src)? sa_src->sa_family : ((sa_dst)? sa_dst->sa_family : AF_UNSPEC); if (family == AF_UNSPEC) return main_dev; /* * get net_alias_type object for this family */ if ( (nat = nat_getbytype(family)) == NULL ) return main_dev; /* * first step: find out if dst addr is main_dev's or one of its aliases' */ if (sa_dst) { if (nat_dev_addr_chk_1(nat, main_dev,sa_dst)) return main_dev; dev = nat_addr_chk(nat, alias_info, sa_dst, IFF_UP, 0); if (dev != NULL) { net_alias_inc_rx(dev->my_alias); return dev; } } /* * second step: find the rcv addr 'closest' alias through nat method call */ if ( sa_src == NULL || nat->dev_select == NULL) return main_dev; dev = nat->dev_select(nat, main_dev, sa_src); if (dev == NULL || dev->family != family) return main_dev; /* * dev ok only if it is alias of main_dev */ if (net_alias_is(dev)) { struct net_alias *alias=dev->my_alias; if (alias->main_dev == main_dev) { net_alias_inc_rx(alias); return dev; } } /* * do not return NULL. */ return main_dev; } /* * dev_rx32: dev_rx selection for 'pa_addr' protocols. */ struct device * net_alias_dev_rx32(struct device *main_dev, int family, __u32 src, __u32 dst) { struct net_alias_type *nat; struct net_alias_info *alias_info; struct sockaddr_in sin_src; struct device *dev; if (main_dev == NULL) return NULL; /* * if not aliased, don't bother any more */ if ((alias_info = main_dev->alias_info) == NULL) return main_dev; /* * early return if dst is main_dev's address */ if (dst == main_dev->pa_addr) return main_dev; if (family == AF_UNSPEC) return main_dev; /* * get net_alias_type object for this family */ if ( (nat = nat_getbytype(family)) == NULL ) return main_dev; /* * first step: find out if dst address one of main_dev aliases' */ if (dst) { dev = nat_addr_chk32(nat, alias_info, family, dst, IFF_UP, 0); if (dev) { net_alias_inc_rx(dev->my_alias); return dev; } } /* * second step: find the rcv addr 'closest' alias through nat method call */ if ( src == 0 || nat->dev_select == NULL) return main_dev; sin_src.sin_family = family; sin_src.sin_addr.s_addr = src; dev = nat->dev_select(nat, main_dev, (struct sockaddr *)&sin_src); if (dev == NULL || dev->family != family) return main_dev; /* * dev ok only if it is alias of main_dev */ if (net_alias_is(dev)) { struct net_alias *alias=dev->my_alias; if (alias->main_dev == main_dev) { net_alias_inc_rx(alias); return dev; } } /* * do not return NULL. */ return main_dev; } /* * device event hook */ static struct notifier_block net_alias_dev_notifier = { net_alias_device_event, NULL, 0 }; /* * net_alias initialisation * called from net_dev_init(). */ #ifndef ALIAS_USER_LAND_DEBUG #ifdef CONFIG_PROC_FS static struct proc_dir_entry pde1 = { PROC_NET_ALIAS_TYPES, 11, "alias_types", S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, net_alias_types_getinfo }; static struct proc_dir_entry pde2 = { PROC_NET_ALIASES, 7, "aliases", S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, net_alias_getinfo }; #endif #endif void net_alias_init(void) { /* * register dev events notifier */ register_netdevice_notifier(&net_alias_dev_notifier); /* * register /proc/net entries */ #ifndef ALIAS_USER_LAND_DEBUG #ifdef CONFIG_PROC_FS proc_net_register(&pde1); proc_net_register(&pde2); #endif #endif } /* * net_alias type object registering func. */ int register_net_alias_type(struct net_alias_type *nat, int type) { unsigned hash; unsigned long flags; if (!nat) { printk(KERN_ERR "register_net_alias_type(): NULL arg\n"); return -EINVAL; } nat->type = type; nat->n_attach = 0; hash = nat->type & 0x0f; save_flags(flags); cli(); nat->next = net_alias_type_base[hash]; net_alias_type_base[hash] = nat; restore_flags(flags); return 0; } /* * net_alias type object unreg. */ int unregister_net_alias_type(struct net_alias_type *nat) { struct net_alias_type **natp; unsigned hash; unsigned long flags; if (!nat) { printk(KERN_ERR "unregister_net_alias_type(): NULL arg\n"); return -EINVAL; } /* * only allow unregistration if it has no attachments */ if (nat->n_attach) { printk(KERN_ERR "unregister_net_alias_type(): has %d attachments. failed\n", nat->n_attach); return -EINVAL; } hash = nat->type & 0x0f; save_flags(flags); cli(); for (natp = &net_alias_type_base[hash]; *natp ; natp = &(*natp)->next) { if (nat==(*natp)) { *natp = nat->next; restore_flags(flags); return 0; } } restore_flags(flags); printk(KERN_ERR "unregister_net_alias_type(type=%d): not found!\n", nat->type); return -EINVAL; } /* * Log sysctl's net_alias_max changes. */ int proc_do_net_alias_max(ctl_table *ctl, int write, struct file *filp, void *buffer, size_t *lenp) { int old = sysctl_net_alias_max; int ret; ret = proc_dointvec(ctl, write, filp, buffer, lenp); if (write) { if (sysctl_net_alias_max != old) { printk(KERN_INFO "sysctl: net_alias_max changed (max.aliases=%d, hashsize=%d).\n", sysctl_net_alias_max, NET_ALIAS_HASH_TAB_SIZE(sysctl_net_alias_max)); if (!sysctl_net_alias_max) printk(KERN_INFO "sysctl: net_alias creation disabled.\n"); if (!old) printk(KERN_INFO "sysctl: net_alias creation enabled.\n"); } } return ret; }
Go to most recent revision | Compare with Previous | Blame | View Log