Source to bsd/netat/ddp_lap.c


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @[email protected]
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @[email protected]
 */

/*
 *	Copyright (c) 1988, 1989, 1993-1998 Apple Computer, Inc. 
 *
 *	The information contained herein is subject to change without
 *	notice and  should not be  construed as a commitment by Apple
 *	Computer, Inc. Apple Computer, Inc. assumes no responsibility
 *	for any errors that may appear.
 *
 *	Confidential and Proprietary to Apple Computer, Inc.
 */

/* at_elap.c: 2.0, 1.29; 10/4/93; Apple Computer, Inc. */

/* This is the file which implements all the streams driver 
 * functionality required for EtherTalk.
 */



#define RESOLVE_DBG				/* for debug.h global resolution */
#include <sysglue.h>
#include <sys/malloc.h>
#include <at/appletalk.h>
#include <at/ddp.h>
#include <at/elap.h>
#include <lap.h>  /* for at_statep */
#include <routing_tables.h>     /* rtmp+zip table structs  */

#include <at/at_lap.h>
#include <at_pat.h>
#include <at_elap.h>
#include <at_aarp.h>
#include <at_ddp.h>
#include <at_zip.h>
#include <nbp.h>
#include <at_snmp.h>
#include <atlog.h>

#ifndef NULL
#define NULL 0
#endif

	/* globals */

elap_specifics_t	elap_specifics[IF_TYPE_ET_MAX];
at_state_t 	at_state;		/* global state of AT network */
at_state_t *at_statep = &at_state;
char *if_types[] = { IF_TYPE_1, IF_TYPE_2, IF_TYPE_3, IF_TYPE_4, IF_TYPE_5};
gref_t *shutdown_gref = NULL;
gref_t *at_qioctl = NULL;
gbuf_t *at_mioctl = NULL;
gbuf_t *at_delay_m = NULL;
gref_t *at_delay_gref = NULL;
int (*at_delay_func)() = NULL;
int at_delay_errno = 0;
char at_delay_flag = 0;
char at_ddp_debug;
int RoutingMix= 2000; /* default for nbr of ppsec */
snmpFlags_t		snmpFlags;

void ddp_bit_reverse();

	/* snmp defines */
#define MAX_BUFSIZE		8192   
#define MAX_RTMP		(MAX_BUFSIZE/sizeof(RT_entry)-1)
#define MAX_NBP 		\
	((MAX_BUFSIZE - SNMP_NBP_HEADER_SIZE)/sizeof(snmpNbpEntry_t)-1)
#define MAX_NBP_BYTES	(MAX_NBP * sizeof(snmpNbpEntry_t))
#define MAX_ZIP			(MAX_BUFSIZE/sizeof(ZT_entry)-1)
#define MAX_RTMP_BYTES	(MAX_RTMP * sizeof(RT_entry))
#define MAX_ZIP_BYTES	(MAX_ZIP * sizeof(ZT_entry))

	/* externs */
extern at_if_t 		*ifID_table[];
extern at_if_t		at_ifQueueHead;
extern int xpatcnt;
extern snmpStats_t	snmpStats;
extern atlock_t ddpinp_lock;
extern atlock_t arpinp_lock;
extern short appletalk_inited;
pat_unit_t pat_units[IF_TOTAL_MAX];


	/* protos */
extern snmpAarpEnt_t * getAarp(int *);
extern RT_entry *rt_getNextRoute(int);
extern ZT_entryno *zt_getNextZone(int);
extern int setLocalZones(at_nbptuple_t * , int);
extern int getRTRLocalZone(if_zone_t *);
extern at_nvestr_t *getDefZone(int);
StaticProc void getIfNames(if_name_t *);
StaticProc void get_ifs_stat();
StaticProc void add_route();
StaticProc int set_zones();
StaticProc void pat_init();
StaticProc void dodefer();
StaticProc int domcast();
StaticProc int elap_offline();
StaticProc void routerShutdown();
StaticProc void elap_hangup();
static getSnmpCfg();
	/* temp globals, etc. */

#define	NDEFERS	20
/* *** static int ndefers = NDEFERS;   not currently used *** */
static int deferno = 0;
static int gotIfs = 0;

#define ZT_BAD ZT_MAXENTRY +1



/***********************************************************************
 * lap_close()
 *
 **********************************************************************/
int lap_close(gref)
	gref_t *gref;
{
#ifdef CHECK_DDPR_FLAG
	extern int ddprunning_flag;
#endif
	int s;

	ddp_close(gref); /* for 2225395 */
	ATDISABLE(s, ddpinp_lock);
	if (gref == shutdown_gref) {
#ifdef CHECK_DDPR_FLAG
		while (ddprunning_flag)
			;
#endif
		if (at_mioctl != NULL) {
			gbuf_freem(at_mioctl);
			at_mioctl = NULL;
		}
		shutdown_gref = NULL;
		xpatcnt = 0;
		at_statep->flags = 0;
	}
	ATENABLE(s, ddpinp_lock);

	return 0;
}

StaticProc
validate_msg_size(m, gref, elapp)
	register gbuf_t *m;
	gref_t		*gref;
	elap_specifics_t **elapp;

/* checks ioctl message type for minimum expected message size & 
   sends error back if size invalid
*/
{
	register ioc_t		*iocbp;
	register at_elap_cfg_t		*cfgp;
	int i, size = 1;
	int if_specific = FALSE;
	
	*elapp = NULL;		
	iocbp = (ioc_t *) gbuf_rptr(m);

	switch (iocbp->ioc_cmd) {
	    case LAP_IOC_ONLINE:
		case LAP_IOC_OFFLINE:
		case ELAP_IOC_SWITCHZONE:
		case ELAP_IOC_SET_CFG:
		case ELAP_IOC_SET_ZONE :
			size = sizeof(at_elap_cfg_t);
			if_specific = TRUE;
			break;
		case LAP_IOC_ADD_ROUTE:
			size = sizeof(RT_entry);
			break;
		case LAP_IOC_ADD_ZONE:
			size = sizeof(if_zone_info_t);
			break;
		case LAP_IOC_GET_ROUTE:
			size = sizeof(RT_entry);
			break;
		case LAP_IOC_GET_ZONE:
			size = sizeof(ZT_entryno);
			break;
		case LAP_IOC_GET_IFID:
			size = sizeof(at_if_name_t);
			break;
		case LAP_IOC_SET_DBG:
			size = sizeof(dbgBits_t);
			break;
		case LAP_IOC_SNMP_GET_CFG:
		case LAP_IOC_SNMP_GET_AARP:
		case LAP_IOC_SNMP_GET_ZIP:
		case LAP_IOC_SNMP_GET_RTMP:
		case LAP_IOC_GET_DEFAULT_ZONE:
		case LAP_IOC_SNMP_GET_NBP:
			size = sizeof(int);
			break;
		case LAP_IOC_SET_MIX:
			size = sizeof(short);
			break;
		case LAP_IOC_GET_LOCAL_ZONE:
			size = sizeof(if_zone_t);
			break;
		case LAP_IOC_IS_ZONE_LOCAL:
			size = sizeof(at_nvestr_t);
			break;
		case LAP_IOC_CHECK_STATE:
			size = 1;
			break;
		case LAP_IOC_SET_DEFAULT_ZONES:
			size = sizeof(int) * IF_TOTAL_MAX;
			break;

		case ELAP_IOC_GET_STATS:
		case ELAP_IOC_GET_CFG:
		case LAP_IOC_GET_DBG:
		case LAP_IOC_GET_IFS_STAT: 
		case LAP_IOC_ROUTER_START:
		case LAP_IOC_ROUTER_SHUTDOWN:
		case LAP_IOC_ROUTER_INIT:
		case LAP_IOC_DO_DEFER:
		case LAP_IOC_DO_DELAY:
		case LAP_IOC_SHUT_DOWN:
		case LAP_IOC_SNMP_GET_DDP:
		case LAP_IOC_GET_MODE:
		case LAP_IOC_GET_IF_NAMES:
			size = 0;
			break;
				   		/* these ioctls  send variable length data */
		case LAP_IOC_SET_LOCAL_ZONES:
			size = -1;
			break;
		default:
			dPrintf(D_M_ELAP, D_L_ERROR, ("elap_wput: unknown ioctl\n"));
			goto error;
	}

	if (size == 0) {				/* a non-data ioctl */
		return(0);
	}

	/* if I/F related, validate further */
	if (if_specific) {
	  	/* get elapp from msg */
		int lap_id;
		cfgp = (at_elap_cfg_t * ) gbuf_rptr(gbuf_cont(m));
		lap_id = pat_ID(cfgp->if_name);
		if (lap_id < 0 ||  IF_ANY_MAX <= lap_id)  {
	 		ioc_ack(EINVAL, m, gref); 
			dPrintf(D_M_ELAP, D_L_ERROR, ("validate_msg:bad cmd (%x) for %s\n",
				iocbp->ioc_cmd, cfgp->if_name));
			return(EINVAL);
		}
		*elapp = &elap_specifics[lap_id]; 
	}	

	if (gbuf_cont(m) != NULL) {
		i = gbuf_len(gbuf_cont(m));
		if (size == -1)
			if (i >1)
				return(0);
			else
				goto error;
	}
	if (iocbp->ioc_count < size || (gbuf_cont(m) == NULL) || i < size) {
			dPrintf(D_M_ELAP, D_L_ERROR,
				("ioctl msg error:s:%d c:%d bcont:%c delta:%d\n",
				   size, iocbp->ioc_count,
				   gbuf_cont(m)? 'Y' : 'N', i));
	   goto error;
	}
	else
		return(0);
error:
	ioc_ack(EMSGSIZE, m, gref);
	return (EMSGSIZE);
} /* validate_msg_size */

void
elap_input(mp, elapp, src)
register elap_specifics_t *elapp;
register gbuf_t		*mp;
register char *src;
{
	/* Let ddp glean link address if it wishes to */
	elapp->stats.rcv_packets++;
	elapp->stats.rcv_bytes += gbuf_msgsize(mp);

	if (!MULTIPORT_MODE)
		ddp_glean (mp, &elapp->elap_if, src);
	gbuf_next(mp) = 0;
	ddp_input(mp, &elapp->elap_if);
} /* elap_input */


/***********************************************************************
 * elap_wput()
 *
 **********************************************************************/
