lib: add AF_ETHERNET/AFI_ETHER
diff --git a/lib/plist.c b/lib/plist.c
index abce633..2176c03 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -734,6 +734,11 @@
 	  return CMD_WARNING;
 	}
       break;
+    case AFI_ETHER:
+    default:
+      vty_out (vty, "%% Unrecognized AFI (%d)%s", afi, VTY_NEWLINE);
+      return CMD_WARNING;
+      break;
     }
 
   /* ge and le check. */
diff --git a/lib/prefix.c b/lib/prefix.c
index aeb627b..43fc317 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -194,8 +194,9 @@
     return AF_INET;
   else if (!strcmp("ipv6", string))
     return AF_INET6;
-  else
-    return -1;
+  else if (!strcmp("ethernet", string))
+    return AF_ETHERNET;
+  return -1;
 }
 
 /* Address Famiy Identifier to Address Family converter. */
@@ -208,6 +209,8 @@
   else if (afi == AFI_IP6)
     return AF_INET6;
 #endif /* HAVE_IPV6 */
+  else if (afi == AFI_ETHER)
+    return AF_ETHERNET;
   return 0;
 }
 
@@ -220,10 +223,26 @@
   else if (family == AF_INET6)
     return AFI_IP6;
 #endif /* HAVE_IPV6 */
+  else if (family == AF_ETHERNET)
+    return AFI_ETHER;
   return 0;
 }
 
 const char *
+afi2str(afi_t afi)
+{
+  switch (afi) {
+    case AFI_IP:
+	return "IPv4";
+    case AFI_IP6:
+	return "IPv6";
+    case AFI_ETHER:
+	return "ethernet";
+  }
+  return NULL;
+}
+
+const char *
 safi2str(safi_t safi)
 {
   switch (safi) {
@@ -286,6 +305,10 @@
       dest->u.lp.id = src->u.lp.id;
       dest->u.lp.adv_router = src->u.lp.adv_router;
     }
+  else if (src->family == AF_ETHERNET)
+    {
+      dest->u.prefix_eth = src->u.prefix_eth;
+    }
   else
     {
       zlog (NULL, LOG_ERR, "prefix_copy(): Unknown address family %d",
@@ -315,6 +338,10 @@
 	if (IPV6_ADDR_SAME (&p1->u.prefix6.s6_addr, &p2->u.prefix6.s6_addr))
 	  return 1;
 #endif /* HAVE_IPV6 */
+      if (p1->family == AF_ETHERNET) {
+	if (!memcmp(p1->u.prefix_eth.octet, p2->u.prefix_eth.octet, ETHER_ADDR_LEN))
+	    return 1;
+      }
     }
   return 0;
 }
@@ -406,6 +433,8 @@
   if (p->family == AF_INET6)
     return "inet6";
 #endif /* HAVE_IPV6 */
+  if (p->family == AF_ETHERNET)
+    return "ether";
   return "unspec";
 }
 
@@ -476,6 +505,60 @@
   return ret;
 }
 
+/* When string format is invalid return 0. */
+int
+str2prefix_eth (const char *str, struct prefix_eth *p)
+{
+  int		ret = 0;
+  int		plen = 48;
+  char		*pnt;
+  char		*cp = NULL;
+  const char	*str_addr = str;
+  unsigned int	a[6];
+  int		i;
+
+  /* Find slash inside string. */
+  pnt = strchr (str, '/');
+
+  if (pnt)
+    {
+      /* Get prefix length. */
+      plen = (u_char) atoi (++pnt);
+      if (plen > 48)
+	{
+	  ret = 0;
+	  goto done;
+	}
+
+      cp = XMALLOC (MTYPE_TMP, (pnt - str) + 1);
+      strncpy (cp, str, pnt - str);
+      *(cp + (pnt - str)) = '\0';
+
+      str_addr = cp;
+    }
+
+  /* Convert string to prefix. */
+  if (sscanf(str_addr, "%2x:%2x:%2x:%2x:%2x:%2x",
+    a+0, a+1, a+2, a+3, a+4, a+5) != 6)
+    {
+      ret = 0;
+      goto done;
+    }
+  for (i = 0; i < 6; ++i)
+    {
+      p->eth_addr.octet[i] = a[i] & 0xff;
+    }
+  p->prefixlen = plen;
+  p->family = AF_ETHERNET;
+  ret = 1;
+
+done:
+  if (cp)
+    XFREE (MTYPE_TMP, cp);
+
+  return ret;
+}
+
 /* Convert masklen into IP address's netmask (network byte order). */
 void
 masklen2ip (const int masklen, struct in_addr *netmask)
@@ -762,6 +845,8 @@
       return IPV6_MAX_BYTELEN;
       break;
 #endif /* HAVE_IPV6 */
