ftp.nice.ch/pub/next/unix/network/system/gated.2.1pl2.NI.bs.tar.gz#/gated-2.1/src/rip.c

This is rip.c in view mode; [Download] [Up]

/*
 *  $Header: /disk/d/src/devel/gated/dist/src/RCS/rip.c,v 2.1 92/02/24 14:12:51 jch Exp $
 */

/*%Copyright%*/
/************************************************************************
*									*
*	GateD, Release 2						*
*									*
*	Copyright (c) 1990,1991,1992 by Cornell University		*
*	    All rights reserved.					*
*									*
*	THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY		*
*	EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT		*
*	LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY		*
*	AND FITNESS FOR A PARTICULAR PURPOSE.				*
*									*
*	Royalty-free licenses to redistribute GateD Release		*
*	2 in whole or in part may be obtained by writing to:		*
*									*
*	    GateDaemon Project						*
*	    Information Technologies/Network Resources			*
*	    143 Caldwell Hall						*
*	    Cornell University						*
*	    Ithaca, NY 14853-2602					*
*									*
*	GateD is based on Kirton's EGP, UC Berkeley's routing		*
*	daemon	 (routed), and DCN's HELLO routing Protocol.		*
*	Development of Release 2 has been supported by the		*
*	National Science Foundation.					*
*									*
*	Please forward bug fixes, enhancements and questions to the	*
*	gated mailing list: gated-people@gated.cornell.edu.		*
*									*
*	Authors:							*
*									*
*		Jeffrey C Honig <jch@gated.cornell.edu>			*
*		Scott W Brim <swb@gated.cornell.edu>			*
*									*
*************************************************************************
*									*
*      Portions of this software may fall under the following		*
*      copyrights:							*
*									*
*	Copyright (c) 1988 Regents of the University of California.	*
*	All rights reserved.						*
*									*
*	Redistribution and use in source and binary forms are		*
*	permitted provided that the above copyright notice and		*
*	this paragraph are duplicated in all such forms and that	*
*	any documentation, advertising materials, and other		*
*	materials related to such distribution and use			*
*	acknowledge that the software was developed by the		*
*	University of California, Berkeley.  The name of the		*
*	University may not be used to endorse or promote		*
*	products derived from this software without specific		*
*	prior written permission.  THIS SOFTWARE IS PROVIDED		*
*	``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,	*
*	INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF	*
*	MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.		*
*									*
************************************************************************/


#define	RIPCMDS
#include "include.h"
#include "routed.h"
#include "rip.h"
#include "icmp.h"

#ifdef	PROTO_RIP

#define	RIP_TIMER_UPDATE	0
#define	RIP_TIMER_FLASH		1

static task *rip_task = (task *) 0;
static time_t rip_next_flash = (time_t) 0;

static struct servent rip_sp;

static char rip_packet[RIPPACKETSIZE + 1];
static struct rip *ripmsg = (struct rip *) rip_packet;

int rip_pointopoint = FALSE;		/* Are we ONLY doing pointopoint RIP? */
int rip_supplier = -1;			/* Are we broadcasting RIP protocols? */
int doing_rip = TRUE;			/* Are we running RIP protocols? */
metric_t rip_default_metric;		/* Default metric to use when propogating */
pref_t rip_preference;			/* Preference for RIP routes */

int rip_n_trusted = 0;			/* Number of trusted gateways */
int rip_n_source = 0;			/* Number of source gateways */
gw_entry *rip_gw_list = NULL;		/* List of RIP gateways */
adv_entry *rip_accept_list = NULL;	/* List of nets to accept from RIP */
adv_entry *rip_propagate_list = NULL;	/* List of sources to propagates routes to RIP */
adv_entry **rip_int_accept = NULL;	/* List of accept lists per interface */
adv_entry **rip_int_propagate = NULL;	/* List of propagate lists per interface */

#ifdef	RTM_ADD
#define	dst2sock(addr, dst)	memset ((addr)->sa_data, 0, sizeof (addr)->sa_data); \
		    (addr)->sa_family = ntohs((dst)->rip_family); \
    		    (addr)->sa_len = (sizeof (struct sockaddr_in)); \
		    socktype_in((addr))->sin_addr = (dst)->rip_addr
#else	/* RTM_ADD */
#define	dst2sock(addr, dst)	memset ((addr)->sa_data, 0, sizeof (addr)->sa_data); \
		    (addr)->sa_family = ntohs((dst)->rip_family); \
		    socktype_in((addr))->sin_addr = (dst)->rip_addr
