2004-06-06 Paul Jakma <paul.jakma@sun.com>

	* ripd.h: Add define for the RIPv2 Authentication Data family
	  Move the auth type defines up to where other defines live.
	  Add RIP_AUTH_MD5_COMPAT_SIZE, for backwards compatible
	  md5->auth_len size. Add md5_auth_len field to struct
          rip_interface: (rip_interface_new) Init md5_auth_len to compatible
          size.
          (ip_rip_authentication_mode_cmd) Extended to handle setting
          md5 auth-length. Appropriate aliases added.
          (no_ip_rip_authentication_mode_cmd) Reset md5_auth_len to
          compatible size.
          (rip_interface_config_write) Teach it about md5_auth_len.
          _always_ write out the auth-length, so that everyone will get
          the setting in their config file, and hence allow for a future
          change of default for md5_auth_len to be less painful - every md5
          user will have this setting in their config file.
          ripd.c: (rip_packet_dump) Change nasty hard coded constants to
          symbolic defines. Change various tests of 'ntoh.(variable) ==
          constant' to test 'variable == ntoh.(constant)'. Clean up
          indentation on some long lines.
          (rip_auth_simple_password) ditto.
          (rip_auth_md5) ditto, also add length argument and sanity check
          md5 data offset field. Sanity check md5 auth length, accept RFC
          or old-ripd/cisco lengths.
          (rip_auth_md5_set) as per (rip_packet_dump), also write out
          the configured md5 auth length for the interface (old-ripd or rfc)
          (rip_read) as per (rip_packet_dump)
          (rip_write_rte) ditto
          (rip_response_process) ditto
          (rip_write_rte) ditto
diff --git a/ripd/ChangeLog b/ripd/ChangeLog
index 2b65db3..848efee 100644
--- a/ripd/ChangeLog
+++ b/ripd/ChangeLog
@@ -1,3 +1,35 @@
+2004-06-06 Paul Jakma <paul.jakma@sun.com>
+
+	* ripd.h: Add define for the RIPv2 Authentication Data family
+	  Move the auth type defines up to where other defines live.
+	  Add RIP_AUTH_MD5_COMPAT_SIZE, for backwards compatible
+	  md5->auth_len size. Add md5_auth_len field to struct
+          rip_interface: (rip_interface_new) Init md5_auth_len to compatible 
+          size.
+          (ip_rip_authentication_mode_cmd) Extended to handle setting
+          md5 auth-length. Appropriate aliases added.
+          (no_ip_rip_authentication_mode_cmd) Reset md5_auth_len to
+          compatible size.
+          (rip_interface_config_write) Teach it about md5_auth_len.
+          _always_ write out the auth-length, so that everyone will get
+          the setting in their config file, and hence allow for a future
+          change of default for md5_auth_len to be less painful - every md5
+          user will have this setting in their config file.
+          ripd.c: (rip_packet_dump) Change nasty hard coded constants to 
+          symbolic defines. Change various tests of 'ntoh.(variable) ==
+          constant' to test 'variable == ntoh.(constant)'. Clean up
+          indentation on some long lines.
+          (rip_auth_simple_password) ditto.
+          (rip_auth_md5) ditto, also add length argument and sanity check 
+          md5 data offset field. Sanity check md5 auth length, accept RFC
+          or old-ripd/cisco lengths.
+          (rip_auth_md5_set) as per (rip_packet_dump), also write out
+          the configured md5 auth length for the interface (old-ripd or rfc)
+          (rip_read) as per (rip_packet_dump)
+          (rip_write_rte) ditto
+          (rip_response_process) ditto
+          (rip_write_rte) ditto
+          
 2004-06-04 JJ Ludman <jacques.ludman@sun.com>
 
 	* ripd.c: Interoperability fix. Correct value for MD5 auth length
diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c
index 3d69d6c..4daa5b3 100644
--- a/ripd/rip_interface.c
+++ b/ripd/rip_interface.c
@@ -125,6 +125,7 @@
      compatibility. */
   /* ri->auth_type = RIP_NO_AUTH; */
   ri->auth_type = RIP_AUTH_SIMPLE_PASSWORD;
