ng_lmi.c revision 111119
1
2/*
3 * ng_lmi.c
4 *
5 * Copyright (c) 1996-1999 Whistle Communications, Inc.
6 * All rights reserved.
7 *
8 * Subject to the following obligations and disclaimer of warranty, use and
9 * redistribution of this software, in source or object code forms, with or
10 * without modifications are expressly permitted by Whistle Communications;
11 * provided, however, that:
12 * 1. Any and all reproductions of the source or object code must include the
13 *    copyright notice above and the following disclaimer of warranties; and
14 * 2. No rights are granted, in any manner or form, to use Whistle
15 *    Communications, Inc. trademarks, including the mark "WHISTLE
16 *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
17 *    such appears in the above copyright notice or in the software.
18 *
19 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
20 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
21 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
22 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
24 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
25 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
26 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
27 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
28 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
29 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
30 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
35 * OF SUCH DAMAGE.
36 *
37 * Author: Julian Elischer <julian@freebsd.org>
38 *
39 * $FreeBSD: head/sys/netgraph/ng_lmi.c 111119 2003-02-19 05:47:46Z imp $
40 * $Whistle: ng_lmi.c,v 1.38 1999/11/01 09:24:52 julian Exp $
41 */
42
43/*
44 * This node performs the frame relay LMI protocol. It knows how
45 * to do ITU Annex A, ANSI Annex D, and "Group-of-Four" variants
46 * of the protocol.
47 *
48 * A specific protocol can be forced by connecting the corresponding
49 * hook to DLCI 0 or 1023 (as appropriate) of a frame relay link.
50 *
51 * Alternately, this node can do auto-detection of the LMI protocol
52 * by connecting hook "auto0" to DLCI 0 and "auto1023" to DLCI 1023.
53 */
54
55#include <sys/param.h>
56#include <sys/systm.h>
57#include <sys/errno.h>
58#include <sys/kernel.h>
59#include <sys/malloc.h>
60#include <sys/mbuf.h>
61#include <sys/syslog.h>
62#include <netgraph/ng_message.h>
63#include <netgraph/netgraph.h>
64#include <netgraph/ng_lmi.h>
65
66/*
67 * Human readable names for LMI
68 */
69#define NAME_ANNEXA	NG_LMI_HOOK_ANNEXA
70#define NAME_ANNEXD	NG_LMI_HOOK_ANNEXD
71#define NAME_GROUP4	NG_LMI_HOOK_GROUPOF4
72#define NAME_NONE	"None"
73
74#define MAX_DLCIS	128
75#define MAXDLCI		1023
76
77/*
78 * DLCI states
79 */
80#define DLCI_NULL	0
81#define DLCI_UP		1
82#define DLCI_DOWN	2
83
84/*
85 * Any received LMI frame should be at least this long
86 */
87#define LMI_MIN_LENGTH	8	/* XXX verify */
88
89/*
90 * Netgraph node methods and type descriptor
91 */
92static ng_constructor_t	nglmi_constructor;
93static ng_rcvmsg_t	nglmi_rcvmsg;
94static ng_shutdown_t	nglmi_shutdown;
95static ng_newhook_t	nglmi_newhook;
96static ng_rcvdata_t	nglmi_rcvdata;
97static ng_disconnect_t	nglmi_disconnect;
98static int	nglmi_checkdata(hook_p hook, struct mbuf *m);
99
100static struct ng_type typestruct = {
101	NG_ABI_VERSION,
102	NG_LMI_NODE_TYPE,
103	NULL,
104	nglmi_constructor,
105	nglmi_rcvmsg,
106	nglmi_shutdown,
107	nglmi_newhook,
108	NULL,
109	NULL,
110	nglmi_rcvdata,
111	nglmi_disconnect,
112	NULL
113};
114NETGRAPH_INIT(lmi, &typestruct);
115
116/*
117 * Info and status per node
118 */
119struct nglmi_softc {
120	node_p  node;		/* netgraph node */
121	int     flags;		/* state */
122	int     poll_count;	/* the count of times for autolmi */
123	int     poll_state;	/* state of auto detect machine */
124	u_char  remote_seq;	/* sequence number the remote sent */
125	u_char  local_seq;	/* last sequence number we sent */
126	u_char  protoID;	/* 9 for group of 4, 8 otherwise */
127	u_long  seq_retries;	/* sent this how many time so far */
128	struct callout_handle handle;	/* see timeout(9) */
129	int     liv_per_full;
130	int     liv_rate;
131	int     livs;
132	int     need_full;
133	hook_p  lmi_channel;	/* whatever we ended up using */
134	hook_p  lmi_annexA;
135	hook_p  lmi_annexD;
136	hook_p  lmi_group4;
137	hook_p  lmi_channel0;	/* auto-detect on DLCI 0 */
138	hook_p  lmi_channel1023;/* auto-detect on DLCI 1023 */
139	char   *protoname;	/* cache protocol name */
140	u_char  dlci_state[MAXDLCI + 1];
141	int     invalidx;	/* next dlci's to invalidate */
142};
143typedef struct nglmi_softc *sc_p;
144
145/*
146 * Other internal functions
147 */
148static void	LMI_ticker(void *arg);
149static void	nglmi_startup_fixed(sc_p sc, hook_p hook);
150static void	nglmi_startup_auto(sc_p sc);
151static void	nglmi_startup(sc_p sc);
152static void	nglmi_inquire(sc_p sc, int full);
153static void	ngauto_state_machine(sc_p sc);
154
155/*
156 * Values for 'flags' field
157 * NB: the SCF_CONNECTED flag is set if and only if the timer is running.
158 */
159#define	SCF_CONNECTED	0x01	/* connected to something */
160#define	SCF_AUTO	0x02	/* we are auto-detecting */
161#define	SCF_FIXED	0x04	/* we are fixed from the start */
162
163#define	SCF_LMITYPE	0x18	/* mask for determining Annex mode */
164#define	SCF_NOLMI	0x00	/* no LMI type selected yet */
165#define	SCF_ANNEX_A	0x08	/* running annex A mode */
166#define	SCF_ANNEX_D	0x10	/* running annex D mode */
167#define	SCF_GROUP4	0x18	/* running group of 4 */
168
169#define SETLMITYPE(sc, annex)						\
170do {									\
171	(sc)->flags &= ~SCF_LMITYPE;					\
172	(sc)->flags |= (annex);						\
173} while (0)
174
175#define NOPROTO(sc) (((sc)->flags & SCF_LMITYPE) == SCF_NOLMI)
176#define ANNEXA(sc) (((sc)->flags & SCF_LMITYPE) == SCF_ANNEX_A)
177#define ANNEXD(sc) (((sc)->flags & SCF_LMITYPE) == SCF_ANNEX_D)
178#define GROUP4(sc) (((sc)->flags & SCF_LMITYPE) == SCF_GROUP4)
179
180#define LMIPOLLSIZE	3
181#define LMI_PATIENCE	8	/* declare all DLCI DOWN after N LMI failures */
182
183/*
184 * Node constructor
185 */
186static int
187nglmi_constructor(node_p node)
188{
189	sc_p sc;
190
191	MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO);
192	if (sc == NULL)
193		return (ENOMEM);
194	callout_handle_init(&sc->handle);
195	NG_NODE_SET_PRIVATE(node, sc);
196	sc->protoname = NAME_NONE;
197	sc->node = node;
198	sc->liv_per_full = NG_LMI_SEQ_PER_FULL;	/* make this dynamic */
199	sc->liv_rate = NG_LMI_KEEPALIVE_RATE;
200	return (0);
201}
202
203/*
204 * The LMI channel has a private pointer which is the same as the
205 * node private pointer. The debug channel has a NULL private pointer.
206 */
207static int
208nglmi_newhook(node_p node, hook_p hook, const char *name)
209{
210	sc_p sc = NG_NODE_PRIVATE(node);
211
212	if (strcmp(name, NG_LMI_HOOK_DEBUG) == 0) {
213		NG_HOOK_SET_PRIVATE(hook, NULL);
214		return (0);
215	}
216	if (sc->flags & SCF_CONNECTED) {
217		/* already connected, return an error */
218		return (EINVAL);
219	}
220	if (strcmp(name, NG_LMI_HOOK_ANNEXA) == 0) {
221		sc->lmi_annexA = hook;
222		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
223		sc->protoID = 8;
224		SETLMITYPE(sc, SCF_ANNEX_A);
225		sc->protoname = NAME_ANNEXA;
226		nglmi_startup_fixed(sc, hook);
227	} else if (strcmp(name, NG_LMI_HOOK_ANNEXD) == 0) {
228		sc->lmi_annexD = hook;
229		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
230		sc->protoID = 8;
231		SETLMITYPE(sc, SCF_ANNEX_D);
232		sc->protoname = NAME_ANNEXD;
233		nglmi_startup_fixed(sc, hook);
234	} else if (strcmp(name, NG_LMI_HOOK_GROUPOF4) == 0) {
235		sc->lmi_group4 = hook;
236		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
237		sc->protoID = 9;
238		SETLMITYPE(sc, SCF_GROUP4);
239		sc->protoname = NAME_GROUP4;
240		nglmi_startup_fixed(sc, hook);
241	} else if (strcmp(name, NG_LMI_HOOK_AUTO0) == 0) {
242		/* Note this, and if B is already installed, we're complete */
243		sc->lmi_channel0 = hook;
244		sc->protoname = NAME_NONE;
245		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
246		if (sc->lmi_channel1023)
247			nglmi_startup_auto(sc);
248	} else if (strcmp(name, NG_LMI_HOOK_AUTO1023) == 0) {
249		/* Note this, and if A is already installed, we're complete */
250		sc->lmi_channel1023 = hook;
251		sc->protoname = NAME_NONE;
252		NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
253		if (sc->lmi_channel0)
254			nglmi_startup_auto(sc);
255	} else
256		return (EINVAL);		/* unknown hook */
257	return (0);
258}
259
260/*
261 * We have just attached to a live (we hope) node.
262 * Fire out a LMI inquiry, and then start up the timers.
263 */
264static void
265LMI_ticker(void *arg)
266{
267	sc_p sc = arg;
268	int s = splnet();
269
270	if (sc->flags & SCF_AUTO) {
271		ngauto_state_machine(sc);
272		sc->handle = timeout(LMI_ticker, sc, NG_LMI_POLL_RATE * hz);
273	} else {
274		if (sc->livs++ >= sc->liv_per_full) {
275			nglmi_inquire(sc, 1);
276			/* sc->livs = 0; *//* do this when we get the answer! */
277		} else {
278			nglmi_inquire(sc, 0);
279		}
280		sc->handle = timeout(LMI_ticker, sc, sc->liv_rate * hz);
281	}
282	splx(s);
283}
284
285static void
286nglmi_startup_fixed(sc_p sc, hook_p hook)
287{
288	sc->flags |= (SCF_FIXED | SCF_CONNECTED);
289	sc->lmi_channel = hook;
290	nglmi_startup(sc);
291}
292
293static void
294nglmi_startup_auto(sc_p sc)
295{
296	sc->flags |= (SCF_AUTO | SCF_CONNECTED);
297	sc->poll_state = 0;	/* reset state machine */
298	sc->poll_count = 0;
299	nglmi_startup(sc);
300}
301
302static void
303nglmi_startup(sc_p sc)
304{
305	sc->remote_seq = 0;
306	sc->local_seq = 1;
307	sc->seq_retries = 0;
308	sc->livs = sc->liv_per_full - 1;
309	/* start off the ticker in 1 sec */
310	sc->handle = timeout(LMI_ticker, sc, hz);
311}
312
313#define META_PAD 16
314static void
315nglmi_inquire(sc_p sc, int full)
316{
317	struct mbuf *m;
318	char   *cptr, *start;
319	int     error;
320	meta_p  meta = NULL;
321
322	if (sc->lmi_channel == NULL)
323		return;
324	MGETHDR(m, M_DONTWAIT, MT_DATA);
325	if (m == NULL) {
326		log(LOG_ERR, "nglmi: unable to start up LMI processing\n");
327		return;
328	}
329	m->m_pkthdr.rcvif = NULL;
330	/* Allocate a meta struct (and leave some slop for options to be
331	 * added by other modules). */
332	MALLOC(meta, meta_p, sizeof(*meta) + META_PAD, M_NETGRAPH_META, M_NOWAIT);
333	if (meta != NULL) {	/* if it failed, well, it was optional anyhow */
334		meta->used_len = (u_short) sizeof(struct ng_meta);
335		meta->allocated_len
336		    = (u_short) sizeof(struct ng_meta) + META_PAD;
337		meta->flags = 0;
338		meta->priority = NG_LMI_LMI_PRIORITY;
339		meta->discardability = -1;
340	}
341	m->m_data += 4;		/* leave some room for a header */
342	cptr = start = mtod(m, char *);
343	/* add in the header for an LMI inquiry. */
344	*cptr++ = 0x03;		/* UI frame */
345	if (GROUP4(sc))
346		*cptr++ = 0x09;	/* proto discriminator */
347	else
348		*cptr++ = 0x08;	/* proto discriminator */
349	*cptr++ = 0x00;		/* call reference */
350	*cptr++ = 0x75;		/* inquiry */
351
352	/* If we are Annex-D, there is this extra thing.. */
353	if (ANNEXD(sc))
354		*cptr++ = 0x95;	/* ??? */
355	/* Add a request type */
356	if (ANNEXA(sc))
357		*cptr++ = 0x51;	/* report type */
358	else
359		*cptr++ = 0x01;	/* report type */
360	*cptr++ = 0x01;		/* size = 1 */
361	if (full)
362		*cptr++ = 0x00;	/* full */
363	else
364		*cptr++ = 0x01;	/* partial */
365
366	/* Add a link verification IE */
367	if (ANNEXA(sc))
368		*cptr++ = 0x53;	/* verification IE */
369	else
370		*cptr++ = 0x03;	/* verification IE */
371	*cptr++ = 0x02;		/* 2 extra bytes */
372	*cptr++ = sc->local_seq;
373	*cptr++ = sc->remote_seq;
374	sc->seq_retries++;
375
376	/* Send it */
377	m->m_len = m->m_pkthdr.len = cptr - start;
378	NG_SEND_DATA(error, sc->lmi_channel, m, meta);
379
380	/* If we've been sending requests for long enough, and there has
381	 * been no response, then mark as DOWN, any DLCIs that are UP. */
382	if (sc->seq_retries == LMI_PATIENCE) {
383		int     count;
384
385		for (count = 0; count < MAXDLCI; count++)
386			if (sc->dlci_state[count] == DLCI_UP)
387				sc->dlci_state[count] = DLCI_DOWN;
388	}
389}
390
391/*
392 * State machine for LMI auto-detect. The transitions are ordered
393 * to try the more likely possibilities first.
394 */
395static void
396ngauto_state_machine(sc_p sc)
397{
398	if ((sc->poll_count <= 0) || (sc->poll_count > LMIPOLLSIZE)) {
399		/* time to change states in the auto probe machine */
400		/* capture wild values of poll_count while we are at it */
401		sc->poll_count = LMIPOLLSIZE;
402		sc->poll_state++;
403	}
404	switch (sc->poll_state) {
405	case 7:
406		log(LOG_WARNING, "nglmi: no response from exchange\n");
407	default:		/* capture bad states */
408		sc->poll_state = 1;
409	case 1:
410		sc->lmi_channel = sc->lmi_channel0;
411		SETLMITYPE(sc, SCF_ANNEX_D);
412		break;
413	case 2:
414		sc->lmi_channel = sc->lmi_channel1023;
415		SETLMITYPE(sc, SCF_ANNEX_D);
416		break;
417	case 3:
418		sc->lmi_channel = sc->lmi_channel0;
419		SETLMITYPE(sc, SCF_ANNEX_A);
420		break;
421	case 4:
422		sc->lmi_channel = sc->lmi_channel1023;
423		SETLMITYPE(sc, SCF_GROUP4);
424		break;
425	case 5:
426		sc->lmi_channel = sc->lmi_channel1023;
427		SETLMITYPE(sc, SCF_ANNEX_A);
428		break;
429	case 6:
430		sc->lmi_channel = sc->lmi_channel0;
431		SETLMITYPE(sc, SCF_GROUP4);
432		break;
433	}
434
435	/* send an inquirey encoded appropriatly */
436	nglmi_inquire(sc, 0);
437	sc->poll_count--;
438}
439
440/*
441 * Receive a netgraph control message.
442 */
443static int
444nglmi_rcvmsg(node_p node, item_p item, hook_p lasthook)
445{
446	sc_p    sc = NG_NODE_PRIVATE(node);
447	struct ng_mesg *resp = NULL;
448	int     error = 0;
449	struct ng_mesg *msg;
450
451	NGI_GET_MSG(item, msg);
452	switch (msg->header.typecookie) {
453	case NGM_GENERIC_COOKIE:
454		switch (msg->header.cmd) {
455		case NGM_TEXT_STATUS:
456		    {
457			char   *arg;
458			int     pos, count;
459
460			NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
461			if (resp == NULL) {
462				error = ENOMEM;
463				break;
464			}
465			arg = resp->data;
466			pos = sprintf(arg, "protocol %s ", sc->protoname);
467			if (sc->flags & SCF_FIXED)
468				pos += sprintf(arg + pos, "fixed\n");
469			else if (sc->flags & SCF_AUTO)
470				pos += sprintf(arg + pos, "auto-detecting\n");
471			else
472				pos += sprintf(arg + pos, "auto on dlci %d\n",
473				    (sc->lmi_channel == sc->lmi_channel0) ?
474				    0 : 1023);
475			pos += sprintf(arg + pos,
476			    "keepalive period: %d seconds\n", sc->liv_rate);
477			pos += sprintf(arg + pos,
478			    "unacknowledged keepalives: %ld\n",
479			    sc->seq_retries);
480			for (count = 0;
481			     ((count <= MAXDLCI)
482			      && (pos < (NG_TEXTRESPONSE - 20)));
483			     count++) {
484				if (sc->dlci_state[count]) {
485					pos += sprintf(arg + pos,
486					       "dlci %d %s\n", count,
487					       (sc->dlci_state[count]
488					== DLCI_UP) ? "up" : "down");
489				}
490			}
491			resp->header.arglen = pos + 1;
492			break;
493		    }
494		default:
495			error = EINVAL;
496			break;
497		}
498		break;
499	case NGM_LMI_COOKIE:
500		switch (msg->header.cmd) {
501		case NGM_LMI_GET_STATUS:
502		    {
503			struct nglmistat *stat;
504			int k;
505
506			NG_MKRESPONSE(resp, msg, sizeof(*stat), M_NOWAIT);
507			if (!resp) {
508				error = ENOMEM;
509				break;
510			}
511			stat = (struct nglmistat *) resp->data;
512			strncpy(stat->proto,
513			     sc->protoname, sizeof(stat->proto) - 1);
514			strncpy(stat->hook,
515			      sc->protoname, sizeof(stat->hook) - 1);
516			stat->autod = !!(sc->flags & SCF_AUTO);
517			stat->fixed = !!(sc->flags & SCF_FIXED);
518			for (k = 0; k <= MAXDLCI; k++) {
519				switch (sc->dlci_state[k]) {
520				case DLCI_UP:
521					stat->up[k / 8] |= (1 << (k % 8));
522					/* fall through */
523				case DLCI_DOWN:
524					stat->seen[k / 8] |= (1 << (k % 8));
525					break;
526				}
527			}
528			break;
529		    }
530		default:
531			error = EINVAL;
532			break;
533		}
534		break;
535	default:
536		error = EINVAL;
537		break;
538	}
539
540	NG_RESPOND_MSG(error, node, item, resp);
541	NG_FREE_MSG(msg);
542	return (error);
543}
544
545#define STEPBY(stepsize)			\
546	do {					\
547		packetlen -= (stepsize);	\
548		data += (stepsize);		\
549	} while (0)
550
551/*
552 * receive data, and use it to update our status.
553 * Anything coming in on the debug port is discarded.
554 */
555static int
556nglmi_rcvdata(hook_p hook, item_p item)
557{
558	sc_p    sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
559	const	u_char *data;
560	unsigned short dlci;
561	u_short packetlen;
562	int     resptype_seen = 0;
563	int     seq_seen = 0;
564	struct mbuf *m;
565
566	NGI_GET_M(item, m);
567	NG_FREE_ITEM(item);
568	if (NG_HOOK_PRIVATE(hook) == NULL) {
569		goto drop;
570	}
571	packetlen = m->m_hdr.mh_len;
572
573	/* XXX what if it's more than 1 mbuf? */
574	if ((packetlen > MHLEN) && !(m->m_flags & M_EXT)) {
575		log(LOG_WARNING, "nglmi: packetlen (%d) too big\n", packetlen);
576		goto drop;
577	}
578	if (m->m_len < packetlen && (m = m_pullup(m, packetlen)) == NULL) {
579		log(LOG_WARNING,
580		    "nglmi: m_pullup failed for %d bytes\n", packetlen);
581		return (0);
582	}
583	if (nglmi_checkdata(hook, m) == 0)
584		return (0);
585
586	/* pass the first 4 bytes (already checked in the nglmi_checkdata()) */
587	data = mtod(m, const u_char *);
588	STEPBY(4);
589
590	/* Now check if there is a 'locking shift'. This is only seen in
591	 * Annex D frames. don't bother checking, we already did that. Don't
592	 * increment immediatly as it might not be there. */
593	if (ANNEXD(sc))
594		STEPBY(1);
595
596	/* If we get this far we should consider that it is a legitimate
597	 * frame and we know what it is. */
598	if (sc->flags & SCF_AUTO) {
599		/* note the hook that this valid channel came from and drop
600		 * out of auto probe mode. */
601		if (ANNEXA(sc))
602			sc->protoname = NAME_ANNEXA;
603		else if (ANNEXD(sc))
604			sc->protoname = NAME_ANNEXD;
605		else if (GROUP4(sc))
606			sc->protoname = NAME_GROUP4;
607		else {
608			log(LOG_ERR, "nglmi: No known type\n");
609			goto drop;
610		}
611		sc->lmi_channel = hook;
612		sc->flags &= ~SCF_AUTO;
613		log(LOG_INFO, "nglmi: auto-detected %s LMI on DLCI %d\n",
614		    sc->protoname, hook == sc->lmi_channel0 ? 0 : 1023);
615	}
616
617	/* While there is more data in the status packet, keep processing
618	 * status items. First make sure there is enough data for the
619	 * segment descriptor's length field. */
620	while (packetlen >= 2) {
621		u_int   segtype = data[0];
622		u_int   segsize = data[1];
623
624		/* Now that we know how long it claims to be, make sure
625		 * there is enough data for the next seg. */
626		if (packetlen < segsize + 2)
627			break;
628		switch (segtype) {
629		case 0x01:
630		case 0x51:
631			if (resptype_seen) {
632				log(LOG_WARNING, "nglmi: dup MSGTYPE\n");
633				goto nextIE;
634			}
635			resptype_seen++;
636			/* The remote end tells us what kind of response
637			 * this is. Only expect a type 0 or 1. if we are a
638			 * full status, invalidate a few DLCIs just to see
639			 * that they are still ok. */
640			if (segsize != 1)
641				goto nextIE;
642			switch (data[2]) {
643			case 1:
644				/* partial status, do no extra processing */
645				break;
646			case 0:
647			    {
648				int     count = 0;
649				int     idx = sc->invalidx;
650
651				for (count = 0; count < 10; count++) {
652					if (idx > MAXDLCI)
653						idx = 0;
654					if (sc->dlci_state[idx] == DLCI_UP)
655						sc->dlci_state[idx] = DLCI_DOWN;
656					idx++;
657				}
658				sc->invalidx = idx;
659				/* we got and we wanted one. relax
660				 * now.. but don't reset to 0 if it
661				 * was unrequested. */
662				if (sc->livs > sc->liv_per_full)
663					sc->livs = 0;
664				break;
665			    }
666			}
667			break;
668		case 0x03:
669		case 0x53:
670			/* The remote tells us what it thinks the sequence
671			 * numbers are. If it's not size 2, it must be a
672			 * duplicate to have gotten this far, skip it. */
673			if (seq_seen != 0)	/* already seen seq numbers */
674				goto nextIE;
675			if (segsize != 2)
676				goto nextIE;
677			sc->remote_seq = data[2];
678			if (sc->local_seq == data[3]) {
679				sc->local_seq++;
680				sc->seq_retries = 0;
681				/* Note that all 3 Frame protocols seem to
682				 * not like 0 as a sequence number. */
683				if (sc->local_seq == 0)
684					sc->local_seq = 1;
685			}
686			break;
687		case 0x07:
688		case 0x57:
689			/* The remote tells us about a DLCI that it knows
690			 * about. There may be many of these in a single
691			 * status response */
692			switch (segsize) {
693			case 6:/* only on 'group of 4' */
694				dlci = ((u_short) data[2] & 0xff) << 8;
695				dlci |= (data[3] & 0xff);
696				if ((dlci < 1024) && (dlci > 0)) {
697				  /* XXX */
698				}
699				break;
700			case 3:
701				dlci = ((u_short) data[2] & 0x3f) << 4;
702				dlci |= ((data[3] & 0x78) >> 3);
703				if ((dlci < 1024) && (dlci > 0)) {
704					/* set up the bottom half of the
705					 * support for that dlci if it's not
706					 * already been done */
707					/* store this information somewhere */
708				}
709				break;
710			default:
711				goto nextIE;
712			}
713			if (sc->dlci_state[dlci] != DLCI_UP) {
714				/* bring new DLCI to life */
715				/* may do more here some day */
716				if (sc->dlci_state[dlci] != DLCI_DOWN)
717					log(LOG_INFO,
718					    "nglmi: DLCI %d became active\n",
719					    dlci);
720				sc->dlci_state[dlci] = DLCI_UP;
721			}
722			break;
723		}
724nextIE:
725		STEPBY(segsize + 2);
726	}
727	NG_FREE_M(m);
728	return (0);
729
730drop:
731	NG_FREE_M(m);
732	return (EINVAL);
733}
734
735/*
736 * Check that a packet is entirely kosha.
737 * return 1 of ok, and 0 if not.
738 * All data is discarded if a 0 is returned.
739 */
740static int
741nglmi_checkdata(hook_p hook, struct mbuf *m)
742{
743	sc_p    sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
744	const	u_char *data;
745	u_short packetlen;
746	unsigned short dlci;
747	u_char  type;
748	u_char  nextbyte;
749	int     seq_seen = 0;
750	int     resptype_seen = 0;	/* 0 , 1 (partial) or 2 (full) */
751	int     highest_dlci = 0;
752
753	packetlen = m->m_hdr.mh_len;
754	data = mtod(m, const u_char *);
755	if (*data != 0x03) {
756		log(LOG_WARNING, "nglmi: unexpected value in LMI(%d)\n", 1);
757		goto reject;
758	}
759	STEPBY(1);
760
761	/* look at the protocol ID */
762	nextbyte = *data;
763	if (sc->flags & SCF_AUTO) {
764		SETLMITYPE(sc, SCF_NOLMI);	/* start with a clean slate */
765		switch (nextbyte) {
766		case 0x8:
767			sc->protoID = 8;
768			break;
769		case 0x9:
770			SETLMITYPE(sc, SCF_GROUP4);
771			sc->protoID = 9;
772			break;
773		default:
774			log(LOG_WARNING, "nglmi: bad Protocol ID(%d)\n",
775			    (int) nextbyte);
776			goto reject;
777		}
778	} else {
779		if (nextbyte != sc->protoID) {
780			log(LOG_WARNING, "nglmi: unexpected Protocol ID(%d)\n",
781			    (int) nextbyte);
782			goto reject;
783		}
784	}
785	STEPBY(1);
786
787	/* check call reference (always null in non ISDN frame relay) */
788	if (*data != 0x00) {
789		log(LOG_WARNING, "nglmi: unexpected Call Reference (0x%x)\n",
790		    data[-1]);
791		goto reject;
792	}
793	STEPBY(1);
794
795	/* check message type */
796	switch ((type = *data)) {
797	case 0x75:		/* Status enquiry */
798		log(LOG_WARNING, "nglmi: unexpected message type(0x%x)\n",
799		    data[-1]);
800		goto reject;
801	case 0x7D:		/* Status message */
802		break;
803	default:
804		log(LOG_WARNING,
805		    "nglmi: unexpected msg type(0x%x) \n", (int) type);
806		goto reject;
807	}
808	STEPBY(1);
809
810	/* Now check if there is a 'locking shift'. This is only seen in
811	 * Annex D frames. Don't increment immediately as it might not be
812	 * there. */
813	nextbyte = *data;
814	if (sc->flags & SCF_AUTO) {
815		if (!(GROUP4(sc))) {
816			if (nextbyte == 0x95) {
817				SETLMITYPE(sc, SCF_ANNEX_D);
818				STEPBY(1);
819			} else
820				SETLMITYPE(sc, SCF_ANNEX_A);
821		} else if (nextbyte == 0x95) {
822			log(LOG_WARNING, "nglmi: locking shift seen in G4\n");
823			goto reject;
824		}
825	} else {
826		if (ANNEXD(sc)) {
827			if (*data == 0x95)
828				STEPBY(1);
829			else {
830				log(LOG_WARNING,
831				    "nglmi: locking shift missing\n");
832				goto reject;
833			}
834		} else if (*data == 0x95) {
835			log(LOG_WARNING, "nglmi: locking shift seen\n");
836			goto reject;
837		}
838	}
839
840	/* While there is more data in the status packet, keep processing
841	 * status items. First make sure there is enough data for the
842	 * segment descriptor's length field. */
843	while (packetlen >= 2) {
844		u_int   segtype = data[0];
845		u_int   segsize = data[1];
846
847		/* Now that we know how long it claims to be, make sure
848		 * there is enough data for the next seg. */
849		if (packetlen < (segsize + 2)) {
850			log(LOG_WARNING, "nglmi: IE longer than packet\n");
851			break;
852		}
853		switch (segtype) {
854		case 0x01:
855		case 0x51:
856			/* According to MCI's HP analyser, we should just
857			 * ignore if there is mor ethan one of these (?). */
858			if (resptype_seen) {
859				log(LOG_WARNING, "nglmi: dup MSGTYPE\n");
860				goto nextIE;
861			}
862			if (segsize != 1) {
863				log(LOG_WARNING, "nglmi: MSGTYPE wrong size\n");
864				goto reject;
865			}
866			/* The remote end tells us what kind of response
867			 * this is. Only expect a type 0 or 1. if it was a
868			 * full (type 0) check we just asked for a type
869			 * full. */
870			switch (data[2]) {
871			case 1:/* partial */
872				if (sc->livs > sc->liv_per_full) {
873					log(LOG_WARNING,
874					  "nglmi: LIV when FULL expected\n");
875					goto reject;	/* need full */
876				}
877				resptype_seen = 1;
878				break;
879			case 0:/* full */
880				/* Full response is always acceptable */
881				resptype_seen = 2;
882				break;
883			default:
884				log(LOG_WARNING,
885				 "nglmi: Unknown report type %d\n", data[2]);
886				goto reject;
887			}
888			break;
889		case 0x03:
890		case 0x53:
891			/* The remote tells us what it thinks the sequence
892			 * numbers are. I would have thought that there
893			 * needs to be one and only one of these, but MCI
894			 * want us to just ignore extras. (?) */
895			if (resptype_seen == 0) {
896				log(LOG_WARNING, "nglmi: no TYPE before SEQ\n");
897				goto reject;
898			}
899			if (seq_seen != 0)	/* already seen seq numbers */
900				goto nextIE;
901			if (segsize != 2) {
902				log(LOG_WARNING, "nglmi: bad SEQ sts size\n");
903				goto reject;
904			}
905			if (sc->local_seq != data[3]) {
906				log(LOG_WARNING, "nglmi: unexpected SEQ\n");
907				goto reject;
908			}
909			seq_seen = 1;
910			break;
911		case 0x07:
912		case 0x57:
913			/* The remote tells us about a DLCI that it knows
914			 * about. There may be many of these in a single
915			 * status response */
916			if (seq_seen != 1) {	/* already seen seq numbers? */
917				log(LOG_WARNING,
918				    "nglmi: No sequence before DLCI\n");
919				goto reject;
920			}
921			if (resptype_seen != 2) {	/* must be full */
922				log(LOG_WARNING,
923				    "nglmi: No resp type before DLCI\n");
924				goto reject;
925			}
926			if (GROUP4(sc)) {
927				if (segsize != 6) {
928					log(LOG_WARNING,
929					    "nglmi: wrong IE segsize\n");
930					goto reject;
931				}
932				dlci = ((u_short) data[2] & 0xff) << 8;
933				dlci |= (data[3] & 0xff);
934			} else {
935				if (segsize != 3) {
936					log(LOG_WARNING,
937					    "nglmi: DLCI headersize of %d"
938					    " not supported\n", segsize - 1);
939					goto reject;
940				}
941				dlci = ((u_short) data[2] & 0x3f) << 4;
942				dlci |= ((data[3] & 0x78) >> 3);
943			}
944			/* async can only have one of these */
945#if 0				/* async not yet accepted */
946			if (async && highest_dlci) {
947				log(LOG_WARNING,
948				    "nglmi: Async with > 1 DLCI\n");
949				goto reject;
950			}
951#endif
952			/* Annex D says these will always be Ascending, but
953			 * the HP test for G4 says we should accept
954			 * duplicates, so for now allow that. ( <= vs. < ) */
955#if 0
956			/* MCI tests want us to accept out of order for AnxD */
957			if ((!GROUP4(sc)) && (dlci < highest_dlci)) {
958				/* duplicate or mis-ordered dlci */
959				/* (spec says they will increase in number) */
960				log(LOG_WARNING, "nglmi: DLCI out of order\n");
961				goto reject;
962			}
963#endif
964			if (dlci > 1023) {
965				log(LOG_WARNING, "nglmi: DLCI out of range\n");
966				goto reject;
967			}
968			highest_dlci = dlci;
969			break;
970		default:
971			log(LOG_WARNING,
972			    "nglmi: unknown LMI segment type %d\n", segtype);
973		}
974nextIE:
975		STEPBY(segsize + 2);
976	}
977	if (packetlen != 0) {	/* partial junk at end? */
978		log(LOG_WARNING,
979		    "nglmi: %d bytes extra at end of packet\n", packetlen);
980		goto print;
981	}
982	if (resptype_seen == 0) {
983		log(LOG_WARNING, "nglmi: No response type seen\n");
984		goto reject;	/* had no response type */
985	}
986	if (seq_seen == 0) {
987		log(LOG_WARNING, "nglmi: No sequence numbers seen\n");
988		goto reject;	/* had no sequence numbers */
989	}
990	return (1);
991
992print:
993	{
994		int     i, j, k, pos;
995		char    buf[100];
996		int     loc;
997		const	u_char *bp = mtod(m, const u_char *);
998
999		k = i = 0;
1000		loc = (m->m_hdr.mh_len - packetlen);
1001		log(LOG_WARNING, "nglmi: error at location %d\n", loc);
1002		while (k < m->m_hdr.mh_len) {
1003			pos = 0;
1004			j = 0;
1005			while ((j++ < 16) && k < m->m_hdr.mh_len) {
1006				pos += sprintf(buf + pos, "%c%02x",
1007					       ((loc == k) ? '>' : ' '),
1008					       bp[k]);
1009				k++;
1010			}
1011			if (i == 0)
1012				log(LOG_WARNING, "nglmi: packet data:%s\n", buf);
1013			else
1014				log(LOG_WARNING, "%04d              :%s\n", k, buf);
1015			i++;
1016		}
1017	}
1018	return (1);
1019reject:
1020	{
1021		int     i, j, k, pos;
1022		char    buf[100];
1023		int     loc;
1024		const	u_char *bp = mtod(m, const u_char *);
1025
1026		k = i = 0;
1027		loc = (m->m_hdr.mh_len - packetlen);
1028		log(LOG_WARNING, "nglmi: error at location %d\n", loc);
1029		while (k < m->m_hdr.mh_len) {
1030			pos = 0;
1031			j = 0;
1032			while ((j++ < 16) && k < m->m_hdr.mh_len) {
1033				pos += sprintf(buf + pos, "%c%02x",
1034					       ((loc == k) ? '>' : ' '),
1035					       bp[k]);
1036				k++;
1037			}
1038			if (i == 0)
1039				log(LOG_WARNING, "nglmi: packet data:%s\n", buf);
1040			else
1041				log(LOG_WARNING, "%04d              :%s\n", k, buf);
1042			i++;
1043		}
1044	}
1045	NG_FREE_M(m);
1046	return (0);
1047}
1048
1049/*
1050 * Do local shutdown processing..
1051 * Cut any remaining links and free our local resources.
1052 */
1053static int
1054nglmi_shutdown(node_p node)
1055{
1056	const sc_p sc = NG_NODE_PRIVATE(node);
1057
1058	NG_NODE_SET_PRIVATE(node, NULL);
1059	NG_NODE_UNREF(sc->node);
1060	FREE(sc, M_NETGRAPH);
1061	return (0);
1062}
1063
1064/*
1065 * Hook disconnection
1066 * For this type, removal of any link except "debug" destroys the node.
1067 */
1068static int
1069nglmi_disconnect(hook_p hook)
1070{
1071	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1072
1073	/* OK to remove debug hook(s) */
1074	if (NG_HOOK_PRIVATE(hook) == NULL)
1075		return (0);
1076
1077	/* Stop timer if it's currently active */
1078	if (sc->flags & SCF_CONNECTED)
1079		untimeout(LMI_ticker, sc, sc->handle);
1080
1081	/* Self-destruct */
1082	if (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))
1083		ng_rmnode_self(NG_HOOK_NODE(hook));
1084	return (0);
1085}
1086
1087