[ospfd] Add support for oversized LSAs.

2006-01-18 Juergen Kammer <j.kammer@eurodata.de>

	* ospf_lsa.c: (ospf_router_lsa_new) dont take reference to the
	  stream data until it is constructed, data reference is
	  volatile due to the potential resize in link_info_set

2006-01-18 Paul Jakma <paul.jakma@sun.com>

	* ospf_lsa.c: (link_info_set) Resize the stream if required and
	  possible. Return number of links added.
	  (lsa_link_*_set) use return value from previous.
	* ospf_lsa.h: Add OSPF_ROUTER_LSA_LINK_SIZE define.
diff --git a/ospfd/ChangeLog b/ospfd/ChangeLog
index a13ed6f..e5f8337 100644
--- a/ospfd/ChangeLog
+++ b/ospfd/ChangeLog
@@ -1,3 +1,16 @@
+2006-01-18 Juergen Kammer <j.kammer@eurodata.de>
+
+	* ospf_lsa.c: (ospf_router_lsa_new) dont take reference to the
+	  stream data until it is constructed, data reference is
+	  volatile due to the potential resize in link_info_set
+
+2006-01-18 Paul Jakma <paul.jakma@sun.com>
+
+	* ospf_lsa.c: (link_info_set) Resize the stream if required and
+	  possible. Return number of links added.
+	  (lsa_link_*_set) use return value from previous.
+	* ospf_lsa.h: Add OSPF_ROUTER_LSA_LINK_SIZE define.
+	  
 2006-01-17 Paul Jakma <paul.jakma@sun.com>
 
 	* ospf_packet.c: (ospf_verify_header) print out the types
diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c
index ff018fd..89e4d53 100644
--- a/ospfd/ospf_lsa.c
+++ b/ospfd/ospf_lsa.c
@@ -497,16 +497,49 @@
 }
 
 /* Set a link information. */
-static void
+static char
 link_info_set (struct stream *s, struct in_addr id,
 	       struct in_addr data, u_char type, u_char tos, u_int16_t cost)
 {
+  /* LSA stream is initially allocated to OSPF_MAX_LSA_SIZE, suits
+   * vast majority of cases. Some rare routers with lots of links need more.
+   * we try accomodate those here.
+   */
+  if (STREAM_WRITEABLE(s) < OSPF_ROUTER_LSA_LINK_SIZE)
+    {
+      size_t ret = OSPF_MAX_LSA_SIZE;
+      
+      /* Can we enlarge the stream still? */
+      if (STREAM_SIZE(s) == OSPF_MAX_LSA_SIZE)
+        {
+          /* we futz the size here for simplicity, really we need to account
+           * for just:
+           * IP Header - (sizeof (struct ip))
+           * OSPF Header - OSPF_HEADER_SIZE
+           * LSA Header - OSPF_LSA_HEADER_SIZE
+           * MD5 auth data, if MD5 is configured - OSPF_AUTH_MD5_SIZE.
+           *
+           * Simpler just to subtract OSPF_MAX_LSA_SIZE though.
+           */
+          ret = stream_resize (s, OSPF_MAX_PACKET_SIZE - OSPF_MAX_LSA_SIZE);
+        }
+      
+      if (ret == OSPF_MAX_LSA_SIZE)
+        {
+          zlog_warn ("%s: Out of space in LSA stream, left %ld, size %ld",
+                     __func__, STREAM_REMAIN (s), STREAM_SIZE (s));
+          return 0;
+        }
+    }
+  
   /* TOS based routing is not supported. */
   stream_put_ipv4 (s, id.s_addr);		/* Link ID. */
   stream_put_ipv4 (s, data.s_addr);		/* Link Data. */
   stream_putc (s, type);			/* Link Type. */
   stream_putc (s, tos);				/* TOS = 0. */
   stream_putw (s, cost);			/* Link Cost. */
+  
+  return 1;
 }
 
 /* Describe Point-to-Point link. */
@@ -526,9 +559,8 @@
       {
 	/* For unnumbered point-to-point networks, the Link Data field
 	   should specify the interface's MIB-II ifIndex value. */
-	link_info_set (s, nbr->router_id, oi->address->u.prefix4,
-		       LSA_LINK_TYPE_POINTOPOINT, 0, cost);
-	links++;
+	links += link_info_set (s, nbr->router_id, oi->address->u.prefix4,
+		                LSA_LINK_TYPE_POINTOPOINT, 0, cost);
       }
 
   if (CONNECTED_DEST_HOST(oi->connected))
@@ -541,7 +573,8 @@
       
       id.s_addr = oi->connected->destination->u.prefix4.s_addr;
       mask.s_addr = 0xffffffff;
