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

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

/*
 *  $Header: /disk/d/src/devel/gated/dist/src/RCS/parse.c,v 2.1 92/02/24 14:12:47 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.		*
*									*
************************************************************************/


#include	"include.h"
#ifdef	SYSV
#include	<dirent.h>
#endif	/* SYSV */
#include	"parse.h"
#include	"parser.h"
#include	"rip.h"
#include	"hello.h"
#include	"egp.h"
#include	"bgp.h"
#include	"snmp.h"

static int n_keywords;

/*
 *	Table of keywords recognized.  This table is sorted at runtime
 *	to facilitate binary searching.
 */

static bits keywords[] =
{
    {T_INTERFACE, "interface"},
    {T_INTERFACE, "intf"},
    {T_DIRECT, "direct"},
    {T_PROTO, "protocol"},
    {T_PROTO, "proto"},
    {T_METRIC, "metric"},
    {T_LEX, "lex"},
    {T_PARSE, "yacc"},
    {T_PARSE, "parse"},
    {T_CONFIG, "config"},
    {T_DEFAULT, "default"},
    {T_INCLUDE, "include"},
    {T_DIRECTORY, "directory"},
#if	YYDEBUG != 0
    {T_YYDEBUG, "yydebug"},
    {T_YYSTATE, "yystate"},
    {T_YYQUIT, "yyquit"},
#endif				/* YYDEBUG */
    {T_ON, "on"},
    {T_ON, "yes"},
    {T_OFF, "off"},
    {T_OFF, "no"},
    {T_QUIET, "quiet"},
    {T_POINTOPOINT, "pointopoint"},
    {T_POINTOPOINT, "pointtopoint"},
    {T_POINTOPOINT, "p2p"},
    {T_SUPPLIER, "supplier"},
    {T_GATEWAY, "gateway"},
    {T_GATEWAY, "gw"},
    {T_PREFERENCE, "preference"},
    {T_PREFERENCE, "pref"},
    {T_DEFAULTMETRIC, "defaultmetric"},
#if	defined(PROTO_EGP) || defined(PROTO_BGP)
    {T_NEIGHBOR, "neighbor"},
    {T_NEIGHBOR, "peer"},
    {T_ASIN, "asin"},
    {T_ASOUT, "asout"},
    {T_METRICOUT, "metricout"},
    {T_VERSION, "version"},
    {T_GENDEFAULT, "gendefault"},
    {T_NOGENDEFAULT, "nogendefault"},
    {T_DEFAULTIN, "acceptdefault"},
    {T_DEFAULTOUT, "propagatedefault"},
#endif				/* defined(PROTO_EGP) || defined(PROTO_BGP) */
#ifdef	PROTO_EGP
    {T_EGP, "egp"},
    {T_GROUP, "group"},
    {T_MAXUP, "maxup"},
    {T_SOURCENET, "sourcenet"},
    {T_P1, "p1"},
    {T_P1, "minhello"},
    {T_P2, "p2"},
    {T_P2, "minpoll"},
    {T_PKTSIZE, "packetsize"},
    {T_PKTSIZE, "packet-size"},
#endif				/* PROTO_EGP */
#ifdef	PROTO_BGP
    {T_BGP, "bgp"},
    {T_LINKTYPE, "linktype"},
    {T_INTERNAL, "internal"},
    {T_HORIZONTAL, "horizontal"},
    {T_HOLDTIME, "holdtime"},
#endif				/* PROTO_BGP */
#if	defined(PROTO_RIP) || defined(PROTO_HELLO)
    {T_TRUSTEDGATEWAYS, "trustedgateways"},
    {T_SOURCEGATEWAYS, "sourcegateways"},
#endif				/* defined(PROTO_RIP) || defined(PROTO_HELLO) */
#ifdef	PROTO_RIP
    {T_RIP, "rip"},
    {T_NORIPOUT, "noripout"},
    {T_NORIPIN, "noripin"},
#endif				/* PROTO_RIP */
#ifdef	PROTO_HELLO
    {T_HELLO, "hello"},
    {T_NOHELLOOUT, "nohelloout"},
    {T_NOHELLOIN, "nohelloin"},
#endif				/* PROTO_HELLO */
#ifdef	PROTO_ICMP
    {T_ICMP, "icmp"},
    {T_NOICMPIN, "noicmpin"},
#endif				/* PROTO_ICMP */
#if	defined(PROTO_ICMP) || defined(RTM_ADD)
    {T_REDIRECT, "redirect"},
    {T_REDIRECT, "redirects"},
#endif				/* defined(PROTO_ICMP) || defined(RTM_ADD) */
#ifdef	AGENT_SNMP
    {T_SNMP, "snmp"},
#endif				/* AGENT_SNMP */
    {T_PASSIVE, "passive"},
    {T_STATIC, "static"},
    {T_ANNOUNCE, "announce"},
    {T_NOANNOUNCE, "noannounce"},
    {T_LISTEN, "listen"},
    {T_NOLISTEN, "nolisten"},
    {T_MARTIANS, "martians"},
    {T_AS, "as"},
    {T_AS, "autonomoussystem"},
    {T_AS, "autonomous-system"},
    {T_PROPAGATE, "propagate"},
    {T_ACCEPT, "accept"},
    {T_RESTRICT, "restrict"},
    {T_NORESTRICT, "norestrict"},
    {T_MASK, "mask"},
    {T_OPTIONS, "options"},
    {T_NOINSTALL, "noinstall"},
    {T_TRACEOPTIONS, "traceoptions"},
    {T_TRACEFILE, "tracefile"},
    {T_REPLACE, "replace"},
    {T_ALL, "all"},
    {T_NONE, "none"},
    {T_GENERAL, "general"},
    {T_EXTERNAL, "external"},
    {T_ROUTE, "route"},
    {T_UPDATE, "update"},
    {T_KERNEL, "kernel"},
    {T_TASK, "task"},
    {T_TIMER, "timer"},
    {T_NOSTAMP, "nostamp"},
    {T_MARK, "mark"},
    {0, NULL}
};


