ospfd, vtysh: Add support for Route tags

[Forward ported by Cumulus]

Credit
------
A huge amount of credit for this patch goes to Piotr Chytla for
their 'route tags support' patch that was submitted to quagga-dev
in June 2007.

Documentation
-------------
All ipv4 and ipv6 static route commands now have a "tag" option
which allows the user to set a tag between 1 and 65535.

quagga(config)# ip route 1.1.1.1/32 10.1.1.1 tag ?
 <1-65535>  Tag value
quagga(config)# ip route 1.1.1.1/32 10.1.1.1 tag 40
quagga(config)#

quagga# show ip route 1.1.1.1/32
Routing entry for 1.1.1.1/32
  Known via "static", distance 1, metric 0, tag 40, best
  * 10.1.1.1, via swp1

quagga#

The route-map parser supports matching on tags and setting tags
!
route-map MATCH_TAG_18 permit 10
 match tag 18
!

!
route-map SET_TAG_22 permit 10
 set tag 22
!

BGP and OSPF support:
- matching on tags when redistribing routes from the RIB into BGP/OSPF.
- setting tags when redistribing routes from the RIB into BGP/OSPF.

Signed-off-by: Daniel Walton <dwalton@cumulusnetworks.com>
Signed-off-by: Piotr Chytla <pch@packetconsulting.pl>
Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c
index 122e70b..4b53690 100644
--- a/ospfd/ospf_asbr.c
+++ b/ospfd/ospf_asbr.c
@@ -135,7 +135,8 @@
 /* Add an External info for AS-external-LSA. */
 struct external_info *
 ospf_external_info_add (u_char type, struct prefix_ipv4 p,
-			ifindex_t ifindex, struct in_addr nexthop)
+			ifindex_t ifindex, struct in_addr nexthop,
+                        u_short tag)
 {
   struct external_info *new;
   struct route_node *rn;
@@ -162,7 +163,7 @@
   new->p = p;
   new->ifindex = ifindex;
   new->nexthop = nexthop;
-  new->tag = 0;
+  new->tag = tag;
 
   if (rn)
     rn->info = new;
diff --git a/ospfd/ospf_asbr.h b/ospfd/ospf_asbr.h
index d151cb1..2709a6c 100644
--- a/ospfd/ospf_asbr.h
+++ b/ospfd/ospf_asbr.h
@@ -62,7 +62,8 @@
 extern struct external_info *ospf_external_info_add (u_char, 
                                               struct prefix_ipv4,
 					      ifindex_t, 
-					      struct in_addr);
+					      struct in_addr,
+					      u_short);
 extern void ospf_external_info_delete (u_char, struct prefix_ipv4);
 extern struct external_info *ospf_external_info_lookup (u_char, 
                                                  struct prefix_ipv4 *);
diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c
index c1b1e0e..4341cd9 100644
--- a/ospfd/ospf_lsa.c
+++ b/ospfd/ospf_lsa.c
@@ -1649,8 +1649,8 @@
   /* Put forwarding address. */
   stream_put_ipv4 (s, fwd_addr.s_addr);
   
-  /* Put route tag -- This value should be introduced from configuration. */
-  stream_putl (s, 0);
+  /* Put route tag -- only first 16bits are used for compatibility */
+  stream_putl (s, ei->tag);
 }
 
 /* Create new external-LSA. */
@@ -2163,7 +2163,7 @@
       /* If there is no default route via redistribute,
 	 then originate AS-external-LSA with nexthop 0 (self). */
       nexthop.s_addr = 0;
-      ospf_external_info_add (DEFAULT_ROUTE, p, 0, nexthop);
+      ospf_external_info_add (DEFAULT_ROUTE, p, 0, nexthop, 0);
     }
 
   if ((ei = ospf_default_external_info (ospf)))
diff --git a/ospfd/ospf_routemap.c b/ospfd/ospf_routemap.c
index d0ebce6..7bd6c3d 100644
--- a/ospfd/ospf_routemap.c
+++ b/ospfd/ospf_routemap.c
@@ -417,6 +417,67 @@
   route_match_interface_free
 };
 
