ng_frame_relay.c revision 129823
161033Sdfr
261033Sdfr/*
361033Sdfr * ng_frame_relay.c
461033Sdfr *
561033Sdfr * Copyright (c) 1996-1999 Whistle Communications, Inc.
661033Sdfr * All rights reserved.
761033Sdfr *
861033Sdfr * Subject to the following obligations and disclaimer of warranty, use and
961033Sdfr * redistribution of this software, in source or object code forms, with or
1061033Sdfr * without modifications are expressly permitted by Whistle Communications;
1161033Sdfr * provided, however, that:
1261033Sdfr * 1. Any and all reproductions of the source or object code must include the
1361033Sdfr *    copyright notice above and the following disclaimer of warranties; and
1461033Sdfr * 2. No rights are granted, in any manner or form, to use Whistle
1561033Sdfr *    Communications, Inc. trademarks, including the mark "WHISTLE
1661033Sdfr *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1761033Sdfr *    such appears in the above copyright notice or in the software.
1861033Sdfr *
1961033Sdfr * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2061033Sdfr * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2161033Sdfr * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2261033Sdfr * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2361033Sdfr * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2461033Sdfr * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2561033Sdfr * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2661033Sdfr * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
27116182Sobrien * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
28116182Sobrien * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
29116182Sobrien * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3061033Sdfr * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3185521Sjhb * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3265822Sjhb * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3385560Sjhb * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3461033Sdfr * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
35123614Sjhb * OF SUCH DAMAGE.
3685521Sjhb *
3761033Sdfr * Author: Julian Elischer <julian@freebsd.org>
3885521Sjhb *
39145729Ssam * $FreeBSD: head/sys/netgraph/ng_frame_relay.c 129823 2004-05-29 00:51:19Z julian $
40154333Sscottl * $Whistle: ng_frame_relay.c,v 1.20 1999/11/01 09:24:51 julian Exp $
4185521Sjhb */
42119708Sken
43154333Sscottl/*
4461033Sdfr * This node implements the frame relay protocol, not including
4569774Sphk * the LMI line management. This means basically keeping track
46123614Sjhb * of which DLCI's are active, doing frame (de)multiplexing, etc.
47123614Sjhb *
4861033Sdfr * It has a 'downstream' hook that goes to the line, and a
4985521Sjhb * hook for each DLCI (eg, 'dlci16').
5067551Sjhb */
5161033Sdfr
5261033Sdfr#include <sys/param.h>
5361033Sdfr#include <sys/systm.h>
5461033Sdfr#include <sys/kernel.h>
5561033Sdfr#include <sys/errno.h>
5661033Sdfr#include <sys/malloc.h>
57145473Ssam#include <sys/mbuf.h>
5885521Sjhb#include <sys/syslog.h>
59178015Ssam#include <sys/ctype.h>
60178015Ssam
61180588Skmacy#include <netgraph/ng_message.h>
62154333Sscottl#include <netgraph/netgraph.h>
6361033Sdfr#include <netgraph/ng_frame_relay.h>
6461033Sdfr
65154333Sscottl/*
66177621Sscottl * Line info, and status per channel.
67177621Sscottl */
68154333Sscottlstruct ctxinfo {		/* one per active hook */
69180588Skmacy	u_int   flags;
70180588Skmacy#define CHAN_VALID	0x01	/* assigned to a channel */
71180588Skmacy#define CHAN_ACTIVE	0x02	/* bottom level active */
72180588Skmacy	int     dlci;		/* the dlci assigned to this context */
73180588Skmacy	hook_p  hook;		/* if there's a hook assigned.. */
74180588Skmacy};
75180588Skmacy
76180588Skmacy#define MAX_CT 16		/* # of dlci's active at a time (POWER OF 2!) */
77154167Sscottlstruct frmrel_softc {
78180588Skmacy	int     unit;		/* which card are we? */
79180588Skmacy	int     datahooks;	/* number of data hooks attached */
80180588Skmacy	node_p  node;		/* netgraph node */
81180588Skmacy	int     addrlen;	/* address header length */
82180588Skmacy	int     flags;		/* state */
83180588Skmacy	int     mtu;		/* guess */
84180588Skmacy	u_char  remote_seq;	/* sequence number the remote sent */
85180588Skmacy	u_char  local_seq;	/* sequence number the remote rcvd */
86154167Sscottl	u_short ALT[1024];	/* map DLCIs to CTX */
8785521Sjhb#define	CTX_VALID	0x8000		/* this bit means it's a valid CTX */
8885521Sjhb#define	CTX_VALUE	(MAX_CT - 1)	/* mask for context part */
89154167Sscottl	struct	ctxinfo channel[MAX_CT];
90154167Sscottl	struct	ctxinfo downstream;
91154167Sscottl};
92154167Sscottltypedef struct frmrel_softc *sc_p;
93180588Skmacy
94154167Sscottl#define BYTEX_EA	0x01	/* End Address. Always 0 on byte1 */
95154167Sscottl#define BYTE1_C_R	0x02
96154167Sscottl#define BYTE2_FECN	0x08	/* forwards congestion notification */
97154167Sscottl#define BYTE2_BECN	0x04	/* Backward congestion notification */
9885521Sjhb#define BYTE2_DE	0x02	/* Discard elligability */
9985521Sjhb#define LASTBYTE_D_C	0x02	/* last byte is dl_core or dlci info */
10085521Sjhb
10185521Sjhb/* Used to do headers */
10293818Sjhbstatic struct segment {
10385521Sjhb	u_char  mask;
10485521Sjhb	u_char  shift;
10585521Sjhb	u_char  width;
10685521Sjhb} makeup[] = {
10785521Sjhb	{ 0xfc, 2, 6 },
108154167Sscottl	{ 0xf0, 4, 4 },
109154167Sscottl	{ 0xfe, 1, 7 },
110145729Ssam	{ 0xfc, 2, 6 }
111154167Sscottl};
11261033Sdfr
11361033Sdfr#define SHIFTIN(segment, byte, dlci) 					     \
114180588Skmacy	{								     \
11585521Sjhb		(dlci) <<= (segment)->width;				     \
11661033Sdfr		(dlci) |=						     \
117188058Simp			(((byte) & (segment)->mask) >> (segment)->shift);    \
118180588Skmacy	}
11961033Sdfr
12061033Sdfr#define SHIFTOUT(segment, byte, dlci)					     \
12161033Sdfr	{								     \
12261033Sdfr		(byte) |= (((dlci) << (segment)->shift) & (segment)->mask);  \
123180588Skmacy		(dlci) >>= (segment)->width;				     \
124180588Skmacy	}
125154167Sscottl
12661033Sdfr/* Netgraph methods */
12785521Sjhbstatic ng_constructor_t	ngfrm_constructor;
12861033Sdfrstatic ng_shutdown_t	ngfrm_shutdown;
12985521Sjhbstatic ng_newhook_t	ngfrm_newhook;
13061033Sdfrstatic ng_rcvdata_t	ngfrm_rcvdata;
13161033Sdfrstatic ng_disconnect_t	ngfrm_disconnect;
13261033Sdfr
13361033Sdfr/* Other internal functions */
134154167Sscottlstatic int ngfrm_decode(node_p node, item_p item);
135154167Sscottlstatic int ngfrm_addrlen(char *hdr);
136154333Sscottlstatic int ngfrm_allocate_CTX(sc_p sc, int dlci);
137154167Sscottl
138154333Sscottl/* Netgraph type */
139154167Sscottlstatic struct ng_type typestruct = {
140154167Sscottl	.version =	NG_ABI_VERSION,
141154167Sscottl	.name =		NG_FRAMERELAY_NODE_TYPE,
142145729Ssam	.constructor =	ngfrm_constructor,
143145729Ssam	.shutdown =	ngfrm_shutdown,
144145729Ssam	.newhook =	ngfrm_newhook,
145145729Ssam	.rcvdata =	ngfrm_rcvdata,
146178015Ssam	.disconnect =	ngfrm_disconnect,
147145729Ssam};
148145729SsamNETGRAPH_INIT(framerelay, &typestruct);
149178015Ssam
150154333Sscottl/*
151154333Sscottl * Given a DLCI, return the index of the  context table entry for it,
152145729Ssam * Allocating a new one if needs be, or -1 if none available.
153145729Ssam */
154145729Ssamstatic int
15561033Sdfrngfrm_allocate_CTX(sc_p sc, int dlci)
15661033Sdfr{
15761033Sdfr	u_int   ctxnum = -1;	/* what ctx number we are using */
15885521Sjhb	volatile struct ctxinfo *CTXp = NULL;
15985521Sjhb
16061033Sdfr	/* Sanity check the dlci value */
16185521Sjhb	if (dlci > 1023)
16261033Sdfr		return (-1);
163154167Sscottl
164154333Sscottl	/* Check to see if we already have an entry for this DLCI */
165131246Sjhb	if (sc->ALT[dlci]) {
166178015Ssam		if ((ctxnum = sc->ALT[dlci] & CTX_VALUE) < MAX_CT) {
16785521Sjhb			CTXp = sc->channel + ctxnum;
168178015Ssam		} else {
16961033Sdfr			ctxnum = -1;
17061033Sdfr			sc->ALT[dlci] = 0;	/* paranoid but... */
17161033Sdfr		}
17285521Sjhb	}
17385521Sjhb
17485521Sjhb	/*
17561033Sdfr	 * If the index has no valid entry yet, then we need to allocate a
17661033Sdfr	 * CTX number to it
17761033Sdfr	 */
17861033Sdfr	if (CTXp == NULL) {
17961033Sdfr		for (ctxnum = 0; ctxnum < MAX_CT; ctxnum++) {
18085521Sjhb			/*
18185521Sjhb			 * If the VALID flag is empty it is unused
182123614Sjhb			 */
183154167Sscottl			if ((sc->channel[ctxnum].flags & CHAN_VALID) == 0) {
18485521Sjhb				bzero(sc->channel + ctxnum,
18561033Sdfr				      sizeof(struct ctxinfo));
18661033Sdfr				CTXp = sc->channel + ctxnum;
18785521Sjhb				sc->ALT[dlci] = ctxnum | CTX_VALID;
18885521Sjhb				sc->channel[ctxnum].dlci = dlci;
189123614Sjhb				sc->channel[ctxnum].flags = CHAN_VALID;
19061033Sdfr				break;
19161033Sdfr			}
19261033Sdfr		}
19361033Sdfr	}
19461033Sdfr
19561033Sdfr	/*
19661033Sdfr	 * If we still don't have a CTX pointer, then we never found a free
19761033Sdfr	 * spot so give up now..
198154167Sscottl	 */
19985560Sjhb	if (!CTXp) {
20061033Sdfr		log(LOG_ERR, "No CTX available for dlci %d\n", dlci);
20161033Sdfr		return (-1);
20261033Sdfr	}
203180588Skmacy	return (ctxnum);
20461033Sdfr}
205154167Sscottl
20661033Sdfr/*
20761033Sdfr * Node constructor
20861033Sdfr */
20961033Sdfrstatic int
21061033Sdfrngfrm_constructor(node_p node)
21161033Sdfr{
21264199Shsu	sc_p sc;
21361033Sdfr
21461033Sdfr	MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO);
21561033Sdfr	if (!sc)
216188058Simp		return (ENOMEM);
21761033Sdfr	sc->addrlen = 2;	/* default */
21861033Sdfr
21961033Sdfr	/* Link the node and our private info */
22061033Sdfr	NG_NODE_SET_PRIVATE(node, sc);
22161033Sdfr	sc->node = node;
22261033Sdfr	return (0);
22361033Sdfr}
22461033Sdfr
22561033Sdfr/*
22661033Sdfr * Add a new hook
22761033Sdfr *
22861033Sdfr * We allow hooks called "debug", "downstream" and dlci[0-1023]
229180588Skmacy * The hook's private info points to our stash of info about that
230177621Sscottl * channel. A NULL pointer is debug and a DLCI of -1 means downstream.
231180588Skmacy */
232177621Sscottlstatic int
23385560Sjhbngfrm_newhook(node_p node, hook_p hook, const char *name)
234154167Sscottl{
23585560Sjhb	const sc_p sc = NG_NODE_PRIVATE(node);
23661033Sdfr	const char *cp;
23761033Sdfr	char *eptr;
23861033Sdfr	int dlci = 0;
23961033Sdfr	int ctxnum;
240177621Sscottl
241177621Sscottl	/* Check if it's our friend the control hook */
242177621Sscottl	if (strcmp(name, NG_FRAMERELAY_HOOK_DEBUG) == 0) {
243177621Sscottl		NG_HOOK_SET_PRIVATE(hook, NULL);	/* paranoid */
244177621Sscottl		return (0);
245177621Sscottl	}
246177621Sscottl
247177621Sscottl	/*
248177621Sscottl	 * All other hooks either start with 'dlci' and have a decimal
249177621Sscottl	 * trailing channel number up to 4 digits, or are the downstream
250177621Sscottl	 * hook.
251177621Sscottl	 */
252177621Sscottl	if (strncmp(name, NG_FRAMERELAY_HOOK_DLCI,
253177621Sscottl	    strlen(NG_FRAMERELAY_HOOK_DLCI)) != 0) {
254177621Sscottl
255177621Sscottl		/* It must be the downstream connection */
256177621Sscottl		if (strcmp(name, NG_FRAMERELAY_HOOK_DOWNSTREAM) != 0)
257177621Sscottl			return EINVAL;
258177621Sscottl
259177621Sscottl		/* Make sure we haven't already got one (paranoid) */
260177621Sscottl		if (sc->downstream.hook)
261177621Sscottl			return (EADDRINUSE);
26261033Sdfr
26361033Sdfr		/* OK add it */
26461033Sdfr		NG_HOOK_SET_PRIVATE(hook, &sc->downstream);
265131246Sjhb		sc->downstream.hook = hook;
26661033Sdfr		sc->downstream.dlci = -1;
267131246Sjhb		sc->downstream.flags |= CHAN_ACTIVE;
268131246Sjhb		sc->datahooks++;
269154167Sscottl		return (0);
27061033Sdfr	}
27161033Sdfr
27261033Sdfr	/* Must be a dlci hook at this point */
27361033Sdfr	cp = name + strlen(NG_FRAMERELAY_HOOK_DLCI);
27461033Sdfr	if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))
27561033Sdfr		return (EINVAL);
27661033Sdfr	dlci = (int)strtoul(cp, &eptr, 10);
27761033Sdfr	if (*eptr != '\0' || dlci < 0 || dlci > 1023)
27861033Sdfr		return (EINVAL);
279145473Ssam
280154167Sscottl	/*
28161033Sdfr	 * We have a dlci, now either find it, or allocate it. It's possible
28285560Sjhb	 * that we might have seen packets for it already and made an entry
28361033Sdfr	 * for it.
284154167Sscottl	 */
285145473Ssam	ctxnum = ngfrm_allocate_CTX(sc, dlci);
286136131Simp	if (ctxnum == -1)
28761033Sdfr		return (ENOBUFS);
288131246Sjhb
289131246Sjhb	/*
290131246Sjhb	 * Be paranoid: if it's got a hook already, that dlci is in use .
291131246Sjhb	 * Generic code can not catch all the synonyms (e.g. dlci016 vs
292131246Sjhb	 * dlci16)
293131246Sjhb	 */
294154167Sscottl	if (sc->channel[ctxnum].hook != NULL)
29561033Sdfr		return (EADDRINUSE);
29661033Sdfr
297136131Simp	/*
298136131Simp	 * Put our hooks into it (pun not intended)
299136131Simp	 */
300180588Skmacy	sc->channel[ctxnum].flags |= CHAN_ACTIVE;
301154167Sscottl	NG_HOOK_SET_PRIVATE(hook, sc->channel + ctxnum);
302154167Sscottl	sc->channel[ctxnum].hook = hook;
303154167Sscottl	sc->datahooks++;
304154167Sscottl	return (0);
305154167Sscottl}
306154167Sscottl
307145729Ssam/*
308154167Sscottl * Count up the size of the address header if we don't already know
309154167Sscottl */
310154167Sscottlint
311154167Sscottlngfrm_addrlen(char *hdr)
312154167Sscottl{
313136131Simp	if (hdr[0] & BYTEX_EA)
314136131Simp		return 0;
31561033Sdfr	if (hdr[1] & BYTEX_EA)
31661033Sdfr		return 2;
31761033Sdfr	if (hdr[2] & BYTEX_EA)
31888900Sjhb		return 3;
31961033Sdfr	if (hdr[3] & BYTEX_EA)
32061033Sdfr		return 4;
32161033Sdfr	return 0;
32267551Sjhb}
32361033Sdfr
32461033Sdfr/*
32561033Sdfr * Receive data packet
32661033Sdfr */
327111528Sscottlstatic int
328111528Sscottlngfrm_rcvdata(hook_p hook, item_p item)
329111528Sscottl{
330111528Sscottl	struct	ctxinfo *const ctxp = NG_HOOK_PRIVATE(hook);
331111528Sscottl	int     error = 0;
332111528Sscottl	int     dlci;
333111528Sscottl	sc_p    sc;
334111528Sscottl	int     alen;
335111528Sscottl	char   *data;
336111528Sscottl	struct mbuf *m;
337111528Sscottl
338111528Sscottl	/* Data doesn't come in from just anywhere (e.g debug hook) */
339154333Sscottl	if (ctxp == NULL) {
340154333Sscottl		error = ENETDOWN;
341154333Sscottl		goto bad;
342154333Sscottl	}
343154333Sscottl
344178015Ssam	/* If coming from downstream, decode it to a channel */
345154333Sscottl	dlci = ctxp->dlci;
346178015Ssam	if (dlci == -1)
347154333Sscottl		return (ngfrm_decode(NG_HOOK_NODE(hook), item));
348154333Sscottl
349154333Sscottl	NGI_GET_M(item, m);
350154333Sscottl	/* Derive the softc we will need */
351178015Ssam	sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
352154333Sscottl
353154333Sscottl	/* If there is no live channel, throw it away */
354154333Sscottl	if ((sc->downstream.hook == NULL)
355154333Sscottl	    || ((ctxp->flags & CHAN_ACTIVE) == 0)) {
356154333Sscottl		error = ENETDOWN;
357154333Sscottl		goto bad;
358178015Ssam	}
359157314Ssam
360178015Ssam	/* Store the DLCI on the front of the packet */
361157314Ssam	alen = sc->addrlen;
362157314Ssam	if (alen == 0)
363157314Ssam		alen = 2;	/* default value for transmit */
364157314Ssam	M_PREPEND(m, alen, M_DONTWAIT);
365154333Sscottl	if (m == NULL) {
366154333Sscottl		error = ENOBUFS;
367178015Ssam		goto bad;
368178015Ssam	}
369154333Sscottl	data = mtod(m, char *);
370178015Ssam
371178015Ssam	/*
372178015Ssam	 * Shift the lowest bits into the address field untill we are done.
373158904Ssam	 * First byte is MSBits of addr so work backwards.
374157314Ssam	 */
375178015Ssam	switch (alen) {
376178015Ssam	case 2:
377178015Ssam		data[0] = data[1] = '\0';
378158904Ssam		SHIFTOUT(makeup + 1, data[1], dlci);
379178015Ssam		SHIFTOUT(makeup + 0, data[0], dlci);
380154333Sscottl		data[1] |= BYTEX_EA;
381158904Ssam		break;
382178015Ssam	case 3:
383158904Ssam		data[0] = data[1] = data[2] = '\0';
384178015Ssam		SHIFTOUT(makeup + 3, data[2], dlci);	/* 3 and 2 is correct */
385170307Sjeff		SHIFTOUT(makeup + 1, data[1], dlci);
386158904Ssam		SHIFTOUT(makeup + 0, data[0], dlci);
387166188Sjeff		data[2] |= BYTEX_EA;
388170307Sjeff		break;
389158904Ssam	case 4:
390154333Sscottl		data[0] = data[1] = data[2] = data[3] = '\0';
391154333Sscottl		SHIFTOUT(makeup + 3, data[3], dlci);
392154333Sscottl		SHIFTOUT(makeup + 2, data[2], dlci);
393154333Sscottl		SHIFTOUT(makeup + 1, data[1], dlci);
394133305Sjmg		SHIFTOUT(makeup + 0, data[0], dlci);
395133305Sjmg		data[3] |= BYTEX_EA;
396119708Sken		break;
397133305Sjmg	default:
398131246Sjhb		panic(__func__);
399133305Sjmg	}
400133305Sjmg
401154167Sscottl	/* Send it */
402188548Sthompsa	NG_FWD_NEW_DATA(error, item, sc->downstream.hook, m);
403133305Sjmg	return (error);
404157815Sjhb
405188548Sthompsabad:
406145729Ssam	NG_FREE_ITEM(item);
407145729Ssam	NG_FREE_M(m);
408178015Ssam	return (error);
409178015Ssam}
410154167Sscottl
411178123Sjhb/*
412119708Sken * Decode an incoming frame coming from the switch
413119708Sken */
414133305Sjmgstatic int
415119708Skenngfrm_decode(node_p node, item_p item)
416119708Sken{
417133305Sjmg	const sc_p  sc = NG_NODE_PRIVATE(node);
418131246Sjhb	char       *data;
419133305Sjmg	int         alen;
420133305Sjmg	u_int	    dlci = 0;
421133305Sjmg	int	    error = 0;
422133305Sjmg	int	    ctxnum;
423145729Ssam	struct mbuf *m;
424119708Sken
425119708Sken	NGI_GET_M(item, m);
426188058Simp	if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) {
427111528Sscottl		error = ENOBUFS;
428111528Sscottl		goto out;
429111528Sscottl	}
430188058Simp	data = mtod(m, char *);
431151656Sjhb	if ((alen = sc->addrlen) == 0) {
432111528Sscottl		sc->addrlen = alen = ngfrm_addrlen(data);
433119708Sken	}
434133305Sjmg	switch (alen) {
435119789Ssam	case 2:
436154167Sscottl		SHIFTIN(makeup + 0, data[0], dlci);
437154167Sscottl		SHIFTIN(makeup + 1, data[1], dlci);
438154333Sscottl		break;
439119789Ssam	case 3:
440154333Sscottl		SHIFTIN(makeup + 0, data[0], dlci);
441154167Sscottl		SHIFTIN(makeup + 1, data[1], dlci);
442119789Ssam		SHIFTIN(makeup + 3, data[2], dlci);	/* 3 and 2 is correct */
443119789Ssam		break;
444154167Sscottl	case 4:
445154167Sscottl		SHIFTIN(makeup + 0, data[0], dlci);
446154167Sscottl		SHIFTIN(makeup + 1, data[1], dlci);
447119789Ssam		SHIFTIN(makeup + 2, data[2], dlci);
448154167Sscottl		SHIFTIN(makeup + 3, data[3], dlci);
449119789Ssam		break;
450119789Ssam	default:
451119789Ssam		error = EINVAL;
452119789Ssam		goto out;
453119789Ssam	}
454154167Sscottl
455119789Ssam	if (dlci > 1023) {
456119789Ssam		error = EINVAL;
457119789Ssam		goto out;
458119789Ssam	}
459119789Ssam	ctxnum = sc->ALT[dlci];
460119789Ssam	if ((ctxnum & CTX_VALID) && sc->channel[ctxnum &= CTX_VALUE].hook) {
461119789Ssam		/* Send it */
462154167Sscottl		m_adj(m, alen);
463119789Ssam		NG_FWD_NEW_DATA(error, item, sc->channel[ctxnum].hook, m);
464119789Ssam		return (error);
465188058Simp	} else {
466154167Sscottl		error = ENETDOWN;
467154167Sscottl	}
468out:
469	NG_FREE_ITEM(item);
470	NG_FREE_M(m);
471	return (error);
472}
473
474/*
475 * Shutdown node
476 */
477static int
478ngfrm_shutdown(node_p node)
479{
480	const sc_p sc = NG_NODE_PRIVATE(node);
481
482	NG_NODE_SET_PRIVATE(node, NULL);
483	FREE(sc, M_NETGRAPH);
484	NG_NODE_UNREF(node);
485	return (0);
486}
487
488/*
489 * Hook disconnection
490 *
491 * Invalidate the private data associated with this dlci.
492 * For this type, removal of the last link resets tries to destroy the node.
493 */
494static int
495ngfrm_disconnect(hook_p hook)
496{
497	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
498	struct ctxinfo *const cp = NG_HOOK_PRIVATE(hook);
499	int dlci;
500
501	/* If it's a regular dlci hook, then free resources etc.. */
502	if (cp != NULL) {
503		cp->hook = NULL;
504		dlci = cp->dlci;
505		if (dlci != -1)
506			sc->ALT[dlci] = 0;
507		cp->flags = 0;
508		sc->datahooks--;
509	}
510	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
511	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
512		ng_rmnode_self(NG_HOOK_NODE(hook));
513	return (0);
514}
515