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$
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
188220768Sglebius	sc = malloc(sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO);
189140066Sglebius
19070784Sjulian	NG_NODE_SET_PRIVATE(node, sc);
191140066Sglebius	sc->node = node;
192140066Sglebius
193140066Sglebius	ng_callout_init(&sc->handle);
19452419Sjulian	sc->protoname = NAME_NONE;
19552419Sjulian	sc->liv_per_full = NG_LMI_SEQ_PER_FULL;	/* make this dynamic */
19652419Sjulian	sc->liv_rate = NG_LMI_KEEPALIVE_RATE;
19752419Sjulian	return (0);
19852419Sjulian}
19952419Sjulian
20052419Sjulian/*
20152419Sjulian * The LMI channel has a private pointer which is the same as the
20252419Sjulian * node private pointer. The debug channel has a NULL private pointer.
20352419Sjulian */
20452419Sjulianstatic int
20552419Sjuliannglmi_newhook(node_p node, hook_p hook, const char *name)
20652419Sjulian{
20770784Sjulian	sc_p sc = NG_NODE_PRIVATE(node);
20852419Sjulian
20952419Sjulian	if (strcmp(name, NG_LMI_HOOK_DEBUG) == 0) {
21070784Sjulian		NG_HOOK_SET_PRIVATE(hook, NULL);
21152419Sjulian		return (0);
21252419Sjulian	}
21352419Sjulian	if (sc->flags & SCF_CONNECTED) {
21452419Sjulian		/* already connected, return an error */
21552419Sjulian		return (EINVAL);
21652419Sjulian	}
21752419Sjulian	if (strcmp(name, NG_LMI_HOOK_ANNEXA) == 0) {
21852419Sjulian		sc->lmi_annexA = hook;
21970784Sjulian		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
22052419Sjulian		sc->protoID = 8;
22152419Sjulian		SETLMITYPE(sc, SCF_ANNEX_A);
22252419Sjulian		sc->protoname = NAME_ANNEXA;
22352419Sjulian		nglmi_startup_fixed(sc, hook);
22452419Sjulian	} else if (strcmp(name, NG_LMI_HOOK_ANNEXD) == 0) {
22552419Sjulian		sc->lmi_annexD = hook;
22670784Sjulian		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
22752419Sjulian		sc->protoID = 8;
22852419Sjulian		SETLMITYPE(sc, SCF_ANNEX_D);
22952419Sjulian		sc->protoname = NAME_ANNEXD;
23052419Sjulian		nglmi_startup_fixed(sc, hook);
23152419Sjulian	} else if (strcmp(name, NG_LMI_HOOK_GROUPOF4) == 0) {
23252419Sjulian		sc->lmi_group4 = hook;
23370784Sjulian		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
23452419Sjulian		sc->protoID = 9;
23552419Sjulian		SETLMITYPE(sc, SCF_GROUP4);
23652419Sjulian		sc->protoname = NAME_GROUP4;
23752419Sjulian		nglmi_startup_fixed(sc, hook);
23852419Sjulian	} else if (strcmp(name, NG_LMI_HOOK_AUTO0) == 0) {
23952419Sjulian		/* Note this, and if B is already installed, we're complete */
24052419Sjulian		sc->lmi_channel0 = hook;
24152419Sjulian		sc->protoname = NAME_NONE;
24270784Sjulian		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
24352419Sjulian		if (sc->lmi_channel1023)
24452419Sjulian			nglmi_startup_auto(sc);
24552419Sjulian	} else if (strcmp(name, NG_LMI_HOOK_AUTO1023) == 0) {
24652419Sjulian		/* Note this, and if A is already installed, we're complete */
24752419Sjulian		sc->lmi_channel1023 = hook;
24852419Sjulian		sc->protoname = NAME_NONE;
24970784Sjulian		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
25052419Sjulian		if (sc->lmi_channel0)
25152419Sjulian			nglmi_startup_auto(sc);
25252419Sjulian	} else
25352419Sjulian		return (EINVAL);		/* unknown hook */
25452419Sjulian	return (0);
25552419Sjulian}
25652419Sjulian
25752419Sjulian/*
25852419Sjulian * We have just attached to a live (we hope) node.
25952419Sjulian * Fire out a LMI inquiry, and then start up the timers.
26052419Sjulian */
26152419Sjulianstatic void
262140066SglebiusLMI_ticker(node_p node, hook_p hook, void *arg1, int arg2)
26352419Sjulian{
264140066Sglebius	sc_p sc = NG_NODE_PRIVATE(node);
26552419Sjulian
26652419Sjulian	if (sc->flags & SCF_AUTO) {
26752419Sjulian		ngauto_state_machine(sc);
268140066Sglebius		ng_callout(&sc->handle, node, NULL, NG_LMI_POLL_RATE * hz,
269140066Sglebius		    LMI_ticker, NULL, 0);
27052419Sjulian	} else {
27152419Sjulian		if (sc->livs++ >= sc->liv_per_full) {
27252419Sjulian			nglmi_inquire(sc, 1);
27352419Sjulian			/* sc->livs = 0; *//* do this when we get the answer! */
27452419Sjulian		} else {
27552419Sjulian			nglmi_inquire(sc, 0);
27652419Sjulian		}
277140066Sglebius		ng_callout(&sc->handle, node, NULL, sc->liv_rate * hz,
278140066Sglebius		    LMI_ticker, NULL, 0);
27952419Sjulian	}
28052419Sjulian}
28152419Sjulian
28252419Sjulianstatic void
28352419Sjuliannglmi_startup_fixed(sc_p sc, hook_p hook)
28452419Sjulian{
28552419Sjulian	sc->flags |= (SCF_FIXED | SCF_CONNECTED);
28652419Sjulian	sc->lmi_channel = hook;
28752419Sjulian	nglmi_startup(sc);
28852419Sjulian}
28952419Sjulian
29052419Sjulianstatic void
29152419Sjuliannglmi_startup_auto(sc_p sc)
29252419Sjulian{
29352419Sjulian	sc->flags |= (SCF_AUTO | SCF_CONNECTED);
29452419Sjulian	sc->poll_state = 0;	/* reset state machine */
29552419Sjulian	sc->poll_count = 0;
29652419Sjulian	nglmi_startup(sc);
29752419Sjulian}
29852419Sjulian
29952419Sjulianstatic void
30052419Sjuliannglmi_startup(sc_p sc)
30152419Sjulian{
30252419Sjulian	sc->remote_seq = 0;
30352419Sjulian	sc->local_seq = 1;
30452419Sjulian	sc->seq_retries = 0;
30552419Sjulian	sc->livs = sc->liv_per_full - 1;
30652419Sjulian	/* start off the ticker in 1 sec */
307140066Sglebius	ng_callout(&sc->handle, sc->node, NULL, hz, LMI_ticker, NULL, 0);
30852419Sjulian}
30952419Sjulian
31052419Sjulianstatic void
31152419Sjuliannglmi_inquire(sc_p sc, int full)
31252419Sjulian{
31352419Sjulian	struct mbuf *m;
314131108Sjulian	struct ng_tag_prio *ptag;
31552419Sjulian	char   *cptr, *start;
31652419Sjulian	int     error;
31752419Sjulian
31852419Sjulian	if (sc->lmi_channel == NULL)
31952419Sjulian		return;
320111119Simp	MGETHDR(m, M_DONTWAIT, MT_DATA);
32152419Sjulian	if (m == NULL) {
32252419Sjulian		log(LOG_ERR, "nglmi: unable to start up LMI processing\n");
32352419Sjulian		return;
32452419Sjulian	}
32553284Sarchie	m->m_pkthdr.rcvif = NULL;
326131108Sjulian
327131108Sjulian	/* Attach a tag to packet, marking it of link level state priority, so
328131108Sjulian	 * that device driver would put it in the beginning of queue */
329131108Sjulian
330131108Sjulian	ptag = (struct ng_tag_prio *)m_tag_alloc(NGM_GENERIC_COOKIE, NG_TAG_PRIO,
331131108Sjulian	    (sizeof(struct ng_tag_prio) - sizeof(struct m_tag)), M_NOWAIT);
332131108Sjulian	if (ptag != NULL) {	/* if it failed, well, it was optional anyhow */
333131108Sjulian		ptag->priority = NG_PRIO_LINKSTATE;
334131108Sjulian		ptag->discardability = -1;
335131108Sjulian		m_tag_prepend(m, &ptag->tag);
33652419Sjulian	}
337131108Sjulian
33852419Sjulian	m->m_data += 4;		/* leave some room for a header */
33952419Sjulian	cptr = start = mtod(m, char *);
34052419Sjulian	/* add in the header for an LMI inquiry. */
34152419Sjulian	*cptr++ = 0x03;		/* UI frame */
34252419Sjulian	if (GROUP4(sc))
34352419Sjulian		*cptr++ = 0x09;	/* proto discriminator */
34452419Sjulian	else
34552419Sjulian		*cptr++ = 0x08;	/* proto discriminator */
34652419Sjulian	*cptr++ = 0x00;		/* call reference */
34752419Sjulian	*cptr++ = 0x75;		/* inquiry */
34852419Sjulian
349140365Srik	/* If we are Annex-D, add locking shift to codeset 5. */
35052419Sjulian	if (ANNEXD(sc))
351140358Srik		*cptr++ = 0x95;	/* locking shift */
35252419Sjulian	/* Add a request type */
35352419Sjulian	if (ANNEXA(sc))
35452419Sjulian		*cptr++ = 0x51;	/* report type */
35552419Sjulian	else
35652419Sjulian		*cptr++ = 0x01;	/* report type */
35752419Sjulian	*cptr++ = 0x01;		/* size = 1 */
35852419Sjulian	if (full)
35952419Sjulian		*cptr++ = 0x00;	/* full */
36052419Sjulian	else
36152419Sjulian		*cptr++ = 0x01;	/* partial */
36252419Sjulian
36352419Sjulian	/* Add a link verification IE */
36452419Sjulian	if (ANNEXA(sc))
36552419Sjulian		*cptr++ = 0x53;	/* verification IE */
36652419Sjulian	else
36752419Sjulian		*cptr++ = 0x03;	/* verification IE */
36852419Sjulian	*cptr++ = 0x02;		/* 2 extra bytes */
36952419Sjulian	*cptr++ = sc->local_seq;
37052419Sjulian	*cptr++ = sc->remote_seq;
37152419Sjulian	sc->seq_retries++;
37252419Sjulian
37352419Sjulian	/* Send it */
37452419Sjulian	m->m_len = m->m_pkthdr.len = cptr - start;
375131108Sjulian	NG_SEND_DATA_ONLY(error, sc->lmi_channel, m);
37652419Sjulian
37752419Sjulian	/* If we've been sending requests for long enough, and there has
37852419Sjulian	 * been no response, then mark as DOWN, any DLCIs that are UP. */
37952419Sjulian	if (sc->seq_retries == LMI_PATIENCE) {
38052419Sjulian		int     count;
38152419Sjulian
38252419Sjulian		for (count = 0; count < MAXDLCI; count++)
38352419Sjulian			if (sc->dlci_state[count] == DLCI_UP)
38452419Sjulian				sc->dlci_state[count] = DLCI_DOWN;
38552419Sjulian	}
38652419Sjulian}
38752419Sjulian
38852419Sjulian/*
38952419Sjulian * State machine for LMI auto-detect. The transitions are ordered
39052419Sjulian * to try the more likely possibilities first.
39152419Sjulian */
39252419Sjulianstatic void
39352419Sjulianngauto_state_machine(sc_p sc)
39452419Sjulian{
39552419Sjulian	if ((sc->poll_count <= 0) || (sc->poll_count > LMIPOLLSIZE)) {
39652419Sjulian		/* time to change states in the auto probe machine */
39752419Sjulian		/* capture wild values of poll_count while we are at it */
39852419Sjulian		sc->poll_count = LMIPOLLSIZE;
39952419Sjulian		sc->poll_state++;
40052419Sjulian	}
40152419Sjulian	switch (sc->poll_state) {
40252419Sjulian	case 7:
40352419Sjulian		log(LOG_WARNING, "nglmi: no response from exchange\n");
40452419Sjulian	default:		/* capture bad states */
40552419Sjulian		sc->poll_state = 1;
40652419Sjulian	case 1:
40752419Sjulian		sc->lmi_channel = sc->lmi_channel0;
40852419Sjulian		SETLMITYPE(sc, SCF_ANNEX_D);
40952419Sjulian		break;
41052419Sjulian	case 2:
41152419Sjulian		sc->lmi_channel = sc->lmi_channel1023;
41252419Sjulian		SETLMITYPE(sc, SCF_ANNEX_D);
41352419Sjulian		break;
41452419Sjulian	case 3:
41552419Sjulian		sc->lmi_channel = sc->lmi_channel0;
41652419Sjulian		SETLMITYPE(sc, SCF_ANNEX_A);
41752419Sjulian		break;
41852419Sjulian	case 4:
41952419Sjulian		sc->lmi_channel = sc->lmi_channel1023;
42052419Sjulian		SETLMITYPE(sc, SCF_GROUP4);
42152419Sjulian		break;
42252419Sjulian	case 5:
42352419Sjulian		sc->lmi_channel = sc->lmi_channel1023;
42452419Sjulian		SETLMITYPE(sc, SCF_ANNEX_A);
42552419Sjulian		break;
42652419Sjulian	case 6:
42752419Sjulian		sc->lmi_channel = sc->lmi_channel0;
42852419Sjulian		SETLMITYPE(sc, SCF_GROUP4);
42952419Sjulian		break;
43052419Sjulian	}
43152419Sjulian
43252419Sjulian	/* send an inquirey encoded appropriatly */
43352419Sjulian	nglmi_inquire(sc, 0);
43452419Sjulian	sc->poll_count--;
43552419Sjulian}
43652419Sjulian
43752419Sjulian/*
43852419Sjulian * Receive a netgraph control message.
43952419Sjulian */
44052419Sjulianstatic int
44170700Sjuliannglmi_rcvmsg(node_p node, item_p item, hook_p lasthook)
44252419Sjulian{
44370784Sjulian	sc_p    sc = NG_NODE_PRIVATE(node);
44470159Sjulian	struct ng_mesg *resp = NULL;
44552419Sjulian	int     error = 0;
44670700Sjulian	struct ng_mesg *msg;
44752419Sjulian
44870700Sjulian	NGI_GET_MSG(item, msg);
44952419Sjulian	switch (msg->header.typecookie) {
45052419Sjulian	case NGM_GENERIC_COOKIE:
45152419Sjulian		switch (msg->header.cmd) {
45252419Sjulian		case NGM_TEXT_STATUS:
45352419Sjulian		    {
45452419Sjulian			char   *arg;
45552419Sjulian			int     pos, count;
45652419Sjulian
45770159Sjulian			NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
45870159Sjulian			if (resp == NULL) {
45952419Sjulian				error = ENOMEM;
46052419Sjulian				break;
46152419Sjulian			}
46270159Sjulian			arg = resp->data;
46352419Sjulian			pos = sprintf(arg, "protocol %s ", sc->protoname);
46452419Sjulian			if (sc->flags & SCF_FIXED)
46552419Sjulian				pos += sprintf(arg + pos, "fixed\n");
46652419Sjulian			else if (sc->flags & SCF_AUTO)
46752419Sjulian				pos += sprintf(arg + pos, "auto-detecting\n");
46852419Sjulian			else
46952419Sjulian				pos += sprintf(arg + pos, "auto on dlci %d\n",
47052419Sjulian				    (sc->lmi_channel == sc->lmi_channel0) ?
47152419Sjulian				    0 : 1023);
47252419Sjulian			pos += sprintf(arg + pos,
47352419Sjulian			    "keepalive period: %d seconds\n", sc->liv_rate);
47452419Sjulian			pos += sprintf(arg + pos,
47552419Sjulian			    "unacknowledged keepalives: %ld\n",
47652419Sjulian			    sc->seq_retries);
47752419Sjulian			for (count = 0;
47852419Sjulian			     ((count <= MAXDLCI)
47952419Sjulian			      && (pos < (NG_TEXTRESPONSE - 20)));
48052419Sjulian			     count++) {
48152419Sjulian				if (sc->dlci_state[count]) {
48252419Sjulian					pos += sprintf(arg + pos,
48352419Sjulian					       "dlci %d %s\n", count,
48452419Sjulian					       (sc->dlci_state[count]
48552419Sjulian					== DLCI_UP) ? "up" : "down");
48652419Sjulian				}
48752419Sjulian			}
48870159Sjulian			resp->header.arglen = pos + 1;
48952419Sjulian			break;
49052419Sjulian		    }
49152419Sjulian		default:
49252419Sjulian			error = EINVAL;
49352419Sjulian			break;
49452419Sjulian		}
49552419Sjulian		break;
49652419Sjulian	case NGM_LMI_COOKIE:
49752419Sjulian		switch (msg->header.cmd) {
49852419Sjulian		case NGM_LMI_GET_STATUS:
49952419Sjulian		    {
50052419Sjulian			struct nglmistat *stat;
50152419Sjulian			int k;
50252419Sjulian
50370159Sjulian			NG_MKRESPONSE(resp, msg, sizeof(*stat), M_NOWAIT);
50470159Sjulian			if (!resp) {
50552419Sjulian				error = ENOMEM;
50652419Sjulian				break;
50752419Sjulian			}
50870159Sjulian			stat = (struct nglmistat *) resp->data;
50952419Sjulian			strncpy(stat->proto,
51052419Sjulian			     sc->protoname, sizeof(stat->proto) - 1);
51152419Sjulian			strncpy(stat->hook,
51252419Sjulian			      sc->protoname, sizeof(stat->hook) - 1);
51352419Sjulian			stat->autod = !!(sc->flags & SCF_AUTO);
51452419Sjulian			stat->fixed = !!(sc->flags & SCF_FIXED);
51552419Sjulian			for (k = 0; k <= MAXDLCI; k++) {
51652419Sjulian				switch (sc->dlci_state[k]) {
51752419Sjulian				case DLCI_UP:
51852419Sjulian					stat->up[k / 8] |= (1 << (k % 8));
51952419Sjulian					/* fall through */
52052419Sjulian				case DLCI_DOWN:
52152419Sjulian					stat->seen[k / 8] |= (1 << (k % 8));
52252419Sjulian					break;
52352419Sjulian				}
52452419Sjulian			}
52552419Sjulian			break;
52652419Sjulian		    }
52752419Sjulian		default:
52852419Sjulian			error = EINVAL;
52952419Sjulian			break;
53052419Sjulian		}
53152419Sjulian		break;
53252419Sjulian	default:
53352419Sjulian		error = EINVAL;
53452419Sjulian		break;
53552419Sjulian	}
53670159Sjulian
53770700Sjulian	NG_RESPOND_MSG(error, node, item, resp);
53870700Sjulian	NG_FREE_MSG(msg);
53952419Sjulian	return (error);
54052419Sjulian}
54152419Sjulian
54252419Sjulian#define STEPBY(stepsize)			\
54352419Sjulian	do {					\
54452419Sjulian		packetlen -= (stepsize);	\
54552419Sjulian		data += (stepsize);		\
54652419Sjulian	} while (0)
54752419Sjulian
54852419Sjulian/*
54952419Sjulian * receive data, and use it to update our status.
55052419Sjulian * Anything coming in on the debug port is discarded.
55152419Sjulian */
55252419Sjulianstatic int
55370700Sjuliannglmi_rcvdata(hook_p hook, item_p item)
55452419Sjulian{
55570784Sjulian	sc_p    sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
55697897Sarchie	const	u_char *data;
55752419Sjulian	unsigned short dlci;
55852419Sjulian	u_short packetlen;
55952419Sjulian	int     resptype_seen = 0;
56070700Sjulian	struct mbuf *m;
56152419Sjulian
56270700Sjulian	NGI_GET_M(item, m);
56370700Sjulian	NG_FREE_ITEM(item);
56470784Sjulian	if (NG_HOOK_PRIVATE(hook) == NULL) {
56552419Sjulian		goto drop;
56652419Sjulian	}
567147163Sru	packetlen = m->m_len;
56852419Sjulian
56952419Sjulian	/* XXX what if it's more than 1 mbuf? */
57052419Sjulian	if ((packetlen > MHLEN) && !(m->m_flags & M_EXT)) {
57152419Sjulian		log(LOG_WARNING, "nglmi: packetlen (%d) too big\n", packetlen);
57252419Sjulian		goto drop;
57352419Sjulian	}
57452539Sjulian	if (m->m_len < packetlen && (m = m_pullup(m, packetlen)) == NULL) {
57552539Sjulian		log(LOG_WARNING,
57652539Sjulian		    "nglmi: m_pullup failed for %d bytes\n", packetlen);
57752419Sjulian		return (0);
57852419Sjulian	}
57970700Sjulian	if (nglmi_checkdata(hook, m) == 0)
58052419Sjulian		return (0);
58152419Sjulian
58252419Sjulian	/* pass the first 4 bytes (already checked in the nglmi_checkdata()) */
58397897Sarchie	data = mtod(m, const u_char *);
58452419Sjulian	STEPBY(4);
58552419Sjulian
58652419Sjulian	/* Now check if there is a 'locking shift'. This is only seen in
58752419Sjulian	 * Annex D frames. don't bother checking, we already did that. Don't
58852419Sjulian	 * increment immediatly as it might not be there. */
58952419Sjulian	if (ANNEXD(sc))
59052419Sjulian		STEPBY(1);
59152419Sjulian
59252419Sjulian	/* If we get this far we should consider that it is a legitimate
59352419Sjulian	 * frame and we know what it is. */
59452419Sjulian	if (sc->flags & SCF_AUTO) {
59552419Sjulian		/* note the hook that this valid channel came from and drop
59652419Sjulian		 * out of auto probe mode. */
59752419Sjulian		if (ANNEXA(sc))
59852419Sjulian			sc->protoname = NAME_ANNEXA;
59952419Sjulian		else if (ANNEXD(sc))
60052419Sjulian			sc->protoname = NAME_ANNEXD;
60152419Sjulian		else if (GROUP4(sc))
60252419Sjulian			sc->protoname = NAME_GROUP4;
60352419Sjulian		else {
60452419Sjulian			log(LOG_ERR, "nglmi: No known type\n");
60552419Sjulian			goto drop;
60652419Sjulian		}
60752419Sjulian		sc->lmi_channel = hook;
60852419Sjulian		sc->flags &= ~SCF_AUTO;
60952419Sjulian		log(LOG_INFO, "nglmi: auto-detected %s LMI on DLCI %d\n",
61052419Sjulian		    sc->protoname, hook == sc->lmi_channel0 ? 0 : 1023);
61152419Sjulian	}
61252419Sjulian
61352419Sjulian	/* While there is more data in the status packet, keep processing
61452419Sjulian	 * status items. First make sure there is enough data for the
61552419Sjulian	 * segment descriptor's length field. */
61652419Sjulian	while (packetlen >= 2) {
61752419Sjulian		u_int   segtype = data[0];
61852419Sjulian		u_int   segsize = data[1];
61952419Sjulian
62052419Sjulian		/* Now that we know how long it claims to be, make sure
62152419Sjulian		 * there is enough data for the next seg. */
62252419Sjulian		if (packetlen < segsize + 2)
62352419Sjulian			break;
62452419Sjulian		switch (segtype) {
62552419Sjulian		case 0x01:
62652419Sjulian		case 0x51:
62752419Sjulian			if (resptype_seen) {
62852419Sjulian				log(LOG_WARNING, "nglmi: dup MSGTYPE\n");
62952419Sjulian				goto nextIE;
63052419Sjulian			}
63152419Sjulian			resptype_seen++;
63252419Sjulian			/* The remote end tells us what kind of response
63352419Sjulian			 * this is. Only expect a type 0 or 1. if we are a
63452419Sjulian			 * full status, invalidate a few DLCIs just to see
63552419Sjulian			 * that they are still ok. */
63652419Sjulian			if (segsize != 1)
63752419Sjulian				goto nextIE;
63852419Sjulian			switch (data[2]) {
63952419Sjulian			case 1:
64052419Sjulian				/* partial status, do no extra processing */
64152419Sjulian				break;
64252419Sjulian			case 0:
64352419Sjulian			    {
64452419Sjulian				int     count = 0;
64552419Sjulian				int     idx = sc->invalidx;
64652419Sjulian
64752419Sjulian				for (count = 0; count < 10; count++) {
64852419Sjulian					if (idx > MAXDLCI)
64952419Sjulian						idx = 0;
65052419Sjulian					if (sc->dlci_state[idx] == DLCI_UP)
65152419Sjulian						sc->dlci_state[idx] = DLCI_DOWN;
65252419Sjulian					idx++;
65352419Sjulian				}
65452419Sjulian				sc->invalidx = idx;
65552419Sjulian				/* we got and we wanted one. relax
65652419Sjulian				 * now.. but don't reset to 0 if it
65752419Sjulian				 * was unrequested. */
65852419Sjulian				if (sc->livs > sc->liv_per_full)
65952419Sjulian					sc->livs = 0;
66052419Sjulian				break;
66152419Sjulian			    }
66252419Sjulian			}
66352419Sjulian			break;
66452419Sjulian		case 0x03:
66552419Sjulian		case 0x53:
66652419Sjulian			/* The remote tells us what it thinks the sequence
66752419Sjulian			 * numbers are. If it's not size 2, it must be a
66852419Sjulian			 * duplicate to have gotten this far, skip it. */
66952419Sjulian			if (segsize != 2)
67052419Sjulian				goto nextIE;
67152419Sjulian			sc->remote_seq = data[2];
67252419Sjulian			if (sc->local_seq == data[3]) {
67352419Sjulian				sc->local_seq++;
67452419Sjulian				sc->seq_retries = 0;
67552419Sjulian				/* Note that all 3 Frame protocols seem to
67652419Sjulian				 * not like 0 as a sequence number. */
67752419Sjulian				if (sc->local_seq == 0)
67852419Sjulian					sc->local_seq = 1;
67952419Sjulian			}
68052419Sjulian			break;
68152419Sjulian		case 0x07:
68252419Sjulian		case 0x57:
68352419Sjulian			/* The remote tells us about a DLCI that it knows
68452419Sjulian			 * about. There may be many of these in a single
68552419Sjulian			 * status response */
68652419Sjulian			switch (segsize) {
68752419Sjulian			case 6:/* only on 'group of 4' */
68852419Sjulian				dlci = ((u_short) data[2] & 0xff) << 8;
68952419Sjulian				dlci |= (data[3] & 0xff);
69052419Sjulian				if ((dlci < 1024) && (dlci > 0)) {
69152419Sjulian				  /* XXX */
69252419Sjulian				}
69352419Sjulian				break;
69452419Sjulian			case 3:
69552419Sjulian				dlci = ((u_short) data[2] & 0x3f) << 4;
69652419Sjulian				dlci |= ((data[3] & 0x78) >> 3);
69752419Sjulian				if ((dlci < 1024) && (dlci > 0)) {
69852419Sjulian					/* set up the bottom half of the
69952419Sjulian					 * support for that dlci if it's not
70052419Sjulian					 * already been done */
70152419Sjulian					/* store this information somewhere */
70252419Sjulian				}
70352419Sjulian				break;
70452419Sjulian			default:
70552419Sjulian				goto nextIE;
70652419Sjulian			}
70752419Sjulian			if (sc->dlci_state[dlci] != DLCI_UP) {
70852419Sjulian				/* bring new DLCI to life */
70952419Sjulian				/* may do more here some day */
71052419Sjulian				if (sc->dlci_state[dlci] != DLCI_DOWN)
71152419Sjulian					log(LOG_INFO,
71252419Sjulian					    "nglmi: DLCI %d became active\n",
71352419Sjulian					    dlci);
71452419Sjulian				sc->dlci_state[dlci] = DLCI_UP;
71552419Sjulian			}
71652419Sjulian			break;
71752419Sjulian		}
71852419SjuliannextIE:
71952419Sjulian		STEPBY(segsize + 2);
72052419Sjulian	}
72170700Sjulian	NG_FREE_M(m);
72252419Sjulian	return (0);
72352419Sjulian
72452419Sjuliandrop:
72570700Sjulian	NG_FREE_M(m);
72652419Sjulian	return (EINVAL);
72752419Sjulian}
72852419Sjulian
72952419Sjulian/*
73052419Sjulian * Check that a packet is entirely kosha.
73152419Sjulian * return 1 of ok, and 0 if not.
73252419Sjulian * All data is discarded if a 0 is returned.
73352419Sjulian */
73452419Sjulianstatic int
73570700Sjuliannglmi_checkdata(hook_p hook, struct mbuf *m)
73652419Sjulian{
73770784Sjulian	sc_p    sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
73897897Sarchie	const	u_char *data;
73952419Sjulian	u_short packetlen;
74052419Sjulian	unsigned short dlci;
74152419Sjulian	u_char  type;
74252419Sjulian	u_char  nextbyte;
74352419Sjulian	int     seq_seen = 0;
74452419Sjulian	int     resptype_seen = 0;	/* 0 , 1 (partial) or 2 (full) */
74552419Sjulian	int     highest_dlci = 0;
74652419Sjulian
747147163Sru	packetlen = m->m_len;
74897897Sarchie	data = mtod(m, const u_char *);
74952419Sjulian	if (*data != 0x03) {
75052419Sjulian		log(LOG_WARNING, "nglmi: unexpected value in LMI(%d)\n", 1);
75152419Sjulian		goto reject;
75252419Sjulian	}
75352419Sjulian	STEPBY(1);
75452419Sjulian
75552419Sjulian	/* look at the protocol ID */
75652419Sjulian	nextbyte = *data;
75752419Sjulian	if (sc->flags & SCF_AUTO) {
75852419Sjulian		SETLMITYPE(sc, SCF_NOLMI);	/* start with a clean slate */
75952419Sjulian		switch (nextbyte) {
76052419Sjulian		case 0x8:
76152419Sjulian			sc->protoID = 8;
76252419Sjulian			break;
76352419Sjulian		case 0x9:
76452419Sjulian			SETLMITYPE(sc, SCF_GROUP4);
76552419Sjulian			sc->protoID = 9;
76652419Sjulian			break;
76752419Sjulian		default:
76852419Sjulian			log(LOG_WARNING, "nglmi: bad Protocol ID(%d)\n",
76952419Sjulian			    (int) nextbyte);
77052419Sjulian			goto reject;
77152419Sjulian		}
77252419Sjulian	} else {
77352419Sjulian		if (nextbyte != sc->protoID) {
77452419Sjulian			log(LOG_WARNING, "nglmi: unexpected Protocol ID(%d)\n",
77552419Sjulian			    (int) nextbyte);
77652419Sjulian			goto reject;
77752419Sjulian		}
77852419Sjulian	}
77952419Sjulian	STEPBY(1);
78052419Sjulian
78152419Sjulian	/* check call reference (always null in non ISDN frame relay) */
78252419Sjulian	if (*data != 0x00) {
78352419Sjulian		log(LOG_WARNING, "nglmi: unexpected Call Reference (0x%x)\n",
78452419Sjulian		    data[-1]);
78552419Sjulian		goto reject;
78652419Sjulian	}
78752419Sjulian	STEPBY(1);
78852419Sjulian
78952419Sjulian	/* check message type */
79052419Sjulian	switch ((type = *data)) {
79152419Sjulian	case 0x75:		/* Status enquiry */
79252419Sjulian		log(LOG_WARNING, "nglmi: unexpected message type(0x%x)\n",
79352419Sjulian		    data[-1]);
79452419Sjulian		goto reject;
79552419Sjulian	case 0x7D:		/* Status message */
79652419Sjulian		break;
79752419Sjulian	default:
79852419Sjulian		log(LOG_WARNING,
79952419Sjulian		    "nglmi: unexpected msg type(0x%x) \n", (int) type);
80052419Sjulian		goto reject;
80152419Sjulian	}
80252419Sjulian	STEPBY(1);
80352419Sjulian
80452419Sjulian	/* Now check if there is a 'locking shift'. This is only seen in
80552419Sjulian	 * Annex D frames. Don't increment immediately as it might not be
80652419Sjulian	 * there. */
80752419Sjulian	nextbyte = *data;
80852419Sjulian	if (sc->flags & SCF_AUTO) {
80952419Sjulian		if (!(GROUP4(sc))) {
81052419Sjulian			if (nextbyte == 0x95) {
81152419Sjulian				SETLMITYPE(sc, SCF_ANNEX_D);
81252419Sjulian				STEPBY(1);
81352419Sjulian			} else
81452419Sjulian				SETLMITYPE(sc, SCF_ANNEX_A);
81552419Sjulian		} else if (nextbyte == 0x95) {
81652419Sjulian			log(LOG_WARNING, "nglmi: locking shift seen in G4\n");
81752419Sjulian			goto reject;
81852419Sjulian		}
81952419Sjulian	} else {
82052419Sjulian		if (ANNEXD(sc)) {
82152419Sjulian			if (*data == 0x95)
82252419Sjulian				STEPBY(1);
82352419Sjulian			else {
82452419Sjulian				log(LOG_WARNING,
82552419Sjulian				    "nglmi: locking shift missing\n");
82652419Sjulian				goto reject;
82752419Sjulian			}
82852419Sjulian		} else if (*data == 0x95) {
82952419Sjulian			log(LOG_WARNING, "nglmi: locking shift seen\n");
83052419Sjulian			goto reject;
83152419Sjulian		}
83252419Sjulian	}
83352419Sjulian
83452419Sjulian	/* While there is more data in the status packet, keep processing
83552419Sjulian	 * status items. First make sure there is enough data for the
83652419Sjulian	 * segment descriptor's length field. */
83752419Sjulian	while (packetlen >= 2) {
83852419Sjulian		u_int   segtype = data[0];
83952419Sjulian		u_int   segsize = data[1];
84052419Sjulian
84152419Sjulian		/* Now that we know how long it claims to be, make sure
84252419Sjulian		 * there is enough data for the next seg. */
84352419Sjulian		if (packetlen < (segsize + 2)) {
84452419Sjulian			log(LOG_WARNING, "nglmi: IE longer than packet\n");
84552419Sjulian			break;
84652419Sjulian		}
84752419Sjulian		switch (segtype) {
84852419Sjulian		case 0x01:
84952419Sjulian		case 0x51:
85052419Sjulian			/* According to MCI's HP analyser, we should just
85152419Sjulian			 * ignore if there is mor ethan one of these (?). */
85252419Sjulian			if (resptype_seen) {
85352419Sjulian				log(LOG_WARNING, "nglmi: dup MSGTYPE\n");
85452419Sjulian				goto nextIE;
85552419Sjulian			}
85652419Sjulian			if (segsize != 1) {
85752419Sjulian				log(LOG_WARNING, "nglmi: MSGTYPE wrong size\n");
85852419Sjulian				goto reject;
85952419Sjulian			}
86052419Sjulian			/* The remote end tells us what kind of response
86152419Sjulian			 * this is. Only expect a type 0 or 1. if it was a
86252419Sjulian			 * full (type 0) check we just asked for a type
86352419Sjulian			 * full. */
86452419Sjulian			switch (data[2]) {
86552419Sjulian			case 1:/* partial */
86652419Sjulian				if (sc->livs > sc->liv_per_full) {
86752419Sjulian					log(LOG_WARNING,
86852419Sjulian					  "nglmi: LIV when FULL expected\n");
86952419Sjulian					goto reject;	/* need full */
87052419Sjulian				}
87152419Sjulian				resptype_seen = 1;
87252419Sjulian				break;
87352419Sjulian			case 0:/* full */
87452419Sjulian				/* Full response is always acceptable */
87552419Sjulian				resptype_seen = 2;
87652419Sjulian				break;
87752419Sjulian			default:
87852419Sjulian				log(LOG_WARNING,
87952419Sjulian				 "nglmi: Unknown report type %d\n", data[2]);
88052419Sjulian				goto reject;
88152419Sjulian			}
88252419Sjulian			break;
88352419Sjulian		case 0x03:
88452419Sjulian		case 0x53:
88552419Sjulian			/* The remote tells us what it thinks the sequence
88652419Sjulian			 * numbers are. I would have thought that there
88752419Sjulian			 * needs to be one and only one of these, but MCI
88852419Sjulian			 * want us to just ignore extras. (?) */
88952419Sjulian			if (resptype_seen == 0) {
89052419Sjulian				log(LOG_WARNING, "nglmi: no TYPE before SEQ\n");
89152419Sjulian				goto reject;
89252419Sjulian			}
89352419Sjulian			if (seq_seen != 0)	/* already seen seq numbers */
89452419Sjulian				goto nextIE;
89552419Sjulian			if (segsize != 2) {
89652419Sjulian				log(LOG_WARNING, "nglmi: bad SEQ sts size\n");
89752419Sjulian				goto reject;
89852419Sjulian			}
89952419Sjulian			if (sc->local_seq != data[3]) {
90052419Sjulian				log(LOG_WARNING, "nglmi: unexpected SEQ\n");
90152419Sjulian				goto reject;
90252419Sjulian			}
90352419Sjulian			seq_seen = 1;
90452419Sjulian			break;
90552419Sjulian		case 0x07:
90652419Sjulian		case 0x57:
90752419Sjulian			/* The remote tells us about a DLCI that it knows
90852419Sjulian			 * about. There may be many of these in a single
90952419Sjulian			 * status response */
91052419Sjulian			if (seq_seen != 1) {	/* already seen seq numbers? */
91152419Sjulian				log(LOG_WARNING,
91252419Sjulian				    "nglmi: No sequence before DLCI\n");
91352419Sjulian				goto reject;
91452419Sjulian			}
91552419Sjulian			if (resptype_seen != 2) {	/* must be full */
91652419Sjulian				log(LOG_WARNING,
91752419Sjulian				    "nglmi: No resp type before DLCI\n");
91852419Sjulian				goto reject;
91952419Sjulian			}
92052419Sjulian			if (GROUP4(sc)) {
92152419Sjulian				if (segsize != 6) {
92252419Sjulian					log(LOG_WARNING,
92352419Sjulian					    "nglmi: wrong IE segsize\n");
92452419Sjulian					goto reject;
92552419Sjulian				}
92652419Sjulian				dlci = ((u_short) data[2] & 0xff) << 8;
92752419Sjulian				dlci |= (data[3] & 0xff);
92852419Sjulian			} else {
92952419Sjulian				if (segsize != 3) {
93052419Sjulian					log(LOG_WARNING,
93152419Sjulian					    "nglmi: DLCI headersize of %d"
93252419Sjulian					    " not supported\n", segsize - 1);
93352419Sjulian					goto reject;
93452419Sjulian				}
93552419Sjulian				dlci = ((u_short) data[2] & 0x3f) << 4;
93652419Sjulian				dlci |= ((data[3] & 0x78) >> 3);
93752419Sjulian			}
93852419Sjulian			/* async can only have one of these */
93952419Sjulian#if 0				/* async not yet accepted */
94052419Sjulian			if (async && highest_dlci) {
94152419Sjulian				log(LOG_WARNING,
94252419Sjulian				    "nglmi: Async with > 1 DLCI\n");
94352419Sjulian				goto reject;
94452419Sjulian			}
94552419Sjulian#endif
94652419Sjulian			/* Annex D says these will always be Ascending, but
94752419Sjulian			 * the HP test for G4 says we should accept
94852419Sjulian			 * duplicates, so for now allow that. ( <= vs. < ) */
94952419Sjulian#if 0
95052419Sjulian			/* MCI tests want us to accept out of order for AnxD */
95152419Sjulian			if ((!GROUP4(sc)) && (dlci < highest_dlci)) {
95252419Sjulian				/* duplicate or mis-ordered dlci */
95352419Sjulian				/* (spec says they will increase in number) */
95452419Sjulian				log(LOG_WARNING, "nglmi: DLCI out of order\n");
95552419Sjulian				goto reject;
95652419Sjulian			}
95752419Sjulian#endif
95852419Sjulian			if (dlci > 1023) {
95952419Sjulian				log(LOG_WARNING, "nglmi: DLCI out of range\n");
96052419Sjulian				goto reject;
96152419Sjulian			}
96252419Sjulian			highest_dlci = dlci;
96352419Sjulian			break;
96452419Sjulian		default:
96552419Sjulian			log(LOG_WARNING,
96652419Sjulian			    "nglmi: unknown LMI segment type %d\n", segtype);
96752419Sjulian		}
96852419SjuliannextIE:
96952419Sjulian		STEPBY(segsize + 2);
97052419Sjulian	}
97152419Sjulian	if (packetlen != 0) {	/* partial junk at end? */
97252419Sjulian		log(LOG_WARNING,
97352419Sjulian		    "nglmi: %d bytes extra at end of packet\n", packetlen);
97452419Sjulian		goto print;
97552419Sjulian	}
97652419Sjulian	if (resptype_seen == 0) {
97752419Sjulian		log(LOG_WARNING, "nglmi: No response type seen\n");
97852419Sjulian		goto reject;	/* had no response type */
97952419Sjulian	}
98052419Sjulian	if (seq_seen == 0) {
98152419Sjulian		log(LOG_WARNING, "nglmi: No sequence numbers seen\n");
98252419Sjulian		goto reject;	/* had no sequence numbers */
98352419Sjulian	}
98452419Sjulian	return (1);
98552419Sjulian
98652419Sjulianprint:
98752419Sjulian	{
98852419Sjulian		int     i, j, k, pos;
98952419Sjulian		char    buf[100];
99052419Sjulian		int     loc;
99197897Sarchie		const	u_char *bp = mtod(m, const u_char *);
99252419Sjulian
99352419Sjulian		k = i = 0;
994147163Sru		loc = (m->m_len - packetlen);
99552419Sjulian		log(LOG_WARNING, "nglmi: error at location %d\n", loc);
996147163Sru		while (k < m->m_len) {
99752419Sjulian			pos = 0;
99852419Sjulian			j = 0;
999147163Sru			while ((j++ < 16) && k < m->m_len) {
100052419Sjulian				pos += sprintf(buf + pos, "%c%02x",
100152419Sjulian					       ((loc == k) ? '>' : ' '),
100252419Sjulian					       bp[k]);
100352419Sjulian				k++;
100452419Sjulian			}
100552419Sjulian			if (i == 0)
100652419Sjulian				log(LOG_WARNING, "nglmi: packet data:%s\n", buf);
100752419Sjulian			else
100852419Sjulian				log(LOG_WARNING, "%04d              :%s\n", k, buf);
100952419Sjulian			i++;
101052419Sjulian		}
101152419Sjulian	}
101252419Sjulian	return (1);
101352419Sjulianreject:
101452419Sjulian	{
101552419Sjulian		int     i, j, k, pos;
101652419Sjulian		char    buf[100];
101752419Sjulian		int     loc;
101897897Sarchie		const	u_char *bp = mtod(m, const u_char *);
101952419Sjulian
102052419Sjulian		k = i = 0;
1021147163Sru		loc = (m->m_len - packetlen);
102252419Sjulian		log(LOG_WARNING, "nglmi: error at location %d\n", loc);
1023147163Sru		while (k < m->m_len) {
102452419Sjulian			pos = 0;
102552419Sjulian			j = 0;
1026147163Sru			while ((j++ < 16) && k < m->m_len) {
102752419Sjulian				pos += sprintf(buf + pos, "%c%02x",
102852419Sjulian					       ((loc == k) ? '>' : ' '),
102952419Sjulian					       bp[k]);
103052419Sjulian				k++;
103152419Sjulian			}
103252419Sjulian			if (i == 0)
103352419Sjulian				log(LOG_WARNING, "nglmi: packet data:%s\n", buf);
103452419Sjulian			else
103552419Sjulian				log(LOG_WARNING, "%04d              :%s\n", k, buf);
103652419Sjulian			i++;
103752419Sjulian		}
103852419Sjulian	}
103970700Sjulian	NG_FREE_M(m);
104052419Sjulian	return (0);
104152419Sjulian}
104252419Sjulian
104352419Sjulian/*
104452419Sjulian * Do local shutdown processing..
104552419Sjulian * Cut any remaining links and free our local resources.
104652419Sjulian */
104752419Sjulianstatic int
104870700Sjuliannglmi_shutdown(node_p node)
104952419Sjulian{
105070784Sjulian	const sc_p sc = NG_NODE_PRIVATE(node);
105152419Sjulian
105270784Sjulian	NG_NODE_SET_PRIVATE(node, NULL);
105370784Sjulian	NG_NODE_UNREF(sc->node);
1054184205Sdes	free(sc, M_NETGRAPH);
105552419Sjulian	return (0);
105652419Sjulian}
105752419Sjulian
105852419Sjulian/*
105952419Sjulian * Hook disconnection
106052419Sjulian * For this type, removal of any link except "debug" destroys the node.
106152419Sjulian */
106252419Sjulianstatic int
106352419Sjuliannglmi_disconnect(hook_p hook)
106452419Sjulian{
106570784Sjulian	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
106652419Sjulian
106752419Sjulian	/* OK to remove debug hook(s) */
106870784Sjulian	if (NG_HOOK_PRIVATE(hook) == NULL)
106952419Sjulian		return (0);
107052419Sjulian
107152419Sjulian	/* Stop timer if it's currently active */
107252419Sjulian	if (sc->flags & SCF_CONNECTED)
1073140066Sglebius		ng_uncallout(&sc->handle, sc->node);
107452419Sjulian
107552419Sjulian	/* Self-destruct */
107670784Sjulian	if (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))
107770784Sjulian		ng_rmnode_self(NG_HOOK_NODE(hook));
107852419Sjulian	return (0);
107952419Sjulian}
108052419Sjulian
1081