1/*
2 * Copyright (c) 1998-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 *   Modified, March 17, 1997 by Tuyen Nguyen for MacOSX.
30 */
31
32#include <string.h>
33
34#include <sys/errno.h>
35#include <sys/types.h>
36#include <sys/param.h>
37#include <machine/spl.h>
38#include <sys/systm.h>
39#include <sys/kernel.h>
40#include <sys/proc.h>
41#include <sys/filedesc.h>
42#include <sys/fcntl.h>
43#include <sys/mbuf.h>
44#include <sys/ioctl.h>
45#include <sys/malloc.h>
46#include <sys/socket.h>
47#include <sys/socketvar.h>
48
49#include <net/if.h>
50#include <net/if_types.h>
51
52#include <netat/sysglue.h>
53#include <netat/appletalk.h>
54#include <netat/at_pcb.h>
55#include <netat/at_var.h>
56#include <netat/ddp.h>
57#include <netat/nbp.h>
58#include <netat/zip.h>
59#include <netat/rtmp.h>
60#include <netat/routing_tables.h>	/* router */
61#include <netat/at_snmp.h>
62#include <netat/debug.h>
63
64/* reaching for DDP and NBP headers in the datagram */
65#define DATA_DDP(mp)	((at_ddp_t *)(gbuf_rptr(mp)))
66#define	DATA_NBP(mp)	((at_nbp_t *)((DATA_DDP(mp))->data))
67
68/* Get to the nve_entry_t part ofthe buffer */
69#define	NVE_ENTRY(mp)	(nve_entry_t *)(gbuf_rptr(mp))
70
71#ifndef	MIN
72#define	MIN(a,b)	((a)>(b)?(b):(a))
73#endif
74
75#define	errno	nbperrno
76
77	/* externs */
78extern at_ifaddr_t *ifID_table[];
79extern at_ifaddr_t *ifID_home;
80
81TAILQ_HEAD(name_registry, _nve_) name_registry;
82
83
84/* statics */
85static	int		errno;
86static  gbuf_t  *lzones=0;	/* head of local zones list */
87static int	lzonecnt=0;		/* # zones stored in lzones	*/
88static u_int  hzonehash=0;	/* hash val of home zone */
89
90static int	nbp_lkup_reply(nbp_req_t *, nve_entry_t *);
91static int	nbp_strcmp(at_nvestr_t *, at_nvestr_t *, u_char);
92static int	nbp_setup_resp(nbp_req_t *, int);
93static int	nbp_send_resp(nbp_req_t *);
94static int	nbp_validate_n_hash(nbp_req_t *, int, int);
95static	nve_entry_t	*nbp_search_nve(nbp_req_t *, at_ifaddr_t *);
96static int isZoneLocal(at_nvestr_t *);
97static int nbp_enum_gen (nve_entry_t *);
98static	void	nbp_setup_hdr (nbp_req_t *);
99static	void	nbp_upshift (u_char *, int);
100static u_char *nbp2zone(at_nbp_t *, u_char *);
101
102/* macros */
103#define NVE_LOCK nve_lock
104
105
106static long nbp_id_count = 0;
107
108void sethzonehash(elapp)
109     at_ifaddr_t *elapp;
110{
111	if (elapp->startup_zone.len)  {
112		hzonehash = nbp_strhash(&elapp->startup_zone);
113	}
114}
115
116void nbp_shutdown(void)
117{
118	/* delete all NVE's and release buffers */
119	register nve_entry_t	*nve_entry, *nve_next;
120
121	for ((nve_entry = TAILQ_FIRST(&name_registry)); nve_entry; nve_entry = nve_next) {
122			nve_next = TAILQ_NEXT(nve_entry, nve_link);
123
124                /* NB: nbp_delete_entry calls TAILQ_REMOVE */
125		nbp_delete_entry(nve_entry);
126	}
127
128	if (lzones) {
129		gbuf_freem(lzones);
130		lzonecnt = 0;
131		lzones = NULL;
132	}
133} /* nbp_shutdown */
134
135static
136u_char *nbp2zone(nbp, maxp)
137	at_nbp_t *nbp;
138	u_char *maxp;
139{
140
141	u_char *p;
142
143	p = (u_char*)&nbp->tuple[0].enu_entity;	/* p -> object */
144	if (p >= maxp) return NULL;
145	p += (*p +1);				/* p -> type   */
146	if (p >= maxp) return NULL;
147	p += (*p +1);				/* p -> zone   */
148	if (p >= maxp) return NULL;
149	if ((p + *p) >= maxp) return NULL;
150	return(p);
151}
152
153void nbp_input(m, ifID)
154     register gbuf_t	*m;
155     register at_ifaddr_t *ifID;
156
157{
158	register at_ddp_t 	*ddp = DATA_DDP(m);
159	register at_nbp_t	*nbp = DATA_NBP(m);
160	register RT_entry	*rt;
161	register int ddpSent = FALSE; 	/* true if we re-sent this pkt (don't free) */
162	struct etalk_addr mcastAddr;
163	nbp_req_t	nbp_req;
164	u_char *p;
165
166	/* from original nbp_input() when this function was nbp_handler() */
167	if ((gbuf_type(m) != MT_DATA && gbuf_type(m) != MSG_DATA) ||
168	    ddp->type != DDP_NBP) {
169		gbuf_freem(m);
170		return;
171	}
172
173	/* Some initializations */
174	nbp_req.response = NULL;
175	nbp_req.request = m;
176	nbp_req.space_unused = nbp_req.flags = 0;
177
178	dPrintf(D_M_NBP_LOW, D_L_USR1,
179		("nbp_input control:%d tuplecount:%d id:%d\n",
180		nbp->control, nbp->tuple_count, nbp->at_nbp_id));
181	switch (nbp->control) {
182	case NBP_LKUP :
183	  {
184		at_net_al dst_net;
185
186		dst_net = NET_VALUE(ddp->dst_net);
187		dPrintf(D_M_NBP_LOW, D_L_USR2, (" LKUP %s\n",
188			ifID != ifID_home ? "non-home" : "home"));
189		if ( ROUTING_MODE && (NET_VALUE(ddp->dst_net) != 0)
190			&& ((dst_net < ifID->ifThisCableStart)
191			    || (dst_net > ifID->ifThisCableEnd)) ) {
192			routing_needed(m, ifID, TRUE);
193			ddpSent = TRUE;
194			break;
195		}
196	  }
197
198		if (nbp_validate_n_hash (&nbp_req, TRUE, FALSE) == 0) {
199			nbp_req.func = nbp_lkup_reply;
200			(void) nbp_search_nve(&nbp_req, ifID);
201			if (nbp_req.response) {
202				nbp_send_resp(&nbp_req);
203			}
204		}
205#ifdef NBP_DEBUG
206	{
207		char zone[35],object[35],type[35];
208		strlcpy(zone,nbp_req.nve.zone.str, sizeof(zone));
209		strlcpy(object,nbp_req.nve.object.str, sizeof(object));
210		strlcpy(type,nbp_req.nve.type.str, sizeof(type));
211		if (ifID != ifID_home)
212			dPrintf(D_M_NBP_LOW,D_L_USR2,
213				("nbp_LKUP for:%s:%s@%s", object, type, zone));
214	}
215#endif /* NBP_DEBUG */
216
217		break;
218	case NBP_FWDRQ:
219		{
220 		register int	zhome=0;
221				/* true if home zone == destination zone */
222 		register int	zno, i;
223 		register  gbuf_t	*m2;
224		register int error_found =0;
225		register at_ifaddr_t *ifIDorig;
226
227		if (!ROUTING_MODE)	/* for routers only! */
228			break;
229
230		ifIDorig = ifID;
231		ifID= NULL;
232		for (i = 0 ; i < RT_maxentry; i++) {
233			rt = &RT_table[i];
234			if ((rt->EntryState & RTE_STATE_PERMANENT) &&
235				NET_VALUE(ddp->dst_net) >= rt->NetStart &&
236				NET_VALUE(ddp->dst_net) <=	rt->NetStop
237			   ) {
238			   	/* sanity check */
239			   	if (rt->NetPort >= IF_TOTAL_MAX) {
240					dPrintf(D_M_NBP,D_L_ERROR,
241						("nbp_input:FWDREQ: bad port# from RT_table\n"));
242					error_found = TRUE;
243					break;
244				}
245			 	ifID = ifID_table[rt->NetPort];
246				if (!ifID) {
247					dPrintf(D_M_NBP,D_L_ERROR,
248						("nbp_input:FWDREQ: ifID %s\n",
249						!ifID ? "not found" : "invalid"));
250					error_found = TRUE;
251					break;
252				}
253				if (ifID->ifState == LAP_OFFLINE) {
254					dPrintf(D_M_NBP,D_L_ERROR,
255						("nbp_input:FWDREQ: ifID offline (port %d)\n",
256					  	rt->NetPort));
257					error_found = TRUE;
258					break;
259				}
260			   break;
261			}
262		}
263		if (error_found) /* the port is not correct */
264			break;
265
266		if (!ifID) { /* this packet is not for us, let the routing engine handle it  */
267			routing_needed(m, ifIDorig, TRUE);
268			ddpSent= TRUE;
269			break;
270		}
271
272		/*
273		 * At this point, we have a valid Forward request for one of our
274		 * directly connected port. Convert it to a NBP Lookup
275		 */
276
277		nbp->control = NBP_LKUP;
278	 	NET_ASSIGN(ddp->dst_net, 0);
279	 	ddp->dst_node = 255;
280
281
282 /*### LD 01/18/94 Check if the dest is also the home zone. */
283
284 		p = nbp2zone(nbp, (u_char *)gbuf_wptr(m));
285 		if ((p == NULL) || !(zno = zt_find_zname((at_nvestr_t *)p))) {
286 			dPrintf(D_M_NBP,D_L_WARNING,
287 				("nbp_input: FWDRQ:zone not found\n"));
288			break;
289 		}
290		if (isZoneLocal((at_nvestr_t*)p))
291			zhome = TRUE;				/* one of our  ports is in destination zone */
292		if (!zt_get_zmcast(ifID, (at_nvestr_t*)p, (char *)&mcastAddr)) {
293			dPrintf(D_M_NBP,D_L_ERROR,
294				("nbp_input: FDWREQ:zt_get_zmcast error\n"));
295			break;
296		}
297
298
299 		if (zhome) { /*### LD 01/18/95  In case our home is here, call back nbp */
300
301 			if (!(m2 = (gbuf_t *)gbuf_copym((gbuf_t *)m))) {
302 				dPrintf(D_M_NBP,D_L_ERROR,
303 					("nbp_input: FWDRQ:gbuf_copym failed\n"));
304 				break;
305 			}
306
307 			ddp = DATA_DDP(m2);
308 			nbp = DATA_NBP(m2);
309 			nbp->control  = NBP_LKUP;
310 	 		NET_ASSIGN(ddp->dst_net, 0);
311 	 		ddp->dst_node = 255;
312 			dPrintf(D_M_NBP,D_L_INFO,
313 				("nbp_input: FWDRQ:loop back for us\n"));
314 			nbp_input(m2, ifID_home);
315 		}
316
317		if (FDDI_OR_TOKENRING(ifID->aa_ifp->if_type))
318			ddp_bit_reverse((unsigned char *)&mcastAddr);
319		ddp_router_output(m, ifID, ET_ADDR, 0, 0, &mcastAddr);
320		ddpSent = TRUE;
321		}
322		break;
323
324	case NBP_BRRQ:
325		{
326		register int	zno;		/* zone table entry numb */
327		register int 	ztind;		/* zone bitmap index into RT_entry */
328		register int	ztbit;		/* zone bit to check within above index */
329		register int	zhome=0;	/* true if home zone == destination zone */
330		register int	i;
331		register  gbuf_t	*m2, *m3;
332		register int fromUs = FALSE;
333		register at_socket ourSkt = 0;	/* originating skt */
334
335		/* for router & MH local only */
336		if ((!(MULTIHOME_MODE && FROM_US(ddp))) && !ROUTING_MODE) {
337			dPrintf(D_M_NBP,D_L_USR2,
338				("nbp_input: BRREQ:non router or MH local\n"));
339
340			break;
341		}
342 		p = nbp2zone(nbp, (u_char *)gbuf_wptr(m));
343		if ((p == NULL) || !(zno = zt_find_zname((at_nvestr_t *)p))) {
344			break;
345		}
346		if (MULTIHOME_MODE && ifID->ifRouterState == NO_ROUTER) {
347			((at_nvestr_t*)p)->len = 1;
348			((at_nvestr_t*)p)->str[0] = '*';
349		}
350		if (isZoneLocal((at_nvestr_t*)p)) {
351			zhome = TRUE;		/* one of our ports is in destination zone */
352		}
353		if (FROM_US(ddp)){	/* save, before we munge it */
354			fromUs = TRUE;
355			ourSkt = ddp->src_socket;
356			dPrintf(D_M_NBP,D_L_USR2,
357				("nbp_input:BRRQ from us net:%d\n",
358				(int)NET_VALUE(ddp->src_net)));
359		}
360			/* from ZT_CLR_ZMAP */
361		i = zno - 1;
362		ztind = i >> 3;
363		ztbit = 0x80 >> (i % 8);
364		for (i=0,rt=RT_table; i<RT_maxentry; i++,rt++) {
365			if (!(rt->ZoneBitMap[ztind] & ztbit)) 		/* if zone not in route, skip*/
366				continue;
367/*		dPrintf(D_M_NBP, D_L_USR3,
368			("nbp_input: BRREQ: port:%d, entry %d\n",
369				rt->NetPort, i));
370*/
371
372			ifID = ifID_table[rt->NetPort];
373			if (!ifID) {
374				dPrintf(D_M_NBP, D_L_ERROR,
375					("nbp_input:BRRQ: ifID %s\n",
376					!ifID ? "not found" : "invalid"));
377				break;
378			}
379
380			ddp = DATA_DDP(m);
381			ddp->src_node = ifID->ifThisNode.s_node;
382			NET_ASSIGN(ddp->src_net,  ifID->ifThisNode.s_net);
383			ddp->src_socket = NBP_SOCKET;
384			if (!(m2 = (gbuf_t *)gbuf_copym((gbuf_t *)m))) {
385				dPrintf(D_M_NBP,D_L_ERROR,
386					("nbp_input: BRREQ:gbuf_copym failed\n"));
387				break;
388			}
389
390			ddp = DATA_DDP(m2);
391			nbp = DATA_NBP(m2);
392/*			nbp->tuple[0].enu_addr.socket = NBP_SOCKET; */
393			if (MULTIHOME_MODE && fromUs ) {
394				/* set the return address of the lookup to that of the
395				   interface it's going out on so that replies come back
396				   on that net */
397				dPrintf(D_M_NBP,D_L_USR3,
398				   ("nbp_input: BRREQ: src changed to %d.%d.%d\n",
399					ifID->ifThisNode.s_net,
400					ifID->ifThisNode.s_node, ourSkt));
401				nbp->tuple[0].enu_addr.net = htons(ifID->ifThisNode.s_net);
402				nbp->tuple[0].enu_addr.node = ifID->ifThisNode.s_node;
403				nbp->tuple[0].enu_addr.socket = ourSkt;
404				ddp->src_socket = NBP_SOCKET;
405			}
406#if DEBUG
407			else
408				dPrintf(D_M_NBP, D_L_USR3,
409					("nbp_input: BRREQ: not from us\n"));
410#endif /* DEBUG */
411			dPrintf(D_M_NBP, D_L_USR3,
412				("nbp_input dist:%d\n", rt->NetDist));
413			if (rt->NetDist == 0) {			/* if direct connect, *we* do the LKUP */
414				nbp->control  = NBP_LKUP;
415	 			NET_ASSIGN(ddp->dst_net, 0);
416	 			ddp->dst_node = 255;
417				if (!zt_get_zmcast(ifID, (at_nvestr_t*)p, (char *)&mcastAddr)) {
418					dPrintf(D_M_NBP,D_L_ERROR,
419						("nbp_input: BRRQ:zt_get_zmcast error\n"));
420					break;
421				}
422				if (FDDI_OR_TOKENRING(ifID->aa_ifp->if_type))
423					ddp_bit_reverse((unsigned char *)&mcastAddr);
424				ddp_router_output(m2, ifID, ET_ADDR, 0, 0, &mcastAddr);
425			}
426			else {							/* else fwd to router */
427				ddp->dst_node = 0;
428				if (rt->NetStart == 0)		/* if Ltalk */
429					NET_ASSIGN(ddp->dst_net, rt->NetStop);
430				else
431					NET_ASSIGN(ddp->dst_net, rt->NetStart);
432				nbp->control  = NBP_FWDRQ;
433				ddp_router_output(m2, ifID, AT_ADDR,
434						  rt->NextIRNet, rt->NextIRNode,
435						  NULL);
436			}
437		}
438		if (!zhome)
439			break;
440
441		if (!(m3 = (gbuf_t *)gbuf_copym((gbuf_t *)m))) {
442			dPrintf(D_M_NBP,D_L_ERROR,
443				("nbp_input: BRREQ:gbuf_copym failed\n"));
444			break;
445		}
446
447		ddp = DATA_DDP(m3);
448		nbp = DATA_NBP(m3);
449
450		nbp->control  = NBP_LKUP;
451	 	NET_ASSIGN(ddp->dst_net, 0);
452	 	ddp->dst_node = 255;
453 		dPrintf(D_M_NBP,D_L_INFO, ("nbp_input: BRRQ:loop back for us\n"));
454		nbp_input(m3, ifID_home);
455		break;
456		}
457
458	case NBP_LKUP_REPLY:
459
460		if (!ROUTING_MODE)	/* for routers only! */
461			break;
462
463		dPrintf(D_M_NBP,D_L_WARNING,
464			("nbp_input: routing needed for LKUP_REPLY: from %d.%d\n",
465			 NET_VALUE(ddp->src_net), ddp->src_node));
466		routing_needed(m, ifID, TRUE);
467		ddpSent = TRUE;
468		break;
469
470	default :
471		dPrintf(D_M_NBP,D_L_ERROR,
472			("nbp_input: unhandled pkt: type:%d\n", nbp->control));
473
474		routing_needed(m, ifID, TRUE);
475		ddpSent = TRUE;
476		break;
477	} /* switch control */
478
479	if (!ddpSent)
480		gbuf_freem(m);
481	return;
482} /* nbp_input */
483
484static	int	nbp_validate_n_hash (nbp_req, wild_ok, checkLocal)
485     register nbp_req_t	*nbp_req;
486     register int	wild_ok;
487     register int	checkLocal;	/* if true check if local zone */
488{
489        register at_nvestr_t	*object, *type, *zone;
490	at_nbptuple_t	*tuple;
491	register int	i, part_wild;
492
493	tuple = DATA_NBP(nbp_req->request)->tuple;
494	nbp_req->flags = 0;
495#ifdef COMMENTED_OUT
496	{
497		int net,node,skt;
498		net = ntohs(tuple->enu_addr.net);
499		node = tuple->enu_addr.node;
500		skt = tuple->enu_addr.socket;
501		dPrintf(D_M_NBP_LOW,D_L_USR4,
502			("nbp_validate: tuple addr:%d:%d:%d\n",net,node,skt));
503	}
504#endif /* COMMENTED_OUT */
505
506	/* tuple is in the compressed (no "filler") format */
507	object = (at_nvestr_t *)&tuple->enu_entity;
508	type = (at_nvestr_t *)(&object->str[object->len]);
509	zone = (at_nvestr_t *)(&type->str[type->len]);
510
511	if (object->len > NBP_NVE_STR_SIZE || type->len > NBP_NVE_STR_SIZE ||
512		zone->len > NBP_NVE_STR_SIZE) {
513		dPrintf(D_M_NBP_LOW, D_L_WARNING,
514			("nbp_val_n_hash: bad str len\n"));
515		errno = EINVAL;
516		return (-1);
517	}
518
519#ifdef NBP_DEBUG
520	{
521		char xzone[35],xobject[35],xtype[35];
522		strlcpy(xzone,zone->str, sizeof(xzone));
523		strlcpy(xobject,object->str, sizeof(xobject));
524		strlcpy(xtype,type->str, sizeof(xtype));
525		dPrintf(D_M_NBP_LOW, D_L_USR4,
526			("nbp_validate: looking for %s:%s@%s\n",
527			xobject, xtype, xzone));
528	}
529#endif /* NBP_DEBUG */
530	/* Is this request for our zone ?? */
531	nbp_req->nve.zone.len = zone->len;
532	nbp_req->nve.zone_hash = 0;
533	bcopy(zone->str,nbp_req->nve.zone.str, zone->len);
534
535	if (checkLocal && !isZoneLocal(zone)) {
536		char str[35];
537		strlcpy((char *)str,(char *)zone->str,sizeof(str));
538		dPrintf(D_M_NBP_LOW,D_L_WARNING,
539			("nbp_val_n_hash bad zone: %s\n", str));
540		errno = EINVAL;
541		return(-1);
542	}
543
544	if (!DEFAULT_ZONE(zone)) {
545		nbp_req->nve.zone_hash = nbp_strhash(& nbp_req->nve.zone);
546	}
547
548	nbp_req->nve.address = tuple->enu_addr;
549	nbp_req->nve.object.len = object->len;
550	nbp_req->nve.object_hash = 0;
551	if (object->len == 1 && (object->str[0] == NBP_ORD_WILDCARD ||
552		object->str[0] == NBP_SPL_WILDCARD)) {
553		if (wild_ok)
554			nbp_req->flags |= NBP_WILD_OBJECT;
555		else {
556			dPrintf(D_M_NBP_LOW, D_L_WARNING,
557				("nbp_val_n_hash: wild not okay\n"));
558			errno = EINVAL;
559			return (-1);
560		}
561	} else{
562		for (i = part_wild = 0; (unsigned) i<object->len; i++) {
563			if (object->str[i] == NBP_SPL_WILDCARD) {
564				if (wild_ok) {
565					if (part_wild) {
566					  dPrintf(D_M_NBP_LOW, D_L_WARNING,
567						  ("nbp_val_n_hash: too many parts wild\n"));
568					  errno = EINVAL;
569					  return (-1);
570					} else
571					  part_wild++;
572				} else {
573				  dPrintf(D_M_NBP_LOW, D_L_WARNING,
574					  ("nbp_val_n_hash: wild not okay2\n"));
575				  errno = EINVAL;
576				  return (-1);
577				}
578			}
579			nbp_req->nve.object.str[i] = object->str[i];
580		}
581		if (!part_wild)
582			nbp_req->nve.object_hash =
583				nbp_strhash(&nbp_req->nve.object);
584	}
585
586	nbp_req->nve.type.len = type->len;
587	nbp_req->nve.type_hash = 0;
588	if (type->len == 1 && (type->str[0] == NBP_ORD_WILDCARD ||
589		type->str[0] == NBP_SPL_WILDCARD)) {
590		if (wild_ok)
591			nbp_req->flags |= NBP_WILD_TYPE;
592		else {
593			dPrintf(D_M_NBP_LOW, D_L_WARNING,
594				("nbp_val_n_hash: wild not okay3\n"));
595			errno = EINVAL;
596			return (-1);
597		}
598	} else {
599		for (i = part_wild = 0; (unsigned) i<type->len; i++) {
600			if (type->str[i] == NBP_SPL_WILDCARD) {
601				if (wild_ok) {
602					if (part_wild) {
603					  dPrintf(D_M_NBP_LOW, D_L_WARNING,
604						  ("nbp_val_n_hash: too many parts wild2\n"));
605					  errno = EINVAL;
606					  return (-1);
607					} else
608					  part_wild++;
609				} else {
610					errno = EINVAL;
611					return (-1);
612				}
613			}
614			nbp_req->nve.type.str[i] = type->str[i];
615		}
616		if (!part_wild)
617			nbp_req->nve.type_hash =
618				nbp_strhash(&nbp_req->nve.type);
619	}
620#ifdef NBP_DEBUG
621	{
622		char zone[35],object[35],type[35];
623		strlcpy(zone,nbp_req.nve.zone.str, sizeof(zone));
624		strlcpy(object,nbp_req.nve.object.str, sizeof(object));
625		strlcpy(type,nbp_req.nve.type.str, sizeof(type));
626		dPrintf(D_M_NBP_LOW,D_L_USR4,
627			("nbp_validate: after hash: %s:%s@%s\n",
628			object, type, zone));
629	}
630#endif /* NBP_DEBUG */
631	return(0);
632} /* nbp_validate_n_hash */
633
634
635/* Upshifts in place */
636static	void	nbp_upshift (str, count)
637register u_char	*str;
638register int	count;
639{
640	register int	i, j;
641	register u_char	ch;
642	static	unsigned char	lower_case[] =
643		{0x8a, 0x8c, 0x8d, 0x8e, 0x96, 0x9a, 0x9f, 0xbe,
644		 0xbf, 0xcf, 0x9b, 0x8b, 0x88, 0};
645	static	unsigned char	upper_case[] =
646		{0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0xae,
647		 0xaf, 0xce, 0xcd, 0xcc, 0xcb, 0};
648
649	for (j=0 ; j<count ; j++) {
650		ch = str[j];
651		if (ch >= 'a' && ch <= 'z')
652			str[j] = ch + 'A' - 'a';
653		else if (ch & 0x80)
654			for (i=0; lower_case[i]; i++)
655				if (ch == lower_case[i])
656					str[j] = upper_case[i];
657	}
658}
659
660
661u_int nbp_strhash (nvestr)
662     register at_nvestr_t	*nvestr;
663{
664	/* upshift while hashing */
665	register u_int	hash = 0;
666	register int	i, len;
667	union {
668		u_char	h_4char[4];
669		int	h_int;
670	} un;
671
672	for (i=0; (unsigned) i < nvestr->len; i+=sizeof(int)) {
673		len = MIN((size_t)(nvestr->len-i), sizeof(int));
674		if (len == sizeof(int))
675			bcopy(&(nvestr->str[i]), &un, sizeof(un));
676		else {
677			un.h_int = -1;
678			for ( ; (unsigned) i<nvestr->len; i++)
679				un.h_4char[i % sizeof(int)] = nvestr->str[i];
680		}
681		nbp_upshift (un.h_4char, len);
682		hash ^= un.h_int;
683	}
684
685	return (hash);
686} /* nbp_strhash */
687
688static	nve_entry_t *nbp_search_nve (nbp_req, ifID)
689     register nbp_req_t	*nbp_req;
690     register at_ifaddr_t 	*ifID;		/* NULL ok */
691{
692	register nve_entry_t	*nve_entry;
693
694#ifdef NBP_DEBUG
695	{
696		char zone[35],object[35],type[35];
697		strlcpy(zone,nbp_req.nve.zone.str, sizeof(zone));
698		strlcpy(object,nbp_req.nve.object.str, sizeof(object));
699		strlcpy(type,nbp_req.nve.type.str, sizeof(type));
700		dPrintf(D_M_NBP_LOW, D_L_USR4,
701				("nbp_search: looking for %s:%s@%s resp:0x%x\n",object,type,zone,
702				(u_int) nbp_req->response));
703	}
704#endif /* NBP_DEBUG */
705	TAILQ_FOREACH(nve_entry, &name_registry, nve_link) {
706		if ((nbp_req->nve.zone_hash) &&
707			((nbp_req->nve.zone_hash !=
708			  nve_entry->zone_hash) &&
709			 (nbp_req->nve.zone_hash != hzonehash)
710		    )
711		   ) {
712			dPrintf(D_M_NBP_LOW,D_L_USR4,
713				("nbp_search: no match for zone, req hash:%x\n",
714			nbp_req->nve.zone_hash));
715			continue;
716		}
717		else { 	/* for this entry's zone OR no zone in request or entry */
718			/* only in singleport mode (!MULTIPORT_MODE) with
719			   empty PRAM can an entry have '*' for it's zone
720			*/
721			at_nvestr_t *ezone=&nve_entry->zone;
722			at_nvestr_t *rzone=&nbp_req->nve.zone;
723			if (!DEFAULT_ZONE(rzone) && !DEFAULT_ZONE(ezone))  {
724				if (nbp_strcmp (rzone, ezone, 0) != 0)
725					continue;
726			}
727			else {
728			    if (MULTIHOME_MODE && ifID &&
729				(nve_entry->address.net !=
730				 ifID->ifThisNode.s_net)) {
731				dPrintf(D_M_NBP, D_L_USR4,
732					("nbp search ifID (%d) & req net (%d) not eq\n",
733					 nve_entry->address.net,
734					 ifID->ifThisNode.s_net));
735				continue;
736			    }
737#if DEBUG
738			    if (ifID)
739				dPrintf(D_M_NBP, D_L_USR4,
740					("nbp search ifID (%d) & req net (%d)  equal\n",
741					 nve_entry->address.net,
742					 ifID->ifThisNode.s_net));
743#endif /* DEBUG */
744			}
745
746		}
747		if (!(nbp_req->flags & NBP_WILD_OBJECT)) {
748			if ((nbp_req->nve.object_hash) &&
749				(nbp_req->nve.object_hash !=
750				nve_entry->object_hash))
751				continue;
752			else {
753				if (nbp_strcmp (&nbp_req->nve.object,
754					&nve_entry->object,
755					NBP_SPL_WILDCARD) != 0)
756					continue;
757			}
758		}
759
760
761		if (!(nbp_req->flags & NBP_WILD_TYPE)) {
762			if ((nbp_req->nve.type_hash) &&
763				(nbp_req->nve.type_hash !=nve_entry->type_hash))
764				continue;
765			else {
766				if (nbp_strcmp (&nbp_req->nve.type,
767					&nve_entry->type,
768					NBP_SPL_WILDCARD) != 0)
769					continue;
770			}
771		}
772
773		/* Found a match! */
774#ifdef NBP_DEBUG
775	{
776		char zone[35],object[35],type[35];
777
778		strlcpy(zone,nbp_req.nve.zone.str, sizeof(zone));
779		strlcpy(object,nbp_req.nve.object.str, sizeof(object));
780		strlcpy(type,nbp_req.nve.type.str, sizeof(type));
781		dPrintf(D_M_NBP_LOW, D_L_USR2,
782			("nbp_search: found  %s:%s@%s  net:%d\n",
783			object, type, zone, (int)nve_entry->address.net));
784	}
785#endif /* NBP_DEBUG */
786		if (nbp_req->func != NULL) {
787			if ((*(nbp_req->func))(nbp_req, nve_entry) != 0) {
788				/* errno expected to be set by func */
789				return (NULL);
790			}
791		} else
792			return (nve_entry);
793	}
794
795	errno = 0;
796	return (NULL);
797} /* nbp_search_nve */
798
799static	int	nbp_lkup_reply (nbp_req, nve_entry)
800register nbp_req_t	*nbp_req;
801register nve_entry_t	*nve_entry;
802{
803	register at_nbptuple_t	*tuple;
804	register int	tuple_size, buf_len;
805	register int	obj_len, type_len;
806	u_char *p;
807
808	/* size of the current tuple we want to write... */
809	tuple_size = nve_entry->object.len + 1 + 	/* object */
810			nve_entry->type.len + 1 + 	/* type */
811			2 + 				/* zone */
812			sizeof (at_inet_t) + 1;		/* addr + enum */
813
814	buf_len = ((nbp_req->flags & NBP_WILD_MASK) ? DDP_DATA_SIZE:tuple_size);
815	if (nbp_req->response == NULL) {
816		if (nbp_setup_resp (nbp_req, buf_len) != 0)
817			/* errno expected to be set by nbp_setup_resp() */
818			return (-1);
819	}
820
821	if ((nbp_req->space_unused < tuple_size) ||
822		(DATA_NBP(nbp_req->response)->tuple_count == NBP_TUPLE_MAX)) {
823		if (nbp_send_resp (nbp_req) != 0)
824			return (-1);
825		if (nbp_setup_resp (nbp_req, buf_len) != 0)
826			return (-1);
827	}
828
829	/* At this point, we have a response buffer that can accommodate the
830	 * tuple we want to write. Write it!
831	 */
832	tuple = (at_nbptuple_t *)gbuf_wptr(nbp_req->response);
833	tuple->enu_addr.net = htons(nve_entry->address.net);
834	tuple->enu_addr.node = nve_entry->address.node;
835	tuple->enu_addr.socket = nve_entry->address.socket;
836	tuple->enu_enum = nve_entry->enumerator;
837
838        /* tuple is in the compressed (no "filler") format */
839	p = (u_char *)&tuple->enu_entity.object;
840	obj_len = nve_entry->object.len + 1;
841	bcopy(&nve_entry->object, p, obj_len);
842	p += obj_len;
843	type_len = nve_entry->type.len + 1;
844	bcopy(&nve_entry->type, p, type_len);
845	p += type_len;
846	p[0] = (u_char)1;
847	p[1] = '*';
848
849	nbp_req->space_unused -= tuple_size;
850	gbuf_winc(nbp_req->response, tuple_size);
851
852	/* increment the tuple count in header by 1 */
853	DATA_NBP(nbp_req->response)->tuple_count++;
854
855	return (0);
856}
857
858
859static	int	nbp_strcmp (str1, str2, embedded_wildcard)
860register at_nvestr_t	*str1, *str2;
861register u_char	embedded_wildcard;	/* If str1 may contain a character
862					 * that's to be treated as an
863					 * embedded wildcard, this character
864					 * is it.  Making this special case
865					 * since for zone names, squiggly
866					 * equal is not to be treated as a
867					 * wildcard.
868					 */
869{
870	u_char	        ch1,ch2;
871	register int	i1, i2;
872	register int	reverse = 0;
873	register int	left_index;
874
875	/* Embedded wildcard, if any, could only be in the first string (str1).
876	 * returns 0 if two strings are equal (modulo case), -1 otherwise
877	 */
878
879	if (str1->len == 0 || str2->len == 0) {
880		return (-1);
881	}
882
883	/* Wildcards are not allowed in str2.
884	 *
885	 * If str1 could potentially contain an embedded wildcard, since the
886	 * embedded wildcard matches ZERO OR MORE characters, str1 can not be
887	 * more than 1 character longer than str2.
888	 *
889	 * If str1 is not supposed to have embedded wildcards, the two strs
890	 * must be of equal length.
891	 */
892	if ((embedded_wildcard && (str2->len < (unsigned) (str1->len-1))) ||
893		(!embedded_wildcard && (str2->len !=  str1->len))) {
894		return (-1);
895	}
896
897	for (i1 = i2 = left_index = 0; (unsigned) i1 < str1->len ;) {
898		ch1 = str1->str[i1];
899		ch2 = str2->str[i2];
900
901		if (embedded_wildcard && (ch1==embedded_wildcard)) {
902			/* hit the embedded wild card... start comparing from
903			 * the other end of the string.
904			 */
905			reverse++;
906			/* But, if embedded wildcard was the last character of
907			 * the string, the two strings match, so return okay.
908			 */
909			if (i1 == str1->len-1) {
910				return (0);
911			}
912
913			i1 = str1->len - 1;
914			i2 = str2->len - 1;
915
916			continue;
917		}
918
919		nbp_upshift(&ch1, 1);
920		nbp_upshift(&ch2, 1);
921
922		if (ch1 != ch2) {
923			return (-1);
924		}
925
926		if (reverse) {
927			i1--; i2--;
928			if (i1 == left_index) {
929				return (0);
930			}
931		} else {
932			i1++; i2++; left_index++;
933		}
934	}
935	return (0);
936}
937
938
939static	void	nbp_setup_hdr (nbp_req)
940register nbp_req_t	*nbp_req;
941{
942	register at_ddp_t	*ddp;
943	register at_nbp_t	*nbp;
944
945	ddp = DATA_DDP(nbp_req->response);
946	nbp = DATA_NBP(nbp_req->response);
947
948	ddp->type = DDP_NBP;
949	UAS_ASSIGN(ddp->checksum, 0);
950	ddp->unused = ddp->hopcount = 0;
951
952	switch(DATA_NBP(nbp_req->request)->control) {
953	case NBP_LKUP :
954		ddp->dst_socket = nbp_req->nve.address.socket;
955		ddp->dst_node = nbp_req->nve.address.node;
956		NET_ASSIGN_NOSWAP(ddp->dst_net, nbp_req->nve.address.net);
957		nbp->control = NBP_LKUP_REPLY;
958		break;
959	}
960	nbp->at_nbp_id = DATA_NBP(nbp_req->request)->at_nbp_id;
961	return;
962}
963
964
965static	int	nbp_setup_resp (nbp_req, tuples_size)
966register nbp_req_t	*nbp_req;
967register int		tuples_size;
968{
969	int	buf_size = tuples_size + DDP_X_HDR_SIZE + NBP_HDR_SIZE;
970	nbp_req->response = gbuf_alloc(AT_WR_OFFSET+buf_size, PRI_MED);
971	if (nbp_req->response == NULL) {
972		errno = ENOBUFS;
973		return(-1);
974	}
975	gbuf_rinc(nbp_req->response, AT_WR_OFFSET);
976	gbuf_wset(nbp_req->response, DDP_X_HDR_SIZE + NBP_HDR_SIZE);
977	nbp_setup_hdr(nbp_req);
978
979	DATA_NBP(nbp_req->response)->tuple_count = 0;
980	nbp_req->space_unused = tuples_size;
981
982	return (0);
983} /* nbp_setup_resp */
984
985
986static	int	nbp_send_resp (nbp_req)
987register nbp_req_t	*nbp_req;
988{
989	int		status;
990
991	status = ddp_output(&nbp_req->response, (at_socket)NBP_SOCKET, FALSE);
992	nbp_req->response = NULL;
993	errno = status;
994	return(errno?-1:0);
995}
996
997void nbp_add_multicast(zone, ifID)
998     at_nvestr_t *zone;
999     at_ifaddr_t *ifID;
1000{
1001	char data[ETHERNET_ADDR_LEN];
1002
1003	if (zone->str[0] == '*')
1004		return;
1005
1006	{
1007	  char str[35];
1008	  strlcpy((char *)str,(char *)zone->str,sizeof(str));
1009	  dPrintf(D_M_NBP_LOW, D_L_USR3,
1010		  ("nbp_add_multi getting mc for %s\n", str));
1011	}
1012	zt_get_zmcast(ifID, zone, data);
1013	if (FDDI_OR_TOKENRING(ifID->aa_ifp->if_type))
1014	  ddp_bit_reverse((unsigned char *)data);
1015	dPrintf(D_M_NBP_LOW,D_L_USR3,
1016		("nbp_add_multi adding  0x%x%x port:%d ifID:0x%x if:%s\n",
1017		 *(unsigned*)data, (*(unsigned *)(data+2))&0x0000ffff,
1018		 /*i*/0, (u_int) ifID, ifID->ifName));
1019
1020	bcopy((caddr_t)data, (caddr_t)&ifID->ZoneMcastAddr, ETHERNET_ADDR_LEN);
1021	(void)at_reg_mcast(ifID, (caddr_t)&ifID->ZoneMcastAddr);
1022}
1023
1024int
1025getNbpTableSize(void)
1026
1027/* for SNMP, returns size in # of entries */
1028{
1029	register nve_entry_t *nve;
1030	register int i=0;
1031
1032	for (nve = TAILQ_FIRST(&name_registry); nve; nve = TAILQ_NEXT(nve, nve_link), i++)
1033		i++;
1034	return(i);
1035}
1036
1037int
1038getNbpTable(p, s, c)
1039     snmpNbpEntry_t	*p;
1040     int 		s;		/* starting entry */
1041     int		c;		/* # entries to copy */
1042
1043/* for SNMP, returns section of nbp table */
1044{
1045	register nve_entry_t *nve;
1046	register int i=0;
1047	static   int nextNo=0;		/* entry that *next points to */
1048	static	 nve_entry_t  *next = (nve_entry_t*)NULL;
1049
1050	if (s && next && nextNo == s) {
1051		nve = next;
1052		i = nextNo;
1053	}
1054	else
1055		nve = TAILQ_FIRST(&name_registry);
1056
1057	for ( ; nve && c ; nve = TAILQ_NEXT(nve, nve_link), p++,i++) {
1058		if (i>= s) {
1059			p->nbpe_object = nve->object;
1060			p->nbpe_type   = nve->type;
1061			c--;
1062		}
1063	}
1064	if (nve) {
1065		next = nve;
1066		nextNo = i;
1067	} else {
1068		next = (nve_entry_t*)NULL;
1069		nextNo = 0;
1070	}
1071
1072	return 0;
1073}
1074
1075
1076#define ZONES_PER_BLK		31	/* 31 fits within a 1k blk) */
1077#define ZONE_BLK_SIZE		ZONES_PER_BLK * sizeof(at_nvestr_t)
1078
1079int setLocalZones(newzones, size)
1080     at_nvestr_t *newzones;
1081     int size;
1082/* updates list of zones which are local to all active ports
1083   missing zones are not deleted, only missing zones are added.
1084*/
1085{
1086	int	bytesread=0;		/* #bytes read from tuple */
1087	int i=0, dupe;
1088	gbuf_t	*m;
1089	at_nvestr_t		*pnve, *pnew = newzones;
1090
1091	if (!lzones) {
1092		if(!(lzones = gbuf_alloc(ZONE_BLK_SIZE, PRI_MED)))
1093			return(ENOBUFS);
1094		gbuf_wset(lzones,0);
1095	}
1096	while (bytesread < size) {		/* for each new zone */
1097		{
1098			char str[35];
1099			strlcpy((char *)str,(char *)pnew->str,sizeof(str));
1100		}
1101		m = lzones;
1102		pnve = (at_nvestr_t*)gbuf_rptr(m);
1103		dupe = 0;
1104		for (i=0; i<lzonecnt && !dupe; i++,pnve++)  {
1105			if (i && !(i%ZONES_PER_BLK)) {
1106				if (gbuf_cont(m)) {
1107					m = gbuf_cont(m);
1108					pnve = (at_nvestr_t*)gbuf_rptr(m);
1109				}
1110				else
1111					break;
1112			}
1113			if (pnew->len != pnve->len)
1114				continue;
1115			if (pnew->len > NBP_NVE_STR_SIZE) {
1116				return(0);
1117			}
1118			if (!strncmp((char *)pnew->str, (char *)pnve->str, pnew->len)) {
1119				dupe=1;
1120				continue;
1121			}
1122		}
1123		if (!dupe) {
1124			/* add new zone */
1125			if (lzonecnt && !(lzonecnt%ZONES_PER_BLK)) {
1126				if(!(gbuf_cont(m) = gbuf_alloc(ZONE_BLK_SIZE, PRI_MED)))
1127					return(ENOBUFS);
1128				gbuf_wset(gbuf_cont(m),0);
1129				pnve = (at_nvestr_t*)gbuf_rptr(gbuf_cont(m));
1130			}
1131			strlcpy((char *)pnve->str,(char *)pnew->str,sizeof(pnve->str));
1132			pnve->len = pnew->len;
1133			lzonecnt++;
1134		}
1135		bytesread += (pnew->len+1);
1136		pnew = (at_nvestr_t*) (((char *)pnew) + pnew->len + 1);
1137	}
1138	/* showLocalZones1(); */
1139	return(0);
1140}
1141
1142/**********
1143showLocalZones1()
1144{
1145	int i;
1146	at_nvestr_t *pnve;
1147	gbuf_t	*m;
1148	char str[35];
1149
1150	for (i=0;  ; i++) {
1151		if (!(pnve = getLocalZone(i))) {
1152			break;
1153		}
1154		strlcpy(str,pnve->str,sizeof(str));
1155	}
1156}
1157
1158*********/
1159
1160int
1161isZoneLocal(zone)
1162at_nvestr_t *zone;
1163{
1164	at_nvestr_t *pnve;
1165	int i;
1166	if (DEFAULT_ZONE(zone))
1167		return(1);
1168	for (i=0;  ; i++) {
1169		if (!(pnve = getLocalZone(i)))
1170			break;
1171		if (!nbp_strcmp(pnve,zone,0))
1172			return(1);
1173	}
1174	return(0);
1175}
1176
1177
1178#define NULL_PNVESTR (at_nvestr_t *) 0
1179
1180at_nvestr_t *getLocalZone(zno)
1181	int zno;			/* zone number in virtual list to
1182					   return, 0 for first zone */
1183/* returns pointer to a new local zone number zno,
1184   returns null when no zones left.
1185*/
1186{
1187	zone_usage_t ifz;
1188	ifz.zone_index = zno;
1189	if (MULTIPORT_MODE)
1190		return(getRTRLocalZone(&ifz));
1191	else
1192		return(getSPLocalZone(zno));
1193}
1194
1195
1196at_nvestr_t *getSPLocalZone(zno)
1197	int zno;			/* zone number in virtual list to
1198						   return, 0 for first zone */
1199/* single port mode version */
1200{
1201	int curz=0;		/* current zone */
1202	gbuf_t *m;
1203	at_nvestr_t *pnve;
1204
1205	if (lzones) {
1206		m = lzones;
1207		pnve = (at_nvestr_t*)gbuf_rptr(m);
1208	}
1209	else
1210		return(NULL_PNVESTR);
1211	if ( zno>=lzonecnt )
1212		return(NULL_PNVESTR);
1213	for (curz=0; curz<zno; curz++,pnve++ ) {
1214		if ( curz<lzonecnt ) {
1215			if (curz && !(curz%ZONES_PER_BLK) ) {
1216				if (gbuf_cont(m)) {
1217					m = gbuf_cont(m);
1218					pnve = (at_nvestr_t*)gbuf_rptr(m);
1219				}
1220				else {
1221					return(NULL_PNVESTR);
1222				}
1223			}
1224			if (pnve->len > NBP_NVE_STR_SIZE) {
1225				return(NULL_PNVESTR);
1226			}
1227		}
1228		else
1229			return(NULL_PNVESTR);
1230	}
1231	return(pnve);
1232}
1233
1234/* The following functions are used in name registration and removal */
1235
1236int nbp_fillin_nve(entity, nve)
1237     at_entity_t   	*entity;
1238     nve_entry_t     	*nve;
1239{
1240	register int i;
1241
1242	if (entity->object.len > NBP_NVE_STR_SIZE ||
1243	    entity->type.len > NBP_NVE_STR_SIZE ||
1244	    entity->zone.len > NBP_NVE_STR_SIZE) {
1245		dPrintf(D_M_NBP_LOW, D_L_WARNING,
1246			("nbp_fillin_nve: bad str len\n"));
1247		errno = EINVAL;
1248		return (-1);
1249	}
1250
1251	nve->zone = entity->zone;
1252	nve->zone_hash = 0;
1253	if (!isZoneLocal(&entity->zone)) {
1254		errno = EINVAL;
1255		return(-1);
1256	}
1257	/* if there's no zone, '*' gets filled in when entry is created */
1258	if (!DEFAULT_ZONE(&entity->zone))
1259		nve->zone_hash = nbp_strhash(&nve->zone);
1260
1261	nve->object = entity->object;
1262	nve->object_hash = 0;
1263	if (entity->object.len == 1 &&
1264	    (entity->object.str[0] == NBP_ORD_WILDCARD ||
1265	     entity->object.str[0] == NBP_SPL_WILDCARD)) {
1266		dPrintf(D_M_NBP_LOW, D_L_WARNING,
1267			("nbp_fillin_nve: wildcard\n"));
1268		errno = EINVAL;
1269		return (-1);
1270	}
1271	for (i = 0; i < entity->object.len; i++) {
1272		if (entity->object.str[i] == NBP_SPL_WILDCARD) {
1273		dPrintf(D_M_NBP_LOW, D_L_WARNING,
1274			("nbp_fillin_nve: wildcard2\n"));
1275			errno = EINVAL;
1276			return (-1);
1277		}
1278	}
1279	nve->object_hash = nbp_strhash(&nve->object);
1280
1281	nve->type = entity->type;
1282	nve->type_hash = 0;
1283	if (entity->type.len == 1 &&
1284	    (entity->type.str[0] == NBP_ORD_WILDCARD ||
1285	     entity->type.str[0] == NBP_SPL_WILDCARD)) {
1286		errno = EINVAL;
1287		return (-1);
1288	}
1289	for (i = 0; i < entity->type.len; i++) {
1290		if (entity->type.str[i] == NBP_SPL_WILDCARD) {
1291		dPrintf(D_M_NBP_LOW, D_L_WARNING,
1292			("nbp_fillin_nve: wildcard3\n"));
1293			errno = EINVAL;
1294			return (-1);
1295		}
1296	}
1297	nve->type_hash = nbp_strhash(&nve->type);
1298
1299	return(0);
1300} /* nbp_fillin_nve */
1301
1302nve_entry_t *nbp_find_nve(nve)
1303     nve_entry_t *nve;
1304{
1305	register nve_entry_t	*nve_entry;
1306
1307	TAILQ_FOREACH(nve_entry, &name_registry, nve_link) {
1308		if (nve->zone_hash &&
1309		    ((nve->zone_hash != nve_entry->zone_hash) &&
1310		     (nve->zone_hash != hzonehash))) {
1311			dPrintf(D_M_NBP_LOW,D_L_USR4,
1312				("nbp_find_nve: no match for zone, req hash:%x\n",
1313				 nve->zone_hash));
1314			continue;
1315		}
1316
1317		if ((nve->object_hash) &&
1318		    (nve->object_hash != nve_entry->object_hash))
1319			continue;
1320
1321		if ((nve->type_hash) &&
1322		    (nve->type_hash != nve_entry->type_hash))
1323			continue;
1324
1325		/* Found a match! */
1326		return (nve_entry);
1327	}
1328
1329	return (NULL);
1330} /* nbp_find_nve */
1331
1332static int nbp_enum_gen (nve_entry)
1333     register nve_entry_t	*nve_entry;
1334{
1335	register int		new_enum = 0;
1336	register nve_entry_t	*ne;
1337
1338re_do:
1339	TAILQ_FOREACH(ne, &name_registry, nve_link) {
1340		if ((*(int *)&ne->address == *(int *)&nve_entry->address) &&
1341			(ne->enumerator == new_enum)) {
1342			if (new_enum == 255)
1343				return(EADDRNOTAVAIL);
1344			else {
1345				new_enum++;
1346				goto re_do;
1347			}
1348		}
1349	}
1350
1351	nve_entry->enumerator = new_enum;
1352	return (0);
1353}
1354
1355int nbp_new_nve_entry(nve_entry, ifID)
1356     nve_entry_t *nve_entry;
1357     at_ifaddr_t *ifID;
1358{
1359	gbuf_t		*tag;
1360	nve_entry_t	*new_entry;
1361	at_nvestr_t 	*zone;
1362	int error;
1363
1364	if (!(valid_at_addr((at_inet_t *)&nve_entry->address))) {
1365		dPrintf(D_M_NBP_LOW, D_L_WARNING,
1366			("nbp_new_nve_entry: valid_at_addr\n"));
1367		return(EINVAL);
1368	}
1369	if ((error = nbp_enum_gen(nve_entry)))
1370		return(error);
1371
1372	nve_entry->unique_nbp_id = ++nbp_id_count;
1373
1374	/* Got an nve entry on hand.... allocate a buffer, copy the entry
1375	 * on to it and stick it in the registry.
1376	 */
1377	if ((tag = gbuf_alloc(sizeof(nve_entry_t), PRI_HI)) == NULL){
1378		return(ENOBUFS);
1379	}
1380	gbuf_wset(tag, sizeof(nve_entry_t));
1381	new_entry = (nve_entry_t *)gbuf_rptr(tag);
1382	bcopy(nve_entry, new_entry, sizeof(nve_entry_t));
1383
1384	if (DEFAULT_ZONE(&nve_entry->zone)) {
1385	   /* put actual zone name in entry instead of "*" */
1386	   /* if single port mode and no zone name, then a router
1387	      is down, so use pram zone name hint from elap cfg */
1388		if (!MULTIPORT_MODE && ifID_home->ifZoneName.str[0] == '*') {
1389			zone = &ifID_home->startup_zone;
1390		} else {
1391			zone = &ifID_home->ifZoneName;
1392		}
1393		new_entry->zone = *zone;
1394		if ( new_entry->zone.len == 0 ) {
1395			new_entry->zone.str[0] = '*';
1396			new_entry->zone.len = 1;
1397		}
1398		new_entry->zone_hash = nbp_strhash(&new_entry->zone);
1399	}
1400	new_entry->tag = tag;
1401	new_entry->pid =  proc_selfpid();
1402
1403	TAILQ_INSERT_TAIL(&name_registry, new_entry, nve_link);
1404	at_state.flags |= AT_ST_NBP_CHANGED;
1405
1406#ifdef NBP_DEBUG
1407	{
1408		char zone[35],object[35],type[35];
1409		strlcpy(zone,nbp_req.nve.zone.str, sizeof(zone));
1410		strlcpy(object,nbp_req.nve.object.str, sizeof(object));
1411		strlcpy(type,nbp_req.nve.type.str, sizeof(type));
1412		dPrintf(D_M_NBP_LOW, D_L_USR4,
1413			("nbp_insert: adding %s:%s@%s addr:%d.%d ",
1414			 object, type, zone,
1415			 new_entry->address.net, new_entry->address.node));
1416	}
1417#endif /* NBP_DEBUG */
1418
1419	nbp_add_multicast(&new_entry->zone, ifID);
1420	return (0);
1421} /* nbp_new_nve_entry */
1422
1423void nbp_delete_entry (nve_entry)
1424     nve_entry_t	*nve_entry;
1425{
1426	TAILQ_REMOVE(&name_registry, nve_entry, nve_link);
1427	gbuf_freem(nve_entry->tag);
1428	at_state.flags |= AT_ST_NBP_CHANGED;
1429}
1430
1431/* Registration of an NBP entity in multihoming mode, from AIOCNBPREG
1432   in at.c */
1433int nbp_mh_reg(nbpP)
1434     at_nbp_reg_t *nbpP;
1435{
1436	nve_entry_t nve;
1437	at_ifaddr_t *ifID = 0;
1438	int registered = 0;
1439	int finished = FALSE;
1440
1441	if (nbp_fillin_nve(&nbpP->name, &nve) != 0) {
1442		/* bad tuple... */
1443		dPrintf(D_M_NBP_LOW, D_L_WARNING,
1444			("nbp_mh_reg: bad tuple\n"));
1445		return(EINVAL);
1446	}
1447	nve.address = nbpP->addr;
1448	nve.ddptype = nbpP->ddptype;
1449
1450	if (DEFAULT_ZONE(&nbpP->name.zone)) {
1451	  /* multihoming mode with the default zone specified */
1452
1453		/* now find the matching interfaces */
1454		TAILQ_FOREACH(ifID, &at_ifQueueHd, aa_link) {
1455			if (nbpP->addr.net || nbpP->addr.node) {
1456				/* if address is specified */
1457				if ((nbpP->addr.net != ifID->ifThisNode.s_net ||
1458				     nbpP->addr.node != ifID->ifThisNode.s_node))
1459					continue;
1460				else
1461				  	/* the address was specified, and
1462					   we found the matching interface */
1463				  	finished = TRUE;
1464			} else {
1465				/* address is not specified, so fill in
1466				   the address for the interface */
1467				nve.address.net = ifID->ifThisNode.s_net;
1468				nve.address.node = ifID->ifThisNode.s_node;
1469			}
1470			nve.zone = ifID->ifZoneName;
1471			nve.zone_hash = nbp_strhash(&nve.zone);
1472			if (nbp_find_nve(&nve))
1473				continue;
1474			if (nbp_new_nve_entry(&nve, ifID) == 0)
1475				registered++;
1476		}
1477		if (registered && !nbpP->addr.net && !nbpP->addr.node) {
1478			nbpP->addr.net = ifID_home->ifThisNode.s_net;
1479			nbpP->addr.node = ifID_home->ifThisNode.s_node;
1480		}
1481	} else {
1482	        /* multihoming mode with a specific zone specified */
1483	        /* see which segments (interfaces) are seeded for this zone */
1484		int zno;
1485		at_ifnames_t ifs_in_zone;
1486		if (!(zno = zt_find_zname(&nve.zone))) {
1487			dPrintf(D_M_NBP_LOW, D_L_WARNING,
1488				("nbp_mh_reg: didn't find zone name\n"));
1489			return(EINVAL);
1490		}
1491		getIfUsage(zno-1, &ifs_in_zone);
1492
1493		/* now find the matching interfaces */
1494		TAILQ_FOREACH(ifID, &at_ifQueueHd, aa_link) {
1495			if (!ifs_in_zone.at_if[ifID->ifPort])
1496					/* zone doesn't match */
1497				continue;
1498			else
1499				/* the zone matches, so unless the
1500				   address is specified and doesn't
1501				   match, we only need to do this once */
1502				finished = TRUE;
1503
1504			if (nbpP->addr.net || nbpP->addr.node) {
1505			    /* address is specified */
1506			    finished = FALSE;
1507			    if ((nbpP->addr.net != ifID->ifThisNode.s_net ||
1508				 nbpP->addr.node != ifID->ifThisNode.s_node))
1509				continue;
1510			    else
1511			  	/* the address was specified, and
1512				   we found the matching interface */
1513			  	finished = TRUE;
1514			} else {
1515				/* address is not specified, so fill in
1516				   the address for the interface */
1517				nve.address.net = ifID->ifThisNode.s_net;
1518				nve.address.node = ifID->ifThisNode.s_node;
1519			}
1520			if (nbp_find_nve(&nve))
1521				continue;
1522			if (nbp_new_nve_entry(&nve, ifID) == 0)
1523				registered++;
1524			if (registered && !nbpP->addr.net && !nbpP->addr.node) {
1525				nbpP->addr.net = ifID->ifThisNode.s_net;
1526				nbpP->addr.node = ifID->ifThisNode.s_node;
1527			}
1528
1529		}
1530	}
1531	nbpP->unique_nbp_id = (registered > 1)? 0: nve.unique_nbp_id;
1532
1533	if (registered)
1534		return(0);
1535	else
1536		return(EADDRNOTAVAIL);
1537
1538} /* nbp_mh_reg */
1539