+  ri->md5_auth_len = RIP_AUTH_MD5_COMPAT_SIZE;
 
   /* Set default split-horizon behavior.  If the interface is Frame
      Relay or SMDS is enabled, the default value for split-horizon is
@@ -1678,6 +1679,12 @@
   ifp = (struct interface *)vty->index;
   ri = ifp->info;
 
+  if ( (argc < 1) || (argc > 2) )
+    {
+      vty_out (vty, "incorrect argument count%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+    
   if (strncmp ("md5", argv[0], strlen (argv[0])) == 0)
     ri->auth_type = RIP_AUTH_MD5;
   else if (strncmp ("text", argv[0], strlen (argv[0])) == 0)
@@ -1688,9 +1695,38 @@
       return CMD_WARNING;
     }
 
+  if (argc == 1)
+  return CMD_SUCCESS;
+
+  if ( (argc == 2) && (ri->auth_type != RIP_AUTH_MD5) )
+    {
+      vty_out (vty, "auth length argument only valid for md5%s", VTY_NEWLINE);
+      return CMD_WARNING;
+}
+
+  if (strncmp ("r", argv[1], 1) == 0)
+    ri->md5_auth_len = RIP_AUTH_MD5_SIZE;
+  else if (strncmp ("o", argv[1], 1) == 0)
+    ri->md5_auth_len = RIP_AUTH_MD5_COMPAT_SIZE;
+  else 
+    return CMD_WARNING;
+
   return CMD_SUCCESS;
 }
 
+ALIAS (ip_rip_authentication_mode,
+       ip_rip_authentication_mode_authlen_cmd,
+       "ip rip authentication mode (md5|text) auth-length (rfc|old-ripd)",
+       IP_STR
+       "Routing Information Protocol\n"
+       "Authentication control\n"
+       "Authentication mode\n"
+       "Keyed message digest\n"
+       "Clear text authentication\n"
+       "MD5 authentication data length\n"
+       "RFC compatible\n"
+       "Old ripd compatible\n")
+
 DEFUN (no_ip_rip_authentication_mode,
        no_ip_rip_authentication_mode_cmd,
        "no ip rip authentication mode",
@@ -1708,6 +1744,7 @@
 
   /* ri->auth_type = RIP_NO_AUTH; */
   ri->auth_type = RIP_AUTH_SIMPLE_PASSWORD;
+  ri->md5_auth_len = RIP_AUTH_MD5_COMPAT_SIZE;
 
   return CMD_SUCCESS;
 }
@@ -1723,6 +1760,20 @@
        "Keyed message digest\n"
        "Clear text authentication\n")
 
+ALIAS (no_ip_rip_authentication_mode,
+       no_ip_rip_authentication_mode_type_authlen_cmd,
+       "no ip rip authentication mode (md5|text) auth-length (rfc|old-ripd)",
+       NO_STR
+       IP_STR
+       "Routing Information Protocol\n"
+       "Authentication control\n"
+       "Authentication mode\n"
+       "Keyed message digest\n"
+       "Clear text authentication\n"
+       "MD5 authentication data length\n"
+       "RFC compatible\n"
+       "Old ripd compatible\n")
+
 DEFUN (ip_rip_authentication_string,
        ip_rip_authentication_string_cmd,
        "ip rip authentication string LINE",
@@ -1988,6 +2039,7 @@
           (ri->ri_send == RI_RIP_UNSPEC)                   &&
           (ri->ri_receive == RI_RIP_UNSPEC)                &&
           (ri->auth_type != RIP_AUTH_MD5)                  &&
+          (ri->md5_auth_len != RIP_AUTH_MD5_SIZE)          &&
           (!ri->auth_str)                                  &&
           (!ri->key_chain)                                 )
         continue;
@@ -2034,8 +2086,16 @@
       if (ri->auth_type == RIP_AUTH_SIMPLE_PASSWORD)
 	vty_out (vty, " ip rip authentication mode text%s", VTY_NEWLINE);
 #endif /* 0 */
+
       if (ri->auth_type == RIP_AUTH_MD5)