/*
 *	A string of alpha characters which is either a keyword or a host/network name.
 *	First do a binary search (Knuth 6.2.1) to lookup up a keyword, if it is not found
 *	assume it is a host/network name.  The proper thing to do is a REJECT, but flex
 *	won't optimize if we use a REJECT.
 */
int
parse_keyword(yytext)
char *yytext;
{
    int c;
    bits *i, *l, *u, *p = (bits *) 0;

    l = keywords;
    u = &keywords[n_keywords - 1];
    do {
	i = (u - l) / 2 + l;
	c = strcasecmp(yytext, i->t_name);
	if (!c) {
	    p = i;
	    break;
	} else if (c < 0) {
	    u = i - 1;
	} else {
	    l = i + 1;
	}
    } while (u >= l);

    return (p ? p->t_bits : UNKNOWN);
}


/*
 *	Look up a token name given it's ID in the keyword table.  There is no way to select
 *	between multiple keywords mapping to the same token once the table is sorted.
 */
const char *
parse_keyword_lookup(token)
int token;
{
    bits *p;
    static char invalid_string[2];
    static bits special_keywords[] =
    {
	{EOS, "end-of-statement"},
	{UNKNOWN, "unknown-keyword"},
	{0, NULL}
    };

    if (token > 0 && token < 257 /* XXX - ASCII dependency here*/ ) {
	*invalid_string = token;
	return (invalid_string);
    }
    for (p = keywords; p->t_name; p++) {
	if (token == p->t_bits) {
	    return (p->t_name);
	}
    }

    for (p = special_keywords; p->t_name; p++) {
	if (token == p->t_bits) {
	    return (p->t_name);
	}
    }

    return ((char *) 0);
}


/*
 *	A comparison routine used by the sorting routine that follows
 */