+/* Match function return 1 if match is success else return zero. */
+static route_map_result_t
+route_match_tag (void *rule, struct prefix *prefix,
+                 route_map_object_t type, void *object)
+{
+  u_short *tag;
+  struct external_info *ei;
+
+  if (type == RMAP_OSPF)
+    {
+      tag = rule;
+      ei = object;
+
+      return ((ei->tag == *tag)? RMAP_MATCH : RMAP_NOMATCH);
+    }
+
+  return RMAP_NOMATCH;
+}
+
+/*  Route map `match tag' match statement. `arg' is TAG value */
+static void *
+route_match_tag_compile (const char *arg)
+{
+  u_short *tag;
+  u_short tmp;
+
+  /* tag value shoud be integer. */
+  if (! all_digit (arg))
+    return NULL;
+
+  tmp = atoi(arg);
+  if (tmp < 1)
+    return NULL;
+
+  tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_short));
+
+  if (!tag)
+    return tag;
+
+  *tag = tmp;
+
+  return tag;
+}
+
+/* Free route map's compiled 'match tag' value. */
+static void
+route_match_tag_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for tag matching. */
+struct route_map_rule_cmd route_match_tag_cmd =
+{
+  "tag",
+  route_match_tag,
+  route_match_tag_compile,
+  route_match_tag_free,
+};
+
+
 /* `set metric METRIC' */
 /* Set metric to attribute. */
 static route_map_result_t
@@ -531,6 +592,67 @@
   route_set_metric_type_free,
 };
 