-	vty_out (vty, " ip rip authentication mode md5%s", VTY_NEWLINE);
+        {
+          vty_out (vty, " ip rip authentication mode md5");
+          if (ri->md5_auth_len == RIP_AUTH_MD5_COMPAT_SIZE)
+            vty_out (vty, " auth-length old-ripd");
+          else 
+            vty_out (vty, " auth-length rfc");
+          vty_out (vty, "%s", VTY_NEWLINE);
+        }
 
       if (ri->auth_str)
 	vty_out (vty, " ip rip authentication string %s%s",
@@ -2165,8 +2225,10 @@
   install_element (INTERFACE_NODE, &no_ip_rip_receive_version_num_cmd);
 
   install_element (INTERFACE_NODE, &ip_rip_authentication_mode_cmd);
+  install_element (INTERFACE_NODE, &ip_rip_authentication_mode_authlen_cmd);
   install_element (INTERFACE_NODE, &no_ip_rip_authentication_mode_cmd);
   install_element (INTERFACE_NODE, &no_ip_rip_authentication_mode_type_cmd);
+  install_element (INTERFACE_NODE, &no_ip_rip_authentication_mode_type_authlen_cmd);
 
   install_element (INTERFACE_NODE, &ip_rip_authentication_key_chain_cmd);
   install_element (INTERFACE_NODE, &no_ip_rip_authentication_key_chain_cmd);
diff --git a/ripd/ripd.c b/ripd/ripd.c
index 7567ba2..6914822 100644
--- a/ripd/ripd.c
+++ b/ripd/ripd.c
@@ -701,16 +701,16 @@
 	{
 	  netmask = ip_masklen (rte->mask);
 
-	  if (ntohs (rte->family) == 0xffff)
+          if (rte->family == htons (RIP_FAMILY_AUTH))
             {
-	      if (ntohs (rte->tag) == RIP_AUTH_SIMPLE_PASSWORD)
+              if (rte->tag == htons (RIP_AUTH_SIMPLE_PASSWORD))
 		{
 		  p = (u_char *)&rte->prefix;
 
 		  zlog_info ("  family 0x%X type %d auth string: %s",
 			     ntohs (rte->family), ntohs (rte->tag), p);
 		}
-	      else if (ntohs (rte->tag) == RIP_AUTH_MD5)
+              else if (rte->tag == htons (RIP_AUTH_MD5))
 		{
 		  struct rip_md5_info *md5;
 
@@ -719,11 +719,13 @@
 		  zlog_info ("  family 0x%X type %d (MD5 authentication)",
 			     ntohs (md5->family), ntohs (md5->type));
 		  zlog_info ("    RIP-2 packet len %d Key ID %d"
-			     " Auth Data len %d", ntohs (md5->packet_len),
-			     md5->keyid, md5->auth_len);
-		  zlog_info ("    Sequence Number %ld", (u_long)ntohl (md5->sequence));
+                             " Auth Data len %d",
+                             ntohs (md5->packet_len), md5->keyid,
+                             md5->auth_len);
+                  zlog_info ("    Sequence Number %ld",
+                             (u_long) ntohl (md5->sequence));
 		}
-	      else if (ntohs (rte->tag) == RIP_AUTH_DATA)
+              else if (rte->tag == htons (RIP_AUTH_DATA))
 		{
 		  p = (u_char *)&rte->prefix;
 
@@ -731,8 +733,9 @@
 			     ntohs (rte->family), ntohs (rte->tag));
 		  zlog_info ("    MD5: %02X%02X%02X%02X%02X%02X%02X%02X"
 			     "%02X%02X%02X%02X%02X%02X%02X",
-			     p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7],
-			     p[9],p[10],p[11],p[12],p[13],p[14],p[15]);
+                             p[0], p[1], p[2], p[3], p[4], p[5], p[6],
+                             p[7], p[9], p[10], p[11], p[12], p[13],
+                             p[14], p[15]);
 		}
 	      else
 		{
@@ -742,10 +745,10 @@
             }
 	  else
 	    zlog_info ("  %s/%d -> %s family %d tag %d metric %ld",
-		       inet_ntop (AF_INET, &rte->prefix, pbuf, BUFSIZ),netmask,
-		       inet_ntop (AF_INET, &rte->nexthop, nbuf, BUFSIZ),
-		       ntohs (rte->family), ntohs (rte->tag), 
-		       (u_long)ntohl (rte->metric));
+                       inet_ntop (AF_INET, &rte->prefix, pbuf, BUFSIZ),
+                       netmask, inet_ntop (AF_INET, &rte->nexthop, nbuf,
+                                           BUFSIZ), ntohs (rte->family),
+                       ntohs (rte->tag), (u_long) ntohl (rte->metric));
 	}
       else
 	{
@@ -801,7 +804,7 @@
   ri = ifp->info;
 
   if (ri->auth_type != RIP_AUTH_SIMPLE_PASSWORD
-      || ntohs (rte->tag) != RIP_AUTH_SIMPLE_PASSWORD)
+      || rte->tag != htons(RIP_AUTH_SIMPLE_PASSWORD))
     return 0;
 
   /* Simple password authentication. */