int
elap_wput(gref, m)
gref_t 		*gref;
register	gbuf_t	*m;
{
	elap_specifics_t 	*elapp;
	register ioc_t		*iocbp;
	register at_elap_cfg_t	*cfgp;
	at_elap_stats_t		*statsp;
	static int		ifID_entry = 0;
	int			elap_online();
	int			error;
	int			i;
	int			(*func)();
	gbuf_t		*tmpm;
	pat_unit_t *patp;


	switch (gbuf_type(m)) {
	case MSG_DATA:
			gbuf_freem(m);
			dPrintf(D_M_ELAP,D_L_ERROR,
				("Output data to control channel is ignored\n"));
	break;

	case MSG_IOCTL:
		iocbp = (ioc_t *) gbuf_rptr(m);
		if (!xpatcnt) {
			gotIfs = 0;
			if (!(at_statep->flags & AT_ST_PAT_INIT)) {
				bzero(elap_specifics, sizeof(elap_specifics));
				pat_init();
				at_statep->flags |= AT_ST_PAT_INIT;
				at_ddp_debug = 0;
			}
		}

		switch (iocbp->ioc_cmd) {
		case LAP_IOC_ADD_IFNAME:
			/* Has this interface already been added? */
			if ((i = pat_ID(gbuf_rptr(gbuf_cont(m)))) != -1) {
			   ioc_ack(0, m, gref);
			   return 0;
			}

			shutdown_gref = gref;
			patp = &pat_units[xpatcnt];
			bcopy(gbuf_rptr(gbuf_cont(m)), patp->xname, sizeof(patp->xname));
			patp->xname[sizeof(patp->xname)-1] = '\0';
			patp->xunit = (char)IF_UNIT(patp->xname);
			if (strncmp(IF_TYPE_1, patp->xname, 2) == 0)
				patp->xtype = IFTYPE_ETHERTALK;
			else if (strncmp(IF_TYPE_3, patp->xname, 2) == 0)
				patp->xtype = IFTYPE_FDDITALK;
			else if (strncmp(IF_TYPE_4, patp->xname, 2) == 0)
				patp->xtype = IFTYPE_TOKENTALK;
			else if (strncmp(IF_TYPE_5, patp->xname, 2) == 0)
				patp->xtype = IFTYPE_NULLTALK;
			else
				patp->xtype = IFTYPE_LOCALTALK;
			ioc_ack(0, m, gref);
			xpatcnt++;
			return 0;

		/* *** fix this later: this code may no longer be needed *** */
		case LAP_IOC_DEL_IFNAME:
			if ((i = pat_ID(gbuf_rptr(gbuf_cont(m)))) != -1) {
				patp = &pat_units[i];
				patp->xname[0] = '\0';
			}
			ioc_ack(0, m, gref);
			return 0;
		}
		/* *** end of code that may no longer be needed *** */

		if (validate_msg_size(m, gref, &elapp))
			break;	

		if (elapp)
			cfgp = (at_elap_cfg_t*) gbuf_rptr(gbuf_cont(m));

		if (LAP_IOC_MYIOCTL(iocbp->ioc_cmd) || 
			ELAP_IOC_MYIOCTL(iocbp->ioc_cmd)) {

			switch (iocbp->ioc_cmd) {

			case LAP_IOC_CHECK_STATE:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_CHECK_STATE\n");
#endif
				error = (shutdown_gref == NULL) ? ENOTREADY : 0;
				if (error == 0) {
					dPrintf(D_M_ELAP,D_L_INFO,
						("elap_wput: CHECK_STATE 1\n"));
//### LD 7/23 was 0x1000: too big for 2K cluster size
					if ((tmpm = gbuf_alloc(0x512, PRI_HI)) == NULL)
						ioc_ack(ENOBUFS, m, gref);
					else {
						*gbuf_rptr(tmpm) = *gbuf_rptr(gbuf_cont(m));
						gbuf_wset(tmpm,sizeof(int));
						gbuf_freeb(gbuf_cont(m));
						gbuf_cont(m) = tmpm;
						ddp_stop(m, gref);
					}
				} else {
					dPrintf(D_M_ELAP,D_L_INFO,
						("elap_wput: CHECK_STATE 2\n"));
					ioc_ack(error, m, gref);
				}
				break;

			/* *** fix this later: this code may no longer be needed *** */
			case LAP_IOC_SHUT_DOWN:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_SHUT_DOWN\n");
#endif
				if (shutdown_gref != NULL) {
					if ((tmpm = gbuf_alloc(1, PRI_HI)) == NULL) {
						ioc_ack(ENOBUFS, m, gref);
						break;
					}
					gbuf_wset(tmpm,1);
					*gbuf_rptr(tmpm) = ESHUTDOWN;
					gbuf_set_type(tmpm, MSG_ERROR);
					atalk_putnext(shutdown_gref, tmpm);
				}
				ioc_ack(0, m, gref);
				break;
			/* *** end of code that may no longer be needed *** */

			case LAP_IOC_DO_DEFER:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_DO_DEFER\n");
#endif
			if (gbuf_cont(m) == NULL) {
				dodefer();
				ioc_ack(0, m, gref);
			} else {
				ioc_ack(EINVAL, m, gref);
			}
				break;
			case LAP_IOC_DO_DELAY:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_DO_DELAY\n");
#endif
				dodefer();
				if ((func = at_delay_func) != NULL) {
					at_delay_func = NULL;
					error = (*func)(at_delay_flag, m, gref);
					if (error != ENOTREADY)
						ioc_ack(error, m, gref);
				} else
					ioc_ack(0, m, gref);
				break;

		    case LAP_IOC_ONLINE:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_ONLINE\n");
#endif
				if ( elapp->elap_if.ifState == LAP_ONLINE ||
				     (elapp->elap_if.ifState == LAP_ONLINE_ZONELESS &&
					   !(cfgp->flags & ELAP_CFG_SET_RANGE)
					 )
				   ) {
					/* This don't make much sense...
					 * we're already ONLINE!
					 */
					ioc_ack(EALREADY, m, gref);
					break;
				}
				elapp->flags = cfgp->flags;
				if (elapp->flags & ELAP_CFG_HOME) {
					/* copy over the configured zone if any */
					if (cfgp->zonename.len)
						bcopy((caddr_t) &cfgp->zonename,
						      (caddr_t) &elapp->elap_if.ifZoneName, 
						      cfgp->zonename.len+1);
					if  (ifID_table[IFID_HOME])  {
						/* if home flag is set, and
						 * there is already a home
						 * ifID, it better be us!
						 */
						if (&elapp->elap_if !=
						    ifID_table[IFID_HOME] ) {
							/* only 1 home allowed! */
							ioc_ack(EEXIST, m, gref);
							break;
						}
						dPrintf(D_M_ELAP, D_L_STARTUP, ("elap_wput home I/F:%s\n",
							cfgp->if_name));
					}
					else {
						/* home port designation not
						 * allowed unless
						 * i/f is off line
						 */
						if ( elapp->elap_if.ifState != LAP_OFFLINE) {
			    			ioc_ack(EPERM, m, gref);			
							break;
						}
					}
				} /* if CFG_HOME */

				if (elapp->elap_if.ifState == LAP_OFFLINE)
					bzero ((caddr_t) &elapp->elap_if, sizeof(at_if_t));

				if (elapp->flags & ELAP_CFG_SET_RANGE ||
				    elapp->flags & ELAP_CFG_SEED) {

					if ( elapp->elap_if.ifState != LAP_ONLINE_ZONELESS &&
					     elapp->elap_if.ifState != LAP_OFFLINE
					   ) {				/* can't change range if ONLINE */
			    		ioc_ack(EFAULT, m, gref);			
						break;
					}
					dPrintf(D_M_ELAP, D_L_STARTUP_INFO,
						("elap_wput: found to be seed/set range\n"));


					ATALK_ASSIGN(elapp->cfg.initial_addr, 0, 0, 0);
					elapp->elap_if.ifThisCableStart = cfgp->netStart;
					elapp->elap_if.ifThisCableEnd   = cfgp->netEnd;
				}
				else {
					dPrintf(D_M_ELAP,D_L_ERROR, 
						("elap_wput: we believe we're not seed\n"));
				}

				at_delay_errno = 0;
				error = elap_online (gref, cfgp->if_name, NULL, m); 

				if (error != ENOTREADY)
					ioc_ack(error, m, gref);
				break;

			case LAP_IOC_OFFLINE:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_OFFLINE\n");
#endif
				if (!xpatcnt) {
					ioc_ack(0, m, gref);
					break;
				}
				error = elap_offline(elapp);
				ioc_ack(error, m, gref);
				break;

			case LAP_IOC_GET_IFS_STAT:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_GET_IFS_STAT\n");
#endif
				if ((gbuf_cont(m) = gbuf_alloc(sizeof(if_cfg_t), 
			    		PRI_MED)) == NULL) {
					ioc_ack(ENOBUFS, m, gref);
					break;
				}
				get_ifs_stat(gbuf_rptr(gbuf_cont(m)));
				gbuf_wset(gbuf_cont(m),sizeof(if_cfg_t));
				iocbp->ioc_count = sizeof(if_cfg_t);
				ioc_ack(0, m, gref);
				ifID_entry = 0;
				break;
				
			case ELAP_IOC_GET_CFG:
#ifdef APPLETALK_DEBUG
kprintf("ELAP_IOC_GET_CFG\n");
#endif
				/* an at_elap_cfg_t is passed down with this
				   ioctl, the elapp is obtained from it via
				   the if_name we then send back a copy of the
				   current at_elap_cfg_t by gbuf_alloc'ing a new
				   message 
				 */
				gbuf_freem(gbuf_cont(m));
				gbuf_cont(m) = NULL;
				if ((gbuf_cont(m) = gbuf_alloc(sizeof(at_elap_cfg_t), 
			    		PRI_MED)) == NULL) {
					ioc_ack(ENOBUFS, m, gref);
					break;
				}
				cfgp = (at_elap_cfg_t *) gbuf_rptr(gbuf_cont(m));
                                if  (ifID_table[IFID_HOME])  {
			    	   elapp = (elap_specifics_t *)ifID_table[IFID_HOME]->ifLapp;
				   if (elapp) {
			    		*cfgp = elapp->cfg;
					gbuf_wset(gbuf_cont(m),sizeof(at_elap_cfg_t));
					iocbp->ioc_count = sizeof(at_elap_cfg_t);
					ioc_ack(0, m, gref);
				        }
				   else
					ioc_ack(EINVAL, m, gref);
				   }
                                else
				   ioc_ack(EINVAL, m, gref);
				break;

			case LAP_IOC_GET_IFID:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_GET_IFID\n");
#endif
				/* return at_if_t struct for requested interface */
			{
				at_if_name_t *ifIN;
				at_if_t *ifID;
				int lap_id;

				ifIN = (at_if_name_t *)gbuf_rptr(gbuf_cont(m));
				lap_id = pat_ID(ifIN->if_name);
				{
					if (lap_id < 0 || IF_ANY_MAX <= lap_id)  {
						ioc_ack(EINVAL, m, gref);
						break;
					}
					else {
						ifID = &elap_specifics[lap_id].elap_if;
						gbuf_freem(gbuf_cont(m));
						gbuf_cont(m) = NULL;

						if ((gbuf_cont(m) = gbuf_alloc(sizeof(at_if_name_t), 
				    		PRI_MED)) == NULL) {
							ioc_ack(ENOBUFS, m, gref);
							break;
						}
						ifID = &elap_specifics[lap_id].elap_if; 
						ifIN = (at_if_name_t *)gbuf_rptr(gbuf_cont(m));
						ifIN->ifID = *ifID; 
						gbuf_wset(gbuf_cont(m),sizeof(at_if_name_t));
						iocbp->ioc_count = sizeof(at_if_name_t);
					}
				}
				ioc_ack(0, m, gref);
			}
			break;
				

			case ELAP_IOC_GET_STATS:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_GET_STATS\n");
#endif
				if ( (gbuf_cont(m) == NULL)
						|| ((i = pat_ID(gbuf_rptr(gbuf_cont(m)))) == -1) ) {
					ioc_ack(EINVAL, m, gref);
					break;
				}
				gbuf_freem(gbuf_cont(m));
				if ((gbuf_cont(m) =gbuf_alloc(sizeof(at_elap_stats_t), 
			    		PRI_MED)) == NULL) {
					ioc_ack(ENOBUFS, m, gref);
					break;
				}
				statsp = ((at_elap_stats_t *)gbuf_rptr(gbuf_cont(m)));
				elapp = &elap_specifics[i]; 
				if (elapp) {
					*statsp = elapp->stats;
					gbuf_wset(gbuf_cont(m),sizeof(at_elap_stats_t));
					iocbp->ioc_count = sizeof(at_elap_stats_t);
					ioc_ack(0, m, gref);
				}
				else 
					ioc_ack(EINVAL, m, gref);
				break;
			case ELAP_IOC_SET_CFG:
#ifdef APPLETALK_DEBUG
kprintf("ELAP_IOC_SET_CFG\n");
#endif
				if (elapp->elap_if.ifState == LAP_ONLINE) {
					/* Can not allow setting config after
					 * we're already ONLINE
					 */
					ioc_ack(EALREADY, m, gref);
					break;
				}
				ATALK_ASSIGN(elapp->cfg.initial_addr, 0, 0, 0);
		
				bcopy ((caddr_t) cfgp->if_name, (caddr_t) elapp->cfg.if_name, AT_IF_NAME_LEN);
				if (ATALK_VALUE(cfgp->initial_addr)) {
					at_net_al	initial_net;
					at_node	initial_node;

					initial_node = *(at_node *)&cfgp->
							initial_addr.atalk_node;
					initial_net = NET_VALUE(cfgp->
							initial_addr.atalk_net);
					/*
					 * The net effect of the code that was here after A/UX
					 * version 29 was to ignore the prior node number settings. 
					 * As a result * Barracuda 3.0.1 machines cannot "remember" 
					 * their AppleTalk node number from the last AppleTalk 
					 * session.
					 * The following code reverts this code back to pre version 
					 * 29 style.
					 * The problem that version 29 was fixing really happens because
					 * readxpram (and writexpram)  in lap_init was returning a -1 
					 * instead of 0 on failure.
					 * Madan Valluri. May 12, 1993.
					 */
					if ((initial_node<0xfe) && (initial_node>0) &&
					    !((initial_net == 0) ||
						((initial_net >= DDP_STARTUP_LOW)&&
					  	(initial_net <= DDP_STARTUP_HIGH))))
						elapp->cfg.initial_addr = 
						    cfgp->initial_addr;
				}
				ioc_ack(0, m, gref);
				break;

			case ELAP_IOC_SET_ZONE :
#ifdef APPLETALK_DEBUG
kprintf("ELAP_IOC_SET_ZONE\n");
#endif
				/* ioctl to set the desired zone name for the 
				 * interface and when no router is present, to set
				 * multicast addr for pram zone for when router returns
				 */
				cfgp = (at_elap_cfg_t * ) gbuf_rptr(gbuf_cont(m));

				if (cfgp->flags & ELAP_CFG_ZONE_MCAST) {
					nbp_add_multicast(&cfgp->zonename, &elapp->elap_if);
				}
				else  {
					if (elapp->elap_if.ifState == LAP_ONLINE) {
						/* Can not allow setting zonename
					 	* after we're already ONLINE
						 */
						ioc_ack(EALREADY, m, gref);
						break;
					}
				}

				if (cfgp->zonename.len)
					bcopy((caddr_t) &cfgp->zonename, 
						(caddr_t) &elapp->cfg.zonename, 
						cfgp->zonename.len+1);
				ioc_ack(0, m, gref);
				break;
			case LAP_IOC_ADD_ZONE:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_ADD_ZONE\n");
#endif
				i = set_zones((if_zone_info_t *)gbuf_rptr(gbuf_cont(m)));
				ioc_ack(i, m, gref);
				break;

			case LAP_IOC_ADD_ROUTE:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_ADD_ROUTE\n");
#endif
				add_route((RT_entry *)gbuf_rptr(gbuf_cont(m)));
				ioc_ack(0, m, gref);
				break;

			case LAP_IOC_ROUTER_INIT:	/* set routing tables sizes*/
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_ROUTER_INIT\n");
#endif
			{
				router_init_t *p = (router_init_t *)gbuf_rptr(gbuf_cont(m));

				at_state.flags = p->flags;
				if (rt_table_init(p) == ENOBUFS) {
					ioc_ack(ENOBUFS, m, gref);
					break;
				}
				ioc_ack(0, m, gref);
			}
				break;
			case LAP_IOC_ROUTER_START:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_ROUTER_START\n");
#endif
				gbuf_freem(gbuf_cont(m));
				gbuf_cont(m) = NULL;
				routerStart(0, m, gref);
				break;

			case LAP_IOC_ROUTER_SHUTDOWN:
			{ 
				int i, lap_id;
				gbuf_t *tmpm;

#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_ROUTER_SHUTDOWN\n");
#endif

				routerShutdown(); 

				/* go through ifID_table */
				for (i = 0; i < IF_TOTAL_MAX; i++)
				    if (ifID_table[i] && ifID_table[i]->ifName) {
					  lap_id = pat_ID(ifID_table[i]->ifName);

					  /* was LAP_IOC_OFFLINE processing */
					  elap_offline(&elap_specifics[lap_id]);

					  /* was LAP_IOC_DEL_IFNAME processing */
					  pat_units[lap_id].xname[0] = '\0';
				    }

				/* was LAP_IOC_SHUT_DOWN processing */
				if (shutdown_gref != NULL) {
					if ((tmpm = gbuf_alloc(1, PRI_HI)) == NULL) {
						ioc_ack(ENOBUFS, m, gref);
						break;
					}
					gbuf_wset(tmpm,1);
					*gbuf_rptr(tmpm) = ESHUTDOWN;
					gbuf_set_type(tmpm, MSG_ERROR);
					atalk_putnext(shutdown_gref, tmpm);
				}
				ioc_ack(0, m, gref);
			}
				break;


			case LAP_IOC_SET_DBG :
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_SET_DBG\n");
#endif
				dbgBits = *(dbgBits_t *)gbuf_rptr(gbuf_cont(m));
				ioc_ack(0, m, gref);
				break;


			case LAP_IOC_GET_DBG:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_GET_DBG\n");
#endif
				if ((gbuf_cont(m) = gbuf_alloc(sizeof(dbgBits_t), 
			    		PRI_MED)) == NULL) {
					ioc_ack(ENOBUFS, m, gref);
					break;
				}
				 *(dbgBits_t *)gbuf_rptr(gbuf_cont(m)) = dbgBits;
				gbuf_wset(gbuf_cont(m),sizeof(dbgBits_t));
				iocbp->ioc_count = sizeof(dbgBits_t);
				ioc_ack(0, m, gref);
				break;

			case LAP_IOC_GET_ZONE:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_GET_ZONE\n");
#endif
						/* return next ZT_entryno from ZT_table 
						   a pointer to the struct ZT_entryno is passed down from
						   user space and the first byte is cast to a int, if
						   this int is non-zero, then the first ZT_entry is
						   returned and subsequent calls with a zero value
						   will return the next entry in the table. The next
						   read after the last valid entry will return ENOMSG
						 */
			{
				ZT_entryno *pZTe;

				i =  *(int *)gbuf_rptr(gbuf_cont(m));
				gbuf_freem(gbuf_cont(m));
				gbuf_cont(m) = NULL;

				pZTe = zt_getNextZone(i);
				if (pZTe) {
					if ((gbuf_cont(m) = gbuf_alloc(sizeof(ZT_entryno), PRI_MED)) == NULL) {
						ioc_ack(ENOBUFS, m, gref);
						break;
					}	
					*(ZT_entryno *)gbuf_rptr(gbuf_cont(m)) = *pZTe;
					gbuf_wset(gbuf_cont(m),sizeof(ZT_entryno));
					iocbp->ioc_count = sizeof(ZT_entryno);
					ioc_ack(0, m, gref);
				}
				else
					ioc_ack(ENOMSG, m, gref);
			}
				break;
				
			case LAP_IOC_GET_DEFAULT_ZONE:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_GET_DEFAULT_ZONE\n");
#endif
				{
				at_nvestr_t	 *pnve;

				i =  *(int *)gbuf_rptr(gbuf_cont(m));
				gbuf_freem(gbuf_cont(m));
				gbuf_cont(m) = NULL;
				pnve = getDefZone(i);
				if (pnve) {
					if ((gbuf_cont(m) = gbuf_alloc(sizeof(*pnve), PRI_MED)) == NULL) {
						ioc_ack(ENOBUFS, m, gref);
						break;
					}	
					*(at_nvestr_t *)gbuf_rptr(gbuf_cont(m)) = *pnve;
					gbuf_wset(gbuf_cont(m),sizeof(*pnve));
					iocbp->ioc_count = sizeof(*pnve);
				}
				else 
					iocbp->ioc_count = 0;
				}
				ioc_ack(0, m, gref);
				break;
			case LAP_IOC_GET_LOCAL_ZONE:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_GET_LOCAL_ZONE\n");
#endif
				if (!(ROUTING_MODE || MULTIHOME_MODE)) {
					ioc_ack(EPERM, m, gref);
					break;
				}
			{
				if_zone_t 	 *ifz;
				int zone;

				zone =  ((if_zone_t *)gbuf_rptr(gbuf_cont(m)))->ifzn.zone;
				gbuf_freem(gbuf_cont(m));
				gbuf_cont(m) = NULL;
				if ((gbuf_cont(m) = gbuf_alloc(sizeof(if_zone_t), PRI_MED)) == NULL) {
					ioc_ack(ENOBUFS, m, gref);
					break;
				}	
				ifz = (if_zone_t *)gbuf_rptr(gbuf_cont(m));
				ifz->ifzn.zone = zone;
				getRTRLocalZone(ifz);
				gbuf_wset(gbuf_cont(m),sizeof(*ifz));
				iocbp->ioc_count = sizeof(*ifz);
			}
				ioc_ack(0, m, gref);
				break;
			case LAP_IOC_GET_ROUTE:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_GET_ROUTE\n");
#endif
				/* return next RT_entry from RT_table 
				 * a pointer to the struct RT_entry is
				 * passed down from user space and the first
				 * byte is cast to a int, if this int is
				 * non-zero, then the first RT_entry is
				 * returned and subsequent calls with a
				 * zero value will return the next entry in
				 * the table. The next read after the last
				 * valid entry will return ENOMSG
				 */
			{
				RT_entry *pRT;

				i =  *(int *)gbuf_rptr(gbuf_cont(m));
				gbuf_freem(gbuf_cont(m));
				gbuf_cont(m) = NULL;

				pRT = rt_getNextRoute(i);
				if (pRT) {
					if ((gbuf_cont(m) = gbuf_alloc(sizeof(RT_entry), PRI_MED)) == NULL) {
						ioc_ack(ENOBUFS, m, gref);
						break;
					}	
					*(RT_entry *)gbuf_rptr(gbuf_cont(m)) = *pRT;
					gbuf_wset(gbuf_cont(m),sizeof(RT_entry));
					iocbp->ioc_count = sizeof(RT_entry);
					ioc_ack(0, m, gref);
				}
				else
					ioc_ack(ENOMSG, m, gref);
			}
				break;
			
			case LAP_IOC_SNMP_GET_DDP:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_SNMP_GET_DDP\n");
#endif
				if (!shutdown_gref) {
					ioc_ack(ENOTREADY, m, gref);
					break;
				}
				if ((gbuf_cont(m) = gbuf_alloc(sizeof(snmpStats_t), 
						PRI_MED)) == NULL) {
					ioc_ack(ENOBUFS, m, gref);
					break;
				}
				
				*(snmpStats_t *)gbuf_rptr(gbuf_cont(m)) = snmpStats;
				gbuf_wset(gbuf_cont(m),sizeof(snmpStats));
				iocbp->ioc_count = sizeof(snmpStats);
				ioc_ack(0, m, gref);
				break;
			case LAP_IOC_SNMP_GET_CFG:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_SNMP_GET_CFG\n");
#endif
			{
				int i,size;
				snmpCfg_t 	snmp;

				i =  *(int *)gbuf_rptr(gbuf_cont(m));
				gbuf_freem(gbuf_cont(m));
				gbuf_cont(m) = NULL;
				if (!shutdown_gref) {		/* if stack down */
					iocbp->ioc_count = 0;
					ioc_ack(ENOTREADY, m, gref);
					dPrintf(D_M_ELAP_LOW, D_L_INFO,
						("elap_wput: cfg req, stack down\n"));
					break;
				}
				if (i == UPDATE_IF_CHANGED && 
					!(at_statep->flags & AT_ST_IF_CHANGED)) {
					iocbp->ioc_count = 0;
					ioc_ack(0, m, gref);
					dPrintf(D_M_ELAP_LOW, D_L_INFO,
						("elap_wput: cfg req, unchanged\n"));
					break;
				}
				dPrintf(D_M_ELAP_LOW, D_L_INFO,
					("elap_wput: cfg req, changed\n"));

				if (getSnmpCfg(&snmp)) {
					dPrintf(D_M_ELAP,D_L_ERROR,
						("elap_wput:SNMP_GET_CFG error\n"));
					ioc_ack(ENOMSG, m, gref);
					break;
				}
					/* send up only used part of table */
				size = sizeof(snmp) - 
					   sizeof(snmpIfCfg_t) * (MAX_IFS - snmp.cfg_ifCnt);

				if ((gbuf_cont(m) = gbuf_alloc(size, PRI_MED)) == NULL) {
					ioc_ack(ENOBUFS, m, gref);
					break;
				}
				bcopy(&snmp,gbuf_rptr(gbuf_cont(m)),size);
				gbuf_wset(gbuf_cont(m),size);
				iocbp->ioc_count = size;
				at_statep->flags &= ~AT_ST_IF_CHANGED;
				ioc_ack(0, m, gref);
			}
			break;

			case LAP_IOC_SNMP_GET_AARP:
			{
				snmpAarpEnt_t *snmpp;
				int bytes;
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_SNMP_GET_AARP\n");
#endif
				i =  *(int *)gbuf_rptr(gbuf_cont(m));
				gbuf_freem(gbuf_cont(m));
				gbuf_cont(m) = NULL;
				dPrintf(D_M_ELAP,D_L_INFO,
					("elap_wput:calling getarp,i=%d\n", i));
				snmpp = getAarp(&i); 
				bytes = i * sizeof(snmpAarpEnt_t);
				dPrintf(D_M_ELAP,D_L_INFO,
					("elap_wput:getarp returned, i=%d,bytes=%d\n", 
					i, bytes));
				if (snmpp) {
					if ((gbuf_cont(m) = gbuf_alloc(bytes, PRI_MED)) == NULL) {
						ioc_ack(ENOBUFS, m, gref);
						break;
					}	
					bcopy(snmpp, gbuf_rptr(gbuf_cont(m)), bytes);
					gbuf_wset(gbuf_cont(m),bytes);
					iocbp->ioc_count = bytes;
					ioc_ack(0, m, gref);
				}
				else
					ioc_ack(ENOMSG, m, gref);
			}
			break;

			case LAP_IOC_SNMP_GET_ZIP:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_SNMP_GET_ZIP\n");
#endif
			{ /* matching brace NOT in this case */
				register int i,j;
				register int size, total, tabsize;
				gbuf_t	*mn;		/* new gbuf */
				gbuf_t	*mo;		/* old gbuf */
				gbuf_t	*mt;		/* temp */
				snmpNbpTable_t		*nbp;

				i =  *(int *)gbuf_rptr(gbuf_cont(m));
				gbuf_freem(gbuf_cont(m));
				gbuf_cont(m) = NULL;
				if (!shutdown_gref) {
					ioc_ack(ENOTREADY, m, gref);
					break;
				}
				if (i == UPDATE_IF_CHANGED && 
					!(at_statep->flags & AT_ST_ZT_CHANGED)) {
					iocbp->ioc_count = 0;
					ioc_ack(0, m, gref);
					break;
				}
				mo=(gbuf_t*)NULL;
				tabsize = getZipTableSize();

					/* retrieve table into multiple gbufs */
				for (i =0; i<tabsize;  i+=j) {
					j = tabsize - i > 
						MAX_ZIP ? MAX_ZIP : tabsize - i;
					size = j < MAX_ZIP ? sizeof(ZT_entry)*j : MAX_ZIP_BYTES;
					if ((mn = gbuf_alloc(size, PRI_MED)) == NULL) {
						if (gbuf_cont(m))
							gbuf_freem(gbuf_cont(m));
						ioc_ack(ENOBUFS, m, gref);
						break;
					}
					if (!mo)	{ 		/* if first new one */
						mt = mn;
						total = size;
					}
					else {
						gbuf_cont(mo) = mn;
						total += size;
					}
					mo = mn;
					getZipTable((ZT_entry*)gbuf_rptr(mn),i,j); 
					gbuf_wset(mn,size);
				}
				if ((gbuf_cont(m) = gbuf_alloc(sizeof(int), PRI_MED)) == NULL) {
					if (mt)
						gbuf_freem(mt);
					iocbp->ioc_count = 0;
					ioc_ack(ENOBUFS, m, gref);
					break;
				}
				if (!tabsize) {
					dPrintf(D_M_ELAP,D_L_WARNING,
						("elap_wput:snmp: empty zip table\n"));
					total = 0;
				}
				*(int*)gbuf_rptr(gbuf_cont(m)) = total; 	/* return table size */
				gbuf_wset(gbuf_cont(m),sizeof(int));
				iocbp->ioc_count = sizeof(int);
				ioc_ack(0, m, gref);
				if (tabsize)
					atalk_putnext(gref,mt);		/* send up table */
				at_statep->flags &= ~AT_ST_ZT_CHANGED;
				break;

			case LAP_IOC_SNMP_GET_RTMP:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_SNMP_GET_RTMP\n");
#endif
				i =  *(int *)gbuf_rptr(gbuf_cont(m));
				gbuf_freem(gbuf_cont(m));
				gbuf_cont(m) = NULL;
				if (!shutdown_gref) {
					ioc_ack(ENOTREADY, m, gref);
					break;
				}
				if (i == UPDATE_IF_CHANGED && 
					!(at_statep->flags & AT_ST_RT_CHANGED)) {
					iocbp->ioc_count = 0;
					ioc_ack(0, m, gref);
					break;
				}

				mo=(gbuf_t*)NULL;
				tabsize = getRtmpTableSize();

					/* retrieve table into multiple gbufs */
				for (i =0; i<tabsize;  i+=j) {
					j = tabsize - i > 
						MAX_RTMP ? MAX_RTMP : tabsize - i;
					size = j < MAX_RTMP ? sizeof(RT_entry)*j : MAX_RTMP_BYTES;
					if ((mn = gbuf_alloc(size, PRI_MED)) == NULL) {
						if (gbuf_cont(m))
							gbuf_freem(gbuf_cont(m));
						ioc_ack(ENOBUFS, m, gref);
						break;
					}
					if (!mo)	{ 		/* if first new one */
						mt = mn;
						total = size;
					}
					else {
						gbuf_cont(mo) = mn;
						total += size;
					}
					mo = mn;
					getRtmpTable((RT_entry*)gbuf_rptr(mn),i,j); 
					gbuf_wset(mn,size);
				}
				if ((gbuf_cont(m) = gbuf_alloc(sizeof(int), PRI_MED)) == NULL) {
					if (mt)
						gbuf_freem(mt);
					iocbp->ioc_count = 0;
					ioc_ack(ENOBUFS, m, gref);
					break;
				}
				if (!tabsize)
					total = 0;
				*(int*)gbuf_rptr(gbuf_cont(m)) = total;	/* return table size */
				gbuf_wset(gbuf_cont(m),sizeof(int));
				iocbp->ioc_count = sizeof(int);
				ioc_ack(0, m, gref);
				if (tabsize)
					atalk_putnext(gref,mt);		/* send up table */
				at_statep->flags &= ~AT_ST_RT_CHANGED;
				break;

			case LAP_IOC_SNMP_GET_NBP:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_SNMP_GET_NBP\n");
#endif
				i =  *(int *)gbuf_rptr(gbuf_cont(m));
				gbuf_freem(gbuf_cont(m));
				gbuf_cont(m) = NULL;
				if (!shutdown_gref) {
					ioc_ack(ENOTREADY, m, gref);
					break;
				}
				if (i == UPDATE_IF_CHANGED && 
					!(at_statep->flags & AT_ST_NBP_CHANGED)) {
					iocbp->ioc_count = 0;
					ioc_ack(0, m, gref);
					dPrintf(D_M_ELAP_LOW, D_L_INFO,
						("elap_wput: nbp req denied, no change\n"));
					break;
				}

				mo=(gbuf_t*)NULL;
				tabsize = getNbpTableSize();

					/* retrieve table into multiple gbufs */
				for (i =0; i<tabsize;  i+=j) {
					j = tabsize - i > 
						MAX_NBP ? MAX_NBP : tabsize - i;
					size = j < MAX_NBP ? sizeof(snmpNbpEntry_t)*j : MAX_NBP_BYTES;
					if (!i)
						size += SNMP_NBP_HEADER_SIZE;
					if ((mn = gbuf_alloc(size, PRI_MED)) == NULL) {
						if (gbuf_cont(m))
							gbuf_freem(gbuf_cont(m));
						ioc_ack(ENOBUFS, m, gref);
						break;
					}
					if (!mo)	{ 		/* if first new one */
						mt = mn;
						total = size;
						nbp = (snmpNbpTable_t*)gbuf_rptr(mn);
						nbp->nbpt_entries = tabsize;
						nbp->nbpt_zone = 
							ifID_table[IFID_HOME]->ifZoneName;
						getNbpTable(nbp->nbpt_table,i,j); 
					}
					else {
						gbuf_cont(mo) = mn;
						total += size;
						getNbpTable((snmpNbpEntry_t *)gbuf_rptr(mn),i,j); 
					}
					mo = mn;
					gbuf_wset(mn,size);
				}
				if ((gbuf_cont(m) = gbuf_alloc(sizeof(int), PRI_MED)) == NULL) {
					if (mt)
						gbuf_freem(mt);
					iocbp->ioc_count = 0;
					ioc_ack(ENOBUFS, m, gref);
					break;
				}
				if (!tabsize)
					total = 0;
				*(int*)gbuf_rptr(gbuf_cont(m)) = total;	/* return table size */
				gbuf_wset(gbuf_cont(m),sizeof(int));
				iocbp->ioc_count = sizeof(int);
				ioc_ack(0, m, gref);
				if (tabsize)
					atalk_putnext(gref,mt);		/* send up table */
				at_statep->flags &= ~AT_ST_NBP_CHANGED;
				break;
			}
				
			case LAP_IOC_SET_MIX:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_SET_MIX\n");
#endif
				RoutingMix = (*(short *)gbuf_rptr(gbuf_cont(m)) & 0xFFFF) * 2;
				ioc_ack(0, m, gref);
				break;
			case LAP_IOC_SET_LOCAL_ZONES:
				{	int i;
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_SET_LOCAL_ZONES\n");
#endif
					i = setLocalZones((at_nbptuple_t*)gbuf_rptr(gbuf_cont(m)),
						gbuf_len(gbuf_cont(m)));
					ioc_ack(i,m,gref);
				}
				break;
				
			case LAP_IOC_IS_ZONE_LOCAL:
				{
					at_nvestr_t *zone;
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_IS_ZONE_LOCAL\n");
#endif
					zone = (at_nvestr_t *)gbuf_rptr(gbuf_cont(m));
					if (isZoneLocal(zone))
						ioc_ack(0,m,gref);
					else
						ioc_ack(ENOENT,m,gref);
				}
				break;
			case LAP_IOC_GET_MODE:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_GET_MODE\n");
#endif
				if ((gbuf_cont(m) = gbuf_alloc(sizeof(int), 
			    		PRI_MED)) == NULL) {
					ioc_ack(ENOBUFS, m, gref);
					break;
				}
				if (MULTIHOME_MODE)
				 	*(int *)gbuf_rptr(gbuf_cont(m)) =  AT_MODE_MHOME;
				else if (ROUTING_MODE) 
				 	*(int *)gbuf_rptr(gbuf_cont(m)) =  AT_MODE_ROUTER; 
				else
				 	*(int*)gbuf_rptr(gbuf_cont(m)) =  AT_MODE_SPORT;
				gbuf_wset(gbuf_cont(m),sizeof(int));
				iocbp->ioc_count = sizeof(int);
				ioc_ack(0, m, gref);
				break;
			case LAP_IOC_GET_IF_NAMES:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_GET_IF_NAMES\n");
#endif
				if ((gbuf_cont(m) = gbuf_alloc(sizeof(if_name_t) * IF_ANY_MAX, 
			    		PRI_MED)) == NULL) {
					ioc_ack(ENOBUFS, m, gref);
					break;
				}
				getIfNames((if_name_t *)gbuf_rptr(gbuf_cont(m)));
				gbuf_wset(gbuf_cont(m),sizeof(if_name_t) * IF_ANY_MAX);
				iocbp->ioc_count   = sizeof(if_name_t) * IF_ANY_MAX;
				ioc_ack(0, m, gref);
				break;
			case LAP_IOC_SET_DEFAULT_ZONES:
#ifdef APPLETALK_DEBUG
kprintf("LAP_IOC_SET_DEFAULT_ZONES\n");
#endif
				setDefaultZones((int*)gbuf_rptr(gbuf_cont(m)));
				ioc_ack(0, m, gref);
				break;

			default:
#ifdef APPLETALK_DEBUG
kprintf("unknown ioctl %d\n", iocbp->ioc_cmd);
#endif
				ioc_ack(ENOTTY, m, gref);
				dPrintf(D_M_ELAP, D_L_WARNING,
					("elap_wput: unknown ioctl (%d)\n", iocbp->ioc_cmd));

				if (elapp)
					elapp->stats.unknown_mblks++;
				break;
			}
		}
		break;

	default:
		gbuf_freem(m);
		break;
	}

	return 0;
} /* elap_wput */


