ng_device.c revision 139823
1193323Sed/*-
2193323Sed * Copyright (c) 2002 Mark Santcroos <marks@ripe.net>
3193323Sed * Copyright (c) 2004 Gleb Smirnoff <glebius@FreeBSD.org>
4193323Sed *
5193323Sed * Redistribution and use in source and binary forms, with or without
6193323Sed * modification, are permitted provided that the following conditions
7193323Sed * are met:
8193323Sed * 1. Redistributions of source code must retain the above copyright
9193323Sed *    notice, this list of conditions and the following disclaimer.
10193323Sed * 2. Redistributions in binary form must reproduce the above copyright
11193323Sed *    notice, this list of conditions and the following disclaimer in the
12193323Sed *    documentation and/or other materials provided with the distribution.
13193323Sed *
14193323Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15193323Sed * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16193323Sed * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17193323Sed * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18193323Sed * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19193323Sed * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20198090Srdivacky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21198892Srdivacky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22193323Sed * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23193323Sed * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24193323Sed *
25193323Sed * Netgraph "device" node
26193323Sed *
27193323Sed * This node presents a /dev/ngd%d device that interfaces to an other
28193323Sed * netgraph node.
29193323Sed *
30193323Sed * $FreeBSD: head/sys/netgraph/ng_device.c 139823 2005-01-07 01:45:51Z imp $
31193323Sed *
32198090Srdivacky */
33198090Srdivacky
34193323Sed#if 0
35199481Srdivacky#define	DBG do { printf("ng_device: %s\n", __func__ ); } while (0)
36193323Sed#else
37193323Sed#define	DBG do {} while (0)
38193323Sed#endif
39193323Sed
40193323Sed#include <sys/param.h>
41193323Sed#include <sys/conf.h>
42193323Sed#include <sys/ioccom.h>
43193323Sed#include <sys/kernel.h>
44199511Srdivacky#include <sys/malloc.h>
45199481Srdivacky#include <sys/mbuf.h>
46193323Sed#include <sys/poll.h>
47199511Srdivacky#include <sys/queue.h>
48193323Sed#include <sys/socket.h>
49193323Sed#include <sys/systm.h>
50199481Srdivacky#include <sys/uio.h>
51193323Sed#include <sys/vnode.h>
52193323Sed
53193323Sed#include <net/if.h>
54199481Srdivacky#include <net/if_var.h>
55199481Srdivacky#include <netinet/in.h>
56199481Srdivacky#include <netinet/in_systm.h>
57199481Srdivacky#include <netinet/ip.h>
58199481Srdivacky
59199481Srdivacky#include <netgraph/ng_message.h>
60193323Sed#include <netgraph/netgraph.h>
61199481Srdivacky#include <netgraph/ng_device.h>
62199481Srdivacky
63199481Srdivacky#define	ERROUT(x) do { error = (x); goto done; } while (0)
64199481Srdivacky
65199481Srdivacky/* Netgraph methods */
66199481Srdivackystatic ng_constructor_t	ng_device_constructor;
67199481Srdivackystatic ng_rcvmsg_t	ng_device_rcvmsg;
68199481Srdivackystatic ng_shutdown_t	ng_device_shutdown;
69199481Srdivackystatic ng_newhook_t	ng_device_newhook;
70199481Srdivackystatic ng_rcvdata_t	ng_device_rcvdata;
71199481Srdivackystatic ng_disconnect_t	ng_device_disconnect;
72199481Srdivacky
73199481Srdivacky/* Netgraph type */
74199481Srdivackystatic struct ng_type ngd_typestruct = {
75198090Srdivacky	.version =	NG_ABI_VERSION,
76193323Sed	.name =		NG_DEVICE_NODE_TYPE,
77199481Srdivacky	.constructor =	ng_device_constructor,
78198090Srdivacky	.rcvmsg	=	ng_device_rcvmsg,
79198090Srdivacky	.shutdown = 	ng_device_shutdown,
80193323Sed	.newhook =	ng_device_newhook,
81198090Srdivacky	.rcvdata =	ng_device_rcvdata,
82198090Srdivacky	.disconnect =	ng_device_disconnect,
83198090Srdivacky};
84198090SrdivackyNETGRAPH_INIT(device, &ngd_typestruct);
85198090Srdivacky
86198090Srdivacky/* per node data */
87193323Sedstruct ngd_private {
88198090Srdivacky	struct	ifqueue	readq;
89198090Srdivacky	SLIST_ENTRY(ngd_private) links;
90198090Srdivacky	struct	ng_node	*node;
91198090Srdivacky	struct	ng_hook	*hook;
92198090Srdivacky	struct	cdev	*ngddev;
93198090Srdivacky	struct	mtx	ngd_mtx;
94198090Srdivacky	int 		unit;
95193323Sed	uint16_t	flags;
96193323Sed#define	NGDF_OPEN	0x0001
97193323Sed#define	NGDF_RWAIT	0x0002
98193323Sed};
99193323Sedtypedef struct ngd_private *priv_p;
100193323Sed
101201360Srdivacky/* List of all active nodes and mutex to protect it */
102199481Srdivackystatic SLIST_HEAD(, ngd_private) ngd_nodes = SLIST_HEAD_INITIALIZER(ngd_nodes);
103193323Sedstatic struct mtx	ng_device_mtx;
104193323SedMTX_SYSINIT(ng_device, &ng_device_mtx, "ng_device", MTX_DEF);
105193323Sed
106193323Sed/* Maximum number of NGD devices */
107199481Srdivacky#define MAX_NGD	25	/* should be more than enough for now */
108193323Sed
109193323Sedstatic d_close_t ngdclose;
110193323Sedstatic d_open_t ngdopen;
111193323Sedstatic d_read_t ngdread;
112193323Sedstatic d_write_t ngdwrite;
113193323Sed#if 0
114193323Sedstatic d_ioctl_t ngdioctl;
115193323Sed#endif
116193323Sedstatic d_poll_t ngdpoll;
117193323Sed
118199481Srdivackystatic struct cdevsw ngd_cdevsw = {
119193323Sed	.d_version =	D_VERSION,
120193323Sed	.d_open =	ngdopen,
121193323Sed	.d_close =	ngdclose,
122193323Sed	.d_read =	ngdread,
123193323Sed	.d_write =	ngdwrite,
124193323Sed#if 0
125193323Sed	.d_ioctl =	ngdioctl,
126193323Sed#endif
127193323Sed	.d_poll =	ngdpoll,
128193323Sed	.d_name =	NG_DEVICE_DEVNAME,
129193323Sed};
130193323Sed
131193323Sed/* Helper functions */
132193323Sedstatic int get_free_unit(void);
133193323Sed
134193323Sed/******************************************************************************
135193323Sed *  Netgraph methods
136203954Srdivacky ******************************************************************************/
137193323Sed
138193323Sed/*
139193323Sed * create new node
140198090Srdivacky */
141193323Sedstatic int
142193323Sedng_device_constructor(node_p node)
143193323Sed{
144193323Sed	priv_p	priv;
145193323Sed
146193323Sed	DBG;
147193323Sed
148193323Sed	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
149193323Sed	if (priv == NULL)
150193323Sed		return (ENOMEM);
151193323Sed
152193323Sed	mtx_init(&priv->ngd_mtx, "ng_device", NULL, MTX_DEF);
153193323Sed	mtx_lock(&priv->ngd_mtx);
154193323Sed
155193323Sed	mtx_lock(&ng_device_mtx);
156193323Sed
157193323Sed	priv->unit = get_free_unit();
158193323Sed	if(priv->unit < 0) {
159193323Sed		printf("%s: No free unit found by get_free_unit(), "
160193323Sed				"increase MAX_NGD\n",__func__);
161193323Sed		mtx_unlock(&ng_device_mtx);
162193323Sed		mtx_destroy(&priv->ngd_mtx);
163193323Sed		FREE(priv, M_NETGRAPH);
164193323Sed		return(EINVAL);
165193323Sed	}
166193323Sed
167193323Sed	priv->ngddev = make_dev(&ngd_cdevsw, unit2minor(priv->unit), UID_ROOT,
168193323Sed	    GID_WHEEL, 0600, NG_DEVICE_DEVNAME "%d", priv->unit);
169193323Sed	if(priv->ngddev == NULL) {
170193323Sed		printf("%s(): make_dev() failed\n",__func__);
171193323Sed		mtx_unlock(&ng_device_mtx);
172193323Sed		mtx_destroy(&priv->ngd_mtx);
173193323Sed		FREE(priv, M_NETGRAPH);
174198090Srdivacky		return(EINVAL);
175198090Srdivacky	}
176198090Srdivacky
177198090Srdivacky	SLIST_INSERT_HEAD(&ngd_nodes, priv, links);
178198090Srdivacky
179198090Srdivacky	mtx_unlock(&ng_device_mtx);
180198090Srdivacky
181193323Sed	mtx_init(&priv->readq.ifq_mtx, "ng_device queue", NULL, MTX_DEF);
182198090Srdivacky	IFQ_SET_MAXLEN(&priv->readq, ifqmaxlen);
183198090Srdivacky
184198090Srdivacky	/* Link everything together */
185193323Sed	NG_NODE_SET_PRIVATE(node, priv);
186198090Srdivacky	priv->node = node;
187198090Srdivacky	priv->ngddev->si_drv1 = priv;
188193323Sed
189198090Srdivacky	mtx_unlock(&priv->ngd_mtx);
190193323Sed
191193323Sed	return(0);
192193323Sed}
193193323Sed
194198090Srdivacky/*
195198090Srdivacky * Process control message.
196193323Sed */
197193323Sed
198193323Sedstatic int
199193323Sedng_device_rcvmsg(node_p node, item_p item, hook_p lasthook)
200193323Sed{
201193323Sed	const priv_p priv = NG_NODE_PRIVATE(node);
202193323Sed	struct ng_mesg *msg;
203198090Srdivacky	struct ng_mesg *resp = NULL;
204193323Sed	int error = 0;
205193323Sed
206193323Sed	NGI_GET_MSG(item, msg);
207193323Sed
208193323Sed	if (msg->header.typecookie == NGM_DEVICE_COOKIE) {
209203954Srdivacky		switch (msg->header.cmd) {
210203954Srdivacky		case NGM_DEVICE_GET_DEVNAME:
211203954Srdivacky			/* XXX: Fix when NGD_MAX us bigger */
212203954Srdivacky			NG_MKRESPONSE(resp, msg,
213203954Srdivacky			    strlen(NG_DEVICE_DEVNAME) + 3, M_NOWAIT);
214193323Sed
215203954Srdivacky			if (resp == NULL)
216203954Srdivacky				ERROUT(ENOMEM);
217203954Srdivacky
218199481Srdivacky			strlcpy((char *)resp->data, priv->ngddev->si_name,
219203954Srdivacky			    strlen(priv->ngddev->si_name) + 1);
220203954Srdivacky			break;
221193323Sed
222203954Srdivacky		default:
223203954Srdivacky			error = EINVAL;
224203954Srdivacky			break;
225203954Srdivacky		}
226203954Srdivacky	} else
227203954Srdivacky		error = EINVAL;
228203954Srdivacky
229203954Srdivackydone:
230199481Srdivacky	NG_RESPOND_MSG(error, node, item, resp);
231203954Srdivacky	NG_FREE_MSG(msg);
232203954Srdivacky	return (error);
233203954Srdivacky}
234203954Srdivacky
235203954Srdivacky/*
236203954Srdivacky * Accept incoming hook. We support only one hook per node.
237203954Srdivacky */
238203954Srdivackystatic int
239203954Srdivackyng_device_newhook(node_p node, hook_p hook, const char *name)
240203954Srdivacky{
241203954Srdivacky	priv_p priv = NG_NODE_PRIVATE(node);
242203954Srdivacky
243203954Srdivacky	DBG;
244193323Sed
245203954Srdivacky	/* We have only one hook per node */
246203954Srdivacky	if (priv->hook != NULL)
247193323Sed		return (EISCONN);
248193323Sed
249198090Srdivacky	priv->hook = hook;
250203954Srdivacky
251203954Srdivacky	return(0);
252203954Srdivacky}
253203954Srdivacky
254203954Srdivacky/*
255203954Srdivacky * Receive data from hook, write it to device.
256203954Srdivacky */
257203954Srdivackystatic int
258203954Srdivackyng_device_rcvdata(hook_p hook, item_p item)
259193323Sed{
260198090Srdivacky	priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
261193323Sed	struct mbuf *m;
262193323Sed
263193323Sed	DBG;
264193323Sed
265193323Sed	NGI_GET_M(item, m);
266193323Sed	NG_FREE_ITEM(item);
267193323Sed
268193323Sed	IF_LOCK(&priv->readq);
269193323Sed	if (_IF_QFULL(&priv->readq)) {
270193323Sed		_IF_DROP(&priv->readq);
271193323Sed		IF_UNLOCK(&priv->readq);
272199481Srdivacky		NG_FREE_M(m);
273193323Sed		return (ENOBUFS);
274193323Sed	}
275193323Sed
276193323Sed	_IF_ENQUEUE(&priv->readq, m);
277193323Sed	IF_UNLOCK(&priv->readq);
278193323Sed	mtx_lock(&priv->ngd_mtx);
279193323Sed	if (priv->flags & NGDF_RWAIT) {
280193323Sed		priv->flags &= ~NGDF_RWAIT;
281193323Sed		wakeup(priv);
282193323Sed	}
283193323Sed	mtx_unlock(&priv->ngd_mtx);
284193323Sed
285193323Sed	return(0);
286193323Sed}
287193323Sed
288193323Sed/*
289193323Sed * Removal of the hook destroys the node.
290193323Sed */
291193323Sedstatic int
292193323Sedng_device_disconnect(hook_p hook)
293193323Sed{
294199481Srdivacky	priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
295193323Sed
296193323Sed	DBG;
297193323Sed
298193323Sed	destroy_dev(priv->ngddev);
299193323Sed	mtx_destroy(&priv->ngd_mtx);
300193323Sed
301199481Srdivacky	mtx_lock(&ng_device_mtx);
302199481Srdivacky	SLIST_REMOVE(&ngd_nodes, priv, ngd_private, links);
303193323Sed	mtx_unlock(&ng_device_mtx);
304193323Sed
305193323Sed	IF_DRAIN(&priv->readq);
306193323Sed	mtx_destroy(&(priv)->readq.ifq_mtx);
307193323Sed
308193323Sed	FREE(priv, M_NETGRAPH);
309193323Sed
310193323Sed	ng_rmnode_self(NG_HOOK_NODE(hook));
311199481Srdivacky
312193323Sed	return(0);
313193323Sed}
314199481Srdivacky
315193323Sed/*
316193323Sed * Node shutdown. Everything is already done in disconnect method.
317199481Srdivacky */
318193323Sedstatic int
319193323Sedng_device_shutdown(node_p node)
320193323Sed{
321193323Sed	NG_NODE_UNREF(node);
322193323Sed	return (0);
323193323Sed}
324193323Sed
325193323Sed/******************************************************************************
326193323Sed *  Device methods
327193323Sed ******************************************************************************/
328193323Sed
329193323Sed/*
330193323Sed * the device is opened
331193323Sed */
332193323Sedstatic int
333199481Srdivackyngdopen(struct cdev *dev, int flag, int mode, struct thread *td)
334193323Sed{
335193323Sed	priv_p	priv = (priv_p )dev->si_drv1;
336193323Sed
337204792Srdivacky	DBG;
338204792Srdivacky
339204792Srdivacky	mtx_lock(&priv->ngd_mtx);
340193323Sed	priv->flags |= NGDF_OPEN;
341193323Sed	mtx_unlock(&priv->ngd_mtx);
342193323Sed
343193323Sed	return(0);
344193323Sed}
345203954Srdivacky
346193323Sed/*
347193323Sed * the device is closed
348193323Sed */
349193323Sedstatic int
350193323Sedngdclose(struct cdev *dev, int flag, int mode, struct thread *td)
351193323Sed{
352193323Sed	priv_p	priv = (priv_p )dev->si_drv1;
353193323Sed
354193323Sed	DBG;
355193323Sed	mtx_lock(&priv->ngd_mtx);
356193323Sed	priv->flags &= ~NGDF_OPEN;
357193323Sed	mtx_unlock(&priv->ngd_mtx);
358193323Sed
359193323Sed	return(0);
360199481Srdivacky}
361193323Sed
362193323Sed#if 0	/*
363193323Sed	 * The ioctl is transformed into netgraph control message.
364199481Srdivacky	 * We do not process them, yet.
365193323Sed	 */
366193323Sed/*
367193323Sed * process ioctl
368193323Sed *
369193323Sed * they are translated into netgraph messages and passed on
370193323Sed *
371193323Sed */
372193323Sedstatic int
373193323Sedngdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
374193323Sed{
375193323Sed	struct ngd_softc *sc = &ngd_softc;
376193323Sed	struct ngd_connection * connection = NULL;
377193323Sed	struct ngd_connection * tmp;
378193323Sed	int error = 0;
379193323Sed	struct ng_mesg *msg;
380193323Sed	struct ngd_param_s * datap;
381193323Sed
382193323Sed	DBG;
383193323Sed
384193323Sed	SLIST_FOREACH(tmp,&sc->head,links) {
385193323Sed		if(tmp->ngddev == dev) {
386193323Sed			connection = tmp;
387193323Sed		}
388193323Sed	}
389199481Srdivacky	if(connection == NULL) {
390193323Sed		printf("%s(): connection is still NULL, no dev found\n",__func__);
391193323Sed		return(-1);
392199481Srdivacky	}
393193323Sed
394193323Sed	NG_MKMESSAGE(msg, NGM_DEVICE_COOKIE, cmd, sizeof(struct ngd_param_s),
395193323Sed			M_NOWAIT);
396193323Sed	if (msg == NULL) {
397193323Sed		printf("%s(): msg == NULL\n",__func__);
398193323Sed		goto nomsg;
399193323Sed	}
400193323Sed
401198090Srdivacky	/* pass the ioctl data into the ->data area */
402193323Sed	datap = (struct ngd_param_s *)msg->data;
403199481Srdivacky	datap->p = addr;
404193323Sed
405193323Sed	NG_SEND_MSG_HOOK(error, sc->node, msg, connection->active_hook, 0);
406193323Sed	if(error)
407193323Sed		printf("%s(): NG_SEND_MSG_HOOK error: %d\n",__func__,error);
408193323Sed
409193323Sednomsg:
410193323Sed
411193323Sed	return(0);
412193323Sed}
413193323Sed#endif /* if 0 */
414193323Sed
415193323Sed/*
416193323Sed * This function is called when a read(2) is done to our device.
417204792Srdivacky * We process one mbuf from queue.
418204792Srdivacky */
419193323Sedstatic int
420193323Sedngdread(struct cdev *dev, struct uio *uio, int flag)
421193323Sed{
422193323Sed	priv_p	priv = (priv_p )dev->si_drv1;
423193323Sed	struct mbuf *m;
424193323Sed	int len, error = 0;
425193323Sed
426193323Sed	DBG;
427193323Sed
428193323Sed	/* get an mbuf */
429193323Sed	do {
430193323Sed		IF_DEQUEUE(&priv->readq, m);
431193323Sed		if (m == NULL) {
432193323Sed			if (flag & IO_NDELAY)
433193323Sed				return (EWOULDBLOCK);
434199481Srdivacky			mtx_lock(&priv->ngd_mtx);
435193323Sed			priv->flags |= NGDF_RWAIT;
436193323Sed			if ((error = msleep(priv, &priv->ngd_mtx,
437200581Srdivacky			    PDROP | PCATCH | (PZERO + 1),
438193323Sed			    "ngdread", 0)) != 0)
439193323Sed				return (error);
440193323Sed		}
441193323Sed	} while (m == NULL);
442193323Sed
443193323Sed	while (m && uio->uio_resid > 0 && error == 0) {
444193323Sed		len = MIN(uio->uio_resid, m->m_len);
445193323Sed		if (len != 0)
446193323Sed			error = uiomove(mtod(m, void *), len, uio);
447193323Sed		m = m_free(m);
448193323Sed	}
449193323Sed
450193323Sed	if (m)
451193323Sed		m_freem(m);
452193323Sed
453193323Sed	return (error);
454199481Srdivacky}
455199481Srdivacky
456199481Srdivacky
457199481Srdivacky/*
458199481Srdivacky * This function is called when our device is written to.
459199481Srdivacky * We read the data from userland into mbuf chain and pass it to the remote hook.
460199481Srdivacky *
461199481Srdivacky */
462199481Srdivackystatic int
463199481Srdivackyngdwrite(struct cdev *dev, struct uio *uio, int flag)
464199481Srdivacky{
465199481Srdivacky	priv_p	priv = (priv_p )dev->si_drv1;
466199481Srdivacky	struct mbuf *m;
467193323Sed	int error = 0;
468199481Srdivacky
469193323Sed	DBG;
470199481Srdivacky
471199481Srdivacky	if (uio->uio_resid == 0)
472199481Srdivacky		return (0);
473199481Srdivacky
474199481Srdivacky	if (uio->uio_resid < 0 || uio->uio_resid > IP_MAXPACKET)
475199481Srdivacky		return (EIO);
476199481Srdivacky
477199481Srdivacky	if ((m = m_uiotombuf(uio, M_DONTWAIT, 0)) == NULL)
478199481Srdivacky		return (ENOBUFS);
479199481Srdivacky
480199481Srdivacky	NG_SEND_DATA_ONLY(error, priv->hook, m);
481199481Srdivacky
482199481Srdivacky	return (error);
483199481Srdivacky}
484199481Srdivacky
485193323Sed/*
486199481Srdivacky * we are being polled/selected
487199481Srdivacky * check if there is data available for read
488199481Srdivacky */
489199481Srdivackystatic int
490199481Srdivackyngdpoll(struct cdev *dev, int events, struct thread *td)
491193323Sed{
492193323Sed	priv_p	priv = (priv_p )dev->si_drv1;
493198892Srdivacky	int revents = 0;
494198892Srdivacky
495198892Srdivacky	if (events & (POLLIN | POLLRDNORM) &&
496198892Srdivacky	    !IFQ_IS_EMPTY(&priv->readq))
497198892Srdivacky		revents |= events & (POLLIN | POLLRDNORM);
498198892Srdivacky
499198892Srdivacky	return (revents);
500198892Srdivacky}
501198892Srdivacky
502199481Srdivacky/******************************************************************************
503199481Srdivacky *  Helper subroutines
504199481Srdivacky ******************************************************************************/
505198892Srdivacky
506198892Srdivackystatic int
507198892Srdivackyget_free_unit()
508198892Srdivacky{
509198892Srdivacky	struct ngd_private *priv = NULL;
510198892Srdivacky	int n = 0;
511199481Srdivacky	int unit = -1;
512199481Srdivacky
513199481Srdivacky	DBG;
514199481Srdivacky
515199481Srdivacky	mtx_assert(&ng_device_mtx, MA_OWNED);
516199481Srdivacky
517199481Srdivacky	/* When there is no list yet, the first device unit is always 0. */
518199481Srdivacky	if SLIST_EMPTY(&ngd_nodes)
519199481Srdivacky		return(0);
520199481Srdivacky
521199481Srdivacky	/* Just do a brute force loop to find the first free unit that is
522199481Srdivacky	 * smaller than MAX_NGD.
523199481Srdivacky	 * Set MAX_NGD to a large value, doesn't impact performance.
524199481Srdivacky	 */
525199481Srdivacky	for(n = 0; n<MAX_NGD && unit == -1; n++) {
526199481Srdivacky		SLIST_FOREACH(priv, &ngd_nodes, links) {
527199481Srdivacky
528199481Srdivacky			if(priv->unit == n) {
529199481Srdivacky				unit = -1;
530199481Srdivacky				break;
531199481Srdivacky			}
532199481Srdivacky			unit = n;
533199481Srdivacky		}
534199481Srdivacky	}
535199481Srdivacky
536199481Srdivacky	return (unit);
537199481Srdivacky}
538199481Srdivacky