-      link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, oi->output_cost);
+      links += link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0,
+                              oi->output_cost);
     }
   else
     {
@@ -549,10 +582,9 @@
 	 network regardless of the state of the neighbor */
       masklen2ip (oi->address->prefixlen, &mask);
       id.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr;
-      link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, oi->output_cost);
+      links += link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, 
+                              oi->output_cost);
     }
-  links++;
-
   return links;
 }
 
@@ -569,8 +601,8 @@
     {
       masklen2ip (oi->address->prefixlen, &mask);
       id.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr;
-      link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, oi->output_cost);
-      return 1;
+      return link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0,
+                            oi->output_cost);
     }
 
   dr = ospf_nbr_lookup_by_addr (oi->nbrs, &DR (oi));
@@ -579,17 +611,17 @@
 	     IPV4_ADDR_SAME (&oi->address->u.prefix4, &DR (oi))) &&
       ospf_nbr_count (oi, NSM_Full) > 0)
     {
-      link_info_set (s, DR (oi), oi->address->u.prefix4,
-		     LSA_LINK_TYPE_TRANSIT, 0, cost);
+      return link_info_set (s, DR (oi), oi->address->u.prefix4,
+                            LSA_LINK_TYPE_TRANSIT, 0, cost);
     }
   /* Describe type 3 link. */
   else
     {
       masklen2ip (oi->address->prefixlen, &mask);
       id.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr;
-      link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, oi->output_cost);
+      return link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0,
+                            oi->output_cost);
     }
-  return 1;
 }
 
 static int
@@ -603,8 +635,7 @@
 
   mask.s_addr = 0xffffffff;
   id.s_addr = oi->address->u.prefix4.s_addr;
-  link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, oi->output_cost);
-  return 1;
+  return link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, oi->output_cost);
 }
 
 /* Describe Virtual Link. */
@@ -618,9 +649,8 @@
     if ((nbr = ospf_nbr_lookup_ptop (oi)))
       if (nbr->state == NSM_Full)
 	{
-	  link_info_set (s, nbr->router_id, oi->address->u.prefix4,
-			 LSA_LINK_TYPE_VIRTUALLINK, 0, cost);
-	  return 1;
+	  return link_info_set (s, nbr->router_id, oi->address->u.prefix4,
+			        LSA_LINK_TYPE_VIRTUALLINK, 0, cost);
 	}
 
   return 0;
@@ -643,8 +673,7 @@
 
   mask.s_addr = 0xffffffff;
   id.s_addr = oi->address->u.prefix4.s_addr;
-  link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, 0);
-  links++;
+  links += link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, 0);
 
   if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
     zlog_debug ("PointToMultipoint: running ptomultip_set");
@@ -657,9 +686,8 @@
 	if (nbr->state == NSM_Full)
 
 	  {
-	    link_info_set (s, nbr->router_id, oi->address->u.prefix4,
-			   LSA_LINK_TYPE_POINTOPOINT, 0, cost);
-	    links++;
+	    links += link_info_set (s, nbr->router_id, oi->address->u.prefix4,
+			            LSA_LINK_TYPE_POINTOPOINT, 0, cost);
             if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
  	      zlog_debug ("PointToMultipoint: set link to %s",
 		         inet_ntoa(oi->address->u.prefix4));
@@ -816,8 +844,6 @@
   
   /* Create a stream for LSA. */
   s = stream_new (OSPF_MAX_LSA_SIZE);
-  lsah = (struct lsa_header *) STREAM_DATA (s);
-
   /* Set LSA common header fields. */
   lsa_header_set (s, LSA_OPTIONS_GET (area) | LSA_OPTIONS_NSSA_GET (area),
 		  OSPF_ROUTER_LSA, ospf->router_id, ospf->router_id);
@@ -827,6 +853,7 @@
 
   /* Set length. */
   length = stream_get_endp (s);
+  lsah = (struct lsa_header *) STREAM_DATA (s);
   lsah->length = htons (length);
 
   /* Now, create OSPF LSA instance. */
diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h
index 42c0882..9e480de 100644
--- a/ospfd/ospf_lsa.h
+++ b/ospfd/ospf_lsa.h
@@ -47,8 +47,9 @@
 #define OSPF_OPAQUE_AREA_LSA	     10
 #define OSPF_OPAQUE_AS_LSA	     11
 
-#define OSPF_LSA_HEADER_SIZE	20U
-#define OSPF_MAX_LSA_SIZE	1500U
+#define OSPF_LSA_HEADER_SIZE	     20U
+#define OSPF_ROUTER_LSA_LINK_SIZE    12U
+#define OSPF_MAX_LSA_SIZE	   1500U
 
 /* AS-external-LSA refresh method. */
 #define LSA_REFRESH_IF_CHANGED	0