patch-1.3.51 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

diff -u --recursive --new-file v1.3.50/linux/net/core/net_alias.c linux/net/core/net_alias.c
@@ -1,7 +1,8 @@
 /*
- *		NET_ALIAS device aliasing module.
+ *		NET_ALIAS network device aliasing module.
  *
- * Version:	@(#)net_alias.c	0.42   12/11/95
+ *
+ * Version:	@(#)net_alias.c	0.43   12/20/95
  *
  * Authors:	Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
  *		Marcelo Fabian Roccasalva, <mfroccas@raiz.uncu.edu.ar>
@@ -13,15 +14,13 @@
  *	-	fast hashed alias address lookup
  *	-	net_alias_type objs registration/unreg., module-ables.
  *	-	/proc/net/aliases & /proc/net/alias_types entries
+ * Fixes:
+ *	JJC	:	several net_alias_type func. renamed.
+ *	JJC	:	net_alias_type object methods now pass *this.
+ *	JJC	:	xxx_rcv device selection based on <src,dst> addrs
  *
  * FIXME:
  *	- User calls sleep/wake_up locking.
- *	- Define a way to select the "best" alias device for an incoming
- *	  packet to allow xxx_rcv() device switching based ALSO on pkt's
- *	  src address (this would require a routing query).
- *	  Related stuff:
- *		IP: Test routing between aliases (possible ICMP redirects).
- *		IP: ARP proxy entries attached to aliases are not visible.
  *
  *
  *	This program is free software; you can redistribute it and/or
@@ -37,6 +36,7 @@
 #include <linux/notifier.h>
 #include <linux/if.h>
 #include <linux/inet.h>
+#include <linux/in.h>
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 
@@ -68,7 +68,7 @@
 static void net_alias_free(struct device *dev);
 					   
 /*
- * net_alias_type base array, will hold net_alias_type objects.
+ * net_alias_type base array, will hold net_alias_type obj hashed list heads.
  */
 
 struct net_alias_type *nat_base[16];
@@ -99,7 +99,7 @@
 nat_addr32(struct net_alias_type *nat, struct sockaddr *sa)
 {
   if (nat->get_addr32)
-    return nat->get_addr32(sa);
+    return nat->get_addr32(nat, sa);
   else
     return (*(struct sockaddr_in *)sa).sin_addr.s_addr;
 }
@@ -122,7 +122,7 @@
 /*
  * get hash key for supplied net alias type and address
  * nat must be !NULL
- * the purpose here is to map an net_alias_type and a generic
+ * the purpose here is to map a net_alias_type and a generic
  * address to a hash code.
  */
 
