ospf6d: implement admin distance

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

Signed-off-by: Maitane Zotes <maz@open.ch>
diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c
index c8f20d8..1f0daa1 100644
--- a/ospf6d/ospf6_zebra.c
+++ b/ospf6d/ospf6_zebra.c
@@ -477,6 +477,8 @@
   SET_FLAG (api.message, ZAPI_MESSAGE_METRIC);
   api.metric = (request->path.metric_type == 2 ?
                 request->path.cost_e2 : request->path.cost);
+  SET_FLAG (api.message, ZAPI_MESSAGE_DISTANCE);
+  api.distance = ospf6_distance_apply (request, ospf6);
 
   dest = (struct prefix_ipv6 *) &request->prefix;
   if (type == REM)
@@ -579,6 +581,147 @@
   zclient_send_requests (zclient, VRF_DEFAULT);
 }
 
+static struct ospf6_distance *
+ospf6_distance_new (void)
+{
+  return XCALLOC (MTYPE_OSPF6_DISTANCE, sizeof (struct ospf6_distance));
+}
+
+static void
+ospf6_distance_free (struct ospf6_distance *odistance)
+{
+  XFREE (MTYPE_OSPF6_DISTANCE, odistance);
+}
+
+int
+ospf6_distance_set (struct vty *vty, struct ospf6 *o,
+                    const char *distance_str,
+                    const char *ip_str,
+                    const char *access_list_str)
+{
+  int ret;
+  struct prefix_ipv6 p;
+  u_char distance;
+  struct route_node *rn;
+  struct ospf6_distance *odistance;
+
+  ret = str2prefix_ipv6 (ip_str, &p);
+  if (ret == 0)
+    {
+      vty_out (vty, "Malformed prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  distance = atoi (distance_str);
+
+  /* Get OSPF6 distance node. */
+  rn = route_node_get (o->distance_table, (struct prefix *) &p);
+  if (rn->info)
+    {
+      odistance = rn->info;
+      route_unlock_node (rn);
+    }
+  else
+    {
+      odistance = ospf6_distance_new ();
+      rn->info = odistance;
+    }
+
+  /* Set distance value. */
+  odistance->distance = distance;
+
+  /*Reset access-list configuration. */
+  if (odistance->access_list)
+    {
+      free (odistance->access_list);
+      odistance->access_list = NULL;
+    }
+  if (access_list_str)
+    odistance->access_list = strdup (access_list_str);
+
+  return CMD_SUCCESS;
+}
+
+int
+ospf6_distance_unset (struct vty *vty, struct ospf6 *o,
+                      const char *ip_str,
+                      const char *access_list_str)
+{
+  int ret;
+  struct prefix_ipv6 p;
+  struct route_node *rn;
+  struct ospf6_distance *odistance;
+
+  ret = str2prefix_ipv6 (ip_str, &p);
+  if (ret == 0)
+    {
+      vty_out (vty, "Malformed prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rn = route_node_lookup (o->distance_table, (struct prefix *) &p);
+  if (!rn)
+    {
+      vty_out (vty, "Cant't find specified prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  odistance = rn->info;
+
+  if (odistance->access_list)
+    free (odistance->access_list);
+  ospf6_distance_free (odistance);
+
+  rn->info = NULL;
+  route_unlock_node (rn);
+  route_unlock_node (rn);
+
+  return CMD_SUCCESS;
+}
+
+void
+ospf6_distance_reset (struct ospf6 *o)
+{
+  struct route_node *rn;
+  struct ospf6_distance *odistance;
+
+  for (rn = route_top (o->distance_table); rn; rn = route_next (rn))
+    if ((odistance = rn->info) != NULL)
+      {
+        if (odistance->access_list)
+          free (odistance->access_list);
+        ospf6_distance_free (odistance);
+        rn->info = NULL;
+        route_unlock_node (rn);
+      }
+}
+
+u_char
+ospf6_distance_apply (struct ospf6_route *or, struct ospf6 *o)
+{
+
+  if (o == NULL)
+    return 0;
+
+  if (o->distance_intra)
+    if (or->path.type == OSPF6_PATH_TYPE_INTRA)
+      return o->distance_intra;
+
+  if (o->distance_inter)
+    if (or->path.type == OSPF6_PATH_TYPE_INTER)
+      return o->distance_inter;
+
+  if (o->distance_external)
+    if(or->path.type == OSPF6_PATH_TYPE_EXTERNAL1
+       || or->path.type == OSPF6_PATH_TYPE_EXTERNAL2)
+      return o->distance_external;
+
+  if (o->distance_all)
+    return o->distance_all;
+
+  return 0;
+}
+
 void
 ospf6_zebra_init (struct thread_master *master)
 {