2005-02-04 Paul Jakma <paul@dishone.st>

	* ripd.c: Untangle the construction of RIP auth data.
	  (rip_auth_prepare_str_send) new helper function, prepare
	  correct key string.
	  (rip_auth_simple_write) new helper, write out the
	  rip simple password auth psuedo-RTE.
	  (rip_auth_md5_ah_write) new helper, write out the
	  MD5 auth-header psuedo-RTE.
	  (rip_auth_header_write) new helper, write out correct
	  auth header data / psuedo-RTE.
	  (rip_auth_md5_set) rip out the memmove and writing of the
	  auth header psuedo-RTE. So that all that is left is to
	  write the trailing auth digest, and update digest offset
	  field in the original header.
	  (rip_write_rte) rip out writing of RIP header, writing of
	  simple auth data psuedo-RTE. Make it do what its name suggests,
	  write out actual RTEs.
	  (rip_output_process) remove the incorrect additional decrements
	  of rtemax. Prepare the auth_str, which simple or MD5 auth will
	  need. Move write out of RIP header and auth data to inside the
	  loop. Adjust paramaters as required.
diff --git a/ripd/ChangeLog b/ripd/ChangeLog
index 60be27e..edb504a 100644
--- a/ripd/ChangeLog
+++ b/ripd/ChangeLog
@@ -1,3 +1,26 @@
+2005-02-04 Paul Jakma <paul@dishone.st>
+
+	* ripd.c: Untangle the construction of RIP auth data.
+	  (rip_auth_prepare_str_send) new helper function, prepare
+	  correct key string.
+	  (rip_auth_simple_write) new helper, write out the
+	  rip simple password auth psuedo-RTE.
+	  (rip_auth_md5_ah_write) new helper, write out the
+	  MD5 auth-header psuedo-RTE.
+	  (rip_auth_header_write) new helper, write out correct
+	  auth header data / psuedo-RTE.
+	  (rip_auth_md5_set) rip out the memmove and writing of the
+	  auth header psuedo-RTE. So that all that is left is to
+	  write the trailing auth digest, and update digest offset
+	  field in the original header.
+	  (rip_write_rte) rip out writing of RIP header, writing of
+	  simple auth data psuedo-RTE. Make it do what its name suggests,
+	  write out actual RTEs.
+	  (rip_output_process) remove the incorrect additional decrements 
+	  of rtemax. Prepare the auth_str, which simple or MD5 auth will
+	  need. Move write out of RIP header and auth data to inside the
+	  loop. Adjust paramaters as required.
+
 2005-01-30 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
 
 	* ripd.c: (rip_create_socket) Replace perror with zlog_err.
diff --git a/ripd/ripd.c b/ripd/ripd.c
index b5d130e..c7ae00d 100644
--- a/ripd/ripd.c
+++ b/ripd/ripd.c
@@ -929,72 +929,73 @@
     return 0;
 }
 
-void
-rip_auth_md5_set (struct stream *s, struct interface *ifp)
+/* Pick correct auth string for sends, prepare auth_str buffer for use.
+ * (left justified and padded).
+ *
+ * presumes one of ri or key is valid, and that the auth strings they point
+ * to are nul terminated. If neither are present, auth_str will be fully
+ * zero padded.
+ *
+ */
+static void
+rip_auth_prepare_str_send (struct rip_interface *ri, struct key *key, 
+                           char *auth_str, int len)
 {
-  struct rip_interface *ri;
-  struct keychain *keychain = NULL;
-  struct key *key = NULL;
-  unsigned long len;
-  struct md5_ctx ctx;
-  unsigned char secret[RIP_AUTH_MD5_SIZE];
-  unsigned char digest[RIP_AUTH_MD5_SIZE];
-  char *auth_str = NULL;
+  assert (ri || key);
 
-  ri = ifp->info;
+  memset (auth_str, 0, len);
+  if (key && key->string)
+    strncpy (auth_str, key->string, len);
+  else if (ri->auth_str)
+    strncpy (auth_str, ri->auth_str, len);
 
-  /* Make it sure this interface is configured as MD5
-     authentication. */
-  if (ri->auth_type != RIP_AUTH_MD5)
-    return;
+  return;
+}
 
