mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 04:50:35 +00:00 
			
		
		
		
	2008-08-14 Stephen Hemminger <stephen.hemminger@vyatta.com>
	* lib/log.{c,h}: struct message argument should point to const
	* */*.c: adjust to suit,
Signed-off-by: Paul Jakma <paul@quagga.net>
		
	
			
		
			
				
	
	
		
			4050 lines
		
	
	
		
			109 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			4050 lines
		
	
	
		
			109 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* RIP version 1 and 2.
 | 
						||
 * Copyright (C) 2005 6WIND <alain.ritoux@6wind.com>
 | 
						||
 * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro <kunihiro@zebra.org>
 | 
						||
 *
 | 
						||
 * This file is part of GNU Zebra.
 | 
						||
 *
 | 
						||
 * GNU Zebra is free software; you can redistribute it and/or modify it
 | 
						||
 * under the terms of the GNU General Public License as published by the
 | 
						||
 * Free Software Foundation; either version 2, or (at your option) any
 | 
						||
 * later version.
 | 
						||
 *
 | 
						||
 * GNU Zebra is distributed in the hope that it will be useful, but
 | 
						||
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						||
 * General Public License for more details.
 | 
						||
 *
 | 
						||
 * You should have received a copy of the GNU General Public License
 | 
						||
 * along with GNU Zebra; see the file COPYING.  If not, write to the Free
 | 
						||
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
						||
 * 02111-1307, USA.  
 | 
						||
 */
 | 
						||
 | 
						||
#include <zebra.h>
 | 
						||
 | 
						||
#include "if.h"
 | 
						||
#include "command.h"
 | 
						||
#include "prefix.h"
 | 
						||
#include "table.h"
 | 
						||
#include "thread.h"
 | 
						||
#include "memory.h"
 | 
						||
#include "log.h"
 | 
						||
#include "stream.h"
 | 
						||
#include "filter.h"
 | 
						||
#include "sockunion.h"
 | 
						||
#include "sockopt.h"
 | 
						||
#include "routemap.h"
 | 
						||
#include "if_rmap.h"
 | 
						||
#include "plist.h"
 | 
						||
#include "distribute.h"
 | 
						||
#include "md5.h"
 | 
						||
#include "keychain.h"
 | 
						||
#include "privs.h"
 | 
						||
 | 
						||
#include "ripd/ripd.h"
 | 
						||
#include "ripd/rip_debug.h"
 | 
						||
 | 
						||
/* UDP receive buffer size */
 | 
						||
#define RIP_UDP_RCV_BUF 41600
 | 
						||
 | 
						||
/* privileges global */
 | 
						||
extern struct zebra_privs_t ripd_privs;
 | 
						||
 | 
						||
/* RIP Structure. */
 | 
						||
struct rip *rip = NULL;
 | 
						||
 | 
						||
/* RIP neighbor address table. */
 | 
						||
struct route_table *rip_neighbor_table;
 | 
						||
 | 
						||
/* RIP route changes. */
 | 
						||
long rip_global_route_changes = 0;
 | 
						||
 | 
						||
/* RIP queries. */
 | 
						||
long rip_global_queries = 0;
 | 
						||
 | 
						||
/* Prototypes. */
 | 
						||
static void rip_event (enum rip_event, int);
 | 
						||
static void rip_output_process (struct connected *, struct sockaddr_in *, int, u_char);
 | 
						||
static int rip_triggered_update (struct thread *);
 | 
						||
static int rip_update_jitter (unsigned long);
 | 
						||
 | 
						||
/* RIP output routes type. */
 | 
						||
enum
 | 
						||
{
 | 
						||
  rip_all_route,
 | 
						||
  rip_changed_route
 | 
						||
};
 | 
						||
 | 
						||
/* RIP command strings. */
 | 
						||
static const struct message rip_msg[] =
 | 
						||
{
 | 
						||
  {RIP_REQUEST,    "REQUEST"},
 | 
						||
  {RIP_RESPONSE,   "RESPONSE"},
 | 
						||
  {RIP_TRACEON,    "TRACEON"},
 | 
						||
  {RIP_TRACEOFF,   "TRACEOFF"},
 | 
						||
  {RIP_POLL,       "POLL"},
 | 
						||
  {RIP_POLL_ENTRY, "POLL ENTRY"},
 | 
						||
  {0, NULL},
 | 
						||
};
 | 
						||
 | 
						||
/* Utility function to set boradcast option to the socket. */
 | 
						||
static int
 | 
						||
sockopt_broadcast (int sock)
 | 
						||
{
 | 
						||
  int ret;
 | 
						||
  int on = 1;
 | 
						||
 | 
						||
  ret = setsockopt (sock, SOL_SOCKET, SO_BROADCAST, (char *) &on, sizeof on);
 | 
						||
  if (ret < 0)
 | 
						||
    {
 | 
						||
      zlog_warn ("can't set sockopt SO_BROADCAST to socket %d", sock);
 | 
						||
      return -1;
 | 
						||
    }
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
static int
 | 
						||
rip_route_rte (struct rip_info *rinfo)
 | 
						||
{
 | 
						||
  return (rinfo->type == ZEBRA_ROUTE_RIP && rinfo->sub_type == RIP_ROUTE_RTE);
 | 
						||
}
 | 
						||
 | 
						||
static struct rip_info *
 | 
						||
rip_info_new ()
 | 
						||
{
 | 
						||
  struct rip_info *new;
 | 
						||
 | 
						||
  new = XMALLOC (MTYPE_RIP_INFO, sizeof (struct rip_info));
 | 
						||
  memset (new, 0, sizeof (struct rip_info));
 | 
						||
  return new;
 | 
						||
}
 | 
						||
 | 
						||
void
 | 
						||
rip_info_free (struct rip_info *rinfo)
 | 
						||
{
 | 
						||
  XFREE (MTYPE_RIP_INFO, rinfo);
 | 
						||
}
 | 
						||
 | 
						||
/* RIP route garbage collect timer. */
 | 
						||
static int
 | 
						||
rip_garbage_collect (struct thread *t)
 | 
						||
{
 | 
						||
  struct rip_info *rinfo;
 | 
						||
  struct route_node *rp;
 | 
						||
 | 
						||
  rinfo = THREAD_ARG (t);
 | 
						||
  rinfo->t_garbage_collect = NULL;
 | 
						||
 | 
						||
  /* Off timeout timer. */
 | 
						||
  RIP_TIMER_OFF (rinfo->t_timeout);
 | 
						||
  
 | 
						||
  /* Get route_node pointer. */
 | 
						||
  rp = rinfo->rp;
 | 
						||
 | 
						||
  /* Unlock route_node. */
 | 
						||
  rp->info = NULL;
 | 
						||
  route_unlock_node (rp);
 | 
						||
 | 
						||
  /* Free RIP routing information. */
 | 
						||
  rip_info_free (rinfo);
 | 
						||
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* Timeout RIP routes. */
 | 
						||
static int
 | 
						||
rip_timeout (struct thread *t)
 | 
						||
{
 | 
						||
  struct rip_info *rinfo;
 | 
						||
  struct route_node *rn;
 | 
						||
 | 
						||
  rinfo = THREAD_ARG (t);
 | 
						||
  rinfo->t_timeout = NULL;
 | 
						||
 | 
						||
  rn = rinfo->rp;
 | 
						||
 | 
						||
  /* - The garbage-collection timer is set for 120 seconds. */
 | 
						||
  RIP_TIMER_ON (rinfo->t_garbage_collect, rip_garbage_collect, 
 | 
						||
		rip->garbage_time);
 | 
						||
 | 
						||
  rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rn->p, &rinfo->nexthop,
 | 
						||
			 rinfo->metric);
 | 
						||
  /* - The metric for the route is set to 16 (infinity).  This causes
 | 
						||
     the route to be removed from service. */
 | 
						||
  rinfo->metric = RIP_METRIC_INFINITY;
 | 
						||
  rinfo->flags &= ~RIP_RTF_FIB;
 | 
						||
 | 
						||
  /* - The route change flag is to indicate that this entry has been
 | 
						||
     changed. */
 | 
						||
  rinfo->flags |= RIP_RTF_CHANGED;
 | 
						||
 | 
						||
  /* - The output process is signalled to trigger a response. */
 | 
						||
  rip_event (RIP_TRIGGERED_UPDATE, 0);
 | 
						||
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
rip_timeout_update (struct rip_info *rinfo)
 | 
						||
{
 | 
						||
  if (rinfo->metric != RIP_METRIC_INFINITY)
 | 
						||
    {
 | 
						||
      RIP_TIMER_OFF (rinfo->t_timeout);
 | 
						||
      RIP_TIMER_ON (rinfo->t_timeout, rip_timeout, rip->timeout_time);
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
static int
 | 
						||
rip_incoming_filter (struct prefix_ipv4 *p, struct rip_interface *ri)
 | 
						||
{
 | 
						||
  struct distribute *dist;
 | 
						||
  struct access_list *alist;
 | 
						||
  struct prefix_list *plist;
 | 
						||
 | 
						||
  /* Input distribute-list filtering. */
 | 
						||
  if (ri->list[RIP_FILTER_IN])
 | 
						||
    {
 | 
						||
      if (access_list_apply (ri->list[RIP_FILTER_IN], 
 | 
						||
			     (struct prefix *) p) == FILTER_DENY)
 | 
						||
	{
 | 
						||
	  if (IS_RIP_DEBUG_PACKET)
 | 
						||
	    zlog_debug ("%s/%d filtered by distribute in",
 | 
						||
		       inet_ntoa (p->prefix), p->prefixlen);
 | 
						||
	  return -1;
 | 
						||
	}
 | 
						||
    }
 | 
						||
  if (ri->prefix[RIP_FILTER_IN])
 | 
						||
    {
 | 
						||
      if (prefix_list_apply (ri->prefix[RIP_FILTER_IN], 
 | 
						||
			     (struct prefix *) p) == PREFIX_DENY)
 | 
						||
	{
 | 
						||
	  if (IS_RIP_DEBUG_PACKET)
 | 
						||
	    zlog_debug ("%s/%d filtered by prefix-list in",
 | 
						||
		       inet_ntoa (p->prefix), p->prefixlen);
 | 
						||
	  return -1;
 | 
						||
	}
 | 
						||
    }
 | 
						||
 | 
						||
  /* All interface filter check. */
 | 
						||
  dist = distribute_lookup (NULL);
 | 
						||
  if (dist)
 | 
						||
    {
 | 
						||
      if (dist->list[DISTRIBUTE_IN])
 | 
						||
	{
 | 
						||
	  alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_IN]);
 | 
						||
	    
 | 
						||
	  if (alist)
 | 
						||
	    {
 | 
						||
	      if (access_list_apply (alist,
 | 
						||
				     (struct prefix *) p) == FILTER_DENY)
 | 
						||
		{
 | 
						||
		  if (IS_RIP_DEBUG_PACKET)
 | 
						||
		    zlog_debug ("%s/%d filtered by distribute in",
 | 
						||
			       inet_ntoa (p->prefix), p->prefixlen);
 | 
						||
		  return -1;
 | 
						||
		}
 | 
						||
	    }
 | 
						||
	}
 | 
						||
      if (dist->prefix[DISTRIBUTE_IN])
 | 
						||
	{
 | 
						||
	  plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_IN]);
 | 
						||
	  
 | 
						||
	  if (plist)
 | 
						||
	    {
 | 
						||
	      if (prefix_list_apply (plist,
 | 
						||
				     (struct prefix *) p) == PREFIX_DENY)
 | 
						||
		{
 | 
						||
		  if (IS_RIP_DEBUG_PACKET)
 | 
						||
		    zlog_debug ("%s/%d filtered by prefix-list in",
 | 
						||
			       inet_ntoa (p->prefix), p->prefixlen);
 | 
						||
		  return -1;
 | 
						||
		}
 | 
						||
	    }
 | 
						||
	}
 | 
						||
    }
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
static int
 | 
						||
rip_outgoing_filter (struct prefix_ipv4 *p, struct rip_interface *ri)
 | 
						||
{
 | 
						||
  struct distribute *dist;
 | 
						||
  struct access_list *alist;
 | 
						||
  struct prefix_list *plist;
 | 
						||
 | 
						||
  if (ri->list[RIP_FILTER_OUT])
 | 
						||
    {
 | 
						||
      if (access_list_apply (ri->list[RIP_FILTER_OUT],
 | 
						||
			     (struct prefix *) p) == FILTER_DENY)
 | 
						||
	{
 | 
						||
	  if (IS_RIP_DEBUG_PACKET)
 | 
						||
	    zlog_debug ("%s/%d is filtered by distribute out",
 | 
						||
		       inet_ntoa (p->prefix), p->prefixlen);
 | 
						||
	  return -1;
 | 
						||
	}
 | 
						||
    }
 | 
						||
  if (ri->prefix[RIP_FILTER_OUT])
 | 
						||
    {
 | 
						||
      if (prefix_list_apply (ri->prefix[RIP_FILTER_OUT],
 | 
						||
			     (struct prefix *) p) == PREFIX_DENY)
 | 
						||
	{
 | 
						||
	  if (IS_RIP_DEBUG_PACKET)
 | 
						||
	    zlog_debug ("%s/%d is filtered by prefix-list out",
 | 
						||
		       inet_ntoa (p->prefix), p->prefixlen);
 | 
						||
	  return -1;
 | 
						||
	}
 | 
						||
    }
 | 
						||
 | 
						||
  /* All interface filter check. */
 | 
						||
  dist = distribute_lookup (NULL);
 | 
						||
  if (dist)
 | 
						||
    {
 | 
						||
      if (dist->list[DISTRIBUTE_OUT])
 | 
						||
	{
 | 
						||
	  alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_OUT]);
 | 
						||
	    
 | 
						||
	  if (alist)
 | 
						||
	    {
 | 
						||
	      if (access_list_apply (alist,
 | 
						||
				     (struct prefix *) p) == FILTER_DENY)
 | 
						||
		{
 | 
						||
		  if (IS_RIP_DEBUG_PACKET)
 | 
						||
		    zlog_debug ("%s/%d filtered by distribute out",
 | 
						||
			       inet_ntoa (p->prefix), p->prefixlen);
 | 
						||
		  return -1;
 | 
						||
		}
 | 
						||
	    }
 | 
						||
	}
 | 
						||
      if (dist->prefix[DISTRIBUTE_OUT])
 | 
						||
	{
 | 
						||
	  plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_OUT]);
 | 
						||
	  
 | 
						||
	  if (plist)
 | 
						||
	    {
 | 
						||
	      if (prefix_list_apply (plist,
 | 
						||
				     (struct prefix *) p) == PREFIX_DENY)
 | 
						||
		{
 | 
						||
		  if (IS_RIP_DEBUG_PACKET)
 | 
						||
		    zlog_debug ("%s/%d filtered by prefix-list out",
 | 
						||
			       inet_ntoa (p->prefix), p->prefixlen);
 | 
						||
		  return -1;
 | 
						||
		}
 | 
						||
	    }
 | 
						||
	}
 | 
						||
    }
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* Check nexthop address validity. */
 | 
						||
static int
 | 
						||
rip_nexthop_check (struct in_addr *addr)
 | 
						||
{
 | 
						||
  struct listnode *node;
 | 
						||
  struct listnode *cnode;
 | 
						||
  struct interface *ifp;
 | 
						||
  struct connected *ifc;
 | 
						||
  struct prefix *p;
 | 
						||
 | 
						||
  /* If nexthop address matches local configured address then it is
 | 
						||
     invalid nexthop. */
 | 
						||
  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
 | 
						||
    {
 | 
						||
      for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, ifc))
 | 
						||
	{	    
 | 
						||
	  p = ifc->address;
 | 
						||
 | 
						||
	  if (p->family == AF_INET
 | 
						||
	      && IPV4_ADDR_SAME (&p->u.prefix4, addr))
 | 
						||
	    return -1;
 | 
						||
	}
 | 
						||
    }
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* RIP add route to routing table. */
 | 
						||
static void
 | 
						||
