ospf6d: add support for route tags

[ported by Paul Jakma, paul@jakma.org]
diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c
index 4b7d214..a442506 100644
--- a/ospf6d/ospf6_asbr.c
+++ b/ospf6d/ospf6_asbr.c
@@ -93,7 +93,10 @@
     UNSET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F);
 
   /* external route tag */
-  UNSET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T);
+  if (info->tag)
+    SET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T);
+  else
+    UNSET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T);
 
   /* Set metric */
   OSPF6_ASBR_METRIC_SET (as_external_lsa, route->path.cost);
@@ -123,7 +126,10 @@
   /* External Route Tag */
   if (CHECK_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T))
     {
-      /* xxx */
+      route_tag_t network_order = htonl(info->tag);
+
+      memcpy (p, &network_order, sizeof(network_order));
+      p += sizeof(network_order);
     }
 
   /* Fill LSA Header */
@@ -146,6 +152,29 @@
   ospf6_lsa_originate_process (lsa, ospf6);
 }
 
+static route_tag_t
+ospf6_as_external_lsa_get_tag (struct ospf6_lsa *lsa)
+{
+  struct ospf6_as_external_lsa *external;
+  ptrdiff_t tag_offset;
+  route_tag_t network_order;
+
+  if (!lsa)
+    return 0;
+
+  external = (struct ospf6_as_external_lsa *)
+    OSPF6_LSA_HEADER_END (lsa->header);
+
+  if (!CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_T))
+    return 0;
+
+  tag_offset = sizeof(*external) + OSPF6_PREFIX_SPACE(external->prefix.prefix_length);
+  if (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_F))
+    tag_offset += sizeof(struct in6_addr);
+
+  memcpy(&network_order, (caddr_t)external + tag_offset, sizeof(network_order));
+  return ntohl(network_order);
+}
 
 void
 ospf6_asbr_lsa_add (struct ospf6_lsa *lsa)
@@ -223,6 +252,8 @@
       route->path.cost_e2 = 0;
     }
 
+  route->path.tag = ospf6_as_external_lsa_get_tag (lsa);
+  
   for (i = 0; i < OSPF6_MULTI_PATH_LIMIT; i++)
     ospf6_nexthop_copy (&route->nexthop[i], &asbr_entry->nexthop[i]);
 
@@ -413,7 +444,7 @@
 
 void
 ospf6_asbr_redistribute_add (int type, ifindex_t ifindex, struct prefix *prefix,
-                             u_int nexthop_num, struct in6_addr *nexthop)
+                             u_int nexthop_num, struct in6_addr *nexthop, route_tag_t tag)
 {
   int ret;
   struct ospf6_route troute;
@@ -455,6 +486,7 @@
       memset (&tinfo, 0, sizeof (tinfo));
       troute.route_option = &tinfo;
       tinfo.ifindex = ifindex;
+      tinfo.tag = tag;
 
       ret = route_map_apply (ospf6->rmap[type].map, prefix,
                              RMAP_OSPF6, &troute);
@@ -481,6 +513,12 @@
           if (! IN6_IS_ADDR_UNSPECIFIED (&tinfo.forwarding))
             memcpy (&info->forwarding, &tinfo.forwarding,
                     sizeof (struct in6_addr));
+          info->tag = tinfo.tag;
+        }
+      else
+        {
+          /* If there is no route-map, simply update the tag */
+          info->tag = tag;
         }
 
       info->type = type;
@@ -526,6 +564,12 @@
       if (! IN6_IS_ADDR_UNSPECIFIED (&tinfo.forwarding))
         memcpy (&info->forwarding, &tinfo.forwarding,
                 sizeof (struct in6_addr));
+      info->tag = tinfo.tag;
+    }
+  else
+    {
+      /* If there is no route-map, simply set the tag */
+      info->tag = tag;
     }
 
   info->type = type;
@@ -844,6 +888,30 @@
   ospf6_routemap_rule_match_interface_free
 };
 
+/* Match function for matching route tags */
+static route_map_result_t
+ospf6_routemap_rule_match_tag (void *rule, struct prefix *prefix,
+                               route_map_object_t type, void *object)
+{
+  route_tag_t *tag = rule;
+  struct ospf6_route *route = object;
+  struct ospf6_external_info *info = route->route_option;
+
+  if (type == RMAP_OSPF6 && info->tag == *tag)
+    return RMAP_MATCH;
+
+  return RMAP_NOMATCH;
+}
+
+static struct route_map_rule_cmd
+ospf6_routemap_rule_match_tag_cmd =
+{
+  "tag",
+  ospf6_routemap_rule_match_tag,
+  route_map_rule_tag_compile,
+  route_map_rule_tag_free,
+};
+
 static route_map_result_t
 ospf6_routemap_rule_set_metric_type (void *rule, struct prefix *prefix,
                                      route_map_object_t type, void *object)