-  /* Lookup key chain. */
-  if (ri->key_chain)
-    {
-      keychain = keychain_lookup (ri->key_chain);
-      if (keychain == NULL)
-	return;
-
-      /* Lookup key. */
-      key = key_lookup_for_send (keychain);
-      if (key == NULL)
-	return;
-
-      auth_str = key->string;
-    }
-
-  if (ri->auth_str)
-    auth_str = ri->auth_str;
-
-  if (! auth_str)
-    return;
-
-  /* Get packet length. */
-  len = s->putp;
-
-  /* Check packet length. */
-  if (len < (RIP_HEADER_SIZE + RIP_RTE_SIZE))
-    {
-      zlog_err ("rip_auth_md5_set(): packet length %ld is less than minimum length.", len);
-      return;
-    }
-
-  /* Move RTE. */
-  memmove (s->data + RIP_HEADER_SIZE + RIP_RTE_SIZE,
-	   s->data + RIP_HEADER_SIZE,
-	   len - RIP_HEADER_SIZE);
+/* Write RIPv2 simple password authentication information
+ *
+ * auth_str is presumed to be 2 bytes and correctly prepared 
+ * (left justified and zero padded).
+ */
+static void
+rip_auth_simple_write (struct stream *s, char *auth_str, int len)
+{
+  assert (s && len == RIP_AUTH_SIMPLE_SIZE);
   
-  /* Set pointer to authentication header. */
-  stream_set_putp (s, RIP_HEADER_SIZE);
-  len += RIP_RTE_SIZE;
+  stream_putw (s, RIP_FAMILY_AUTH);
+  stream_putw (s, RIP_AUTH_SIMPLE_PASSWORD);
+  stream_put (s, auth_str, RIP_AUTH_SIMPLE_SIZE);
+  
+  return;
+}
+
+/* write RIPv2 MD5 "authentication header" 
+ * (uses the auth key data field)
+ *
+ * Digest offset field is set to 0.
+ *
+ * returns: offset of the digest offset field, which must be set when
+ * length to the auth-data MD5 digest is known.
+ */
+static size_t
+rip_auth_md5_ah_write (struct stream *s, struct rip_interface *ri, 
+                       struct key *key)
+{
+  size_t len = 0;
+
+  assert (s && ri && ri->auth_type == RIP_AUTH_MD5);
 
   /* MD5 authentication. */
   stream_putw (s, RIP_FAMILY_AUTH);
   stream_putw (s, RIP_AUTH_MD5);
 
-  /* RIP-2 Packet length.  Actual value is filled in
-     rip_auth_md5_set(). */
-  stream_putw (s, len);
+  /* MD5 AH digest offset field.
+   *
+   * Set to placeholder value here, to true value when RIP-2 Packet length
+   * is known.  Actual value is set in .....().
+   */
+  len = stream_get_putp(s);
+  stream_putw (s, 0);
 
   /* Key ID. */
   if (key)
@@ -1018,19 +1019,66 @@
   stream_putl (s, 0);
   stream_putl (s, 0);
 
-  /* Set pointer to authentication data. */
-  stream_set_putp (s, len);
+  return len;
+}
 