static int
parse_keyword_sort_compare(p1, p2)
bits *p1, *p2;
{
    return (strcasecmp(p1->t_name, p2->t_name));
}


/*
 *	Quicksort the table of keywords to insure that they are in order, easier
 *	than trying to keep the source sorted and less prone to mistakes.
 *
 *	The sort is only done the first time we are called, i.e. when n_keyword is
 *	zero.
 */
static void
parse_keyword_sort()
{
    bits *p;

    if (!n_keywords) {
	/* Calculate size of table */
	for (p = keywords; p->t_name; p++, n_keywords++) ;

	qsort((caddr_t) keywords, n_keywords, sizeof(bits), parse_keyword_sort_compare);
    }
}


/*
 *	Front end for yyparse().  Exit if any errors
 */
int
parse_parse(file)
const char *file;
{
    int errors;
    extern int yynerrs;
    static int first_parse = TRUE;

    if (first_parse) {
	parse_keyword_sort();		/* Sort the keyword table */
    }

    parse_directory = parse_strdup(task_path_name);
    
    sethostent(1);
    setnetent(1);

    errors = parse_open(parse_strdup(file));
    if (!errors) {
	protos_seen = (proto_t) 0;
	parse_state = PS_DEFINE;
	errors = yyparse();
    }
    errors += yynerrs;

    endhostent();
    endnetent();

    if (errors) {
	trace(TR_ALL, 0, NULL);
	trace(TR_ALL, LOG_ERR, "parse_parse: %d parse error%s", errors, errors > 1 ? "s" : "");
	trace(TR_ALL, 0, NULL);
    }

    if (parse_directory) {
	(void) free(parse_directory);
	parse_directory = (char *) 0;
    }
    first_parse = FALSE;

    return (errors);
}


/*
 *	Duplicate a string and return pointer
 */
char *
parse_strdup(s)
char *s;
{
    char *cp;

    cp = (char *) malloc((u_int) (strlen(s) + 1));
    if (!cp) {
	trace(TR_ALL, LOG_ERR, "parse_strdup: %s malloc: %m",
	      parse_where());
	quit(errno);
    }
    (void) strcpy(cp, s);
    return (cp);
}

/*
 *	Format a message indicating the current line number and return
 *	a pointer to it.
 */
char *
parse_where()
{
    static char where[BUFSIZ];

    if (parse_filename) {
	(void) sprintf(where, "%s:%d",
		       parse_filename,
		       yylineno);
    } else {
	(void) sprintf(where, "%d",
		       yylineno);
    }

    return (where);
}


/*
 *	Limit check a number
 */
int
parse_limit_check(type, value, lower, upper)
const char *type;
int value;
int lower;
int upper;
{
    if ((value < lower) || ((upper != -1) && (value > upper))) {
	(void) sprintf(parse_error, "invalid %s value at '%d'",
		       type,
		       value);
	return (1);
    }
    trace(TR_PARSE, 0, "parse: %s %s: %d",
	  parse_where(),
	  type,
	  value);
    return (0);
}


/*	Translate a u_long IP address into a sockaddr_in	*/
void
parse_addr_long(sockaddr, addr)
sockaddr_un *sockaddr;
u_long addr;
{
    sockclear_in(sockaddr);
    sockaddr->in.sin_addr.s_addr = addr;

    trace(TR_PARSE, 0, "parse: %s IP_ADDR: %A",
	  parse_where(),
	  sockaddr);
}


/*
 *	Look up a string as a host or network name, returning it's IP address
 *	in normalized network byte order.  Also recognizes a network name of
 *	"default", translating to network 0.
 */