@@ -831,7 +834,7 @@
 /* RIP version 2 authentication with MD5. */
 int
 rip_auth_md5 (struct rip_packet *packet, struct sockaddr_in *from,
-	      struct interface *ifp)
+              int length, struct interface *ifp)
 {
   struct rip_interface *ri;
   struct rip_md5_info *md5;
@@ -845,27 +848,44 @@
   char *auth_str = NULL;
   
   if (IS_RIP_DEBUG_EVENT)
-    zlog_info ("RIPv2 MD5 authentication from %s", inet_ntoa (from->sin_addr));
+    zlog_info ("RIPv2 MD5 authentication from %s",
+               inet_ntoa (from->sin_addr));
 
   ri = ifp->info;
   md5 = (struct rip_md5_info *) &packet->rte;
 
   /* Check auth type. */
-  if (ri->auth_type != RIP_AUTH_MD5 || ntohs (md5->type) != RIP_AUTH_MD5)
+  if (ri->auth_type != RIP_AUTH_MD5 || md5->type != htons(RIP_AUTH_MD5))
     return 0;
 
-/*
- * If the authentication length is less than 16, then it must be wrong for
- * any interpretation of rfc2082.
+  /* If the authentication length is less than 16, then it must be wrong for
+   * any interpretation of rfc2082. Some implementations also interpret
+   * this as RIP_HEADER_SIZE+ RIP_AUTH_MD5_SIZE, aka RIP_AUTH_MD5_COMPAT_SIZE.
  */
-  if (md5->auth_len < RIP_AUTH_MD5_SIZE)
+  if ( !((md5->auth_len == RIP_AUTH_MD5_SIZE)
+         || (md5->auth_len == RIP_AUTH_MD5_COMPAT_SIZE)))
     {
       if (IS_RIP_DEBUG_EVENT)
-       zlog_info ("RIPv2 MD5 authentication, authentication length field too \
-         short");
+        zlog_warn ("RIPv2 MD5 authentication, strange authentication "
+                   "length field %d", md5->auth_len);
     return 0;
     }
 
+  /* grab and verify check packet length */
+  packet_len = ntohs (md5->packet_len);
+
+  if (packet_len > (length - RIP_HEADER_SIZE - RIP_AUTH_MD5_SIZE))
+    {
+      if (IS_RIP_DEBUG_EVENT)
+        zlog_warn ("RIPv2 MD5 authentication, packet length field %d "
+                   "greater than received length %d!",
+                   md5->packet_len, length);
+      return 0;
+    }
+
+  /* retrieve authentication data */
+  md5data = (struct rip_md5_data *) (((u_char *) packet) + packet_len);
+
   if (ri->key_chain)
     {
       keychain = keychain_lookup (ri->key_chain);
@@ -886,8 +906,6 @@
     return 0;
 
   /* MD5 digest authentication. */
-  packet_len = ntohs (md5->packet_len);
-  md5data = (struct rip_md5_data *)(((u_char *) packet) + packet_len);
 
   /* Save digest to pdigest. */
   memcpy (pdigest, md5data->digest, RIP_AUTH_MD5_SIZE);
@@ -897,7 +915,7 @@
   strncpy ((char *)md5data->digest, auth_str, RIP_AUTH_MD5_SIZE);
 
   md5_init_ctx (&ctx);
-  md5_process_bytes (packet, packet_len + RIP_HEADER_SIZE + RIP_AUTH_MD5_SIZE, \
+  md5_process_bytes (packet, packet_len + RIP_HEADER_SIZE + RIP_AUTH_MD5_SIZE,
     &ctx);
   md5_finish_ctx (&ctx, digest);
 
@@ -967,7 +985,7 @@
   len += RIP_RTE_SIZE;
 
   /* MD5 authentication. */
-  stream_putw (s, 0xffff);
+  stream_putw (s, RIP_FAMILY_AUTH);
   stream_putw (s, RIP_AUTH_MD5);
 
   /* RIP-2 Packet length.  Actual value is filled in
@@ -980,9 +998,11 @@
   else
     stream_putc (s, 1);
 
-  /* Auth Data Len.  Set 16 for MD5 authentication
-     data. */
-  stream_putc (s, RIP_AUTH_MD5_SIZE);
+  /* Auth Data Len.  Set 16 for MD5 authentication data. Older ripds 
+   * however expect RIP_HEADER_SIZE + RIP_AUTH_MD5_SIZE so we allow for this
+   * to be configurable. 
+   */
+  stream_putc (s, ri->md5_auth_len);
 
   /* Sequence Number (non-decreasing). */
   /* RFC2080: The value used in the sequence number is
@@ -998,8 +1018,8 @@
   stream_set_putp (s, len);
 
   /* Set authentication data. */
-  stream_putw (s, 0xffff);
-  stream_putw (s, 0x01);
+  stream_putw (s, RIP_FAMILY_AUTH);
+  stream_putw (s, RIP_AUTH_DATA);
 
   /* Generate a digest for the RIP packet. */
   memset (secret, 0, RIP_AUTH_MD5_SIZE);
@@ -1030,7 +1050,7 @@
 
   /* The Response must be ignored if it is not from the RIP
      port. (RFC2453 - Sec. 3.9.2)*/
-  if (ntohs (from->sin_port) != RIP_PORT_DEFAULT) 
+  if (from->sin_port != htons(RIP_PORT_DEFAULT))
     {
       zlog_info ("response doesn't come from RIP port: %d",
 		 from->sin_port);
@@ -1070,10 +1090,10 @@
       /* Check is done in rip_read(). So, just skipping it */
       if (packet->version == RIPv2 &&
 	  rte == packet->rte &&
-	  rte->family == 0xffff)
+	  rte->family == htons(RIP_FAMILY_AUTH))
 	continue;
 
-      if (ntohs (rte->family) != AF_INET)
+      if (rte->family != htons(AF_INET))
 	{
 	  /* Address family check.  RIP only supports AF_INET. */
 	  zlog_info ("Unsupported family %d from %s.",
@@ -1795,7 +1815,8 @@
 
   if ((ri->auth_type == RIP_NO_AUTH) 
       && rtenum 
-      && (packet->version == RIPv2) && (packet->rte->family == 0xffff))
+      && (packet->version == RIPv2) 
+      && (packet->rte->family == htons(RIP_FAMILY_AUTH)))
     {
       if (IS_RIP_DEBUG_EVENT)
 	zlog_warn ("packet RIPv%d is dropped because authentication disabled", 
@@ -1814,14 +1835,16 @@
      unauthenticated manner. */
 
   if ((ri->auth_type == RIP_AUTH_SIMPLE_PASSWORD 
-       || ri->auth_type == RIP_AUTH_MD5)
-      && rtenum)
+       || ri->auth_type == RIP_AUTH_MD5) && rtenum)
     {
       /* We follow maximum security. */
-      if (packet->version == RIPv1 && packet->rte->family == 0xffff)
+      if (packet->version == RIPv1 
+      && packet->rte->family == htons(RIP_FAMILY_AUTH))
 	{
 	  if (IS_RIP_DEBUG_PACKET)
-	    zlog_warn ("packet RIPv%d is dropped because authentication enabled", packet->version);
+            zlog_warn
+              ("packet RIPv%d is dropped because authentication enabled",
+               packet->version);
 	  rip_peer_bad_packet (&from);
 	  return -1;
 	}
@@ -1829,27 +1852,29 @@
       /* Check RIPv2 authentication. */
       if (packet->version == RIPv2)
 	{
-	  if (packet->rte->family == 0xffff)
+          if (packet->rte->family == htons(RIP_FAMILY_AUTH))
 	    {
-	      if (ntohs (packet->rte->tag) == RIP_AUTH_SIMPLE_PASSWORD)
+              if (packet->rte->tag == htons(RIP_AUTH_SIMPLE_PASSWORD))
                 {
 		  ret = rip_auth_simple_password (packet->rte, &from, ifp);
 		  if (! ret)
 		    {
 		      if (IS_RIP_DEBUG_EVENT)
-			zlog_warn ("RIPv2 simple password authentication failed");
+                        zlog_warn
+                          ("RIPv2 simple password authentication failed");
 		      rip_peer_bad_packet (&from);
 		      return -1;
 		    }
 		  else
 		    {
 		      if (IS_RIP_DEBUG_EVENT)
-			zlog_info ("RIPv2 simple password authentication success");
+                        zlog_info
+                          ("RIPv2 simple password authentication success");
 		    }
                 }
-	      else if (ntohs (packet->rte->tag) == RIP_AUTH_MD5)
+              else if (packet->rte->tag == htons(RIP_AUTH_MD5))
                 {
-		  ret = rip_auth_md5 (packet, &from, ifp);
+                  ret = rip_auth_md5 (packet, &from, len, ifp);
 		  if (! ret)
 		    {
 		      if (IS_RIP_DEBUG_EVENT)
@@ -1880,7 +1905,8 @@
 	      if (ri->auth_str || ri->key_chain)
 		{
 		  if (IS_RIP_DEBUG_EVENT)
-		    zlog_warn ("RIPv2 authentication failed: no authentication in packet");
+                    zlog_warn
+                      ("RIPv2 authentication failed: no authentication in packet");
 		  rip_peer_bad_packet (&from);
 		  return -1;
 		}
@@ -1996,7 +2022,7 @@
 	    {
 	      if (ri->auth_str)
 		{
-		  stream_putw (s, 0xffff);
+		  stream_putw (s, RIP_FAMILY_AUTH);
 		  stream_putw (s, RIP_AUTH_SIMPLE_PASSWORD);
 
 		  memset ((s->data + s->putp), 0, 16);
@@ -2018,7 +2044,7 @@
 		      
 		      if (key)
 			{
-			  stream_putw (s, 0xffff);
+			  stream_putw (s, RIP_FAMILY_AUTH);
 			  stream_putw (s, RIP_AUTH_SIMPLE_PASSWORD);
 
 			  memset ((s->data + s->putp), 0, 16);
diff --git a/ripd/ripd.h b/ripd/ripd.h
index 13a72a1..5c50c57 100644
--- a/ripd/ripd.h
+++ b/ripd/ripd.h
@@ -78,8 +78,18 @@
 #define RIP_ROUTE_REDISTRIBUTE           3
 #define RIP_ROUTE_INTERFACE              4
 
-/* RIP MD5 authentication. */
+/* RIPv2 special RTE family types */
+#define RIP_FAMILY_AUTH                  0xffff
+
+/* RIPv2 authentication types, for RIP_FAMILY_AUTH RTE's */
+#define RIP_NO_AUTH                0
+#define RIP_AUTH_DATA              1
+#define RIP_AUTH_SIMPLE_PASSWORD   2
+#define RIP_AUTH_MD5               3
+
+/* RIPv2 MD5 authentication. */
 #define RIP_AUTH_MD5_SIZE               16
+#define RIP_AUTH_MD5_COMPAT_SIZE        RIP_RTE_SIZE
 
 /* RIP structure. */
 struct rip 
@@ -237,10 +247,6 @@
   int ri_receive;
 
   /* RIPv2 authentication type. */
-#define RIP_NO_AUTH                0
-#define RIP_AUTH_DATA              1
-#define RIP_AUTH_SIMPLE_PASSWORD   2
-#define RIP_AUTH_MD5               3
   int auth_type;
 
   /* RIPv2 authentication string. */
@@ -249,6 +255,9 @@
   /* RIPv2 authentication key chain. */
   char *key_chain;
 
+  /* value to use for md5->auth_len */
+  u_int8_t md5_auth_len;
+
   /* Split horizon flag. */
   split_horizon_policy_t split_horizon;
   split_horizon_policy_t split_horizon_default;