patch-2.0.31 linux/net/core/net_alias.c
Next file: linux/net/core/sock.c
Previous file: linux/net/core/dev.c
Back to the patch index
Back to the overall index
- Lines: 474
- Date:
Tue Aug 12 14:15:56 1997
- Orig file:
v2.0.30/linux/net/core/net_alias.c
- Orig date:
Sat Aug 31 23:15:34 1996
diff -u --recursive --new-file v2.0.30/linux/net/core/net_alias.c linux/net/core/net_alias.c
@@ -2,7 +2,8 @@
* NET_ALIAS network device aliasing module.
*
*
- * Version: @(#)net_alias.c 0.43 12/20/95
+ * 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>
@@ -14,13 +15,16 @@
* - 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:
- * JJC : several net_alias_type func. renamed.
- * JJC : net_alias_type object methods now pass
+ * Juan Jose Ciarlante : several net_alias_type func. renamed.
+ * Juan Jose Ciarlante : net_alias_type object methods now pass
* *this.
- * JJC : xxx_rcv device selection based on <src,dst>
- * addrs
+ * 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.
@@ -39,10 +43,12 @@
#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"
@@ -55,11 +61,35 @@
#include <linux/kerneld.h>
#endif
+
/*
- * Only allow the following flags to pass from main device to aliases
+ * 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_IFF_MASK (IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_NOARP|IFF_LOOPBACK|IFF_POINTOPOINT)
+#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);
@@ -68,6 +98,7 @@
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);
@@ -79,7 +110,22 @@
* net_alias_type base array, will hold net_alias_type obj hashed list heads.
*/
-struct net_alias_type *nat_base[16];
+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;
+}
/*
@@ -90,7 +136,7 @@
nat_getbytype(int type)
{
struct net_alias_type *nat;
- for(nat = nat_base[type & 0x0f]; nat ; nat = nat->next)
+ for(nat = net_alias_type_base[type & 0x0f]; nat ; nat = nat->next)
{
if (nat->type == type) return nat;
}
@@ -119,11 +165,14 @@
*/
static __inline__ unsigned
-HASH(__u32 addr, int af)
+hash_key(unsigned hsize, __u32 addr)
{
unsigned tmp = addr ^ (addr>>16); /* 4 -> 2 */
tmp ^= (tmp>>8); /* 2 -> 1 */
- return (tmp^(tmp>>4)^af) & 0x0f; /* 1 -> 1/2 */
+ if (hsize == 256)
+ return (tmp & 0xff);
+ else
+ return (tmp^(tmp>>4)) & 0x0f; /* 1 -> 1/2 */
}
@@ -135,9 +184,9 @@
*/
static __inline__ int
-nat_hash_key(struct net_alias_type *nat, struct sockaddr *sa)
+nat_hash_key(struct net_alias_type *nat, unsigned hsize, struct sockaddr *sa)
{
- return HASH(nat_addr32(nat,sa), sa->sa_family);
+ return hash_key(hsize, nat_addr32(nat,sa));
}
@@ -236,13 +285,13 @@
static int
-net_alias_open(struct device * dev)
+net_alias_dev_open(struct device * dev)
{
return 0;
}
static int
-net_alias_close(struct device * dev)
+net_alias_dev_close(struct device * dev)
{
return 0;
}
@@ -271,13 +320,16 @@
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_open;
- dev->stop = net_alias_close;
+ 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);
@@ -328,7 +380,7 @@
*/
n_aliases = alias_info->n_aliases;
- for (idx=0; idx < 16 ; idx++)
+ 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;
@@ -353,6 +405,7 @@
unsigned long flags;
int family;
__u32 addr32;
+ int max_aliases;
/* FIXME: lock */
alias_info = main_dev->alias_info;
@@ -387,6 +440,19 @@
#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
*/
@@ -403,10 +469,25 @@
*err = -ENOMEM;
if (!alias_info)
- {
- alias_info = kmalloc(sizeof(struct net_alias_info), GFP_KERNEL);
- if (!alias_info) return NULL; /* ENOMEM */
- memset(alias_info, 0, sizeof(struct net_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)))
@@ -453,7 +534,7 @@
* store hash key in alias: will speed-up rehashing and deletion
*/
- alias->hash = HASH(addr32, family);
+ alias->hash = hash_key(alias_info->hash_tab_size, addr32);
/*
* insert alias in hashed linked list
@@ -595,7 +676,7 @@
kfree_s(alias, sizeof(struct net_alias));
if (main_dev->alias_info == NULL)
- kfree_s(alias_info, sizeof(struct net_alias_info));
+ kfree_s(alias_info, alias_info->truesize);
/*
* deletion ok (*err=0), NULL device returned.
@@ -723,14 +804,12 @@
if (!(dev=dev_get(dev_name)))
return NULL;
*sptr++=':';
-
+
/*
* fetch slot number
*/
slot = simple_strtoul(sptr,&eptr,10);
- if (slot >= NET_ALIAS_MAX_SLOT)
- return NULL;
/*
* if last char is '-', it is a deletion request
@@ -829,7 +908,7 @@
* new hash key. if same as old AND same type (family) return;
*/
- n_hash = nat_hash_key(n_nat, sa);
+ n_hash = nat_hash_key(n_nat, alias_info->hash_tab_size, sa);
if (n_hash == alias->hash && o_nat == n_nat )
return 0;
@@ -905,7 +984,7 @@
unsigned idx;
len=sprintf(buffer,"type name n_attach\n");
for (idx=0 ; idx < 16 ; idx++)
- for (nat = nat_base[idx]; nat ; nat = nat->next)
+ 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);
@@ -1027,7 +1106,9 @@
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;
- for(alias = alias_info->hash_tab[nat_hash_key(nat,sa)];
+ 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;
@@ -1052,7 +1133,9 @@
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;
- for (alias=alias_info->hash_tab[HASH(addr32,family)];
+ 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;
@@ -1126,7 +1209,7 @@
*/
struct device *
-net_alias_dev_rcv_sel(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst)
+net_alias_dev_rx(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst)
{
int family;
struct net_alias_type *nat;
@@ -1166,7 +1249,10 @@
dev = nat_addr_chk(nat, alias_info, sa_dst, IFF_UP, 0);
- if (dev != NULL) return dev;
+ if (dev != NULL) {
+ net_alias_inc_rx(dev->my_alias);
+ return dev;
+ }
}
/*
@@ -1181,24 +1267,29 @@
/*
* dev ok only if it is alias of main_dev
*/
-
- dev = net_alias_is(dev)?
- ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL;
+ 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 (dev)? dev : main_dev;
+ return main_dev;
}
/*
- * dev_rcv_sel32: dev_rcv_sel for 'pa_addr' protocols.
+ * dev_rx32: dev_rx selection for 'pa_addr' protocols.
*/
struct device *
-net_alias_dev_rcv_sel32(struct device *main_dev, int family, __u32 src, __u32 dst)
+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;
@@ -1236,7 +1327,10 @@
if (dst)
{
dev = nat_addr_chk32(nat, alias_info, family, dst, IFF_UP, 0);
- if (dev) return dev;
+ if (dev) {
+ net_alias_inc_rx(dev->my_alias);
+ return dev;
+ }
}
/*
@@ -1255,19 +1349,23 @@
/*
* dev ok only if it is alias of main_dev
*/
-
- dev = net_alias_is(dev)?
- ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL;
+
+ 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 (dev)? dev : main_dev;
+ return main_dev;
}
-
/*
* device event hook
*/
@@ -1333,8 +1431,8 @@
hash = nat->type & 0x0f;
save_flags(flags);
cli();
- nat->next = nat_base[hash];
- nat_base[hash] = nat;
+ nat->next = net_alias_type_base[hash];
+ net_alias_type_base[hash] = nat;
restore_flags(flags);
return 0;
}
@@ -1366,7 +1464,7 @@
hash = nat->type & 0x0f;
save_flags(flags);
cli();
- for (natp = &nat_base[hash]; *natp ; natp = &(*natp)->next)
+ for (natp = &net_alias_type_base[hash]; *natp ; natp = &(*natp)->next)
{
if (nat==(*natp))
{
@@ -1380,3 +1478,26 @@
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;
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov