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

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

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


/*
 *	Hello output routines were taken from Mike Petry (petry@trantor.umd.edu)
 *	Also, hello input routines were written by Bill Nesheim, Cornell
 *	CS Dept,  Currently at nesheim@think.com
 */

#include "include.h"
#include "hello.h"
#if	defined(_IBMR2)
#include <time.h>
#endif				/* defined(_IBMR2) */
#include <sys/time.h>

#ifdef	PROTO_HELLO

#define	HELLO_TIMER_UPDATE	0
#define	HELLO_TIMER_FLASH	1

static task *hello_task = (task *) 0;
static time_t hello_next_flash = (time_t) 0;

int hello_pointopoint = FALSE;		/* Are we ONLY doing pointopoint HELLO? */
int hello_supplier = -1;		/* Are we broadcasting HELLO protocols? */
int doing_hello = FALSE;		/* Are we running HELLO protocols? */
metric_t hello_default_metric;	/* Default metric to use when propogating */
pref_t hello_preference;	/* Preference for HELLO routes */

int hello_n_trusted = 0;		/* Number of trusted gateways */
int hello_n_source = 0;			/* Number of source gateways */
gw_entry *hello_gw_list = NULL;		/* List of HELLO gateways */
adv_entry *hello_accept_list = NULL;	/* List of nets to accept from HELLO */
adv_entry *hello_propagate_list = NULL;	/* LIst of sources to propagates routes to HELLO */
adv_entry **hello_int_accept = NULL;	/* List of accept lists per interface */
adv_entry **hello_int_propagate = NULL;	/* List of propagate lists per interface */


metric_t hop_to_hello[] =
{					/* Translate interface metric to hello metric */
    0,					/* 0 */
    100,				/* 1 */
    148,				/* 2 */
    219,				/* 3 */
    325,				/* 4 */
    481,				/* 5 */
    713,				/* 6 */
    1057,				/* 7 */
    1567,				/* 8 */
    2322,				/* 9 */
    3440,				/* 10 */
    5097,				/* 11 */
    7552,				/* 12 */
    11190,				/* 13 */
    16579,				/* 14 */
    24564,				/* 15 */
    30000				/* 16 */
};


/* Routines for support of the hello window system */

/*
 * initialize the sliding HELLO history window.
 */
static void
hello_win_init(hwp, tdelay)
struct hello_win *hwp;
int tdelay;
{
    int msf = 0;

    while (msf < HWINSIZE)
	hwp->h_win[msf++] = DELAY_INFINITY;
    hwp->h_index = 0;
    hwp->h_min = tdelay;
    hwp->h_min_ttl = 0;
    hwp->h_win[0] = tdelay;
}


/*
 * add a HELLO derived time delay to the route entries HELLO window.
 */
static void
hello_win_add(hwp, tdelay)
struct hello_win *hwp;
int tdelay;
{
    int msf, t_index = 0;

    hwp->h_index++;
    if (hwp->h_index >= HWINSIZE)
	hwp->h_index = 0;
    hwp->h_win[hwp->h_index] = tdelay;
    if (tdelay > hwp->h_min)
	hwp->h_min_ttl++;
    else {
	hwp->h_min = tdelay;
	hwp->h_min_ttl = 0;
    }
    if (hwp->h_min_ttl >= HWINSIZE) {
	hwp->h_min = DELAY_INFINITY;
	for (msf = 0; msf < HWINSIZE; msf++)
	    if (hwp->h_win[msf] <= hwp->h_min) {
		hwp->h_min = hwp->h_win[msf];
		t_index = msf;
	    }
	hwp->h_min_ttl = 0;
	if (t_index < hwp->h_index)
	    hwp->h_min_ttl = hwp->h_index - t_index;
	else if (t_index > hwp->h_index)
	    hwp->h_min_ttl = HWINSIZE - (t_index - hwp->h_index);
    }
}


/*
 *	Dump info about a HELLO route
 */
static void
hello_rt_dump(fd, rt)
FILE *fd;
rt_entry *rt;
{
    int cnt, ind;
    struct hello_win *hwp = (struct hello_win *) rt->rt_data->rtd_data;

    (void) fprintf(fd, "\t\t\tMinimum HELLO time delay in last %d updates: %d\n",
		   HWINSIZE,
		   hwp->h_min);
    (void) fprintf(fd, "\tLast %d HELLO time delays:\n\t\t",
		   HELLO_REPORT);
    ind = hwp->h_index;
    for (cnt = HELLO_REPORT; cnt; cnt--) {
	(void) fprintf(fd, "%d ",
		       hwp->h_win[ind]);
	if (++ind >= HWINSIZE) {
	    ind = 0;
	}
    }
    (void) fprintf(fd, "\n");
}

/*
 *	Trace a HELLO packet
 */
/* ARGSUSED */
static void
hello_trace(comment, src, dst, hello, length, nets)
char *comment;
struct sockaddr_in *src, *dst;
char *hello;
int length, nets;
{
    int i;
    const char *cp;
    char *end = hello + length;
    struct hm_hdr hm_hdr;
    struct hellohdr hellohdr;
    struct type0pair type0pair;
    struct type1pair type1pair;
    struct iovec iov;

    tracef("HELLO %s %A -> %A  %d bytes",
	   comment,
	   src,
	   dst,
	   length);
    if (nets >= 0) {
	tracef("  %d nets", nets);
    }

    /* Calculate the checksum of this packet */
    iov.iov_base = hello;
    iov.iov_len = length;
    if (gd_inet_cksum(&iov, 1, length)) {
	tracef(" *checksum bad*");
    }
    trace(TR_HELLO, 0, NULL);

    PickUp_hellohdr(hello, hellohdr);

    if (trace_flags & TR_UPDATE) {
	switch (hellohdr.h_date & H_DATE_BITS) {
	    case H_DATE_LEAPADD:
		cp = "add_leap_second ";
		break;
	    case H_DATE_LEAPDEL:
		cp = "del_leap_second ";
		break;
	    case H_DATE_UNSYNC:
		cp = "unsync ";
		break;
	    default:
		cp = "";
	}
	hellohdr.h_date &= ~H_DATE_BITS;
	trace(TR_NOSTAMP | TR_HELLO, 0, "%s %s%d/%d/%d %02d:%02d:%02d.%03d GMT  tstp %d",
	      comment,
	      cp,
	      (hellohdr.h_date >> H_DATE_MON_SHIFT) & H_DATE_MON_MASK,
	      (hellohdr.h_date >> H_DATE_DAY_SHIFT) & H_DATE_DAY_MASK,
	      ((hellohdr.h_date >> H_DATE_YEAR_SHIFT) & H_DATE_YEAR_MASK) + H_DATE_YEAR_BASE,
	      hellohdr.h_time / (60 * 60 * 1000),
	      (hellohdr.h_time / (60 * 1000)) % 60,
	      (hellohdr.h_time / (1000)) % 60,
	      hellohdr.h_time % 1000,
	      hellohdr.h_tstp);

	while (hello < end) {
	    PickUp_hm_hdr(hello, hm_hdr);
	    trace(TR_NOSTAMP | TR_HELLO, 0, "%s\ttype %d  count %d", comment, hm_hdr.hm_type, hm_hdr.hm_count);
	    for (i = 0; i < hm_hdr.hm_count; i++) {
		switch (hm_hdr.hm_type) {
		    case 0:
			PickUp_type0pair(hello, type0pair);
			trace(TR_NOSTAMP | TR_HELLO, 0, "%s\t\tdelay %d  offset %d",
			      comment,
			      type0pair.d0_delay,
			      type0pair.d0_offset);
			break;
		    case 1:
			PickUp_type1pair(hello, type1pair);
			trace(TR_NOSTAMP | TR_HELLO, 0, "%s\t\t%-15s  delay %5d  offset %d",
			      comment,
			      inet_ntoa(type1pair.d1_dst),
			      type1pair.d1_delay,
			      type1pair.d1_offset);
			break;
		    default:
			trace(TR_NOSTAMP | TR_HELLO, 0, "%s\t\tInvalid type - giving up!", comment);
			return;
		}
	    }
	}
	trace(TR_HELLO, 0, NULL);
    }
}


/*
 * hello_send():
 * 	Fill in the hello header and checksum, then send the packet.
 */
static void
hello_send(tp, dst, src, flags, iov, length, n_nets)
task *tp;
struct sockaddr_in *dst, *src;
flag_t flags;
struct iovec *iov;
int length;				/* length in octets of hello packet */
int n_nets;				/* number of nets in this update for debugging */
{
    char *hello = iov->iov_base;
    struct hellohdr hellohdr;
    struct tm *gmt;
    struct timeval timep;
    int error = FALSE;

    (void) gettimeofday(&timep, (struct timezone *) 0);
    gmt = (struct tm *) gmtime(&timep.tv_sec);

    /*
     * set the date field in the HELLO header.  Be very careful here as
     * the last two bits (14&15) should be set so the Fuzzware doesn't use
     * this packet to synchronize its Master Clock.  Using bitwise OR's
     * instead of addition just to be safe when dealing with h_date which
     * is an unsigned short.
     */
    hellohdr.h_date = ((gmt->tm_year - H_DATE_YEAR_BASE) & H_DATE_YEAR_MASK) |
		 ((gmt->tm_mday & H_DATE_DAY_MASK) << H_DATE_DAY_SHIFT) |
	    (((gmt->tm_mon + 1) & H_DATE_MON_MASK) << H_DATE_MON_SHIFT) |
				       H_DATE_UNSYNC;
    /*
     * milliseconds since midnight UT of current day
     */
    hellohdr.h_time = (gmt->tm_sec + gmt->tm_min * 60 + gmt->tm_hour * 3600)
	* 1000 + timep.tv_usec / 1000;
    /*
     * 16 bit field used in rt calculation,  0 for ethernets
     */
    hellohdr.h_tstp = 0;
    hellohdr.h_cksum = 0;
    PutDown_hellohdr(hello, hellohdr);
    hellohdr.h_cksum = gd_inet_cksum(iov, 1, length);
    hello = iov->iov_base;
    PutDown(hello, hellohdr.h_cksum);
    if (task_send_packet(tp,
			 iov->iov_base,
			 length,
			 flags,
			 (sockaddr_un *) dst) < 0) {
	error = TRUE;
    }
    TRACE_HELLOPKT(error ? "*NOT* SENT" : "SENT",
		   src,
		   dst,
		   iov->iov_base,
		   length,
		   n_nets);
    return;
}


/*
 *	Process an incomming HELLO packet
 */