#endif	/* RTM_ADD */

#define	sock2dst(dst, addr)	memset ((dst), 0, sizeof *(dst)); \
    		    (dst)->rip_family = htons((addr)->sa_family); \
    		    (dst)->rip_addr = socktype_in((addr))->sin_addr

/*
 *	Trace RIP packets
 */
static void
rip_trace(dir, who, cp, size)
struct sockaddr_in *who;		/* should be sockaddr */
char *dir, *cp;
register int size;
{
    register struct rip *rpmsg = (struct rip *) cp;
    register struct netinfo *n;
    register const char *cmd = "Invalid";
    struct sockaddr addr;

    if (rpmsg->rip_cmd && rpmsg->rip_cmd < RIPCMD_MAX) {
	cmd = ripcmds[rpmsg->rip_cmd];
    }
    tracef("RIP %s %#A vers %d, cmd %s, length %d",
	   dir,
	   who,
	   rpmsg->rip_vers,
	   cmd, size);
    switch (rpmsg->rip_cmd) {
#ifdef	RIPCMD_POLL
	case RIPCMD_POLL:
#endif				/* RIPCMD_POLL */
	case RIPCMD_REQUEST:
	case RIPCMD_RESPONSE:
	    trace(TR_RIP, 0, NULL);
	    if (trace_flags & TR_UPDATE) {
		size -= 4 * sizeof(char);
		n = rpmsg->rip_nets;
		for (; size > 0; n++, size -= sizeof(struct netinfo)) {
		    if (size < sizeof(struct netinfo)) {
			break;
		    }
		    dst2sock(&addr, &n->rip_dst);
		    if (addr.sa_family == AF_UNSPEC &&
			!socktype_in(&addr)->sin_addr.s_addr &&
			ntohl(n->rip_metric) == RIPHOPCNT_INFINITY) {
			trace(TR_RIP | TR_NOSTAMP, 0, "\trouting table request");
		    } else {
			trace(TR_RIP | TR_NOSTAMP, 0, "\tnet %-15A  metric %2d",
			      &addr,
			      ntohl(n->rip_metric));
		    }
		}
		trace(TR_RIP, 0, "RIP %s end of packet", dir);
	    }
	    break;
	case RIPCMD_TRACEON:
	    trace(TR_RIP, 0, ", file %*s", size, rpmsg->rip_tracefile);
	    break;
#ifdef	RIPCMD_POLLENTRY
	case RIPCMD_POLLENTRY:
	    n = rpmsg->rip_nets;
	    dst2sock(&addr, &n->rip_dst);
	    trace(TR_RIP, 0, ", net %A",
		  &addr);
	    break;
#endif				/* RIPCMD_POLLENTRY */
	default:
	    trace(TR_RIP, 0, NULL);
	    break;
    }
    trace(TR_RIP, 0, NULL);
}


static void
rip_send(tp, flags, sin, size)
task *tp;
flag_t flags;
struct sockaddr_in *sin;
int size;
{
    struct sockaddr_in dst;

    dst = *sin;
    sin = &dst;
    if (sin->sin_port == 0) {
	sin->sin_port = rip_sp.s_port;
    }
    (void) task_send_packet(tp, rip_packet, size, flags, (sockaddr_un *) sin);
    if (trace_flags & TR_RIP) {
	rip_trace("SENT", sin, rip_packet, size);
    }
}


/*
 * Supply dst with the contents of the routing tables.
 * If this won't fit in one packet, chop it up into several.
 */