/* Called directly by ddp/zip.
 */
elap_dataput(m, elapp, addr_flag, addr)
register	gbuf_t	*m;
register elap_specifics_t *elapp;
u_char			addr_flag;
char *addr;
{
	register int		size;
	int			error;
	extern	int		zip_type_packet();
	struct	etalk_addr	dest_addr;
	struct	atalk_addr	dest_at_addr;
	gbuf_t			*tmp;
	extern	gbuf_t		*growmsg();
	int		loop = TRUE;		/* flag to aarp to loopback (default) */

	/* the incoming frame is of the form {flag, address, ddp...}
	 * where "flag" indicates whether the address is an 802.3
	 * (link) address, or an appletalk address.  If it's an
	 * 802.3 address, the packet can just go out to the network
	 * through PAT, if it's an appletalk address, AT->802.3 address
	 * resolution needs to be done.
	 * If 802.3 address is known, strip off the flag and 802.3
	 * address, and prepend 802.2 and 802.3 headers.
	 */
	
	if (addr == NULL) {
		addr_flag = *(u_char *)gbuf_rptr(m);
		gbuf_rinc(m,1);
	}
	
	switch (addr_flag) {
	case AT_ADDR_NO_LOOP :
		loop = FALSE;
		/* pass thru */
	case AT_ADDR :
	if (addr == NULL) {
	    dest_at_addr = *(struct atalk_addr *)gbuf_rptr(m);
	    gbuf_rinc(m,sizeof(struct atalk_addr));
	} else
	    dest_at_addr = *(struct atalk_addr *)addr;
	    break;
	case ET_ADDR :
	if (addr == NULL) {
	    dest_addr = *(struct etalk_addr *)gbuf_rptr(m);
	    gbuf_rinc(m,sizeof(struct etalk_addr));
	} else
	    dest_addr = *(struct etalk_addr *)addr;
	    break;
	default :
	    gbuf_freel(m);		/* unknown address type, chuck it */
	    return(EINVAL);
        }

	while (gbuf_len(m) == 0) {
		tmp = m;
		m = gbuf_cont(m);
		gbuf_freeb(tmp); 
	}

	/* At this point, rptr points to ddp header for sure */

	if (elapp->elap_if.ifState == LAP_OFFLINE) {
	    gbuf_freel(m);
		return(ENETDOWN);
	}

	if (elapp->elap_if.ifState == LAP_ONLINE_FOR_ZIP) {
		/* see if this is a ZIP packet that we need
		 * to let through even though network is
		 * not yet alive!!
		 */
		if (zip_type_packet(m) == 0) {
	    	gbuf_freel(m);
			return(ENETDOWN);
		}
	}
	
	elapp->stats.xmit_packets++;
	size = gbuf_msgsize(m);
	elapp->stats.xmit_bytes += size;
	snmpStats.dd_outLong++;
	
	switch (addr_flag) {
	case AT_ADDR_NO_LOOP :
	case AT_ADDR :
	    /*
	     * we don't want elap to be looking into ddp header, so
	     * it doesn't know net#, consequently can't do 
	     * AMT_LOOKUP.  That task left to aarp now.
	     */
	    error = aarp_send_data(m,elapp,&dest_at_addr, loop);
	    break;
	case ET_ADDR :
	    error = pat_output(elapp->pat_id, m, &dest_addr, 0);
	    break;
        }
	return (error);
} /* elap_dataput */