@@ -969,6 +1037,30 @@
   ospf6_routemap_rule_set_forwarding_free,
 };
 
+static route_map_result_t
+ospf6_routemap_rule_set_tag (void *rule, struct prefix *prefix,
+                             route_map_object_t type, void *object)
+{
+  route_tag_t *tag = rule;
+  struct ospf6_route *route = object;
+  struct ospf6_external_info *info = route->route_option;
+
+  if (type != RMAP_OSPF6)
+    return RMAP_OKAY;
+
+  info->tag = *tag;
+  return RMAP_OKAY;
+}
+
+static struct route_map_rule_cmd
+ospf6_routemap_rule_set_tag_cmd =
+{
+  "tag",
+  ospf6_routemap_rule_set_tag,
+  route_map_rule_tag_compile,
+  route_map_rule_tag_free,
+};
+
 static int
 route_map_command_status (struct vty *vty, int ret)
 {
@@ -1037,8 +1129,8 @@
 DEFUN (ospf6_routemap_no_match_interface,
        ospf6_routemap_no_match_interface_cmd,
        "no match interface",
-       MATCH_STR
        NO_STR
+       MATCH_STR
        "Match first hop interface of route\n")
 {
   int ret = route_map_delete_match ((struct route_map_index *) vty->index,
@@ -1049,11 +1141,45 @@
 ALIAS (ospf6_routemap_no_match_interface,
        ospf6_routemap_no_match_interface_val_cmd,
        "no match interface WORD",
-       MATCH_STR
        NO_STR
+       MATCH_STR
        "Match first hop interface of route\n"
        "Interface name\n")
 
+/* add "match tag" */
+DEFUN (ospf6_routemap_match_tag,
+       ospf6_routemap_match_tag_cmd,
+       "match tag <1-4294967295>",
+       MATCH_STR
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+{
+  int ret = route_map_add_match ((struct route_map_index *) vty->index,
+                               "tag", argv[0]);
+  return route_map_command_status (vty, ret);
+}
+
+/* delete "match tag" */
+DEFUN (ospf6_routemap_no_match_tag,
+       ospf6_routemap_no_match_tag_cmd,
+       "no match tag",
+       NO_STR
+       MATCH_STR
+       "Tag value for routing protocol\n")
+{
+  int ret = route_map_delete_match ((struct route_map_index *) vty->index,
+                                  "tag", argc ? argv[0] : NULL);
+  return route_map_command_status (vty, ret);
+}
+
+ALIAS (ospf6_routemap_no_match_tag,
+       ospf6_routemap_no_match_tag_val_cmd,
+       "no match tag <1-4294967295>",
+       NO_STR
+       MATCH_STR
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+
 /* add "set metric-type" */
 DEFUN (ospf6_routemap_set_metric_type,
        ospf6_routemap_set_metric_type_cmd,
@@ -1150,6 +1276,40 @@
   return route_map_command_status (vty, ret);
 }
 
+/* add "set tag" */
+DEFUN (ospf6_routemap_set_tag,
+       ospf6_routemap_set_tag_cmd,
+       "set tag <1-4294967295>",
+       "Set value\n"
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+{
+  int ret = route_map_add_set ((struct route_map_index *) vty->index,
+                               "tag", argv[0]);
+  return route_map_command_status (vty, ret);
+}
+
+/* delete "set tag" */
+DEFUN (ospf6_routemap_no_set_tag,
+       ospf6_routemap_no_set_tag_cmd,
+       "no set tag",
+       NO_STR
+       "Set value\n"
+       "Tag value for routing protocol\n")
+{
+  int ret = route_map_delete_set ((struct route_map_index *) vty->index,
+                                  "tag", argc ? argv[0] : NULL);
+  return route_map_command_status (vty, ret);
+}
+
+ALIAS (ospf6_routemap_no_set_tag,
+       ospf6_routemap_no_set_tag_val_cmd,
+       "no set tag <1-4294967295>",
+       NO_STR
+       "Set value\n"
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+
 static void
 ospf6_routemap_init (void)
 {
@@ -1160,10 +1320,12 @@
 
   route_map_install_match (&ospf6_routemap_rule_match_address_prefixlist_cmd);
   route_map_install_match (&ospf6_routemap_rule_match_interface_cmd);
+  route_map_install_match (&ospf6_routemap_rule_match_tag_cmd);
 
   route_map_install_set (&ospf6_routemap_rule_set_metric_type_cmd);
   route_map_install_set (&ospf6_routemap_rule_set_metric_cmd);
   route_map_install_set (&ospf6_routemap_rule_set_forwarding_cmd);
+  route_map_install_set (&ospf6_routemap_rule_set_tag_cmd);
 
   /* Match address prefix-list */
   install_element (RMAP_NODE, &ospf6_routemap_match_address_prefixlist_cmd);
@@ -1174,6 +1336,11 @@
   install_element (RMAP_NODE, &ospf6_routemap_no_match_interface_cmd);
   install_element (RMAP_NODE, &ospf6_routemap_no_match_interface_val_cmd);
 
+  /* Match tag */
+  install_element (RMAP_NODE, &ospf6_routemap_match_tag_cmd);
+  install_element (RMAP_NODE, &ospf6_routemap_no_match_tag_cmd);
+  install_element (RMAP_NODE, &ospf6_routemap_no_match_tag_val_cmd);
+
   /* ASE Metric Type (e.g. Type-1/Type-2) */
   install_element (RMAP_NODE, &ospf6_routemap_set_metric_type_cmd);
   install_element (RMAP_NODE, &ospf6_routemap_no_set_metric_type_cmd);
@@ -1183,9 +1350,14 @@
   install_element (RMAP_NODE, &no_set_metric_cmd);
   install_element (RMAP_NODE, &no_set_metric_val_cmd);
 
-  /* ASE Metric */
+  /* Forwarding address */
   install_element (RMAP_NODE, &ospf6_routemap_set_forwarding_cmd);
   install_element (RMAP_NODE, &ospf6_routemap_no_set_forwarding_cmd);
+
+  /* Tag */
+  install_element (RMAP_NODE, &ospf6_routemap_set_tag_cmd);
+  install_element (RMAP_NODE, &ospf6_routemap_no_set_tag_cmd);
+  install_element (RMAP_NODE, &ospf6_routemap_no_set_tag_val_cmd);
 }
 
 
@@ -1263,6 +1435,13 @@
 	       VNL);
     }
 
+  /* Tag */
+  if (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_T))
+    {
+      vty_out (vty, "     Tag: %u%s",
+               ospf6_as_external_lsa_get_tag (lsa), VNL);
+    }
+
   return 0;
 }
 
diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h
index f3df90b..14113b0 100644
--- a/ospf6d/ospf6_asbr.h
+++ b/ospf6d/ospf6_asbr.h
@@ -47,7 +47,8 @@
   u_int32_t id;
 
   struct in6_addr forwarding;
-  /* u_int32_t tag; */
+
+  route_tag_t tag;
 
   ifindex_t ifindex;
 };
@@ -82,7 +83,8 @@
 extern void ospf6_asbr_redistribute_add (int type, ifindex_t ifindex,
                                          struct prefix *prefix,
                                          u_int nexthop_num,
-                                         struct in6_addr *nexthop);
+                                         struct in6_addr *nexthop,
+                                         route_tag_t tag);
 extern void ospf6_asbr_redistribute_remove (int type, ifindex_t ifindex,
                                             struct prefix *prefix);
 
diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h
index 2fb2c1d..027a648 100644
--- a/ospf6d/ospf6_route.h
+++ b/ospf6d/ospf6_route.h
@@ -97,6 +97,7 @@
   u_int8_t metric_type;
   u_int32_t cost;
   u_int32_t cost_e2;
+  u_int32_t tag;
 };
 
 #define OSPF6_PATH_TYPE_NONE         0
diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c
index f80bb23..2976214 100644
--- a/ospf6d/ospf6_zebra.c
+++ b/ospf6d/ospf6_zebra.c
@@ -247,6 +247,11 @@
   else
     api.metric = 0;
 
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG))
+    api.tag = stream_getl (s);
+  else
+    api.tag = 0;
+
   if (IS_OSPF6_DEBUG_ZEBRA (RECV))
     {
       char prefixstr[128], nexthopstr[128];
@@ -256,14 +261,14 @@
       else
         snprintf (nexthopstr, sizeof (nexthopstr), "::");
 
-      zlog_debug ("Zebra Receive route %s: %s %s nexthop %s ifindex %ld",
+      zlog_debug ("Zebra Receive route %s: %s %s nexthop %s ifindex %ld tag %u",
 		  (command == ZEBRA_IPV6_ROUTE_ADD ? "add" : "delete"),
-		  zebra_route_string(api.type), prefixstr, nexthopstr, ifindex);
+		  zebra_route_string(api.type), prefixstr, nexthopstr, ifindex, api.tag);
     }
  
   if (command == ZEBRA_IPV6_ROUTE_ADD)
     ospf6_asbr_redistribute_add (api.type, ifindex, (struct prefix *) &p,
-                                 api.nexthop_num, nexthop);
+                                 api.nexthop_num, nexthop, api.tag);
   else
     ospf6_asbr_redistribute_remove (api.type, ifindex, (struct prefix *) &p);
 
@@ -470,9 +475,12 @@
   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);
-
+  if (request->path.tag)
+    {
+      SET_FLAG (api.message, ZAPI_MESSAGE_TAG);
+      api.tag = request->path.tag;
+    }
+  
   dest = (struct prefix_ipv6 *) &request->prefix;
   if (type == REM)
     ret = zapi_ipv6_route (ZEBRA_IPV6_ROUTE_DELETE, zclient, dest, &api);