int
parse_addr_hname(addr, hname, host_ok, net_ok)
sockaddr_un *addr;
char *hname;
int host_ok, net_ok;
{
    u_long network;
    struct netent *netent;
    struct hostent *hostent;
    int net_unknown = TRUE;
    const char *errmsg = 0;

#ifdef	HOST_NOT_FOUND
    extern int h_errno;
    extern int h_nerr;
    extern char *h_errlist[];
    h_errno = HOST_NOT_FOUND;

#endif				/* HOST_NOT_FOUND */

    if (net_ok) {
	netent = getnetbyname(hname);
	if (netent) {
	    if (netent->n_addrtype != AF_INET) {
		/* XXX - Should we pretend it does not exist if it is not an INET name? */
		(void) sprintf(parse_error, "network not INET at '%s'",
			       hname);
		return (1);
	    }
	    network = netent->n_net;
	    if (network) {
		while (!(network & 0xff000000)) {
		    network <<= 8;
		}
	    }
	    parse_addr_long(addr, htonl(network));
	    return (0);
#ifdef	HOST_NOT_FOUND
	} else {
	    errmsg = "Unknown network";
	    net_unknown = TRUE;
#endif				/* HOST_NOT_FOUND */
	}
    }
    if (host_ok) {
	hostent = gethostbyname(hname);
	if (hostent) {
	    if (hostent->h_addrtype != AF_INET) {
		/* XXX - Should we pretend it does not exist if it is not an INET name? */
		(void) sprintf(parse_error, "host not INET at '%s'",
			       hname);
		return (1);
	    }
#ifdef	h_addr
	    if (hostent->h_addr_list[1]) {
		/* XXX - For a gateway we could use just the address on our network if there was only one */
		(void) sprintf(parse_error, "host has multiple addresses at '%s'",
			       hname);
		return (1);
	    }
#endif				/* h_addr */
	    memcpy((caddr_t) & network, hostent->h_addr, sizeof(network));
	    parse_addr_long(addr, network);
	    return (0);
	} else {
#ifdef	HOST_NOT_FOUND
	    if ((!h_errno || (h_errno == HOST_NOT_FOUND)) && net_unknown) {
		errmsg = "Unknown host/network";
	    } else if (h_errno && (h_errno < h_nerr)) {
		errmsg = h_errlist[h_errno];
	    } else {
		errmsg = "Unknown host";
	    }
#else				/* HOST_NOT_FOUND */
	    if (net_unknown) {
		errmsg = "Unknown host/network";
	    } else {
		errmsg = "Unknown host";
	    }
#endif				/* HOST_NOT_FOUND */
	}
    }
    (void) sprintf(parse_error, "error resolving '%s': %s",
		   hname,
		   errmsg);
    return (1);
}


/*
 *	Append an advlist to another advlist
 */
adv_entry *
parse_adv_append(old, new, free)
adv_entry *old, *new;
int free;
{
    adv_entry *alo, *aln, *last = NULL;

    /* Add this network to the end of the list */
    if (old) {
	for (alo = old; alo; alo = alo->adv_next) {
	    if (!alo->adv_next) {
		last = alo;
	    }
	    /* Scan list for duplicates */
	    for (aln = new; aln; aln = aln->adv_next) {
		if ((aln->adv_flag & ADVF_TYPE) != (alo->adv_flag & ADVF_TYPE)) {
		    continue;
		}
		switch (aln->adv_flag & ADVF_TYPE) {
		    case ADVF_TANY:
			break;
		    case ADVF_TGW:
			if (aln->adv_gwp == alo->adv_gwp) {
			    (void) sprintf(parse_error, "duplicate gateway in list at '%A'",
					   &aln->adv_gwp->gw_addr);
			    return ((adv_entry *) 0);
			}
			break;
		    case ADVF_TINTF:
			if (aln->adv_ifp == alo->adv_ifp) {
			    (void) sprintf(parse_error, "duplicate interface in list at '%A'",
			    (aln->adv_ifp->int_state & IFS_POINTOPOINT) ?
					   &aln->adv_ifp->int_dstaddr :
					   &aln->adv_ifp->int_addr);
			    return ((adv_entry *) 0);
			}
			break;
		    case ADVF_TAS:
			if (aln->adv_as == alo->adv_as) {
			    (void) sprintf(parse_error, "duplicate autonomous-system in list at '%u'",
					   aln->adv_as);
			    return ((adv_entry *) 0);
			}
			break;
		    case ADVF_TDM:
			/* XXX - make sure same address family */
			if (equal(&aln->adv_dm.dm_dest, &alo->adv_dm.dm_dest) &&
			    equal(&aln->adv_dm.dm_mask, &alo->adv_dm.dm_mask)) {
			    (void) sprintf(parse_error, "duplicate dest and mask in list at '%A mask %A'",
					   &aln->adv_dm.dm_dest,
					   &aln->adv_dm.dm_mask);
			    return ((adv_entry *) 0);
			}
			break;
		}
	    }
	}
	last->adv_next = new;
    } else {
	old = new;
    }
    if (new) {
	/* XXX - bump the refcount, then free it? */
	for (aln = new; aln; aln = aln->adv_next) {
	    aln->adv_refcount++;
	}
	if (free) {
	    adv_free_list(new);
	}
    }
    return (old);
}


