Unbreak HAMMER root mounts
[dragonfly.git] / sys / netgraph / lmi / ng_lmi.c
blob732a3e7f1c550ee4340048366b9b52ef8b8c33ea
2 /*
3 * ng_lmi.c
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.
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.
37 * Author: Julian Elischer <julian@freebsd.org>
39 * $FreeBSD: src/sys/netgraph/ng_lmi.c,v 1.5.2.3 2002/07/02 22:17:18 archie Exp $
40 * $DragonFly: src/sys/netgraph/lmi/ng_lmi.c,v 1.8 2008/01/05 14:02:39 swildner Exp $
41 * $Whistle: ng_lmi.c,v 1.38 1999/11/01 09:24:52 julian Exp $
45 * This node performs the frame relay LMI protocol. It knows how
46 * to do ITU Annex A, ANSI Annex D, and "Group-of-Four" variants
47 * of the protocol.
49 * A specific protocol can be forced by connecting the corresponding
50 * hook to DLCI 0 or 1023 (as appropriate) of a frame relay link.
52 * Alternately, this node can do auto-detection of the LMI protocol
53 * by connecting hook "auto0" to DLCI 0 and "auto1023" to DLCI 1023.
56 #include <sys/param.h>
57 #include <sys/systm.h>
58 #include <sys/errno.h>
59 #include <sys/kernel.h>
60 #include <sys/malloc.h>
61 #include <sys/mbuf.h>
62 #include <sys/syslog.h>
63 #include <sys/thread2.h>
64 #include <netgraph/ng_message.h>
65 #include <netgraph/netgraph.h>
66 #include "ng_lmi.h"
69 * Human readable names for LMI
71 #define NAME_ANNEXA NG_LMI_HOOK_ANNEXA
72 #define NAME_ANNEXD NG_LMI_HOOK_ANNEXD
73 #define NAME_GROUP4 NG_LMI_HOOK_GROUPOF4
74 #define NAME_NONE "None"
76 #define MAX_DLCIS 128
77 #define MAXDLCI 1023
80 * DLCI states
82 #define DLCI_NULL 0
83 #define DLCI_UP 1
84 #define DLCI_DOWN 2
87 * Any received LMI frame should be at least this long
89 #define LMI_MIN_LENGTH 8 /* XXX verify */
92 * Netgraph node methods and type descriptor
94 static ng_constructor_t nglmi_constructor;
95 static ng_rcvmsg_t nglmi_rcvmsg;
96 static ng_shutdown_t nglmi_rmnode;
97 static ng_newhook_t nglmi_newhook;
98 static ng_rcvdata_t nglmi_rcvdata;
99 static ng_disconnect_t nglmi_disconnect;
100 static int nglmi_checkdata(hook_p hook, struct mbuf *m, meta_p meta);
102 static struct ng_type typestruct = {
103 NG_VERSION,
104 NG_LMI_NODE_TYPE,
105 NULL,
106 nglmi_constructor,
107 nglmi_rcvmsg,
108 nglmi_rmnode,
109 nglmi_newhook,
110 NULL,
111 NULL,
112 nglmi_rcvdata,
113 nglmi_rcvdata,
114 nglmi_disconnect,
115 NULL
117 NETGRAPH_INIT(lmi, &typestruct);
120 * Info and status per node
122 struct nglmi_softc {
123 node_p node; /* netgraph node */
124 int flags; /* state */
125 int poll_count; /* the count of times for autolmi */
126 int poll_state; /* state of auto detect machine */
127 u_char remote_seq; /* sequence number the remote sent */
128 u_char local_seq; /* last sequence number we sent */
129 u_char protoID; /* 9 for group of 4, 8 otherwise */
130 u_long seq_retries; /* sent this how many time so far */
131 struct callout timeout; /* see timeout(9) */
132 int liv_per_full;
133 int liv_rate;
134 int livs;
135 int need_full;
136 hook_p lmi_channel; /* whatever we ended up using */
137 hook_p lmi_annexA;
138 hook_p lmi_annexD;
139 hook_p lmi_group4;
140 hook_p lmi_channel0; /* auto-detect on DLCI 0 */
141 hook_p lmi_channel1023;/* auto-detect on DLCI 1023 */
142 char *protoname; /* cache protocol name */
143 u_char dlci_state[MAXDLCI + 1];
144 int invalidx; /* next dlci's to invalidate */
146 typedef struct nglmi_softc *sc_p;
149 * Other internal functions
151 static void LMI_ticker(void *arg);
152 static void nglmi_startup_fixed(sc_p sc, hook_p hook);
153 static void nglmi_startup_auto(sc_p sc);
154 static void nglmi_startup(sc_p sc);
155 static void nglmi_inquire(sc_p sc, int full);
156 static void ngauto_state_machine(sc_p sc);
159 * Values for 'flags' field
160 * NB: the SCF_CONNECTED flag is set if and only if the timer is running.
162 #define SCF_CONNECTED 0x01 /* connected to something */
163 #define SCF_AUTO 0x02 /* we are auto-detecting */
164 #define SCF_FIXED 0x04 /* we are fixed from the start */
166 #define SCF_LMITYPE 0x18 /* mask for determining Annex mode */
167 #define SCF_NOLMI 0x00 /* no LMI type selected yet */
168 #define SCF_ANNEX_A 0x08 /* running annex A mode */
169 #define SCF_ANNEX_D 0x10 /* running annex D mode */
170 #define SCF_GROUP4 0x18 /* running group of 4 */
172 #define SETLMITYPE(sc, annex) \
173 do { \
174 (sc)->flags &= ~SCF_LMITYPE; \
175 (sc)->flags |= (annex); \
176 } while (0)
178 #define NOPROTO(sc) (((sc)->flags & SCF_LMITYPE) == SCF_NOLMI)
179 #define ANNEXA(sc) (((sc)->flags & SCF_LMITYPE) == SCF_ANNEX_A)
180 #define ANNEXD(sc) (((sc)->flags & SCF_LMITYPE) == SCF_ANNEX_D)
181 #define GROUP4(sc) (((sc)->flags & SCF_LMITYPE) == SCF_GROUP4)
183 #define LMIPOLLSIZE 3
184 #define LMI_PATIENCE 8 /* declare all DLCI DOWN after N LMI failures */
187 * Node constructor
189 static int
190 nglmi_constructor(node_p *nodep)
192 sc_p sc;
193 int error = 0;
195 MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO);
196 if (sc == NULL)
197 return (ENOMEM);
199 callout_init(&sc->timeout);
200 if ((error = ng_make_node_common(&typestruct, nodep))) {
201 FREE(sc, M_NETGRAPH);
202 return (error);
204 (*nodep)->private = sc;
205 sc->protoname = NAME_NONE;
206 sc->node = *nodep;
207 sc->liv_per_full = NG_LMI_SEQ_PER_FULL; /* make this dynamic */
208 sc->liv_rate = NG_LMI_KEEPALIVE_RATE;
209 return (0);
213 * The LMI channel has a private pointer which is the same as the
214 * node private pointer. The debug channel has a NULL private pointer.
216 static int
217 nglmi_newhook(node_p node, hook_p hook, const char *name)
219 sc_p sc = node->private;
221 if (strcmp(name, NG_LMI_HOOK_DEBUG) == 0) {
222 hook->private = NULL;
223 return (0);
225 if (sc->flags & SCF_CONNECTED) {
226 /* already connected, return an error */
227 return (EINVAL);
229 if (strcmp(name, NG_LMI_HOOK_ANNEXA) == 0) {
230 sc->lmi_annexA = hook;
231 hook->private = node->private;
232 sc->protoID = 8;
233 SETLMITYPE(sc, SCF_ANNEX_A);
234 sc->protoname = NAME_ANNEXA;
235 nglmi_startup_fixed(sc, hook);
236 } else if (strcmp(name, NG_LMI_HOOK_ANNEXD) == 0) {
237 sc->lmi_annexD = hook;
238 hook->private = node->private;
239 sc->protoID = 8;
240 SETLMITYPE(sc, SCF_ANNEX_D);
241 sc->protoname = NAME_ANNEXD;
242 nglmi_startup_fixed(sc, hook);
243 } else if (strcmp(name, NG_LMI_HOOK_GROUPOF4) == 0) {
244 sc->lmi_group4 = hook;
245 hook->private = node->private;
246 sc->protoID = 9;
247 SETLMITYPE(sc, SCF_GROUP4);
248 sc->protoname = NAME_GROUP4;
249 nglmi_startup_fixed(sc, hook);
250 } else if (strcmp(name, NG_LMI_HOOK_AUTO0) == 0) {
251 /* Note this, and if B is already installed, we're complete */
252 sc->lmi_channel0 = hook;
253 sc->protoname = NAME_NONE;
254 hook->private = node->private;
255 if (sc->lmi_channel1023)
256 nglmi_startup_auto(sc);
257 } else if (strcmp(name, NG_LMI_HOOK_AUTO1023) == 0) {
258 /* Note this, and if A is already installed, we're complete */
259 sc->lmi_channel1023 = hook;
260 sc->protoname = NAME_NONE;
261 hook->private = node->private;
262 if (sc->lmi_channel0)
263 nglmi_startup_auto(sc);
264 } else
265 return (EINVAL); /* unknown hook */
266 return (0);
270 * We have just attached to a live (we hope) node.
271 * Fire out a LMI inquiry, and then start up the timers.
273 static void
274 LMI_ticker(void *arg)
276 sc_p sc = arg;
278 crit_enter();
279 if (sc->flags & SCF_AUTO) {
280 ngauto_state_machine(sc);
281 callout_reset(&sc->timeout, NG_LMI_POLL_RATE * hz,
282 LMI_ticker, sc);
283 } else {
284 if (sc->livs++ >= sc->liv_per_full) {
285 nglmi_inquire(sc, 1);
286 /* sc->livs = 0; *//* do this when we get the answer! */
287 } else {
288 nglmi_inquire(sc, 0);
290 callout_reset(&sc->timeout, sc->liv_rate * hz, LMI_ticker, sc);
292 crit_exit();
295 static void
296 nglmi_startup_fixed(sc_p sc, hook_p hook)
298 sc->flags |= (SCF_FIXED | SCF_CONNECTED);
299 sc->lmi_channel = hook;
300 nglmi_startup(sc);
303 static void
304 nglmi_startup_auto(sc_p sc)
306 sc->flags |= (SCF_AUTO | SCF_CONNECTED);
307 sc->poll_state = 0; /* reset state machine */
308 sc->poll_count = 0;
309 nglmi_startup(sc);
312 static void
313 nglmi_startup(sc_p sc)
315 sc->remote_seq = 0;
316 sc->local_seq = 1;
317 sc->seq_retries = 0;
318 sc->livs = sc->liv_per_full - 1;
319 /* start off the ticker in 1 sec */
320 callout_reset(&sc->timeout, hz, LMI_ticker, sc);
323 #define META_PAD 16
324 static void
325 nglmi_inquire(sc_p sc, int full)
327 struct mbuf *m;
328 char *cptr, *start;
329 int error;
330 meta_p meta = NULL;
332 if (sc->lmi_channel == NULL)
333 return;
334 MGETHDR(m, MB_DONTWAIT, MT_DATA);
335 if (m == NULL) {
336 log(LOG_ERR, "nglmi: unable to start up LMI processing\n");
337 return;
339 m->m_pkthdr.rcvif = NULL;
340 /* Allocate a meta struct (and leave some slop for options to be
341 * added by other modules). */
342 /* MALLOC(meta, meta_p, sizeof( struct ng_meta) + META_PAD,
343 * M_NETGRAPH, M_NOWAIT); */
344 MALLOC(meta, meta_p, sizeof(*meta) + META_PAD, M_NETGRAPH, M_NOWAIT);
345 if (meta != NULL) { /* if it failed, well, it was optional anyhow */
346 meta->used_len = (u_short) sizeof(struct ng_meta);
347 meta->allocated_len
348 = (u_short) sizeof(struct ng_meta) + META_PAD;
349 meta->flags = 0;
350 meta->priority = NG_LMI_LMI_PRIORITY;
351 meta->discardability = -1;
353 m->m_data += 4; /* leave some room for a header */
354 cptr = start = mtod(m, char *);
355 /* add in the header for an LMI inquiry. */
356 *cptr++ = 0x03; /* UI frame */
357 if (GROUP4(sc))
358 *cptr++ = 0x09; /* proto discriminator */
359 else
360 *cptr++ = 0x08; /* proto discriminator */
361 *cptr++ = 0x00; /* call reference */
362 *cptr++ = 0x75; /* inquiry */
364 /* If we are Annex-D, there is this extra thing.. */
365 if (ANNEXD(sc))
366 *cptr++ = 0x95; /* ??? */
367 /* Add a request type */
368 if (ANNEXA(sc))
369 *cptr++ = 0x51; /* report type */
370 else
371 *cptr++ = 0x01; /* report type */
372 *cptr++ = 0x01; /* size = 1 */
373 if (full)
374 *cptr++ = 0x00; /* full */
375 else
376 *cptr++ = 0x01; /* partial */
378 /* Add a link verification IE */
379 if (ANNEXA(sc))
380 *cptr++ = 0x53; /* verification IE */
381 else
382 *cptr++ = 0x03; /* verification IE */
383 *cptr++ = 0x02; /* 2 extra bytes */
384 *cptr++ = sc->local_seq;
385 *cptr++ = sc->remote_seq;
386 sc->seq_retries++;
388 /* Send it */
389 m->m_len = m->m_pkthdr.len = cptr - start;
390 NG_SEND_DATA(error, sc->lmi_channel, m, meta);
392 /* If we've been sending requests for long enough, and there has
393 * been no response, then mark as DOWN, any DLCIs that are UP. */
394 if (sc->seq_retries == LMI_PATIENCE) {
395 int count;
397 for (count = 0; count < MAXDLCI; count++)
398 if (sc->dlci_state[count] == DLCI_UP)
399 sc->dlci_state[count] = DLCI_DOWN;
404 * State machine for LMI auto-detect. The transitions are ordered
405 * to try the more likely possibilities first.
407 static void
408 ngauto_state_machine(sc_p sc)
410 if ((sc->poll_count <= 0) || (sc->poll_count > LMIPOLLSIZE)) {
411 /* time to change states in the auto probe machine */
412 /* capture wild values of poll_count while we are at it */
413 sc->poll_count = LMIPOLLSIZE;
414 sc->poll_state++;
416 switch (sc->poll_state) {
417 case 7:
418 log(LOG_WARNING, "nglmi: no response from exchange\n");
419 default: /* capture bad states */
420 sc->poll_state = 1;
421 case 1:
422 sc->lmi_channel = sc->lmi_channel0;
423 SETLMITYPE(sc, SCF_ANNEX_D);
424 break;
425 case 2:
426 sc->lmi_channel = sc->lmi_channel1023;
427 SETLMITYPE(sc, SCF_ANNEX_D);
428 break;
429 case 3:
430 sc->lmi_channel = sc->lmi_channel0;
431 SETLMITYPE(sc, SCF_ANNEX_A);
432 break;
433 case 4:
434 sc->lmi_channel = sc->lmi_channel1023;
435 SETLMITYPE(sc, SCF_GROUP4);
436 break;
437 case 5:
438 sc->lmi_channel = sc->lmi_channel1023;
439 SETLMITYPE(sc, SCF_ANNEX_A);
440 break;
441 case 6:
442 sc->lmi_channel = sc->lmi_channel0;
443 SETLMITYPE(sc, SCF_GROUP4);
444 break;
447 /* send an inquirey encoded appropriatly */
448 nglmi_inquire(sc, 0);
449 sc->poll_count--;
453 * Receive a netgraph control message.
455 static int
456 nglmi_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr,
457 struct ng_mesg **resp)
459 int error = 0;
460 sc_p sc = node->private;
462 switch (msg->header.typecookie) {
463 case NGM_GENERIC_COOKIE:
464 switch (msg->header.cmd) {
465 case NGM_TEXT_STATUS:
467 char *arg;
468 int pos, count;
470 NG_MKRESPONSE(*resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
471 if (*resp == NULL) {
472 error = ENOMEM;
473 break;
475 arg = (*resp)->data;
476 pos = ksprintf(arg, "protocol %s ", sc->protoname);
477 if (sc->flags & SCF_FIXED)
478 pos += ksprintf(arg + pos, "fixed\n");
479 else if (sc->flags & SCF_AUTO)
480 pos += ksprintf(arg + pos, "auto-detecting\n");
481 else
482 pos += ksprintf(arg + pos, "auto on dlci %d\n",
483 (sc->lmi_channel == sc->lmi_channel0) ?
484 0 : 1023);
485 pos += ksprintf(arg + pos,
486 "keepalive period: %d seconds\n", sc->liv_rate);
487 pos += ksprintf(arg + pos,
488 "unacknowledged keepalives: %ld\n",
489 sc->seq_retries);
490 for (count = 0;
491 ((count <= MAXDLCI)
492 && (pos < (NG_TEXTRESPONSE - 20)));
493 count++) {
494 if (sc->dlci_state[count]) {
495 pos += ksprintf(arg + pos,
496 "dlci %d %s\n", count,
497 (sc->dlci_state[count]
498 == DLCI_UP) ? "up" : "down");
501 (*resp)->header.arglen = pos + 1;
502 break;
504 default:
505 error = EINVAL;
506 break;
508 break;
509 case NGM_LMI_COOKIE:
510 switch (msg->header.cmd) {
511 case NGM_LMI_GET_STATUS:
513 struct nglmistat *stat;
514 int k;
516 NG_MKRESPONSE(*resp, msg, sizeof(*stat), M_NOWAIT);
517 if (!*resp) {
518 error = ENOMEM;
519 break;
521 stat = (struct nglmistat *) (*resp)->data;
522 strncpy(stat->proto,
523 sc->protoname, sizeof(stat->proto) - 1);
524 strncpy(stat->hook,
525 sc->protoname, sizeof(stat->hook) - 1);
526 stat->autod = !!(sc->flags & SCF_AUTO);
527 stat->fixed = !!(sc->flags & SCF_FIXED);
528 for (k = 0; k <= MAXDLCI; k++) {
529 switch (sc->dlci_state[k]) {
530 case DLCI_UP:
531 stat->up[k / 8] |= (1 << (k % 8));
532 /* fall through */
533 case DLCI_DOWN:
534 stat->seen[k / 8] |= (1 << (k % 8));
535 break;
538 break;
540 default:
541 error = EINVAL;
542 break;
544 break;
545 default:
546 error = EINVAL;
547 break;
549 FREE(msg, M_NETGRAPH);
550 return (error);
553 #define STEPBY(stepsize) \
554 do { \
555 packetlen -= (stepsize); \
556 data += (stepsize); \
557 } while (0)
560 * receive data, and use it to update our status.
561 * Anything coming in on the debug port is discarded.
563 static int
564 nglmi_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
566 sc_p sc = hook->node->private;
567 const u_char *data;
568 unsigned short dlci;
569 u_short packetlen;
570 int resptype_seen = 0;
571 int seq_seen = 0;
573 if (hook->private == NULL) {
574 goto drop;
576 packetlen = m->m_hdr.mh_len;
578 /* XXX what if it's more than 1 mbuf? */
579 if ((packetlen > MHLEN) && !(m->m_flags & M_EXT)) {
580 log(LOG_WARNING, "nglmi: packetlen (%d) too big\n", packetlen);
581 goto drop;
583 if (m->m_len < packetlen && (m = m_pullup(m, packetlen)) == NULL) {
584 log(LOG_WARNING,
585 "nglmi: m_pullup failed for %d bytes\n", packetlen);
586 NG_FREE_META(meta);
587 return (0);
589 if (nglmi_checkdata(hook, m, meta) == 0)
590 return (0);
592 /* pass the first 4 bytes (already checked in the nglmi_checkdata()) */
593 data = mtod(m, const u_char *);
594 STEPBY(4);
596 /* Now check if there is a 'locking shift'. This is only seen in
597 * Annex D frames. don't bother checking, we already did that. Don't
598 * increment immediatly as it might not be there. */
599 if (ANNEXD(sc))
600 STEPBY(1);
602 /* If we get this far we should consider that it is a legitimate
603 * frame and we know what it is. */
604 if (sc->flags & SCF_AUTO) {
605 /* note the hook that this valid channel came from and drop
606 * out of auto probe mode. */
607 if (ANNEXA(sc))
608 sc->protoname = NAME_ANNEXA;
609 else if (ANNEXD(sc))
610 sc->protoname = NAME_ANNEXD;
611 else if (GROUP4(sc))
612 sc->protoname = NAME_GROUP4;
613 else {
614 log(LOG_ERR, "nglmi: No known type\n");
615 goto drop;
617 sc->lmi_channel = hook;
618 sc->flags &= ~SCF_AUTO;
619 log(LOG_INFO, "nglmi: auto-detected %s LMI on DLCI %d\n",
620 sc->protoname, hook == sc->lmi_channel0 ? 0 : 1023);
623 /* While there is more data in the status packet, keep processing
624 * status items. First make sure there is enough data for the
625 * segment descriptor's length field. */
626 while (packetlen >= 2) {
627 u_int segtype = data[0];
628 u_int segsize = data[1];
630 /* Now that we know how long it claims to be, make sure
631 * there is enough data for the next seg. */
632 if (packetlen < segsize + 2)
633 break;
634 switch (segtype) {
635 case 0x01:
636 case 0x51:
637 if (resptype_seen) {
638 log(LOG_WARNING, "nglmi: dup MSGTYPE\n");
639 goto nextIE;
641 resptype_seen++;
642 /* The remote end tells us what kind of response
643 * this is. Only expect a type 0 or 1. if we are a
644 * full status, invalidate a few DLCIs just to see
645 * that they are still ok. */
646 if (segsize != 1)
647 goto nextIE;
648 switch (data[2]) {
649 case 1:
650 /* partial status, do no extra processing */
651 break;
652 case 0:
654 int count = 0;
655 int idx = sc->invalidx;
657 for (count = 0; count < 10; count++) {
658 if (idx > MAXDLCI)
659 idx = 0;
660 if (sc->dlci_state[idx] == DLCI_UP)
661 sc->dlci_state[idx] = DLCI_DOWN;
662 idx++;
664 sc->invalidx = idx;
665 /* we got and we wanted one. relax
666 * now.. but don't reset to 0 if it
667 * was unrequested. */
668 if (sc->livs > sc->liv_per_full)
669 sc->livs = 0;
670 break;
673 break;
674 case 0x03:
675 case 0x53:
676 /* The remote tells us what it thinks the sequence
677 * numbers are. If it's not size 2, it must be a
678 * duplicate to have gotten this far, skip it. */
679 if (seq_seen != 0) /* already seen seq numbers */
680 goto nextIE;
681 if (segsize != 2)
682 goto nextIE;
683 sc->remote_seq = data[2];
684 if (sc->local_seq == data[3]) {
685 sc->local_seq++;
686 sc->seq_retries = 0;
687 /* Note that all 3 Frame protocols seem to
688 * not like 0 as a sequence number. */
689 if (sc->local_seq == 0)
690 sc->local_seq = 1;
692 break;
693 case 0x07:
694 case 0x57:
695 /* The remote tells us about a DLCI that it knows
696 * about. There may be many of these in a single
697 * status response */
698 switch (segsize) {
699 case 6:/* only on 'group of 4' */
700 dlci = ((u_short) data[2] & 0xff) << 8;
701 dlci |= (data[3] & 0xff);
702 if ((dlci < 1024) && (dlci > 0)) {
703 /* XXX */
705 break;
706 case 3:
707 dlci = ((u_short) data[2] & 0x3f) << 4;
708 dlci |= ((data[3] & 0x78) >> 3);
709 if ((dlci < 1024) && (dlci > 0)) {
710 /* set up the bottom half of the
711 * support for that dlci if it's not
712 * already been done */
713 /* store this information somewhere */
715 break;
716 default:
717 goto nextIE;
719 if (sc->dlci_state[dlci] != DLCI_UP) {
720 /* bring new DLCI to life */
721 /* may do more here some day */
722 if (sc->dlci_state[dlci] != DLCI_DOWN)
723 log(LOG_INFO,
724 "nglmi: DLCI %d became active\n",
725 dlci);
726 sc->dlci_state[dlci] = DLCI_UP;
728 break;
730 nextIE:
731 STEPBY(segsize + 2);
733 NG_FREE_DATA(m, meta);
734 return (0);
736 drop:
737 NG_FREE_DATA(m, meta);
738 return (EINVAL);
742 * Check that a packet is entirely kosha.
743 * return 1 of ok, and 0 if not.
744 * All data is discarded if a 0 is returned.
746 static int
747 nglmi_checkdata(hook_p hook, struct mbuf *m, meta_p meta)
749 sc_p sc = hook->node->private;
750 const u_char *data;
751 u_short packetlen;
752 unsigned short dlci;
753 u_char type;
754 u_char nextbyte;
755 int seq_seen = 0;
756 int resptype_seen = 0; /* 0 , 1 (partial) or 2 (full) */
757 int highest_dlci = 0;
759 packetlen = m->m_hdr.mh_len;
760 data = mtod(m, const u_char *);
761 if (*data != 0x03) {
762 log(LOG_WARNING, "nglmi: unexpected value in LMI(%d)\n", 1);
763 goto reject;
765 STEPBY(1);
767 /* look at the protocol ID */
768 nextbyte = *data;
769 if (sc->flags & SCF_AUTO) {
770 SETLMITYPE(sc, SCF_NOLMI); /* start with a clean slate */
771 switch (nextbyte) {
772 case 0x8:
773 sc->protoID = 8;
774 break;
775 case 0x9:
776 SETLMITYPE(sc, SCF_GROUP4);
777 sc->protoID = 9;
778 break;
779 default:
780 log(LOG_WARNING, "nglmi: bad Protocol ID(%d)\n",
781 (int) nextbyte);
782 goto reject;
784 } else {
785 if (nextbyte != sc->protoID) {
786 log(LOG_WARNING, "nglmi: unexpected Protocol ID(%d)\n",
787 (int) nextbyte);
788 goto reject;
791 STEPBY(1);
793 /* check call reference (always null in non ISDN frame relay) */
794 if (*data != 0x00) {
795 log(LOG_WARNING, "nglmi: unexpected Call Reference (0x%x)\n",
796 data[-1]);
797 goto reject;
799 STEPBY(1);
801 /* check message type */
802 switch ((type = *data)) {
803 case 0x75: /* Status enquiry */
804 log(LOG_WARNING, "nglmi: unexpected message type(0x%x)\n",
805 data[-1]);
806 goto reject;
807 case 0x7D: /* Status message */
808 break;
809 default:
810 log(LOG_WARNING,
811 "nglmi: unexpected msg type(0x%x) \n", (int) type);
812 goto reject;
814 STEPBY(1);
816 /* Now check if there is a 'locking shift'. This is only seen in
817 * Annex D frames. Don't increment immediately as it might not be
818 * there. */
819 nextbyte = *data;
820 if (sc->flags & SCF_AUTO) {
821 if (!(GROUP4(sc))) {
822 if (nextbyte == 0x95) {
823 SETLMITYPE(sc, SCF_ANNEX_D);
824 STEPBY(1);
825 } else
826 SETLMITYPE(sc, SCF_ANNEX_A);
827 } else if (nextbyte == 0x95) {
828 log(LOG_WARNING, "nglmi: locking shift seen in G4\n");
829 goto reject;
831 } else {
832 if (ANNEXD(sc)) {
833 if (*data == 0x95)
834 STEPBY(1);
835 else {
836 log(LOG_WARNING,
837 "nglmi: locking shift missing\n");
838 goto reject;
840 } else if (*data == 0x95) {
841 log(LOG_WARNING, "nglmi: locking shift seen\n");
842 goto reject;
846 /* While there is more data in the status packet, keep processing
847 * status items. First make sure there is enough data for the
848 * segment descriptor's length field. */
849 while (packetlen >= 2) {
850 u_int segtype = data[0];
851 u_int segsize = data[1];
853 /* Now that we know how long it claims to be, make sure
854 * there is enough data for the next seg. */
855 if (packetlen < (segsize + 2)) {
856 log(LOG_WARNING, "nglmi: IE longer than packet\n");
857 break;
859 switch (segtype) {
860 case 0x01:
861 case 0x51:
862 /* According to MCI's HP analyser, we should just
863 * ignore if there is mor ethan one of these (?). */
864 if (resptype_seen) {
865 log(LOG_WARNING, "nglmi: dup MSGTYPE\n");
866 goto nextIE;
868 if (segsize != 1) {
869 log(LOG_WARNING, "nglmi: MSGTYPE wrong size\n");
870 goto reject;
872 /* The remote end tells us what kind of response
873 * this is. Only expect a type 0 or 1. if it was a
874 * full (type 0) check we just asked for a type
875 * full. */
876 switch (data[2]) {
877 case 1:/* partial */
878 if (sc->livs > sc->liv_per_full) {
879 log(LOG_WARNING,
880 "nglmi: LIV when FULL expected\n");
881 goto reject; /* need full */
883 resptype_seen = 1;
884 break;
885 case 0:/* full */
886 /* Full response is always acceptable */
887 resptype_seen = 2;
888 break;
889 default:
890 log(LOG_WARNING,
891 "nglmi: Unknown report type %d\n", data[2]);
892 goto reject;
894 break;
895 case 0x03:
896 case 0x53:
897 /* The remote tells us what it thinks the sequence
898 * numbers are. I would have thought that there
899 * needs to be one and only one of these, but MCI
900 * want us to just ignore extras. (?) */
901 if (resptype_seen == 0) {
902 log(LOG_WARNING, "nglmi: no TYPE before SEQ\n");
903 goto reject;
905 if (seq_seen != 0) /* already seen seq numbers */
906 goto nextIE;
907 if (segsize != 2) {
908 log(LOG_WARNING, "nglmi: bad SEQ sts size\n");
909 goto reject;
911 if (sc->local_seq != data[3]) {
912 log(LOG_WARNING, "nglmi: unexpected SEQ\n");
913 goto reject;
915 seq_seen = 1;
916 break;
917 case 0x07:
918 case 0x57:
919 /* The remote tells us about a DLCI that it knows
920 * about. There may be many of these in a single
921 * status response */
922 if (seq_seen != 1) { /* already seen seq numbers? */
923 log(LOG_WARNING,
924 "nglmi: No sequence before DLCI\n");
925 goto reject;
927 if (resptype_seen != 2) { /* must be full */
928 log(LOG_WARNING,
929 "nglmi: No resp type before DLCI\n");
930 goto reject;
932 if (GROUP4(sc)) {
933 if (segsize != 6) {
934 log(LOG_WARNING,
935 "nglmi: wrong IE segsize\n");
936 goto reject;
938 dlci = ((u_short) data[2] & 0xff) << 8;
939 dlci |= (data[3] & 0xff);
940 } else {
941 if (segsize != 3) {
942 log(LOG_WARNING,
943 "nglmi: DLCI headersize of %d"
944 " not supported\n", segsize - 1);
945 goto reject;
947 dlci = ((u_short) data[2] & 0x3f) << 4;
948 dlci |= ((data[3] & 0x78) >> 3);
950 /* async can only have one of these */
951 #if 0 /* async not yet accepted */
952 if (async && highest_dlci) {
953 log(LOG_WARNING,
954 "nglmi: Async with > 1 DLCI\n");
955 goto reject;
957 #endif
958 /* Annex D says these will always be Ascending, but
959 * the HP test for G4 says we should accept
960 * duplicates, so for now allow that. ( <= vs. < ) */
961 #if 0
962 /* MCI tests want us to accept out of order for AnxD */
963 if ((!GROUP4(sc)) && (dlci < highest_dlci)) {
964 /* duplicate or mis-ordered dlci */
965 /* (spec says they will increase in number) */
966 log(LOG_WARNING, "nglmi: DLCI out of order\n");
967 goto reject;
969 #endif
970 if (dlci > 1023) {
971 log(LOG_WARNING, "nglmi: DLCI out of range\n");
972 goto reject;
974 highest_dlci = dlci;
975 break;
976 default:
977 log(LOG_WARNING,
978 "nglmi: unknown LMI segment type %d\n", segtype);
980 nextIE:
981 STEPBY(segsize + 2);
983 if (packetlen != 0) { /* partial junk at end? */
984 log(LOG_WARNING,
985 "nglmi: %d bytes extra at end of packet\n", packetlen);
986 goto print;
988 if (resptype_seen == 0) {
989 log(LOG_WARNING, "nglmi: No response type seen\n");
990 goto reject; /* had no response type */
992 if (seq_seen == 0) {
993 log(LOG_WARNING, "nglmi: No sequence numbers seen\n");
994 goto reject; /* had no sequence numbers */
996 return (1);
998 print:
1000 int i, j, k, pos;
1001 char buf[100];
1002 int loc;
1003 const u_char *bp = mtod(m, const u_char *);
1005 k = i = 0;
1006 loc = (m->m_hdr.mh_len - packetlen);
1007 log(LOG_WARNING, "nglmi: error at location %d\n", loc);
1008 while (k < m->m_hdr.mh_len) {
1009 pos = 0;
1010 j = 0;
1011 while ((j++ < 16) && k < m->m_hdr.mh_len) {
1012 pos += ksprintf(buf + pos, "%c%02x",
1013 ((loc == k) ? '>' : ' '),
1014 bp[k]);
1015 k++;
1017 if (i == 0)
1018 log(LOG_WARNING, "nglmi: packet data:%s\n", buf);
1019 else
1020 log(LOG_WARNING, "%04d :%s\n", k, buf);
1021 i++;
1024 return (1);
1025 reject:
1027 int i, j, k, pos;
1028 char buf[100];
1029 int loc;
1030 const u_char *bp = mtod(m, const u_char *);
1032 k = i = 0;
1033 loc = (m->m_hdr.mh_len - packetlen);
1034 log(LOG_WARNING, "nglmi: error at location %d\n", loc);
1035 while (k < m->m_hdr.mh_len) {
1036 pos = 0;
1037 j = 0;
1038 while ((j++ < 16) && k < m->m_hdr.mh_len) {
1039 pos += ksprintf(buf + pos, "%c%02x",
1040 ((loc == k) ? '>' : ' '),
1041 bp[k]);
1042 k++;
1044 if (i == 0)
1045 log(LOG_WARNING, "nglmi: packet data:%s\n", buf);
1046 else
1047 log(LOG_WARNING, "%04d :%s\n", k, buf);
1048 i++;
1051 NG_FREE_DATA(m, meta);
1052 return (0);
1056 * Do local shutdown processing..
1057 * Cut any remaining links and free our local resources.
1059 static int
1060 nglmi_rmnode(node_p node)
1062 const sc_p sc = node->private;
1064 node->flags |= NG_INVALID;
1065 ng_cutlinks(node);
1066 ng_unname(node);
1067 node->private = NULL;
1068 ng_unref(sc->node);
1069 FREE(sc, M_NETGRAPH);
1070 return (0);
1074 * Hook disconnection
1075 * For this type, removal of any link except "debug" destroys the node.
1077 static int
1078 nglmi_disconnect(hook_p hook)
1080 const sc_p sc = hook->node->private;
1082 /* OK to remove debug hook(s) */
1083 if (hook->private == NULL)
1084 return (0);
1086 /* Stop timer if it's currently active */
1087 if (sc->flags & SCF_CONNECTED)
1088 callout_stop(&sc->timeout);
1090 /* Self-destruct */
1091 ng_rmnode(hook->node);
1092 return (0);