static void
hello_recv(tp)
task *tp;
{
    int count;
    int i;
    flag_t rte_table;
    char *hello, *end;
    struct hm_hdr hm_hdr;
    struct type1pair type1pair;
    rt_entry *rt;
    if_entry *ifp, *ifpc;
    gw_entry *gwp;
    pref_t preference;
    struct sockaddr_in src;
    struct sockaddr_in dst;

    if (task_receive_packet(tp, &count)) {
	return;
    }
    hello = (char *) recv_iovec[RECV_IOVEC_DATA].iov_base;
    end = hello + recv_ip.ip_len;

    sockclear_in(&src);
    sockclear_in(&dst);
    src.sin_addr = recv_ip.ip_src;	/* struct copy */
    dst.sin_addr = recv_ip.ip_dst;	/* struct copy */
    TRACE_HELLOPKT("RECV", &src, &dst, hello, recv_ip.ip_len, -1);

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

    /* If we have a list of trusted gateways, verify that this gateway is trusted */
    if (hello_n_trusted && !(gwp->gw_flags & GWF_TRUSTED)) {
	return;
    }
    /* Do we share a net with the sender? */
    if ((ifp = if_withdst((sockaddr_un *) & recv_addr)) == NULL) {
	trace(TR_EXT, LOG_WARNING, "hello_recv: gw %A no shared net?",
	      &recv_addr);
	return;
    }
    if (ifp->int_state & IFS_NOHELLOIN) {
	return;
    }
    /*
     * Are we talking to ourselves?
     *
     * if_withaddr() handles PTP links also.  If packet came
     * from other end of a PTP, let it fall through for further
     * processing.  We shouldn't ever hear our own HELLOs on a PTP link.
     */
    ifpc = if_withaddr((sockaddr_un *) & recv_addr);
    if (ifpc) {
	if_rtupdate(ifpc);
	if ((ifpc->int_state & IFS_POINTOPOINT) == 0) {
	    return;
	}
    }
    /*
     * update the interface timer on interface the packet came in on.
     */
    if_rtupdate(ifp);

    gwp = gw_timestamp(&hello_gw_list, hello_task->task_rtproto, (sockaddr_un *) & recv_addr);
    /* check the hello checksum */

    if (gd_inet_cksum(&recv_iovec[RECV_IOVEC_DATA], 1, recv_ip.ip_len) != (u_short) 0) {
	trace(TR_HELLO, LOG_WARNING, "hello_recv: bad HELLO checksum from %A",
	      &recv_addr);
	return;
    }

    /* message is made up of one or more sub messages */
    rt_open(tp);

    hello += Size_hellohdr;
    while (hello < end) {
	PickUp_hm_hdr(hello, hm_hdr);
	switch (hm_hdr.hm_type) {
	    case 0:
		hello += Size_type0pair * hm_hdr.hm_count;
		/* not interested in type 0 messages */
		break;
	    case 1:
		for (i = 0; i < hm_hdr.hm_count; i++) {
		    preference = hello_preference;
		    PickUp_type1pair(hello, type1pair);
		    sockclear_in(&dst);
		    dst.sin_addr = type1pair.d1_dst;

		    /*  is this new net acceptable? */
		    if (!is_valid_in((sockaddr_un *) & dst,
				     hello_accept_list,
				     INT_CONTROL(hello_int_accept, ifp),
				     gwp->gw_accept,
				     &preference)) {
			continue;
		    }
		    /* Force delay to be valid */
		    if (type1pair.d1_delay > DELAY_INFINITY) {
			type1pair.d1_delay = DELAY_INFINITY;
		    }
		    /*
	             *	Add the interface metric converted to a HELLO delay.
	             */
		    type1pair.d1_delay += hop_to_hello[ifp->int_metric] + HELLO_DELAY;

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

		    /* check for internal route */
		    rt = rt_locate(rte_table, (sockaddr_un *) & dst, tp->task_rtproto);
		    if (!rt) {		/* new route */
			if (type1pair.d1_delay >= DELAY_INFINITY) {
			    continue;
			}
			rt = rt_add((sockaddr_un *) & dst,
				    (sockaddr_un *) 0,
				    (sockaddr_un *) & recv_addr,
				    gwp,
				    (metric_t) type1pair.d1_delay,
				    rte_table,
				    hello_task->task_rtproto,
				    0,
				    (time_t) 0,
				    preference);
			if (rt) {
			    rt->rt_data = rtd_alloc(sizeof(struct hello_win));
			    rt->rt_data->rtd_dump = hello_rt_dump;
			    hello_win_init((struct hello_win *) rt->rt_data->rtd_data, (metric_t) type1pair.d1_delay);
			}
		    } else {		/* update existing route */
			if (type1pair.d1_delay >= DELAY_INFINITY) {
			    /* destination now unreachable */
			    if (equal(&rt->rt_router, &recv_addr)) {
				(void) rt_unreach(rt);
				hello_win_add((struct hello_win *) rt->rt_data->rtd_data, DELAY_INFINITY);
			    }
			    continue;
			}
			if (equal(&rt->rt_router, &recv_addr)) {
			    if ((METRIC_DIFF(rt->rt_metric, type1pair.d1_delay) >= HELLO_HYST(rt->rt_metric)) ||
				(rt->rt_state & RTS_HOLDDOWN)) {
				if (rt_change(rt,
					      (sockaddr_un *) & recv_addr,
					   (metric_t) type1pair.d1_delay,
					      (time_t) 0,
					      preference)) {
				    hello_win_add((struct hello_win *) rt->rt_data->rtd_data, (metric_t) type1pair.d1_delay);
				}
			    }
			    rt_refresh(rt);
			} else {
			    if (rt->rt_state & RTS_HOLDDOWN) {
				continue;
			    }
			    /*
	                     * use the new gateway.
	                     */
			    if (((type1pair.d1_delay < rt->rt_metric) &&
				 (METRIC_DIFF(type1pair.d1_delay, rt->rt_metric) >= HELLO_HYST(rt->rt_metric))) ||
				(rt->rt_timer > rt->rt_timer_max / 2 &&
				 !(rt->rt_state & (RTS_CHANGED | RTS_REFRESH)))) {
				if (rt_change(rt,
					      (sockaddr_un *) & recv_addr,
					   (metric_t) type1pair.d1_delay,
					      (time_t) 0,
					      hello_preference)) {
				    hello_win_init((struct hello_win *) rt->rt_data->rtd_data, (metric_t) type1pair.d1_delay);
				}
			    }
			}
		    }
		}			/* for each advertized net */
		break;
	    default:
		trace(TR_INT, LOG_ERR, "hello_recv: invalid type %d", hm_hdr.hm_type);
	}				/* switch (mh->hm_type) */
    }					/* while not end of packet */

    if (rt_close(tp, gwp, (int) hm_hdr.hm_count)) {
	task_flash(tp);
    }
}