/*
 *	Set a flag in the gw structure for each element in a list
 */
int
parse_gw_flag(list, proto, flag)
adv_entry *list;
proto_t proto;
flag_t flag;
{
    int n = 0;
    adv_entry *adv;

    for (adv = list; adv; adv = adv->adv_next) {
	adv->adv_gwp->gw_flags |= flag;
	adv->adv_gwp->gw_proto = proto;
	n++;
    }
    return (n);
}


/*
 *	Display an adv entry
 */
void
parse_adv_entry(list)
adv_entry *list;
{
    int first = TRUE;
    adv_entry *adv;

    if (list) {
	if (list->adv_proto) {
	    tracef("proto %s ",
		   gd_lower(trace_bits(rt_proto_bits, list->adv_proto)));
	}
	adv = list;
	do {
	    switch (adv->adv_flag & ADVF_TYPE) {
		case ADVF_TDM:
		    /* XXX - should not happen */
		case ADVF_TANY:
		    break;
		case ADVF_TGW:
		    tracef("%s%A ",
			   first ? "gateway " : "",
			   &adv->adv_gwp->gw_addr);
		    break;
		case ADVF_TINTF:
		    tracef("%s%A(%s) ",
			   first ? "interface " : "",
			   (adv->adv_ifp->int_state & IFS_POINTOPOINT) ?
			   &adv->adv_ifp->int_dstaddr :
			   &adv->adv_ifp->int_addr,
			   adv->adv_ifp->int_name);
		    break;
		case ADVF_TAS:
		    tracef("%s%d ",
			   first ? "as " : "",
			   adv->adv_as);
		    break;
	    }
	    first = FALSE;
	} while ((adv = adv->adv_next) && !(adv->adv_flag & ADVF_FIRST));
	switch (list->adv_flag & ADVF_OTYPE) {
	    case ADVF_OTNONE:
		break;
	    case ADVF_OTMETRIC:
		tracef("metric %d ", list->adv_metric);
		break;
	    case ADVF_OTPREFERENCE:
		tracef("preference %d ", list->adv_preference);
		break;
	}
    }
}


/*
 *	Display a dest_mask list
 */
void
parse_adv_destmask(dest, name1, name2)
adv_entry *dest;
const char *name1, *name2;
{
    adv_entry *adv;

    if (dest) {
	ADV_LIST(dest, adv) {
	    tracef("parse: %s%s%s%.*s %A mask %A",
		   parse_where(),
		   name1,
		   (adv->adv_flag & ADVF_NO) ? "no" : "",
		   name2 ? strlen(name2) : 0,
		   name2,
		   &adv->adv_dm.dm_dest,
		   &adv->adv_dm.dm_mask);
	    switch (adv->adv_flag & ADVF_OTYPE) {
		case ADVF_OTNONE:
		    break;
		case ADVF_OTMETRIC:
		    tracef(" metric %d", adv->adv_metric);
		    break;
		case ADVF_OTPREFERENCE:
		    tracef(" preference %d", adv->adv_preference);
		    break;
	    }
	    trace(TR_CONFIG, 0, " ;");
	} ADV_LISTEND;
    }
}