/************************************************************************
 * elap_online()
 *
 ************************************************************************/

int	elap_online (gref, if_name, aerr, m)
gref_t	*gref;
char	*if_name;	/* name of h/w interface (e.g. en2) */
int	*aerr;
gbuf_t *m;

{
	register elap_specifics_t	*elapp;
	int			errno;
	int i,j;

	if (if_name == NULL) {
		elapp = (elap_specifics_t *)gref;
		gref = elapp->wait_q;
		if (aerr != NULL)
			goto AARP_sleep;
		if (elapp->elap_if.ifZipError != ZIP_RE_AARP) {
			errno = at_delay_errno ? at_delay_errno : elapp->elap_if.ifZipError;
			at_delay_errno = 0;
			ioc_ack(errno, m, gref);
			elapp->wait_p = 0; elapp->wait_q = 0;
			dPrintf(D_M_ELAP, D_L_STARTUP_INFO,
				("elap_online: ifZipError=%d\n",
				elapp->elap_if.ifZipError));
		}
		goto ZIP_sleep;
	}

	if ((i = pat_ID(if_name)) == -1)
		return(EINVAL);
	elapp = &elap_specifics[i];
	elapp->wait_p = elap_online; elapp->wait_q = gref; elapp->wait_m = m;
	if (elapp->elap_if.ifState == LAP_ONLINE_FOR_ZIP) {
		dPrintf(D_M_ELAP, D_L_STARTUP_INFO, 
			("elap_online: goto zip_online_only\n"));
		goto zip_online_only;
	}
	if (elapp->elap_if.ifState == LAP_ONLINE_ZONELESS) {
		ATALK_ASSIGN(elapp->cfg.initial_addr, 0, 0, 0);
		dPrintf(D_M_ELAP_LOW, D_L_STARTUP_INFO,
			("elap_online: goto re_aarp port=%d\n", elapp->elap_if.ifPort));
		goto re_aarp; 				/* just reset the net range */
	}

	if (!xpatcnt && !(at_statep->flags & AT_ST_PAT_INIT)) {
		pat_init();
	    at_statep->flags |= AT_ST_PAT_INIT;
	}

	dPrintf(D_M_ELAP, D_L_STARTUP_INFO, ("elap_online:%s elapp:0x%x\n",
		if_name[0] ? if_name : "NULL interface", (u_int) elapp));
	
	elapp->pat_id = pat_online(if_name, &elapp->elap_if.ifType);
	if (elapp->pat_id == -1) {
		/* pat_online returned in error, interface name must be bad */
		elapp->wait_m = 0;
		return(EINVAL);
	}
	if (elapp->elap_if.ifType == IFTYPE_TOKENTALK)
	{
		elapp->cable_multicast_addr = ttalk_multicast_addr;
		ddp_bit_reverse(&elapp->cable_multicast_addr);
	}
	else
	{
		elapp->cable_multicast_addr = etalk_multicast_addr;
		if (elapp->elap_if.ifType == IFTYPE_FDDITALK)
			ddp_bit_reverse(&elapp->cable_multicast_addr);
	}
	bcopy((caddr_t) if_name, (caddr_t) elapp->cfg.if_name, AT_IF_NAME_LEN);
	bcopy((caddr_t) if_name, (caddr_t) elapp->elap_if.ifName, AT_IF_NAME_LEN);
	elapp->elap_if.ifUnit = IF_UNIT(if_name);

	if (elapp->elap_if.ifState != LAP_OFFLINE) {
		/* the network must be up already or hanging 
		 * up! 
		 */
		elapp->wait_m = 0;
		return (elapp->elap_if.ifState == LAP_HANGING_UP?EAGAIN:EALREADY);
	}
	
	bzero ((caddr_t) &elapp->stats, sizeof(at_elap_stats_t));
	elapp->elap_if.ifFlags = AT_IFF_ETHERTALK;
	if (elapp->flags & ELAP_CFG_HOME)	/* tell ddp_add_if if this is home */
		elapp->elap_if.ifFlags |= AT_IFF_DEFAULT;
		
	elapp->elap_if.ifLapp = (void*)elapp;

	/* ELAP/DDP interface is not streams based anymore */

	/* Get DDP started */
	if (errno = ddp_add_if(&elapp->elap_if)) {
		elapp->wait_m = 0;
		return (errno);
	}

		/* set up multicast address for cable-wide broadcasts */
	i = pat_control(elapp->pat_id, PAT_REG_MCAST, &elapp->cable_multicast_addr);
	j = pat_control(elapp->pat_id, PAT_REG_CONTEXT, (caddr_t)elapp);


re_aarp :
	/* We now call aarp_init() to assign an apple
	 * talk node addr 
	 */
	ATALK_ASSIGN(elapp->cfg.node, 0, 0, 0);
AARP_sleep :
	if (aerr != NULL)
		errno = *aerr;
	else
		errno = aarp_init(elapp, 0);
	if (errno != 0) {
		if (errno == ENOTREADY)
			return (errno);
		dPrintf(D_M_ELAP, D_L_STATE_CHG, ("elap_online aarp_init for %s\n",
			elapp->elap_if.ifName));
		pat_control(elapp->pat_id, PAT_UNREG_MCAST, &elapp->cable_multicast_addr);
		pat_offline(elapp->pat_id);
		ddp_rem_if (&elapp->elap_if);
		elapp->elap_if.ifState = LAP_OFFLINE;
		if ((aerr != NULL) || (elapp->elap_if.ifZipError == ZIP_RE_AARP)) {
			ioc_ack(EADDRNOTAVAIL, m, gref);
			elapp->wait_p = 0; elapp->wait_q = 0; elapp->wait_m = 0;
			dPrintf(D_M_ELAP, D_L_STARTUP_INFO, ("elap_online: ack 2\n"));
		}
		return (EADDRNOTAVAIL);
	}
	else 
		dPrintf(D_M_ELAP,D_L_STARTUP_INFO,
			("elap_online: aarp_init returns zero\n"));

	if (ROUTING_MODE)
	{
		dPrintf(D_M_ELAP,D_L_STARTUP_INFO, 
			("elap_online: re_aarp, we know it's a router...\n"));

		if (elapp->flags & ELAP_CFG_SEED) {
					/* add route table entry (zones to be added later) */
			at_if_t *ifID;
			ifID = &elapp->elap_if;
			dPrintf(D_M_ELAP, D_L_STARTUP_INFO,
				("elap_online: rt_insert Cable %d-%d port =%d as SEED\n",
				ifID->ifThisCableStart, ifID->ifThisCableEnd, ifID->ifPort));
			rt_insert(	ifID->ifThisCableEnd,
						ifID->ifThisCableStart,
						0,0,0,
						ifID->ifPort,
						RTE_STATE_PERMANENT | RTE_STATE_ZKNOWN | RTE_STATE_GOOD
					 );
			/* LD 081694: set the RTR_SEED_PORT flag for seed ports */
			ifID->ifFlags |= RTR_SEED_PORT;
		}
		else 
			dPrintf(D_M_ELAP,D_L_STARTUP_INFO,
				("elap_online: it's a router, but non seed\n"));
	}
	if (elapp->flags & ELAP_CFG_ZONELESS) {
		elapp->elap_if.ifState = LAP_ONLINE_ZONELESS;
		if (aerr != NULL) {
			errno = at_delay_errno;
			at_delay_errno = 0;
			ioc_ack(errno, m, gref);
			elapp->wait_p = 0; elapp->wait_q = 0; elapp->wait_m = 0;
			dPrintf(D_M_ELAP, D_L_STARTUP_INFO, ("elap_online: ack 3\n"));
		}
		return(0);		/* if not home, it's zoneless */
	}

zip_online_only:
	if (ifID_table[IFID_HOME]) {
		{
			char str[35];
			at_nvestr_t *nve = &ifID_table[IFID_HOME]->ifZoneName;
			strncpy(str,nve->str,nve->len);
			str[nve->len] = '\0';
		}
	}
	sethzonehash(elapp);

	/* Get ZIP rolling to get zone multicast address, etc. */

	elapp->elap_if.ifState = LAP_ONLINE_FOR_ZIP;
	elapp->cfg.network_up = LAP_ONLINE_FOR_ZIP;
 	elapp->wait_m = m;
	if ((errno = zip_control(&elapp->elap_if, ZIP_ONLINE)) != 0) {
		if (errno == ENOTREADY) 
			return (ENOTREADY);
		
ZIP_sleep:
		switch (elapp->elap_if.ifZipError) {
		case 0 :
			break;
		case ENOTREADY :
			elapp->elap_if.ifZipError = 0;
			return (ENOTREADY);
		case ZIP_RE_AARP :
 			elapp->wait_m = m;
			goto re_aarp;
		case ENODEV :
			/* return ENODEV to /etc/appletalk and let the 
			 * network be ONLINE.... this is to tackle the 
			 * case where zone name resolution is not 
			 * complete.
			 */
			elapp->elap_if.ifState = LAP_ONLINE_FOR_ZIP;
			elapp->cfg.network_up = LAP_ONLINE_FOR_ZIP;
			return (ENODEV);
		default :
			return (elapp->elap_if.ifZipError);
		}
	}

	elapp->elap_if.ifState = LAP_ONLINE;
	elapp->cfg.network_up = LAP_ONLINE;

	return (0);
} /* elap_online */