/*ARGSUSED*/
static void
hello_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;
{
    int length;
    char *hello;
    char *save_ptr, *hm_hdr_ptr;
    rt_entry *rt;
    struct hm_hdr hm_hdr;
    struct type1pair type1pair;
    metric_t delay = 0, split_horizon;
    struct iovec *iovec = &recv_iovec[RECV_IOVEC_DATA];	/* Share receive buffer */

    hello = iovec->iov_base + Size_hellohdr;
    hm_hdr_ptr = hello;
    hello += Size_hm_hdr;
    save_ptr = hello;
    hm_hdr.hm_type = 1;
    hm_hdr.hm_count = 0;

    RT_TABLE(rt) {
	if (rt->rt_state & RTS_NOADVISE) {
	    continue;
	}
	if (flash_update && (hello_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;
	    }
	}

	/* XXX - is this adequate? */
	if ((rt->rt_proto & hello_task->task_rtproto) && (ifp == rt->rt_ifp)) {
	    split_horizon = DELAY_INFINITY;
	} else {
	    split_horizon = 0;
	}

	if (rt->rt_ifp->int_state & IFS_LOOPBACK) {
	    /* Routes via the loopback interface must have an explicit metric */ 
	    delay = DELAY_INFINITY;
	}
		
	delay = (rt->rt_proto & RTPROTO_DIRECT) ? HELLO_DELAY : hello_default_metric;

	if (!propagate(rt,
		       hello_task->task_rtproto,
		       hello_propagate_list,
		       INT_CONTROL(hello_int_propagate, ifp),
		       gwp ? gwp->gw_propagate : NULL,
		       &delay)) {
	    continue;
	}
	delay += hop_to_hello[ifp->int_metric];
	if (delay > DELAY_INFINITY) {
	    delay = DELAY_INFINITY;
	}
	if (split_horizon) {
	    delay = split_horizon;
	}
	if (!(rt->rt_ifp->int_state & IFS_UP) || (rt->rt_state & (RTS_HOLDDOWN | RTS_DELETE))) {
	    delay = DELAY_INFINITY;
	}
	if (flash_update && (delay == DELAY_INFINITY) && (rt->rt_ifp == ifp) && (rt->rt_proto & hello_task->task_rtproto)) {
	    /* Don't flash deleted or unreachable routes back to their source */
	    continue;
	}
	if ((hello + Size_type1pair - iovec->iov_base) >= HELLOMAXPACKETSIZE) {
	    /* XXX - fragments should be rate limited to one every 0.5-2.0 seconds */
	    /* XXX - Maybe HELLO should have a random timer running all the time */
	    length = hello - iovec->iov_base;
	    hello = hm_hdr_ptr;
	    PutDown_hm_hdr(hello, hm_hdr);
	    hello_send(tp, dst, src, 0, iovec, length, (int) hm_hdr.hm_count);
	    hello = save_ptr;
	    hm_hdr.hm_count = 0;
	}
	type1pair.d1_delay = delay;
	type1pair.d1_offset = 0;	/* should be signed clock offset */
	type1pair.d1_dst = rt->rt_dest.in.sin_addr;	/* struct copy */

	PutDown_type1pair(hello, type1pair);
	hm_hdr.hm_count++;
    } RT_TABLEEND;

    if (hm_hdr.hm_count) {
	length = hello - iovec->iov_base;
	PutDown_hm_hdr(hm_hdr_ptr, hm_hdr);
	hello_send(tp, dst, src, 0, iovec, length, (int) hm_hdr.hm_count);
    }
}


/*
 *	call hello_supply to supply hello packets to all our nets
 */
/*ARGSUSED*/
static void
hello_job(tip, interval)
timer *tip;
time_t interval;
{
    task_toall(tip->timer_task,
	       hello_supply,
	       hello_pointopoint,
	       IFS_NOHELLOOUT,
	       hello_n_source ? hello_gw_list : NULL,
	       FALSE);
}


/*
 *	send a flash update packet
 */
