ospfd: respect max-metric over configured cost for summary LSAs

ISSUE

When max-metric router-lsa administrative is invoked on an ABR created with...
area <area> range <addr/mask>
the summary LSAs are sent out with 65535 (max-metric) added to the normal cost.

When max-metric router-lsa administrative is invoked on an ABR created with...
area <area> range <addr/mask> cost <cost>
the summary LSAs are sent out with <cost> (the max-metric is ignored).  This
second behavior effectively incapacitates the max-metric function.

PATCH

This patch evaluates the state of the router and if it's isolated as a stub
router (rfc3137) via `max-metric router-lsa`, we unconditionally uses the
value of 0xff0000 when advertising summary LSAs.

Signed-off-by: JR Rivers <jrrivers@cumulusnetworks.com>
Signed-off-by: Scott Feldman <sfeldma@cumulusnetworks.com>
Reviewed-by: Ayan Banerjee <ayan@cumulusnetworks.com>
Reviewed-by: Dinesh Dutt <ddutt@cumulusnetworks.com>
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c
index 2876eaa..f5edc99 100644
--- a/ospfd/ospf_abr.c
+++ b/ospfd/ospf_abr.c
@@ -566,12 +566,20 @@
 
 static void
 ospf_abr_update_aggregate (struct ospf_area_range *range,
-                           struct ospf_route *or)
+                           struct ospf_route *or, struct ospf_area *area)
 {
   if (IS_DEBUG_OSPF_EVENT)
     zlog_debug ("ospf_abr_update_aggregate(): Start");
 
-  if (range->cost_config != OSPF_AREA_RANGE_COST_UNSPEC)
+  if (CHECK_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED) &&
+      (range->cost != OSPF_STUB_MAX_METRIC_SUMMARY_COST))
+    {
+      range->cost = OSPF_STUB_MAX_METRIC_SUMMARY_COST;
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("ospf_abr_update_aggregate(): use summary max-metric 0x%08x",
+                   range->cost);
+    }
+  else if (range->cost_config != OSPF_AREA_RANGE_COST_UNSPEC)
     {
       if (IS_DEBUG_OSPF_EVENT)
         zlog_debug ("ospf_abr_update_aggregate(): use configured cost %d",
@@ -582,12 +590,18 @@
   else
     {
       if (range->specifics == 0)
-        range->cost = or->cost; /* 1st time get 1st cost */
+	{
+	  if (IS_DEBUG_OSPF_EVENT)
+	    zlog_debug ("ospf_abr_update_aggregate(): use or->cost %d",
+			or->cost);
+
+	  range->cost = or->cost; /* 1st time get 1st cost */
+	}
 
       if (or->cost > range->cost)
         {
           if (IS_DEBUG_OSPF_EVENT)
-            zlog_debug ("ospf_abr_update_aggregate(): largest cost, update");
+            zlog_debug ("ospf_abr_update_aggregate(): update to %d", or->cost);
 
           range->cost = or->cost;
         }
@@ -711,10 +725,16 @@
 {
   struct ospf_lsa *lsa, *old = NULL;
   struct summary_lsa *sl = NULL;
+  u_int32_t full_cost;
 
   if (IS_DEBUG_OSPF_EVENT)
     zlog_debug ("ospf_abr_announce_network_to_area(): Start");
 
+  if (CHECK_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED))
+    full_cost = OSPF_STUB_MAX_METRIC_SUMMARY_COST;
+  else
+    full_cost = cost;
+
   old = ospf_lsa_lookup_by_prefix (area->lsdb, OSPF_SUMMARY_LSA, 
                                    (struct prefix_ipv4 *) p,
                                    area->ospf->router_id);
@@ -730,7 +750,7 @@
         	   "old metric: %d, new metric: %d",
                GET_METRIC (sl->metric), cost);
 
-      if ((GET_METRIC (sl->metric) == cost) &&
+      if ((GET_METRIC (sl->metric) == full_cost) &&
 	  ((old->flags & OSPF_LSA_IN_MAXAGE) == 0))
         {
           /* unchanged. simply reapprove it */
@@ -745,7 +765,7 @@
           if (IS_DEBUG_OSPF_EVENT)
             zlog_debug ("ospf_abr_announce_network_to_area(): "
                        "refreshing summary");
-          set_metric (old, cost);
+          set_metric (old, full_cost);
           lsa = ospf_lsa_refresh (area->ospf, old);
           
           if (!lsa)
@@ -769,7 +789,7 @@
       if (IS_DEBUG_OSPF_EVENT)
         zlog_debug ("ospf_abr_announce_network_to_area(): "
         	   "creating new summary");
-      lsa = ospf_summary_lsa_originate ( (struct prefix_ipv4 *)p, cost, area);
+      lsa = ospf_summary_lsa_originate ( (struct prefix_ipv4 *)p, full_cost, area);
           /* This will flood through area. */
       
       if (!lsa)
@@ -930,7 +950,7 @@
                        inet_ntoa (p->prefix), p->prefixlen);
             if ((range = ospf_area_range_match (or_area, p)) 
                  && !ospf_area_is_transit (area))
-              ospf_abr_update_aggregate (range, or);
+              ospf_abr_update_aggregate (range, or, area);
             else
               ospf_abr_announce_network_to_area (p, or->cost, area);
         }
diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h
index 7a56d16..bf825d1 100644
--- a/ospfd/ospfd.h
+++ b/ospfd/ospfd.h
@@ -196,6 +196,8 @@
   unsigned int stub_router_shutdown_time;	/* seconds */
 #define OSPF_STUB_ROUTER_UNCONFIGURED	  0
 
+#define OSPF_STUB_MAX_METRIC_SUMMARY_COST	0x00ff0000
+
   /* SPF parameters */
   unsigned int spf_delay;		/* SPF delay time. */
   unsigned int spf_holdtime;		/* SPF hold time. */