/*ARGSUSED*/
static void
rip_supply(tp, dst, src, ifp, gwp, do_split_horizon, flash_update)
task *tp;
struct sockaddr_in *dst, *src;
if_entry *ifp;
gw_entry *gwp;
int do_split_horizon;
int flash_update;
{
    rt_entry *rt;
    struct netinfo *n;
    int size;
    metric_t metric = 0, split_horizon;

    ripmsg->rip_cmd = RIPCMD_RESPONSE;
    ripmsg->rip_vers = RIPVERSION;
    n = ripmsg->rip_nets;

    RT_TABLE(rt) {
	if (rt->rt_state & RTS_NOADVISE) {
	    continue;
	}
	if (flash_update && (rip_task->task_rtrevision >= rt->rt_revision)) {
	    continue;
	}
	/* Do not send interface routes back to the same interface */
	if ((rt->rt_ifp == ifp) && (rt->rt_proto & RTPROTO_DIRECT)) {
	    continue;
	}
	/* Subnets and host routes do not go everywhere */
	if (rt->rt_state & RTS_HOSTROUTE) {
	    if (((ifp->int_net.in.sin_addr.s_addr ^ rt->rt_dest.in.sin_addr.s_addr) &
		 ifp->int_netmask.in.sin_addr.s_addr) &&
		!((rt->rt_ifp->int_net.in.sin_addr.s_addr ^ rt->rt_dest.in.sin_addr.s_addr) &
		  rt->rt_ifp->int_netmask.in.sin_addr.s_addr)) {
		/* Host is being sent to another network and we learned it via it's home network */
		/* XXX - This assumes we are announcing the network route */
		continue;
	    }
	} else if (rt->rt_state & RTS_SUBNET) {
	    if ((rt->rt_dest.in.sin_addr.s_addr & ifp->int_netmask.in.sin_addr.s_addr) != ifp->int_net.in.sin_addr.s_addr) {
		/* Only send subnets to interfaces of the same network */
		continue;
	    }
	} else {
	    if (rt->rt_dest.in.sin_addr.s_addr == ifp->int_net.in.sin_addr.s_addr) {
		/* Do not send the whole net to a subnet */
		continue;
	    }
	}

	if ((rt->rt_proto & rip_task->task_rtproto) &&
	    (ifp == rt->rt_ifp) &&
	    do_split_horizon &&
	    (ifp->int_state & (IFS_BROADCAST|IFS_POINTOPOINT) || equal_in(rt->rt_router.in.sin_addr, dst->sin_addr))) {
	    split_horizon = RIPHOPCNT_INFINITY;
	} else {
	    split_horizon = 0;
	}

	if (rt->rt_ifp->int_state & IFS_LOOPBACK) {
	    /* Routes via the loopback interface must have an explicit metric */ 
	    metric = RIPHOPCNT_INFINITY;
	}
		
	metric = (rt->rt_proto & RTPROTO_DIRECT) ? RIP_HOP : rip_default_metric;
	if (!propagate(rt,
		       rip_task->task_rtproto,
		       rip_propagate_list,
		       INT_CONTROL(rip_int_propagate, ifp),
		       gwp ? gwp->gw_propagate : NULL,
		       &metric)) {
	    continue;
	}
	/* Add the interface mteric */
	metric += ifp->int_metric;

	if (split_horizon) {
	    metric = split_horizon;
	}
	if (!(rt->rt_ifp->int_state & IFS_UP) || (rt->rt_state & (RTS_HOLDDOWN | RTS_DELETE))) {
	    metric = RIPHOPCNT_INFINITY;
	}
	if (flash_update && (metric >= RIPHOPCNT_INFINITY) && (rt->rt_ifp == ifp) && (rt->rt_proto & rip_task->task_rtproto)) {
	    /* Don't flash deleted or unreachable routes back to their source */
	    continue;
	}
	size = (char *) n - rip_packet;
	if (size > (RIPPACKETSIZE - sizeof(struct netinfo))) {
	    rip_send(tp, 0, dst, size);
	    n = ripmsg->rip_nets;
	}
	sock2dst(&n->rip_dst, &rt->rt_dest.a);

	/* Make sure metric is valid */
	if (metric > RIPHOPCNT_INFINITY) {
	    metric = RIPHOPCNT_INFINITY;
	}
	n->rip_metric = metric;
	n->rip_metric = htonl(n->rip_metric);
	n++;
    } RT_TABLEEND;

    if ((n != ripmsg->rip_nets) || !src) {	/* OK to reply to a RIPQUERY with an empty packet */
	size = (char *) n - rip_packet;
	rip_send(tp, 0, dst, size);
    }
}


/*
 * 	Check out a newly received RIP packet.
 */