/****************************************************************************
 * elap_offline()
 *
 ****************************************************************************/

StaticProc
int	elap_offline(elapp)
register elap_specifics_t *elapp;

{
	void	zip_sched_getnetinfo(); /* forward reference */
	int	errno;
	int s;

	if (elapp->elap_if.ifState == LAP_OFFLINE) {
#ifdef APPLETALK_DEBUG
kprintf("elap_offline: network is down already.\n");
#endif
		/* the network must be down already! */
		return (EALREADY);
	      }

	elapp->elap_if.ifState = LAP_HANGING_UP;
	elap_hangup(elapp);

	ATDISABLE(s, ddpinp_lock);
	if (MULTIPORT_MODE)
		RT_DELETE(elapp->elap_if.ifThisCableEnd,
			  elapp->elap_if.ifThisCableStart);
	ATENABLE(s, ddpinp_lock);

	if (errno = ddp_rem_if(&elapp->elap_if)) {
#ifdef APPLETALK_DEBUG
kprintf("elap_offline: ddp_rem_if ret %d\n", errno);
#endif
		return (errno);
	}

	dPrintf(D_M_ELAP, D_L_SHUTDN_INFO, ("elap_offline:%s\n", elapp->cfg.if_name));

	/* make sure no zip timeouts are left running */
	atalk_untimeout (zip_sched_getnetinfo, &elapp->elap_if,
		((at_if_t *)&elapp->elap_if)->tmo_3);

	/* clean up the elapp-> and elapp->cfg structures.
	 * initializing with 0 is okay,(state LAP_OFFLINE == 0).
	 */
	bzero ((caddr_t) elapp, sizeof(elap_specifics_t));
	/* Deal with deferred multicast requests */
	dodefer();

	/* *** all of the static variables (and more globals?) should
	   be reinitialized. *** */
	deferno = 0;
	gotIfs = 0;

	/* reset this as the last operation in the shutdown */
	/* *** appletalk_inited cannot be reinitialized until such time as
	   AppleTalk is no longer permanently linked into the kernel.
	   appletalk_inited = 0;
	   */

	return (0);
} /* elap_offline */