+/* If authentication is in used, write the appropriate header
+ * returns stream offset to which length must later be written
+ * or 0 if this is not required
+ */
+static size_t
+rip_auth_header_write (struct stream *s, struct rip_interface *ri, 
+                       struct key *key, char *auth_str, int len)
+{
+  assert (ri->auth_type != RIP_NO_AUTH);
+  
+  switch (ri->auth_type)
+    {
+      case RIP_AUTH_SIMPLE_PASSWORD:
+        rip_auth_prepare_str_send (ri, key, auth_str, len);
+        rip_auth_simple_write (s, auth_str, len);
+        return 0;
+      case RIP_AUTH_MD5:
+        return rip_auth_md5_ah_write (s, ri, key);
+    }
+  assert (1);
+}
+
+/* Write RIPv2 MD5 authentication data trailer */
+static void
+rip_auth_md5_set (struct stream *s, struct rip_interface *ri, size_t doff,
+                  char *auth_str, int authlen)
+{
+  unsigned long len;
+  struct md5_ctx ctx;
+  unsigned char digest[RIP_AUTH_MD5_SIZE];
+
+  /* Make it sure this interface is configured as MD5
+     authentication. */
+  assert ((ri->auth_type == RIP_AUTH_MD5) && (authlen == RIP_AUTH_MD5_SIZE));
+  assert (doff > 0);
+  
+  /* Get packet length. */
+  len = stream_get_endp(s);
+
+  /* Check packet length. */
+  if (len < (RIP_HEADER_SIZE + RIP_RTE_SIZE))
+    {
+      zlog_err ("rip_auth_md5_set(): packet length %ld is less than minimum length.", len);
+      return;
+    }
+
+  /* Set the digest offset length in the header */
+  stream_putw_at (s, doff, len);
+  
   /* Set authentication data. */
   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);
-  strncpy ((char *)secret, auth_str, RIP_AUTH_MD5_SIZE);
   md5_init_ctx (&ctx);
   md5_process_bytes (s->data, s->endp, &ctx);
-  md5_process_bytes (secret, RIP_AUTH_MD5_SIZE, &ctx);
+  md5_process_bytes (auth_str, RIP_AUTH_MD5_SIZE, &ctx);
   md5_finish_ctx (&ctx, digest);
 
   /* Copy the digest to the packet. */
@@ -2015,68 +2063,14 @@
   return sock;
 }
 
+
 /* Write routing table entry to the stream and return next index of
    the routing table entry in the stream. */
 int
 rip_write_rte (int num, struct stream *s, struct prefix_ipv4 *p,
-	       u_char version, struct rip_info *rinfo, struct interface *ifp)
+               u_char version, struct rip_info *rinfo)
 {
   struct in_addr mask;
-  struct rip_interface *ri;
-
-  /* RIP packet header. */
-  if (num == 0)
-    {
-      stream_putc (s, RIP_RESPONSE);
-      stream_putc (s, version);
-      stream_putw (s, 0);
-
-      /* In case of we need RIPv2 authentication. */
-      if (version == RIPv2 && ifp)
-	{
-	  ri = ifp->info;
-	      
-	  if (ri->auth_type == RIP_AUTH_SIMPLE_PASSWORD)
-	    {
-	      if (ri->auth_str)
-		{
-		  stream_putw (s, RIP_FAMILY_AUTH);
-		  stream_putw (s, RIP_AUTH_SIMPLE_PASSWORD);
-
-		  memset ((s->data + s->putp), 0, 16);
-		  strncpy ((char *)(s->data + s->putp), ri->auth_str, 16);
-		  stream_set_putp (s, s->putp + 16);
-
-		  num++;
-		}
-	      if (ri->key_chain)
-		{
-		  struct keychain *keychain;
-		  struct key *key;
-
-		  keychain = keychain_lookup (ri->key_chain);
-
-		  if (keychain)
-		    {
-		      key = key_lookup_for_send (keychain);
-		      
-		      if (key)
-			{
-			  stream_putw (s, RIP_FAMILY_AUTH);
-			  stream_putw (s, RIP_AUTH_SIMPLE_PASSWORD);
-
-			  memset ((s->data + s->putp), 0, 16);
-			  strncpy ((char *)(s->data + s->putp), 
-				   key->string, 16);
-			  stream_set_putp (s, s->putp + 16);
-
-			  num++;
-			}
-		    }
-		}
-	    }
-	}
-    }
 
   /* Write routing table entry. */
   if (version == RIPv1)
@@ -2116,6 +2110,11 @@
   struct prefix_ipv4 *p;
   struct prefix_ipv4 classfull;
   struct prefix_ipv4 ifaddrclass;
+  struct key *key = NULL;
+  /* this might need to made dynamic if RIP ever supported auth methods
+     with larger key string sizes */
+  char auth_str[RIP_AUTH_SIMPLE_SIZE];
+  size_t doff; /* offset of digest offset field */
   int num;
   int rtemax;
   int subnetted = 0;
@@ -2153,7 +2152,7 @@
 
   /* If output interface is in simple password authentication mode
      and string or keychain is specified we need space for auth. data */
-  if (ri->auth_type == RIP_AUTH_SIMPLE_PASSWORD)
+  if (ri->auth_type != RIP_NO_AUTH)
     {
       if (ri->key_chain)
        {
@@ -2161,12 +2160,10 @@
 
          keychain = keychain_lookup (ri->key_chain);
          if (keychain)
-           if (key_lookup_for_send (keychain))
-             rtemax -=1;
+           key = key_lookup_for_send (keychain);
        }
-      else
-       if (ri->auth_str)
-         rtemax -=1;
+      /* to be passed to auth functions later */
+      rip_auth_prepare_str_send (ri, key, auth_str, RIP_AUTH_SIMPLE_SIZE);
     }
 
   if (version == RIPv1)
@@ -2344,13 +2341,27 @@
               prefix_match((struct prefix *)p, ifc->address))
 	       rinfo->metric_out = RIP_METRIC_INFINITY;
 	}