void
rip_recv(tp)
task *tp;
{
    int size;
    register rt_entry *rt;
    register struct netinfo *n;
    register if_entry *ifp, *ifpc;
    gw_entry *gwp = (gw_entry *) 0;
    register int OK = TRUE;
    struct rip *inripmsg = (struct rip *) recv_iovec[RECV_IOVEC_DATA].iov_base;
    int change = FALSE;
#ifdef	RIP_CHECK_ZERO
    int check_zero = FALSE;
#endif	/* RIP_CHECK_ZERO */
    int newsize;
    flag_t rte_table;
    u_short src_port;
    metric_t metric;
    const char *reject_msg = (char *) 0;
    char type[MAXHOSTNAMELENGTH];
    int answer = FALSE;
    int split_horizon = TRUE;
    struct sockaddr dest;
		

    if (task_receive_packet(tp, &size)) {
	return;
    }
    if (recv_addr.in.sin_family != AF_INET) {
	reject_msg = "protocol not INET";
	goto Reject;
    }
    src_port = recv_addr.in.sin_port;
    recv_addr.in.sin_port = 0;		/* For comparisons */

    switch (inripmsg->rip_vers) {
	case 0:
	    reject_msg = "ignoring version 0 packets";
	    goto Reject;
	case 1:
#ifdef	RIP_CHECK_ZERO
	    check_zero++;
#endif	/* RIP_CHECK_ZERO */
	    break;
    }

    /* Locate or create a gateway structure for this gateway */
    gwp = gw_timestamp(&rip_gw_list, rip_task->task_rtproto, (sockaddr_un *) & recv_addr);

    /* If we have a list of trusted gateways, verify that this gateway is trusted */
    if (rip_n_trusted && !(gwp->gw_flags & GWF_TRUSTED)) {
	OK = FALSE;
    }
    if (trace_flags & TR_RIP) {
	rip_trace("RECV", &recv_addr.in, (caddr_t) inripmsg, size);
    }
#ifdef	RIP_CHECK_ZERO
    if (check_zero && inripmsg->rip_res) {
	/* XXX - Should check later on that the sin_zero fields are zero */
	reject_msg = "reserved fields not zero";
	goto Reject;
    }
#endif				/* RIP_CHECK_ZERO */

    switch (inripmsg->rip_cmd) {
#ifdef	RIPCMD_POLL
	case RIPCMD_POLL:
	    answer = TRUE;
	    split_horizon = FALSE;
#endif				/* RIPCMD_POLL */
	case RIPCMD_REQUEST:
	    if ((src_port != rip_sp.s_port) || answer) {
		recv_addr.in.sin_port = src_port;

		if ((ifp = if_withdst((sockaddr_un *) & recv_addr)) <= (if_entry *) 0) {
		    struct sockaddr_in dst;

		    dst = recv_addr.in;	/* struct copy */

		    dst.sin_addr.s_addr = htonl(gd_inet_netof(dst.sin_addr));
		    if (!(rt = rt_lookup(RTS_NETROUTE, (sockaddr_un *) & dst))) {
			if (!(rt = rt_lookup(RTS_NETROUTE, (sockaddr_un *) & default_net))) {
			    reject_msg = "can not find interface for route";
			    goto Reject;
			}
		    }
		    ifp = rt->rt_ifp;
		}
	    } else {
		ifpc = if_withaddr((sockaddr_un *) & recv_addr);
		if (ifpc) {
		    return;
		}
		if (!OK) {
		    reject_msg = "not on trustedripgateways list";
		    goto Reject;
		}
		if ((ifp = if_withdst((sockaddr_un *) & recv_addr)) <= (if_entry *) 0) {
		    reject_msg = "not on same net";
		    goto Reject;
		}
		if (ifp->int_state & (IFS_NORIPIN | IFS_NORIPOUT)) {
		    reject_msg = "interface marked for no RIP in/out";
		    goto Reject;
		}
		if (!rip_supplier) {
		    reject_msg = "not supplying RIP";
		    goto Reject;
		}
	    }

	    gwp->gw_flags |= GWF_QUERY | GWF_ACCEPT;
	    newsize = 0;
	    size -= 4 * sizeof(char);
	    n = inripmsg->rip_nets;
	    while (size > 0) {
		if (size < sizeof(struct netinfo)) {
		    break;
		}
		size -= sizeof(struct netinfo);
		dst2sock(&dest, &n->rip_dst);
		n->rip_metric = ntohl(n->rip_metric);
		if (dest.sa_family == AF_UNSPEC &&
		    n->rip_metric == RIPHOPCNT_INFINITY &&
		    !size) {
		    recv_addr.in.sin_port = src_port;

		    rip_supply(tp, &recv_addr.in, (struct sockaddr_in *) 0, ifp, gwp, split_horizon, FALSE);
		    return;
		}
		rt = rt_lookup(RTS_INTERIOR, (sockaddr_un *) &dest);
		n->rip_metric = htonl(!rt ? RIPHOPCNT_INFINITY : min(rt->rt_metric + ifp->int_metric, RIPHOPCNT_INFINITY));
		n++;
		newsize += sizeof(struct netinfo);
	    }

	    if (newsize > 0) {
		recv_addr.in.sin_port = src_port;

		inripmsg->rip_cmd = RIPCMD_RESPONSE;
		newsize += sizeof(int);
		memcpy((char *) ripmsg, (char *) inripmsg, newsize);
		rip_send(tp, 0, &recv_addr.in, newsize);
	    }
	    return;
	case RIPCMD_TRACEON:
	case RIPCMD_TRACEOFF:
	    if (!OK) {
		reject_msg = "not on trustedripgateways list";
		goto Reject;
	    }
	    if (ntohs(src_port) > IPPORT_RESERVED) {
		reject_msg = "not from a trusted port";
		goto Reject;
	    }
	    if ((ifp = if_withdst((sockaddr_un *) & recv_addr)) <= (if_entry *) 0) {
		reject_msg = "not on same net";
		goto Reject;
	    }
	    if (ifp->int_state & IFS_NORIPIN) {
		reject_msg = "not listening to RIP on this interface";
		goto Reject;
	    }
	    reject_msg = "TRACE packets not supported";
	    goto Reject;
#ifdef	RIPCMD_POLLENTRY
	case RIPCMD_POLLENTRY:
	    n = inripmsg->rip_nets;
	    newsize = sizeof(struct entryinfo);
	    dst2sock(&dest, &n->rip_dst);
	    switch (dest.sa_family) {
	    case AF_INET:
		rt = rt_lookup(RTS_INTERIOR, (sockaddr_un *) &dest);
		break;

	    default:
		rt = 0;
	    }
	    if (rt) {			/* don't bother to check rip_vers */
		struct entryinfo *e = (struct entryinfo *) n;

		sock2dst(&e->rtu_dst, &rt->rt_dest.a);
		sock2dst(&e->rtu_router, &rt->rt_router.a);
		e->rtu_flags = htons((unsigned short) rt->rt_flags);
		e->rtu_state = htons((unsigned short) rt->rt_state);
		e->rtu_timer = htonl((unsigned long) rt->rt_timer);
		e->rtu_metric = rt->rt_metric;
		e->rtu_metric = htonl((u_long) e->rtu_metric);
		ifp = rt->rt_ifp;
		if (ifp) {
		    e->int_flags = htonl((unsigned long) ifp->int_state);
		    (void) strncpy(e->int_name, rt->rt_ifp->int_name, sizeof(e->int_name));
		} else {
		    e->int_flags = 0;
		    (void) strcpy(e->int_name, "(none)");
		}
	    } else {
		memset((char *) n, (char) 0, newsize);
	    }

	    newsize += sizeof (struct rip) - sizeof (struct netinfo);
	    memcpy((char *) ripmsg, (char *) inripmsg, newsize);
	    gwp->gw_flags |= GWF_QUERY | GWF_ACCEPT;
	    recv_addr.in.sin_port = src_port;
	    rip_send(tp, 0, &recv_addr.in, newsize);
	    return;
#endif				/* RIPCMD_POLLENTRY */
	case RIPCMD_RESPONSE:
	    /*
             *  Are we talking to ourselves???
             *
             *  if_withaddr() handles PTP's also.  If from a
             *  dst of a PTP link, let it through for further processing.
             *  you shouldn't receive your own RIPs on a PTP.
             */

	    ifpc = if_withaddr((sockaddr_un *) & recv_addr);
	    if (ifpc) {
		if_rtupdate(ifpc);
		if ((ifpc->int_state & IFS_POINTOPOINT) == 0) {
		    return;
		}
	    }
	    if (!OK) {
#ifndef	notdef
		reject_msg = "not on trustedripgateways list";
		goto Reject;
#else				/* notdef */
		return;
#endif				/* notdef */
	    }
	    if (src_port != rip_sp.s_port) {
		reject_msg = "not from a trusted port";
		goto Reject;
	    }
	    if ((ifp = if_withdst((sockaddr_un *) & recv_addr)) <= (if_entry *) 0) {
		reject_msg = "not on same net";
		goto Reject;
	    }
	    if (ifp->int_state & IFS_NORIPIN) {
		reject_msg = "interface marked for no RIP in";
		goto Reject;
	    }
	    gwp->gw_flags |= GWF_ACCEPT;

	    /*
             * update interface timer on interface that packet came in on.
             */
	    if_rtupdate(ifp);

	    rt_open(tp);

	    size -= 4 * sizeof(char);
	    n = inripmsg->rip_nets;
	    for (; size >= sizeof(struct netinfo); size -= sizeof(struct netinfo), n++) {
	        pref_t preference = rip_preference;

		dst2sock(&dest, &n->rip_dst);
		switch (dest.sa_family) {
		case AF_INET:
		    if (gd_inet_checkhost((struct sockaddr_in *) &dest)) {
			break;
		    }
		    /* Fall through */

		default:
		    continue;
		}

		/* Verify that this is a valid metric */
		if (!is_valid_in((sockaddr_un *) &dest,
				 rip_accept_list,
				 INT_CONTROL(rip_int_accept, ifp),
				 gwp->gw_accept,
				 &preference)) {
		    continue;
		}
		/*
	         *  Convert metric to host byte order.  If metric is zero, ignore this network
	         */
		if (!(metric = ntohl(n->rip_metric))) {
		    continue;
		}
		/* Now add hop count to metric */
		metric += ifp->int_metric + RIP_HOP;

		/* Determine routing table based on host bits */
		rte_table = gd_inet_ishost((struct sockaddr_in *) &dest) ? RTS_HOSTROUTE : RTS_INTERIOR;

		rt = rt_locate(rte_table, (sockaddr_un *) &dest, rip_task->task_rtproto);
		if (!rt) {
		    /* new route */

		    if (metric >= RIPHOPCNT_INFINITY) {
			continue;
		    }
		    (void) rt_add((sockaddr_un *) &dest,
				  (sockaddr_un *) 0,
				  (sockaddr_un *) & recv_addr,
				  gwp,
				  metric,
				  rte_table,
				  rip_task->task_rtproto,
				  0,
				  (time_t) 0,
				  preference);
		    change = TRUE;
		} else {
		    /* Existing route */
		    if ((rt->rt_flags & RTF_GATEWAY) == 0) {
			continue;
		    }
		    if (equal(&rt->rt_router, &recv_addr)) {
			if (metric >= RIPHOPCNT_INFINITY) {
			    change += rt_unreach(rt);
			    continue;
			}
			if ((metric != rt->rt_metric) || (rt->rt_state & RTS_HOLDDOWN)) {
			    if (rt_change(rt,
					  (sockaddr_un *) & recv_addr,
					  metric,
					  (time_t) 0,
					  preference)) {
				change = TRUE;
			    }
			}
			rt_refresh(rt);
		    } else {
			if ((metric >= RIPHOPCNT_INFINITY) || (rt->rt_state & RTS_HOLDDOWN)) {
			    continue;
			}
			if ((metric < rt->rt_metric) ||
			    ((rt->rt_timer > (rt->rt_timer_max / 2)) &&
			     (rt->rt_metric == metric) && !(rt->rt_state & (RTS_CHANGED | RTS_REFRESH)))) {
			    if (rt_change(rt,
					  (sockaddr_un *) & recv_addr,
					  metric,
					  (time_t) 0,
					  preference)) {
				change = TRUE;
			    }
			}
		    }
		}
	    }				/*  for each route */
	    if (rt_close(tp, gwp, change)) {
		task_flash(rip_task);
	    }
	    break;
	default:
	    reject_msg = "invalid or not implemented command";
	    goto Reject;
    }
    return;

  Reject:
    if (inripmsg->rip_cmd < RIPCMD_MAX) {
	(void) strcpy(type, ripcmds[inripmsg->rip_cmd]);
    } else {
	(void) sprintf(type, "#%d", inripmsg->rip_cmd);
    }
    trace(TR_RIP, 0, "rip_recv: ignoring RIP %s packet from %#A - %s",
	  type,
	  &recv_addr,
	  reject_msg);
    trace(TR_RIP, 0, NULL);
    if (gwp) {
	gwp->gw_flags |= GWF_REJECT;
    }
    return;
}