StaticProc
void elap_hangup(elapp)
register elap_specifics_t	*elapp;
{
	/* Since AppleTalk is going away, remove the cable
	 * multicast address  and turn the interface off so that all AppleTalk
	 * packets are dropped in the driver itself.
	 */
	/* Get rid of the zone multicast address prior to going Offline. */
	if (zone_multicast_addr.etalk_addr_octet[0] &&	
	    (elapp->flags & ELAP_CFG_HOME)) {
	    /* if zone is set && if this is the home port */
		pat_control(elapp->pat_id, PAT_UNREG_MCAST, 
			    &zone_multicast_addr);
		bzero((caddr_t) &zone_multicast_addr, AARP_ETHER_ADDR_LEN);
	}
	else if (*(int*)&elapp->ZoneMcastAddr)
		pat_control(elapp->pat_id, PAT_REG_MCAST, &elapp->ZoneMcastAddr);
	pat_control(elapp->pat_id, PAT_UNREG_MCAST, &elapp->cable_multicast_addr);
	pat_offline(elapp->pat_id);

	elapp->elap_if.ifState = LAP_OFFLINE;
	return;
}


int	elap_control (ifID, control, data)
register at_if_t  *ifID;
int	          control;
u_char	          *data;
{
	register elap_specifics_t	*elapp;

	/* extract elapp & verify */
	dPrintf(D_M_ELAP, D_L_INFO, ("elap_control:%d\n", control));
	if (!( elapp = (elap_specifics_t *) ifID->ifLapp)) {
		return(ENXIO);
	}

	switch (control) {
	case ELAP_CABLE_BROADCAST_FOR_ZONE :
		/* the cable-broadcast address is to be used as
		 * zone-multicast address.
		 */
		zone_multicast_addr = elapp->cable_multicast_addr;
		pat_control(elapp->pat_id, PAT_REG_MCAST, &zone_multicast_addr);
		break;
	case ELAP_REG_ZONE_MCAST :
		bcopy((caddr_t) data, (caddr_t) &zone_multicast_addr, AARP_ETHER_ADDR_LEN);
		pat_control(elapp->pat_id, PAT_REG_MCAST, &zone_multicast_addr);
		break;
	case ELAP_UNREG_ZONE_MCAST :
		if (*(int *)&zone_multicast_addr) {
			pat_control(elapp->pat_id, PAT_UNREG_MCAST, 
				&zone_multicast_addr);
			bzero((caddr_t) &zone_multicast_addr, AARP_ETHER_ADDR_LEN);
		}
		break;
	case ELAP_RESET_INITNODE :
		ATALK_ASSIGN(elapp->cfg.initial_addr, 0, 0, 0);
		break;
	case ELAP_DESIRED_ZONE :
		if (elapp->cfg.zonename.len)
			bcopy((caddr_t) &elapp->cfg.zonename, (caddr_t) data, 
				elapp->cfg.zonename.len+1);
		break;
	default :
		dPrintf(D_M_ELAP, D_L_ERROR, ("elap_control:invalid control:%d\n",
			control));
		break;
	}
	return (0);
}

