ng_lmi.c revision 259065
1130368Smlaier/*
2130365Smlaier * ng_lmi.c
3130365Smlaier */
4130365Smlaier
5130365Smlaier/*-
6130365Smlaier * Copyright (c) 1996-1999 Whistle Communications, Inc.
7130365Smlaier * All rights reserved.
8130365Smlaier *
9130365Smlaier * Subject to the following obligations and disclaimer of warranty, use and
10130365Smlaier * redistribution of this software, in source or object code forms, with or
11130365Smlaier * without modifications are expressly permitted by Whistle Communications;
12130365Smlaier * provided, however, that:
13130365Smlaier * 1. Any and all reproductions of the source or object code must include the
14130365Smlaier *    copyright notice above and the following disclaimer of warranties; and
15130365Smlaier * 2. No rights are granted, in any manner or form, to use Whistle
16130365Smlaier *    Communications, Inc. trademarks, including the mark "WHISTLE
17130365Smlaier *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
18130365Smlaier *    such appears in the above copyright notice or in the software.
19130365Smlaier *
20130365Smlaier * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
21130365Smlaier * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
22130365Smlaier * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
23130365Smlaier * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
24130365Smlaier * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
25130365Smlaier * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
26130365Smlaier * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
27130365Smlaier * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
28130365Smlaier * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
29130365Smlaier * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
30130365Smlaier * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
31130365Smlaier * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
32130365Smlaier * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
33130365Smlaier * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34130365Smlaier * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35130365Smlaier * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
36130365Smlaier * OF SUCH DAMAGE.
37130365Smlaier *
38130365Smlaier * Author: Julian Elischer <julian@freebsd.org>
39130365Smlaier *
40130365Smlaier * $FreeBSD: releng/10.0/sys/netgraph/ng_lmi.c 243882 2012-12-05 08:04:20Z glebius $
41130365Smlaier * $Whistle: ng_lmi.c,v 1.38 1999/11/01 09:24:52 julian Exp $
42130365Smlaier */
43130365Smlaier
44130365Smlaier/*
45130365Smlaier * This node performs the frame relay LMI protocol. It knows how
46130365Smlaier * to do ITU Annex A, ANSI Annex D, and "Group-of-Four" variants
47130365Smlaier * of the protocol.
48130365Smlaier *
49130365Smlaier * A specific protocol can be forced by connecting the corresponding
50130365Smlaier * hook to DLCI 0 or 1023 (as appropriate) of a frame relay link.
51130365Smlaier *
52130365Smlaier * Alternately, this node can do auto-detection of the LMI protocol
53130365Smlaier * by connecting hook "auto0" to DLCI 0 and "auto1023" to DLCI 1023.
54130365Smlaier */
55130365Smlaier
56130365Smlaier#include <sys/param.h>
57130365Smlaier#include <sys/systm.h>
58130365Smlaier#include <sys/errno.h>
59130365Smlaier#include <sys/kernel.h>
60130365Smlaier#include <sys/malloc.h>
61130365Smlaier#include <sys/mbuf.h>
62130365Smlaier#include <sys/syslog.h>
63130365Smlaier#include <netgraph/ng_message.h>
64130365Smlaier#include <netgraph/netgraph.h>
65130365Smlaier#include <netgraph/ng_lmi.h>
66130365Smlaier
67130365Smlaier/*
68130365Smlaier * Human readable names for LMI
69130365Smlaier */
70130365Smlaier#define NAME_ANNEXA	NG_LMI_HOOK_ANNEXA
71130365Smlaier#define NAME_ANNEXD	NG_LMI_HOOK_ANNEXD
72130365Smlaier#define NAME_GROUP4	NG_LMI_HOOK_GROUPOF4
73130365Smlaier#define NAME_NONE	"None"
74130365Smlaier
75130365Smlaier#define MAX_DLCIS	128
76130365Smlaier#define MAXDLCI		1023
77130365Smlaier
78130365Smlaier/*
79130365Smlaier * DLCI states
80130365Smlaier */
81130365Smlaier#define DLCI_NULL	0
82130365Smlaier#define DLCI_UP		1
83130365Smlaier#define DLCI_DOWN	2
84130365Smlaier
85130365Smlaier/*
86130365Smlaier * Any received LMI frame should be at least this long
87130365Smlaier */
88130365Smlaier#define LMI_MIN_LENGTH	8	/* XXX verify */
89130365Smlaier
90130365Smlaier/*
91130365Smlaier * Netgraph node methods and type descriptor
92130365Smlaier */
93130365Smlaierstatic ng_constructor_t	nglmi_constructor;
94130365Smlaierstatic ng_rcvmsg_t	nglmi_rcvmsg;
95130365Smlaierstatic ng_shutdown_t	nglmi_shutdown;
96130365Smlaierstatic ng_newhook_t	nglmi_newhook;
97130365Smlaierstatic ng_rcvdata_t	nglmi_rcvdata;
98130365Smlaierstatic ng_disconnect_t	nglmi_disconnect;
99130365Smlaierstatic int	nglmi_checkdata(hook_p hook, struct mbuf *m);
100130365Smlaier
101130365Smlaierstatic struct ng_type typestruct = {
102130365Smlaier	.version =	NG_ABI_VERSION,
103130365Smlaier	.name =		NG_LMI_NODE_TYPE,
104130365Smlaier	.constructor =	nglmi_constructor,
105130365Smlaier	.rcvmsg	=	nglmi_rcvmsg,
106130365Smlaier	.shutdown =	nglmi_shutdown,
107130365Smlaier	.newhook =	nglmi_newhook,
108130365Smlaier	.rcvdata =	nglmi_rcvdata,
109130365Smlaier	.disconnect =	nglmi_disconnect,
110130365Smlaier};
111130365SmlaierNETGRAPH_INIT(lmi, &typestruct);
112130365Smlaier
113130365Smlaier/*
114130365Smlaier * Info and status per node
115130365Smlaier */
116130365Smlaierstruct nglmi_softc {
117130365Smlaier	node_p  node;		/* netgraph node */
118130365Smlaier	int     flags;		/* state */
119130365Smlaier	int     poll_count;	/* the count of times for autolmi */
120130365Smlaier	int     poll_state;	/* state of auto detect machine */
121130365Smlaier	u_char  remote_seq;	/* sequence number the remote sent */
122130365Smlaier	u_char  local_seq;	/* last sequence number we sent */
123130365Smlaier	u_char  protoID;	/* 9 for group of 4, 8 otherwise */
124130365Smlaier	u_long  seq_retries;	/* sent this how many time so far */
125130365Smlaier	struct	callout	handle;	/* see timeout(9) */
126130365Smlaier	int     liv_per_full;
127130365Smlaier	int     liv_rate;
128130365Smlaier	int     livs;
129130365Smlaier	int     need_full;
130130365Smlaier	hook_p  lmi_channel;	/* whatever we ended up using */
131130365Smlaier	hook_p  lmi_annexA;
132130365Smlaier	hook_p  lmi_annexD;
133130365Smlaier	hook_p  lmi_group4;
134130365Smlaier	hook_p  lmi_channel0;	/* auto-detect on DLCI 0 */
135130365Smlaier	hook_p  lmi_channel1023;/* auto-detect on DLCI 1023 */
136130365Smlaier	char   *protoname;	/* cache protocol name */
137130365Smlaier	u_char  dlci_state[MAXDLCI + 1];
138130365Smlaier	int     invalidx;	/* next dlci's to invalidate */
139130365Smlaier};
140130365Smlaiertypedef struct nglmi_softc *sc_p;
141130365Smlaier
142130365Smlaier/*
143130365Smlaier * Other internal functions
144130365Smlaier */
145130365Smlaierstatic void	LMI_ticker(node_p node, hook_p hook, void *arg1, int arg2);
146130365Smlaierstatic void	nglmi_startup_fixed(sc_p sc, hook_p hook);
147130365Smlaierstatic void	nglmi_startup_auto(sc_p sc);
148130365Smlaierstatic void	nglmi_startup(sc_p sc);
149130365Smlaierstatic void	nglmi_inquire(sc_p sc, int full);
150130365Smlaierstatic void	ngauto_state_machine(sc_p sc);
151130365Smlaier
152130365Smlaier/*
153130365Smlaier * Values for 'flags' field
154130365Smlaier * NB: the SCF_CONNECTED flag is set if and only if the timer is running.
155130365Smlaier */
156130365Smlaier#define	SCF_CONNECTED	0x01	/* connected to something */
157130365Smlaier#define	SCF_AUTO	0x02	/* we are auto-detecting */
158130365Smlaier#define	SCF_FIXED	0x04	/* we are fixed from the start */
159130365Smlaier
160130365Smlaier#define	SCF_LMITYPE	0x18	/* mask for determining Annex mode */
161130365Smlaier#define	SCF_NOLMI	0x00	/* no LMI type selected yet */
162130365Smlaier#define	SCF_ANNEX_A	0x08	/* running annex A mode */
163130365Smlaier#define	SCF_ANNEX_D	0x10	/* running annex D mode */
164130365Smlaier#define	SCF_GROUP4	0x18	/* running group of 4 */
165130365Smlaier
166130365Smlaier#define SETLMITYPE(sc, annex)						\
167130365Smlaierdo {									\
168130365Smlaier	(sc)->flags &= ~SCF_LMITYPE;					\
169130365Smlaier	(sc)->flags |= (annex);						\
170130365Smlaier} while (0)
171130365Smlaier
172130365Smlaier#define NOPROTO(sc) (((sc)->flags & SCF_LMITYPE) == SCF_NOLMI)
173130365Smlaier#define ANNEXA(sc) (((sc)->flags & SCF_LMITYPE) == SCF_ANNEX_A)
174130365Smlaier#define ANNEXD(sc) (((sc)->flags & SCF_LMITYPE) == SCF_ANNEX_D)
175130365Smlaier#define GROUP4(sc) (((sc)->flags & SCF_LMITYPE) == SCF_GROUP4)
176130365Smlaier
177130365Smlaier#define LMIPOLLSIZE	3
178130365Smlaier#define LMI_PATIENCE	8	/* declare all DLCI DOWN after N LMI failures */
179130365Smlaier
180130365Smlaier/*
181130365Smlaier * Node constructor
182130365Smlaier */
183130365Smlaierstatic int
184130365Smlaiernglmi_constructor(node_p node)
185130365Smlaier{
186130365Smlaier	sc_p sc;
187130365Smlaier
188130365Smlaier	sc = malloc(sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO);
189130365Smlaier
190130365Smlaier	NG_NODE_SET_PRIVATE(node, sc);
191130365Smlaier	sc->node = node;
192130365Smlaier
193130365Smlaier	ng_callout_init(&sc->handle);
194130365Smlaier	sc->protoname = NAME_NONE;
195130365Smlaier	sc->liv_per_full = NG_LMI_SEQ_PER_FULL;	/* make this dynamic */
196130365Smlaier	sc->liv_rate = NG_LMI_KEEPALIVE_RATE;
197130365Smlaier	return (0);
198130365Smlaier}
199130365Smlaier
200130365Smlaier/*
201130365Smlaier * The LMI channel has a private pointer which is the same as the
202130365Smlaier * node private pointer. The debug channel has a NULL private pointer.
203130365Smlaier */
204130365Smlaierstatic int
205130365Smlaiernglmi_newhook(node_p node, hook_p hook, const char *name)
206130365Smlaier{
207240824Sglebius	sc_p sc = NG_NODE_PRIVATE(node);
208130365Smlaier
209130365Smlaier	if (strcmp(name, NG_LMI_HOOK_DEBUG) == 0) {
210130365Smlaier		NG_HOOK_SET_PRIVATE(hook, NULL);
211130365Smlaier		return (0);
212130365Smlaier	}
213130365Smlaier	if (sc->flags & SCF_CONNECTED) {
214130365Smlaier		/* already connected, return an error */
215130365Smlaier		return (EINVAL);
216130365Smlaier	}
217130365Smlaier	if (strcmp(name, NG_LMI_HOOK_ANNEXA) == 0) {
218130365Smlaier		sc->lmi_annexA = hook;
219130365Smlaier		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
220130365Smlaier		sc->protoID = 8;
221130365Smlaier		SETLMITYPE(sc, SCF_ANNEX_A);
222130365Smlaier		sc->protoname = NAME_ANNEXA;
223130365Smlaier		nglmi_startup_fixed(sc, hook);
224130365Smlaier	} else if (strcmp(name, NG_LMI_HOOK_ANNEXD) == 0) {
225130365Smlaier		sc->lmi_annexD = hook;
226130365Smlaier		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
227130365Smlaier		sc->protoID = 8;
228130365Smlaier		SETLMITYPE(sc, SCF_ANNEX_D);
229130365Smlaier		sc->protoname = NAME_ANNEXD;
230130365Smlaier		nglmi_startup_fixed(sc, hook);
231130365Smlaier	} else if (strcmp(name, NG_LMI_HOOK_GROUPOF4) == 0) {
232130365Smlaier		sc->lmi_group4 = hook;
233130365Smlaier		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
234130365Smlaier		sc->protoID = 9;
235130365Smlaier		SETLMITYPE(sc, SCF_GROUP4);
236130365Smlaier		sc->protoname = NAME_GROUP4;
237130365Smlaier		nglmi_startup_fixed(sc, hook);
238130365Smlaier	} else if (strcmp(name, NG_LMI_HOOK_AUTO0) == 0) {
239130365Smlaier		/* Note this, and if B is already installed, we're complete */
240130365Smlaier		sc->lmi_channel0 = hook;
241130365Smlaier		sc->protoname = NAME_NONE;
242130365Smlaier		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
243130365Smlaier		if (sc->lmi_channel1023)
244130365Smlaier			nglmi_startup_auto(sc);
245130365Smlaier	} else if (strcmp(name, NG_LMI_HOOK_AUTO1023) == 0) {
246130365Smlaier		/* Note this, and if A is already installed, we're complete */
247130365Smlaier		sc->lmi_channel1023 = hook;
248130365Smlaier		sc->protoname = NAME_NONE;
249130365Smlaier		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
250130365Smlaier		if (sc->lmi_channel0)
251130365Smlaier			nglmi_startup_auto(sc);
252130365Smlaier	} else
253130365Smlaier		return (EINVAL);		/* unknown hook */
254130365Smlaier	return (0);
255130365Smlaier}
256130365Smlaier
257130365Smlaier/*
258130365Smlaier * We have just attached to a live (we hope) node.
259130365Smlaier * Fire out a LMI inquiry, and then start up the timers.
260130365Smlaier */
261130365Smlaierstatic void
262130365SmlaierLMI_ticker(node_p node, hook_p hook, void *arg1, int arg2)
263130365Smlaier{
264130365Smlaier	sc_p sc = NG_NODE_PRIVATE(node);
265130365Smlaier
266130365Smlaier	if (sc->flags & SCF_AUTO) {
267130365Smlaier		ngauto_state_machine(sc);
268130365Smlaier		ng_callout(&sc->handle, node, NULL, NG_LMI_POLL_RATE * hz,
269130365Smlaier		    LMI_ticker, NULL, 0);
270130365Smlaier	} else {
271130365Smlaier		if (sc->livs++ >= sc->liv_per_full) {
272130365Smlaier			nglmi_inquire(sc, 1);
273130365Smlaier			/* sc->livs = 0; *//* do this when we get the answer! */
274130365Smlaier		} else {
275130365Smlaier			nglmi_inquire(sc, 0);
276130365Smlaier		}
277130365Smlaier		ng_callout(&sc->handle, node, NULL, sc->liv_rate * hz,
278130365Smlaier		    LMI_ticker, NULL, 0);
279130365Smlaier	}
280130365Smlaier}
281130365Smlaier
282130365Smlaierstatic void
283130365Smlaiernglmi_startup_fixed(sc_p sc, hook_p hook)
284130365Smlaier{
285130365Smlaier	sc->flags |= (SCF_FIXED | SCF_CONNECTED);
286130365Smlaier	sc->lmi_channel = hook;
287130365Smlaier	nglmi_startup(sc);
288130365Smlaier}
289130365Smlaier
290130365Smlaierstatic void
291130365Smlaiernglmi_startup_auto(sc_p sc)
292130365Smlaier{
293184205Sdes	sc->flags |= (SCF_AUTO | SCF_CONNECTED);
294130365Smlaier	sc->poll_state = 0;	/* reset state machine */
295130365Smlaier	sc->poll_count = 0;
296130365Smlaier	nglmi_startup(sc);
297130365Smlaier}
298130365Smlaier
299130365Smlaierstatic void
300130365Smlaiernglmi_startup(sc_p sc)
301130365Smlaier{
302130365Smlaier	sc->remote_seq = 0;
303130365Smlaier	sc->local_seq = 1;
304130365Smlaier	sc->seq_retries = 0;
305130365Smlaier	sc->livs = sc->liv_per_full - 1;
306130365Smlaier	/* start off the ticker in 1 sec */
307130365Smlaier	ng_callout(&sc->handle, sc->node, NULL, hz, LMI_ticker, NULL, 0);
308130365Smlaier}
309130365Smlaier
310130365Smlaierstatic void
311130365Smlaiernglmi_inquire(sc_p sc, int full)
312130365Smlaier{
313130365Smlaier	struct mbuf *m;
314130365Smlaier	struct ng_tag_prio *ptag;
315130365Smlaier	char   *cptr, *start;
316130365Smlaier	int     error;
317130365Smlaier
318130365Smlaier	if (sc->lmi_channel == NULL)
319130365Smlaier		return;
320130365Smlaier	MGETHDR(m, M_NOWAIT, MT_DATA);
321130365Smlaier	if (m == NULL) {
322130365Smlaier		log(LOG_ERR, "nglmi: unable to start up LMI processing\n");
323130365Smlaier		return;
324130365Smlaier	}
325130365Smlaier	m->m_pkthdr.rcvif = NULL;
326130365Smlaier
327130365Smlaier	/* Attach a tag to packet, marking it of link level state priority, so
328130365Smlaier	 * that device driver would put it in the beginning of queue */
329130365Smlaier
330130365Smlaier	ptag = (struct ng_tag_prio *)m_tag_alloc(NGM_GENERIC_COOKIE, NG_TAG_PRIO,
331147256Sbrooks	    (sizeof(struct ng_tag_prio) - sizeof(struct m_tag)), M_NOWAIT);
332130365Smlaier	if (ptag != NULL) {	/* if it failed, well, it was optional anyhow */
333130365Smlaier		ptag->priority = NG_PRIO_LINKSTATE;
334130365Smlaier		ptag->discardability = -1;
335130365Smlaier		m_tag_prepend(m, &ptag->tag);
336130365Smlaier	}
337130365Smlaier
338130365Smlaier	m->m_data += 4;		/* leave some room for a header */
339130365Smlaier	cptr = start = mtod(m, char *);
340130365Smlaier	/* add in the header for an LMI inquiry. */
341130365Smlaier	*cptr++ = 0x03;		/* UI frame */
342130365Smlaier	if (GROUP4(sc))
343130365Smlaier		*cptr++ = 0x09;	/* proto discriminator */
344130365Smlaier	else
345130365Smlaier		*cptr++ = 0x08;	/* proto discriminator */
346130365Smlaier	*cptr++ = 0x00;		/* call reference */
347130365Smlaier	*cptr++ = 0x75;		/* inquiry */
348130365Smlaier
349130365Smlaier	/* If we are Annex-D, add locking shift to codeset 5. */
350130365Smlaier	if (ANNEXD(sc))
351130365Smlaier		*cptr++ = 0x95;	/* locking shift */
352130365Smlaier	/* Add a request type */
353130365Smlaier	if (ANNEXA(sc))
354130365Smlaier		*cptr++ = 0x51;	/* report type */
355130365Smlaier	else
356130365Smlaier		*cptr++ = 0x01;	/* report type */
357130365Smlaier	*cptr++ = 0x01;		/* size = 1 */
358130365Smlaier	if (full)
359130365Smlaier		*cptr++ = 0x00;	/* full */
360130365Smlaier	else
361130365Smlaier		*cptr++ = 0x01;	/* partial */
362130365Smlaier
363130365Smlaier	/* Add a link verification IE */
364130365Smlaier	if (ANNEXA(sc))
365130365Smlaier		*cptr++ = 0x53;	/* verification IE */
366130365Smlaier	else
367130365Smlaier		*cptr++ = 0x03;	/* verification IE */
368130365Smlaier	*cptr++ = 0x02;		/* 2 extra bytes */
369130365Smlaier	*cptr++ = sc->local_seq;
370130365Smlaier	*cptr++ = sc->remote_seq;
371130365Smlaier	sc->seq_retries++;
372130365Smlaier
373130365Smlaier	/* Send it */
374130365Smlaier	m->m_len = m->m_pkthdr.len = cptr - start;
375130365Smlaier	NG_SEND_DATA_ONLY(error, sc->lmi_channel, m);
376130365Smlaier
377130365Smlaier	/* If we've been sending requests for long enough, and there has
378130365Smlaier	 * been no response, then mark as DOWN, any DLCIs that are UP. */
379130365Smlaier	if (sc->seq_retries == LMI_PATIENCE) {
380130365Smlaier		int     count;
381130365Smlaier
382130365Smlaier		for (count = 0; count < MAXDLCI; count++)
383130365Smlaier			if (sc->dlci_state[count] == DLCI_UP)
384130365Smlaier				sc->dlci_state[count] = DLCI_DOWN;
385130365Smlaier	}
386130365Smlaier}
387130365Smlaier
388130365Smlaier/*
389130365Smlaier * State machine for LMI auto-detect. The transitions are ordered
390130365Smlaier * to try the more likely possibilities first.
391130365Smlaier */
392130365Smlaierstatic void
393130365Smlaierngauto_state_machine(sc_p sc)
394130365Smlaier{
395130365Smlaier	if ((sc->poll_count <= 0) || (sc->poll_count > LMIPOLLSIZE)) {
396130365Smlaier		/* time to change states in the auto probe machine */
397130365Smlaier		/* capture wild values of poll_count while we are at it */
398130365Smlaier		sc->poll_count = LMIPOLLSIZE;
399130365Smlaier		sc->poll_state++;
400130365Smlaier	}
401130365Smlaier	switch (sc->poll_state) {
402130365Smlaier	case 7:
403130365Smlaier		log(LOG_WARNING, "nglmi: no response from exchange\n");
404130365Smlaier	default:		/* capture bad states */
405130365Smlaier		sc->poll_state = 1;
406130365Smlaier	case 1:
407130365Smlaier		sc->lmi_channel = sc->lmi_channel0;
408130365Smlaier		SETLMITYPE(sc, SCF_ANNEX_D);
409130365Smlaier		break;
410130365Smlaier	case 2:
411130365Smlaier		sc->lmi_channel = sc->lmi_channel1023;
412130365Smlaier		SETLMITYPE(sc, SCF_ANNEX_D);
413130365Smlaier		break;
414130365Smlaier	case 3:
415130365Smlaier		sc->lmi_channel = sc->lmi_channel0;
416130365Smlaier		SETLMITYPE(sc, SCF_ANNEX_A);
417130365Smlaier		break;
418130365Smlaier	case 4:
419130365Smlaier		sc->lmi_channel = sc->lmi_channel1023;
420130365Smlaier		SETLMITYPE(sc, SCF_GROUP4);
421130365Smlaier		break;
422130365Smlaier	case 5:
423130365Smlaier		sc->lmi_channel = sc->lmi_channel1023;
424130365Smlaier		SETLMITYPE(sc, SCF_ANNEX_A);
425130365Smlaier		break;
426130365Smlaier	case 6:
427130365Smlaier		sc->lmi_channel = sc->lmi_channel0;
428130365Smlaier		SETLMITYPE(sc, SCF_GROUP4);
429130365Smlaier		break;
430130365Smlaier	}
431130365Smlaier
432130365Smlaier	/* send an inquirey encoded appropriatly */
433130365Smlaier	nglmi_inquire(sc, 0);
434130365Smlaier	sc->poll_count--;
435130365Smlaier}
436130365Smlaier
437130365Smlaier/*
438130365Smlaier * Receive a netgraph control message.
439130365Smlaier */
440130365Smlaierstatic int
441130365Smlaiernglmi_rcvmsg(node_p node, item_p item, hook_p lasthook)
442130365Smlaier{
443130365Smlaier	sc_p    sc = NG_NODE_PRIVATE(node);
444130365Smlaier	struct ng_mesg *resp = NULL;
445130365Smlaier	int     error = 0;
446130365Smlaier	struct ng_mesg *msg;
447130365Smlaier
448130365Smlaier	NGI_GET_MSG(item, msg);
449130365Smlaier	switch (msg->header.typecookie) {
450130365Smlaier	case NGM_GENERIC_COOKIE:
451130365Smlaier		switch (msg->header.cmd) {
452130365Smlaier		case NGM_TEXT_STATUS:
453130365Smlaier		    {
454130365Smlaier			char   *arg;
455130365Smlaier			int     pos, count;
456130365Smlaier
457130365Smlaier			NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
458130365Smlaier			if (resp == NULL) {
459130365Smlaier				error = ENOMEM;
460130365Smlaier				break;
461130365Smlaier			}
462130365Smlaier			arg = resp->data;
463130365Smlaier			pos = sprintf(arg, "protocol %s ", sc->protoname);
464130365Smlaier			if (sc->flags & SCF_FIXED)
465130365Smlaier				pos += sprintf(arg + pos, "fixed\n");
466130365Smlaier			else if (sc->flags & SCF_AUTO)
467130365Smlaier				pos += sprintf(arg + pos, "auto-detecting\n");
468130365Smlaier			else
469130365Smlaier				pos += sprintf(arg + pos, "auto on dlci %d\n",
470130365Smlaier				    (sc->lmi_channel == sc->lmi_channel0) ?
471130365Smlaier				    0 : 1023);
472130365Smlaier			pos += sprintf(arg + pos,
473130365Smlaier			    "keepalive period: %d seconds\n", sc->liv_rate);
474130365Smlaier			pos += sprintf(arg + pos,
475130365Smlaier			    "unacknowledged keepalives: %ld\n",
476130365Smlaier			    sc->seq_retries);
477130365Smlaier			for (count = 0;
478130365Smlaier			     ((count <= MAXDLCI)
479130365Smlaier			      && (pos < (NG_TEXTRESPONSE - 20)));
480130365Smlaier			     count++) {
481130365Smlaier				if (sc->dlci_state[count]) {
482130365Smlaier					pos += sprintf(arg + pos,
483130365Smlaier					       "dlci %d %s\n", count,
484130365Smlaier					       (sc->dlci_state[count]
485130365Smlaier					== DLCI_UP) ? "up" : "down");
486130365Smlaier				}
487130365Smlaier			}
488130365Smlaier			resp->header.arglen = pos + 1;
489130365Smlaier			break;
490130365Smlaier		    }
491130365Smlaier		default:
492130365Smlaier			error = EINVAL;
493130365Smlaier			break;
494130365Smlaier		}
495130365Smlaier		break;
496130365Smlaier	case NGM_LMI_COOKIE:
497130365Smlaier		switch (msg->header.cmd) {
498130365Smlaier		case NGM_LMI_GET_STATUS:
499130365Smlaier		    {
500130365Smlaier			struct nglmistat *stat;
501130365Smlaier			int k;
502130365Smlaier
503130365Smlaier			NG_MKRESPONSE(resp, msg, sizeof(*stat), M_NOWAIT);
504130365Smlaier			if (!resp) {
505130365Smlaier				error = ENOMEM;
506130365Smlaier				break;
507130365Smlaier			}
508130365Smlaier			stat = (struct nglmistat *) resp->data;
509130365Smlaier			strncpy(stat->proto,
510130365Smlaier			     sc->protoname, sizeof(stat->proto) - 1);
511130365Smlaier			strncpy(stat->hook,
512130365Smlaier			      sc->protoname, sizeof(stat->hook) - 1);
513130365Smlaier			stat->autod = !!(sc->flags & SCF_AUTO);
514130365Smlaier			stat->fixed = !!(sc->flags & SCF_FIXED);
515130365Smlaier			for (k = 0; k <= MAXDLCI; k++) {
516130365Smlaier				switch (sc->dlci_state[k]) {
517130365Smlaier				case DLCI_UP:
518130365Smlaier					stat->up[k / 8] |= (1 << (k % 8));
519130365Smlaier					/* fall through */
520130365Smlaier				case DLCI_DOWN:
521130365Smlaier					stat->seen[k / 8] |= (1 << (k % 8));
522130365Smlaier					break;
523130365Smlaier				}
524130365Smlaier			}
525130365Smlaier			break;
526130365Smlaier		    }
527130365Smlaier		default:
528130365Smlaier			error = EINVAL;
529130365Smlaier			break;
530130365Smlaier		}
531164033Srwatson		break;
532164033Srwatson	default:
533164033Srwatson		error = EINVAL;
534164033Srwatson		break;
535130365Smlaier	}
536130365Smlaier
537130365Smlaier	NG_RESPOND_MSG(error, node, item, resp);
538130365Smlaier	NG_FREE_MSG(msg);
539130365Smlaier	return (error);
540130365Smlaier}
541130365Smlaier
542130365Smlaier#define STEPBY(stepsize)			\
543130365Smlaier	do {					\
544130365Smlaier		packetlen -= (stepsize);	\
545130365Smlaier		data += (stepsize);		\
546130365Smlaier	} while (0)
547130365Smlaier
548130365Smlaier/*
549130365Smlaier * receive data, and use it to update our status.
550130365Smlaier * Anything coming in on the debug port is discarded.
551130365Smlaier */
552130365Smlaierstatic int
553130365Smlaiernglmi_rcvdata(hook_p hook, item_p item)
554130365Smlaier{
555130365Smlaier	sc_p    sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
556130365Smlaier	const	u_char *data;
557130365Smlaier	unsigned short dlci;
558130365Smlaier	u_short packetlen;
559130365Smlaier	int     resptype_seen = 0;
560130365Smlaier	struct mbuf *m;
561130365Smlaier
562130365Smlaier	NGI_GET_M(item, m);
563130365Smlaier	NG_FREE_ITEM(item);
564130365Smlaier	if (NG_HOOK_PRIVATE(hook) == NULL) {
565130365Smlaier		goto drop;
566130365Smlaier	}
567130365Smlaier	packetlen = m->m_len;
568130365Smlaier
569130365Smlaier	/* XXX what if it's more than 1 mbuf? */
570130365Smlaier	if ((packetlen > MHLEN) && !(m->m_flags & M_EXT)) {
571130365Smlaier		log(LOG_WARNING, "nglmi: packetlen (%d) too big\n", packetlen);
572184205Sdes		goto drop;
573130365Smlaier	}
574130365Smlaier	if (m->m_len < packetlen && (m = m_pullup(m, packetlen)) == NULL) {
575130365Smlaier		log(LOG_WARNING,
576130365Smlaier		    "nglmi: m_pullup failed for %d bytes\n", packetlen);
577130365Smlaier		return (0);
578130365Smlaier	}
579184205Sdes	if (nglmi_checkdata(hook, m) == 0)
580130365Smlaier		return (0);
581130365Smlaier
582184205Sdes	/* pass the first 4 bytes (already checked in the nglmi_checkdata()) */
583130365Smlaier	data = mtod(m, const u_char *);
584130365Smlaier	STEPBY(4);
585130365Smlaier
586130365Smlaier	/* Now check if there is a 'locking shift'. This is only seen in
587130365Smlaier	 * Annex D frames. don't bother checking, we already did that. Don't
588130365Smlaier	 * increment immediatly as it might not be there. */
589130365Smlaier	if (ANNEXD(sc))
590184205Sdes		STEPBY(1);
591184205Sdes
592130365Smlaier	/* If we get this far we should consider that it is a legitimate
593130365Smlaier	 * frame and we know what it is. */
594130365Smlaier	if (sc->flags & SCF_AUTO) {
595130365Smlaier		/* note the hook that this valid channel came from and drop
596130365Smlaier		 * out of auto probe mode. */
597130365Smlaier		if (ANNEXA(sc))
598130365Smlaier			sc->protoname = NAME_ANNEXA;
599130365Smlaier		else if (ANNEXD(sc))
600130365Smlaier			sc->protoname = NAME_ANNEXD;
601130365Smlaier		else if (GROUP4(sc))
602130365Smlaier			sc->protoname = NAME_GROUP4;
603130365Smlaier		else {
604130365Smlaier			log(LOG_ERR, "nglmi: No known type\n");
605130365Smlaier			goto drop;
606130365Smlaier		}
607130365Smlaier		sc->lmi_channel = hook;
608130365Smlaier		sc->flags &= ~SCF_AUTO;
609130365Smlaier		log(LOG_INFO, "nglmi: auto-detected %s LMI on DLCI %d\n",
610184205Sdes		    sc->protoname, hook == sc->lmi_channel0 ? 0 : 1023);
611184205Sdes	}
612130365Smlaier
613130365Smlaier	/* While there is more data in the status packet, keep processing
614130365Smlaier	 * status items. First make sure there is enough data for the
615130365Smlaier	 * segment descriptor's length field. */
616130365Smlaier	while (packetlen >= 2) {
617130365Smlaier		u_int   segtype = data[0];
618130365Smlaier		u_int   segsize = data[1];
619130365Smlaier
620130365Smlaier		/* Now that we know how long it claims to be, make sure
621130365Smlaier		 * there is enough data for the next seg. */
622130365Smlaier		if (packetlen < segsize + 2)
623130365Smlaier			break;
624130365Smlaier		switch (segtype) {
625130365Smlaier		case 0x01:
626130365Smlaier		case 0x51:
627130365Smlaier			if (resptype_seen) {
628130365Smlaier				log(LOG_WARNING, "nglmi: dup MSGTYPE\n");
629130365Smlaier				goto nextIE;
630130365Smlaier			}
631130365Smlaier			resptype_seen++;
632130365Smlaier			/* The remote end tells us what kind of response
633130365Smlaier			 * this is. Only expect a type 0 or 1. if we are a
634130365Smlaier			 * full status, invalidate a few DLCIs just to see
635130365Smlaier			 * that they are still ok. */
636130365Smlaier			if (segsize != 1)
637130365Smlaier				goto nextIE;
638130365Smlaier			switch (data[2]) {
639130365Smlaier			case 1:
640130365Smlaier				/* partial status, do no extra processing */
641130365Smlaier				break;
642130365Smlaier			case 0:
643130365Smlaier			    {
644130365Smlaier				int     count = 0;
645130365Smlaier				int     idx = sc->invalidx;
646130365Smlaier
647130365Smlaier				for (count = 0; count < 10; count++) {
648130365Smlaier					if (idx > MAXDLCI)
649130365Smlaier						idx = 0;
650130365Smlaier					if (sc->dlci_state[idx] == DLCI_UP)
651130365Smlaier						sc->dlci_state[idx] = DLCI_DOWN;
652130365Smlaier					idx++;
653130365Smlaier				}
654130365Smlaier				sc->invalidx = idx;
655130365Smlaier				/* we got and we wanted one. relax
656130365Smlaier				 * now.. but don't reset to 0 if it
657130365Smlaier				 * was unrequested. */
658130365Smlaier				if (sc->livs > sc->liv_per_full)
659130365Smlaier					sc->livs = 0;
660130365Smlaier				break;
661130365Smlaier			    }
662130365Smlaier			}
663130365Smlaier			break;
664130365Smlaier		case 0x03:
665130365Smlaier		case 0x53:
666130365Smlaier			/* The remote tells us what it thinks the sequence
667130365Smlaier			 * numbers are. If it's not size 2, it must be a
668130365Smlaier			 * duplicate to have gotten this far, skip it. */
669130365Smlaier			if (segsize != 2)
670130365Smlaier				goto nextIE;
671130365Smlaier			sc->remote_seq = data[2];
672130365Smlaier			if (sc->local_seq == data[3]) {
673130365Smlaier				sc->local_seq++;
674130365Smlaier				sc->seq_retries = 0;
675130365Smlaier				/* Note that all 3 Frame protocols seem to
676130365Smlaier				 * not like 0 as a sequence number. */
677130365Smlaier				if (sc->local_seq == 0)
678130365Smlaier					sc->local_seq = 1;
679130365Smlaier			}
680130365Smlaier			break;
681130365Smlaier		case 0x07:
682130365Smlaier		case 0x57:
683130365Smlaier			/* The remote tells us about a DLCI that it knows
684130365Smlaier			 * about. There may be many of these in a single
685130365Smlaier			 * status response */
686130365Smlaier			switch (segsize) {
687130365Smlaier			case 6:/* only on 'group of 4' */
688130365Smlaier				dlci = ((u_short) data[2] & 0xff) << 8;
689130365Smlaier				dlci |= (data[3] & 0xff);
690130365Smlaier				if ((dlci < 1024) && (dlci > 0)) {
691130365Smlaier				  /* XXX */
692130365Smlaier				}
693130365Smlaier				break;
694130365Smlaier			case 3:
695130365Smlaier				dlci = ((u_short) data[2] & 0x3f) << 4;
696130365Smlaier				dlci |= ((data[3] & 0x78) >> 3);
697130365Smlaier				if ((dlci < 1024) && (dlci > 0)) {
698130365Smlaier					/* set up the bottom half of the
699130365Smlaier					 * support for that dlci if it's not
700130365Smlaier					 * already been done */
701130365Smlaier					/* store this information somewhere */
702130365Smlaier				}
703130365Smlaier				break;
704130365Smlaier			default:
705130365Smlaier				goto nextIE;
706130365Smlaier			}
707130365Smlaier			if (sc->dlci_state[dlci] != DLCI_UP) {
708130365Smlaier				/* bring new DLCI to life */
709130365Smlaier				/* may do more here some day */
710130365Smlaier				if (sc->dlci_state[dlci] != DLCI_DOWN)
711130365Smlaier					log(LOG_INFO,
712130365Smlaier					    "nglmi: DLCI %d became active\n",
713130365Smlaier					    dlci);
714130365Smlaier				sc->dlci_state[dlci] = DLCI_UP;
715130365Smlaier			}
716130365Smlaier			break;
717130365Smlaier		}
718130365SmlaiernextIE:
719130365Smlaier		STEPBY(segsize + 2);
720130365Smlaier	}
721130365Smlaier	NG_FREE_M(m);
722130365Smlaier	return (0);
723130365Smlaier
724130365Smlaierdrop:
725130365Smlaier	NG_FREE_M(m);
726130365Smlaier	return (EINVAL);
727130365Smlaier}
728130365Smlaier
729130365Smlaier/*
730130365Smlaier * Check that a packet is entirely kosha.
731130365Smlaier * return 1 of ok, and 0 if not.
732130365Smlaier * All data is discarded if a 0 is returned.
733130365Smlaier */
734130365Smlaierstatic int
735130365Smlaiernglmi_checkdata(hook_p hook, struct mbuf *m)
736130365Smlaier{
737130365Smlaier	sc_p    sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
738130365Smlaier	const	u_char *data;
739130365Smlaier	u_short packetlen;
740130365Smlaier	unsigned short dlci;
741130365Smlaier	u_char  type;
742130365Smlaier	u_char  nextbyte;
743130365Smlaier	int     seq_seen = 0;
744130365Smlaier	int     resptype_seen = 0;	/* 0 , 1 (partial) or 2 (full) */
745130365Smlaier	int     highest_dlci = 0;
746130365Smlaier
747130365Smlaier	packetlen = m->m_len;
748130365Smlaier	data = mtod(m, const u_char *);
749130365Smlaier	if (*data != 0x03) {
750130365Smlaier		log(LOG_WARNING, "nglmi: unexpected value in LMI(%d)\n", 1);
751130365Smlaier		goto reject;
752130365Smlaier	}
753130365Smlaier	STEPBY(1);
754130365Smlaier
755130365Smlaier	/* look at the protocol ID */
756130365Smlaier	nextbyte = *data;
757130365Smlaier	if (sc->flags & SCF_AUTO) {
758130365Smlaier		SETLMITYPE(sc, SCF_NOLMI);	/* start with a clean slate */
759184205Sdes		switch (nextbyte) {
760184205Sdes		case 0x8:
761130365Smlaier			sc->protoID = 8;
762130365Smlaier			break;
763130365Smlaier		case 0x9:
764130365Smlaier			SETLMITYPE(sc, SCF_GROUP4);
765130365Smlaier			sc->protoID = 9;
766130365Smlaier			break;
767130365Smlaier		default:
768130365Smlaier			log(LOG_WARNING, "nglmi: bad Protocol ID(%d)\n",
769130365Smlaier			    (int) nextbyte);
770130365Smlaier			goto reject;
771130365Smlaier		}
772130365Smlaier	} else {
773130365Smlaier		if (nextbyte != sc->protoID) {
774130365Smlaier			log(LOG_WARNING, "nglmi: unexpected Protocol ID(%d)\n",
775130368Smlaier			    (int) nextbyte);
776130368Smlaier			goto reject;
777130365Smlaier		}
778130365Smlaier	}
779130365Smlaier	STEPBY(1);
780130365Smlaier
781130365Smlaier	/* check call reference (always null in non ISDN frame relay) */
782130365Smlaier	if (*data != 0x00) {
783130365Smlaier		log(LOG_WARNING, "nglmi: unexpected Call Reference (0x%x)\n",
784130365Smlaier		    data[-1]);
785130365Smlaier		goto reject;
786130365Smlaier	}
787130365Smlaier	STEPBY(1);
788130365Smlaier
789130365Smlaier	/* check message type */
790130365Smlaier	switch ((type = *data)) {
791130365Smlaier	case 0x75:		/* Status enquiry */
792130365Smlaier		log(LOG_WARNING, "nglmi: unexpected message type(0x%x)\n",
793130365Smlaier		    data[-1]);
794130365Smlaier		goto reject;
795130365Smlaier	case 0x7D:		/* Status message */
796130365Smlaier		break;
797130365Smlaier	default:
798130365Smlaier		log(LOG_WARNING,
799130365Smlaier		    "nglmi: unexpected msg type(0x%x) \n", (int) type);
800130365Smlaier		goto reject;
801130365Smlaier	}
802130368Smlaier	STEPBY(1);
803130368Smlaier
804130365Smlaier	/* Now check if there is a 'locking shift'. This is only seen in
805130365Smlaier	 * Annex D frames. Don't increment immediately as it might not be
806130365Smlaier	 * there. */
807130365Smlaier	nextbyte = *data;
808130365Smlaier	if (sc->flags & SCF_AUTO) {
809130365Smlaier		if (!(GROUP4(sc))) {
810130365Smlaier			if (nextbyte == 0x95) {
811130365Smlaier				SETLMITYPE(sc, SCF_ANNEX_D);
812130365Smlaier				STEPBY(1);
813130365Smlaier			} else
814130365Smlaier				SETLMITYPE(sc, SCF_ANNEX_A);
815130365Smlaier		} else if (nextbyte == 0x95) {
816130365Smlaier			log(LOG_WARNING, "nglmi: locking shift seen in G4\n");
817130365Smlaier			goto reject;
818130365Smlaier		}
819130365Smlaier	} else {
820130365Smlaier		if (ANNEXD(sc)) {
821130365Smlaier			if (*data == 0x95)
822130365Smlaier				STEPBY(1);
823130365Smlaier			else {
824130365Smlaier				log(LOG_WARNING,
825130365Smlaier				    "nglmi: locking shift missing\n");
826130365Smlaier				goto reject;
827130368Smlaier			}
828130368Smlaier		} else if (*data == 0x95) {
829130365Smlaier			log(LOG_WARNING, "nglmi: locking shift seen\n");
830130365Smlaier			goto reject;
831130365Smlaier		}
832130365Smlaier	}
833130365Smlaier
834130365Smlaier	/* While there is more data in the status packet, keep processing
835130365Smlaier	 * status items. First make sure there is enough data for the
836130365Smlaier	 * segment descriptor's length field. */
837130365Smlaier	while (packetlen >= 2) {
838130365Smlaier		u_int   segtype = data[0];
839130365Smlaier		u_int   segsize = data[1];
840130365Smlaier
841130365Smlaier		/* Now that we know how long it claims to be, make sure
842130365Smlaier		 * there is enough data for the next seg. */
843130365Smlaier		if (packetlen < (segsize + 2)) {
844130365Smlaier			log(LOG_WARNING, "nglmi: IE longer than packet\n");
845130365Smlaier			break;
846130365Smlaier		}
847130365Smlaier		switch (segtype) {
848130365Smlaier		case 0x01:
849130365Smlaier		case 0x51:
850130365Smlaier			/* According to MCI's HP analyser, we should just
851			 * ignore if there is mor ethan one of these (?). */
852			if (resptype_seen) {
853				log(LOG_WARNING, "nglmi: dup MSGTYPE\n");
854				goto nextIE;
855			}
856			if (segsize != 1) {
857				log(LOG_WARNING, "nglmi: MSGTYPE wrong size\n");
858				goto reject;
859			}
860			/* The remote end tells us what kind of response
861			 * this is. Only expect a type 0 or 1. if it was a
862			 * full (type 0) check we just asked for a type
863			 * full. */
864			switch (data[2]) {
865			case 1:/* partial */
866				if (sc->livs > sc->liv_per_full) {
867					log(LOG_WARNING,
868					  "nglmi: LIV when FULL expected\n");
869					goto reject;	/* need full */
870				}
871				resptype_seen = 1;
872				break;
873			case 0:/* full */
874				/* Full response is always acceptable */
875				resptype_seen = 2;
876				break;
877			default:
878				log(LOG_WARNING,
879				 "nglmi: Unknown report type %d\n", data[2]);
880				goto reject;
881			}
882			break;
883		case 0x03:
884		case 0x53:
885			/* The remote tells us what it thinks the sequence
886			 * numbers are. I would have thought that there
887			 * needs to be one and only one of these, but MCI
888			 * want us to just ignore extras. (?) */
889			if (resptype_seen == 0) {
890				log(LOG_WARNING, "nglmi: no TYPE before SEQ\n");
891				goto reject;
892			}
893			if (seq_seen != 0)	/* already seen seq numbers */
894				goto nextIE;
895			if (segsize != 2) {
896				log(LOG_WARNING, "nglmi: bad SEQ sts size\n");
897				goto reject;
898			}
899			if (sc->local_seq != data[3]) {
900				log(LOG_WARNING, "nglmi: unexpected SEQ\n");
901				goto reject;
902			}
903			seq_seen = 1;
904			break;
905		case 0x07:
906		case 0x57:
907			/* The remote tells us about a DLCI that it knows
908			 * about. There may be many of these in a single
909			 * status response */
910			if (seq_seen != 1) {	/* already seen seq numbers? */
911				log(LOG_WARNING,
912				    "nglmi: No sequence before DLCI\n");
913				goto reject;
914			}
915			if (resptype_seen != 2) {	/* must be full */
916				log(LOG_WARNING,
917				    "nglmi: No resp type before DLCI\n");
918				goto reject;
919			}
920			if (GROUP4(sc)) {
921				if (segsize != 6) {
922					log(LOG_WARNING,
923					    "nglmi: wrong IE segsize\n");
924					goto reject;
925				}
926				dlci = ((u_short) data[2] & 0xff) << 8;
927				dlci |= (data[3] & 0xff);
928			} else {
929				if (segsize != 3) {
930					log(LOG_WARNING,
931					    "nglmi: DLCI headersize of %d"
932					    " not supported\n", segsize - 1);
933					goto reject;
934				}
935				dlci = ((u_short) data[2] & 0x3f) << 4;
936				dlci |= ((data[3] & 0x78) >> 3);
937			}
938			/* async can only have one of these */
939#if 0				/* async not yet accepted */
940			if (async && highest_dlci) {
941				log(LOG_WARNING,
942				    "nglmi: Async with > 1 DLCI\n");
943				goto reject;
944			}
945#endif
946			/* Annex D says these will always be Ascending, but
947			 * the HP test for G4 says we should accept
948			 * duplicates, so for now allow that. ( <= vs. < ) */
949#if 0
950			/* MCI tests want us to accept out of order for AnxD */
951			if ((!GROUP4(sc)) && (dlci < highest_dlci)) {
952				/* duplicate or mis-ordered dlci */
953				/* (spec says they will increase in number) */
954				log(LOG_WARNING, "nglmi: DLCI out of order\n");
955				goto reject;
956			}
957#endif
958			if (dlci > 1023) {
959				log(LOG_WARNING, "nglmi: DLCI out of range\n");
960				goto reject;
961			}
962			highest_dlci = dlci;
963			break;
964		default:
965			log(LOG_WARNING,
966			    "nglmi: unknown LMI segment type %d\n", segtype);
967		}
968nextIE:
969		STEPBY(segsize + 2);
970	}
971	if (packetlen != 0) {	/* partial junk at end? */
972		log(LOG_WARNING,
973		    "nglmi: %d bytes extra at end of packet\n", packetlen);
974		goto print;
975	}
976	if (resptype_seen == 0) {
977		log(LOG_WARNING, "nglmi: No response type seen\n");
978		goto reject;	/* had no response type */
979	}
980	if (seq_seen == 0) {
981		log(LOG_WARNING, "nglmi: No sequence numbers seen\n");
982		goto reject;	/* had no sequence numbers */
983	}
984	return (1);
985
986print:
987	{
988		int     i, j, k, pos;
989		char    buf[100];
990		int     loc;
991		const	u_char *bp = mtod(m, const u_char *);
992
993		k = i = 0;
994		loc = (m->m_len - packetlen);
995		log(LOG_WARNING, "nglmi: error at location %d\n", loc);
996		while (k < m->m_len) {
997			pos = 0;
998			j = 0;
999			while ((j++ < 16) && k < m->m_len) {
1000				pos += sprintf(buf + pos, "%c%02x",
1001					       ((loc == k) ? '>' : ' '),
1002					       bp[k]);
1003				k++;
1004			}
1005			if (i == 0)
1006				log(LOG_WARNING, "nglmi: packet data:%s\n", buf);
1007			else
1008				log(LOG_WARNING, "%04d              :%s\n", k, buf);
1009			i++;
1010		}
1011	}
1012	return (1);
1013reject:
1014	{
1015		int     i, j, k, pos;
1016		char    buf[100];
1017		int     loc;
1018		const	u_char *bp = mtod(m, const u_char *);
1019
1020		k = i = 0;
1021		loc = (m->m_len - packetlen);
1022		log(LOG_WARNING, "nglmi: error at location %d\n", loc);
1023		while (k < m->m_len) {
1024			pos = 0;
1025			j = 0;
1026			while ((j++ < 16) && k < m->m_len) {
1027				pos += sprintf(buf + pos, "%c%02x",
1028					       ((loc == k) ? '>' : ' '),
1029					       bp[k]);
1030				k++;
1031			}
1032			if (i == 0)
1033				log(LOG_WARNING, "nglmi: packet data:%s\n", buf);
1034			else
1035				log(LOG_WARNING, "%04d              :%s\n", k, buf);
1036			i++;
1037		}
1038	}
1039	NG_FREE_M(m);
1040	return (0);
1041}
1042
1043/*
1044 * Do local shutdown processing..
1045 * Cut any remaining links and free our local resources.
1046 */
1047static int
1048nglmi_shutdown(node_p node)
1049{
1050	const sc_p sc = NG_NODE_PRIVATE(node);
1051
1052	NG_NODE_SET_PRIVATE(node, NULL);
1053	NG_NODE_UNREF(sc->node);
1054	free(sc, M_NETGRAPH);
1055	return (0);
1056}
1057
1058/*
1059 * Hook disconnection
1060 * For this type, removal of any link except "debug" destroys the node.
1061 */
1062static int
1063nglmi_disconnect(hook_p hook)
1064{
1065	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1066
1067	/* OK to remove debug hook(s) */
1068	if (NG_HOOK_PRIVATE(hook) == NULL)
1069		return (0);
1070
1071	/* Stop timer if it's currently active */
1072	if (sc->flags & SCF_CONNECTED)
1073		ng_uncallout(&sc->handle, sc->node);
1074
1075	/* Self-destruct */
1076	if (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))
1077		ng_rmnode_self(NG_HOOK_NODE(hook));
1078	return (0);
1079}
1080
1081