/*
 * Output a preformed RIP packet.
 */

/*ARGSUSED*/
static void
rip_out(tp, dst, src, ifp, gwp, do_split_horizon, flash_update)
task *tp;
struct sockaddr_in *dst, *src;
if_entry *ifp;
gw_entry *gwp;
int do_split_horizon;
int flash_update;
{
    metric_t tmp = ntohl(ripmsg->rip_nets[0].rip_metric);
    rt_entry *rt;
    struct netinfo *n = ripmsg->rip_nets;
    struct sockaddr dest;

    dst2sock(&dest, &n->rip_dst);

    if (dst->sin_family != AF_INET) {
	return;
    }
    /*
     * Check to see if we are sending the initial RIP request to other
     * gateways.  That request has no restrictions other than whether RIP
     * is allowed on that interface or not.  This restriction is handled
     * in toall().
     */

    if (dest.sa_family != AF_UNSPEC ||
	ntohl(n->rip_metric) != RIPHOPCNT_INFINITY) {

	rt = rt_lookup(RTS_INTERIOR, (sockaddr_un *) &dest);
	if (rt == NULL) {
	    rt = rt_lookup(RTS_HOSTROUTE, (sockaddr_un *) &dest);
	    if (rt == NULL) {
		trace(TR_ALL, LOG_ERR, "rip_out: bad route %A",
		      &dest);
		return;
	    }
	}

	/*
         * XXX - make sure this route can be announced via this interface/proto.
        if (!is_valid(rt, rip_task->task_rtproto, ifp)) {
          return;
        }
         */
	/*
         * since we are only sending out this one packet, we can add the
         * interface metric here.  Don't forget Split Horizon.
         */
	if ((rt->rt_proto & rip_task->task_rtproto) && (ifp == rt->rt_ifp) && do_split_horizon &&
	    (ifp->int_state & (IFS_BROADCAST|IFS_POINTOPOINT) || equal_in(rt->rt_router.in.sin_addr, dst->sin_addr))) {
	    tmp = ntohl(n->rip_metric);
	    n->rip_metric = htonl(RIPHOPCNT_INFINITY);
	} else if ((tmp = ntohl(n->rip_metric)) != RIPHOPCNT_INFINITY) {
	    if ((tmp + ifp->int_metric) >= RIPHOPCNT_INFINITY) {
		n->rip_metric = htonl(RIPHOPCNT_INFINITY);
	    } else {
		n->rip_metric = htonl((tmp + ifp->int_metric));
	    }
	}
    }
    rip_send(rip_task, 0, dst, sizeof(struct rip));
    n->rip_metric = tmp;
    n->rip_metric = htonl(n->rip_metric);
}