+static route_map_result_t
+route_set_tag (void *rule, struct prefix *prefix,
+               route_map_object_t type, void *object)
+{
+  u_short *tag;
+  struct external_info *ei;
+
+  if (type == RMAP_OSPF)
+    {
+      tag = rule;
+      ei = object;
+
+      /* Set tag value */
+      ei->tag=*tag;
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Route map `tag' compile function.  Given string is converted to u_short. */
+static void *
+route_set_tag_compile (const char *arg)
+{
+  u_short *tag;
+  u_short tmp;
+
+  /* tag value shoud be integer. */
+  if (! all_digit (arg))
+    return NULL;
+
+  tmp = atoi(arg);
+
+  if (tmp < 1)
+      return NULL;
+
+  tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_short));
+
+  if (!tag)
+    return tag;
+
+  *tag = tmp;
+
+  return tag;
+}
+
+/* Free route map's tag value. */
+static void
+route_set_tag_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for tag set. */
+struct route_map_rule_cmd route_set_tag_cmd =
+{
+  "tag",
+  route_set_tag,
+  route_set_tag_compile,
+  route_set_tag_free,
+};
+
 DEFUN (match_ip_nexthop,
        match_ip_nexthop_cmd,
        "match ip next-hop (<1-199>|<1300-2699>|WORD)",
@@ -716,6 +838,37 @@
        "Match first hop interface of route\n"
        "Interface name\n")
 
+DEFUN (match_tag,
+       match_tag_cmd,
+       "match tag <1-65535>",
+       MATCH_STR
+       "Match tag of route\n"
+       "Tag value\n")
+{
+  return ospf_route_match_add (vty, vty->index, "tag", argv[0]);
+}
+
+DEFUN (no_match_tag,
+       no_match_tag_cmd,
+       "no match tag",
+       NO_STR
+       MATCH_STR
+       "Match tag of route\n")
+{
+  if (argc == 0)
+    return ospf_route_match_delete (vty, vty->index, "tag", NULL);
+
+  return ospf_route_match_delete (vty, vty->index, "tag", argv[0]);
+}
+
+ALIAS (no_match_tag,
+       no_match_tag_val_cmd,
+       "no match tag <1-65535>",
+       NO_STR
+       MATCH_STR
+       "Match tag of route\n"
+       "Tag value\n")
+
 DEFUN (set_metric,
        set_metric_cmd,
        "set metric <0-4294967295>",
@@ -785,6 +938,37 @@
        "OSPF[6] external type 1 metric\n"
        "OSPF[6] external type 2 metric\n")
 
+DEFUN (set_tag,
+       set_tag_cmd,
+       "set tag <1-65535>",
+       SET_STR
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+{
+  return ospf_route_set_add (vty, vty->index, "tag", argv[0]);
+}
+
+DEFUN (no_set_tag,
+       no_set_tag_cmd,
+       "no set tag",
+       NO_STR
+       SET_STR
+       "Tag value for routing protocol\n")
+{
+  if (argc == 0)
+      ospf_route_set_delete(vty, vty->index, "tag", NULL);
+
+  return ospf_route_set_delete (vty, vty->index, "tag", argv[0]);
+}
+
+ALIAS (no_set_tag,
+       no_set_tag_val_cmd,
+       "no set tag <1-65535>",
+       NO_STR
+       SET_STR
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+
 /* Route-map init */
 void
 ospf_route_map_init (void)
@@ -801,9 +985,11 @@
   route_map_install_match (&route_match_ip_address_cmd);
   route_map_install_match (&route_match_ip_address_prefix_list_cmd);
   route_map_install_match (&route_match_interface_cmd);
+  route_map_install_match (&route_match_tag_cmd);
 
   route_map_install_set (&route_set_metric_cmd);
   route_map_install_set (&route_set_metric_type_cmd);
+  route_map_install_set (&route_set_tag_cmd);
 
   install_element (RMAP_NODE, &match_ip_nexthop_cmd);
   install_element (RMAP_NODE, &no_match_ip_nexthop_cmd);
@@ -820,6 +1006,9 @@
   install_element (RMAP_NODE, &match_interface_cmd);
   install_element (RMAP_NODE, &no_match_interface_cmd);
   install_element (RMAP_NODE, &no_match_interface_val_cmd);
+  install_element (RMAP_NODE, &match_tag_cmd);
+  install_element (RMAP_NODE, &no_match_tag_cmd);
+  install_element (RMAP_NODE, &no_match_tag_val_cmd);
 
   install_element (RMAP_NODE, &set_metric_cmd);
   install_element (RMAP_NODE, &no_set_metric_cmd);
@@ -827,4 +1016,7 @@
   install_element (RMAP_NODE, &set_metric_type_cmd);
   install_element (RMAP_NODE, &no_set_metric_type_cmd);
   install_element (RMAP_NODE, &no_set_metric_type_val_cmd);
+  install_element (RMAP_NODE, &set_tag_cmd);
+  install_element (RMAP_NODE, &no_set_tag_cmd);
+  install_element (RMAP_NODE, &no_set_tag_val_cmd);
 }
diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c
index d9b0837..ce268cd 100644
--- a/ospfd/ospf_zebra.c
+++ b/ospfd/ospf_zebra.c
@@ -370,6 +370,12 @@
       if (distance)
         SET_FLAG (message, ZAPI_MESSAGE_DISTANCE);
 
+      /* Check if path type is ASE and use only 16bit tags */
+      if (((or->path_type == OSPF_PATH_TYPE1_EXTERNAL) ||
+          (or->path_type == OSPF_PATH_TYPE2_EXTERNAL)) &&
+           (or->u.ext.tag > 0) && (or->u.ext.tag < UINT16_MAX))
+        SET_FLAG (message, ZAPI_MESSAGE_TAG);
+
       /* Make packet. */
       s = zclient->obuf;
       stream_reset (s);
@@ -437,6 +443,9 @@
             stream_putl (s, or->cost);
         }
 
+      if (CHECK_FLAG (message, ZAPI_MESSAGE_TAG))
+         stream_putw (s, (u_short)or->u.ext.tag);
+
       stream_putw_at (s, 0, stream_get_endp (s));
 
       zclient_send_message(zclient);
@@ -911,8 +920,12 @@
        *     || CHECK_FLAG (api.flags, ZEBRA_FLAG_REJECT))
        * return 0;
        */
-        
-      ei = ospf_external_info_add (api.type, p, ifindex, nexthop);
+
+      /* Protocol tag overwrites all other tag value send by zebra */
+      if (ospf->dtag[api.type] > 0)
+       api.tag = ospf->dtag[api.type];
+
+      ei = ospf_external_info_add (api.type, p, ifindex, nexthop, api.tag);
 
       if (ospf->router_id.s_addr == 0)
         /* Set flags to generate AS-external-LSA originate event
diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c
index a01af60..98d6d77 100644
--- a/ospfd/ospfd.c
+++ b/ospfd/ospfd.c
@@ -197,6 +197,7 @@
     {
       new->dmetric[i].type = -1;
       new->dmetric[i].value = -1;
+      new->dtag[i] = 0;
     }
   new->default_metric = -1;
   new->ref_bandwidth = OSPF_DEFAULT_REF_BANDWIDTH;
diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h
index 508f623..4181d11 100644
--- a/ospfd/ospfd.h
+++ b/ospfd/ospfd.h
@@ -238,6 +238,9 @@
 				   -1 means metric value is not set. */
   } dmetric [ZEBRA_ROUTE_MAX + 1];
 
+  /* Redistribute tag info. */
+  u_short dtag [ZEBRA_ROUTE_MAX + 1];
+
   /* For redistribute route map. */
   struct
   {
diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in
index 0bbc2a7..81adf35 100755
--- a/vtysh/extract.pl.in
+++ b/vtysh/extract.pl.in
@@ -181,7 +181,7 @@
     }
 }
 
-my $bad_cli_stomps = 90;
+my $bad_cli_stomps = 96;
 # Currently we have $bad_cli_stomps.  This was determined by
 # running this script and counting up the collisions from what
 # was returned.