/*ARGSUSED*/
static void
hello_do_flash(tip, interval)
timer *tip;
time_t interval;
{
    trace(TR_TASK, 0, "hello_do_flash: Doing flash update for HELLO");
    task_toall(hello_task,
	       hello_supply,
	       hello_pointopoint,
	       IFS_NOHELLOOUT,
	       hello_n_source ? hello_gw_list : NULL,
	       TRUE);
    hello_next_flash = (time_t) 2 + time_sec;
    trace(TR_TASK, 0, "hello_do_flash: Flash update done, none before %T", hello_next_flash);
}


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



/*
 *	Cleanup before re-init
 */
/*ARGSUSED*/
static void
hello_cleanup(tp)
task *tp;
{
    adv_cleanup(&hello_n_trusted, &hello_n_source, hello_gw_list,
		&hello_accept_list, &hello_propagate_list,
		&hello_int_accept, &hello_int_propagate);
}


/*
 *	Dump info about HELLO
 */
static void
hello_dump(fd)
FILE *fd;
{
    (void) fprintf(fd, "HELLO:\n");
    (void) fprintf(fd, "\tDefault metric: %d\t\tDefault preference: %d\n",
		   hello_default_metric,
		   hello_preference);
    if (hello_gw_list) {
	(void) fprintf(fd, "\tActive gateways:\n");
	gw_dump(fd, "\t\t", hello_gw_list);
    }
    control_accept_dump(fd, 1, hello_accept_list, hello_int_accept, hello_gw_list);
    control_propagate_dump(fd, 1, hello_propagate_list, hello_int_propagate, hello_gw_list);
    (void) fprintf(fd, "\n\n");
}


/*
 *	Initialize HELLO socket and task
 */
/*ARGSUSED*/
void
hello_init()
{
    int hello_socket = 1;
    if_entry *ifp;
    void (*flash) () = hello_flash;	/* Hack for UTX/32 and Ultrix */

    if (doing_hello) {
	if (!hello_task) {
	    if (hello_supplier < 0) {
		if (n_interfaces > 1) {
		    hello_supplier = TRUE;
		    trace(TR_ALL, LOG_NOTICE, "hello_init: Acting as HELLO supplier to our direct nets");
		}
		IF_LIST(ifp) {
		    if (ifp->int_state & IFS_POINTOPOINT) {
			hello_supplier = TRUE;
			trace(TR_INT, 0, "init_hello: PointoPoint HELLO supplier to: %s", ifp->int_name);
		    }
		} IF_LISTEND(ifp) ;
	    }
	    if (hello_supplier < 0) {
		hello_supplier = FALSE;
	    }
	    if ((hello_socket = task_get_socket(AF_INET, SOCK_RAW, IPPROTO_HELLO)) < 0) {
		quit(errno);
	    }
	    hello_task = task_alloc("HELLO");
	    hello_task->task_flags = TASKF_IPHEADER;
	    hello_task->task_proto = IPPROTO_HELLO;
	    hello_task->task_socket = hello_socket;
	    hello_task->task_rtproto = RTPROTO_HELLO;
	    hello_task->task_recv = hello_recv;
	    hello_task->task_cleanup = hello_cleanup;
	    hello_task->task_dump = hello_dump;

	    if (hello_supplier) {
		hello_task->task_flash = flash;
		(void) timer_create(hello_task,
				    HELLO_TIMER_UPDATE,
				    "Update",
				    0,
				    (time_t) HELLO_TIMERRATE,
				    hello_job);
		if (task_set_option(hello_task,
				    TASKOPTION_RECVBUF,
				    (caddr_t) (16 * 1024)) < 0) {
		    quit(errno);
		}
		if (task_set_option(hello_task,
				    TASKOPTION_DONTROUTE,
				    (caddr_t) TRUE) < 0) {
		    quit(errno);
		}
	    }
	    if (!task_create(hello_task, HELLOMAXPACKETSIZE)) {
		quit(EINVAL);
	    }
	    if (HELLO_TIMERRATE < rt_timer->timer_interval) {
		timer_interval(rt_timer, (time_t) HELLO_TIMERRATE);
	    }
	}
	if (hello_supplier) {
	    if_rtactive = TRUE;		/* Indicate we are broadcasting */
	    ignore_redirects = TRUE;	/* Gateways don't listen to redirects */
	}
    } else {
	hello_cleanup((task *) 0);
	if (hello_task) {
	    task_delete(hello_task);
	    hello_task = (task *) 0;
	}
    }
}


#endif				/* PROTO_HELLO */

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