/*
 *	send RIP packets
 */
/*ARGSUSED*/
static void
rip_job(tip, interval)
timer *tip;
time_t interval;
{
    task_toall(tip->timer_task,
	       rip_supply,
	       rip_pointopoint,
	       IFS_NORIPOUT,
	       rip_n_source ? rip_gw_list : NULL,
	       FALSE);
}


/*
 *	send a flash update packet
 */
/*ARGSUSED*/
static void
rip_do_flash(tip, interval)
timer *tip;
time_t interval;
{
    trace(TR_TASK, 0, "rip_do_flash: Doing flash update for RIP");
    task_toall(rip_task,
	       rip_supply,
	       rip_pointopoint,
	       IFS_NORIPOUT,
	       rip_n_source ? rip_gw_list : NULL,
	       TRUE);
    rip_next_flash = (time_t) (random() % 4 + 1) + time_sec;
    trace(TR_TASK, 0, "rip_do_flash: Flash update done, none before %T", rip_next_flash);
}


/*
 *	Check to see if a flash update packet is allowed and send or schedule it
 */
static void
rip_flash(tp)
task *tp;
{
    if (time_sec >= rip_next_flash) {
	/* A flash update can be sent now, do it */
	rip_do_flash(tp->task_timer[RIP_TIMER_FLASH], (time_t) 0);
    } else if (!tp->task_timer[RIP_TIMER_FLASH] && (tp->task_timer[RIP_TIMER_UPDATE]->timer_next_time > rip_next_flash)) {
	/* A flash update can't be sent and one is not yet scheduled */
	(void) timer_create(tp,
			    RIP_TIMER_FLASH,
			    "Flash",
			    TIMERF_DELETE | TIMERF_ABSOLUTE,
			    rip_next_flash - time_sec,
			    rip_do_flash);
    }
}


