ng_lmi.c revision 184205
152419Sjulian/*
252419Sjulian * ng_lmi.c
3139823Simp */
4139823Simp
5139823Simp/*-
652419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc.
752419Sjulian * All rights reserved.
852419Sjulian *
952419Sjulian * Subject to the following obligations and disclaimer of warranty, use and
1052419Sjulian * redistribution of this software, in source or object code forms, with or
1152419Sjulian * without modifications are expressly permitted by Whistle Communications;
1252419Sjulian * provided, however, that:
1352419Sjulian * 1. Any and all reproductions of the source or object code must include the
1452419Sjulian *    copyright notice above and the following disclaimer of warranties; and
1552419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle
1652419Sjulian *    Communications, Inc. trademarks, including the mark "WHISTLE
1752419Sjulian *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1852419Sjulian *    such appears in the above copyright notice or in the software.
1952419Sjulian *
2052419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2152419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2252419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2352419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2452419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2552419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2652419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2752419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2852419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2952419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
3052419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3152419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3252419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3352419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3452419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3552419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3652419Sjulian * OF SUCH DAMAGE.
3752419Sjulian *
3867506Sjulian * Author: Julian Elischer <julian@freebsd.org>
3952419Sjulian *
4052419Sjulian * $FreeBSD: head/sys/netgraph/ng_lmi.c 184205 2008-10-23 15:53:51Z des $
4152752Sjulian * $Whistle: ng_lmi.c,v 1.38 1999/11/01 09:24:52 julian Exp $
4252419Sjulian */
4352419Sjulian
4452419Sjulian/*
4552419Sjulian * This node performs the frame relay LMI protocol. It knows how
4652419Sjulian * to do ITU Annex A, ANSI Annex D, and "Group-of-Four" variants
4752419Sjulian * of the protocol.
4852419Sjulian *
4952419Sjulian * A specific protocol can be forced by connecting the corresponding
5052419Sjulian * hook to DLCI 0 or 1023 (as appropriate) of a frame relay link.
5152419Sjulian *
5252419Sjulian * Alternately, this node can do auto-detection of the LMI protocol
5352419Sjulian * by connecting hook "auto0" to DLCI 0 and "auto1023" to DLCI 1023.
5452419Sjulian */
5552419Sjulian
5652419Sjulian#include <sys/param.h>
5752419Sjulian#include <sys/systm.h>
5852419Sjulian#include <sys/errno.h>
5952419Sjulian#include <sys/kernel.h>
6052419Sjulian#include <sys/malloc.h>
6152419Sjulian#include <sys/mbuf.h>
6252419Sjulian#include <sys/syslog.h>
6352419Sjulian#include <netgraph/ng_message.h>
6452419Sjulian#include <netgraph/netgraph.h>
6552419Sjulian#include <netgraph/ng_lmi.h>
6652419Sjulian
6752419Sjulian/*
6852419Sjulian * Human readable names for LMI
6952419Sjulian */
7052419Sjulian#define NAME_ANNEXA	NG_LMI_HOOK_ANNEXA
7152419Sjulian#define NAME_ANNEXD	NG_LMI_HOOK_ANNEXD
7252419Sjulian#define NAME_GROUP4	NG_LMI_HOOK_GROUPOF4
7352419Sjulian#define NAME_NONE	"None"
7452419Sjulian
7552419Sjulian#define MAX_DLCIS	128
7652419Sjulian#define MAXDLCI		1023
7752419Sjulian
7852419Sjulian/*
7952419Sjulian * DLCI states
8052419Sjulian */
8152419Sjulian#define DLCI_NULL	0
8252419Sjulian#define DLCI_UP		1
8352419Sjulian#define DLCI_DOWN	2
8452419Sjulian
8552419Sjulian/*
8652419Sjulian * Any received LMI frame should be at least this long
8752419Sjulian */
8852419Sjulian#define LMI_MIN_LENGTH	8	/* XXX verify */
8952419Sjulian
9052419Sjulian/*
9152419Sjulian * Netgraph node methods and type descriptor
9252419Sjulian */
9352752Sjulianstatic ng_constructor_t	nglmi_constructor;
9452752Sjulianstatic ng_rcvmsg_t	nglmi_rcvmsg;
9570700Sjulianstatic ng_shutdown_t	nglmi_shutdown;
9652752Sjulianstatic ng_newhook_t	nglmi_newhook;
9752752Sjulianstatic ng_rcvdata_t	nglmi_rcvdata;
9852752Sjulianstatic ng_disconnect_t	nglmi_disconnect;
9970700Sjulianstatic int	nglmi_checkdata(hook_p hook, struct mbuf *m);
10052419Sjulian
10152419Sjulianstatic struct ng_type typestruct = {
102129823Sjulian	.version =	NG_ABI_VERSION,
103129823Sjulian	.name =		NG_LMI_NODE_TYPE,
104129823Sjulian	.constructor =	nglmi_constructor,
105129823Sjulian	.rcvmsg	=	nglmi_rcvmsg,
106129823Sjulian	.shutdown =	nglmi_shutdown,
107129823Sjulian	.newhook =	nglmi_newhook,
108129823Sjulian	.rcvdata =	nglmi_rcvdata,
109129823Sjulian	.disconnect =	nglmi_disconnect,
11052419Sjulian};
11152419SjulianNETGRAPH_INIT(lmi, &typestruct);
11252419Sjulian
11352419Sjulian/*
11452419Sjulian * Info and status per node
11552419Sjulian */
11652419Sjulianstruct nglmi_softc {
11752419Sjulian	node_p  node;		/* netgraph node */
11852419Sjulian	int     flags;		/* state */
11952419Sjulian	int     poll_count;	/* the count of times for autolmi */
12052419Sjulian	int     poll_state;	/* state of auto detect machine */
12152419Sjulian	u_char  remote_seq;	/* sequence number the remote sent */
12252419Sjulian	u_char  local_seq;	/* last sequence number we sent */
12352419Sjulian	u_char  protoID;	/* 9 for group of 4, 8 otherwise */
12452419Sjulian	u_long  seq_retries;	/* sent this how many time so far */
125140066Sglebius	struct	callout	handle;	/* see timeout(9) */
12652419Sjulian	int     liv_per_full;
12752419Sjulian	int     liv_rate;
12852419Sjulian	int     livs;
12952419Sjulian	int     need_full;
13052419Sjulian	hook_p  lmi_channel;	/* whatever we ended up using */
13152419Sjulian	hook_p  lmi_annexA;
13252419Sjulian	hook_p  lmi_annexD;
13352419Sjulian	hook_p  lmi_group4;
13452419Sjulian	hook_p  lmi_channel0;	/* auto-detect on DLCI 0 */
13552419Sjulian	hook_p  lmi_channel1023;/* auto-detect on DLCI 1023 */
13652419Sjulian	char   *protoname;	/* cache protocol name */
13752419Sjulian	u_char  dlci_state[MAXDLCI + 1];
13852419Sjulian	int     invalidx;	/* next dlci's to invalidate */
13952419Sjulian};
14052419Sjuliantypedef struct nglmi_softc *sc_p;
14152419Sjulian
14252419Sjulian/*
14352419Sjulian * Other internal functions
14452419Sjulian */
145140066Sglebiusstatic void	LMI_ticker(node_p node, hook_p hook, void *arg1, int arg2);
14652419Sjulianstatic void	nglmi_startup_fixed(sc_p sc, hook_p hook);
14752419Sjulianstatic void	nglmi_startup_auto(sc_p sc);
14852419Sjulianstatic void	nglmi_startup(sc_p sc);
14952419Sjulianstatic void	nglmi_inquire(sc_p sc, int full);
15052419Sjulianstatic void	ngauto_state_machine(sc_p sc);
15152419Sjulian
15252419Sjulian/*
15352419Sjulian * Values for 'flags' field
15452419Sjulian * NB: the SCF_CONNECTED flag is set if and only if the timer is running.
15552419Sjulian */
15652419Sjulian#define	SCF_CONNECTED	0x01	/* connected to something */
15752419Sjulian#define	SCF_AUTO	0x02	/* we are auto-detecting */
15852419Sjulian#define	SCF_FIXED	0x04	/* we are fixed from the start */
15952419Sjulian
16052419Sjulian#define	SCF_LMITYPE	0x18	/* mask for determining Annex mode */
16152419Sjulian#define	SCF_NOLMI	0x00	/* no LMI type selected yet */
16252419Sjulian#define	SCF_ANNEX_A	0x08	/* running annex A mode */
16352419Sjulian#define	SCF_ANNEX_D	0x10	/* running annex D mode */
16452419Sjulian#define	SCF_GROUP4	0x18	/* running group of 4 */
16552419Sjulian
16652419Sjulian#define SETLMITYPE(sc, annex)						\
16752419Sjuliando {									\
16852419Sjulian	(sc)->flags &= ~SCF_LMITYPE;					\
16952419Sjulian	(sc)->flags |= (annex);						\
17052419Sjulian} while (0)
17152419Sjulian
17252419Sjulian#define NOPROTO(sc) (((sc)->flags & SCF_LMITYPE) == SCF_NOLMI)
17352419Sjulian#define ANNEXA(sc) (((sc)->flags & SCF_LMITYPE) == SCF_ANNEX_A)
17452419Sjulian#define ANNEXD(sc) (((sc)->flags & SCF_LMITYPE) == SCF_ANNEX_D)
17552419Sjulian#define GROUP4(sc) (((sc)->flags & SCF_LMITYPE) == SCF_GROUP4)
17652419Sjulian
17752419Sjulian#define LMIPOLLSIZE	3
17852419Sjulian#define LMI_PATIENCE	8	/* declare all DLCI DOWN after N LMI failures */
17952419Sjulian
18052419Sjulian/*
18152419Sjulian * Node constructor
18252419Sjulian */
18352419Sjulianstatic int
18470700Sjuliannglmi_constructor(node_p node)
18552419Sjulian{
18652419Sjulian	sc_p sc;
18752419Sjulian
188184205Sdes	sc = malloc(sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO);
18952419Sjulian	if (sc == NULL)
19052419Sjulian		return (ENOMEM);
191140066Sglebius
19270784Sjulian	NG_NODE_SET_PRIVATE(node, sc);
193140066Sglebius	sc->node = node;
194140066Sglebius
195140066Sglebius	ng_callout_init(&sc->handle);
19652419Sjulian	sc->protoname = NAME_NONE;
19752419Sjulian	sc->liv_per_full = NG_LMI_SEQ_PER_FULL;	/* make this dynamic */
19852419Sjulian	sc->liv_rate = NG_LMI_KEEPALIVE_RATE;
19952419Sjulian	return (0);
20052419Sjulian}
20152419Sjulian
20252419Sjulian/*
20352419Sjulian * The LMI channel has a private pointer which is the same as the
20452419Sjulian * node private pointer. The debug channel has a NULL private pointer.
20552419Sjulian */
20652419Sjulianstatic int
20752419Sjuliannglmi_newhook(node_p node, hook_p hook, const char *name)
20852419Sjulian{
20970784Sjulian	sc_p sc = NG_NODE_PRIVATE(node);
21052419Sjulian
21152419Sjulian	if (strcmp(name, NG_LMI_HOOK_DEBUG) == 0) {
21270784Sjulian		NG_HOOK_SET_PRIVATE(hook, NULL);
21352419Sjulian		return (0);
21452419Sjulian	}
21552419Sjulian	if (sc->flags & SCF_CONNECTED) {
21652419Sjulian		/* already connected, return an error */
21752419Sjulian		return (EINVAL);
21852419Sjulian	}
21952419Sjulian	if (strcmp(name, NG_LMI_HOOK_ANNEXA) == 0) {
22052419Sjulian		sc->lmi_annexA = hook;
22170784Sjulian		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
22252419Sjulian		sc->protoID = 8;
22352419Sjulian		SETLMITYPE(sc, SCF_ANNEX_A);
22452419Sjulian		sc->protoname = NAME_ANNEXA;
22552419Sjulian		nglmi_startup_fixed(sc, hook);
22652419Sjulian	} else if (strcmp(name, NG_LMI_HOOK_ANNEXD) == 0) {
22752419Sjulian		sc->lmi_annexD = hook;
22870784Sjulian		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
22952419Sjulian		sc->protoID = 8;
23052419Sjulian		SETLMITYPE(sc, SCF_ANNEX_D);
23152419Sjulian		sc->protoname = NAME_ANNEXD;
23252419Sjulian		nglmi_startup_fixed(sc, hook);
23352419Sjulian	} else if (strcmp(name, NG_LMI_HOOK_GROUPOF4) == 0) {
23452419Sjulian		sc->lmi_group4 = hook;
23570784Sjulian		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
23652419Sjulian		sc->protoID = 9;
23752419Sjulian		SETLMITYPE(sc, SCF_GROUP4);
23852419Sjulian		sc->protoname = NAME_GROUP4;
23952419Sjulian		nglmi_startup_fixed(sc, hook);
24052419Sjulian	} else if (strcmp(name, NG_LMI_HOOK_AUTO0) == 0) {
24152419Sjulian		/* Note this, and if B is already installed, we're complete */
24252419Sjulian		sc->lmi_channel0 = hook;
24352419Sjulian		sc->protoname = NAME_NONE;
24470784Sjulian		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
24552419Sjulian		if (sc->lmi_channel1023)
24652419Sjulian			nglmi_startup_auto(sc);
24752419Sjulian	} else if (strcmp(name, NG_LMI_HOOK_AUTO1023) == 0) {
24852419Sjulian		/* Note this, and if A is already installed, we're complete */
24952419Sjulian		sc->lmi_channel1023 = hook;
25052419Sjulian		sc->protoname = NAME_NONE;
25170784Sjulian		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
25252419Sjulian		if (sc->lmi_channel0)
25352419Sjulian			nglmi_startup_auto(sc);
25452419Sjulian	} else
25552419Sjulian		return (EINVAL);		/* unknown hook */
25652419Sjulian	return (0);
25752419Sjulian}
25852419Sjulian
25952419Sjulian/*
26052419Sjulian * We have just attached to a live (we hope) node.
26152419Sjulian * Fire out a LMI inquiry, and then start up the timers.
26252419Sjulian */
26352419Sjulianstatic void
264140066SglebiusLMI_ticker(node_p node, hook_p hook, void *arg1, int arg2)
26552419Sjulian{
266140066Sglebius	sc_p sc = NG_NODE_PRIVATE(node);
26752419Sjulian
26852419Sjulian	if (sc->flags & SCF_AUTO) {
26952419Sjulian		ngauto_state_machine(sc);
270140066Sglebius		ng_callout(&sc->handle, node, NULL, NG_LMI_POLL_RATE * hz,
271140066Sglebius		    LMI_ticker, NULL, 0);
27252419Sjulian	} else {
27352419Sjulian		if (sc->livs++ >= sc->liv_per_full) {
27452419Sjulian			nglmi_inquire(sc, 1);
27552419Sjulian			/* sc->livs = 0; *//* do this when we get the answer! */
27652419Sjulian		} else {
27752419Sjulian			nglmi_inquire(sc, 0);
27852419Sjulian		}
279140066Sglebius		ng_callout(&sc->handle, node, NULL, sc->liv_rate * hz,
280140066Sglebius		    LMI_ticker, NULL, 0);
28152419Sjulian	}
28252419Sjulian}
28352419Sjulian
28452419Sjulianstatic void
28552419Sjuliannglmi_startup_fixed(sc_p sc, hook_p hook)
28652419Sjulian{
28752419Sjulian	sc->flags |= (SCF_FIXED | SCF_CONNECTED);
28852419Sjulian	sc->lmi_channel = hook;
28952419Sjulian	nglmi_startup(sc);
29052419Sjulian}
29152419Sjulian
29252419Sjulianstatic void
29352419Sjuliannglmi_startup_auto(sc_p sc)
29452419Sjulian{
29552419Sjulian	sc->flags |= (SCF_AUTO | SCF_CONNECTED);
29652419Sjulian	sc->poll_state = 0;	/* reset state machine */
29752419Sjulian	sc->poll_count = 0;
29852419Sjulian	nglmi_startup(sc);
29952419Sjulian}
30052419Sjulian
30152419Sjulianstatic void
30252419Sjuliannglmi_startup(sc_p sc)
30352419Sjulian{
30452419Sjulian	sc->remote_seq = 0;
30552419Sjulian	sc->local_seq = 1;
30652419Sjulian	sc->seq_retries = 0;
30752419Sjulian	sc->livs = sc->liv_per_full - 1;
30852419Sjulian	/* start off the ticker in 1 sec */
309140066Sglebius	ng_callout(&sc->handle, sc->node, NULL, hz, LMI_ticker, NULL, 0);
31052419Sjulian}
31152419Sjulian
31252419Sjulianstatic void
31352419Sjuliannglmi_inquire(sc_p sc, int full)
31452419Sjulian{
31552419Sjulian	struct mbuf *m;
316131108Sjulian	struct ng_tag_prio *ptag;
31752419Sjulian	char   *cptr, *start;
31852419Sjulian	int     error;
31952419Sjulian
32052419Sjulian	if (sc->lmi_channel == NULL)
32152419Sjulian		return;
322111119Simp	MGETHDR(m, M_DONTWAIT, MT_DATA);
32352419Sjulian	if (m == NULL) {
32452419Sjulian		log(LOG_ERR, "nglmi: unable to start up LMI processing\n");
32552419Sjulian		return;
32652419Sjulian	}
32753284Sarchie	m->m_pkthdr.rcvif = NULL;
328131108Sjulian
329131108Sjulian	/* Attach a tag to packet, marking it of link level state priority, so
330131108Sjulian	 * that device driver would put it in the beginning of queue */
331131108Sjulian
332131108Sjulian	ptag = (struct ng_tag_prio *)m_tag_alloc(NGM_GENERIC_COOKIE, NG_TAG_PRIO,
333131108Sjulian	    (sizeof(struct ng_tag_prio) - sizeof(struct m_tag)), M_NOWAIT);
334131108Sjulian	if (ptag != NULL) {	/* if it failed, well, it was optional anyhow */
335131108Sjulian		ptag->priority = NG_PRIO_LINKSTATE;
336131108Sjulian		ptag->discardability = -1;
337131108Sjulian		m_tag_prepend(m, &ptag->tag);
33852419Sjulian	}
339131108Sjulian
34052419Sjulian	m->m_data += 4;		/* leave some room for a header */
34152419Sjulian	cptr = start = mtod(m, char *);
34252419Sjulian	/* add in the header for an LMI inquiry. */
34352419Sjulian	*cptr++ = 0x03;		/* UI frame */
34452419Sjulian	if (GROUP4(sc))
34552419Sjulian		*cptr++ = 0x09;	/* proto discriminator */
34652419Sjulian	else
34752419Sjulian		*cptr++ = 0x08;	/* proto discriminator */
34852419Sjulian	*cptr++ = 0x00;		/* call reference */
34952419Sjulian	*cptr++ = 0x75;		/* inquiry */
35052419Sjulian
351140365Srik	/* If we are Annex-D, add locking shift to codeset 5. */
35252419Sjulian	if (ANNEXD(sc))
353140358Srik		*cptr++ = 0x95;	/* locking shift */
35452419Sjulian	/* Add a request type */
35552419Sjulian	if (ANNEXA(sc))
35652419Sjulian		*cptr++ = 0x51;	/* report type */
35752419Sjulian	else
35852419Sjulian		*cptr++ = 0x01;	/* report type */
35952419Sjulian	*cptr++ = 0x01;		/* size = 1 */
36052419Sjulian	if (full)
36152419Sjulian		*cptr++ = 0x00;	/* full */
36252419Sjulian	else
36352419Sjulian		*cptr++ = 0x01;	/* partial */
36452419Sjulian
36552419Sjulian	/* Add a link verification IE */
36652419Sjulian	if (ANNEXA(sc))
36752419Sjulian		*cptr++ = 0x53;	/* verification IE */
36852419Sjulian	else
36952419Sjulian		*cptr++ = 0x03;	/* verification IE */
37052419Sjulian	*cptr++ = 0x02;		/* 2 extra bytes */
37152419Sjulian	*cptr++ = sc->local_seq;
37252419Sjulian	*cptr++ = sc->remote_seq;
37352419Sjulian	sc->seq_retries++;
37452419Sjulian
37552419Sjulian	/* Send it */
37652419Sjulian	m->m_len = m->m_pkthdr.len = cptr - start;
377131108Sjulian	NG_SEND_DATA_ONLY(error, sc->lmi_channel, m);
37852419Sjulian
37952419Sjulian	/* If we've been sending requests for long enough, and there has
38052419Sjulian	 * been no response, then mark as DOWN, any DLCIs that are UP. */
38152419Sjulian	if (sc->seq_retries == LMI_PATIENCE) {
38252419Sjulian		int     count;
38352419Sjulian
38452419Sjulian		for (count = 0; count < MAXDLCI; count++)
38552419Sjulian			if (sc->dlci_state[count] == DLCI_UP)
38652419Sjulian				sc->dlci_state[count] = DLCI_DOWN;
38752419Sjulian	}
38852419Sjulian}
38952419Sjulian
39052419Sjulian/*
39152419Sjulian * State machine for LMI auto-detect. The transitions are ordered
39252419Sjulian * to try the more likely possibilities first.
39352419Sjulian */
39452419Sjulianstatic void
39552419Sjulianngauto_state_machine(sc_p sc)
39652419Sjulian{
39752419Sjulian	if ((sc->poll_count <= 0) || (sc->poll_count > LMIPOLLSIZE)) {
39852419Sjulian		/* time to change states in the auto probe machine */
39952419Sjulian		/* capture wild values of poll_count while we are at it */
40052419Sjulian		sc->poll_count = LMIPOLLSIZE;
40152419Sjulian		sc->poll_state++;
40252419Sjulian	}
40352419Sjulian	switch (sc->poll_state) {
40452419Sjulian	case 7:
40552419Sjulian		log(LOG_WARNING, "nglmi: no response from exchange\n");
40652419Sjulian	default:		/* capture bad states */
40752419Sjulian		sc->poll_state = 1;
40852419Sjulian	case 1:
40952419Sjulian		sc->lmi_channel = sc->lmi_channel0;
41052419Sjulian		SETLMITYPE(sc, SCF_ANNEX_D);
41152419Sjulian		break;
41252419Sjulian	case 2:
41352419Sjulian		sc->lmi_channel = sc->lmi_channel1023;
41452419Sjulian		SETLMITYPE(sc, SCF_ANNEX_D);
41552419Sjulian		break;
41652419Sjulian	case 3:
41752419Sjulian		sc->lmi_channel = sc->lmi_channel0;
41852419Sjulian		SETLMITYPE(sc, SCF_ANNEX_A);
41952419Sjulian		break;
42052419Sjulian	case 4:
42152419Sjulian		sc->lmi_channel = sc->lmi_channel1023;
42252419Sjulian		SETLMITYPE(sc, SCF_GROUP4);
42352419Sjulian		break;
42452419Sjulian	case 5:
42552419Sjulian		sc->lmi_channel = sc->lmi_channel1023;
42652419Sjulian		SETLMITYPE(sc, SCF_ANNEX_A);
42752419Sjulian		break;
42852419Sjulian	case 6:
42952419Sjulian		sc->lmi_channel = sc->lmi_channel0;
43052419Sjulian		SETLMITYPE(sc, SCF_GROUP4);
43152419Sjulian		break;
43252419Sjulian	}
43352419Sjulian
43452419Sjulian	/* send an inquirey encoded appropriatly */
43552419Sjulian	nglmi_inquire(sc, 0);
43652419Sjulian	sc->poll_count--;
43752419Sjulian}
43852419Sjulian
43952419Sjulian/*
44052419Sjulian * Receive a netgraph control message.
44152419Sjulian */
44252419Sjulianstatic int
44370700Sjuliannglmi_rcvmsg(node_p node, item_p item, hook_p lasthook)
44452419Sjulian{
44570784Sjulian	sc_p    sc = NG_NODE_PRIVATE(node);
44670159Sjulian	struct ng_mesg *resp = NULL;
44752419Sjulian	int     error = 0;
44870700Sjulian	struct ng_mesg *msg;
44952419Sjulian
45070700Sjulian	NGI_GET_MSG(item, msg);
45152419Sjulian	switch (msg->header.typecookie) {
45252419Sjulian	case NGM_GENERIC_COOKIE:
45352419Sjulian		switch (msg->header.cmd) {
45452419Sjulian		case NGM_TEXT_STATUS:
45552419Sjulian		    {
45652419Sjulian			char   *arg;
45752419Sjulian			int     pos, count;
45852419Sjulian
45970159Sjulian			NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
46070159Sjulian			if (resp == NULL) {
46152419Sjulian				error = ENOMEM;
46252419Sjulian				break;
46352419Sjulian			}
46470159Sjulian			arg = resp->data;
46552419Sjulian			pos = sprintf(arg, "protocol %s ", sc->protoname);
46652419Sjulian			if (sc->flags & SCF_FIXED)
46752419Sjulian				pos += sprintf(arg + pos, "fixed\n");
46852419Sjulian			else if (sc->flags & SCF_AUTO)
46952419Sjulian				pos += sprintf(arg + pos, "auto-detecting\n");
47052419Sjulian			else
47152419Sjulian				pos += sprintf(arg + pos, "auto on dlci %d\n",
47252419Sjulian				    (sc->lmi_channel == sc->lmi_channel0) ?
47352419Sjulian				    0 : 1023);
47452419Sjulian			pos += sprintf(arg + pos,
47552419Sjulian			    "keepalive period: %d seconds\n", sc->liv_rate);
47652419Sjulian			pos += sprintf(arg + pos,
47752419Sjulian			    "unacknowledged keepalives: %ld\n",
47852419Sjulian			    sc->seq_retries);
47952419Sjulian			for (count = 0;
48052419Sjulian			     ((count <= MAXDLCI)
48152419Sjulian			      && (pos < (NG_TEXTRESPONSE - 20)));
48252419Sjulian			     count++) {
48352419Sjulian				if (sc->dlci_state[count]) {
48452419Sjulian					pos += sprintf(arg + pos,
48552419Sjulian					       "dlci %d %s\n", count,
48652419Sjulian					       (sc->dlci_state[count]
48752419Sjulian					== DLCI_UP) ? "up" : "down");
48852419Sjulian				}
48952419Sjulian			}
49070159Sjulian			resp->header.arglen = pos + 1;
49152419Sjulian			break;
49252419Sjulian		    }
49352419Sjulian		default:
49452419Sjulian			error = EINVAL;
49552419Sjulian			break;
49652419Sjulian		}
49752419Sjulian		break;
49852419Sjulian	case NGM_LMI_COOKIE:
49952419Sjulian		switch (msg->header.cmd) {
50052419Sjulian		case NGM_LMI_GET_STATUS:
50152419Sjulian		    {
50252419Sjulian			struct nglmistat *stat;
50352419Sjulian			int k;
50452419Sjulian
50570159Sjulian			NG_MKRESPONSE(resp, msg, sizeof(*stat), M_NOWAIT);
50670159Sjulian			if (!resp) {
50752419Sjulian				error = ENOMEM;
50852419Sjulian				break;
50952419Sjulian			}
51070159Sjulian			stat = (struct nglmistat *) resp->data;
51152419Sjulian			strncpy(stat->proto,
51252419Sjulian			     sc->protoname, sizeof(stat->proto) - 1);
51352419Sjulian			strncpy(stat->hook,
51452419Sjulian			      sc->protoname, sizeof(stat->hook) - 1);
51552419Sjulian			stat->autod = !!(sc->flags & SCF_AUTO);
51652419Sjulian			stat->fixed = !!(sc->flags & SCF_FIXED);
51752419Sjulian			for (k = 0; k <= MAXDLCI; k++) {
51852419Sjulian				switch (sc->dlci_state[k]) {
51952419Sjulian				case DLCI_UP:
52052419Sjulian					stat->up[k / 8] |= (1 << (k % 8));
52152419Sjulian					/* fall through */
52252419Sjulian				case DLCI_DOWN:
52352419Sjulian					stat->seen[k / 8] |= (1 << (k % 8));
52452419Sjulian					break;
52552419Sjulian				}
52652419Sjulian			}
52752419Sjulian			break;
52852419Sjulian		    }
52952419Sjulian		default:
53052419Sjulian			error = EINVAL;
53152419Sjulian			break;
53252419Sjulian		}
53352419Sjulian		break;
53452419Sjulian	default:
53552419Sjulian		error = EINVAL;
53652419Sjulian		break;
53752419Sjulian	}
53870159Sjulian
53970700Sjulian	NG_RESPOND_MSG(error, node, item, resp);
54070700Sjulian	NG_FREE_MSG(msg);
54152419Sjulian	return (error);
54252419Sjulian}
54352419Sjulian
54452419Sjulian#define STEPBY(stepsize)			\
54552419Sjulian	do {					\
54652419Sjulian		packetlen -= (stepsize);	\
54752419Sjulian		data += (stepsize);		\
54852419Sjulian	} while (0)
54952419Sjulian
55052419Sjulian/*
55152419Sjulian * receive data, and use it to update our status.
55252419Sjulian * Anything coming in on the debug port is discarded.
55352419Sjulian */
55452419Sjulianstatic int
55570700Sjuliannglmi_rcvdata(hook_p hook, item_p item)
55652419Sjulian{
55770784Sjulian	sc_p    sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
55897897Sarchie	const	u_char *data;
55952419Sjulian	unsigned short dlci;
56052419Sjulian	u_short packetlen;
56152419Sjulian	int     resptype_seen = 0;
56270700Sjulian	struct mbuf *m;
56352419Sjulian
56470700Sjulian	NGI_GET_M(item, m);
56570700Sjulian	NG_FREE_ITEM(item);
56670784Sjulian	if (NG_HOOK_PRIVATE(hook) == NULL) {
56752419Sjulian		goto drop;
56852419Sjulian	}
569147163Sru	packetlen = m->m_len;
57052419Sjulian
57152419Sjulian	/* XXX what if it's more than 1 mbuf? */
57252419Sjulian	if ((packetlen > MHLEN) && !(m->m_flags & M_EXT)) {
57352419Sjulian		log(LOG_WARNING, "nglmi: packetlen (%d) too big\n", packetlen);
57452419Sjulian		goto drop;
57552419Sjulian	}
57652539Sjulian	if (m->m_len < packetlen && (m = m_pullup(m, packetlen)) == NULL) {
57752539Sjulian		log(LOG_WARNING,
57852539Sjulian		    "nglmi: m_pullup failed for %d bytes\n", packetlen);
57952419Sjulian		return (0);
58052419Sjulian	}
58170700Sjulian	if (nglmi_checkdata(hook, m) == 0)
58252419Sjulian		return (0);
58352419Sjulian
58452419Sjulian	/* pass the first 4 bytes (already checked in the nglmi_checkdata()) */
58597897Sarchie	data = mtod(m, const u_char *);
58652419Sjulian	STEPBY(4);
58752419Sjulian
58852419Sjulian	/* Now check if there is a 'locking shift'. This is only seen in
58952419Sjulian	 * Annex D frames. don't bother checking, we already did that. Don't
59052419Sjulian	 * increment immediatly as it might not be there. */
59152419Sjulian	if (ANNEXD(sc))
59252419Sjulian		STEPBY(1);
59352419Sjulian
59452419Sjulian	/* If we get this far we should consider that it is a legitimate
59552419Sjulian	 * frame and we know what it is. */
59652419Sjulian	if (sc->flags & SCF_AUTO) {
59752419Sjulian		/* note the hook that this valid channel came from and drop
59852419Sjulian		 * out of auto probe mode. */
59952419Sjulian		if (ANNEXA(sc))
60052419Sjulian			sc->protoname = NAME_ANNEXA;
60152419Sjulian		else if (ANNEXD(sc))
60252419Sjulian			sc->protoname = NAME_ANNEXD;
60352419Sjulian		else if (GROUP4(sc))
60452419Sjulian			sc->protoname = NAME_GROUP4;
60552419Sjulian		else {
60652419Sjulian			log(LOG_ERR, "nglmi: No known type\n");
60752419Sjulian			goto drop;
60852419Sjulian		}
60952419Sjulian		sc->lmi_channel = hook;
61052419Sjulian		sc->flags &= ~SCF_AUTO;
61152419Sjulian		log(LOG_INFO, "nglmi: auto-detected %s LMI on DLCI %d\n",
61252419Sjulian		    sc->protoname, hook == sc->lmi_channel0 ? 0 : 1023);
61352419Sjulian	}
61452419Sjulian
61552419Sjulian	/* While there is more data in the status packet, keep processing
61652419Sjulian	 * status items. First make sure there is enough data for the
61752419Sjulian	 * segment descriptor's length field. */
61852419Sjulian	while (packetlen >= 2) {
61952419Sjulian		u_int   segtype = data[0];
62052419Sjulian		u_int   segsize = data[1];
62152419Sjulian
62252419Sjulian		/* Now that we know how long it claims to be, make sure
62352419Sjulian		 * there is enough data for the next seg. */
62452419Sjulian		if (packetlen < segsize + 2)
62552419Sjulian			break;
62652419Sjulian		switch (segtype) {
62752419Sjulian		case 0x01:
62852419Sjulian		case 0x51:
62952419Sjulian			if (resptype_seen) {
63052419Sjulian				log(LOG_WARNING, "nglmi: dup MSGTYPE\n");
63152419Sjulian				goto nextIE;
63252419Sjulian			}
63352419Sjulian			resptype_seen++;
63452419Sjulian			/* The remote end tells us what kind of response
63552419Sjulian			 * this is. Only expect a type 0 or 1. if we are a
63652419Sjulian			 * full status, invalidate a few DLCIs just to see
63752419Sjulian			 * that they are still ok. */
63852419Sjulian			if (segsize != 1)
63952419Sjulian				goto nextIE;
64052419Sjulian			switch (data[2]) {
64152419Sjulian			case 1:
64252419Sjulian				/* partial status, do no extra processing */
64352419Sjulian				break;
64452419Sjulian			case 0:
64552419Sjulian			    {
64652419Sjulian				int     count = 0;
64752419Sjulian				int     idx = sc->invalidx;
64852419Sjulian
64952419Sjulian				for (count = 0; count < 10; count++) {
65052419Sjulian					if (idx > MAXDLCI)
65152419Sjulian						idx = 0;
65252419Sjulian					if (sc->dlci_state[idx] == DLCI_UP)
65352419Sjulian						sc->dlci_state[idx] = DLCI_DOWN;
65452419Sjulian					idx++;
65552419Sjulian				}
65652419Sjulian				sc->invalidx = idx;
65752419Sjulian				/* we got and we wanted one. relax
65852419Sjulian				 * now.. but don't reset to 0 if it
65952419Sjulian				 * was unrequested. */
66052419Sjulian				if (sc->livs > sc->liv_per_full)
66152419Sjulian					sc->livs = 0;
66252419Sjulian				break;
66352419Sjulian			    }
66452419Sjulian			}
66552419Sjulian			break;
66652419Sjulian		case 0x03:
66752419Sjulian		case 0x53:
66852419Sjulian			/* The remote tells us what it thinks the sequence
66952419Sjulian			 * numbers are. If it's not size 2, it must be a
67052419Sjulian			 * duplicate to have gotten this far, skip it. */
67152419Sjulian			if (segsize != 2)
67252419Sjulian				goto nextIE;
67352419Sjulian			sc->remote_seq = data[2];
67452419Sjulian			if (sc->local_seq == data[3]) {
67552419Sjulian				sc->local_seq++;
67652419Sjulian				sc->seq_retries = 0;
67752419Sjulian				/* Note that all 3 Frame protocols seem to
67852419Sjulian				 * not like 0 as a sequence number. */
67952419Sjulian				if (sc->local_seq == 0)
68052419Sjulian					sc->local_seq = 1;
68152419Sjulian			}
68252419Sjulian			break;
68352419Sjulian		case 0x07:
68452419Sjulian		case 0x57:
68552419Sjulian			/* The remote tells us about a DLCI that it knows
68652419Sjulian			 * about. There may be many of these in a single
68752419Sjulian			 * status response */
68852419Sjulian			switch (segsize) {
68952419Sjulian			case 6:/* only on 'group of 4' */
69052419Sjulian				dlci = ((u_short) data[2] & 0xff) << 8;
69152419Sjulian				dlci |= (data[3] & 0xff);
69252419Sjulian				if ((dlci < 1024) && (dlci > 0)) {
69352419Sjulian				  /* XXX */
69452419Sjulian				}
69552419Sjulian				break;
69652419Sjulian			case 3:
69752419Sjulian				dlci = ((u_short) data[2] & 0x3f) << 4;
69852419Sjulian				dlci |= ((data[3] & 0x78) >> 3);
69952419Sjulian				if ((dlci < 1024) && (dlci > 0)) {
70052419Sjulian					/* set up the bottom half of the
70152419Sjulian					 * support for that dlci if it's not
70252419Sjulian					 * already been done */
70352419Sjulian					/* store this information somewhere */
70452419Sjulian				}
70552419Sjulian				break;
70652419Sjulian			default:
70752419Sjulian				goto nextIE;
70852419Sjulian			}
70952419Sjulian			if (sc->dlci_state[dlci] != DLCI_UP) {
71052419Sjulian				/* bring new DLCI to life */
71152419Sjulian				/* may do more here some day */
71252419Sjulian				if (sc->dlci_state[dlci] != DLCI_DOWN)
71352419Sjulian					log(LOG_INFO,
71452419Sjulian					    "nglmi: DLCI %d became active\n",
71552419Sjulian					    dlci);
71652419Sjulian				sc->dlci_state[dlci] = DLCI_UP;
71752419Sjulian			}
71852419Sjulian			break;
71952419Sjulian		}
72052419SjuliannextIE:
72152419Sjulian		STEPBY(segsize + 2);
72252419Sjulian	}
72370700Sjulian	NG_FREE_M(m);
72452419Sjulian	return (0);
72552419Sjulian
72652419Sjuliandrop:
72770700Sjulian	NG_FREE_M(m);
72852419Sjulian	return (EINVAL);
72952419Sjulian}
73052419Sjulian
73152419Sjulian/*
73252419Sjulian * Check that a packet is entirely kosha.
73352419Sjulian * return 1 of ok, and 0 if not.
73452419Sjulian * All data is discarded if a 0 is returned.
73552419Sjulian */
73652419Sjulianstatic int
73770700Sjuliannglmi_checkdata(hook_p hook, struct mbuf *m)
73852419Sjulian{
73970784Sjulian	sc_p    sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
74097897Sarchie	const	u_char *data;
74152419Sjulian	u_short packetlen;
74252419Sjulian	unsigned short dlci;
74352419Sjulian	u_char  type;
74452419Sjulian	u_char  nextbyte;
74552419Sjulian	int     seq_seen = 0;
74652419Sjulian	int     resptype_seen = 0;	/* 0 , 1 (partial) or 2 (full) */
74752419Sjulian	int     highest_dlci = 0;
74852419Sjulian
749147163Sru	packetlen = m->m_len;
75097897Sarchie	data = mtod(m, const u_char *);
75152419Sjulian	if (*data != 0x03) {
75252419Sjulian		log(LOG_WARNING, "nglmi: unexpected value in LMI(%d)\n", 1);
75352419Sjulian		goto reject;
75452419Sjulian	}
75552419Sjulian	STEPBY(1);
75652419Sjulian
75752419Sjulian	/* look at the protocol ID */
75852419Sjulian	nextbyte = *data;
75952419Sjulian	if (sc->flags & SCF_AUTO) {
76052419Sjulian		SETLMITYPE(sc, SCF_NOLMI);	/* start with a clean slate */
76152419Sjulian		switch (nextbyte) {
76252419Sjulian		case 0x8:
76352419Sjulian			sc->protoID = 8;
76452419Sjulian			break;
76552419Sjulian		case 0x9:
76652419Sjulian			SETLMITYPE(sc, SCF_GROUP4);
76752419Sjulian			sc->protoID = 9;
76852419Sjulian			break;
76952419Sjulian		default:
77052419Sjulian			log(LOG_WARNING, "nglmi: bad Protocol ID(%d)\n",
77152419Sjulian			    (int) nextbyte);
77252419Sjulian			goto reject;
77352419Sjulian		}
77452419Sjulian	} else {
77552419Sjulian		if (nextbyte != sc->protoID) {
77652419Sjulian			log(LOG_WARNING, "nglmi: unexpected Protocol ID(%d)\n",
77752419Sjulian			    (int) nextbyte);
77852419Sjulian			goto reject;
77952419Sjulian		}
78052419Sjulian	}
78152419Sjulian	STEPBY(1);
78252419Sjulian
78352419Sjulian	/* check call reference (always null in non ISDN frame relay) */
78452419Sjulian	if (*data != 0x00) {
78552419Sjulian		log(LOG_WARNING, "nglmi: unexpected Call Reference (0x%x)\n",
78652419Sjulian		    data[-1]);
78752419Sjulian		goto reject;
78852419Sjulian	}
78952419Sjulian	STEPBY(1);
79052419Sjulian
79152419Sjulian	/* check message type */
79252419Sjulian	switch ((type = *data)) {
79352419Sjulian	case 0x75:		/* Status enquiry */
79452419Sjulian		log(LOG_WARNING, "nglmi: unexpected message type(0x%x)\n",
79552419Sjulian		    data[-1]);
79652419Sjulian		goto reject;
79752419Sjulian	case 0x7D:		/* Status message */
79852419Sjulian		break;
79952419Sjulian	default:
80052419Sjulian		log(LOG_WARNING,
80152419Sjulian		    "nglmi: unexpected msg type(0x%x) \n", (int) type);
80252419Sjulian		goto reject;
80352419Sjulian	}
80452419Sjulian	STEPBY(1);
80552419Sjulian
80652419Sjulian	/* Now check if there is a 'locking shift'. This is only seen in
80752419Sjulian	 * Annex D frames. Don't increment immediately as it might not be
80852419Sjulian	 * there. */
80952419Sjulian	nextbyte = *data;
81052419Sjulian	if (sc->flags & SCF_AUTO) {
81152419Sjulian		if (!(GROUP4(sc))) {
81252419Sjulian			if (nextbyte == 0x95) {
81352419Sjulian				SETLMITYPE(sc, SCF_ANNEX_D);
81452419Sjulian				STEPBY(1);
81552419Sjulian			} else
81652419Sjulian				SETLMITYPE(sc, SCF_ANNEX_A);
81752419Sjulian		} else if (nextbyte == 0x95) {
81852419Sjulian			log(LOG_WARNING, "nglmi: locking shift seen in G4\n");
81952419Sjulian			goto reject;
82052419Sjulian		}
82152419Sjulian	} else {
82252419Sjulian		if (ANNEXD(sc)) {
82352419Sjulian			if (*data == 0x95)
82452419Sjulian				STEPBY(1);
82552419Sjulian			else {
82652419Sjulian				log(LOG_WARNING,
82752419Sjulian				    "nglmi: locking shift missing\n");
82852419Sjulian				goto reject;
82952419Sjulian			}
83052419Sjulian		} else if (*data == 0x95) {
83152419Sjulian			log(LOG_WARNING, "nglmi: locking shift seen\n");
83252419Sjulian			goto reject;
83352419Sjulian		}
83452419Sjulian	}
83552419Sjulian
83652419Sjulian	/* While there is more data in the status packet, keep processing
83752419Sjulian	 * status items. First make sure there is enough data for the
83852419Sjulian	 * segment descriptor's length field. */
83952419Sjulian	while (packetlen >= 2) {
84052419Sjulian		u_int   segtype = data[0];
84152419Sjulian		u_int   segsize = data[1];
84252419Sjulian
84352419Sjulian		/* Now that we know how long it claims to be, make sure
84452419Sjulian		 * there is enough data for the next seg. */
84552419Sjulian		if (packetlen < (segsize + 2)) {
84652419Sjulian			log(LOG_WARNING, "nglmi: IE longer than packet\n");
84752419Sjulian			break;
84852419Sjulian		}
84952419Sjulian		switch (segtype) {
85052419Sjulian		case 0x01:
85152419Sjulian		case 0x51:
85252419Sjulian			/* According to MCI's HP analyser, we should just
85352419Sjulian			 * ignore if there is mor ethan one of these (?). */
85452419Sjulian			if (resptype_seen) {
85552419Sjulian				log(LOG_WARNING, "nglmi: dup MSGTYPE\n");
85652419Sjulian				goto nextIE;
85752419Sjulian			}
85852419Sjulian			if (segsize != 1) {
85952419Sjulian				log(LOG_WARNING, "nglmi: MSGTYPE wrong size\n");
86052419Sjulian				goto reject;
86152419Sjulian			}
86252419Sjulian			/* The remote end tells us what kind of response
86352419Sjulian			 * this is. Only expect a type 0 or 1. if it was a
86452419Sjulian			 * full (type 0) check we just asked for a type
86552419Sjulian			 * full. */
86652419Sjulian			switch (data[2]) {
86752419Sjulian			case 1:/* partial */
86852419Sjulian				if (sc->livs > sc->liv_per_full) {
86952419Sjulian					log(LOG_WARNING,
87052419Sjulian					  "nglmi: LIV when FULL expected\n");
87152419Sjulian					goto reject;	/* need full */
87252419Sjulian				}
87352419Sjulian				resptype_seen = 1;
87452419Sjulian				break;
87552419Sjulian			case 0:/* full */
87652419Sjulian				/* Full response is always acceptable */
87752419Sjulian				resptype_seen = 2;
87852419Sjulian				break;
87952419Sjulian			default:
88052419Sjulian				log(LOG_WARNING,
88152419Sjulian				 "nglmi: Unknown report type %d\n", data[2]);
88252419Sjulian				goto reject;
88352419Sjulian			}
88452419Sjulian			break;
88552419Sjulian		case 0x03:
88652419Sjulian		case 0x53:
88752419Sjulian			/* The remote tells us what it thinks the sequence
88852419Sjulian			 * numbers are. I would have thought that there
88952419Sjulian			 * needs to be one and only one of these, but MCI
89052419Sjulian			 * want us to just ignore extras. (?) */
89152419Sjulian			if (resptype_seen == 0) {
89252419Sjulian				log(LOG_WARNING, "nglmi: no TYPE before SEQ\n");
89352419Sjulian				goto reject;
89452419Sjulian			}
89552419Sjulian			if (seq_seen != 0)	/* already seen seq numbers */
89652419Sjulian				goto nextIE;
89752419Sjulian			if (segsize != 2) {
89852419Sjulian				log(LOG_WARNING, "nglmi: bad SEQ sts size\n");
89952419Sjulian				goto reject;
90052419Sjulian			}
90152419Sjulian			if (sc->local_seq != data[3]) {
90252419Sjulian				log(LOG_WARNING, "nglmi: unexpected SEQ\n");
90352419Sjulian				goto reject;
90452419Sjulian			}
90552419Sjulian			seq_seen = 1;
90652419Sjulian			break;
90752419Sjulian		case 0x07:
90852419Sjulian		case 0x57:
90952419Sjulian			/* The remote tells us about a DLCI that it knows
91052419Sjulian			 * about. There may be many of these in a single
91152419Sjulian			 * status response */
91252419Sjulian			if (seq_seen != 1) {	/* already seen seq numbers? */
91352419Sjulian				log(LOG_WARNING,
91452419Sjulian				    "nglmi: No sequence before DLCI\n");
91552419Sjulian				goto reject;
91652419Sjulian			}
91752419Sjulian			if (resptype_seen != 2) {	/* must be full */
91852419Sjulian				log(LOG_WARNING,
91952419Sjulian				    "nglmi: No resp type before DLCI\n");
92052419Sjulian				goto reject;
92152419Sjulian			}
92252419Sjulian			if (GROUP4(sc)) {
92352419Sjulian				if (segsize != 6) {
92452419Sjulian					log(LOG_WARNING,
92552419Sjulian					    "nglmi: wrong IE segsize\n");
92652419Sjulian					goto reject;
92752419Sjulian				}
92852419Sjulian				dlci = ((u_short) data[2] & 0xff) << 8;
92952419Sjulian				dlci |= (data[3] & 0xff);
93052419Sjulian			} else {
93152419Sjulian				if (segsize != 3) {
93252419Sjulian					log(LOG_WARNING,
93352419Sjulian					    "nglmi: DLCI headersize of %d"
93452419Sjulian					    " not supported\n", segsize - 1);
93552419Sjulian					goto reject;
93652419Sjulian				}
93752419Sjulian				dlci = ((u_short) data[2] & 0x3f) << 4;
93852419Sjulian				dlci |= ((data[3] & 0x78) >> 3);
93952419Sjulian			}
94052419Sjulian			/* async can only have one of these */
94152419Sjulian#if 0				/* async not yet accepted */
94252419Sjulian			if (async && highest_dlci) {
94352419Sjulian				log(LOG_WARNING,
94452419Sjulian				    "nglmi: Async with > 1 DLCI\n");
94552419Sjulian				goto reject;
94652419Sjulian			}
94752419Sjulian#endif
94852419Sjulian			/* Annex D says these will always be Ascending, but
94952419Sjulian			 * the HP test for G4 says we should accept
95052419Sjulian			 * duplicates, so for now allow that. ( <= vs. < ) */
95152419Sjulian#if 0
95252419Sjulian			/* MCI tests want us to accept out of order for AnxD */
95352419Sjulian			if ((!GROUP4(sc)) && (dlci < highest_dlci)) {
95452419Sjulian				/* duplicate or mis-ordered dlci */
95552419Sjulian				/* (spec says they will increase in number) */
95652419Sjulian				log(LOG_WARNING, "nglmi: DLCI out of order\n");
95752419Sjulian				goto reject;
95852419Sjulian			}
95952419Sjulian#endif
96052419Sjulian			if (dlci > 1023) {
96152419Sjulian				log(LOG_WARNING, "nglmi: DLCI out of range\n");
96252419Sjulian				goto reject;
96352419Sjulian			}
96452419Sjulian			highest_dlci = dlci;
96552419Sjulian			break;
96652419Sjulian		default:
96752419Sjulian			log(LOG_WARNING,
96852419Sjulian			    "nglmi: unknown LMI segment type %d\n", segtype);
96952419Sjulian		}
97052419SjuliannextIE:
97152419Sjulian		STEPBY(segsize + 2);
97252419Sjulian	}
97352419Sjulian	if (packetlen != 0) {	/* partial junk at end? */
97452419Sjulian		log(LOG_WARNING,
97552419Sjulian		    "nglmi: %d bytes extra at end of packet\n", packetlen);
97652419Sjulian		goto print;
97752419Sjulian	}
97852419Sjulian	if (resptype_seen == 0) {
97952419Sjulian		log(LOG_WARNING, "nglmi: No response type seen\n");
98052419Sjulian		goto reject;	/* had no response type */
98152419Sjulian	}
98252419Sjulian	if (seq_seen == 0) {
98352419Sjulian		log(LOG_WARNING, "nglmi: No sequence numbers seen\n");
98452419Sjulian		goto reject;	/* had no sequence numbers */
98552419Sjulian	}
98652419Sjulian	return (1);
98752419Sjulian
98852419Sjulianprint:
98952419Sjulian	{
99052419Sjulian		int     i, j, k, pos;
99152419Sjulian		char    buf[100];
99252419Sjulian		int     loc;
99397897Sarchie		const	u_char *bp = mtod(m, const u_char *);
99452419Sjulian
99552419Sjulian		k = i = 0;
996147163Sru		loc = (m->m_len - packetlen);
99752419Sjulian		log(LOG_WARNING, "nglmi: error at location %d\n", loc);
998147163Sru		while (k < m->m_len) {
99952419Sjulian			pos = 0;
100052419Sjulian			j = 0;
1001147163Sru			while ((j++ < 16) && k < m->m_len) {
100252419Sjulian				pos += sprintf(buf + pos, "%c%02x",
100352419Sjulian					       ((loc == k) ? '>' : ' '),
100452419Sjulian					       bp[k]);
100552419Sjulian				k++;
100652419Sjulian			}
100752419Sjulian			if (i == 0)
100852419Sjulian				log(LOG_WARNING, "nglmi: packet data:%s\n", buf);
100952419Sjulian			else
101052419Sjulian				log(LOG_WARNING, "%04d              :%s\n", k, buf);
101152419Sjulian			i++;
101252419Sjulian		}
101352419Sjulian	}
101452419Sjulian	return (1);
101552419Sjulianreject:
101652419Sjulian	{
101752419Sjulian		int     i, j, k, pos;
101852419Sjulian		char    buf[100];
101952419Sjulian		int     loc;
102097897Sarchie		const	u_char *bp = mtod(m, const u_char *);
102152419Sjulian
102252419Sjulian		k = i = 0;
1023147163Sru		loc = (m->m_len - packetlen);
102452419Sjulian		log(LOG_WARNING, "nglmi: error at location %d\n", loc);
1025147163Sru		while (k < m->m_len) {
102652419Sjulian			pos = 0;
102752419Sjulian			j = 0;
1028147163Sru			while ((j++ < 16) && k < m->m_len) {
102952419Sjulian				pos += sprintf(buf + pos, "%c%02x",
103052419Sjulian					       ((loc == k) ? '>' : ' '),
103152419Sjulian					       bp[k]);
103252419Sjulian				k++;
103352419Sjulian			}
103452419Sjulian			if (i == 0)
103552419Sjulian				log(LOG_WARNING, "nglmi: packet data:%s\n", buf);
103652419Sjulian			else
103752419Sjulian				log(LOG_WARNING, "%04d              :%s\n", k, buf);
103852419Sjulian			i++;
103952419Sjulian		}
104052419Sjulian	}
104170700Sjulian	NG_FREE_M(m);
104252419Sjulian	return (0);
104352419Sjulian}
104452419Sjulian
104552419Sjulian/*
104652419Sjulian * Do local shutdown processing..
104752419Sjulian * Cut any remaining links and free our local resources.
104852419Sjulian */
104952419Sjulianstatic int
105070700Sjuliannglmi_shutdown(node_p node)
105152419Sjulian{
105270784Sjulian	const sc_p sc = NG_NODE_PRIVATE(node);
105352419Sjulian
105470784Sjulian	NG_NODE_SET_PRIVATE(node, NULL);
105570784Sjulian	NG_NODE_UNREF(sc->node);
1056184205Sdes	free(sc, M_NETGRAPH);
105752419Sjulian	return (0);
105852419Sjulian}
105952419Sjulian
106052419Sjulian/*
106152419Sjulian * Hook disconnection
106252419Sjulian * For this type, removal of any link except "debug" destroys the node.
106352419Sjulian */
106452419Sjulianstatic int
106552419Sjuliannglmi_disconnect(hook_p hook)
106652419Sjulian{
106770784Sjulian	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
106852419Sjulian
106952419Sjulian	/* OK to remove debug hook(s) */
107070784Sjulian	if (NG_HOOK_PRIVATE(hook) == NULL)
107152419Sjulian		return (0);
107252419Sjulian
107352419Sjulian	/* Stop timer if it's currently active */
107452419Sjulian	if (sc->flags & SCF_CONNECTED)
1075140066Sglebius		ng_uncallout(&sc->handle, sc->node);
107652419Sjulian
107752419Sjulian	/* Self-destruct */
107870784Sjulian	if (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))
107970784Sjulian		ng_rmnode_self(NG_HOOK_NODE(hook));
108052419Sjulian	return (0);
108152419Sjulian}
108252419Sjulian
1083