StaticProc 
void get_ifs_stat(ifcfgp)
if_cfg_t	*ifcfgp;
{
	static if_cfg_t 	ifcfg;
	int 				ifType, ifMax, i;
	char 				xtype, ifTypeNameBuf[4], ifName[5], *ifTypeName;

	bzero(&ifcfg.state[0][0], sizeof(ifcfg.state));
	for (ifType=0; ifType<IF_TYPENO_CNT; ifType++) {
			
			/* first get reference info on this specific type then status */
		ifTypeName = NULL;
	 	if (!strcmp(if_types[ifType], IF_TYPE_1)) {
			ifMax = IF_TYPE_ET_MAX;
			xtype = IFTYPE_ETHERTALK;
		} else if (!strcmp(if_types[ifType], IF_TYPE_3)) {
			ifMax = IF_TYPE_TR_MAX;
			xtype = IFTYPE_TOKENTALK;
		} else if (!strcmp(if_types[ifType], IF_TYPE_4)) {
			ifMax = IF_TYPE_FD_MAX;
			xtype = IFTYPE_FDDITALK;
		} else if (!strcmp(if_types[ifType], IF_TYPE_5)) {
			ifMax = IF_TYPE_NT_MAX;
			xtype = IFTYPE_NULLTALK;
		} else
			continue;

		for (i=0; i<ifMax; i++) {
			if (pat_units[i].xtype == xtype) {
				ifcfg.state[ifType][pat_units[i].xunit] =
					elap_specifics[i].elap_if.ifState;
				if (ifTypeName == NULL) {
					strcpy(ifTypeNameBuf, pat_units[i].xname);
					ifTypeName = ifTypeNameBuf;
					for (; *ifTypeName != '\0'; ifTypeName++) {
						if ( (*ifTypeName >= '0') && (*ifTypeName <= '9') )
							break;
					}
					*ifTypeName = '\0';
					ifTypeName = ifTypeNameBuf;
				}
			}
		}
		
		/* here we query the OS for each possible I/F type in the system
		 * we only do this one time and keep the results in the static struct 
		 */
		if (!gotIfs && ifTypeName) {
			for (i=0; i<ifMax; i++) {
			  sprintf(ifName,"%s%d",ifTypeName,i);
			  if (!strcmp(ifName, "nt0"))
			    	/* set corresponding bit for I/F */
				ifcfg.avail[ifType] |= 1<<i;
			  else if (ifunit(ifName))
			           /* was (pat_ifpresent(ifName) != -1) */
			  	/* set corresponding bit for I/F */
			    	ifcfg.avail[ifType] |= 1<<i;	
			}
		} /* if !gotIfs */
	} /* for ifType */
	ifcfg.ver_minor = AT_VERSION_MINOR;
	ifcfg.ver_major = AT_VERSION_MAJOR;
	ifcfg.ver_date  = AT_VERSION_DATE;
	bcopy(&ifcfg, ifcfgp, sizeof(if_cfg_t));
	gotIfs = 1;
} /* get_ifs_stat */
	

ifName2Port(name)
	char *name;

/* returns DDP port number of given interface name. to get ifID,
   use port number as index in ifID_table[]

   returns -1 if interface has no ifID entry.
*/

{
	int i;

	for (i=0; i< IF_TOTAL_MAX; i++) {
		if (!ifID_table[i])				/* if we reached end of entries */
			return(-1);
		if (!strcmp(IFID2IFNAME(ifID_table[i]), name))
			return(i);					/* got a match */
	}	
	return(-1);							/* no luck, searched all entries */
}

StaticProc int set_zones(ifz)
	if_zone_info_t *ifz;

/* 1. adds zone to table
   2. looks up each route entry from zone I/F bitmap
   3. sets zone bit in each route entry

   returns  0 if successful
		   -1 if error occurred
*/
{
	int type, iftype,i,j;
	short zno;
	RT_entry *rte;

	zno = zt_add_zone(ifz->zone_name.str, ifz->zone_name.len);

	if (zno == ZT_MAXEDOUT) {
		dPrintf(D_M_ELAP, D_L_ERROR, ("set_zones: error: table full\n"));
		return(-1);
	}
	if (!IFID_VALID(ifID_table[IFID_HOME])) {
		dPrintf(D_M_ELAP, D_L_ERROR, ("set_zones: home ifID is invalid\n"));
		return(-1);
	}
	if (ifz->zone_home)
		ifID_table[IFID_HOME]->ifZoneName = ifz->zone_name;

	for (iftype=0; iftype< IF_TYPENO_CNT; iftype++) {
	 	if (!strcmp(if_types[iftype], IF_TYPE_1))
			type = IFTYPE_ETHERTALK;
	 	else if (!strcmp(if_types[iftype], IF_TYPE_3))
			type = IFTYPE_TOKENTALK;
	 	else if (!strcmp(if_types[iftype], IF_TYPE_4))
			type = IFTYPE_FDDITALK;
		else if (!strcmp(if_types[iftype], IF_TYPE_5))
			type = IFTYPE_NULLTALK;
		else
			continue;
		for (i=0; i<IF_ANY_MAX; i++)  {
			if (ifz->zone_ifs[iftype] & 1<<i) {
				for (j=0; j<IF_ANY_MAX; j++)  {
					if ( (elap_specifics[j].elap_if.ifType == type)
							&& (elap_specifics[j].elap_if.ifUnit == i) )
						break;
				}
				if (j < IF_ANY_MAX)  {
					rte = rt_blookup(ELAP_UNIT2NETSTOP(j));
					if (!rte) {
						dPrintf(D_M_ELAP, D_L_ERROR,
							("set_zones: error: can't find route\n"));
					}
					zt_set_zmap(zno, rte->ZoneBitMap); 

							/* if first zone for this I/F, make default */
					if (!elap_specifics[j].elap_if.ifDefZone)
 						 elap_specifics[j].elap_if.ifDefZone = zno;
				}
			}
		}
	}
	return(0);
}

StaticProc void add_route(rt)
RT_entry 	*rt;

/* support ioctl to manually add routes to table. 
   this is really only for testing
*/
{
	rt_insert( 	rt->NetStop, rt->NetStart, rt->NextIRNet, 
			 	rt->NextIRNode, rt->NetDist, rt->NetPort, 
			 	rt->EntryState);
	dPrintf(D_M_ELAP, D_L_STARTUP_INFO, ("adding route: %ud:%ud dist:%ud\n",
		rt->NetStart, rt->NetStop,rt->NetDist));
}

StaticProc void routerShutdown()
{
	dPrintf(D_M_ELAP, D_L_SHUTDN, ("routerShutdown called\n"));

        if (ROUTING_MODE) {

                rtmp_shutdown();

                /* free memory allocated for the rtmp/zip tables only at this point */
		FREE(ZT_table, M_RTABLE);
                FREE(RT_table, M_RTABLE);
        }             
        nbp_shutdown();         /* clear all known NVE */
        at_state.flags = 0;     /* make sure inits are done on restart */
}

routerStart(flag, m, gref)
int flag;
gbuf_t *m;
gref_t *gref;
{
	register short i;
	register at_if_t *ifID;
	extern rtmp_router_start();

	if (flag) {
		if ((m = at_delay_m) != NULL) {
			at_delay_m = NULL;
			at_delay_func = rtmp_router_start;
			at_delay_flag = 0;
	 		ioc_ack(ENOTREADY, m, at_delay_gref); 
		}
		return (0);
	}

	/*
	 * this will cause the ports to glean from the net the relevant
	 * information before forwarding
	 */
	for (i = 0 ; i < IF_TOTAL_MAX; i++) {
			ifID = ifID_table[i];
			if (ifID) {
				dPrintf(D_M_ELAP, D_L_STARTUP_INFO, 
					("routerStart Port %d (%s) set to activating\n",
					ifID->ifPort, ifID->ifName));
				ifID->ifRoutingState = PORT_ACTIVATING;
				ifID->ifFlags |= RTR_XNET_PORT;
			}
			else 
				break;
	}

	/*
	 * The next step is to check the information for each port before
	 * declaring the ports up and forwarding
	 */
	dPrintf(D_M_ELAP, D_L_STARTUP_INFO,
		("router_start: waiting 20 sec before starting up\n"));

	at_delay_m = m; at_delay_gref = gref;
	atalk_timeout(routerStart, 1, 20 * SYS_HZ); 
	return (0);
}

StaticProc
void ZIPContinue(elapp, m)
elap_specifics_t *elapp;
gbuf_t *m;
{
	if (elapp->wait_p == NULL)
		gbuf_freem(m);
	else {
		(*elapp->wait_p)((void *)elapp, NULL, NULL, m);
	}
}

void ZIPwakeup(ifID)
at_if_t *ifID;
{
	int s;
	gbuf_t *m;
	elap_specifics_t *elapp;

	ATDISABLE(s, ddpinp_lock);
	elapp = (elap_specifics_t *)ifID->ifLapp;
	if ( (elapp != NULL) && ((m = elapp->wait_m) != NULL) ) {
		elapp->wait_m = NULL;
		ATENABLE(s, ddpinp_lock);
		ZIPContinue(elapp, m);
	} else
		ATENABLE(s, ddpinp_lock);
}

StaticProc
void AARPContinue(elapp, m)
elap_specifics_t *elapp;
gbuf_t *m;
{
	int aerr;

	if (elapp->wait_p == NULL)
		gbuf_freem(m);
	else {
		aerr = aarp_init(elapp, 1);
		(*elapp->wait_p)((void *)elapp, NULL, &aerr, m);
	}
}

void AARPwakeup(probe_cb)
aarp_amt_t *probe_cb;
{
	int s;
	gbuf_t *m;
	elap_specifics_t *elapp;

	ATDISABLE(s, arpinp_lock);
	elapp = probe_cb->elapp;
	if ( (elapp != NULL) && ((m = elapp->wait_m) != NULL) ) {
		elapp->wait_m = NULL;
		ATENABLE(s, arpinp_lock);
		AARPContinue(elapp, m);
	} else
		ATENABLE(s, arpinp_lock);
}