/*
 *	Cleanup before re-init
 */
/*ARGSUSED*/
static void
rip_cleanup(tp)
task *tp;
{
    adv_cleanup(&rip_n_trusted, &rip_n_source, rip_gw_list,
		&rip_accept_list, &rip_propagate_list,
		&rip_int_accept, &rip_int_propagate);
}


/*
 *	Dump info about RIP
 */
static void
rip_dump(fd)
FILE *fd;
{
    (void) fprintf(fd, "RIP:\n");
    (void) fprintf(fd, "\tDefault metric: %d\t\tDefault preference: %d\n",
		   rip_default_metric,
		   rip_preference);
    if (rip_gw_list) {
	(void) fprintf(fd, "\tActive gateways:\n");
	gw_dump(fd, "\t\t", rip_gw_list);
    }
    control_accept_dump(fd, 1, rip_accept_list, rip_int_accept, rip_gw_list);
    control_propagate_dump(fd, 1, rip_propagate_list, rip_int_propagate, rip_gw_list);
    (void) fprintf(fd, "\n\n");
}


/*
 * initialize RIP socket and RIP task
 */

/*ARGSUSED*/
void
rip_init()
{
    static struct servent *sp;
    struct sockaddr_in addr;
    if_entry *ifp;
    void (*flash) () = rip_flash;	/* Hack for UTX/32 and Ultrix */

    if (doing_rip) {
	if (!rip_task) {
	    if (rip_supplier < 0) {
		if (n_interfaces > 1) {
		    rip_supplier = TRUE;
		    trace(TR_ALL, LOG_NOTICE, "rip_init: Acting as RIP supplier to our direct nets");
		}
		IF_LIST(ifp) {
		    if (ifp->int_state & IFS_POINTOPOINT) {
			rip_supplier = TRUE;
			trace(TR_INT, 0, "init_if: PointoPoint RIP supplier to: %s", ifp->int_name);
		    }
		} IF_LISTEND(ifp) ;
	    }
	    if (rip_supplier < 0) {
		rip_supplier = FALSE;
	    }
	    if ((sp = getservbyname("router", "udp")) == NULL) {
		trace(TR_ALL, LOG_ERR, "No service for router available, using %d",
		      RIP_PORT);
		memset((caddr_t) & rip_sp, (char) 0, sizeof(rip_sp));
		rip_sp.s_port = htons(RIP_PORT);
	    } else {
		memcpy((char *) &rip_sp, (char *) sp, sizeof(rip_sp));
	    }

	    sockclear_in(&addr);
	    addr.sin_port = rip_sp.s_port;
	    addr.sin_addr.s_addr = INADDR_ANY;

	    rip_task = task_alloc("RIP");
	    sockcopy(&addr, &rip_task->task_addr);
	    rip_task->task_rtproto = RTPROTO_RIP;
	    rip_task->task_recv = rip_recv;
	    rip_task->task_cleanup = rip_cleanup;
	    rip_task->task_dump = rip_dump;

	    if ((rip_task->task_socket = task_get_socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		quit(errno);
	    }
	    if (!test_flag) {
		if (task_set_option(rip_task, TASKOPTION_BROADCAST, (caddr_t) TRUE) < 0) {
		    quit(errno);
		}
		if (task_set_option(rip_task, TASKOPTION_RECVBUF, (caddr_t) (32 * 1024)) < 0) {
		    quit(errno);
		}
		if (task_set_option(rip_task, TASKOPTION_DONTROUTE, (caddr_t) TRUE) < 0) {
		    quit(errno);
		}
		if (bind(rip_task->task_socket, (struct sockaddr *) & addr, socksize(&addr)) < 0) {
		    trace(TR_ALL, LOG_ERR, "rip_init: bind: %m");
		    (void) close(rip_task->task_socket);
		    quit(errno);
		}
	    }
	    if (rip_supplier) {
		rip_task->task_flash = flash;
		(void) timer_create(rip_task,
				    RIP_TIMER_UPDATE,
				    "Update",
				    0,
				    (time_t) RIP_INTERVAL,
				    rip_job);
	    }
	    if (!task_create(rip_task, RIPPACKETSIZE)) {
		quit(EINVAL);
	    }
	    if (RIP_INTERVAL < rt_timer->timer_interval) {
		timer_interval(rt_timer, (time_t) RIP_INTERVAL);
	    }
	    if (!test_flag) {
		/* Generate a RIP REQUEST packet asking for all known RIP routes */
		ripmsg->rip_cmd = RIPCMD_REQUEST;
		ripmsg->rip_vers = RIPVERSION;
		ripmsg->rip_nets[0].rip_dst.rip_family = htons(AF_UNSPEC);
		ripmsg->rip_nets[0].rip_metric = htonl(RIPHOPCNT_INFINITY);
		task_toall(rip_task,
			   rip_out,
			   rip_pointopoint,
			   IFS_NORIPOUT,
			   rip_n_source ? rip_gw_list : NULL,
			   FALSE);
	    }
	}
	if (rip_supplier) {
	    if_rtactive = TRUE;		/* Indicate we are broadcasting */
	    ignore_redirects = TRUE;	/* Gateways don't listen to redirects */
	}
    } else {
	rip_cleanup((task *) 0);
	if (rip_task) {
	    task_delete(rip_task);
	    rip_task = (task *) 0;
	}
    }
}


#endif				/* PROTO_RIP */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.