bgpd: implement admin distance

Until today the admin distance cannot be configured for any IPv6
routing protocol. This patch implements it for bgp.

Signed-off-by: Maitane Zotes <maz@open.ch>

patchwork #993: http://patchwork.quagga.net/patch/993/
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index c364372..494375f 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -14844,12 +14844,12 @@
                   const char *ip_str, const char *access_list_str)
 {
   int ret;
-  struct prefix_ipv4 p;
+  struct prefix p;
   u_char distance;
   struct bgp_node *rn;
   struct bgp_distance *bdistance;
 
-  ret = str2prefix_ipv4 (ip_str, &p);
+  ret = str2prefix (ip_str, &p);
   if (ret == 0)
     {
       vty_out (vty, "Malformed prefix%s", VTY_NEWLINE);
@@ -14891,12 +14891,12 @@
                     const char *ip_str, const char *access_list_str)
 {
   int ret;
-  struct prefix_ipv4 p;
+  struct prefix p;
   u_char distance;
   struct bgp_node *rn;
   struct bgp_distance *bdistance;
 
-  ret = str2prefix_ipv4 (ip_str, &p);
+  ret = str2prefix (ip_str, &p);
   if (ret == 0)
     {
       vty_out (vty, "Malformed prefix%s", VTY_NEWLINE);
@@ -15005,6 +15005,81 @@
     }
 }
 
+#ifdef HAVE_IPV6
+/* Apply BGP information to ipv6 distance method. */
+u_char
+ipv6_bgp_distance_apply (struct prefix *p, struct bgp_info *rinfo, struct bgp *bgp)
+{
+  struct bgp_node *rn;
+  struct prefix_ipv6 q;
+  struct peer *peer;
+  struct bgp_distance *bdistance;
+  struct access_list *alist;
+  struct bgp_static *bgp_static;
+
+  if (! bgp)
+    return 0;
+
+  if (p->family != AF_INET6)
+    return 0;
+
+  peer = rinfo->peer;
+
+  if (peer->su.sa.sa_family != AF_INET6)
+    return 0;
+
+  memset (&q, 0, sizeof (struct prefix_ipv6));
+  q.family = AF_INET;
+  q.prefix = peer->su.sin6.sin6_addr;
+  q.prefixlen = IPV6_MAX_BITLEN;
+
+  /* Check source address. */
+  rn = bgp_node_match (bgp_distance_table, (struct prefix *) &q);
+  if (rn)
+    {
+      bdistance = rn->info;
+      bgp_unlock_node (rn);
+
+      if (bdistance->access_list)
+        {
+          alist = access_list_lookup (AFI_IP6, bdistance->access_list);
+          if (alist && access_list_apply (alist, p) == FILTER_PERMIT)
+            return bdistance->distance;
+        }
+      else
+        return bdistance->distance;
+    }
+  /* Backdoor check. */
+  rn = bgp_node_lookup (bgp->route[AFI_IP6][SAFI_UNICAST], p);
+  if (rn)
+    {
+      bgp_static = rn->info;
+      bgp_unlock_node (rn);
+
+      if (bgp_static->backdoor)
+        {
+          if (bgp->ipv6_distance_local)
+            return bgp->ipv6_distance_local;
+          else
+            return ZEBRA_IBGP_DISTANCE_DEFAULT;
+        }
+    }
+
+  if (peer_sort (peer) == BGP_PEER_EBGP)
+    {
+      if (bgp->ipv6_distance_ebgp)
+        return bgp->ipv6_distance_ebgp;
+      return ZEBRA_EBGP_DISTANCE_DEFAULT;
+    }
+  else
+    {
+      if (bgp->ipv6_distance_ibgp)
+        return bgp->ipv6_distance_ibgp;
+      return ZEBRA_IBGP_DISTANCE_DEFAULT;
+    }
+}
+#endif /* HAVE_IPV6 */
+
 DEFUN (bgp_distance,
        bgp_distance_cmd,
        "distance bgp <1-255> <1-255> <1-255>",
@@ -15099,6 +15174,102 @@
   return CMD_SUCCESS;
 }
 
+#ifdef HAVE_IPV6
+DEFUN (ipv6_bgp_distance,
+       ipv6_bgp_distance_cmd,
+       "distance bgp <1-255> <1-255> <1-255>",
+       "Define an administrative distance\n"
+       "BGP distance\n"
+       "Distance for routes external to the AS\n"
+       "Distance for routes internal to the AS\n"
+       "Distance for local routes\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+
+  bgp->ipv6_distance_ebgp = atoi (argv[0]);
+  bgp->ipv6_distance_ibgp = atoi (argv[1]);
+  bgp->ipv6_distance_local = atoi (argv[2]);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_bgp_distance,
+       no_ipv6_bgp_distance_cmd,
+       "no distance bgp <1-255> <1-255> <1-255>",
+       NO_STR
+       "Define an administrative distance\n"
+       "BGP distance\n"
+       "Distance for routes external to the AS\n"
+       "Distance for routes internal to the AS\n"
+       "Distance for local routes\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+
+  bgp->ipv6_distance_ebgp= 0;
+  bgp->ipv6_distance_ibgp = 0;
+  bgp->ipv6_distance_local = 0;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ipv6_bgp_distance,
+       no_ipv6_bgp_distance2_cmd,
+       "no distance bgp",
+       NO_STR
+       "Define an administrative distance\n"
+       "BGP distance\n")
+
+DEFUN (ipv6_bgp_distance_source,
+       ipv6_bgp_distance_source_cmd,
+       "distance <1-255> X:X::X:X/M",
+       "Define an administrative distance\n"
+       "Administrative distance\n"
+       "IP source prefix\n")
+{
+  bgp_distance_set (vty, argv[0], argv[1], NULL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_bgp_distance_source,
+       no_ipv6_bgp_distance_source_cmd,
+       "no distance <1-255> X:X::X:X/M",
+       NO_STR
+       "Define an administrative distance\n"
+       "Administrative distance\n"
+       "IP source prefix\n")
+{
+  bgp_distance_unset (vty, argv[0], argv[1], NULL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_bgp_distance_source_access_list,
+       ipv6_bgp_distance_source_access_list_cmd,
+       "distance <1-255> X:X::X:X/M WORD",
+       "Define an administrative distance\n"
+       "Administrative distance\n"
+       "IP source prefix\n"
+       "Access list name\n")
+{
+  bgp_distance_set (vty, argv[0], argv[1], argv[2]);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_bgp_distance_source_access_list,
+       no_ipv6_bgp_distance_source_access_list_cmd,
+       "no distance <1-255> X:X::X:X/M WORD",
+       NO_STR
+       "Define an administrative distance\n"
+       "Administrative distance\n"
+       "IP source prefix\n"
+       "Access list name\n")
+{
+  bgp_distance_unset (vty, argv[0], argv[1], argv[2]);
+  return CMD_SUCCESS;
+}
+#endif
+
 DEFUN (bgp_damp_set,
        bgp_damp_set_cmd,
        "bgp dampening <1-45> <1-20000> <1-20000> <1-255>",
@@ -15661,30 +15832,59 @@
 }
 
 int
-bgp_config_write_distance (struct vty *vty, struct bgp *bgp)
+bgp_config_write_distance (struct vty *vty, struct bgp *bgp,
+                           afi_t afi, safi_t safi, int *write)
 {
   struct bgp_node *rn;
   struct bgp_distance *bdistance;
 
-  /* Distance configuration. */
-  if (bgp->distance_ebgp
-      && bgp->distance_ibgp
-      && bgp->distance_local
-      && (bgp->distance_ebgp != ZEBRA_EBGP_DISTANCE_DEFAULT
-	  || bgp->distance_ibgp != ZEBRA_IBGP_DISTANCE_DEFAULT
-	  || bgp->distance_local != ZEBRA_IBGP_DISTANCE_DEFAULT))
-    vty_out (vty, " distance bgp %d %d %d%s",
-	     bgp->distance_ebgp, bgp->distance_ibgp, bgp->distance_local,
-	     VTY_NEWLINE);
-  
-  for (rn = bgp_table_top (bgp_distance_table); rn; rn = bgp_route_next (rn))
-    if ((bdistance = rn->info) != NULL)
-      {
-	vty_out (vty, " distance %d %s/%d %s%s", bdistance->distance,
-		 inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen,
-		 bdistance->access_list ? bdistance->access_list : "",
-		 VTY_NEWLINE);
-      }
+  if (afi == AFI_IP && safi == SAFI_UNICAST)
+    {
+      /* Distance configuration. */
+      if (bgp->distance_ebgp
+          && bgp->distance_ibgp
+          && bgp->distance_local
+          && (bgp->distance_ebgp != ZEBRA_EBGP_DISTANCE_DEFAULT
+              || bgp->distance_ibgp != ZEBRA_IBGP_DISTANCE_DEFAULT
+              || bgp->distance_local != ZEBRA_IBGP_DISTANCE_DEFAULT))
+        vty_out (vty, " distance bgp %d %d %d%s",
+                 bgp->distance_ebgp, bgp->distance_ibgp, bgp->distance_local,
+                 VTY_NEWLINE);
+
+      for (rn = bgp_table_top (bgp_distance_table); rn; rn = bgp_route_next (rn))
+        if ((bdistance = rn->info) != NULL)
+          {
+            vty_out (vty, " distance %d %s/%d %s%s", bdistance->distance,
+                     inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen,
+                     bdistance->access_list ? bdistance->access_list : "",
+                     VTY_NEWLINE);
+          }
+    }
+
+#ifdef HAVE_IPV6
+  else if (afi == AFI_IP6 && safi == SAFI_UNICAST)
+    {
+      bgp_config_write_family_header (vty, afi, safi, write);
+      if (bgp->ipv6_distance_ebgp
+          && bgp->ipv6_distance_ibgp
+          && bgp->ipv6_distance_local
+          && (bgp->ipv6_distance_ebgp != ZEBRA_EBGP_DISTANCE_DEFAULT
+              || bgp->ipv6_distance_ibgp != ZEBRA_IBGP_DISTANCE_DEFAULT
+              || bgp->ipv6_distance_local != ZEBRA_IBGP_DISTANCE_DEFAULT))
+        vty_out (vty, " distance bgp %d %d %d%s",
+                 bgp->ipv6_distance_ebgp, bgp->ipv6_distance_ibgp, bgp->ipv6_distance_local,
+                 VTY_NEWLINE);
+
+        for (rn = bgp_table_top (bgp_distance_table); rn; rn = bgp_route_next (rn))
+          if ((bdistance = rn->info) != NULL)
+            {
+              vty_out (vty, " distance %d %s/%d %s%s", bdistance->distance,
+                       inet6_ntoa (rn->p.u.prefix6), rn->p.prefixlen,
+                       bdistance->access_list ? bdistance->access_list : "",
+                       VTY_NEWLINE);
+            }
+    }
+#endif /* HAVE_IPV6 */
 
   return 0;
 }
@@ -16253,6 +16453,15 @@
   install_element (BGP_NODE, &no_bgp_distance_source_cmd);
   install_element (BGP_NODE, &bgp_distance_source_access_list_cmd);
   install_element (BGP_NODE, &no_bgp_distance_source_access_list_cmd);
+#ifdef HAVE_IPV6
+  install_element (BGP_IPV6_NODE, &ipv6_bgp_distance_cmd);
+  install_element (BGP_IPV6_NODE, &no_ipv6_bgp_distance_cmd);
+  install_element (BGP_IPV6_NODE, &no_ipv6_bgp_distance2_cmd);
+  install_element (BGP_IPV6_NODE, &ipv6_bgp_distance_source_cmd);
+  install_element (BGP_IPV6_NODE, &no_ipv6_bgp_distance_source_cmd);
+  install_element (BGP_IPV6_NODE, &ipv6_bgp_distance_source_access_list_cmd);
+  install_element (BGP_IPV6_NODE, &no_ipv6_bgp_distance_source_access_list_cmd);
+#endif /* HAVE_IPV6 */
 
   install_element (BGP_NODE, &bgp_damp_set_cmd);
   install_element (BGP_NODE, &bgp_damp_set2_cmd);
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index c803759..8483f3d 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -227,7 +227,7 @@
 /* for bgp_nexthop and bgp_damp */
 extern void bgp_process (struct bgp *, struct bgp_node *, afi_t, safi_t);
 extern int bgp_config_write_network (struct vty *, struct bgp *, afi_t, safi_t, int *);
-extern int bgp_config_write_distance (struct vty *, struct bgp *);
+extern int bgp_config_write_distance (struct vty *, struct bgp *, afi_t, safi_t, int *);
 
 extern void bgp_aggregate_increment (struct bgp *, struct prefix *, struct bgp_info *,
 			      afi_t, safi_t);
@@ -235,6 +235,7 @@
 			      afi_t, safi_t);
 
 extern u_char bgp_distance_apply (struct prefix *, struct bgp_info *, struct bgp *);
+extern u_char ipv6_bgp_distance_apply (struct prefix *, struct bgp_info *, struct bgp *);
 
 extern afi_t bgp_node_afi (struct vty *);
 extern safi_t bgp_node_safi (struct vty *);
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index d0b9216..4066a9a 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -814,6 +814,14 @@
       SET_FLAG (api.message, ZAPI_MESSAGE_METRIC);
       api.metric = info->attr->med;
 
+      distance = ipv6_bgp_distance_apply (p, info, bgp);
+
+      if (distance)
+        {
+          SET_FLAG (api.message, ZAPI_MESSAGE_DISTANCE);
+          api.distance = distance;
+        }
+
       if (BGP_DEBUG(zebra, ZEBRA))
 	{
 	  char buf[2][INET6_ADDRSTRLEN];
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 249d20f..3517011 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -5367,6 +5367,8 @@
 
   bgp_config_write_maxpaths (vty, bgp, afi, safi, &write);
 
+  bgp_config_write_distance (vty, bgp, afi, safi, &write);
+
   if (write)
     vty_out (vty, " exit-address-family%s", VTY_NEWLINE);
 
@@ -5547,7 +5549,7 @@
       bgp_config_write_maxpaths (vty, bgp, AFI_IP, SAFI_UNICAST, &write);
 
       /* Distance configuration. */
-      bgp_config_write_distance (vty, bgp);
+      bgp_config_write_distance (vty, bgp, AFI_IP, SAFI_UNICAST, &write);
       
       /* No auto-summary */
       if (bgp_option_check (BGP_OPT_CONFIG_CISCO))
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 7665d9d..d8e687b 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -156,6 +156,11 @@
   u_char distance_ebgp;
   u_char distance_ibgp;
   u_char distance_local;
+
+  /* BGP ipv6 distance configuration.  */
+  u_char ipv6_distance_ebgp;
+  u_char ipv6_distance_ibgp;
+  u_char ipv6_distance_local;
   
   /* BGP default local-preference.  */
   u_int32_t default_local_pref;