/*
 *	Append to an existing list
 */
void
parse_adv_list(name1, name2, list, dest)
const char *name1, *name2;
adv_entry *list, *dest;
{
    if (trace_flags & TR_CONFIG) {
	tracef("parse: %s\t%s ",
	       parse_where(),
	       name1);
	parse_adv_entry(list);
	trace(TR_CONFIG, 0, "{");
	parse_adv_destmask(dest, "\t\t", name2);
	trace(TR_CONFIG, 0, "parse: %s\t} ;",
	      parse_where());
    }
}


/*
 *	Display a propagate clause
 */
void
parse_adv_prop_list(list)
adv_entry *list;
{
    adv_entry *adv;

    if (list) {
	if (trace_flags & TR_CONFIG) {
	    tracef("parse: %s\tpropagate ",
		   parse_where());
	    parse_adv_entry(list);
	    trace(TR_CONFIG, 0, " {");
	    adv = list->adv_list;
	    if (adv) {
		do {
		    tracef("parse: %s\t\t",
			   parse_where());
		    parse_adv_entry(adv);
		    if (adv->adv_list) {
			trace(TR_CONFIG, 0, "{");
			parse_adv_destmask(adv->adv_list, "\t\t\t", "announce");
			tracef("parse: %s\t\t}",
			       parse_where());
		    }
		    trace(TR_CONFIG, 0, " ;");
		    do {
			adv = adv->adv_next;
		    } while (adv && !(adv->adv_flag & ADVF_FIRST));
		} while (adv);
	    }
	    trace(TR_CONFIG, 0, "parse: %s\t} ;",
		  parse_where());
	}
    }
}


/*
 *	Set interface struct with parsed values
 */
static void
parse_interface_set(ifp)
if_entry *ifp;
{
    ifp->int_state |= parse_flags;
    if (parse_metric > 0) {
	ifp->int_metric = parse_metric;
    }
    if (parse_preference) {
	ifp->int_preference = parse_preference;
    }
    if_display("parse", ifp);
}


/*	Set interface flags on specified interface list or all interfaces	*/
void
parse_interface(list)
adv_entry *list;
{
    if_entry *ifp;
    adv_entry *adv;

    if (list) {
	for (adv = list; adv; adv = adv->adv_next) {;
	    parse_interface_set(adv->adv_ifp);
	}
	adv_free_list(list);
    } else {
	IF_LIST(ifp) {
	    parse_interface_set(ifp);
	} IF_LISTEND(ifp) ;
    }
}


/*
 *	Switch to a new state if it is a valid progression from
 *	the current state
 */
int
parse_new_state(state)
int state;
{
    static const char *states[] =
    {
	"define",
	"protocol",
	"route",
	"control"
    };

    if (state < parse_state) {
	(void) sprintf(parse_error, "statement out of order");
	return (1);
    } else if (state > parse_state) {
	parse_state = state;
	trace(TR_PARSE, 0, "parse_new_state: %s %s",
	      parse_where(),
	      states[parse_state]);
    }
    return (0);
}