IF_UNIT(if_name)
char *if_name;          
{       
        for (; *if_name != '\0'; if_name++)
                if ((*if_name >= '0') && (*if_name <= '9'))
                        return strtol(if_name, (char *)NULL, 10);
        return -1;
}               


StaticProc void getIfNames(names)
if_name_t *names;
{
	int i;
	at_if_t *ifID;
	bzero(names,sizeof(if_name_t) * IF_TOTAL_MAX);
	for (i=0, ifID=ifID_table[0]; ifID; ifID = ifID_table[++i], names++)
		if(ifID)
			strncpy((char *)names,ifID->ifName,sizeof(*names));

	return;
}

void ddp_bit_reverse(addr)
	unsigned char *addr;
{
static unsigned char reverse_data[] = {
	0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
	0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
	0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
	0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
	0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
	0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
	0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
	0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
	0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
	0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
	0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
	0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
	0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
	0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
	0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
	0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
	0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
	0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
	0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
	0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
	0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
	0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
	0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
	0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
	0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
	0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
	0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
	0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
	0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
	0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
	0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
	0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
	};

	unsigned char k;

	for (k=0; k < 6; k++)
		addr[k] = reverse_data[addr[k]];
}

int elap_trackMcast(pat_id,func,addr)
	int pat_id;
	int func;
	caddr_t addr;
{
	int i, loc=-1;
	u_char c;
	switch(elap_specifics[pat_id].elap_if.ifType) {
	case IFTYPE_ETHERTALK: 
	case IFTYPE_FDDITALK: 
	case IFTYPE_NULLTALK: 
						/* set addr to point to unique part of addr */
		c = addr[5];
						/* first try to find match */
		for (i=0; i< MAX_MCASTS; i++) 
			if (c == pat_units[pat_id].mcast[i]) {
				loc = i;
				break;
			}
				
		switch (func) {
		case MCAST_TRACK_DELETE:
			if (loc >= 0) 
				pat_units[pat_id].mcast[loc] = 0;

			break;
		case MCAST_TRACK_ADD:
			dPrintf(D_M_PAT_LOW, D_L_USR2, ("mctrack:add loc:%d\n", i));
			if (loc >= 0) {
				dPrintf(D_M_PAT_LOW, D_L_USR2, ("mctrack:add, addr was there\n"));
				return(1);
				break;			/* already there */
			}		
			for (i=0; i< MAX_MCASTS; i++) 
				if ( pat_units[pat_id].mcast[i] == 0) {
					loc = i;
					break;
				}
			dPrintf(D_M_PAT_LOW, D_L_USR2, ("mctrack:add1 loc:%d\n", i));
			if (loc >= 0) {
				pat_units[pat_id].mcast[loc] = c;
				dPrintf(D_M_PAT_LOW, D_L_USR2, ("mctrack:add, adding(%x)\n",
					(*(int*)addr)&0xffffff));
			}
			else {
				/*errno = ENOMEM; */ /*LD 5/7/97 nobody is using that */
				return(-1);
			}
			break;	
		case MCAST_TRACK_CHECK:
			if (loc >= 0) {
				dPrintf(D_M_PAT_LOW, D_L_USR2, ("mctrack:check, addr was there\n"));
				return(0);
			}
			else {
				dPrintf(D_M_PAT_LOW, D_L_USR2, ("mctrack:add, addr was NOT there\n"));
				return(-1);
			}
			
		default:
			/*errno = EINVAL;*/ /*LD 5/7/97 nobody is using that */
			return(-1);
		}

	case IFTYPE_TOKENTALK:
		/* we would use the lowest byte of the addr argument as a value
		   to shift left a 1 to form the mcast mask for TR. We'll do this
		   when the time comes
		 */
	default:
		;
	}
	return(0);
}


static 
getSnmpCfg(snmp)
	snmpCfg_t *snmp;
{
	int i;
	elap_specifics_t 	*elapp;
	snmpIfCfg_t			*ifc;
	at_if_t				*ifID;

	snmp->cfg_ifCnt = 0;
	
	bzero(snmp,sizeof(snmpCfg_t));
	for (i=0, elapp=elap_specifics,ifc=snmp->cfg_ifCfg; 
		 i<IF_TYPE_ET_MAX; i++, elapp++, ifc++) {
			ifID = &elap_specifics[i].elap_if;
		if (ifID->ifState != LAP_OFFLINE) {
			snmp->cfg_ifCnt++;
			strncpy(ifc->ifc_name,elapp->cfg.if_name,IF_NAME_SIZE);
			ifc->ifc_aarpSize = getAarpTableSize(i);
			ifc->ifc_addrSize = getPhysAddrSize(i);
			switch (ifID->ifType) {
				case IFTYPE_ETHERTALK:
					ifc->ifc_type = SNMP_TYPE_ETHER2;
					break;
				case IFTYPE_TOKENTALK:
					ifc->ifc_type = SNMP_TYPE_TOKEN;
					break;
				case IFTYPE_LOCALTALK:
					ifc->ifc_type = SNMP_TYPE_LOCAL;
					break;
				case IFTYPE_FDDITALK:
				case IFTYPE_NULLTALK:
				default:
					ifc->ifc_type = SNMP_TYPE_OTHER;
					break;
			}
			ifc->ifc_start 	= ifID->ifThisCableStart;
			ifc->ifc_end	= ifID->ifThisCableEnd;
			ifc->ifc_ddpAddr= ifID->ifThisNode;
			ifc->ifc_status	= ifID->ifState == LAP_ONLINE ? 1 : 2;
			ifc->ifc_zoneName.len = 0;
			if (ifID->ifZoneName.len != 0) {
				ifc->ifc_zoneName = ifID->ifZoneName;
			}
			else if (ifID->ifDefZone) {
				ifc->ifc_zoneName = ZT_table[ifID->ifDefZone-1].Zone;
			}
			else	/* temp, debug only */
				ifc->ifc_zoneName = ZT_table[0].Zone;
			if (ROUTING_MODE) {
				if (ifID->ifFlags & RTR_SEED_PORT) {
					ifc->ifc_netCfg  = SNMP_CFG_CONFIGURED;
					ifc->ifc_zoneCfg = SNMP_CFG_CONFIGURED;
				}
				else {
					ifc->ifc_netCfg  = SNMP_CFG_GARNERED;
					ifc->ifc_zoneCfg = SNMP_CFG_GARNERED;
				}
			}
			else  { 	/* single-port mode */
				if (ifID->ifRouterState == ROUTER_AROUND) {
					ifc->ifc_netCfg = SNMP_CFG_GARNERED;
				}
				else {
					ifc->ifc_netCfg = SNMP_CFG_GUESSED;
					ifc->ifc_zoneCfg = SNMP_CFG_UNCONFIG;
				}
			}
		}
	} 
	snmp->cfg_flags = at_statep->flags;

		
	return(0);
}	

void
elap_get_addr(pat_id, addr)
	int pat_id;
	unsigned char *addr; /* pointer to buffer where the address is returned */
{
	bcopy(pat_units[pat_id].xaddr, addr, pat_units[pat_id].xaddrlen);
}

int
pat_ID(if_name)
	char	*if_name;
{
	int pat_id;

	for (pat_id=0; pat_id < xpatcnt; pat_id++) {
		if (!strcmp(pat_units[pat_id].xname, if_name))
			return(pat_id);
	}

	return(-1);
}

int
pat_control(pat_id, control, data)
	int pat_id;
	int control;
	void *data;
{
	typedef int (*procptr)();

	switch(control) {
	case PAT_REG_CONTEXT :
		pat_units[pat_id].context = data;
		break;
	case PAT_REG_AARP_UPSTREAM :
		pat_units[pat_id].aarp_func = (procptr)data;
		break;
	case PAT_REG_CHECKADDR :
		pat_units[pat_id].addr_check = (procptr)data;
		break;
	case PAT_REG_MCAST :
		if (elap_trackMcast(pat_id, MCAST_TRACK_ADD,data) == 1)
			return(0);
		return(domcast(pat_id, control, data) == 0? 0 : -1);
	case PAT_UNREG_MCAST :
		elap_trackMcast(pat_id, MCAST_TRACK_DELETE, data);
		return(domcast(pat_id, control, data) == 0? 0 : -1);
	default :
		return(-1);
	}

	return(0);
}

StaticProc void
pat_init()
{
#ifdef CHECK_DDPR_FLAG
	extern int ddprunning_flag;
#endif
	/* zeroing the struct is okay since PAT_OFFLINE is zero */
	bzero(pat_units, sizeof(pat_units));
#ifdef CHECK_DDPR_FLAG
	ddprunning_flag = 0;
#endif
}

/*
 * Hopefully temporary hackery to deal with the need to
 *  register/deregister multicast addresses at interrupt level
 *  (as a result of incoming ZIP info).  ioctl routines can't
 *  be called at interrupt level due to locking constraints (MP
 *  and all that)
 * Unfortunately, I can't find a better way.
 */

static struct multidefer {
	int pat_id;
	int control;
	unsigned char addr[6];
} defer[NDEFERS];

StaticProc int
domcast(pat_id, control, data)
	int pat_id;
	int control;
	unsigned char *data;
{
	if (pat_units[pat_id].xtype == IFTYPE_NULLTALK)
		return 0;
#ifdef _AIX
	if  (getpid() == -1) {
		if (deferno < NDEFERS)  {
			defer[deferno].pat_id = pat_id;
			defer[deferno].control = control;
			bcopy(data, defer[deferno].addr, sizeof(defer[deferno].addr));
			deferno++;
			return 0;
			
		}
		else	
			return EINVAL;
	}
#endif

	dPrintf(D_M_PAT, D_L_STARTUP, ("domcast:%s multicast %08x%04x pat_id:%d\n",
			(control == PAT_REG_MCAST) ? "adding" : "deleting",
			*(unsigned*)data, (*(unsigned *)(data+2))&0x0000ffff, pat_id));

	return pat_mcast(pat_id, control, data);
}

/*
 * Handle any backed-up requests to register/unregister multicasts.
 * Should not be called at interrupt level!
 */
StaticProc void
dodefer()
{
	unsigned char *data;
	while (deferno > 0)
	{	--deferno;
		data = (unsigned char *)defer[deferno].addr;

		dPrintf(D_M_PAT, D_L_STARTUP,
			("dodefer:%s multicast %08x%04x pat_id:%d defno:%d\n",
			(defer[deferno].control == PAT_REG_MCAST) ? "adding" : "deleting",
			*(unsigned*)data,
			(*(unsigned *)(data+2))&0x0000ffff,
			defer[deferno].pat_id,
			deferno));
		
		domcast(defer[deferno].pat_id, defer[deferno].control,
			defer[deferno].addr);
	}
}