+    case AF_ETHERNET:
+      return ETHER_ADDR_LEN;
     }
   return 0;
 }
@@ -784,6 +869,11 @@
     return ret;
 #endif /* HAVE_IPV6 */
 
+  /* Next we try to convert string to struct prefix_eth. */
+  ret = str2prefix_eth (str, (struct prefix_eth *) p);
+  if (ret)
+    return ret;
+
   return 0;
 }
 
@@ -793,6 +883,24 @@
   const struct prefix *p = pu.p;
   char buf[BUFSIZ];
 
+  if (p->family == AF_ETHERNET) {
+    int		i;
+    char	*s = str;
+
+    assert(size > (3*ETHER_ADDR_LEN) + 1 /* slash */ + 3 /* plen */ );
+    for (i = 0; i < ETHER_ADDR_LEN; ++i) {
+	sprintf(s, "%02x", p->u.prefix_eth.octet[i]);
+	if (i < (ETHER_ADDR_LEN - 1)) {
+	    *(s+2) = ':';
+	    s += 3;
+	} else {
+	    s += 2;
+	}
+    }
+    sprintf(s, "/%d", p->prefixlen);
+    return 0;
+  }
+
   inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ);
   snprintf (str, size, "%s/%d", buf, p->prefixlen);
   return str;
diff --git a/lib/prefix.h b/lib/prefix.h
index 4a31750..2cf0b20 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -23,8 +23,30 @@
 #ifndef _ZEBRA_PREFIX_H
 #define _ZEBRA_PREFIX_H
 
+#ifdef SUNOS_5
+# include <sys/ethernet.h>
+#else
+# ifdef GNU_LINUX
+#  include <net/ethernet.h>
+# else
+#  include <netinet/if_ether.h>
+# endif
+#endif
 #include "sockunion.h"
 
+#ifndef ETHER_ADDR_LEN
+#define ETHER_ADDR_LEN  ETHERADDRL
+#endif
+
+/*
+ * there isn't a portable ethernet address type. We define our
+ * own to simplify internal handling
+ */
+struct ethaddr {
+    u_char octet[ETHER_ADDR_LEN];
+} __packed;
+
+
 /*
  * A struct prefix contains an address family, a prefix length, and an
  * address.  This can represent either a 'network prefix' as defined
@@ -34,6 +56,15 @@
  * interface.
  */
 
+/* different OSes use different names */
+#if defined(AF_PACKET)
+#define AF_ETHERNET AF_PACKET
+#else
+#if defined(AF_LINK)
+#define AF_ETHERNET AF_LINK
+#endif
+#endif
+
 /* IPv4 and IPv6 unified prefix structure. */
 struct prefix
 {
@@ -51,6 +82,7 @@
       struct in_addr id;
       struct in_addr adv_router;
     } lp;
+    struct ethaddr prefix_eth;	/* AF_ETHERNET */
     u_char val[8];
     uintptr_t ptr;
   } u __attribute__ ((aligned (8)));
@@ -90,6 +122,14 @@
   u_char val[8] __attribute__ ((aligned (8)));
 };
 
+/* Prefix for ethernet. */
+struct prefix_eth
+{
+  u_char family;
+  u_char prefixlen;
+  struct ethaddr eth_addr __attribute__ ((aligned (8))); /* AF_ETHERNET */
+};
+
 /* Prefix for a generic pointer */
 struct prefix_ptr
 {
@@ -174,6 +214,7 @@
 extern int afi2family (afi_t);
 extern afi_t family2afi (int);
 extern const char *safi2str(safi_t safi);
+extern const char *afi2str(afi_t afi);
 
 /* Check bit of the prefix. */
 extern unsigned int prefix_bit (const u_char *prefix, const u_char prefixlen);
@@ -197,6 +238,8 @@
 extern struct prefix *sockunion2hostprefix (const union sockunion *, struct prefix *p);
 extern void prefix2sockunion (const struct prefix *, union sockunion *);
 
+extern int str2prefix_eth (const char *, struct prefix_eth *);
+
 extern struct prefix_ipv4 *prefix_ipv4_new (void);
 extern void prefix_ipv4_free (struct prefix_ipv4 *);
 extern int str2prefix_ipv4 (const char *, struct prefix_ipv4 *);
diff --git a/lib/zebra.h b/lib/zebra.h
index cb83a11..70d498a 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -493,7 +493,8 @@
 typedef enum {
   AFI_IP  = 1,
   AFI_IP6 = 2,
-#define AFI_MAX 3
+  AFI_ETHER = 3,                /* RFC 1700 has "6" for 802.* */
+#define AFI_MAX 4
 } afi_t;
 
 /* Subsequent Address Family Identifier. */