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