@@ -166,33 +166,33 @@
 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(alias, 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 'done' hook
+ * 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(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->addr_chk,
+ * 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_addr_chk(struct net_alias_type *nat,
+static __inline__ int nat_dev_addr_chk_1(struct net_alias_type *nat,
 				   struct device *dev, struct sockaddr *sa)
 {
-  if (nat->addr_chk)
-    return nat->addr_chk(dev, 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);
 }
@@ -573,6 +573,81 @@
   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("net_alias_free(%s): '%s' is not my alias\n",
+	       main_dev->name, alias->name);
+    }
+    else
+      printk("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.
@@ -645,23 +720,26 @@
 
 
 /*
- * rehash alias with address supplied. 
+ * rehash alias device with address supplied. 
  */
 
 int
-net_alias_rehash(struct net_alias *alias, struct sockaddr *sa)
+net_alias_dev_rehash(struct device *dev, struct sockaddr *sa)
 {
   struct net_alias_info *alias_info;
-  struct net_alias **aliasp;
-  struct device *dev;
+  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("ERROR: net_alias_rehash(): NULL sockaddr passed\n");
@@ -671,8 +749,8 @@
   /*
    * defensive. should not happen.
    */
-  
-  if (!(dev = alias->main_dev))
+
+  if ( (main_dev = alias->main_dev) == NULL )
   {
     printk("ERROR: net_alias_rehash for %s: NULL maindev\n", alias->name);
     return -1;
@@ -682,7 +760,7 @@
    * defensive. should not happen.
    */
 
-  if (!(alias_info=dev->alias_info))
+  if (!(alias_info=main_dev->alias_info))
   {
     printk("ERROR: net_alias_rehash for %s: NULL alias_info\n", alias->name);
     return -1;
@@ -747,7 +825,7 @@
   cli();
   
   /*
-   * if type (family) changed unlink from old type object (o_nat)
+   * if type (family) changed, unlink from old type object (o_nat)
    * will call o_nat->alias_done_1()
    */
   
@@ -780,81 +858,6 @@
 }
 
 
-/*
- * 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("net_alias_free(%s): '%s' is not my alias\n",
-	       main_dev->name, alias->name);
-    }
-    else
-      printk("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;
-}
 
 
 /*
@@ -898,7 +901,7 @@
  *
  */
 
-#define NAT_REC_SIZE 64
+#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;
@@ -908,7 +911,7 @@
   struct net_alias *alias;
   struct device *dev;
 
-  len=sprintf(buffer,"%-*s\n",NAT_REC_SIZE-1,"device           family address");
+  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))
     {
@@ -921,7 +924,7 @@
        */
       
       if (nat->alias_print_1)
-	dlen += nat->alias_print_1(buffer+len+dlen, NAT_REC_SIZE - dlen, alias);
+	dlen += nat->alias_print_1(nat, alias, buffer+len+dlen, NET_ALIASES_RECSIZ - dlen);
       else
 	dlen += sprintf(buffer+len+dlen, "-");
 
@@ -929,12 +932,12 @@
        * fill with spaces if needed 
        */
       
-      if (dlen < NAT_REC_SIZE) memset(buffer+len+dlen, ' ', NAT_REC_SIZE - dlen);
+      if (dlen < NET_ALIASES_RECSIZ) memset(buffer+len+dlen, ' ', NET_ALIASES_RECSIZ - dlen);
       /*
-       * truncate to NAT_REC_SIZE
+       * truncate to NET_ALIASES_RECSIZ
        */
       
-      len += NAT_REC_SIZE;
+      len += NET_ALIASES_RECSIZ;
       buffer[len-1] = '\n';
       
       pos=begin+len;
@@ -984,58 +987,39 @@
 
 
 /*
- * returns alias device with specified address AND flags_1 on AND flags_0 off.
- *   intended for main devices.
- *   typically called on xxx_rcv() to check if packet's dest address is one
- *   of main_dev's alias address.
+ * device aliases address comparison workhorse
+ * no checks for nat and alias_info, must be !NULL
  */
 
-struct device *
-net_alias_chk(struct device *dev, struct sockaddr *sa,int flags_1, int flags_0)
+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_info *alias_info = dev->alias_info;
-  struct net_alias_type *nat;
   struct net_alias *alias;
-  
-  if (!alias_info) return NULL;	/* has aliases? */
-  
-  /*
-   * get alias_type object for sa->sa_family.
-   */
-  
-  nat = nat_getbytype(sa->sa_family);
-  if (!nat)
-    return 0;
-  
   for(alias = alias_info->hash_tab[nat_hash_key(nat,sa)];
       alias; alias = alias->next)
   {
     if (alias->dev.family != sa->sa_family) continue;
 
     /*
-     * nat_addr_chk will call type specific address cmp function.
+     * nat_dev_addr_chk_1 will call type specific address cmp function.
      */
     
-    if (alias->dev.flags & flags_1 && !(alias->dev.flags & flags_0) &&
-	nat_addr_chk(nat,&alias->dev,sa))
+    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;
 }
 
 /*
- * addr_chk enough for protocols whose addr is (fully) stored at pa_addr.
+ * nat_addr_chk enough for protocols whose addr is (fully) stored at pa_addr.
+ * note that nat pointer is ignored because of static comparison.
  */
 
-struct device *
-net_alias_chk32(struct device *dev, int family, __u32 addr32,
-		int flags_1, int flags_0)
+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_info *alias_info = dev->alias_info;
   struct net_alias *alias;
-  
-  if (!alias_info) return NULL;	/* has aliases? */
-  
   for (alias=alias_info->hash_tab[HASH(addr32,family)];
        alias; alias=alias->next)
   {
@@ -1045,13 +1029,212 @@
      * "hard" (static) comparison between addr32 and pa_addr.
      */
     
-    if (alias->dev.flags & flags_1 && !(alias->dev.flags & flags_0) &&
+    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 arrangment).
+ *
+ * Should be called early at xxx_rcv() time for device selection
+ */
+
+struct device *
+net_alias_dev_rcv_sel(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, dont 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) 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
+   */
+  
+  dev = net_alias_is(dev)?
+    ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL;
+
+  /*
+   * do not return NULL.
+   */
+  
+  return (dev)? dev : main_dev;
+
+}
+
+/*
+ * dev_rcv_sel32: dev_rcv_sel for 'pa_addr' protocols.
+ */
+
+struct device *
+net_alias_dev_rcv_sel32(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, dont 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) 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) return main_dev;
+  
+  /*
+   * 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;
+
+  /*
+   * do not return NULL.
+   */
+  
+  return (dev)? dev : main_dev;
+  
+}
+
 
 /*
  * device event hook
@@ -1162,3 +1345,4 @@
   printk("unregister_net_alias_type(type=%d): not found!\n", nat->type);
   return -EINVAL;
 }
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this