int
parse_metric_check(proto, metric)
proto_t proto;
int metric;
{
    const char *string;
    int limit_low, limit_high;

    switch (proto) {
#ifdef	PROTO_RIP
	case RTPROTO_RIP:
	    string = "RIP metric";
	    limit_low = LIMIT_RIP_LOW;
	    limit_high = LIMIT_RIP_HIGH;
	    break;
#endif				/* PROTO_RIP */
#ifdef	PROTO_HELLO
	case RTPROTO_HELLO:
	    string = "HELLO metric";
	    limit_low = LIMIT_HELLO_LOW;
	    limit_high = LIMIT_HELLO_HIGH;
	    break;
#endif				/* PROTO_HELLO */
#ifdef	PROTO_EGP
	case RTPROTO_EGP:
	    string = "EGP metric";
	    limit_low = LIMIT_EGP_LOW;
	    limit_high = LIMIT_EGP_HIGH;
	    break;
#endif				/* PROTO_EGP */
#ifdef	PROTO_BGP
	case RTPROTO_BGP:
	    string = "BGP metric";
	    limit_low = LIMIT_BGP_LOW;
	    limit_high = LIMIT_BGP_HIGH;
	    break;
#endif				/* PROTO_BGP */
	case RTPROTO_DIRECT:
	    string = "interface metric";
	    limit_low = LIMIT_INTERFACE_LOW;
	    limit_high = LIMIT_INTERFACE_HIGH;
	    break;
	default:
	    (void) sprintf(parse_error, "parse_metric_check: invalid protocol %x",
			   proto);
	    return (1);
    }
    return (parse_limit_check(string, metric, limit_low, limit_high));
}


/*
 *	Set metric for each element in list that does not have one
 */
adv_entry *
parse_adv_propagate(list, proto, metric, advlist)
adv_entry *list;
proto_t proto;
metric_t metric;
adv_entry *advlist;
{
    adv_entry *adv;

    for (adv = list; adv; adv = adv->adv_next) {
	adv->adv_proto = proto;
	adv->adv_list = advlist;
	if (metric >= 0) {
	    adv->adv_flag |= ADVF_OTMETRIC;
	    adv->adv_metric = metric;
	}
    }
    return (list);
}


/*
 *	Set preference for each elmit in list that does not have one
 */
void
parse_adv_preference(list, proto, preference)
adv_entry *list;
proto_t proto;
pref_t preference;
{
    for (; list; list = list->adv_next) {
	if ((list->adv_flag & ADVF_OTYPE) == ADVF_OTNONE) {
	    list->adv_proto = proto;
	    list->adv_flag |= ADVF_OTPREFERENCE;
	    list->adv_preference = preference;
	}
    }
}


/*
 *	Return a pointer to a duplicate of this adv_entry
 */
adv_entry *
parse_adv_dup(old)
adv_entry *old;
{
    adv_entry *new = NULL, *adv, *root;

    root = NULL;

    for (; old; old = old->adv_next) {
	adv = adv_alloc(old->adv_flag, old->adv_proto);
	memcpy((caddr_t) adv, (caddr_t) old, sizeof(*adv));
	if (!root) {
	    root = adv;
	} else {
	    new->adv_next = adv;
	}
	if (adv->adv_list) {
	    adv->adv_list->adv_refcount++;
	}
	new = adv;
    }
    return (root);
}


/*
 *	If the pointed to table of adv lists for interfaces does not
 *	exist, create it.
 */
adv_entry **
parse_adv_interface(list)
adv_entry ***list;
{
    if (!*list) {
	*list = (adv_entry **) calloc((u_int) (int_index_max + 1), sizeof(adv_entry));
	if (!*list) {
	    trace(TR_ALL, LOG_ERR, "parse_adv_interface: calloc %m");
	    quit(errno);
	}
    }
    return (*list);
}


/*
 *	Lookup the entry in the list for the exterior protocol and append this list to it.
 */
int
parse_adv_ext(advlist, adv)
adv_entry **advlist;
adv_entry *adv;
{
    adv_entry *list;

    ADV_LIST(*advlist, list) {
	if (adv->adv_as == list->adv_as) {
	    break;
	}
    } ADV_LISTEND;

    if (!list) {
	list = adv_alloc(ADVF_TAS, adv->adv_proto);
	list->adv_as = adv->adv_as;
	*advlist = parse_adv_append(*advlist, list, TRUE);
	if (!*advlist) {
	    return (0);
	}
    }
    list->adv_list = parse_adv_append(list->adv_list, adv, TRUE);
    if (!list->adv_list) {
	return (0);
    }
    return (1);
}

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