rip_rte_process (struct rte *rte, struct sockaddr_in *from,
 | 
						||
                 struct interface *ifp)
 | 
						||
{
 | 
						||
  int ret;
 | 
						||
  struct prefix_ipv4 p;
 | 
						||
  struct route_node *rp;
 | 
						||
  struct rip_info *rinfo, rinfotmp;
 | 
						||
  struct rip_interface *ri;
 | 
						||
  struct in_addr *nexthop;
 | 
						||
  u_char oldmetric;
 | 
						||
  int same = 0;
 | 
						||
  int route_reuse = 0;
 | 
						||
  unsigned char old_dist, new_dist;
 | 
						||
 | 
						||
  /* Make prefix structure. */
 | 
						||
  memset (&p, 0, sizeof (struct prefix_ipv4));
 | 
						||
  p.family = AF_INET;
 | 
						||
  p.prefix = rte->prefix;
 | 
						||
  p.prefixlen = ip_masklen (rte->mask);
 | 
						||
 | 
						||
  /* Make sure mask is applied. */
 | 
						||
  apply_mask_ipv4 (&p);
 | 
						||
 | 
						||
  /* Apply input filters. */
 | 
						||
  ri = ifp->info;
 | 
						||
 | 
						||
  ret = rip_incoming_filter (&p, ri);
 | 
						||
  if (ret < 0)
 | 
						||
    return;
 | 
						||
 | 
						||
  /* Modify entry according to the interface routemap. */
 | 
						||
  if (ri->routemap[RIP_FILTER_IN])
 | 
						||
    {
 | 
						||
      int ret;
 | 
						||
      struct rip_info newinfo;
 | 
						||
 | 
						||
      memset (&newinfo, 0, sizeof (newinfo));
 | 
						||
      newinfo.type = ZEBRA_ROUTE_RIP;
 | 
						||
      newinfo.sub_type = RIP_ROUTE_RTE;
 | 
						||
      newinfo.nexthop = rte->nexthop;
 | 
						||
      newinfo.from = from->sin_addr;
 | 
						||
      newinfo.ifindex = ifp->ifindex;
 | 
						||
      newinfo.metric = rte->metric;
 | 
						||
      newinfo.metric_out = rte->metric; /* XXX */
 | 
						||
      newinfo.tag = ntohs (rte->tag);   /* XXX */
 | 
						||
 | 
						||
      /* The object should be of the type of rip_info */
 | 
						||
      ret = route_map_apply (ri->routemap[RIP_FILTER_IN],
 | 
						||
                             (struct prefix *) &p, RMAP_RIP, &newinfo);
 | 
						||
 | 
						||
      if (ret == RMAP_DENYMATCH)
 | 
						||
        {
 | 
						||
          if (IS_RIP_DEBUG_PACKET)
 | 
						||
            zlog_debug ("RIP %s/%d is filtered by route-map in",
 | 
						||
                       inet_ntoa (p.prefix), p.prefixlen);
 | 
						||
          return;
 | 
						||
        }
 | 
						||
 | 
						||
      /* Get back the object */
 | 
						||
      rte->nexthop = newinfo.nexthop_out;
 | 
						||
      rte->tag = htons (newinfo.tag_out);       /* XXX */
 | 
						||
      rte->metric = newinfo.metric_out; /* XXX: the routemap uses the metric_out field */
 | 
						||
    }
 | 
						||
 | 
						||
  /* Once the entry has been validated, update the metric by
 | 
						||
     adding the cost of the network on wich the message
 | 
						||
     arrived. If the result is greater than infinity, use infinity
 | 
						||
     (RFC2453 Sec. 3.9.2) */
 | 
						||
  /* Zebra ripd can handle offset-list in. */
 | 
						||
  ret = rip_offset_list_apply_in (&p, ifp, &rte->metric);
 | 
						||
 | 
						||
  /* If offset-list does not modify the metric use interface's
 | 
						||
     metric. */
 | 
						||
  if (!ret)
 | 
						||
    rte->metric += ifp->metric;
 | 
						||
 | 
						||
  if (rte->metric > RIP_METRIC_INFINITY)
 | 
						||
    rte->metric = RIP_METRIC_INFINITY;
 | 
						||
 | 
						||
  /* Set nexthop pointer. */
 | 
						||
  if (rte->nexthop.s_addr == 0)
 | 
						||
    nexthop = &from->sin_addr;
 | 
						||
  else
 | 
						||
    nexthop = &rte->nexthop;
 | 
						||
 | 
						||
  /* Check if nexthop address is myself, then do nothing. */
 | 
						||
  if (rip_nexthop_check (nexthop) < 0)
 | 
						||
    {
 | 
						||
      if (IS_RIP_DEBUG_PACKET)
 | 
						||
        zlog_debug ("Nexthop address %s is myself", inet_ntoa (*nexthop));
 | 
						||
      return;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Get index for the prefix. */
 | 
						||
  rp = route_node_get (rip->table, (struct prefix *) &p);
 | 
						||
 | 
						||
  /* Check to see whether there is already RIP route on the table. */
 | 
						||
  rinfo = rp->info;
 | 
						||
 | 
						||
  if (rinfo)
 | 
						||
    {
 | 
						||
      /* Local static route. */
 | 
						||
      if (rinfo->type == ZEBRA_ROUTE_RIP
 | 
						||
          && ((rinfo->sub_type == RIP_ROUTE_STATIC) ||
 | 
						||
              (rinfo->sub_type == RIP_ROUTE_DEFAULT))
 | 
						||
          && rinfo->metric != RIP_METRIC_INFINITY)
 | 
						||
        {
 | 
						||
          route_unlock_node (rp);
 | 
						||
          return;
 | 
						||
        }
 | 
						||
 | 
						||
      /* Redistributed route check. */
 | 
						||
      if (rinfo->type != ZEBRA_ROUTE_RIP
 | 
						||
          && rinfo->metric != RIP_METRIC_INFINITY)
 | 
						||
        {
 | 
						||
          /* Fill in a minimaly temporary rip_info structure, for a future
 | 
						||
             rip_distance_apply() use) */
 | 
						||
          memset (&rinfotmp, 0, sizeof (rinfotmp));
 | 
						||
          IPV4_ADDR_COPY (&rinfotmp.from, &from->sin_addr);
 | 
						||
          rinfotmp.rp = rinfo->rp;
 | 
						||
          new_dist = rip_distance_apply (&rinfotmp);
 | 
						||
          new_dist = new_dist ? new_dist : ZEBRA_RIP_DISTANCE_DEFAULT;
 | 
						||
          old_dist = rinfo->distance;
 | 
						||
          /* Only connected routes may have a valid NULL distance */
 | 
						||
          if (rinfo->type != ZEBRA_ROUTE_CONNECT)
 | 
						||
            old_dist = old_dist ? old_dist : ZEBRA_RIP_DISTANCE_DEFAULT;
 | 
						||
          /* If imported route does not have STRICT precedence, 
 | 
						||
             mark it as a ghost */
 | 
						||
          if (new_dist > old_dist 
 | 
						||
              || rte->metric == RIP_METRIC_INFINITY)
 | 
						||
            {
 | 
						||
              route_unlock_node (rp);
 | 
						||
              return;
 | 
						||
            }
 | 
						||
          else
 | 
						||
            {
 | 
						||
              RIP_TIMER_OFF (rinfo->t_timeout);
 | 
						||
              RIP_TIMER_OFF (rinfo->t_garbage_collect);
 | 
						||
                                                                                
 | 
						||
              rp->info = NULL;
 | 
						||
              if (rip_route_rte (rinfo))
 | 
						||
                rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rp->p, 
 | 
						||
                                        &rinfo->nexthop, rinfo->metric);
 | 
						||
              rip_info_free (rinfo);
 | 
						||
              rinfo = NULL;
 | 
						||
              route_reuse = 1;
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
  if (!rinfo)
 | 
						||
    {
 | 
						||
      /* Now, check to see whether there is already an explicit route
 | 
						||
         for the destination prefix.  If there is no such route, add
 | 
						||
         this route to the routing table, unless the metric is
 | 
						||
         infinity (there is no point in adding a route which
 | 
						||
         unusable). */
 | 
						||
      if (rte->metric != RIP_METRIC_INFINITY)
 | 
						||
        {
 | 
						||
          rinfo = rip_info_new ();
 | 
						||
 | 
						||
          /* - Setting the destination prefix and length to those in
 | 
						||
             the RTE. */
 | 
						||
          rinfo->rp = rp;
 | 
						||
 | 
						||
          /* - Setting the metric to the newly calculated metric (as
 | 
						||
             described above). */
 | 
						||
          rinfo->metric = rte->metric;
 | 
						||
          rinfo->tag = ntohs (rte->tag);
 | 
						||
 | 
						||
          /* - Set the next hop address to be the address of the router
 | 
						||
             from which the datagram came or the next hop address
 | 
						||
             specified by a next hop RTE. */
 | 
						||
          IPV4_ADDR_COPY (&rinfo->nexthop, nexthop);
 | 
						||
          IPV4_ADDR_COPY (&rinfo->from, &from->sin_addr);
 | 
						||
          rinfo->ifindex = ifp->ifindex;
 | 
						||
 | 
						||
          /* - Initialize the timeout for the route.  If the
 | 
						||
             garbage-collection timer is running for this route, stop it
 | 
						||
             (see section 2.3 for a discussion of the timers). */
 | 
						||
          rip_timeout_update (rinfo);
 | 
						||
 | 
						||
          /* - Set the route change flag. */
 | 
						||
          rinfo->flags |= RIP_RTF_CHANGED;
 | 
						||
 | 
						||
          /* - Signal the output process to trigger an update (see section
 | 
						||
             2.5). */
 | 
						||
          rip_event (RIP_TRIGGERED_UPDATE, 0);
 | 
						||
 | 
						||
          /* Finally, route goes into the kernel. */
 | 
						||
          rinfo->type = ZEBRA_ROUTE_RIP;
 | 
						||
          rinfo->sub_type = RIP_ROUTE_RTE;
 | 
						||
 | 
						||
          /* Set distance value. */
 | 
						||
          rinfo->distance = rip_distance_apply (rinfo);
 | 
						||
 | 
						||
          rp->info = rinfo;
 | 
						||
          rip_zebra_ipv4_add (&p, &rinfo->nexthop, rinfo->metric,
 | 
						||
                              rinfo->distance);
 | 
						||
          rinfo->flags |= RIP_RTF_FIB;
 | 
						||
        }
 | 
						||
 | 
						||
      /* Unlock temporary lock, i.e. same behaviour */
 | 
						||
      if (route_reuse)
 | 
						||
        route_unlock_node (rp);
 | 
						||
    }
 | 
						||
  else
 | 
						||
    {
 | 
						||
      /* Route is there but we are not sure the route is RIP or not. */
 | 
						||
      rinfo = rp->info;
 | 
						||
 | 
						||
      /* If there is an existing route, compare the next hop address
 | 
						||
         to the address of the router from which the datagram came.
 | 
						||
         If this datagram is from the same router as the existing
 | 
						||
         route, reinitialize the timeout.  */
 | 
						||
      same = (IPV4_ADDR_SAME (&rinfo->from, &from->sin_addr)
 | 
						||
              && (rinfo->ifindex == ifp->ifindex));
 | 
						||
 | 
						||
      if (same)
 | 
						||
        rip_timeout_update (rinfo);
 | 
						||
 | 
						||
 | 
						||
      /* Fill in a minimaly temporary rip_info structure, for a future
 | 
						||
         rip_distance_apply() use) */
 | 
						||
      memset (&rinfotmp, 0, sizeof (rinfotmp));
 | 
						||
      IPV4_ADDR_COPY (&rinfotmp.from, &from->sin_addr);
 | 
						||
      rinfotmp.rp = rinfo->rp;
 | 
						||
 | 
						||
 | 
						||
      /* Next, compare the metrics.  If the datagram is from the same
 | 
						||
         router as the existing route, and the new metric is different
 | 
						||
         than the old one; or, if the new metric is lower than the old
 | 
						||
         one, or if the tag has been changed; or if there is a route
 | 
						||
         with a lower administrave distance; or an update of the
 | 
						||
         distance on the actual route; do the following actions: */
 | 
						||
      if ((same && rinfo->metric != rte->metric)
 | 
						||
          || (rte->metric < rinfo->metric)
 | 
						||
          || ((same)
 | 
						||
              && (rinfo->metric == rte->metric)
 | 
						||
              && ntohs (rte->tag) != rinfo->tag)
 | 
						||
          || (rinfo->distance > rip_distance_apply (&rinfotmp))
 | 
						||
          || ((rinfo->distance != rip_distance_apply (rinfo)) && same))
 | 
						||
        {
 | 
						||
          /* - Adopt the route from the datagram.  That is, put the
 | 
						||
             new metric in, and adjust the next hop address (if
 | 
						||
             necessary). */
 | 
						||
          oldmetric = rinfo->metric;
 | 
						||
          rinfo->metric = rte->metric;
 | 
						||
          rinfo->tag = ntohs (rte->tag);
 | 
						||
          IPV4_ADDR_COPY (&rinfo->from, &from->sin_addr);
 | 
						||
          rinfo->ifindex = ifp->ifindex;
 | 
						||
          rinfo->distance = rip_distance_apply (rinfo);
 | 
						||
 | 
						||
          /* Should a new route to this network be established
 | 
						||
             while the garbage-collection timer is running, the
 | 
						||
             new route will replace the one that is about to be
 | 
						||
             deleted.  In this case the garbage-collection timer
 | 
						||
             must be cleared. */
 | 
						||
 | 
						||
          if (oldmetric == RIP_METRIC_INFINITY &&
 | 
						||
              rinfo->metric < RIP_METRIC_INFINITY)
 | 
						||
            {
 | 
						||
              rinfo->type = ZEBRA_ROUTE_RIP;
 | 
						||
              rinfo->sub_type = RIP_ROUTE_RTE;
 | 
						||
 | 
						||
              RIP_TIMER_OFF (rinfo->t_garbage_collect);
 | 
						||
 | 
						||
              if (!IPV4_ADDR_SAME (&rinfo->nexthop, nexthop))
 | 
						||
                IPV4_ADDR_COPY (&rinfo->nexthop, nexthop);
 | 
						||
 | 
						||
              rip_zebra_ipv4_add (&p, nexthop, rinfo->metric,
 | 
						||
                                  rinfo->distance);
 | 
						||
              rinfo->flags |= RIP_RTF_FIB;
 | 
						||
            }
 | 
						||
 | 
						||
          /* Update nexthop and/or metric value.  */
 | 
						||
          if (oldmetric != RIP_METRIC_INFINITY)
 | 
						||
            {
 | 
						||
              rip_zebra_ipv4_delete (&p, &rinfo->nexthop, oldmetric);
 | 
						||
              rip_zebra_ipv4_add (&p, nexthop, rinfo->metric,
 | 
						||
                                  rinfo->distance);
 | 
						||
              rinfo->flags |= RIP_RTF_FIB;
 | 
						||
 | 
						||
              if (!IPV4_ADDR_SAME (&rinfo->nexthop, nexthop))
 | 
						||
                IPV4_ADDR_COPY (&rinfo->nexthop, nexthop);
 | 
						||
            }
 | 
						||
 | 
						||
          /* - Set the route change flag and signal the output process
 | 
						||
             to trigger an update. */
 | 
						||
          rinfo->flags |= RIP_RTF_CHANGED;
 | 
						||
          rip_event (RIP_TRIGGERED_UPDATE, 0);
 | 
						||
 | 
						||
          /* - If the new metric is infinity, start the deletion
 | 
						||
             process (described above); */
 | 
						||
          if (rinfo->metric == RIP_METRIC_INFINITY)
 | 
						||
            {
 | 
						||
              /* If the new metric is infinity, the deletion process
 | 
						||
                 begins for the route, which is no longer used for
 | 
						||
                 routing packets.  Note that the deletion process is
 | 
						||
                 started only when the metric is first set to
 | 
						||
                 infinity.  If the metric was already infinity, then a
 | 
						||
                 new deletion process is not started. */
 | 
						||
              if (oldmetric != RIP_METRIC_INFINITY)
 | 
						||
                {
 | 
						||
                  /* - The garbage-collection timer is set for 120 seconds. */
 | 
						||
                  RIP_TIMER_ON (rinfo->t_garbage_collect,
 | 
						||
                                rip_garbage_collect, rip->garbage_time);
 | 
						||
                  RIP_TIMER_OFF (rinfo->t_timeout);
 | 
						||
 | 
						||
                  /* - The metric for the route is set to 16
 | 
						||
                     (infinity).  This causes the route to be removed
 | 
						||
                     from service. */
 | 
						||
                  rip_zebra_ipv4_delete (&p, &rinfo->nexthop, oldmetric);
 | 
						||
                  rinfo->flags &= ~RIP_RTF_FIB;
 | 
						||
 | 
						||
                  /* - The route change flag is to indicate that this
 | 
						||
                     entry has been changed. */
 | 
						||
                  /* - The output process is signalled to trigger a
 | 
						||
                     response. */
 | 
						||
                  ;             /* Above processes are already done previously. */
 | 
						||
                }
 | 
						||
            }
 | 
						||
          else
 | 
						||
            {
 | 
						||
              /* otherwise, re-initialize the timeout. */
 | 
						||
              rip_timeout_update (rinfo);
 | 
						||
            }
 | 
						||
        }
 | 
						||
      /* Unlock tempolary lock of the route. */
 | 
						||
      route_unlock_node (rp);
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/* Dump RIP packet */
 | 
						||
static void
 | 
						||
rip_packet_dump (struct rip_packet *packet, int size, const char *sndrcv)
 | 
						||
{
 | 
						||
  caddr_t lim;
 | 
						||
  struct rte *rte;
 | 
						||
  const char *command_str;
 | 
						||
  char pbuf[BUFSIZ], nbuf[BUFSIZ];
 | 
						||
  u_char netmask = 0;
 | 
						||
  u_char *p;
 | 
						||
 | 
						||
  /* Set command string. */
 | 
						||
  if (packet->command > 0 && packet->command < RIP_COMMAND_MAX)
 | 
						||
    command_str = lookup (rip_msg, packet->command);
 | 
						||
  else
 | 
						||
    command_str = "unknown";
 | 
						||
 | 
						||
  /* Dump packet header. */
 | 
						||
  zlog_debug ("%s %s version %d packet size %d",
 | 
						||
	     sndrcv, command_str, packet->version, size);
 | 
						||
 | 
						||
  /* Dump each routing table entry. */
 | 
						||
  rte = packet->rte;
 | 
						||
  
 | 
						||
  for (lim = (caddr_t) packet + size; (caddr_t) rte < lim; rte++)
 | 
						||
    {
 | 
						||
      if (packet->version == RIPv2)
 | 
						||
	{
 | 
						||
	  netmask = ip_masklen (rte->mask);
 | 
						||
 | 
						||
          if (rte->family == htons (RIP_FAMILY_AUTH))
 | 
						||
            {
 | 
						||
              if (rte->tag == htons (RIP_AUTH_SIMPLE_PASSWORD))
 | 
						||
		{
 | 
						||
		  p = (u_char *)&rte->prefix;
 | 
						||
 | 
						||
		  zlog_debug ("  family 0x%X type %d auth string: %s",
 | 
						||
			     ntohs (rte->family), ntohs (rte->tag), p);
 | 
						||
		}
 | 
						||
              else if (rte->tag == htons (RIP_AUTH_MD5))
 | 
						||
		{
 | 
						||
		  struct rip_md5_info *md5;
 | 
						||
 | 
						||
		  md5 = (struct rip_md5_info *) &packet->rte;
 | 
						||
 | 
						||
		  zlog_debug ("  family 0x%X type %d (MD5 authentication)",
 | 
						||
			     ntohs (md5->family), ntohs (md5->type));
 | 
						||
		  zlog_debug ("    RIP-2 packet len %d Key ID %d"
 | 
						||
                             " Auth Data len %d",
 | 
						||
                             ntohs (md5->packet_len), md5->keyid,
 | 
						||
                             md5->auth_len);
 | 
						||
                  zlog_debug ("    Sequence Number %ld",
 | 
						||
                             (u_long) ntohl (md5->sequence));
 | 
						||
		}
 | 
						||
              else if (rte->tag == htons (RIP_AUTH_DATA))
 | 
						||
		{
 | 
						||
		  p = (u_char *)&rte->prefix;
 | 
						||
 | 
						||
		  zlog_debug ("  family 0x%X type %d (MD5 data)",
 | 
						||
			     ntohs (rte->family), ntohs (rte->tag));
 | 
						||
		  zlog_debug ("    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]);
 | 
						||
		}
 | 
						||
	      else
 | 
						||
		{
 | 
						||
		  zlog_debug ("  family 0x%X type %d (Unknown auth type)",
 | 
						||
			     ntohs (rte->family), ntohs (rte->tag));
 | 
						||
		}
 | 
						||
            }
 | 
						||
	  else
 | 
						||
	    zlog_debug ("  %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));
 | 
						||
	}
 | 
						||
      else
 | 
						||
	{
 | 
						||
	  zlog_debug ("  %s family %d tag %d metric %ld", 
 | 
						||
		     inet_ntop (AF_INET, &rte->prefix, pbuf, BUFSIZ),
 | 
						||
		     ntohs (rte->family), ntohs (rte->tag),
 | 
						||
		     (u_long)ntohl (rte->metric));
 | 
						||
	}
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/* Check if the destination address is valid (unicast; not net 0
 | 
						||
   or 127) (RFC2453 Section 3.9.2 - Page 26).  But we don't
 | 
						||
   check net 0 because we accept default route. */
 | 
						||
static int
 | 
						||
rip_destination_check (struct in_addr addr)
 | 
						||
{
 | 
						||
  u_int32_t destination;
 | 
						||
 | 
						||
  /* Convert to host byte order. */
 | 
						||
  destination = ntohl (addr.s_addr);
 | 
						||
 | 
						||
  if (IPV4_NET127 (destination))
 | 
						||
    return 0;
 | 
						||
 | 
						||
  /* Net 0 may match to the default route. */
 | 
						||
  if (IPV4_NET0 (destination) && destination != 0)
 | 
						||
    return 0;
 | 
						||
 | 
						||
  /* Unicast address must belong to class A, B, C. */
 | 
						||
  if (IN_CLASSA (destination))
 | 
						||
    return 1;
 | 
						||
  if (IN_CLASSB (destination))
 | 
						||
    return 1;
 | 
						||
  if (IN_CLASSC (destination))
 | 
						||
    return 1;
 | 
						||
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* RIP version 2 authentication. */
 | 
						||
static int
 | 
						||
rip_auth_simple_password (struct rte *rte, struct sockaddr_in *from,
 | 
						||
			  struct interface *ifp)
 | 
						||
{
 | 
						||
  struct rip_interface *ri;
 | 
						||
  char *auth_str;
 | 
						||
 | 
						||
  if (IS_RIP_DEBUG_EVENT)
 | 
						||
    zlog_debug ("RIPv2 simple password authentication from %s",
 | 
						||
	       inet_ntoa (from->sin_addr));
 | 
						||
 | 
						||
  ri = ifp->info;
 | 
						||
 | 
						||
  if (ri->auth_type != RIP_AUTH_SIMPLE_PASSWORD
 | 
						||
      || rte->tag != htons(RIP_AUTH_SIMPLE_PASSWORD))
 | 
						||
    return 0;
 | 
						||
 | 
						||
  /* Simple password authentication. */
 | 
						||
  if (ri->auth_str)
 | 
						||
    {
 | 
						||
      auth_str = (char *) &rte->prefix;
 | 
						||
	  
 | 
						||
      if (strncmp (auth_str, ri->auth_str, 16) == 0)
 | 
						||
	return 1;
 | 
						||
    }
 | 
						||
  if (ri->key_chain)
 | 
						||
    {
 | 
						||
      struct keychain *keychain;
 | 
						||
      struct key *key;
 | 
						||
 | 
						||
      keychain = keychain_lookup (ri->key_chain);
 | 
						||
      if (keychain == NULL)
 | 
						||
	return 0;
 | 
						||
 | 
						||
      key = key_match_for_accept (keychain, (char *) &rte->prefix);
 | 
						||
      if (key)
 | 
						||
	return 1;
 | 
						||
    }
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* RIP version 2 authentication with MD5. */
 | 
						||
static int
 | 
						||
rip_auth_md5 (struct rip_packet *packet, struct sockaddr_in *from,
 | 
						||
              int length, struct interface *ifp)
 | 
						||
{
 | 
						||
  struct rip_interface *ri;
 | 
						||
  struct rip_md5_info *md5;
 | 
						||
  struct rip_md5_data *md5data;
 | 
						||
  struct keychain *keychain;
 | 
						||
  struct key *key;
 | 
						||
  MD5_CTX ctx;
 | 
						||
  u_char digest[RIP_AUTH_MD5_SIZE];
 | 
						||
  u_int16_t packet_len;
 | 
						||
  char auth_str[RIP_AUTH_MD5_SIZE];
 | 
						||
  
 | 
						||
  if (IS_RIP_DEBUG_EVENT)
 | 
						||
    zlog_debug ("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 || 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. 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)
 | 
						||
         || (md5->auth_len == RIP_AUTH_MD5_COMPAT_SIZE)))
 | 
						||
    {
 | 
						||
      if (IS_RIP_DEBUG_EVENT)
 | 
						||
        zlog_debug ("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_debug ("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);
 | 
						||
  
 | 
						||
  memset (auth_str, 0, RIP_AUTH_MD5_SIZE);
 | 
						||
 | 
						||
  if (ri->key_chain)
 | 
						||
    {
 | 
						||
      keychain = keychain_lookup (ri->key_chain);
 | 
						||
      if (keychain == NULL)
 | 
						||
	return 0;
 | 
						||
 | 
						||
      key = key_lookup_for_accept (keychain, md5->keyid);
 | 
						||
      if (key == NULL)
 | 
						||
	return 0;
 | 
						||
 | 
						||
      strncpy (auth_str, key->string, RIP_AUTH_MD5_SIZE);
 | 
						||
    }
 | 
						||
  else if (ri->auth_str)
 | 
						||
    strncpy (auth_str, ri->auth_str, RIP_AUTH_MD5_SIZE);
 | 
						||
 | 
						||
  if (auth_str[0] == 0)
 | 
						||
    return 0;
 | 
						||
  
 | 
						||
  /* MD5 digest authentication. */
 | 
						||
  memset (&ctx, 0, sizeof(ctx));
 | 
						||
  MD5Init(&ctx);
 | 
						||
  MD5Update(&ctx, packet, packet_len + RIP_HEADER_SIZE);
 | 
						||
  MD5Update(&ctx, auth_str, RIP_AUTH_MD5_SIZE);
 | 
						||
  MD5Final(digest, &ctx);
 | 
						||
  
 | 
						||
  if (memcmp (md5data->digest, digest, RIP_AUTH_MD5_SIZE) == 0)
 | 
						||
    return packet_len;
 | 
						||
  else
 | 
						||
    return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* 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)
 | 
						||
{
 | 
						||
  assert (ri || key);
 | 
						||
 | 
						||
  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);
 | 
						||
 | 
						||
  return;
 | 
						||
}
 | 
						||
 | 
						||
/* 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);
 | 
						||
  
 | 
						||
  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 doff = 0;
 | 
						||
 | 
						||
  assert (s && ri && ri->auth_type == RIP_AUTH_MD5);
 | 
						||
 | 
						||
  /* MD5 authentication. */
 | 
						||
  stream_putw (s, RIP_FAMILY_AUTH);
 | 
						||
  stream_putw (s, RIP_AUTH_MD5);
 | 
						||
 | 
						||
  /* 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 .....().
 | 
						||
   */
 | 
						||
  doff = stream_get_endp(s);
 | 
						||
  stream_putw (s, 0);
 | 
						||
 | 
						||
  /* Key ID. */
 | 
						||
  if (key)
 | 
						||
    stream_putc (s, key->index % 256);
 | 
						||
  else
 | 
						||
    stream_putc (s, 1);
 | 
						||
 | 
						||
  /* 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
 | 
						||
     arbitrary, but two suggestions are the time of the
 | 
						||
     message's creation or a simple message counter. */
 | 
						||
  stream_putl (s, time (NULL));
 | 
						||
	      
 | 
						||
  /* Reserved field must be zero. */
 | 
						||
  stream_putl (s, 0);
 | 
						||
  stream_putl (s, 0);
 | 
						||
 | 
						||
  return doff;
 | 
						||
}
 | 
						||
 | 
						||
/* 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);
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* 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;
 | 
						||
  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(&ctx, 0, sizeof(ctx));
 | 
						||
  MD5Init(&ctx);
 | 
						||
  MD5Update(&ctx, STREAM_DATA (s), stream_get_endp (s));
 | 
						||
  MD5Update(&ctx, auth_str, RIP_AUTH_MD5_SIZE);
 | 
						||
  MD5Final(digest, &ctx);
 | 
						||
 | 
						||
  /* Copy the digest to the packet. */
 | 
						||
  stream_write (s, digest, RIP_AUTH_MD5_SIZE);
 | 
						||
}
 | 
						||
 | 
						||
/* RIP routing information. */
 | 
						||
static void
 | 
						||
rip_response_process (struct rip_packet *packet, int size, 
 | 
						||
		      struct sockaddr_in *from, struct connected *ifc)
 | 
						||
{
 | 
						||
  caddr_t lim;
 | 
						||
  struct rte *rte;
 | 
						||
  struct prefix_ipv4 ifaddr;
 | 
						||
  struct prefix_ipv4 ifaddrclass;
 | 
						||
  int subnetted;
 | 
						||
      
 | 
						||
  /* We don't know yet. */
 | 
						||
  subnetted = -1;
 | 
						||
 | 
						||
  /* The Response must be ignored if it is not from the RIP
 | 
						||
     port. (RFC2453 - Sec. 3.9.2)*/
 | 
						||
  if (from->sin_port != htons(RIP_PORT_DEFAULT))
 | 
						||
    {
 | 
						||
      zlog_info ("response doesn't come from RIP port: %d",
 | 
						||
		 from->sin_port);
 | 
						||
      rip_peer_bad_packet (from);
 | 
						||
      return;
 | 
						||
    }
 | 
						||
 | 
						||
  /* The datagram's IPv4 source address should be checked to see
 | 
						||
     whether the datagram is from a valid neighbor; the source of the
 | 
						||
     datagram must be on a directly connected network (RFC2453 - Sec. 3.9.2) */
 | 
						||
  if (if_lookup_address(from->sin_addr) == NULL) 
 | 
						||
    {
 | 
						||
      zlog_info ("This datagram doesn't came from a valid neighbor: %s",
 | 
						||
		 inet_ntoa (from->sin_addr));
 | 
						||
      rip_peer_bad_packet (from);
 | 
						||
      return;
 | 
						||
    }
 | 
						||
 | 
						||
  /* It is also worth checking to see whether the response is from one
 | 
						||
     of the router's own addresses. */
 | 
						||
 | 
						||
  ; /* Alredy done in rip_read () */
 | 
						||
 | 
						||
  /* Update RIP peer. */
 | 
						||
  rip_peer_update (from, packet->version);
 | 
						||
 | 
						||
  /* Set RTE pointer. */
 | 
						||
  rte = packet->rte;
 | 
						||
 | 
						||
  for (lim = (caddr_t) packet + size; (caddr_t) rte < lim; rte++)
 | 
						||
    {
 | 
						||
      /* RIPv2 authentication check. */
 | 
						||
      /* If the Address Family Identifier of the first (and only the
 | 
						||
	 first) entry in the message is 0xFFFF, then the remainder of
 | 
						||
	 the entry contains the authentication. */
 | 
						||
      /* If the packet gets here it means authentication enabled */
 | 
						||
      /* Check is done in rip_read(). So, just skipping it */
 | 
						||
      if (packet->version == RIPv2 &&
 | 
						||
	  rte == packet->rte &&
 | 
						||
	  rte->family == htons(RIP_FAMILY_AUTH))
 | 
						||
	continue;
 | 
						||
 | 
						||
      if (rte->family != htons(AF_INET))
 | 
						||
	{
 | 
						||
	  /* Address family check.  RIP only supports AF_INET. */
 | 
						||
	  zlog_info ("Unsupported family %d from %s.",
 | 
						||
		     ntohs (rte->family), inet_ntoa (from->sin_addr));
 | 
						||
	  continue;
 | 
						||
	}
 | 
						||
 | 
						||
      /* - is the destination address valid (e.g., unicast; not net 0
 | 
						||
         or 127) */
 | 
						||
      if (! rip_destination_check (rte->prefix))
 | 
						||
        {
 | 
						||
	  zlog_info ("Network is net 0 or net 127 or it is not unicast network");
 | 
						||
	  rip_peer_bad_route (from);
 | 
						||
	  continue;
 | 
						||
	} 
 | 
						||
 | 
						||
      /* Convert metric value to host byte order. */
 | 
						||
      rte->metric = ntohl (rte->metric);
 | 
						||
 | 
						||
      /* - is the metric valid (i.e., between 1 and 16, inclusive) */
 | 
						||
      if (! (rte->metric >= 1 && rte->metric <= 16))
 | 
						||
	{
 | 
						||
	  zlog_info ("Route's metric is not in the 1-16 range.");
 | 
						||
	  rip_peer_bad_route (from);
 | 
						||
	  continue;
 | 
						||
	}
 | 
						||
 | 
						||
      /* RIPv1 does not have nexthop value. */
 | 
						||
      if (packet->version == RIPv1 && rte->nexthop.s_addr != 0)
 | 
						||
	{
 | 
						||
	  zlog_info ("RIPv1 packet with nexthop value %s",
 | 
						||
		     inet_ntoa (rte->nexthop));
 | 
						||
	  rip_peer_bad_route (from);
 | 
						||
	  continue;
 | 
						||
	}
 | 
						||
 | 
						||
      /* That is, if the provided information is ignored, a possibly
 | 
						||
	 sub-optimal, but absolutely valid, route may be taken.  If
 | 
						||
	 the received Next Hop is not directly reachable, it should be
 | 
						||
	 treated as 0.0.0.0. */
 | 
						||
      if (packet->version == RIPv2 && rte->nexthop.s_addr != 0)
 | 
						||
	{
 | 
						||
	  u_int32_t addrval;
 | 
						||
 | 
						||
	  /* Multicast address check. */
 | 
						||
	  addrval = ntohl (rte->nexthop.s_addr);
 | 
						||
	  if (IN_CLASSD (addrval))
 | 
						||
	    {
 | 
						||
	      zlog_info ("Nexthop %s is multicast address, skip this rte",
 | 
						||
			 inet_ntoa (rte->nexthop));
 | 
						||
	      continue;
 | 
						||
	    }
 | 
						||
 | 
						||
	  if (! if_lookup_address (rte->nexthop))
 | 
						||
	    {
 | 
						||
	      struct route_node *rn;
 | 
						||
	      struct rip_info *rinfo;
 | 
						||
 | 
						||
	      rn = route_node_match_ipv4 (rip->table, &rte->nexthop);
 | 
						||
 | 
						||
	      if (rn)
 | 
						||
		{
 | 
						||
		  rinfo = rn->info;
 | 
						||
 | 
						||
		  if (rinfo->type == ZEBRA_ROUTE_RIP
 | 
						||
		      && rinfo->sub_type == RIP_ROUTE_RTE)
 | 
						||
		    {
 | 
						||
		      if (IS_RIP_DEBUG_EVENT)
 | 
						||
			zlog_debug ("Next hop %s is on RIP network.  Set nexthop to the packet's originator", inet_ntoa (rte->nexthop));
 | 
						||
		      rte->nexthop = rinfo->from;
 | 
						||
		    }
 | 
						||
		  else
 | 
						||
		    {
 | 
						||
		      if (IS_RIP_DEBUG_EVENT)
 | 
						||
			zlog_debug ("Next hop %s is not directly reachable. Treat it as 0.0.0.0", inet_ntoa (rte->nexthop));
 | 
						||
		      rte->nexthop.s_addr = 0;
 | 
						||
		    }
 | 
						||
 | 
						||
		  route_unlock_node (rn);
 | 
						||
		}
 | 
						||
	      else
 | 
						||
		{
 | 
						||
		  if (IS_RIP_DEBUG_EVENT)
 | 
						||
		    zlog_debug ("Next hop %s is not directly reachable. Treat it as 0.0.0.0", inet_ntoa (rte->nexthop));
 | 
						||
		  rte->nexthop.s_addr = 0;
 | 
						||
		}
 | 
						||
 | 
						||
	    }
 | 
						||
	}
 | 
						||
 | 
						||
     /* For RIPv1, there won't be a valid netmask.  
 | 
						||
 | 
						||
	This is a best guess at the masks.  If everyone was using old
 | 
						||
	Ciscos before the 'ip subnet zero' option, it would be almost
 | 
						||
	right too :-)
 | 
						||
      
 | 
						||
	Cisco summarize ripv1 advertisments to the classful boundary
 | 
						||
	(/16 for class B's) except when the RIP packet does to inside
 | 
						||
	the classful network in question.  */
 | 
						||
 | 
						||
      if ((packet->version == RIPv1 && rte->prefix.s_addr != 0) 
 | 
						||
	  || (packet->version == RIPv2 
 | 
						||
	      && (rte->prefix.s_addr != 0 && rte->mask.s_addr == 0)))
 | 
						||
	{
 | 
						||
	  u_int32_t destination;
 | 
						||
 | 
						||
	  if (subnetted == -1)
 | 
						||
            {
 | 
						||
              memcpy (&ifaddr, ifc->address, sizeof (struct prefix_ipv4));
 | 
						||
              memcpy (&ifaddrclass, &ifaddr, sizeof (struct prefix_ipv4));
 | 
						||
              apply_classful_mask_ipv4 (&ifaddrclass);
 | 
						||
              subnetted = 0;
 | 
						||
              if (ifaddr.prefixlen > ifaddrclass.prefixlen)
 | 
						||
                subnetted = 1;
 | 
						||
            }
 | 
						||
 | 
						||
	  destination = ntohl (rte->prefix.s_addr);
 | 
						||
 | 
						||
	  if (IN_CLASSA (destination))
 | 
						||
	      masklen2ip (8, &rte->mask);
 | 
						||
	  else if (IN_CLASSB (destination))
 | 
						||
	      masklen2ip (16, &rte->mask);
 | 
						||
	  else if (IN_CLASSC (destination))
 | 
						||
	      masklen2ip (24, &rte->mask);
 | 
						||
 | 
						||
	  if (subnetted == 1)
 | 
						||
	    masklen2ip (ifaddrclass.prefixlen,
 | 
						||
			(struct in_addr *) &destination);
 | 
						||
	  if ((subnetted == 1) && ((rte->prefix.s_addr & destination) ==
 | 
						||
	      ifaddrclass.prefix.s_addr))
 | 
						||
	    {
 | 
						||
	      masklen2ip (ifaddr.prefixlen, &rte->mask);
 | 
						||
	      if ((rte->prefix.s_addr & rte->mask.s_addr) != rte->prefix.s_addr)
 | 
						||
		masklen2ip (32, &rte->mask);
 | 
						||
	      if (IS_RIP_DEBUG_EVENT)
 | 
						||
		zlog_debug ("Subnetted route %s", inet_ntoa (rte->prefix));
 | 
						||
	    }
 | 
						||
	  else
 | 
						||
	    {
 | 
						||
	      if ((rte->prefix.s_addr & rte->mask.s_addr) != rte->prefix.s_addr)
 | 
						||
		continue;
 | 
						||
	    }
 | 
						||
 | 
						||
	  if (IS_RIP_DEBUG_EVENT)
 | 
						||
	    {
 | 
						||
	      zlog_debug ("Resultant route %s", inet_ntoa (rte->prefix));
 | 
						||
	      zlog_debug ("Resultant mask %s", inet_ntoa (rte->mask));
 | 
						||
	    }
 | 
						||
	}
 | 
						||
 | 
						||
      /* In case of RIPv2, if prefix in RTE is not netmask applied one
 | 
						||
         ignore the entry.  */
 | 
						||
      if ((packet->version == RIPv2) 
 | 
						||
	  && (rte->mask.s_addr != 0) 
 | 
						||
	  && ((rte->prefix.s_addr & rte->mask.s_addr) != rte->prefix.s_addr))
 | 
						||
	{
 | 
						||
	  zlog_warn ("RIPv2 address %s is not mask /%d applied one",
 | 
						||
		     inet_ntoa (rte->prefix), ip_masklen (rte->mask));
 | 
						||
	  rip_peer_bad_route (from);
 | 
						||
	  continue;
 | 
						||
	}
 | 
						||
 | 
						||
      /* Default route's netmask is ignored. */
 | 
						||
      if (packet->version == RIPv2
 | 
						||
	  && (rte->prefix.s_addr == 0)
 | 
						||
	  && (rte->mask.s_addr != 0))
 | 
						||
	{
 | 
						||
	  if (IS_RIP_DEBUG_EVENT)
 | 
						||
	    zlog_debug ("Default route with non-zero netmask.  Set zero to netmask");
 | 
						||
	  rte->mask.s_addr = 0;
 | 
						||
	}
 | 
						||
	  
 | 
						||
      /* Routing table updates. */
 | 
						||
      rip_rte_process (rte, from, ifc->ifp);
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/* Make socket for RIP protocol. */
 | 
						||
static int 
 | 
						||
rip_create_socket (struct sockaddr_in *from)
 | 
						||
{
 | 
						||
  int ret;
 | 
						||
  int sock;
 | 
						||
  struct sockaddr_in addr;
 | 
						||
  
 | 
						||
  memset (&addr, 0, sizeof (struct sockaddr_in));
 | 
						||
  
 | 
						||
  if (!from)
 | 
						||
    {
 | 
						||
      addr.sin_family = AF_INET;
 | 
						||
      addr.sin_addr.s_addr = INADDR_ANY;
 | 
						||
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
 | 
						||
      addr.sin_len = sizeof (struct sockaddr_in);
 | 
						||
#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
 | 
						||
    } else {
 | 
						||
      memcpy(&addr, from, sizeof(addr));
 | 
						||
    }
 | 
						||
  
 | 
						||
  /* sending port must always be the RIP port */
 | 
						||
  addr.sin_port = htons (RIP_PORT_DEFAULT);
 | 
						||
  
 | 
						||
  /* Make datagram socket. */
 | 
						||
  sock = socket (AF_INET, SOCK_DGRAM, 0);
 | 
						||
  if (sock < 0) 
 | 
						||
    {
 | 
						||
      zlog_err("Cannot create UDP socket: %s", safe_strerror(errno));
 | 
						||
      exit (1);
 | 
						||
    }
 | 
						||
 | 
						||
  sockopt_broadcast (sock);
 | 
						||
  sockopt_reuseaddr (sock);
 | 
						||
  sockopt_reuseport (sock);
 | 
						||
#ifdef RIP_RECVMSG
 | 
						||
  setsockopt_pktinfo (sock);
 | 
						||
#endif /* RIP_RECVMSG */
 | 
						||
 | 
						||
  if (ripd_privs.change (ZPRIVS_RAISE))
 | 
						||
      zlog_err ("rip_create_socket: could not raise privs");
 | 
						||
  setsockopt_so_recvbuf (sock, RIP_UDP_RCV_BUF);
 | 
						||
  if ( (ret = bind (sock, (struct sockaddr *) & addr, sizeof (addr))) < 0)
 | 
						||
  
 | 
						||
    {
 | 
						||
      int save_errno = errno;
 | 
						||
      if (ripd_privs.change (ZPRIVS_LOWER))
 | 
						||
        zlog_err ("rip_create_socket: could not lower privs");
 | 
						||
      
 | 
						||
      zlog_err("%s: Can't bind socket %d to %s port %d: %s", __func__,
 | 
						||
	       sock, inet_ntoa(addr.sin_addr), 
 | 
						||
	       (int) ntohs(addr.sin_port), 
 | 
						||
	       safe_strerror(save_errno));
 | 
						||
      
 | 
						||
      close (sock);
 | 
						||
      return ret;
 | 
						||
    }
 | 
						||
  
 | 
						||
  if (ripd_privs.change (ZPRIVS_LOWER))
 | 
						||
      zlog_err ("rip_create_socket: could not lower privs");
 | 
						||
      
 | 
						||
  return sock;
 | 
						||
}
 | 
						||
 | 
						||
/* RIP packet send to destination address, on interface denoted by
 | 
						||
 * by connected argument. NULL to argument denotes destination should be
 | 
						||
 * should be RIP multicast group
 | 
						||
 */
 | 
						||
static int
 | 
						||
rip_send_packet (u_char * buf, int size, struct sockaddr_in *to,
 | 
						||
                 struct connected *ifc)
 | 
						||
{
 | 
						||
  int ret, send_sock;
 | 
						||
  struct sockaddr_in sin;
 | 
						||
  
 | 
						||
  assert (ifc != NULL);
 | 
						||
  
 | 
						||
  if (IS_RIP_DEBUG_PACKET)
 | 
						||
    {
 | 
						||
#define ADDRESS_SIZE 20
 | 
						||
      char dst[ADDRESS_SIZE];
 | 
						||
      dst[ADDRESS_SIZE - 1] = '\0';
 | 
						||
      
 | 
						||
      if (to)
 | 
						||
        {
 | 
						||
          strncpy (dst, inet_ntoa(to->sin_addr), ADDRESS_SIZE - 1);
 | 
						||
        }
 | 
						||
      else
 | 
						||
        {
 | 
						||
          sin.sin_addr.s_addr = htonl (INADDR_RIP_GROUP);
 | 
						||
          strncpy (dst, inet_ntoa(sin.sin_addr), ADDRESS_SIZE - 1);
 | 
						||
        }
 | 
						||
#undef ADDRESS_SIZE
 | 
						||
      zlog_debug("rip_send_packet %s > %s (%s)",
 | 
						||
                inet_ntoa(ifc->address->u.prefix4),
 | 
						||
                dst, ifc->ifp->name);
 | 
						||
    }
 | 
						||
  
 | 
						||
  if ( CHECK_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY) )
 | 
						||
    {
 | 
						||
      /*
 | 
						||
       * ZEBRA_IFA_SECONDARY is set on linux when an interface is configured
 | 
						||
       * with multiple addresses on the same subnet: the first address
 | 
						||
       * on the subnet is configured "primary", and all subsequent addresses
 | 
						||
       * on that subnet are treated as "secondary" addresses. 
 | 
						||
       * In order to avoid routing-table bloat on other rip listeners, 
 | 
						||
       * we do not send out RIP packets with ZEBRA_IFA_SECONDARY source addrs.
 | 
						||
       * XXX Since Linux is the only system for which the ZEBRA_IFA_SECONDARY
 | 
						||
       * flag is set, we would end up sending a packet for a "secondary"
 | 
						||
       * source address on non-linux systems.  
 | 
						||
       */
 | 
						||
      if (IS_RIP_DEBUG_PACKET)
 | 
						||
        zlog_debug("duplicate dropped");
 | 
						||
      return 0;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Make destination address. */
 | 
						||
  memset (&sin, 0, sizeof (struct sockaddr_in));
 | 
						||
  sin.sin_family = AF_INET;
 | 
						||
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
 | 
						||
  sin.sin_len = sizeof (struct sockaddr_in);
 | 
						||
#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
 | 
						||
 | 
						||
  /* When destination is specified, use it's port and address. */
 | 
						||
  if (to)
 | 
						||
    {
 | 
						||
      sin.sin_port = to->sin_port;
 | 
						||
      sin.sin_addr = to->sin_addr;
 | 
						||
      send_sock = rip->sock;
 | 
						||
    }
 | 
						||
  else
 | 
						||
    {
 | 
						||
      struct sockaddr_in from;
 | 
						||
      
 | 
						||
      sin.sin_port = htons (RIP_PORT_DEFAULT);
 | 
						||
      sin.sin_addr.s_addr = htonl (INADDR_RIP_GROUP);
 | 
						||
      
 | 
						||
      /* multicast send should bind to local interface address */
 | 
						||
      from.sin_family = AF_INET;
 | 
						||
      from.sin_port = htons (RIP_PORT_DEFAULT);
 | 
						||
      from.sin_addr = ifc->address->u.prefix4;
 | 
						||
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
 | 
						||
      from.sin_len = sizeof (struct sockaddr_in);
 | 
						||
#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
 | 
						||
      
 | 
						||
      /*
 | 
						||
       * we have to open a new socket for each packet because this
 | 
						||
       * is the most portable way to bind to a different source
 | 
						||
       * ipv4 address for each packet. 
 | 
						||
       */
 | 
						||
      if ( (send_sock = rip_create_socket (&from)) < 0)
 | 
						||
        {
 | 
						||
          zlog_warn("rip_send_packet could not create socket.");
 | 
						||
          return -1;
 | 
						||
        }
 | 
						||
      rip_interface_multicast_set (send_sock, ifc);
 | 
						||
    }
 | 
						||
 | 
						||
  ret = sendto (send_sock, buf, size, 0, (struct sockaddr *)&sin,
 | 
						||
		sizeof (struct sockaddr_in));
 | 
						||
 | 
						||
  if (IS_RIP_DEBUG_EVENT)
 | 
						||
      zlog_debug ("SEND to  %s.%d", inet_ntoa(sin.sin_addr), 
 | 
						||
                  ntohs (sin.sin_port));
 | 
						||
 | 
						||
  if (ret < 0)
 | 
						||
    zlog_warn ("can't send packet : %s", safe_strerror (errno));
 | 
						||
 | 
						||
  if (!to)
 | 
						||
    close(send_sock);
 | 
						||
 | 
						||
  return ret;
 | 
						||
}
 | 
						||
 | 
						||
/* Add redistributed route to RIP table. */
 | 
						||
void
 | 
						||
rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, 
 | 
						||
		      unsigned int ifindex, struct in_addr *nexthop,
 | 
						||
                      unsigned int metric, unsigned char distance)
 | 
						||
{
 | 
						||
  int ret;
 | 
						||
  struct route_node *rp;
 | 
						||
  struct rip_info *rinfo;
 | 
						||
 | 
						||
  /* Redistribute route  */
 | 
						||
  ret = rip_destination_check (p->prefix);
 | 
						||
  if (! ret)
 | 
						||
    return;
 | 
						||
 | 
						||
  rp = route_node_get (rip->table, (struct prefix *) p);
 | 
						||
 | 
						||
  rinfo = rp->info;
 | 
						||
 | 
						||
  if (rinfo)
 | 
						||
    {
 | 
						||
      if (rinfo->type == ZEBRA_ROUTE_CONNECT 
 | 
						||
	  && rinfo->sub_type == RIP_ROUTE_INTERFACE
 | 
						||
	  && rinfo->metric != RIP_METRIC_INFINITY)
 | 
						||
	{
 | 
						||
	  route_unlock_node (rp);
 | 
						||
	  return;
 | 
						||
	}
 | 
						||
 | 
						||
      /* Manually configured RIP route check. */
 | 
						||
      if (rinfo->type == ZEBRA_ROUTE_RIP 
 | 
						||
	  && ((rinfo->sub_type == RIP_ROUTE_STATIC) ||
 | 
						||
	      (rinfo->sub_type == RIP_ROUTE_DEFAULT)) )
 | 
						||
	{
 | 
						||
	  if (type != ZEBRA_ROUTE_RIP || ((sub_type != RIP_ROUTE_STATIC) &&
 | 
						||
	                                  (sub_type != RIP_ROUTE_DEFAULT)))
 | 
						||
	    {
 | 
						||
	      route_unlock_node (rp);
 | 
						||
	      return;
 | 
						||
	    }
 | 
						||
	}
 | 
						||
 | 
						||
      RIP_TIMER_OFF (rinfo->t_timeout);
 | 
						||
      RIP_TIMER_OFF (rinfo->t_garbage_collect);
 | 
						||
 | 
						||
      if (rip_route_rte (rinfo))
 | 
						||
	rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rp->p, &rinfo->nexthop,
 | 
						||
			       rinfo->metric);
 | 
						||
      rp->info = NULL;
 | 
						||
      rip_info_free (rinfo);
 | 
						||
      
 | 
						||
      route_unlock_node (rp);      
 | 
						||
    }
 | 
						||
 | 
						||
  rinfo = rip_info_new ();
 | 
						||
    
 | 
						||
  rinfo->type = type;
 | 
						||
  rinfo->sub_type = sub_type;
 | 
						||
  rinfo->ifindex = ifindex;
 | 
						||
  rinfo->metric = 1;
 | 
						||
  rinfo->external_metric = metric;
 | 
						||
  rinfo->distance = distance;
 | 
						||
  rinfo->rp = rp;
 | 
						||
 | 
						||
  if (nexthop)
 | 
						||
    rinfo->nexthop = *nexthop;
 | 
						||
 | 
						||
  rinfo->flags |= RIP_RTF_FIB;
 | 
						||
  rp->info = rinfo;
 | 
						||
 | 
						||
  rinfo->flags |= RIP_RTF_CHANGED;
 | 
						||
 | 
						||
  if (IS_RIP_DEBUG_EVENT) {
 | 
						||
    if (!nexthop)
 | 
						||
      zlog_debug ("Redistribute new prefix %s/%d on the interface %s",
 | 
						||
                 inet_ntoa(p->prefix), p->prefixlen,
 | 
						||
                 ifindex2ifname(ifindex));
 | 
						||
    else
 | 
						||
      zlog_debug ("Redistribute new prefix %s/%d with nexthop %s on the interface %s",
 | 
						||
                 inet_ntoa(p->prefix), p->prefixlen, inet_ntoa(rinfo->nexthop),
 | 
						||
                 ifindex2ifname(ifindex));
 | 
						||
  }
 | 
						||
 | 
						||
 | 
						||
  rip_event (RIP_TRIGGERED_UPDATE, 0);
 | 
						||
}
 | 
						||
 | 
						||
/* Delete redistributed route from RIP table. */
 | 
						||
void
 | 
						||
rip_redistribute_delete (int type, int sub_type, struct prefix_ipv4 *p, 
 | 
						||
			   unsigned int ifindex)
 | 
						||
{
 | 
						||
  int ret;
 | 
						||
  struct route_node *rp;
 | 
						||
  struct rip_info *rinfo;
 | 
						||
 | 
						||
  ret = rip_destination_check (p->prefix);
 | 
						||
  if (! ret)
 | 
						||
    return;
 | 
						||
 | 
						||
  rp = route_node_lookup (rip->table, (struct prefix *) p);
 | 
						||
  if (rp)
 | 
						||
    {
 | 
						||
      rinfo = rp->info;
 | 
						||
 | 
						||
      if (rinfo != NULL
 | 
						||
	  && rinfo->type == type 
 | 
						||
	  && rinfo->sub_type == sub_type 
 | 
						||
	  && rinfo->ifindex == ifindex)
 | 
						||
	{
 | 
						||
	  /* Perform poisoned reverse. */
 | 
						||
	  rinfo->metric = RIP_METRIC_INFINITY;
 | 
						||
	  RIP_TIMER_ON (rinfo->t_garbage_collect, 
 | 
						||
			rip_garbage_collect, rip->garbage_time);
 | 
						||
	  RIP_TIMER_OFF (rinfo->t_timeout);
 | 
						||
	  rinfo->flags |= RIP_RTF_CHANGED;
 | 
						||
 | 
						||
          if (IS_RIP_DEBUG_EVENT)
 | 
						||
            zlog_debug ("Poisone %s/%d on the interface %s with an infinity metric [delete]",
 | 
						||
                       inet_ntoa(p->prefix), p->prefixlen,
 | 
						||
                       ifindex2ifname(ifindex));
 | 
						||
 | 
						||
	  rip_event (RIP_TRIGGERED_UPDATE, 0);
 | 
						||
	}
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/* Response to request called from rip_read ().*/
 | 
						||
static void
 | 
						||
rip_request_process (struct rip_packet *packet, int size, 
 | 
						||
		     struct sockaddr_in *from, struct connected *ifc)
 | 
						||
{
 | 
						||
  caddr_t lim;
 | 
						||
  struct rte *rte;
 | 
						||
  struct prefix_ipv4 p;
 | 
						||
  struct route_node *rp;
 | 
						||
  struct rip_info *rinfo;
 | 
						||
  struct rip_interface *ri;
 | 
						||
 | 
						||
  /* Does not reponse to the requests on the loopback interfaces */
 | 
						||
  if (if_is_loopback (ifc->ifp))
 | 
						||
    return;
 | 
						||
 | 
						||
  /* Check RIP process is enabled on this interface. */
 | 
						||
  ri = ifc->ifp->info;
 | 
						||
  if (! ri->running)
 | 
						||
    return;
 | 
						||
 | 
						||
  /* When passive interface is specified, suppress responses */
 | 
						||
  if (ri->passive)
 | 
						||
    return;
 | 
						||
  
 | 
						||
  /* RIP peer update. */
 | 
						||
  rip_peer_update (from, packet->version);
 | 
						||
 | 
						||
  lim = ((caddr_t) packet) + size;
 | 
						||
  rte = packet->rte;
 | 
						||
 | 
						||
  /* The Request is processed entry by entry.  If there are no
 | 
						||
     entries, no response is given. */
 | 
						||
  if (lim == (caddr_t) rte)
 | 
						||
    return;
 | 
						||
 | 
						||
  /* There is one special case.  If there is exactly one entry in the
 | 
						||
     request, and it has an address family identifier of zero and a
 | 
						||
     metric of infinity (i.e., 16), then this is a request to send the
 | 
						||
     entire routing table. */
 | 
						||
  if (lim == ((caddr_t) (rte + 1)) &&
 | 
						||
      ntohs (rte->family) == 0 &&
 | 
						||
      ntohl (rte->metric) == RIP_METRIC_INFINITY)
 | 
						||
    {	
 | 
						||
      struct prefix_ipv4 saddr;
 | 
						||
 | 
						||
      /* saddr will be used for determining which routes to split-horizon.
 | 
						||
         Since the source address we'll pick will be on the same subnet as the
 | 
						||
         destination, for the purpose of split-horizoning, we'll
 | 
						||
         pretend that "from" is our source address.  */
 | 
						||
      saddr.family = AF_INET;
 | 
						||
      saddr.prefixlen = IPV4_MAX_BITLEN;
 | 
						||
      saddr.prefix = from->sin_addr;
 | 
						||
 | 
						||
      /* All route with split horizon */
 | 
						||
      rip_output_process (ifc, from, rip_all_route, packet->version);
 | 
						||
    }
 | 
						||
  else
 | 
						||
    {
 | 
						||
      /* Examine the list of RTEs in the Request one by one.  For each
 | 
						||
	 entry, look up the destination in the router's routing
 | 
						||
	 database and, if there is a route, put that route's metric in
 | 
						||
	 the metric field of the RTE.  If there is no explicit route
 | 
						||
	 to the specified destination, put infinity in the metric
 | 
						||
	 field.  Once all the entries have been filled in, change the
 | 
						||
	 command from Request to Response and send the datagram back
 | 
						||
	 to the requestor. */
 | 
						||
      p.family = AF_INET;
 | 
						||
 | 
						||
      for (; ((caddr_t) rte) < lim; rte++)
 | 
						||
	{
 | 
						||
	  p.prefix = rte->prefix;
 | 
						||
	  p.prefixlen = ip_masklen (rte->mask);
 | 
						||
	  apply_mask_ipv4 (&p);
 | 
						||
	  
 | 
						||
	  rp = route_node_lookup (rip->table, (struct prefix *) &p);
 | 
						||
	  if (rp)
 | 
						||
	    {
 | 
						||
	      rinfo = rp->info;
 | 
						||
	      rte->metric = htonl (rinfo->metric);
 | 
						||
	      route_unlock_node (rp);
 | 
						||
	    }
 | 
						||
	  else
 | 
						||
	    rte->metric = htonl (RIP_METRIC_INFINITY);
 | 
						||
	}
 | 
						||
      packet->command = RIP_RESPONSE;
 | 
						||
 | 
						||
      rip_send_packet ((u_char *)packet, size, from, ifc);
 | 
						||
    }
 | 
						||
  rip_global_queries++;
 | 
						||
}
 | 
						||
 | 
						||
#if RIP_RECVMSG
 | 
						||
/* Set IPv6 packet info to the socket. */
 | 
						||
static int
 | 
						||
setsockopt_pktinfo (int sock)
 | 
						||
{
 | 
						||
  int ret;
 | 
						||
  int val = 1;
 | 
						||
    
 | 
						||
  ret = setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof(val));
 | 
						||
  if (ret < 0)
 | 
						||
    zlog_warn ("Can't setsockopt IP_PKTINFO : %s", safe_strerror (errno));
 | 
						||
  return ret;
 | 
						||
}
 | 
						||
 | 
						||
/* Read RIP packet by recvmsg function. */
 | 
						||
int
 | 
						||
rip_recvmsg (int sock, u_char *buf, int size, struct sockaddr_in *from,
 | 
						||
	     int *ifindex)
 | 
						||
{
 | 
						||
  int ret;
 | 
						||
  struct msghdr msg;
 | 
						||
  struct iovec iov;
 | 
						||
  struct cmsghdr *ptr;
 | 
						||
  char adata[1024];
 | 
						||
 | 
						||
  msg.msg_name = (void *) from;
 | 
						||
  msg.msg_namelen = sizeof (struct sockaddr_in);
 | 
						||
  msg.msg_iov = &iov;
 | 
						||
  msg.msg_iovlen = 1;
 | 
						||
  msg.msg_control = (void *) adata;
 | 
						||
  msg.msg_controllen = sizeof adata;
 | 
						||
  iov.iov_base = buf;
 | 
						||
  iov.iov_len = size;
 | 
						||
 | 
						||
  ret = recvmsg (sock, &msg, 0);
 | 
						||
  if (ret < 0)
 | 
						||
    return ret;
 | 
						||
 | 
						||
  for (ptr = ZCMSG_FIRSTHDR(&msg); ptr != NULL; ptr = CMSG_NXTHDR(&msg, ptr))
 | 
						||
    if (ptr->cmsg_level == IPPROTO_IP && ptr->cmsg_type == IP_PKTINFO) 
 | 
						||
      {
 | 
						||
	struct in_pktinfo *pktinfo;
 | 
						||
	int i;
 | 
						||
 | 
						||
	pktinfo = (struct in_pktinfo *) CMSG_DATA (ptr);
 | 
						||
	i = pktinfo->ipi_ifindex;
 | 
						||
      }
 | 
						||
  return ret;
 | 
						||
}
 | 
						||
 | 
						||
/* RIP packet read function. */
 | 
						||
int
 | 
						||
rip_read_new (struct thread *t)
 | 
						||
{
 | 
						||
  int ret;
 | 
						||
  int sock;
 | 
						||
  char buf[RIP_PACKET_MAXSIZ];
 | 
						||
  struct sockaddr_in from;
 | 
						||
  unsigned int ifindex;
 | 
						||
  
 | 
						||
  /* Fetch socket then register myself. */
 | 
						||
  sock = THREAD_FD (t);
 | 
						||
  rip_event (RIP_READ, sock);
 | 
						||
 | 
						||
  /* Read RIP packet. */
 | 
						||
  ret = rip_recvmsg (sock, buf, RIP_PACKET_MAXSIZ, &from, (int *)&ifindex);
 | 
						||
  if (ret < 0)
 | 
						||
    {
 | 
						||
      zlog_warn ("Can't read RIP packet: %s", safe_strerror (errno));
 | 
						||
      return ret;
 | 
						||
    }
 | 
						||
 | 
						||
  return ret;
 | 
						||
}
 | 
						||
#endif /* RIP_RECVMSG */
 | 
						||
 | 
						||
/* First entry point of RIP packet. */
 | 
						||
static int
 | 
						||
rip_read (struct thread *t)
 | 
						||
{
 | 
						||
  int sock;
 | 
						||
  int ret;
 | 
						||
  int rtenum;
 | 
						||
  union rip_buf rip_buf;
 | 
						||
  struct rip_packet *packet;
 | 
						||
  struct sockaddr_in from;
 | 
						||
  int len;
 | 
						||
  int vrecv;
 | 
						||
  socklen_t fromlen;
 | 
						||
  struct interface *ifp;
 | 
						||
  struct connected *ifc;
 | 
						||
  struct rip_interface *ri;
 | 
						||
 | 
						||
  /* Fetch socket then register myself. */
 | 
						||
  sock = THREAD_FD (t);
 | 
						||
  rip->t_read = NULL;
 | 
						||
 | 
						||
  /* Add myself to tne next event */
 | 
						||
  rip_event (RIP_READ, sock);
 | 
						||
 | 
						||
  /* RIPd manages only IPv4. */
 | 
						||
  memset (&from, 0, sizeof (struct sockaddr_in));
 | 
						||
  fromlen = sizeof (struct sockaddr_in);
 | 
						||
 | 
						||
  len = recvfrom (sock, (char *)&rip_buf.buf, sizeof (rip_buf.buf), 0, 
 | 
						||
		  (struct sockaddr *) &from, &fromlen);
 | 
						||
  if (len < 0) 
 | 
						||
    {
 | 
						||
      zlog_info ("recvfrom failed: %s", safe_strerror (errno));
 | 
						||
      return len;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Check is this packet comming from myself? */
 | 
						||
  if (if_check_address (from.sin_addr)) 
 | 
						||
    {
 | 
						||
      if (IS_RIP_DEBUG_PACKET)
 | 
						||
	zlog_debug ("ignore packet comes from myself");
 | 
						||
      return -1;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Which interface is this packet comes from. */
 | 
						||
  ifp = if_lookup_address (from.sin_addr);
 | 
						||
  
 | 
						||
  /* RIP packet received */
 | 
						||
  if (IS_RIP_DEBUG_EVENT)
 | 
						||
    zlog_debug ("RECV packet from %s port %d on %s",
 | 
						||
	       inet_ntoa (from.sin_addr), ntohs (from.sin_port),
 | 
						||
	       ifp ? ifp->name : "unknown");
 | 
						||
 | 
						||
  /* If this packet come from unknown interface, ignore it. */
 | 
						||
  if (ifp == NULL)
 | 
						||
    {
 | 
						||
      zlog_info ("rip_read: cannot find interface for packet from %s port %d",
 | 
						||
		 inet_ntoa(from.sin_addr), ntohs (from.sin_port));
 | 
						||
      return -1;
 | 
						||
    }
 | 
						||
  
 | 
						||
  ifc = connected_lookup_address (ifp, from.sin_addr);
 | 
						||
  
 | 
						||
  if (ifc == NULL)
 | 
						||
    {
 | 
						||
      zlog_info ("rip_read: cannot find connected address for packet from %s "
 | 
						||
		 "port %d on interface %s",
 | 
						||
		 inet_ntoa(from.sin_addr), ntohs (from.sin_port), ifp->name);
 | 
						||
      return -1;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Packet length check. */
 | 
						||
  if (len < RIP_PACKET_MINSIZ)
 | 
						||
    {
 | 
						||
      zlog_warn ("packet size %d is smaller than minimum size %d",
 | 
						||
		 len, RIP_PACKET_MINSIZ);
 | 
						||
      rip_peer_bad_packet (&from);
 | 
						||
      return len;
 | 
						||
    }
 | 
						||
  if (len > RIP_PACKET_MAXSIZ)
 | 
						||
    {
 | 
						||
      zlog_warn ("packet size %d is larger than max size %d",
 | 
						||
		 len, RIP_PACKET_MAXSIZ);
 | 
						||
      rip_peer_bad_packet (&from);
 | 
						||
      return len;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Packet alignment check. */
 | 
						||
  if ((len - RIP_PACKET_MINSIZ) % 20)
 | 
						||
    {
 | 
						||
      zlog_warn ("packet size %d is wrong for RIP packet alignment", len);
 | 
						||
      rip_peer_bad_packet (&from);
 | 
						||
      return len;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Set RTE number. */
 | 
						||
  rtenum = ((len - RIP_PACKET_MINSIZ) / 20);
 | 
						||
 | 
						||
  /* For easy to handle. */
 | 
						||
  packet = &rip_buf.rip_packet;
 | 
						||
 | 
						||
  /* RIP version check. */
 | 
						||
  if (packet->version == 0)
 | 
						||
    {
 | 
						||
      zlog_info ("version 0 with command %d received.", packet->command);
 | 
						||
      rip_peer_bad_packet (&from);
 | 
						||
      return -1;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Dump RIP packet. */
 | 
						||
  if (IS_RIP_DEBUG_RECV)
 | 
						||
    rip_packet_dump (packet, len, "RECV");
 | 
						||
 | 
						||
  /* RIP version adjust.  This code should rethink now.  RFC1058 says
 | 
						||
     that "Version 1 implementations are to ignore this extra data and
 | 
						||
     process only the fields specified in this document.". So RIPv3
 | 
						||
     packet should be treated as RIPv1 ignoring must be zero field. */
 | 
						||
  if (packet->version > RIPv2)
 | 
						||
    packet->version = RIPv2;
 | 
						||
 | 
						||
  /* Is RIP running or is this RIP neighbor ?*/
 | 
						||
  ri = ifp->info;
 | 
						||
  if (! ri->running && ! rip_neighbor_lookup (&from))
 | 
						||
    {
 | 
						||
      if (IS_RIP_DEBUG_EVENT)
 | 
						||
	zlog_debug ("RIP is not enabled on interface %s.", ifp->name);
 | 
						||
      rip_peer_bad_packet (&from);
 | 
						||
      return -1;
 | 
						||
    }
 | 
						||
 | 
						||
  /* RIP Version check. RFC2453, 4.6 and 5.1 */
 | 
						||
  vrecv = ((ri->ri_receive == RI_RIP_UNSPEC) ?
 | 
						||
           rip->version_recv : ri->ri_receive);
 | 
						||
  if ((packet->version == RIPv1) && !(vrecv & RIPv1))
 | 
						||
    {
 | 
						||
      if (IS_RIP_DEBUG_PACKET)
 | 
						||
        zlog_debug ("  packet's v%d doesn't fit to if version spec", 
 | 
						||
                   packet->version);
 | 
						||
      rip_peer_bad_packet (&from);
 | 
						||
      return -1;
 | 
						||
    }
 | 
						||
  if ((packet->version == RIPv2) && !(vrecv & RIPv2))
 | 
						||
    {
 | 
						||
      if (IS_RIP_DEBUG_PACKET)
 | 
						||
        zlog_debug ("  packet's v%d doesn't fit to if version spec", 
 | 
						||
                   packet->version);
 | 
						||
      rip_peer_bad_packet (&from);
 | 
						||
      return -1;
 | 
						||
    }
 | 
						||
  
 | 
						||
  /* RFC2453 5.2 If the router is not configured to authenticate RIP-2
 | 
						||
     messages, then RIP-1 and unauthenticated RIP-2 messages will be
 | 
						||
     accepted; authenticated RIP-2 messages shall be discarded.  */
 | 
						||
  if ((ri->auth_type == RIP_NO_AUTH) 
 | 
						||
      && rtenum 
 | 
						||
      && (packet->version == RIPv2) 
 | 
						||
      && (packet->rte->family == htons(RIP_FAMILY_AUTH)))
 | 
						||
    {
 | 
						||
      if (IS_RIP_DEBUG_EVENT)
 | 
						||
	zlog_debug ("packet RIPv%d is dropped because authentication disabled", 
 | 
						||
		   packet->version);
 | 
						||
      rip_peer_bad_packet (&from);
 | 
						||
      return -1;
 | 
						||
    }
 | 
						||
  
 | 
						||
  /* RFC:
 | 
						||
     If the router is configured to authenticate RIP-2 messages, then
 | 
						||
     RIP-1 messages and RIP-2 messages which pass authentication
 | 
						||
     testing shall be accepted; unauthenticated and failed
 | 
						||
     authentication RIP-2 messages shall be discarded.  For maximum
 | 
						||
     security, RIP-1 messages should be ignored when authentication is
 | 
						||
     in use (see section 4.1); otherwise, the routing information from
 | 
						||
     authenticated messages will be propagated by RIP-1 routers in an
 | 
						||
     unauthenticated manner. 
 | 
						||
  */
 | 
						||
  /* We make an exception for RIPv1 REQUEST packets, to which we'll
 | 
						||
   * always reply regardless of authentication settings, because:
 | 
						||
   *
 | 
						||
   * - if there other authorised routers on-link, the REQUESTor can
 | 
						||
   *   passively obtain the routing updates anyway
 | 
						||
   * - if there are no other authorised routers on-link, RIP can
 | 
						||
   *   easily be disabled for the link to prevent giving out information
 | 
						||
   *   on state of this routers RIP routing table..
 | 
						||
   *
 | 
						||
   * I.e. if RIPv1 has any place anymore these days, it's as a very
 | 
						||
   * simple way to distribute routing information (e.g. to embedded
 | 
						||
   * hosts / appliances) and the ability to give out RIPv1
 | 
						||
   * routing-information freely, while still requiring RIPv2
 | 
						||
   * authentication for any RESPONSEs might be vaguely useful.
 | 
						||
   */
 | 
						||
  if (ri->auth_type != RIP_NO_AUTH 
 | 
						||
      && packet->version == RIPv1)
 | 
						||
    {
 | 
						||
      /* Discard RIPv1 messages other than REQUESTs */
 | 
						||
      if (packet->command != RIP_REQUEST)
 | 
						||
        {
 | 
						||
          if (IS_RIP_DEBUG_PACKET)
 | 
						||
            zlog_debug ("RIPv1" " dropped because authentication enabled");
 | 
						||
          rip_peer_bad_packet (&from);
 | 
						||
          return -1;
 | 
						||
        }
 | 
						||
    }
 | 
						||
  else if (ri->auth_type != RIP_NO_AUTH)
 | 
						||
    {
 | 
						||
      const char *auth_desc;
 | 
						||
      
 | 
						||
      if (rtenum == 0)
 | 
						||
        {
 | 
						||
          /* There definitely is no authentication in the packet. */
 | 
						||
          if (IS_RIP_DEBUG_PACKET)
 | 
						||
            zlog_debug ("RIPv2 authentication failed: no auth RTE in packet");
 | 
						||
          rip_peer_bad_packet (&from);
 | 
						||
          return -1;
 | 
						||
        }
 | 
						||
      
 | 
						||
      /* First RTE must be an Authentication Family RTE */
 | 
						||
      if (packet->rte->family != htons(RIP_FAMILY_AUTH))
 | 
						||
        {
 | 
						||
          if (IS_RIP_DEBUG_PACKET)
 | 
						||
            zlog_debug ("RIPv2" " dropped because authentication enabled");
 | 
						||
	  rip_peer_bad_packet (&from);
 | 
						||
	  return -1;
 | 
						||
        }
 | 
						||
      
 | 
						||
      /* Check RIPv2 authentication. */
 | 
						||
      switch (ntohs(packet->rte->tag))
 | 
						||
        {
 | 
						||
          case RIP_AUTH_SIMPLE_PASSWORD:
 | 
						||
            auth_desc = "simple";
 | 
						||
            ret = rip_auth_simple_password (packet->rte, &from, ifp);
 | 
						||
            break;
 | 
						||
          
 | 
						||
          case RIP_AUTH_MD5:
 | 
						||
            auth_desc = "MD5";
 | 
						||
            ret = rip_auth_md5 (packet, &from, len, ifp);
 | 
						||
            /* Reset RIP packet length to trim MD5 data. */
 | 
						||
            len = ret;
 | 
						||
            break;
 | 
						||
          
 | 
						||
          default:
 | 
						||
            ret = 0;
 | 
						||
            auth_desc = "unknown type";
 | 
						||
            if (IS_RIP_DEBUG_PACKET)
 | 
						||
              zlog_debug ("RIPv2 Unknown authentication type %d",
 | 
						||
                          ntohs (packet->rte->tag));
 | 
						||
        }
 | 
						||
      
 | 
						||
      if (ret)
 | 
						||
        {
 | 
						||
          if (IS_RIP_DEBUG_PACKET)
 | 
						||
            zlog_debug ("RIPv2 %s authentication success", auth_desc);
 | 
						||
        }
 | 
						||
      else
 | 
						||
        {
 | 
						||
          if (IS_RIP_DEBUG_PACKET)
 | 
						||
            zlog_debug ("RIPv2 %s authentication failure", auth_desc);
 | 
						||
          rip_peer_bad_packet (&from);
 | 
						||
          return -1;
 | 
						||
        }
 | 
						||
    }
 | 
						||
  
 | 
						||
  /* Process each command. */
 | 
						||
  switch (packet->command)
 | 
						||
    {
 | 
						||
    case RIP_RESPONSE:
 | 
						||
      rip_response_process (packet, len, &from, ifc);
 | 
						||
      break;
 | 
						||
    case RIP_REQUEST:
 | 
						||
    case RIP_POLL:
 | 
						||
      rip_request_process (packet, len, &from, ifc);
 | 
						||
      break;
 | 
						||
    case RIP_TRACEON:
 | 
						||
    case RIP_TRACEOFF:
 | 
						||
      zlog_info ("Obsolete command %s received, please sent it to routed", 
 | 
						||
		 lookup (rip_msg, packet->command));
 | 
						||
      rip_peer_bad_packet (&from);
 | 
						||
      break;
 | 
						||
    case RIP_POLL_ENTRY:
 | 
						||
      zlog_info ("Obsolete command %s received", 
 | 
						||
		 lookup (rip_msg, packet->command));
 | 
						||
      rip_peer_bad_packet (&from);
 | 
						||
      break;
 | 
						||
    default:
 | 
						||
      zlog_info ("Unknown RIP command %d received", packet->command);
 | 
						||
      rip_peer_bad_packet (&from);
 | 
						||
      break;
 | 
						||
    }
 | 
						||
 | 
						||
  return len;
 | 
						||
}
 | 
						||
 | 
						||
/* Write routing table entry to the stream and return next index of
 | 
						||
   the routing table entry in the stream. */
 | 
						||
static int
 | 
						||
rip_write_rte (int num, struct stream *s, struct prefix_ipv4 *p,
 | 
						||
               u_char version, struct rip_info *rinfo)
 | 
						||
{
 | 
						||
  struct in_addr mask;
 | 
						||
 | 
						||
  /* Write routing table entry. */
 | 
						||
  if (version == RIPv1)
 | 
						||
    {
 | 
						||
      stream_putw (s, AF_INET);
 | 
						||
      stream_putw (s, 0);
 | 
						||
      stream_put_ipv4 (s, p->prefix.s_addr);
 | 
						||
      stream_put_ipv4 (s, 0);
 | 
						||
      stream_put_ipv4 (s, 0);
 | 
						||
      stream_putl (s, rinfo->metric_out);
 | 
						||
    }
 | 
						||
  else
 | 
						||
    {
 | 
						||
      masklen2ip (p->prefixlen, &mask);
 | 
						||
 | 
						||
      stream_putw (s, AF_INET);
 | 
						||
      stream_putw (s, rinfo->tag_out);
 | 
						||
      stream_put_ipv4 (s, p->prefix.s_addr);
 | 
						||
      stream_put_ipv4 (s, mask.s_addr);
 | 
						||
      stream_put_ipv4 (s, rinfo->nexthop_out.s_addr);
 | 
						||
      stream_putl (s, rinfo->metric_out);
 | 
						||
    }
 | 
						||
 | 
						||
  return ++num;
 | 
						||
}
 | 
						||
 | 
						||
/* Send update to the ifp or spcified neighbor. */
 | 
						||
void
 | 
						||
rip_output_process (struct connected *ifc, struct sockaddr_in *to, 
 | 
						||
                    int route_type, u_char version)
 | 
						||
{
 | 
						||
  int ret;
 | 
						||
  struct stream *s;
 | 
						||
  struct route_node *rp;
 | 
						||
  struct rip_info *rinfo;
 | 
						||
  struct rip_interface *ri;
 | 
						||
  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 = 0; /* offset of digest offset field */
 | 
						||
  int num = 0;
 | 
						||
  int rtemax;
 | 
						||
  int subnetted = 0;
 | 
						||
 | 
						||
  /* Logging output event. */
 | 
						||
  if (IS_RIP_DEBUG_EVENT)
 | 
						||
    {
 | 
						||
      if (to)
 | 
						||
	zlog_debug ("update routes to neighbor %s", inet_ntoa (to->sin_addr));
 | 
						||
      else
 | 
						||
	zlog_debug ("update routes on interface %s ifindex %d",
 | 
						||
		   ifc->ifp->name, ifc->ifp->ifindex);
 | 
						||
    }
 | 
						||
 | 
						||
  /* Set output stream. */
 | 
						||
  s = rip->obuf;
 | 
						||
 | 
						||
  /* Reset stream and RTE counter. */
 | 
						||
  stream_reset (s);
 | 
						||
  rtemax = (RIP_PACKET_MAXSIZ - 4) / 20;
 | 
						||
 | 
						||
  /* Get RIP interface. */
 | 
						||
  ri = ifc->ifp->info;
 | 
						||
    
 | 
						||
  /* If output interface is in simple password authentication mode, we
 | 
						||
     need space for authentication data.  */
 | 
						||
  if (ri->auth_type == RIP_AUTH_SIMPLE_PASSWORD)
 | 
						||
    rtemax -= 1;
 | 
						||
 | 
						||
  /* If output interface is in MD5 authentication mode, we need space
 | 
						||
     for authentication header and data. */
 | 
						||
  if (ri->auth_type == RIP_AUTH_MD5)
 | 
						||
    rtemax -= 2;
 | 
						||
 | 
						||
  /* 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_NO_AUTH)
 | 
						||
    {
 | 
						||
      if (ri->key_chain)
 | 
						||
       {
 | 
						||
         struct keychain *keychain;
 | 
						||
 | 
						||
         keychain = keychain_lookup (ri->key_chain);
 | 
						||
         if (keychain)
 | 
						||
           key = key_lookup_for_send (keychain);
 | 
						||
       }
 | 
						||
      /* to be passed to auth functions later */
 | 
						||
      rip_auth_prepare_str_send (ri, key, auth_str, RIP_AUTH_SIMPLE_SIZE);
 | 
						||
    }
 | 
						||
 | 
						||
  if (version == RIPv1)
 | 
						||
    {
 | 
						||
      memcpy (&ifaddrclass, ifc->address, sizeof (struct prefix_ipv4));
 | 
						||
      apply_classful_mask_ipv4 (&ifaddrclass);
 | 
						||
      subnetted = 0;
 | 
						||
      if (ifc->address->prefixlen > ifaddrclass.prefixlen)
 | 
						||
        subnetted = 1;
 | 
						||
    }
 | 
						||
 | 
						||
  for (rp = route_top (rip->table); rp; rp = route_next (rp))
 | 
						||
    if ((rinfo = rp->info) != NULL)
 | 
						||
      {
 | 
						||
	/* For RIPv1, if we are subnetted, output subnets in our network    */
 | 
						||
	/* that have the same mask as the output "interface". For other     */
 | 
						||
	/* networks, only the classfull version is output.                  */
 | 
						||
	
 | 
						||
	if (version == RIPv1)
 | 
						||
	  {
 | 
						||
	    p = (struct prefix_ipv4 *) &rp->p;
 | 
						||
 | 
						||
	    if (IS_RIP_DEBUG_PACKET)
 | 
						||
	      zlog_debug("RIPv1 mask check, %s/%d considered for output",
 | 
						||
			inet_ntoa (rp->p.u.prefix4), rp->p.prefixlen);
 | 
						||
 | 
						||
	    if (subnetted &&
 | 
						||
		prefix_match ((struct prefix *) &ifaddrclass, &rp->p))
 | 
						||
	      {
 | 
						||
		if ((ifc->address->prefixlen != rp->p.prefixlen) &&
 | 
						||
		    (rp->p.prefixlen != 32))
 | 
						||
		  continue;
 | 
						||
	      }
 | 
						||
	    else
 | 
						||
	      {
 | 
						||
		memcpy (&classfull, &rp->p, sizeof(struct prefix_ipv4));
 | 
						||
		apply_classful_mask_ipv4(&classfull);
 | 
						||
		if (rp->p.u.prefix4.s_addr != 0 &&
 | 
						||
		    classfull.prefixlen != rp->p.prefixlen)
 | 
						||
		  continue;
 | 
						||
	      }
 | 
						||
	    if (IS_RIP_DEBUG_PACKET)
 | 
						||
	      zlog_debug("RIPv1 mask check, %s/%d made it through",
 | 
						||
			inet_ntoa (rp->p.u.prefix4), rp->p.prefixlen);
 | 
						||
	  }
 | 
						||
	else 
 | 
						||
	  p = (struct prefix_ipv4 *) &rp->p;
 | 
						||
 | 
						||
	/* Apply output filters. */
 | 
						||
	ret = rip_outgoing_filter (p, ri);
 | 
						||
	if (ret < 0)
 | 
						||
	  continue;
 | 
						||
 | 
						||
	/* Changed route only output. */
 | 
						||
	if (route_type == rip_changed_route &&
 | 
						||
	    (! (rinfo->flags & RIP_RTF_CHANGED)))
 | 
						||
	  continue;
 | 
						||
 | 
						||
	/* Split horizon. */
 | 
						||
	/* if (split_horizon == rip_split_horizon) */
 | 
						||
	if (ri->split_horizon == RIP_SPLIT_HORIZON)
 | 
						||
	  {
 | 
						||
	    /* 
 | 
						||
	     * We perform split horizon for RIP and connected route. 
 | 
						||
	     * For rip routes, we want to suppress the route if we would
 | 
						||
             * end up sending the route back on the interface that we
 | 
						||
             * learned it from, with a higher metric. For connected routes,
 | 
						||
             * we suppress the route if the prefix is a subset of the
 | 
						||
             * source address that we are going to use for the packet 
 | 
						||
             * (in order to handle the case when multiple subnets are
 | 
						||
             * configured on the same interface).
 | 
						||
             */
 | 
						||
	    if (rinfo->type == ZEBRA_ROUTE_RIP  &&
 | 
						||
                 rinfo->ifindex == ifc->ifp->ifindex) 
 | 
						||
	      continue;
 | 
						||
	    if (rinfo->type == ZEBRA_ROUTE_CONNECT &&
 | 
						||
                 prefix_match((struct prefix *)p, ifc->address))
 | 
						||
	      continue;
 | 
						||
	  }
 | 
						||
 | 
						||
	/* Preparation for route-map. */
 | 
						||
	rinfo->metric_set = 0;
 | 
						||
	rinfo->nexthop_out.s_addr = 0;
 | 
						||
	rinfo->metric_out = rinfo->metric;
 | 
						||
	rinfo->tag_out = rinfo->tag;
 | 
						||
	rinfo->ifindex_out = ifc->ifp->ifindex;
 | 
						||
 | 
						||
	/* In order to avoid some local loops,
 | 
						||
	 * if the RIP route has a nexthop via this interface, keep the nexthop,
 | 
						||
	 * otherwise set it to 0. The nexthop should not be propagated
 | 
						||
	 * beyond the local broadcast/multicast area in order
 | 
						||
	 * to avoid an IGP multi-level recursive look-up.
 | 
						||
	 * see (4.4)
 | 
						||
	 */
 | 
						||
	if (rinfo->ifindex == ifc->ifp->ifindex)
 | 
						||
	  rinfo->nexthop_out = rinfo->nexthop;
 | 
						||
 | 
						||
	/* Interface route-map */
 | 
						||
	if (ri->routemap[RIP_FILTER_OUT])
 | 
						||
	  {
 | 
						||
	    ret = route_map_apply (ri->routemap[RIP_FILTER_OUT], 
 | 
						||
				     (struct prefix *) p, RMAP_RIP, 
 | 
						||
				     rinfo);
 | 
						||
 | 
						||
	    if (ret == RMAP_DENYMATCH)
 | 
						||
	      {
 | 
						||
	        if (IS_RIP_DEBUG_PACKET)
 | 
						||
	          zlog_debug ("RIP %s/%d is filtered by route-map out",
 | 
						||
			     inet_ntoa (p->prefix), p->prefixlen);
 | 
						||
		  continue;
 | 
						||
	      }
 | 
						||
	  }
 | 
						||
           
 | 
						||
	/* Apply redistribute route map - continue, if deny */
 | 
						||
	if (rip->route_map[rinfo->type].name
 | 
						||
	    && rinfo->sub_type != RIP_ROUTE_INTERFACE)
 | 
						||
	  {
 | 
						||
	    ret = route_map_apply (rip->route_map[rinfo->type].map,
 | 
						||
				   (struct prefix *)p, RMAP_RIP, rinfo);
 | 
						||
 | 
						||
	    if (ret == RMAP_DENYMATCH) 
 | 
						||
	      {
 | 
						||
		if (IS_RIP_DEBUG_PACKET)
 | 
						||
		  zlog_debug ("%s/%d is filtered by route-map",
 | 
						||
			     inet_ntoa (p->prefix), p->prefixlen);
 | 
						||
		continue;
 | 
						||
	      }
 | 
						||
	  }
 | 
						||
 | 
						||
	/* When route-map does not set metric. */
 | 
						||
	if (! rinfo->metric_set)
 | 
						||
	  {
 | 
						||
	    /* If redistribute metric is set. */
 | 
						||
	    if (rip->route_map[rinfo->type].metric_config
 | 
						||
		&& rinfo->metric != RIP_METRIC_INFINITY)
 | 
						||
	      {
 | 
						||
		rinfo->metric_out = rip->route_map[rinfo->type].metric;
 | 
						||
	      }
 | 
						||
	    else
 | 
						||
	      {
 | 
						||
		/* If the route is not connected or localy generated
 | 
						||
		   one, use default-metric value*/
 | 
						||
		if (rinfo->type != ZEBRA_ROUTE_RIP 
 | 
						||
		    && rinfo->type != ZEBRA_ROUTE_CONNECT
 | 
						||
		    && rinfo->metric != RIP_METRIC_INFINITY)
 | 
						||
		  rinfo->metric_out = rip->default_metric;
 | 
						||
	      }
 | 
						||
	  }
 | 
						||
 | 
						||
	/* Apply offset-list */
 | 
						||
	if (rinfo->metric != RIP_METRIC_INFINITY)
 | 
						||
	  rip_offset_list_apply_out (p, ifc->ifp, &rinfo->metric_out);
 | 
						||
 | 
						||
	if (rinfo->metric_out > RIP_METRIC_INFINITY)
 | 
						||
	  rinfo->metric_out = RIP_METRIC_INFINITY;
 | 
						||
 | 
						||
	/* Perform split-horizon with poisoned reverse 
 | 
						||
	 * for RIP and connected routes.
 | 
						||
	 **/
 | 
						||
	if (ri->split_horizon == RIP_SPLIT_HORIZON_POISONED_REVERSE) {
 | 
						||
	    /* 
 | 
						||
	     * We perform split horizon for RIP and connected route. 
 | 
						||
	     * For rip routes, we want to suppress the route if we would
 | 
						||
             * end up sending the route back on the interface that we
 | 
						||
             * learned it from, with a higher metric. For connected routes,
 | 
						||
             * we suppress the route if the prefix is a subset of the
 | 
						||
             * source address that we are going to use for the packet 
 | 
						||
             * (in order to handle the case when multiple subnets are
 | 
						||
             * configured on the same interface).
 | 
						||
             */
 | 
						||
	  if (rinfo->type == ZEBRA_ROUTE_RIP  &&
 | 
						||
	       rinfo->ifindex == ifc->ifp->ifindex)
 | 
						||
	       rinfo->metric_out = RIP_METRIC_INFINITY;
 | 
						||
	  if (rinfo->type == ZEBRA_ROUTE_CONNECT &&
 | 
						||
              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 !v1 && !no_auth */
 | 
						||
            if ( (ri->auth_type != RIP_NO_AUTH) && (version != RIPv1) )
 | 
						||
              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);
 | 
						||
	if (num == rtemax)
 | 
						||
	  {
 | 
						||
	    if (version == RIPv2 && ri->auth_type == RIP_AUTH_MD5)
 | 
						||
              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);
 | 
						||
 | 
						||
	    if (ret >= 0 && IS_RIP_DEBUG_SEND)
 | 
						||
	      rip_packet_dump ((struct rip_packet *)STREAM_DATA (s),
 | 
						||
			       stream_get_endp(s), "SEND");
 | 
						||
	    num = 0;
 | 
						||
	    stream_reset (s);
 | 
						||
	  }
 | 
						||
      }
 | 
						||
 | 
						||
  /* Flush unwritten RTE. */
 | 
						||
  if (num != 0)
 | 
						||
    {
 | 
						||
      if (version == RIPv2 && ri->auth_type == RIP_AUTH_MD5)
 | 
						||
        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);
 | 
						||
 | 
						||
      if (ret >= 0 && IS_RIP_DEBUG_SEND)
 | 
						||
	rip_packet_dump ((struct rip_packet *)STREAM_DATA (s),
 | 
						||
			 stream_get_endp (s), "SEND");
 | 
						||
      num = 0;
 | 
						||
      stream_reset (s);
 | 
						||
    }
 | 
						||
 | 
						||
  /* Statistics updates. */
 | 
						||
  ri->sent_updates++;
 | 
						||
}
 | 
						||
 | 
						||
/* Send RIP packet to the interface. */
 | 
						||
static void
 | 
						||
rip_update_interface (struct connected *ifc, u_char version, int route_type)
 | 
						||
{
 | 
						||
  struct sockaddr_in to;
 | 
						||
 | 
						||
  /* When RIP version is 2 and multicast enable interface. */
 | 
						||
  if (version == RIPv2 && if_is_multicast (ifc->ifp)) 
 | 
						||
    {
 | 
						||
      if (IS_RIP_DEBUG_EVENT)
 | 
						||
	zlog_debug ("multicast announce on %s ", ifc->ifp->name);
 | 
						||
 | 
						||
      rip_output_process (ifc, NULL, route_type, version);
 | 
						||
      return;
 | 
						||
    }
 | 
						||
  
 | 
						||
  /* If we can't send multicast packet, send it with unicast. */
 | 
						||
  if (if_is_broadcast (ifc->ifp) || if_is_pointopoint (ifc->ifp))
 | 
						||
    {
 | 
						||
      if (ifc->address->family == AF_INET)
 | 
						||
        {
 | 
						||
          /* Destination address and port setting. */
 | 
						||
          memset (&to, 0, sizeof (struct sockaddr_in));
 | 
						||
          if (ifc->destination)
 | 
						||
            /* use specified broadcast or peer destination addr */
 | 
						||
            to.sin_addr = ifc->destination->u.prefix4;
 | 
						||
          else if (ifc->address->prefixlen < IPV4_MAX_PREFIXLEN)
 | 
						||
            /* calculate the appropriate broadcast address */
 | 
						||
            to.sin_addr.s_addr =
 | 
						||
              ipv4_broadcast_addr(ifc->address->u.prefix4.s_addr,
 | 
						||
                                  ifc->address->prefixlen);
 | 
						||
	  else
 | 
						||
	    /* do not know where to send the packet */
 | 
						||
	    return;
 | 
						||
          to.sin_port = htons (RIP_PORT_DEFAULT);
 | 
						||
 | 
						||
          if (IS_RIP_DEBUG_EVENT)
 | 
						||
            zlog_debug("%s announce to %s on %s",
 | 
						||
		       CONNECTED_PEER(ifc) ? "unicast" : "broadcast",
 | 
						||
		       inet_ntoa (to.sin_addr), ifc->ifp->name);
 | 
						||
 | 
						||
          rip_output_process (ifc, &to, route_type, version);
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/* Update send to all interface and neighbor. */
 | 
						||
static void
 | 
						||
rip_update_process (int route_type)
 | 
						||
{
 | 
						||
  struct listnode *node;
 | 
						||
  struct listnode *ifnode, *ifnnode;
 | 
						||
  struct connected *connected;
 | 
						||
  struct interface *ifp;
 | 
						||
  struct rip_interface *ri;
 | 
						||
  struct route_node *rp;
 | 
						||
  struct sockaddr_in to;
 | 
						||
  struct prefix_ipv4 *p;
 | 
						||
 | 
						||
  /* Send RIP update to each interface. */
 | 
						||
  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
 | 
						||
    {
 | 
						||
      if (if_is_loopback (ifp))
 | 
						||
	continue;
 | 
						||
 | 
						||
      if (! if_is_operative (ifp))
 | 
						||
	continue;
 | 
						||
 | 
						||
      /* Fetch RIP interface information. */
 | 
						||
      ri = ifp->info;
 | 
						||
 | 
						||
      /* When passive interface is specified, suppress announce to the
 | 
						||
         interface. */
 | 
						||
      if (ri->passive)
 | 
						||
	continue;
 | 
						||
 | 
						||
      if (ri->running)
 | 
						||
	{
 | 
						||
	  /* 
 | 
						||
	   * If there is no version configuration in the interface,
 | 
						||
	   * use rip's version setting. 
 | 
						||
	   */
 | 
						||
	  int vsend = ((ri->ri_send == RI_RIP_UNSPEC) ?
 | 
						||
		       rip->version_send : ri->ri_send);
 | 
						||
 | 
						||
	  if (IS_RIP_DEBUG_EVENT) 
 | 
						||
	    zlog_debug("SEND UPDATE to %s ifindex %d",
 | 
						||
		       (ifp->name ? ifp->name : "_unknown_"), ifp->ifindex);
 | 
						||
 | 
						||
          /* send update on each connected network */
 | 
						||
	  for (ALL_LIST_ELEMENTS (ifp->connected, ifnode, ifnnode, connected))
 | 
						||
	    {
 | 
						||
	      if (connected->address->family == AF_INET)
 | 
						||
	        {
 | 
						||
		  if (vsend & RIPv1)
 | 
						||
		    rip_update_interface (connected, RIPv1, route_type);
 | 
						||
		  if ((vsend & RIPv2) && if_is_multicast(ifp))
 | 
						||
		    rip_update_interface (connected, RIPv2, route_type);
 | 
						||
		}
 | 
						||
	    }
 | 
						||
	}
 | 
						||
    }
 | 
						||
 | 
						||
  /* RIP send updates to each neighbor. */
 | 
						||
  for (rp = route_top (rip->neighbor); rp; rp = route_next (rp))
 | 
						||
    if (rp->info != NULL)
 | 
						||
      {
 | 
						||
	p = (struct prefix_ipv4 *) &rp->p;
 | 
						||
 | 
						||
	ifp = if_lookup_address (p->prefix);
 | 
						||
	if (! ifp)
 | 
						||
	  {
 | 
						||
	    zlog_warn ("Neighbor %s doesnt have connected interface!",
 | 
						||
		       inet_ntoa (p->prefix));
 | 
						||
	    continue;
 | 
						||
	  }
 | 
						||
        
 | 
						||
        if ( (connected = connected_lookup_address (ifp, p->prefix)) == NULL)
 | 
						||
          {
 | 
						||
            zlog_warn ("Neighbor %s doesnt have connected network",
 | 
						||
                       inet_ntoa (p->prefix));
 | 
						||
            continue;
 | 
						||
          }
 | 
						||
        
 | 
						||
	/* Set destination address and port */
 | 
						||
	memset (&to, 0, sizeof (struct sockaddr_in));
 | 
						||
	to.sin_addr = p->prefix;
 | 
						||
	to.sin_port = htons (RIP_PORT_DEFAULT);
 | 
						||
 | 
						||
	/* RIP version is rip's configuration. */
 | 
						||
	rip_output_process (connected, &to, route_type, rip->version_send);
 | 
						||
      }
 | 
						||
}
 | 
						||
 | 
						||
/* RIP's periodical timer. */
 | 
						||
static int
 | 
						||
rip_update (struct thread *t)
 | 
						||
{
 | 
						||
  /* Clear timer pointer. */
 | 
						||
  rip->t_update = NULL;
 | 
						||
 | 
						||
  if (IS_RIP_DEBUG_EVENT)
 | 
						||
    zlog_debug ("update timer fire!");
 | 
						||
 | 
						||
  /* Process update output. */
 | 
						||
  rip_update_process (rip_all_route);
 | 
						||
 | 
						||
  /* Triggered updates may be suppressed if a regular update is due by
 | 
						||
     the time the triggered update would be sent. */
 | 
						||
  if (rip->t_triggered_interval)
 | 
						||
    {
 | 
						||
      thread_cancel (rip->t_triggered_interval);
 | 
						||
      rip->t_triggered_interval = NULL;
 | 
						||
    }
 | 
						||
  rip->trigger = 0;
 | 
						||
 | 
						||
  /* Register myself. */
 | 
						||
  rip_event (RIP_UPDATE_EVENT, 0);
 | 
						||
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* Walk down the RIP routing table then clear changed flag. */
 | 
						||
static void
 | 
						||
rip_clear_changed_flag (void)
 | 
						||
{
 | 
						||
  struct route_node *rp;
 | 
						||
  struct rip_info *rinfo;
 | 
						||
 | 
						||
  for (rp = route_top (rip->table); rp; rp = route_next (rp))
 | 
						||
    if ((rinfo = rp->info) != NULL)
 | 
						||
      if (rinfo->flags & RIP_RTF_CHANGED)
 | 
						||
	rinfo->flags &= ~RIP_RTF_CHANGED;
 | 
						||
}
 | 
						||
 | 
						||
/* Triggered update interval timer. */
 | 
						||
static int
 | 
						||
rip_triggered_interval (struct thread *t)
 | 
						||
{
 | 
						||
  int rip_triggered_update (struct thread *);
 | 
						||
 | 
						||
  rip->t_triggered_interval = NULL;
 | 
						||
 | 
						||
  if (rip->trigger)
 | 
						||
    {
 | 
						||
      rip->trigger = 0;
 | 
						||
      rip_triggered_update (t);
 | 
						||
    }
 | 
						||
  return 0;
 | 
						||
}     
 | 
						||
 | 
						||
/* Execute triggered update. */
 | 
						||
static int
 | 
						||
rip_triggered_update (struct thread *t)
 | 
						||
{
 | 
						||
  int interval;
 | 
						||
 | 
						||
  /* Clear thred pointer. */
 | 
						||
  rip->t_triggered_update = NULL;
 | 
						||
 | 
						||
  /* Cancel interval timer. */
 | 
						||
  if (rip->t_triggered_interval)
 | 
						||
    {
 | 
						||
      thread_cancel (rip->t_triggered_interval);
 | 
						||
      rip->t_triggered_interval = NULL;
 | 
						||
    }
 | 
						||
  rip->trigger = 0;
 | 
						||
 | 
						||
  /* Logging triggered update. */
 | 
						||
  if (IS_RIP_DEBUG_EVENT)
 | 
						||
    zlog_debug ("triggered update!");
 | 
						||
 | 
						||
  /* Split Horizon processing is done when generating triggered
 | 
						||
     updates as well as normal updates (see section 2.6). */
 | 
						||
  rip_update_process (rip_changed_route);
 | 
						||
 | 
						||
  /* Once all of the triggered updates have been generated, the route
 | 
						||
     change flags should be cleared. */
 | 
						||
  rip_clear_changed_flag ();
 | 
						||
 | 
						||
  /* After a triggered update is sent, a timer should be set for a
 | 
						||
   random interval between 1 and 5 seconds.  If other changes that
 | 
						||
   would trigger updates occur before the timer expires, a single
 | 
						||
   update is triggered when the timer expires. */
 | 
						||
  interval = (random () % 5) + 1;
 | 
						||
 | 
						||
  rip->t_triggered_interval = 
 | 
						||
    thread_add_timer (master, rip_triggered_interval, NULL, interval);
 | 
						||
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* Withdraw redistributed route. */
 | 
						||
void
 | 
						||
rip_redistribute_withdraw (int type)
 | 
						||
{
 | 
						||
  struct route_node *rp;
 | 
						||
  struct rip_info *rinfo;
 | 
						||
 | 
						||
  if (!rip)
 | 
						||
    return;
 | 
						||
 | 
						||
  for (rp = route_top (rip->table); rp; rp = route_next (rp))
 | 
						||
    if ((rinfo = rp->info) != NULL)
 | 
						||
      {
 | 
						||
	if (rinfo->type == type
 | 
						||
	    && rinfo->sub_type != RIP_ROUTE_INTERFACE)
 | 
						||
	  {
 | 
						||
	    /* Perform poisoned reverse. */
 | 
						||
	    rinfo->metric = RIP_METRIC_INFINITY;
 | 
						||
	    RIP_TIMER_ON (rinfo->t_garbage_collect, 
 | 
						||
			  rip_garbage_collect, rip->garbage_time);
 | 
						||
	    RIP_TIMER_OFF (rinfo->t_timeout);
 | 
						||
	    rinfo->flags |= RIP_RTF_CHANGED;
 | 
						||
 | 
						||
	    if (IS_RIP_DEBUG_EVENT) {
 | 
						||
              struct prefix_ipv4 *p = (struct prefix_ipv4 *) &rp->p;
 | 
						||
 | 
						||
              zlog_debug ("Poisone %s/%d on the interface %s with an infinity metric [withdraw]",
 | 
						||
                         inet_ntoa(p->prefix), p->prefixlen,
 | 
						||
                         ifindex2ifname(rinfo->ifindex));
 | 
						||
	    }
 | 
						||
 | 
						||
	    rip_event (RIP_TRIGGERED_UPDATE, 0);
 | 
						||
	  }
 | 
						||
      }
 | 
						||
}
 | 
						||
 | 
						||
/* Create new RIP instance and set it to global variable. */
 | 
						||
static int
 | 
						||
rip_create (void)
 | 
						||
{
 | 
						||
  rip = XMALLOC (MTYPE_RIP, sizeof (struct rip));
 | 
						||
  memset (rip, 0, sizeof (struct rip));
 | 
						||
 | 
						||
  /* Set initial value. */
 | 
						||
  rip->version_send = RI_RIP_VERSION_2;
 | 
						||
  rip->version_recv = RI_RIP_VERSION_1_AND_2;
 | 
						||
  rip->update_time = RIP_UPDATE_TIMER_DEFAULT;
 | 
						||
  rip->timeout_time = RIP_TIMEOUT_TIMER_DEFAULT;
 | 
						||
  rip->garbage_time = RIP_GARBAGE_TIMER_DEFAULT;
 | 
						||
  rip->default_metric = RIP_DEFAULT_METRIC_DEFAULT;
 | 
						||
 | 
						||
  /* Initialize RIP routig table. */
 | 
						||
  rip->table = route_table_init ();
 | 
						||
  rip->route = route_table_init ();
 | 
						||
  rip->neighbor = route_table_init ();
 | 
						||
 | 
						||
  /* Make output stream. */
 | 
						||
  rip->obuf = stream_new (1500);
 | 
						||
 | 
						||
  /* Make socket. */
 | 
						||
  rip->sock = rip_create_socket (NULL);
 | 
						||
  if (rip->sock < 0)
 | 
						||
    return rip->sock;
 | 
						||
 | 
						||
  /* Create read and timer thread. */
 | 
						||
  rip_event (RIP_READ, rip->sock);
 | 
						||
  rip_event (RIP_UPDATE_EVENT, 1);
 | 
						||
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* Sned RIP request to the destination. */
 | 
						||
int
 | 
						||
rip_request_send (struct sockaddr_in *to, struct interface *ifp,
 | 
						||
		  u_char version, struct connected *connected)
 | 
						||
{
 | 
						||
  struct rte *rte;
 | 
						||
  struct rip_packet rip_packet;
 | 
						||
  struct listnode *node, *nnode;
 | 
						||
 | 
						||
  memset (&rip_packet, 0, sizeof (rip_packet));
 | 
						||
 | 
						||
  rip_packet.command = RIP_REQUEST;
 | 
						||
  rip_packet.version = version;
 | 
						||
  rte = rip_packet.rte;
 | 
						||
  rte->metric = htonl (RIP_METRIC_INFINITY);
 | 
						||
 | 
						||
  if (connected) 
 | 
						||
    {
 | 
						||
      /* 
 | 
						||
       * connected is only sent for ripv1 case, or when
 | 
						||
       * interface does not support multicast.  Caller loops
 | 
						||
       * over each connected address for this case.
 | 
						||
       */
 | 
						||
      if (rip_send_packet ((u_char *) &rip_packet, sizeof (rip_packet), 
 | 
						||
                            to, connected) != sizeof (rip_packet))
 | 
						||
        return -1;
 | 
						||
      else
 | 
						||
        return sizeof (rip_packet);
 | 
						||
    }
 | 
						||
	
 | 
						||
  /* send request on each connected network */
 | 
						||
  for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, connected))
 | 
						||
    {
 | 
						||
      struct prefix_ipv4 *p;
 | 
						||
 | 
						||
      p = (struct prefix_ipv4 *) connected->address;
 | 
						||
 | 
						||
      if (p->family != AF_INET)
 | 
						||
        continue;
 | 
						||
 | 
						||
      if (rip_send_packet ((u_char *) &rip_packet, sizeof (rip_packet), 
 | 
						||
                            to, connected) != sizeof (rip_packet))
 | 
						||
        return -1;
 | 
						||
    }
 | 
						||
  return sizeof (rip_packet);
 | 
						||
}
 | 
						||
 | 
						||
static int
 | 
						||
rip_update_jitter (unsigned long time)
 | 
						||
{
 | 
						||
#define JITTER_BOUND 4
 | 
						||
  /* We want to get the jitter to +/- 1/JITTER_BOUND the interval.
 | 
						||
     Given that, we cannot let time be less than JITTER_BOUND seconds.
 | 
						||
     The RIPv2 RFC says jitter should be small compared to
 | 
						||
     update_time.  We consider 1/JITTER_BOUND to be small.
 | 
						||
  */
 | 
						||
  
 | 
						||
  int jitter_input = time;
 | 
						||
  int jitter;
 | 
						||
  
 | 
						||
  if (jitter_input < JITTER_BOUND)
 | 
						||
    jitter_input = JITTER_BOUND;
 | 
						||
  
 | 
						||
  jitter = (((rand () % ((jitter_input * 2) + 1)) - jitter_input));  
 | 
						||
 | 
						||
  return jitter/JITTER_BOUND;
 | 
						||
}
 | 
						||
 | 
						||
void
 | 
						||
rip_event (enum rip_event event, int sock)
 | 
						||
{
 | 
						||
  int jitter = 0;
 | 
						||
 | 
						||
  switch (event)
 | 
						||
    {
 | 
						||
    case RIP_READ:
 | 
						||
      rip->t_read = thread_add_read (master, rip_read, NULL, sock);
 | 
						||
      break;
 | 
						||
    case RIP_UPDATE_EVENT:
 | 
						||
      if (rip->t_update)
 | 
						||
	{
 | 
						||
	  thread_cancel (rip->t_update);
 | 
						||
	  rip->t_update = NULL;
 | 
						||
	}
 | 
						||
      jitter = rip_update_jitter (rip->update_time);
 | 
						||
      rip->t_update = 
 | 
						||
	thread_add_timer (master, rip_update, NULL, 
 | 
						||
			  sock ? 2 : rip->update_time + jitter);
 | 
						||
      break;
 | 
						||
    case RIP_TRIGGERED_UPDATE:
 | 
						||
      if (rip->t_triggered_interval)
 | 
						||
	rip->trigger = 1;
 | 
						||
      else if (! rip->t_triggered_update)
 | 
						||
	rip->t_triggered_update = 
 | 
						||
	  thread_add_event (master, rip_triggered_update, NULL, 0);
 | 
						||
      break;
 | 
						||
    default:
 | 
						||
      break;
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
DEFUN (router_rip,
 | 
						||
       router_rip_cmd,
 | 
						||
       "router rip",
 | 
						||
       "Enable a routing process\n"
 | 
						||
       "Routing Information Protocol (RIP)\n")
 | 
						||
{
 | 
						||
  int ret;
 | 
						||
 | 
						||
  /* If rip is not enabled before. */
 | 
						||
  if (! rip)
 | 
						||
    {
 | 
						||
      ret = rip_create ();
 | 
						||
      if (ret < 0)
 | 
						||
	{
 | 
						||
	  zlog_info ("Can't create RIP");
 | 
						||
	  return CMD_WARNING;
 | 
						||
	}
 | 
						||
    }
 | 
						||
  vty->node = RIP_NODE;
 | 
						||
  vty->index = rip;
 | 
						||
 | 
						||
  return CMD_SUCCESS;
 | 
						||
}
 | 
						||
 | 
						||
DEFUN (no_router_rip,
 | 
						||
       no_router_rip_cmd,
 | 
						||
       "no router rip",
 | 
						||
       NO_STR
 | 
						||
       "Enable a routing process\n"
 | 
						||
       "Routing Information Protocol (RIP)\n")
 | 
						||
{
 | 
						||
  if (rip)
 | 
						||
    rip_clean ();
 | 
						||
  return CMD_SUCCESS;
 | 
						||
}
 | 
						||
 | 
						||
DEFUN (rip_version,
 | 
						||
       rip_version_cmd,
 | 
						||
       "version <1-2>",
 | 
						||
       "Set routing protocol version\n"
 | 
						||
       "version\n")
 | 
						||
{
 | 
						||
  int version;
 | 
						||
 | 
						||
  version = atoi (argv[0]);
 | 
						||
  if (version != RIPv1 && version != RIPv2)
 | 
						||
    {
 | 
						||
      vty_out (vty, "invalid rip version %d%s", version,
 | 
						||
	       VTY_NEWLINE);
 | 
						||
      return CMD_WARNING;
 | 
						||
    }
 | 
						||
  rip->version_send = version;
 | 
						||
  rip->version_recv = version;
 | 
						||
 | 
						||
  return CMD_SUCCESS;
 | 
						||
} 
 | 
						||
 | 
						||
DEFUN (no_rip_version,
 | 
						||
       no_rip_version_cmd,
 | 
						||
       "no version",
 | 
						||
       NO_STR
 | 
						||
       "Set routing protocol version\n")
 | 
						||
{
 | 
						||
  /* Set RIP version to the default. */
 | 
						||
  rip->version_send = RI_RIP_VERSION_2;
 | 
						||
  rip->version_recv = RI_RIP_VERSION_1_AND_2;
 | 
						||
 | 
						||
  return CMD_SUCCESS;
 | 
						||
} 
 | 
						||
 | 
						||
ALIAS (no_rip_version,
 | 
						||
       no_rip_version_val_cmd,
 | 
						||
       "no version <1-2>",
 | 
						||
       NO_STR
 | 
						||
       "Set routing protocol version\n"
 | 
						||
       "version\n")
 | 
						||
 | 
						||
DEFUN (rip_route,
 | 
						||
       rip_route_cmd,
 | 
						||
       "route A.B.C.D/M",
 | 
						||
       "RIP static route configuration\n"
 | 
						||
       "IP prefix <network>/<length>\n")
 | 
						||
{
 | 
						||
  int ret;
 | 
						||
  struct prefix_ipv4 p;
 | 
						||
  struct route_node *node;
 | 
						||
 | 
						||
  ret = str2prefix_ipv4 (argv[0], &p);
 | 
						||
  if (ret < 0)
 | 
						||
    {
 | 
						||
      vty_out (vty, "Malformed address%s", VTY_NEWLINE);
 | 
						||
      return CMD_WARNING;
 | 
						||
    }
 | 
						||
  apply_mask_ipv4 (&p);
 | 
						||
 | 
						||
  /* For router rip configuration. */
 | 
						||
  node = route_node_get (rip->route, (struct prefix *) &p);
 | 
						||
 | 
						||
  if (node->info)
 | 
						||
    {
 | 
						||
      vty_out (vty, "There is already same static route.%s", VTY_NEWLINE);
 | 
						||
      route_unlock_node (node);
 | 
						||
      return CMD_WARNING;
 | 
						||
    }
 | 
						||
 | 
						||
  node->info = (char *)"static";
 | 
						||
 | 
						||
  rip_redistribute_add (ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, 0, NULL, 0, 0);
 | 
						||
 | 
						||
  return CMD_SUCCESS;
 | 
						||
}
 | 
						||
 | 
						||
DEFUN (no_rip_route,
 | 
						||
       no_rip_route_cmd,
 | 
						||
       "no route A.B.C.D/M",
 | 
						||
       NO_STR
 | 
						||
       "RIP static route configuration\n"
 | 
						||
       "IP prefix <network>/<length>\n")
 | 
						||
{
 | 
						||
  int ret;
 | 
						||
  struct prefix_ipv4 p;
 | 
						||
  struct route_node *node;
 | 
						||
 | 
						||
  ret = str2prefix_ipv4 (argv[0], &p);
 | 
						||
  if (ret < 0)
 | 
						||
    {
 | 
						||
      vty_out (vty, "Malformed address%s", VTY_NEWLINE);
 | 
						||
      return CMD_WARNING;
 | 
						||
    }
 | 
						||
  apply_mask_ipv4 (&p);
 | 
						||
 | 
						||
  /* For router rip configuration. */
 | 
						||
  node = route_node_lookup (rip->route, (struct prefix *) &p);
 | 
						||
  if (! node)
 | 
						||
    {
 | 
						||
      vty_out (vty, "Can't find route %s.%s", argv[0],
 | 
						||
	       VTY_NEWLINE);
 | 
						||
      return CMD_WARNING;
 | 
						||
    }
 | 
						||
 | 
						||
  rip_redistribute_delete (ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, 0);
 | 
						||
  route_unlock_node (node);
 | 
						||
 | 
						||
  node->info = NULL;
 | 
						||
  route_unlock_node (node);
 | 
						||
 | 
						||
  return CMD_SUCCESS;
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
rip_update_default_metric (void)
 | 
						||
{
 | 
						||
  struct route_node *np;
 | 
						||
  struct rip_info *rinfo;
 | 
						||
 | 
						||
  for (np = route_top (rip->table); np; np = route_next (np))
 | 
						||
    if ((rinfo = np->info) != NULL)
 | 
						||
      if (rinfo->type != ZEBRA_ROUTE_RIP && rinfo->type != ZEBRA_ROUTE_CONNECT)
 | 
						||
        rinfo->metric = rip->default_metric;
 | 
						||
}
 | 
						||
 | 
						||
DEFUN (rip_default_metric,
 | 
						||
       rip_default_metric_cmd,
 | 
						||
       "default-metric <1-16>",
 | 
						||
       "Set a metric of redistribute routes\n"
 | 
						||
       "Default metric\n")
 | 
						||
{
 | 
						||
  if (rip)
 | 
						||
    {
 | 
						||
      rip->default_metric = atoi (argv[0]);
 | 
						||
      /* rip_update_default_metric (); */
 | 
						||
    }
 | 
						||
  return CMD_SUCCESS;
 | 
						||
}
 | 
						||
 | 
						||
DEFUN (no_rip_default_metric,
 | 
						||
       no_rip_default_metric_cmd,
 | 
						||
       "no default-metric",
 | 
						||
       NO_STR
 | 
						||
       "Set a metric of redistribute routes\n"
 | 
						||
       "Default metric\n")
 | 
						||
{
 | 
						||
  if (rip)
 | 
						||
    {
 | 
						||
      rip->default_metric = RIP_DEFAULT_METRIC_DEFAULT;
 | 
						||
      /* rip_update_default_metric (); */
 | 
						||
    }
 | 
						||
  return CMD_SUCCESS;
 | 
						||
}
 | 
						||
 | 
						||
ALIAS (no_rip_default_metric,
 | 
						||
       no_rip_default_metric_val_cmd,
 | 
						||
       "no default-metric <1-16>",
 | 
						||
       NO_STR
 | 
						||
       "Set a metric of redistribute routes\n"
 | 
						||
       "Default metric\n")
 | 
						||
 | 
						||
DEFUN (rip_timers,
 | 
						||
       rip_timers_cmd,
 | 
						||
       "timers basic <5-2147483647> <5-2147483647> <5-2147483647>",
 | 
						||
       "Adjust routing timers\n"
 | 
						||
       "Basic routing protocol update timers\n"
 | 
						||
       "Routing table update timer value in second. Default is 30.\n"
 | 
						||
       "Routing information timeout timer. Default is 180.\n"
 | 
						||
       "Garbage collection timer. Default is 120.\n")
 | 
						||
{
 | 
						||
  unsigned long update;
 | 
						||
  unsigned long timeout;
 | 
						||
  unsigned long garbage;
 | 
						||
  char *endptr = NULL;
 | 
						||
  unsigned long RIP_TIMER_MAX = 2147483647;
 | 
						||
  unsigned long RIP_TIMER_MIN = 5;
 | 
						||
 | 
						||
  update = strtoul (argv[0], &endptr, 10);
 | 
						||
  if (update > RIP_TIMER_MAX || update < RIP_TIMER_MIN || *endptr != '\0')  
 | 
						||
    {
 | 
						||
      vty_out (vty, "update timer value error%s", VTY_NEWLINE);
 | 
						||
      return CMD_WARNING;
 | 
						||
    }
 | 
						||
  
 | 
						||
  timeout = strtoul (argv[1], &endptr, 10);
 | 
						||
  if (timeout > RIP_TIMER_MAX || timeout < RIP_TIMER_MIN || *endptr != '\0') 
 | 
						||
    {
 | 
						||
      vty_out (vty, "timeout timer value error%s", VTY_NEWLINE);
 | 
						||
      return CMD_WARNING;
 | 
						||
    }
 | 
						||
  
 | 
						||
  garbage = strtoul (argv[2], &endptr, 10);
 | 
						||
  if (garbage > RIP_TIMER_MAX || garbage < RIP_TIMER_MIN || *endptr != '\0') 
 | 
						||
    {
 | 
						||
      vty_out (vty, "garbage timer value error%s", VTY_NEWLINE);
 | 
						||
      return CMD_WARNING;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Set each timer value. */
 | 
						||
  rip->update_time = update;
 | 
						||
  rip->timeout_time = timeout;
 | 
						||
  rip->garbage_time = garbage;
 | 
						||
 | 
						||
  /* Reset update timer thread. */
 | 
						||
  rip_event (RIP_UPDATE_EVENT, 0);
 | 
						||
 | 
						||
  return CMD_SUCCESS;
 | 
						||
}
 | 
						||
 | 
						||
DEFUN (no_rip_timers,
 | 
						||
       no_rip_timers_cmd,
 | 
						||
       "no timers basic",
 | 
						||
       NO_STR
 | 
						||
       "Adjust routing timers\n"
 | 
						||
       "Basic routing protocol update timers\n")
 | 
						||
{
 | 
						||
  /* Set each timer value to the default. */
 | 
						||
  rip->update_time = RIP_UPDATE_TIMER_DEFAULT;
 | 
						||
  rip->timeout_time = RIP_TIMEOUT_TIMER_DEFAULT;
 | 
						||
  rip->garbage_time = RIP_GARBAGE_TIMER_DEFAULT;
 | 
						||
 | 
						||
  /* Reset update timer thread. */
 | 
						||
  rip_event (RIP_UPDATE_EVENT, 0);
 | 
						||
 | 
						||
  return CMD_SUCCESS;
 | 
						||
}
 | 
						||
 | 
						||
ALIAS (no_rip_timers,
 | 
						||
       no_rip_timers_val_cmd,
 | 
						||
       "no timers basic <0-65535> <0-65535> <0-65535>",
 | 
						||
       NO_STR
 | 
						||
       "Adjust routing timers\n"
 | 
						||
       "Basic routing protocol update timers\n"
 | 
						||
       "Routing table update timer value in second. Default is 30.\n"
 | 
						||
       "Routing information timeout timer. Default is 180.\n"
 | 
						||
       "Garbage collection timer. Default is 120.\n")
 | 
						||
 | 
						||
 | 
						||
struct route_table *rip_distance_table;
 | 
						||
 | 
						||
struct rip_distance
 | 
						||
{
 | 
						||
  /* Distance value for the IP source prefix. */
 | 
						||
  u_char distance;
 | 
						||
 | 
						||
  /* Name of the access-list to be matched. */
 | 
						||
  char *access_list;
 | 
						||
};
 | 
						||
 | 
						||
static struct rip_distance *
 | 
						||
rip_distance_new (void)
 | 
						||
{
 | 
						||
  struct rip_distance *new;
 | 
						||
  new = XMALLOC (MTYPE_RIP_DISTANCE, sizeof (struct rip_distance));
 | 
						||
  memset (new, 0, sizeof (struct rip_distance));
 | 
						||
  return new;
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
rip_distance_free (struct rip_distance *rdistance)
 | 
						||
{
 | 
						||
  XFREE (MTYPE_RIP_DISTANCE, rdistance);
 | 
						||
}
 | 
						||
 | 
						||
static int
 | 
						||
rip_distance_set (struct vty *vty, const char *distance_str, const char *ip_str,
 | 
						||
		  const char *access_list_str)
 | 
						||
{
 | 
						||
  int ret;
 | 
						||
  struct prefix_ipv4 p;
 | 
						||
  u_char distance;
 | 
						||
  struct route_node *rn;
 | 
						||
  struct rip_distance *rdistance;
 | 
						||
 | 
						||
  ret = str2prefix_ipv4 (ip_str, &p);
 | 
						||
  if (ret == 0)
 | 
						||
    {
 | 
						||
      vty_out (vty, "Malformed prefix%s", VTY_NEWLINE);
 | 
						||
      return CMD_WARNING;
 | 
						||
    }
 | 
						||
 | 
						||
  distance = atoi (distance_str);
 | 
						||
 | 
						||
  /* Get RIP distance node. */
 | 
						||
  rn = route_node_get (rip_distance_table, (struct prefix *) &p);
 | 
						||
  if (rn->info)
 | 
						||
    {
 | 
						||
      rdistance = rn->info;
 | 
						||
      route_unlock_node (rn);
 | 
						||
    }
 | 
						||
  else
 | 
						||
    {
 | 
						||
      rdistance = rip_distance_new ();
 | 
						||
      rn->info = rdistance;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Set distance value. */
 | 
						||
  rdistance->distance = distance;
 | 
						||
 | 
						||
  /* Reset access-list configuration. */
 | 
						||
  if (rdistance->access_list)
 | 
						||
    {
 | 
						||
      free (rdistance->access_list);
 | 
						||
      rdistance->access_list = NULL;
 | 
						||
    }
 | 
						||
  if (access_list_str)
 | 
						||
    rdistance->access_list = strdup (access_list_str);
 | 
						||
 | 
						||
  return CMD_SUCCESS;
 | 
						||
}
 | 
						||
 | 
						||
static int
 | 
						||
rip_distance_unset (struct vty *vty, const char *distance_str,
 | 
						||
		    const char *ip_str, const char *access_list_str)
 | 
						||
{
 | 
						||
  int ret;
 | 
						||
  struct prefix_ipv4 p;
 | 
						||
  u_char distance;
 | 
						||
  struct route_node *rn;
 | 
						||
  struct rip_distance *rdistance;
 | 
						||
 | 
						||
  ret = str2prefix_ipv4 (ip_str, &p);
 | 
						||
  if (ret == 0)
 | 
						||
    {
 | 
						||
      vty_out (vty, "Malformed prefix%s", VTY_NEWLINE);
 | 
						||
      return CMD_WARNING;
 | 
						||
    }
 | 
						||
 | 
						||
  distance = atoi (distance_str);
 | 
						||
 | 
						||
  rn = route_node_lookup (rip_distance_table, (struct prefix *)&p);
 | 
						||
  if (! rn)
 | 
						||
    {
 | 
						||
      vty_out (vty, "Can't find specified prefix%s", VTY_NEWLINE);
 | 
						||
      return CMD_WARNING;
 | 
						||
    }
 | 
						||
 | 
						||
  rdistance = rn->info;
 | 
						||
 | 
						||
  if (rdistance->access_list)
 | 
						||
    free (rdistance->access_list);
 | 
						||
  rip_distance_free (rdistance);
 | 
						||
 | 
						||
  rn->info = NULL;
 | 
						||
  route_unlock_node (rn);
 | 
						||
  route_unlock_node (rn);
 | 
						||
 | 
						||
  return CMD_SUCCESS;
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
rip_distance_reset (void)
 | 
						||
{
 | 
						||
  struct route_node *rn;
 | 
						||
  struct rip_distance *rdistance;
 | 
						||
 | 
						||
  for (rn = route_top (rip_distance_table); rn; rn = route_next (rn))
 | 
						||
    if ((rdistance = rn->info) != NULL)
 | 
						||
      {
 | 
						||
	if (rdistance->access_list)
 | 
						||
	  free (rdistance->access_list);
 | 
						||
	rip_distance_free (rdistance);
 | 
						||
	rn->info = NULL;
 | 
						||
	route_unlock_node (rn);
 | 
						||
      }
 | 
						||
}
 | 
						||
 | 
						||
/* Apply RIP information to distance method. */
 | 
						||
u_char
 | 
						||
rip_distance_apply (struct rip_info *rinfo)
 | 
						||
{
 | 
						||
  struct route_node *rn;
 | 
						||
  struct prefix_ipv4 p;
 | 
						||
  struct rip_distance *rdistance;
 | 
						||
  struct access_list *alist;
 | 
						||
 | 
						||
  if (! rip)
 | 
						||
    return 0;
 | 
						||
 | 
						||
  memset (&p, 0, sizeof (struct prefix_ipv4));
 | 
						||
  p.family = AF_INET;
 | 
						||
  p.prefix = rinfo->from;
 | 
						||
  p.prefixlen = IPV4_MAX_BITLEN;
 | 
						||
 | 
						||
  /* Check source address. */
 | 
						||
  rn = route_node_match (rip_distance_table, (struct prefix *) &p);
 | 
						||
  if (rn)
 | 
						||
    {
 | 
						||
      rdistance = rn->info;
 | 
						||
      route_unlock_node (rn);
 | 
						||
 | 
						||
      if (rdistance->access_list)
 | 
						||
	{
 | 
						||
	  alist = access_list_lookup (AFI_IP, rdistance->access_list);
 | 
						||
	  if (alist == NULL)
 | 
						||
	    return 0;
 | 
						||
	  if (access_list_apply (alist, &rinfo->rp->p) == FILTER_DENY)
 | 
						||
	    return 0;
 | 
						||
 | 
						||
	  return rdistance->distance;
 | 
						||
	}
 | 
						||
      else
 | 
						||
	return rdistance->distance;
 | 
						||
    }
 | 
						||
 | 
						||
  if (rip->distance)
 | 
						||
    return rip->distance;
 | 
						||
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
rip_distance_show (struct vty *vty)
 | 
						||
{
 | 
						||
  struct route_node *rn;
 | 
						||
  struct rip_distance *rdistance;
 | 
						||
  int header = 1;
 | 
						||
  char buf[BUFSIZ];
 | 
						||
  
 | 
						||
  vty_out (vty, "  Distance: (default is %d)%s",
 | 
						||
	   rip->distance ? rip->distance :ZEBRA_RIP_DISTANCE_DEFAULT,
 | 
						||
	   VTY_NEWLINE);
 | 
						||
 | 
						||
  for (rn = route_top (rip_distance_table); rn; rn = route_next (rn))
 | 
						||
    if ((rdistance = rn->info) != NULL)
 | 
						||
      {
 | 
						||
	if (header)
 | 
						||
	  {
 | 
						||
	    vty_out (vty, "    Address           Distance  List%s",
 | 
						||
		     VTY_NEWLINE);
 | 
						||
	    header = 0;
 | 
						||
	  }
 | 
						||
	sprintf (buf, "%s/%d", inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen);
 | 
						||
	vty_out (vty, "    %-20s  %4d  %s%s",
 | 
						||
		 buf, rdistance->distance,
 | 
						||
		 rdistance->access_list ? rdistance->access_list : "",
 | 
						||
		 VTY_NEWLINE);
 | 
						||
      }
 | 
						||
}
 | 
						||
 | 
						||
DEFUN (rip_distance,
 | 
						||
       rip_distance_cmd,
 | 
						||
       "distance <1-255>",
 | 
						||
       "Administrative distance\n"
 | 
						||
       "Distance value\n")
 | 
						||
{
 | 
						||
  rip->distance = atoi (argv[0]);
 | 
						||
  return CMD_SUCCESS;
 | 
						||
}
 | 
						||
 | 
						||
DEFUN (no_rip_distance,
 | 
						||
       no_rip_distance_cmd,
 | 
						||
       "no distance <1-255>",
 | 
						||
       NO_STR
 | 
						||
       "Administrative distance\n"
 | 
						||
       "Distance value\n")
 | 
						||
{
 | 
						||
  rip->distance = 0;
 | 
						||
  return CMD_SUCCESS;
 | 
						||
}
 | 
						||
 | 
						||
DEFUN (rip_distance_source,
 | 
						||
       rip_distance_source_cmd,
 | 
						||
       "distance <1-255> A.B.C.D/M",
 | 
						||
       "Administrative distance\n"
 | 
						||
       "Distance value\n"
 | 
						||
       "IP source prefix\n")
 | 
						||
{
 | 
						||
  rip_distance_set (vty, argv[0], argv[1], NULL);
 | 
						||
  return CMD_SUCCESS;
 | 
						||
}
 | 
						||
 | 
						||
DEFUN (no_rip_distance_source,
 | 
						||
       no_rip_distance_source_cmd,
 | 
						||
       "no distance <1-255> A.B.C.D/M",
 | 
						||
       NO_STR
 | 
						||
       "Administrative distance\n"
 | 
						||
       "Distance value\n"
 | 
						||
       "IP source prefix\n")
 | 
						||
{
 | 
						||
  rip_distance_unset (vty, argv[0], argv[1], NULL);
 | 
						||
  return CMD_SUCCESS;
 | 
						||
}
 | 
						||
 | 
						||
DEFUN (rip_distance_source_access_list,
 | 
						||
       rip_distance_source_access_list_cmd,
 | 
						||
       "distance <1-255> A.B.C.D/M WORD",
 | 
						||
       "Administrative distance\n"
 | 
						||
       "Distance value\n"
 | 
						||
       "IP source prefix\n"
 | 
						||
       "Access list name\n")
 | 
						||
{
 | 
						||
  rip_distance_set (vty, argv[0], argv[1], argv[2]);
 | 
						||
  return CMD_SUCCESS;
 | 
						||
}
 | 
						||
 | 
						||
DEFUN (no_rip_distance_source_access_list,
 | 
						||
       no_rip_distance_source_access_list_cmd,
 | 
						||
       "no distance <1-255> A.B.C.D/M WORD",
 | 
						||
       NO_STR
 | 
						||
       "Administrative distance\n"
 | 
						||
       "Distance value\n"
 | 
						||
       "IP source prefix\n"
 | 
						||
       "Access list name\n")
 | 
						||
{
 | 
						||
  rip_distance_unset (vty, argv[0], argv[1], argv[2]);
 | 
						||
  return CMD_SUCCESS;
 | 
						||
}
 | 
						||
 | 
						||
/* Print out routes update time. */
 | 
						||
static void
 | 
						||
rip_vty_out_uptime (struct vty *vty, struct rip_info *rinfo)
 | 
						||
{
 | 
						||
  time_t clock;
 | 
						||
  struct tm *tm;
 | 
						||
#define TIME_BUF 25
 | 
						||
  char timebuf [TIME_BUF];
 | 
						||
  struct thread *thread;
 | 
						||
 | 
						||
  if ((thread = rinfo->t_timeout) != NULL)
 | 
						||
    {
 | 
						||
      clock = thread_timer_remain_second (thread);
 | 
						||
      tm = gmtime (&clock);
 | 
						||
      strftime (timebuf, TIME_BUF, "%M:%S", tm);
 | 
						||
      vty_out (vty, "%5s", timebuf);
 | 
						||
    }
 | 
						||
  else if ((thread = rinfo->t_garbage_collect) != NULL)
 | 
						||
    {
 | 
						||
      clock = thread_timer_remain_second (thread);
 | 
						||
      tm = gmtime (&clock);
 | 
						||
      strftime (timebuf, TIME_BUF, "%M:%S", tm);
 | 
						||
      vty_out (vty, "%5s", timebuf);
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
static const char *
 | 
						||
rip_route_type_print (int sub_type)
 | 
						||
{
 | 
						||
  switch (sub_type)
 | 
						||
    {
 | 
						||
      case RIP_ROUTE_RTE:
 | 
						||
	return "n";
 | 
						||
      case RIP_ROUTE_STATIC:
 | 
						||
	return "s";
 | 
						||
      case RIP_ROUTE_DEFAULT:
 | 
						||
	return "d";
 | 
						||
      case RIP_ROUTE_REDISTRIBUTE:
 | 
						||
	return "r";
 | 
						||
      case RIP_ROUTE_INTERFACE:
 | 
						||
	return "i";
 | 
						||
      default:
 | 
						||
	return "?";
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
DEFUN (show_ip_rip,
 | 
						||
       show_ip_rip_cmd,
 | 
						||
       "show ip rip",
 | 
						||
       SHOW_STR
 | 
						||
       IP_STR
 | 
						||
       "Show RIP routes\n")
 | 
						||
{
 | 
						||
  struct route_node *np;
 | 
						||
  struct rip_info *rinfo;
 | 
						||
 | 
						||
  if (! rip)
 | 
						||
    return CMD_SUCCESS;
 | 
						||
 | 
						||
  vty_out (vty, "Codes: R - RIP, C - connected, S - Static, O - OSPF, B - BGP%s"
 | 
						||
	   "Sub-codes:%s"
 | 
						||
           "      (n) - normal, (s) - static, (d) - default, (r) - redistribute,%s"
 | 
						||
	   "      (i) - interface%s%s"
 | 
						||
	   "     Network            Next Hop         Metric From            Tag Time%s",
 | 
						||
	   VTY_NEWLINE, VTY_NEWLINE,  VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
 | 
						||
  
 | 
						||
  for (np = route_top (rip->table); np; np = route_next (np))
 | 
						||
    if ((rinfo = np->info) != NULL)
 | 
						||
      {
 | 
						||
	int len;
 | 
						||
 | 
						||
	len = vty_out (vty, "%c(%s) %s/%d",
 | 
						||
		       /* np->lock, For debugging. */
 | 
						||
		       zebra_route_char(rinfo->type),
 | 
						||
		       rip_route_type_print (rinfo->sub_type),
 | 
						||
		       inet_ntoa (np->p.u.prefix4), np->p.prefixlen);
 | 
						||
	
 | 
						||
	len = 24 - len;
 | 
						||
 | 
						||
	if (len > 0)
 | 
						||
	  vty_out (vty, "%*s", len, " ");
 | 
						||
 | 
						||
        if (rinfo->nexthop.s_addr) 
 | 
						||
	  vty_out (vty, "%-20s %2d ", inet_ntoa (rinfo->nexthop),
 | 
						||
		   rinfo->metric);
 | 
						||
        else
 | 
						||
	  vty_out (vty, "0.0.0.0              %2d ", rinfo->metric);
 | 
						||
 | 
						||
	/* Route which exist in kernel routing table. */
 | 
						||
	if ((rinfo->type == ZEBRA_ROUTE_RIP) && 
 | 
						||
	    (rinfo->sub_type == RIP_ROUTE_RTE))
 | 
						||
	  {
 | 
						||
	    vty_out (vty, "%-15s ", inet_ntoa (rinfo->from));
 | 
						||
	    vty_out (vty, "%3d ", rinfo->tag);
 | 
						||
	    rip_vty_out_uptime (vty, rinfo);
 | 
						||
	  }
 | 
						||
	else if (rinfo->metric == RIP_METRIC_INFINITY)
 | 
						||
	  {
 | 
						||
	    vty_out (vty, "self            ");
 | 
						||
	    vty_out (vty, "%3d ", rinfo->tag);
 | 
						||
	    rip_vty_out_uptime (vty, rinfo);
 | 
						||
	  }
 | 
						||
	else
 | 
						||
	  {
 | 
						||
	    if (rinfo->external_metric)
 | 
						||
	      {
 | 
						||
	        len = vty_out (vty, "self (%s:%d)", 
 | 
						||
			       zebra_route_string(rinfo->type),
 | 
						||
	                       rinfo->external_metric);
 | 
						||
	        len = 16 - len;
 | 
						||
	        if (len > 0)
 | 
						||
	          vty_out (vty, "%*s", len, " ");
 | 
						||
	      }
 | 
						||
	    else
 | 
						||
	      vty_out (vty, "self            ");
 | 
						||
	    vty_out (vty, "%3d", rinfo->tag);
 | 
						||
	  }
 | 
						||
 | 
						||
	vty_out (vty, "%s", VTY_NEWLINE);
 | 
						||
      }
 | 
						||
  return CMD_SUCCESS;
 | 
						||
}
 | 
						||
 | 
						||
/* Vincent: formerly, it was show_ip_protocols_rip: "show ip protocols" */
 | 
						||
DEFUN (show_ip_rip_status,
 | 
						||
       show_ip_rip_status_cmd,
 | 
						||
       "show ip rip status",
 | 
						||
       SHOW_STR
 | 
						||
       IP_STR
 | 
						||
       "Show RIP routes\n"
 | 
						||
       "IP routing protocol process parameters and statistics\n")
 | 
						||
{
 | 
						||
  struct listnode *node;
 | 
						||
  struct interface *ifp;
 | 
						||
  struct rip_interface *ri;
 | 
						||
  extern const struct message ri_version_msg[];
 | 
						||
  const char *send_version;
 | 
						||
  const char *receive_version;
 | 
						||
 | 
						||
  if (! rip)
 | 
						||
    return CMD_SUCCESS;
 | 
						||
 | 
						||
  vty_out (vty, "Routing Protocol is \"rip\"%s", VTY_NEWLINE);
 | 
						||
  vty_out (vty, "  Sending updates every %ld seconds with +/-50%%,",
 | 
						||
	   rip->update_time);
 | 
						||
  vty_out (vty, " next due in %lu seconds%s", 
 | 
						||
	   thread_timer_remain_second(rip->t_update),
 | 
						||
	   VTY_NEWLINE);
 | 
						||
  vty_out (vty, "  Timeout after %ld seconds,", rip->timeout_time);
 | 
						||
  vty_out (vty, " garbage collect after %ld seconds%s", rip->garbage_time,
 | 
						||
	   VTY_NEWLINE);
 | 
						||
 | 
						||
  /* Filtering status show. */
 | 
						||
  config_show_distribute (vty);
 | 
						||
		 
 | 
						||
  /* Default metric information. */
 | 
						||
  vty_out (vty, "  Default redistribution metric is %d%s",
 | 
						||
	   rip->default_metric, VTY_NEWLINE);
 | 
						||
 | 
						||
  /* Redistribute information. */
 | 
						||
  vty_out (vty, "  Redistributing:");
 | 
						||
  config_write_rip_redistribute (vty, 0);
 | 
						||
  vty_out (vty, "%s", VTY_NEWLINE);
 | 
						||
 | 
						||
  vty_out (vty, "  Default version control: send version %s,",
 | 
						||
	   lookup(ri_version_msg,rip->version_send));
 | 
						||
  if (rip->version_recv == RI_RIP_VERSION_1_AND_2)
 | 
						||
    vty_out (vty, " receive any version %s", VTY_NEWLINE);
 | 
						||
  else
 | 
						||
    vty_out (vty, " receive version %s %s",
 | 
						||
	     lookup(ri_version_msg,rip->version_recv), VTY_NEWLINE);
 | 
						||
 | 
						||
  vty_out (vty, "    Interface        Send  Recv   Key-chain%s", VTY_NEWLINE);
 | 
						||
 | 
						||
  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
 | 
						||
    {
 | 
						||
      ri = ifp->info;
 | 
						||
 | 
						||
      if (ri->enable_network || ri->enable_interface)
 | 
						||
	{
 | 
						||
	  if (ri->ri_send == RI_RIP_UNSPEC)
 | 
						||
	    send_version = lookup (ri_version_msg, rip->version_send);
 | 
						||
	  else
 | 
						||
	    send_version = lookup (ri_version_msg, ri->ri_send);
 | 
						||
 | 
						||
	  if (ri->ri_receive == RI_RIP_UNSPEC)
 | 
						||
	    receive_version = lookup (ri_version_msg, rip->version_recv);
 | 
						||
	  else
 | 
						||
	    receive_version = lookup (ri_version_msg, ri->ri_receive);
 | 
						||
	
 | 
						||
	  vty_out (vty, "    %-17s%-3s   %-3s    %s%s", ifp->name,
 | 
						||
		   send_version,
 | 
						||
		   receive_version,
 | 
						||
		   ri->key_chain ? ri->key_chain : "",
 | 
						||
		   VTY_NEWLINE);
 | 
						||
	}
 | 
						||
    }
 | 
						||
 | 
						||
  vty_out (vty, "  Routing for Networks:%s", VTY_NEWLINE);
 | 
						||
  config_write_rip_network (vty, 0);  
 | 
						||
 | 
						||
  {
 | 
						||
    int found_passive = 0;
 | 
						||
    for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
 | 
						||
      {
 | 
						||
	ri = ifp->info;
 | 
						||
 | 
						||
	if ((ri->enable_network || ri->enable_interface) && ri->passive)
 | 
						||
	  {
 | 
						||
	    if (!found_passive)
 | 
						||
	      {
 | 
						||
		vty_out (vty, "  Passive Interface(s):%s", VTY_NEWLINE);
 | 
						||
		found_passive = 1;
 | 
						||
	      }
 | 
						||
	    vty_out (vty, "    %s%s", ifp->name, VTY_NEWLINE);
 | 
						||
	  }
 | 
						||
      }
 | 
						||
  }
 | 
						||
 | 
						||
  vty_out (vty, "  Routing Information Sources:%s", VTY_NEWLINE);
 | 
						||
  vty_out (vty, "    Gateway          BadPackets BadRoutes  Distance Last Update%s", VTY_NEWLINE);
 | 
						||
  rip_peer_display (vty);
 | 
						||
 | 
						||
  rip_distance_show (vty);
 | 
						||
 | 
						||
  return CMD_SUCCESS;
 | 
						||
}
 | 
						||
 | 
						||
/* RIP configuration write function. */
 | 
						||
static int
 | 
						||
config_write_rip (struct vty *vty)
 | 
						||
{
 | 
						||
  int write = 0;
 | 
						||
  struct route_node *rn;
 | 
						||
  struct rip_distance *rdistance;
 | 
						||
 | 
						||
  if (rip)
 | 
						||
    {
 | 
						||
      /* Router RIP statement. */
 | 
						||
      vty_out (vty, "router rip%s", VTY_NEWLINE);
 | 
						||
      write++;
 | 
						||
  
 | 
						||
      /* RIP version statement.  Default is RIP version 2. */
 | 
						||
      if (rip->version_send != RI_RIP_VERSION_2
 | 
						||
	  || rip->version_recv != RI_RIP_VERSION_1_AND_2)
 | 
						||
	vty_out (vty, " version %d%s", rip->version_send,
 | 
						||
		 VTY_NEWLINE);
 | 
						||
 
 | 
						||
      /* RIP timer configuration. */
 | 
						||
      if (rip->update_time != RIP_UPDATE_TIMER_DEFAULT 
 | 
						||
	  || rip->timeout_time != RIP_TIMEOUT_TIMER_DEFAULT 
 | 
						||
	  || rip->garbage_time != RIP_GARBAGE_TIMER_DEFAULT)
 | 
						||
	vty_out (vty, " timers basic %lu %lu %lu%s",
 | 
						||
		 rip->update_time,
 | 
						||
		 rip->timeout_time,
 | 
						||
		 rip->garbage_time,
 | 
						||
		 VTY_NEWLINE);
 | 
						||
 | 
						||
      /* Default information configuration. */
 | 
						||
      if (rip->default_information)
 | 
						||
	{
 | 
						||
	  if (rip->default_information_route_map)
 | 
						||
	    vty_out (vty, " default-information originate route-map %s%s",
 | 
						||
		     rip->default_information_route_map, VTY_NEWLINE);
 | 
						||
	  else
 | 
						||
	    vty_out (vty, " default-information originate%s",
 | 
						||
		     VTY_NEWLINE);
 | 
						||
	}
 | 
						||
 | 
						||
      /* Redistribute configuration. */
 | 
						||
      config_write_rip_redistribute (vty, 1);
 | 
						||
 | 
						||
      /* RIP offset-list configuration. */
 | 
						||
      config_write_rip_offset_list (vty);
 | 
						||
 | 
						||
      /* RIP enabled network and interface configuration. */
 | 
						||
      config_write_rip_network (vty, 1);
 | 
						||
			
 | 
						||
      /* RIP default metric configuration */
 | 
						||
      if (rip->default_metric != RIP_DEFAULT_METRIC_DEFAULT)
 | 
						||
        vty_out (vty, " default-metric %d%s",
 | 
						||
		 rip->default_metric, VTY_NEWLINE);
 | 
						||
 | 
						||
      /* Distribute configuration. */
 | 
						||
      write += config_write_distribute (vty);
 | 
						||
 | 
						||
      /* Interface routemap configuration */
 | 
						||
      write += config_write_if_rmap (vty);
 | 
						||
 | 
						||
      /* Distance configuration. */
 | 
						||
      if (rip->distance)
 | 
						||
	vty_out (vty, " distance %d%s", rip->distance, VTY_NEWLINE);
 | 
						||
 | 
						||
      /* RIP source IP prefix distance configuration. */
 | 
						||
      for (rn = route_top (rip_distance_table); rn; rn = route_next (rn))
 | 
						||
	if ((rdistance = rn->info) != NULL)
 | 
						||
	  vty_out (vty, " distance %d %s/%d %s%s", rdistance->distance,
 | 
						||
		   inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen,
 | 
						||
		   rdistance->access_list ? rdistance->access_list : "",
 | 
						||
		   VTY_NEWLINE);
 | 
						||
 | 
						||
      /* RIP static route configuration. */
 | 
						||
      for (rn = route_top (rip->route); rn; rn = route_next (rn))
 | 
						||
	if (rn->info)
 | 
						||
	  vty_out (vty, " route %s/%d%s", 
 | 
						||
		   inet_ntoa (rn->p.u.prefix4),
 | 
						||
		   rn->p.prefixlen,
 | 
						||
		   VTY_NEWLINE);
 | 
						||
 | 
						||
    }
 | 
						||
  return write;
 | 
						||
}
 | 
						||
 | 
						||
/* RIP node structure. */
 | 
						||
struct cmd_node rip_node =
 | 
						||
{
 | 
						||
  RIP_NODE,
 | 
						||
  "%s(config-router)# ",
 | 
						||
  1
 | 
						||
};
 | 
						||
 | 
						||
/* Distribute-list update functions. */
 | 
						||
static void
 | 
						||
rip_distribute_update (struct distribute *dist)
 | 
						||
{
 | 
						||
  struct interface *ifp;
 | 
						||
  struct rip_interface *ri;
 | 
						||
  struct access_list *alist;
 | 
						||
  struct prefix_list *plist;
 | 
						||
 | 
						||
  if (! dist->ifname)
 | 
						||
    return;
 | 
						||
 | 
						||
  ifp = if_lookup_by_name (dist->ifname);
 | 
						||
  if (ifp == NULL)
 | 
						||
    return;
 | 
						||
 | 
						||
  ri = ifp->info;
 | 
						||
 | 
						||
  if (dist->list[DISTRIBUTE_IN])
 | 
						||
    {
 | 
						||
      alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_IN]);
 | 
						||
      if (alist)
 | 
						||
	ri->list[RIP_FILTER_IN] = alist;
 | 
						||
      else
 | 
						||
	ri->list[RIP_FILTER_IN] = NULL;
 | 
						||
    }
 | 
						||
  else
 | 
						||
    ri->list[RIP_FILTER_IN] = NULL;
 | 
						||
 | 
						||
  if (dist->list[DISTRIBUTE_OUT])
 | 
						||
    {
 | 
						||
      alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_OUT]);
 | 
						||
      if (alist)
 | 
						||
	ri->list[RIP_FILTER_OUT] = alist;
 | 
						||
      else
 | 
						||
	ri->list[RIP_FILTER_OUT] = NULL;
 | 
						||
    }
 | 
						||
  else
 | 
						||
    ri->list[RIP_FILTER_OUT] = NULL;
 | 
						||
 | 
						||
  if (dist->prefix[DISTRIBUTE_IN])
 | 
						||
    {
 | 
						||
      plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_IN]);
 | 
						||
      if (plist)
 | 
						||
	ri->prefix[RIP_FILTER_IN] = plist;
 | 
						||
      else
 | 
						||
	ri->prefix[RIP_FILTER_IN] = NULL;
 | 
						||
    }
 | 
						||
  else
 | 
						||
    ri->prefix[RIP_FILTER_IN] = NULL;
 | 
						||
 | 
						||
  if (dist->prefix[DISTRIBUTE_OUT])
 | 
						||
    {
 | 
						||
      plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_OUT]);
 | 
						||
      if (plist)
 | 
						||
	ri->prefix[RIP_FILTER_OUT] = plist;
 | 
						||
      else
 | 
						||
	ri->prefix[RIP_FILTER_OUT] = NULL;
 | 
						||
    }
 | 
						||
  else
 | 
						||
    ri->prefix[RIP_FILTER_OUT] = NULL;
 | 
						||
}
 | 
						||
 | 
						||
void
 | 
						||
rip_distribute_update_interface (struct interface *ifp)
 | 
						||
{
 | 
						||
  struct distribute *dist;
 | 
						||
 | 
						||
  dist = distribute_lookup (ifp->name);
 | 
						||
  if (dist)
 | 
						||
    rip_distribute_update (dist);
 | 
						||
}
 | 
						||
 | 
						||
/* Update all interface's distribute list. */
 | 
						||
/* ARGSUSED */
 | 
						||
static void
 | 
						||
rip_distribute_update_all (struct prefix_list *notused)
 | 
						||
{
 | 
						||
  struct interface *ifp;
 | 
						||
  struct listnode *node, *nnode;
 | 
						||
 | 
						||
  for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp))
 | 
						||
    rip_distribute_update_interface (ifp);
 | 
						||
}
 | 
						||
/* ARGSUSED */
 | 
						||
static void
 | 
						||
rip_distribute_update_all_wrapper(struct access_list *notused)
 | 
						||
{
 | 
						||
        rip_distribute_update_all(NULL);
 | 
						||
}
 | 
						||
 | 
						||
/* Delete all added rip route. */
 | 
						||
void
 | 
						||
rip_clean (void)
 | 
						||
{
 | 
						||
  int i;
 | 
						||
  struct route_node *rp;
 | 
						||
  struct rip_info *rinfo;
 | 
						||
 | 
						||
  if (rip)
 | 
						||
    {
 | 
						||
      /* Clear RIP routes */
 | 
						||
      for (rp = route_top (rip->table); rp; rp = route_next (rp))
 | 
						||
	if ((rinfo = rp->info) != NULL)
 | 
						||
	  {
 | 
						||
	    if (rinfo->type == ZEBRA_ROUTE_RIP &&
 | 
						||
		rinfo->sub_type == RIP_ROUTE_RTE)
 | 
						||
	      rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rp->p,
 | 
						||
				     &rinfo->nexthop, rinfo->metric);
 | 
						||
	
 | 
						||
	    RIP_TIMER_OFF (rinfo->t_timeout);
 | 
						||
	    RIP_TIMER_OFF (rinfo->t_garbage_collect);
 | 
						||
 | 
						||
	    rp->info = NULL;
 | 
						||
	    route_unlock_node (rp);
 | 
						||
 | 
						||
	    rip_info_free (rinfo);
 | 
						||
	  }
 | 
						||
 | 
						||
      /* Cancel RIP related timers. */
 | 
						||
      RIP_TIMER_OFF (rip->t_update);
 | 
						||
      RIP_TIMER_OFF (rip->t_triggered_update);
 | 
						||
      RIP_TIMER_OFF (rip->t_triggered_interval);
 | 
						||
 | 
						||
      /* Cancel read thread. */
 | 
						||
      if (rip->t_read)
 | 
						||
	{
 | 
						||
	  thread_cancel (rip->t_read);
 | 
						||
	  rip->t_read = NULL;
 | 
						||
	}
 | 
						||
 | 
						||
      /* Close RIP socket. */
 | 
						||
      if (rip->sock >= 0)
 | 
						||
	{
 | 
						||
	  close (rip->sock);
 | 
						||
	  rip->sock = -1;
 | 
						||
	}
 | 
						||
 | 
						||
      /* Static RIP route configuration. */
 | 
						||
      for (rp = route_top (rip->route); rp; rp = route_next (rp))
 | 
						||
	if (rp->info)
 | 
						||
	  {
 | 
						||
	    rp->info = NULL;
 | 
						||
	    route_unlock_node (rp);
 | 
						||
	  }
 | 
						||
 | 
						||
      /* RIP neighbor configuration. */
 | 
						||
      for (rp = route_top (rip->neighbor); rp; rp = route_next (rp))
 | 
						||
	if (rp->info)
 | 
						||
	  {
 | 
						||
	    rp->info = NULL;
 | 
						||
	    route_unlock_node (rp);
 | 
						||
	  }
 | 
						||
 | 
						||
      /* Redistribute related clear. */
 | 
						||
      if (rip->default_information_route_map)
 | 
						||
	free (rip->default_information_route_map);
 | 
						||
 | 
						||
      for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
 | 
						||
	if (rip->route_map[i].name)
 | 
						||
	  free (rip->route_map[i].name);
 | 
						||
 | 
						||
      XFREE (MTYPE_ROUTE_TABLE, rip->table);
 | 
						||
      XFREE (MTYPE_ROUTE_TABLE, rip->route);
 | 
						||
      XFREE (MTYPE_ROUTE_TABLE, rip->neighbor);
 | 
						||
      
 | 
						||
      XFREE (MTYPE_RIP, rip);
 | 
						||
      rip = NULL;
 | 
						||
    }
 | 
						||
 | 
						||
  rip_clean_network ();
 | 
						||
  rip_passive_nondefault_clean ();
 | 
						||
  rip_offset_clean ();
 | 
						||
  rip_interface_clean ();
 | 
						||
  rip_distance_reset ();
 | 
						||
  rip_redistribute_clean ();
 | 
						||
}
 | 
						||
 | 
						||
/* Reset all values to the default settings. */
 | 
						||
void
 | 
						||
rip_reset (void)
 | 
						||
{
 | 
						||
  /* Reset global counters. */
 | 
						||
  rip_global_route_changes = 0;
 | 
						||
  rip_global_queries = 0;
 | 
						||
 | 
						||
  /* Call ripd related reset functions. */
 | 
						||
  rip_debug_reset ();
 | 
						||
  rip_route_map_reset ();
 | 
						||
 | 
						||
  /* Call library reset functions. */
 | 
						||
  vty_reset ();
 | 
						||
  access_list_reset ();
 | 
						||
  prefix_list_reset ();
 | 
						||
 | 
						||
  distribute_list_reset ();
 | 
						||
 | 
						||
  rip_interface_reset ();
 | 
						||
  rip_distance_reset ();
 | 
						||
 | 
						||
  rip_zclient_reset ();
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
rip_if_rmap_update (struct if_rmap *if_rmap)
 | 
						||
{
 | 
						||
  struct interface *ifp;
 | 
						||
  struct rip_interface *ri;
 | 
						||
  struct route_map *rmap;
 | 
						||
 | 
						||
  ifp = if_lookup_by_name (if_rmap->ifname);
 | 
						||
  if (ifp == NULL)
 | 
						||
    return;
 | 
						||
 | 
						||
  ri = ifp->info;
 | 
						||
 | 
						||
  if (if_rmap->routemap[IF_RMAP_IN])
 | 
						||
    {
 | 
						||
      rmap = route_map_lookup_by_name (if_rmap->routemap[IF_RMAP_IN]);
 | 
						||
      if (rmap)
 | 
						||
	ri->routemap[IF_RMAP_IN] = rmap;
 | 
						||
      else
 | 
						||
	ri->routemap[IF_RMAP_IN] = NULL;
 | 
						||
    }
 | 
						||
  else
 | 
						||
    ri->routemap[RIP_FILTER_IN] = NULL;
 | 
						||
 | 
						||
  if (if_rmap->routemap[IF_RMAP_OUT])
 | 
						||
    {
 | 
						||
      rmap = route_map_lookup_by_name (if_rmap->routemap[IF_RMAP_OUT]);
 | 
						||
      if (rmap)
 | 
						||
	ri->routemap[IF_RMAP_OUT] = rmap;
 | 
						||
      else
 | 
						||
	ri->routemap[IF_RMAP_OUT] = NULL;
 | 
						||
    }
 | 
						||
  else
 | 
						||
    ri->routemap[RIP_FILTER_OUT] = NULL;
 | 
						||
}
 | 
						||
 | 
						||
void
 | 
						||
rip_if_rmap_update_interface (struct interface *ifp)
 | 
						||
{
 | 
						||
  struct if_rmap *if_rmap;
 | 
						||
 | 
						||
  if_rmap = if_rmap_lookup (ifp->name);
 | 
						||
  if (if_rmap)
 | 
						||
    rip_if_rmap_update (if_rmap);
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
rip_routemap_update_redistribute (void)
 | 
						||
{
 | 
						||
  int i;
 | 
						||
 | 
						||
  if (rip)
 | 
						||
    {
 | 
						||
      for (i = 0; i < ZEBRA_ROUTE_MAX; i++) 
 | 
						||
	{
 | 
						||
	  if (rip->route_map[i].name)
 | 
						||
	    rip->route_map[i].map = 
 | 
						||
	      route_map_lookup_by_name (rip->route_map[i].name);
 | 
						||
	}
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/* ARGSUSED */
 | 
						||
static void
 | 
						||
rip_routemap_update (const char *notused)
 | 
						||
{
 | 
						||
  struct interface *ifp;
 | 
						||
  struct listnode *node, *nnode;
 | 
						||
 | 
						||
  for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp))
 | 
						||
    rip_if_rmap_update_interface (ifp);
 | 
						||
 | 
						||
  rip_routemap_update_redistribute ();
 | 
						||
}
 | 
						||
 | 
						||
/* Allocate new rip structure and set default value. */
 | 
						||
void
 | 
						||
rip_init (void)
 | 
						||
{
 | 
						||
  /* Randomize for triggered update random(). */
 | 
						||
  srand (time (NULL));
 | 
						||
 | 
						||
  /* Install top nodes. */
 | 
						||
  install_node (&rip_node, config_write_rip);
 | 
						||
 | 
						||
  /* Install rip commands. */
 | 
						||
  install_element (VIEW_NODE, &show_ip_rip_cmd);
 | 
						||
  install_element (VIEW_NODE, &show_ip_rip_status_cmd);
 | 
						||
  install_element (ENABLE_NODE, &show_ip_rip_cmd);
 | 
						||
  install_element (ENABLE_NODE, &show_ip_rip_status_cmd);
 | 
						||
  install_element (CONFIG_NODE, &router_rip_cmd);
 | 
						||
  install_element (CONFIG_NODE, &no_router_rip_cmd);
 | 
						||
 | 
						||
  install_default (RIP_NODE);
 | 
						||
  install_element (RIP_NODE, &rip_version_cmd);
 | 
						||
  install_element (RIP_NODE, &no_rip_version_cmd);
 | 
						||
  install_element (RIP_NODE, &no_rip_version_val_cmd);
 | 
						||
  install_element (RIP_NODE, &rip_default_metric_cmd);
 | 
						||
  install_element (RIP_NODE, &no_rip_default_metric_cmd);
 | 
						||
  install_element (RIP_NODE, &no_rip_default_metric_val_cmd);
 | 
						||
  install_element (RIP_NODE, &rip_timers_cmd);
 | 
						||
  install_element (RIP_NODE, &no_rip_timers_cmd);
 | 
						||
  install_element (RIP_NODE, &no_rip_timers_val_cmd);
 | 
						||
  install_element (RIP_NODE, &rip_route_cmd);
 | 
						||
  install_element (RIP_NODE, &no_rip_route_cmd);
 | 
						||
  install_element (RIP_NODE, &rip_distance_cmd);
 | 
						||
  install_element (RIP_NODE, &no_rip_distance_cmd);
 | 
						||
  install_element (RIP_NODE, &rip_distance_source_cmd);
 | 
						||
  install_element (RIP_NODE, &no_rip_distance_source_cmd);
 | 
						||
  install_element (RIP_NODE, &rip_distance_source_access_list_cmd);
 | 
						||
  install_element (RIP_NODE, &no_rip_distance_source_access_list_cmd);
 | 
						||
 | 
						||
  /* Debug related init. */
 | 
						||
  rip_debug_init ();
 | 
						||
 | 
						||
  /* SNMP init. */
 | 
						||
#ifdef HAVE_SNMP
 | 
						||
  rip_snmp_init ();
 | 
						||
#endif /* HAVE_SNMP */
 | 
						||
 | 
						||
  /* Access list install. */
 | 
						||
  access_list_init ();
 | 
						||
  access_list_add_hook (rip_distribute_update_all_wrapper);
 | 
						||
  access_list_delete_hook (rip_distribute_update_all_wrapper);
 | 
						||
 | 
						||
  /* Prefix list initialize.*/
 | 
						||
  prefix_list_init ();
 | 
						||
  prefix_list_add_hook (rip_distribute_update_all);
 | 
						||
  prefix_list_delete_hook (rip_distribute_update_all);
 | 
						||
 | 
						||
  /* Distribute list install. */
 | 
						||
  distribute_list_init (RIP_NODE);
 | 
						||
  distribute_list_add_hook (rip_distribute_update);
 | 
						||
  distribute_list_delete_hook (rip_distribute_update);
 | 
						||
 | 
						||
  /* Route-map */
 | 
						||
  rip_route_map_init ();
 | 
						||
  rip_offset_init ();
 | 
						||
 | 
						||
  route_map_add_hook (rip_routemap_update);
 | 
						||
  route_map_delete_hook (rip_routemap_update);
 | 
						||
 | 
						||
  if_rmap_init (RIP_NODE);
 | 
						||
  if_rmap_hook_add (rip_if_rmap_update);
 | 
						||
  if_rmap_hook_delete (rip_if_rmap_update);
 | 
						||
 | 
						||
  /* Distance control. */
 | 
						||
  rip_distance_table = route_table_init ();
 | 
						||
}
 |