Merge from vendor branch HEIMDAL:
[dragonfly.git] / sys / netgraph / lmi / ng_lmi.c
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: 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.5 2004/09/16 03:43:09 dillon Exp $
41  * $Whistle: ng_lmi.c,v 1.38 1999/11/01 09:24:52 julian Exp $
42  */
43
44 /*
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.
48  *
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.
51  *
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.
54  */
55
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 <netgraph/ng_message.h>
64 #include <netgraph/netgraph.h>
65 #include "ng_lmi.h"
66
67 /*
68  * Human readable names for LMI
69  */
70 #define NAME_ANNEXA     NG_LMI_HOOK_ANNEXA
71 #define NAME_ANNEXD     NG_LMI_HOOK_ANNEXD
72 #define NAME_GROUP4     NG_LMI_HOOK_GROUPOF4
73 #define NAME_NONE       "None"
74
75 #define MAX_DLCIS       128
76 #define MAXDLCI         1023
77
78 /*
79  * DLCI states
80  */
81 #define DLCI_NULL       0
82 #define DLCI_UP         1
83 #define DLCI_DOWN       2
84
85 /*
86  * Any received LMI frame should be at least this long
87  */
88 #define LMI_MIN_LENGTH  8       /* XXX verify */
89
90 /*
91  * Netgraph node methods and type descriptor
92  */
93 static ng_constructor_t nglmi_constructor;
94 static ng_rcvmsg_t      nglmi_rcvmsg;
95 static ng_shutdown_t    nglmi_rmnode;
96 static ng_newhook_t     nglmi_newhook;
97 static ng_rcvdata_t     nglmi_rcvdata;
98 static ng_disconnect_t  nglmi_disconnect;
99 static int      nglmi_checkdata(hook_p hook, struct mbuf *m, meta_p meta);
100
101 static struct ng_type typestruct = {
102         NG_VERSION,
103         NG_LMI_NODE_TYPE,
104         NULL,
105         nglmi_constructor,
106         nglmi_rcvmsg,
107         nglmi_rmnode,
108         nglmi_newhook,
109         NULL,
110         NULL,
111         nglmi_rcvdata,
112         nglmi_rcvdata,
113         nglmi_disconnect,
114         NULL
115 };
116 NETGRAPH_INIT(lmi, &typestruct);
117
118 /*
119  * Info and status per node
120  */
121 struct nglmi_softc {
122         node_p  node;           /* netgraph node */
123         int     flags;          /* state */
124         int     poll_count;     /* the count of times for autolmi */
125         int     poll_state;     /* state of auto detect machine */
126         u_char  remote_seq;     /* sequence number the remote sent */
127         u_char  local_seq;      /* last sequence number we sent */
128         u_char  protoID;        /* 9 for group of 4, 8 otherwise */
129         u_long  seq_retries;    /* sent this how many time so far */
130         struct callout timeout; /* see timeout(9) */
131         int     liv_per_full;
132         int     liv_rate;
133         int     livs;
134         int     need_full;
135         hook_p  lmi_channel;    /* whatever we ended up using */
136         hook_p  lmi_annexA;
137         hook_p  lmi_annexD;
138         hook_p  lmi_group4;
139         hook_p  lmi_channel0;   /* auto-detect on DLCI 0 */
140         hook_p  lmi_channel1023;/* auto-detect on DLCI 1023 */
141         char   *protoname;      /* cache protocol name */
142         u_char  dlci_state[MAXDLCI + 1];
143         int     invalidx;       /* next dlci's to invalidate */
144 };
145 typedef struct nglmi_softc *sc_p;
146
147 /*
148  * Other internal functions
149  */
150 static void     LMI_ticker(void *arg);
151 static void     nglmi_startup_fixed(sc_p sc, hook_p hook);
152 static void     nglmi_startup_auto(sc_p sc);
153 static void     nglmi_startup(sc_p sc);
154 static void     nglmi_inquire(sc_p sc, int full);
155 static void     ngauto_state_machine(sc_p sc);
156
157 /*
158  * Values for 'flags' field
159  * NB: the SCF_CONNECTED flag is set if and only if the timer is running.
160  */
161 #define SCF_CONNECTED   0x01    /* connected to something */
162 #define SCF_AUTO        0x02    /* we are auto-detecting */
163 #define SCF_FIXED       0x04    /* we are fixed from the start */
164
165 #define SCF_LMITYPE     0x18    /* mask for determining Annex mode */
166 #define SCF_NOLMI       0x00    /* no LMI type selected yet */
167 #define SCF_ANNEX_A     0x08    /* running annex A mode */
168 #define SCF_ANNEX_D     0x10    /* running annex D mode */
169 #define SCF_GROUP4      0x18    /* running group of 4 */
170
171 #define SETLMITYPE(sc, annex)                                           \
172 do {                                                                    \
173         (sc)->flags &= ~SCF_LMITYPE;                                    \
174         (sc)->flags |= (annex);                                         \
175 } while (0)
176
177 #define NOPROTO(sc) (((sc)->flags & SCF_LMITYPE) == SCF_NOLMI)
178 #define ANNEXA(sc) (((sc)->flags & SCF_LMITYPE) == SCF_ANNEX_A)
179 #define ANNEXD(sc) (((sc)->flags & SCF_LMITYPE) == SCF_ANNEX_D)
180 #define GROUP4(sc) (((sc)->flags & SCF_LMITYPE) == SCF_GROUP4)
181
182 #define LMIPOLLSIZE     3
183 #define LMI_PATIENCE    8       /* declare all DLCI DOWN after N LMI failures */
184
185 /*
186  * Node constructor
187  */
188 static int
189 nglmi_constructor(node_p *nodep)
190 {
191         sc_p sc;
192         int error = 0;
193
194         MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT);
195         if (sc == NULL)
196                 return (ENOMEM);
197         bzero(sc, sizeof(*sc));
198
199         callout_init(&sc->timeout);
200         if ((error = ng_make_node_common(&typestruct, nodep))) {
201                 FREE(sc, M_NETGRAPH);
202                 return (error);
203         }
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);
210 }
211
212 /*
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.
215  */
216 static int
217 nglmi_newhook(node_p node, hook_p hook, const char *name)
218 {
219         sc_p sc = node->private;
220
221         if (strcmp(name, NG_LMI_HOOK_DEBUG) == 0) {
222                 hook->private = NULL;
223                 return (0);
224         }
225         if (sc->flags & SCF_CONNECTED) {
226                 /* already connected, return an error */
227                 return (EINVAL);
228         }
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);
267 }
268
269 /*
270  * We have just attached to a live (we hope) node.
271  * Fire out a LMI inquiry, and then start up the timers.
272  */
273 static void
274 LMI_ticker(void *arg)
275 {
276         sc_p sc = arg;
277         int s = splnet();
278
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);
289                 }
290                 callout_reset(&sc->timeout, sc->liv_rate * hz, LMI_ticker, sc);
291         }
292         splx(s);
293 }
294
295 static void
296 nglmi_startup_fixed(sc_p sc, hook_p hook)
297 {
298         sc->flags |= (SCF_FIXED | SCF_CONNECTED);
299         sc->lmi_channel = hook;
300         nglmi_startup(sc);
301 }
302
303 static void
304 nglmi_startup_auto(sc_p sc)
305 {
306         sc->flags |= (SCF_AUTO | SCF_CONNECTED);
307         sc->poll_state = 0;     /* reset state machine */
308         sc->poll_count = 0;
309         nglmi_startup(sc);
310 }
311
312 static void
313 nglmi_startup(sc_p sc)
314 {
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);
321 }
322
323 #define META_PAD 16
324 static void
325 nglmi_inquire(sc_p sc, int full)
326 {
327         struct mbuf *m;
328         char   *cptr, *start;
329         int     error;
330         meta_p  meta = NULL;
331
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;
338         }
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;
352         }
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 */
363
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 */
377
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++;
387
388         /* Send it */
389         m->m_len = m->m_pkthdr.len = cptr - start;
390         NG_SEND_DATA(error, sc->lmi_channel, m, meta);
391
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;
396
397                 for (count = 0; count < MAXDLCI; count++)
398                         if (sc->dlci_state[count] == DLCI_UP)
399                                 sc->dlci_state[count] = DLCI_DOWN;
400         }
401 }
402
403 /*
404  * State machine for LMI auto-detect. The transitions are ordered
405  * to try the more likely possibilities first.
406  */
407 static void
408 ngauto_state_machine(sc_p sc)
409 {
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++;
415         }
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;
445         }
446
447         /* send an inquirey encoded appropriatly */
448         nglmi_inquire(sc, 0);
449         sc->poll_count--;
450 }
451
452 /*
453  * Receive a netgraph control message.
454  */
455 static int
456 nglmi_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr,
457              struct ng_mesg **resp)
458 {
459         int     error = 0;
460         sc_p    sc = node->private;
461
462         switch (msg->header.typecookie) {
463         case NGM_GENERIC_COOKIE:
464                 switch (msg->header.cmd) {
465                 case NGM_TEXT_STATUS:
466                     {
467                         char   *arg;
468                         int     pos, count;
469
470                         NG_MKRESPONSE(*resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
471                         if (*resp == NULL) {
472                                 error = ENOMEM;
473                                 break;
474                         }
475                         arg = (*resp)->data;
476                         pos = sprintf(arg, "protocol %s ", sc->protoname);
477                         if (sc->flags & SCF_FIXED)
478                                 pos += sprintf(arg + pos, "fixed\n");
479                         else if (sc->flags & SCF_AUTO)
480                                 pos += sprintf(arg + pos, "auto-detecting\n");
481                         else
482                                 pos += sprintf(arg + pos, "auto on dlci %d\n",
483                                     (sc->lmi_channel == sc->lmi_channel0) ?
484                                     0 : 1023);
485                         pos += sprintf(arg + pos,
486                             "keepalive period: %d seconds\n", sc->liv_rate);
487                         pos += sprintf(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 += sprintf(arg + pos,
496                                                "dlci %d %s\n", count,
497                                                (sc->dlci_state[count]
498                                         == DLCI_UP) ? "up" : "down");
499                                 }
500                         }
501                         (*resp)->header.arglen = pos + 1;
502                         break;
503                     }
504                 default:
505                         error = EINVAL;
506                         break;
507                 }
508                 break;
509         case NGM_LMI_COOKIE:
510                 switch (msg->header.cmd) {
511                 case NGM_LMI_GET_STATUS:
512                     {
513                         struct nglmistat *stat;
514                         int k;
515
516                         NG_MKRESPONSE(*resp, msg, sizeof(*stat), M_NOWAIT);
517                         if (!*resp) {
518                                 error = ENOMEM;
519                                 break;
520                         }
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;
536                                 }
537                         }
538                         break;
539                     }
540                 default:
541                         error = EINVAL;
542                         break;
543                 }
544                 break;
545         default:
546                 error = EINVAL;
547                 break;
548         }
549         FREE(msg, M_NETGRAPH);
550         return (error);
551 }
552
553 #define STEPBY(stepsize)                        \
554         do {                                    \
555                 packetlen -= (stepsize);        \
556                 data += (stepsize);             \
557         } while (0)
558
559 /*
560  * receive data, and use it to update our status.
561  * Anything coming in on the debug port is discarded.
562  */
563 static int
564 nglmi_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
565 {
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;
572
573         if (hook->private == NULL) {
574                 goto drop;
575         }
576         packetlen = m->m_hdr.mh_len;
577
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;
582         }
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);
588         }
589         if (nglmi_checkdata(hook, m, meta) == 0)
590                 return (0);
591
592         /* pass the first 4 bytes (already checked in the nglmi_checkdata()) */
593         data = mtod(m, const u_char *);
594         STEPBY(4);
595
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);
601
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;
616                 }
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);
621         }
622
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];
629
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;
640                         }
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:
653                             {
654                                 int     count = 0;
655                                 int     idx = sc->invalidx;
656
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++;
663                                 }
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;
671                             }
672                         }
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;
691                         }
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 */
704                                 }
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 */
714                                 }
715                                 break;
716                         default:
717                                 goto nextIE;
718                         }
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;
727                         }
728                         break;
729                 }
730 nextIE:
731                 STEPBY(segsize + 2);
732         }
733         NG_FREE_DATA(m, meta);
734         return (0);
735
736 drop:
737         NG_FREE_DATA(m, meta);
738         return (EINVAL);
739 }
740
741 /*
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.
745  */
746 static int
747 nglmi_checkdata(hook_p hook, struct mbuf *m, meta_p meta)
748 {
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;
758
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;
764         }
765         STEPBY(1);
766
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;
783                 }
784         } else {
785                 if (nextbyte != sc->protoID) {
786                         log(LOG_WARNING, "nglmi: unexpected Protocol ID(%d)\n",
787                             (int) nextbyte);
788                         goto reject;
789                 }
790         }
791         STEPBY(1);
792
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;
798         }
799         STEPBY(1);
800
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;
813         }
814         STEPBY(1);
815
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;
830                 }
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;
839                         }
840                 } else if (*data == 0x95) {
841                         log(LOG_WARNING, "nglmi: locking shift seen\n");
842                         goto reject;
843                 }
844         }
845
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];
852
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;
858                 }
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;
867                         }
868                         if (segsize != 1) {
869                                 log(LOG_WARNING, "nglmi: MSGTYPE wrong size\n");
870                                 goto reject;
871                         }
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 */
882                                 }
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;
893                         }
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;
904                         }
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;
910                         }
911                         if (sc->local_seq != data[3]) {
912                                 log(LOG_WARNING, "nglmi: unexpected SEQ\n");
913                                 goto reject;
914                         }
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;
926                         }
927                         if (resptype_seen != 2) {       /* must be full */
928                                 log(LOG_WARNING,
929                                     "nglmi: No resp type before DLCI\n");
930                                 goto reject;
931                         }
932                         if (GROUP4(sc)) {
933                                 if (segsize != 6) {
934                                         log(LOG_WARNING,
935                                             "nglmi: wrong IE segsize\n");
936                                         goto reject;
937                                 }
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;
946                                 }
947                                 dlci = ((u_short) data[2] & 0x3f) << 4;
948                                 dlci |= ((data[3] & 0x78) >> 3);
949                         }
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;
956                         }
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;
968                         }
969 #endif
970                         if (dlci > 1023) {
971                                 log(LOG_WARNING, "nglmi: DLCI out of range\n");
972                                 goto reject;
973                         }
974                         highest_dlci = dlci;
975                         break;
976                 default:
977                         log(LOG_WARNING,
978                             "nglmi: unknown LMI segment type %d\n", segtype);
979                 }
980 nextIE:
981                 STEPBY(segsize + 2);
982         }
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;
987         }
988         if (resptype_seen == 0) {
989                 log(LOG_WARNING, "nglmi: No response type seen\n");
990                 goto reject;    /* had no response type */
991         }
992         if (seq_seen == 0) {
993                 log(LOG_WARNING, "nglmi: No sequence numbers seen\n");
994                 goto reject;    /* had no sequence numbers */
995         }
996         return (1);
997
998 print:
999         {
1000                 int     i, j, k, pos;
1001                 char    buf[100];
1002                 int     loc;
1003                 const   u_char *bp = mtod(m, const u_char *);
1004
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 += sprintf(buf + pos, "%c%02x",
1013                                                ((loc == k) ? '>' : ' '),
1014                                                bp[k]);
1015                                 k++;
1016                         }
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++;
1022                 }
1023         }
1024         return (1);
1025 reject:
1026         {
1027                 int     i, j, k, pos;
1028                 char    buf[100];
1029                 int     loc;
1030                 const   u_char *bp = mtod(m, const u_char *);
1031
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 += sprintf(buf + pos, "%c%02x",
1040                                                ((loc == k) ? '>' : ' '),
1041                                                bp[k]);
1042                                 k++;
1043                         }
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++;
1049                 }
1050         }
1051         NG_FREE_DATA(m, meta);
1052         return (0);
1053 }
1054
1055 /*
1056  * Do local shutdown processing..
1057  * Cut any remaining links and free our local resources.
1058  */
1059 static int
1060 nglmi_rmnode(node_p node)
1061 {
1062         const sc_p sc = node->private;
1063
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);
1071 }
1072
1073 /*
1074  * Hook disconnection
1075  * For this type, removal of any link except "debug" destroys the node.
1076  */
1077 static int
1078 nglmi_disconnect(hook_p hook)
1079 {
1080         const sc_p sc = hook->node->private;
1081
1082         /* OK to remove debug hook(s) */
1083         if (hook->private == NULL)
1084                 return (0);
1085
1086         /* Stop timer if it's currently active */
1087         if (sc->flags & SCF_CONNECTED)
1088                 callout_stop(&sc->timeout);
1089
1090         /* Self-destruct */
1091         ng_rmnode(hook->node);
1092         return (0);
1093 }
1094