- 
+	
+	/* Prepare preamble, auth headers, if needs be */
+	if (num == 0)
+	  {
+	    stream_putc (s, RIP_RESPONSE);
+	    stream_putc (s, version);
+	    stream_putw (s, 0);
+	    
+	    /* auth header for simple or v2 && MD5 */
+            if ( (ri->auth_type == RIP_AUTH_SIMPLE_PASSWORD)
+                || (version == RIPv2 && ri->auth_type == RIP_AUTH_MD5) )
+              doff = rip_auth_header_write (s, ri, key, auth_str, 
+                                              RIP_AUTH_SIMPLE_SIZE);
+          }
+        
 	/* Write RTE to the stream. */
-	num = rip_write_rte (num, s, p, version, rinfo, to ? NULL : ifc->ifp);
+	num = rip_write_rte (num, s, p, version, rinfo);
 	if (num == rtemax)
 	  {
 	    if (version == RIPv2 && ri->auth_type == RIP_AUTH_MD5)
-	      rip_auth_md5_set (s, ifc->ifp);
+              rip_auth_md5_set (s, ri, doff, auth_str, RIP_AUTH_SIMPLE_SIZE);
 
 	    ret = rip_send_packet (STREAM_DATA (s), stream_get_endp (s),
 				   to, ifc);
@@ -2367,7 +2378,7 @@
   if (num != 0)
     {
       if (version == RIPv2 && ri->auth_type == RIP_AUTH_MD5)
-	rip_auth_md5_set (s, ifc->ifp);
+        rip_auth_md5_set (s, ri, doff, auth_str, RIP_AUTH_SIMPLE_SIZE);
 
       ret = rip_send_packet (STREAM_DATA (s), stream_get_endp (s), to, ifc);
 
diff --git a/ripd/ripd.h b/ripd/ripd.h
index 0334888..7874871 100644
--- a/ripd/ripd.h
+++ b/ripd/ripd.h
@@ -87,6 +87,9 @@
 #define RIP_AUTH_SIMPLE_PASSWORD   2
 #define RIP_AUTH_MD5               3
 
+/* RIPv2 Simple authentication */
+#define RIP_AUTH_SIMPLE_SIZE		16
+
 /* RIPv2 MD5 authentication. */
 #define RIP_AUTH_MD5_SIZE               16
 #define RIP_AUTH_MD5_COMPAT_SIZE        RIP_RTE_SIZE