[quagga-dev 1435] ospfd oversized LSA bug

Paul Jakma Paul.Jakma at Sun.COM
Mon Aug 23 22:52:36 BST 2004


Hi,

See below for a possible fix to the oversized LSA problem which two 
people so far have hit (Peter Frost and someone before that on IRC whose 
name i unfortunately can not remember).

The original symptom was that ospfd quickly balloons in size and dies 
out-of-memory. The cause was that ospfd looped trying to construct LSA 
Update packets, not breaking until the update list was empty (which it 
no longer does in CVS), because an LSA existed on the update which was 
larger than could be sent via the interface concerned - ie an oversized 
LSA which ospfd received either from an interface with jumbo frames, or 
in IP fragments - because the LSA simply is huge.

Peter Frost, IIRC, confirmed that simply allocating a big packet buffer 
worked, the below fix tries to be more discriminatory. It works on a 
normal OSPF network, i'd love to hear from someone with a router which 
generates large LSAs (ie greater than MTU) as to whether this works for 
them.

This patch also removes the almost certainly bogus magic '88' constant 
from ospf_packet_max.

Index: ospf_packet.c
===================================================================
RCS file: /var/cvsroot/quagga/ospfd/ospf_packet.c,v
retrieving revision 1.32
diff -u -r1.32 ospf_packet.c
--- ospf_packet.c	19 Aug 2004 04:43:43 -0000	1.32
+++ ospf_packet.c	23 Aug 2004 13:20:34 -0000
@@ -247,9 +247,11 @@
    int max;

    if ( ospf_auth_type (oi) == OSPF_AUTH_CRYPTOGRAPHIC)
-    max = oi->ifp->mtu - OSPF_AUTH_MD5_SIZE - 88;
+    max = oi->ifp->mtu - OSPF_AUTH_MD5_SIZE;
    else
-    max = oi->ifp->mtu - 88;
+    max = oi->ifp->mtu;
+ 
+  max -= (OSPF_HEADER_SIZE + sizeof (struct ip));

    return max;
  }
@@ -2680,7 +2682,7 @@
      zlog_info ("ospf_make_ls_upd: Start");

    pp = stream_get_putp (s);
-  ospf_output_forward (s, 4);
+  ospf_output_forward (s, OSPF_LS_UPD_MIN_SIZE);

    while ((node = listhead (update)) != NULL)
      {
@@ -2694,12 +2696,8 @@
        assert (lsa);
        assert (lsa->data);

-      /* Check packet size. */
-      /* XXX: LSA can be > packet-headers, eg router-lsas for machines
-       * with hundreds of interfaces, received as several
-       * fragmented packets.
-       */
-      if (length + delta + ntohs (lsa->data->length) > OSPF_PACKET_MAX (oi))
+      /* Will it fit? */
+      if (length + delta + ntohs (lsa->data->length) > stream_get_size (s))
          break;

        /* Keep pointer to LS age. */
@@ -3064,6 +3062,72 @@
    list_delete (update);
  }

+/* Determine size for packet. Must be at least big enough to accomodate next
+ * LSA on list, which may be bigger than MTU size.
+ *
+ * Return pointer to new ospf_packet
+ * NULL if we can not allocate, eg because LSA is bigger than imposed limit
+ * on packet sizes (in which case offending LSA is deleted from update list)
+ */
+static struct ospf_packet *
+ospf_ls_upd_packet_new (struct list *update, struct ospf_interface *oi)
+{
+  struct ospf_lsa *lsa;
+  struct listnode *ln;
+  size_t size;
+  static char warned = 0;
+
+  ln = listhead (update);
+  lsa = getdata (ln);
+  assert (lsa);
+  assert (lsa->data);
+
+  if ((OSPF_LS_UPD_MIN_SIZE + ntohs (lsa->data->length))
+      > ospf_packet_max (oi))
+    {
+      if (!warned)
+        {
+          zlog_warn ("ospf_ls_upd_packet_new: oversized LSA encountered!"
+                     "will need to fragment. Not optimal. Try divide up"
+                     " your network with areas. Use 'debug ospf packet send'"
+                     " to see details, or look at 'show ip ospf database ..'");
+          warned = 1;
+        }
+
+      if (IS_DEBUG_OSPF_PACKET (0, SEND))
+        zlog_warn ("ospf_ls_upd_packet_new: oversized LSA id:%s,"
+                   " %d bytes originated by %s, will be fragmented!",
+                   inet_ntoa (lsa->data->id),
+                   ntohs (lsa->data->length),
+                   inet_ntoa (lsa->data->adv_router));
+
+      /* Allocate just enough to fit this LSA only, to avoid including other
+       * LSAs in fragmented LSA Update. kernel will allocate ip headers for
+       * all but first frag. So we allocate:
+       *
+       * ip header + ospf header + auth data (if required) 
+       *    (which == mtu - ospf_packet_max)
+       *  + lsa data and LSU header (ie size bits).
+       */
+      size = ntohs (lsa->data->length) + (oi->ifp->mtu - ospf_packet_max (oi))
+        + OSPF_LS_UPD_MIN_SIZE;
+    }
+  else
+    size = oi->ifp->mtu;
+
+  if (size > OSPF_MAX_PACKET_SIZE)
+    {
+      zlog_warn ("ospf_ls_upd_packet_new: oversized LSA id:%s too big,"
+                 " %d bytes, dropping it completely."
+                 " OSPF routing is broken!",
+                 inet_ntoa (lsa->data->id), ntohs (lsa->data->length));
+      list_delete_node (update, ln);
+      return NULL;
+    }
+
+  return ospf_packet_new (size);
+}
+
  static void
  ospf_ls_upd_queue_send (struct ospf_interface *oi, list update,
  			struct in_addr addr)
@@ -3073,8 +3137,8 @@

    if (IS_DEBUG_OSPF_EVENT)
      zlog_info ("listcount = %d, dst %s", listcount (update), inet_ntoa(addr));
-
-  op = ospf_packet_new (oi->ifp->mtu);
+ 
+  op = ospf_ls_upd_packet_new (update, oi);

    /* Prepare OSPF common header. */
    ospf_make_header (OSPF_MSG_LS_UPD, oi, op->s);



More information about the Quagga-dev mailing list