/* * ng_lmi.c * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * Author: Julian Elischer * * $FreeBSD$ * $Whistle: ng_lmi.c,v 1.38 1999/11/01 09:24:52 julian Exp $ */ /* * This node performs the frame relay LMI protocol. It knows how * to do ITU Annex A, ANSI Annex D, and "Group-of-Four" variants * of the protocol. * * A specific protocol can be forced by connecting the corresponding * hook to DLCI 0 or 1023 (as appropriate) of a frame relay link. * * Alternately, this node can do auto-detection of the LMI protocol * by connecting hook "auto0" to DLCI 0 and "auto1023" to DLCI 1023. */ #include #include #include #include #include #include #include #include #include #include /* * Human readable names for LMI */ #define NAME_ANNEXA NG_LMI_HOOK_ANNEXA #define NAME_ANNEXD NG_LMI_HOOK_ANNEXD #define NAME_GROUP4 NG_LMI_HOOK_GROUPOF4 #define NAME_NONE "None" #define MAX_DLCIS 128 #define MAXDLCI 1023 /* * DLCI states */ #define DLCI_NULL 0 #define DLCI_UP 1 #define DLCI_DOWN 2 /* * Any received LMI frame should be at least this long */ #define LMI_MIN_LENGTH 8 /* XXX verify */ /* * Netgraph node methods and type descriptor */ static ng_constructor_t nglmi_constructor; static ng_rcvmsg_t nglmi_rcvmsg; static ng_shutdown_t nglmi_rmnode; static ng_newhook_t nglmi_newhook; static ng_rcvdata_t nglmi_rcvdata; static ng_disconnect_t nglmi_disconnect; static int nglmi_checkdata(hook_p hook, struct mbuf *m, meta_p meta); static struct ng_type typestruct = { NG_VERSION, NG_LMI_NODE_TYPE, NULL, nglmi_constructor, nglmi_rcvmsg, nglmi_rmnode, nglmi_newhook, NULL, NULL, nglmi_rcvdata, nglmi_rcvdata, nglmi_disconnect, NULL }; NETGRAPH_INIT(lmi, &typestruct); /* * Info and status per node */ struct nglmi_softc { node_p node; /* netgraph node */ int flags; /* state */ int poll_count; /* the count of times for autolmi */ int poll_state; /* state of auto detect machine */ u_char remote_seq; /* sequence number the remote sent */ u_char local_seq; /* last sequence number we sent */ u_char protoID; /* 9 for group of 4, 8 otherwise */ u_long seq_retries; /* sent this how many time so far */ struct callout_handle handle; /* see timeout(9) */ int liv_per_full; int liv_rate; int livs; int need_full; hook_p lmi_channel; /* whatever we ended up using */ hook_p lmi_annexA; hook_p lmi_annexD; hook_p lmi_group4; hook_p lmi_channel0; /* auto-detect on DLCI 0 */ hook_p lmi_channel1023;/* auto-detect on DLCI 1023 */ char *protoname; /* cache protocol name */ u_char dlci_state[MAXDLCI + 1]; int invalidx; /* next dlci's to invalidate */ }; typedef struct nglmi_softc *sc_p; /* * Other internal functions */ static void LMI_ticker(void *arg); static void nglmi_startup_fixed(sc_p sc, hook_p hook); static void nglmi_startup_auto(sc_p sc); static void nglmi_startup(sc_p sc); static void nglmi_inquire(sc_p sc, int full); static void ngauto_state_machine(sc_p sc); /* * Values for 'flags' field * NB: the SCF_CONNECTED flag is set if and only if the timer is running. */ #define SCF_CONNECTED 0x01 /* connected to something */ #define SCF_AUTO 0x02 /* we are auto-detecting */ #define SCF_FIXED 0x04 /* we are fixed from the start */ #define SCF_LMITYPE 0x18 /* mask for determining Annex mode */ #define SCF_NOLMI 0x00 /* no LMI type selected yet */ #define SCF_ANNEX_A 0x08 /* running annex A mode */ #define SCF_ANNEX_D 0x10 /* running annex D mode */ #define SCF_GROUP4 0x18 /* running group of 4 */ #define SETLMITYPE(sc, annex) \ do { \ (sc)->flags &= ~SCF_LMITYPE; \ (sc)->flags |= (annex); \ } while (0) #define NOPROTO(sc) (((sc)->flags & SCF_LMITYPE) == SCF_NOLMI) #define ANNEXA(sc) (((sc)->flags & SCF_LMITYPE) == SCF_ANNEX_A) #define ANNEXD(sc) (((sc)->flags & SCF_LMITYPE) == SCF_ANNEX_D) #define GROUP4(sc) (((sc)->flags & SCF_LMITYPE) == SCF_GROUP4) #define LMIPOLLSIZE 3 #define LMI_PATIENCE 8 /* declare all DLCI DOWN after N LMI failures */ /* * Node constructor */ static int nglmi_constructor(node_p *nodep) { sc_p sc; int error = 0; MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO); if (sc == NULL) return (ENOMEM); callout_handle_init(&sc->handle); if ((error = ng_make_node_common(&typestruct, nodep))) { FREE(sc, M_NETGRAPH); return (error); } (*nodep)->private = sc; sc->protoname = NAME_NONE; sc->node = *nodep; sc->liv_per_full = NG_LMI_SEQ_PER_FULL; /* make this dynamic */ sc->liv_rate = NG_LMI_KEEPALIVE_RATE; return (0); } /* * The LMI channel has a private pointer which is the same as the * node private pointer. The debug channel has a NULL private pointer. */ static int nglmi_newhook(node_p node, hook_p hook, const char *name) { sc_p sc = node->private; if (strcmp(name, NG_LMI_HOOK_DEBUG) == 0) { hook->private = NULL; return (0); } if (sc->flags & SCF_CONNECTED) { /* already connected, return an error */ return (EINVAL); } if (strcmp(name, NG_LMI_HOOK_ANNEXA) == 0) { sc->lmi_annexA = hook; hook->private = node->private; sc->protoID = 8; SETLMITYPE(sc, SCF_ANNEX_A); sc->protoname = NAME_ANNEXA; nglmi_startup_fixed(sc, hook); } else if (strcmp(name, NG_LMI_HOOK_ANNEXD) == 0) { sc->lmi_annexD = hook; hook->private = node->private; sc->protoID = 8; SETLMITYPE(sc, SCF_ANNEX_D); sc->protoname = NAME_ANNEXD; nglmi_startup_fixed(sc, hook); } else if (strcmp(name, NG_LMI_HOOK_GROUPOF4) == 0) { sc->lmi_group4 = hook; hook->private = node->private; sc->protoID = 9; SETLMITYPE(sc, SCF_GROUP4); sc->protoname = NAME_GROUP4; nglmi_startup_fixed(sc, hook); } else if (strcmp(name, NG_LMI_HOOK_AUTO0) == 0) { /* Note this, and if B is already installed, we're complete */ sc->lmi_channel0 = hook; sc->protoname = NAME_NONE; hook->private = node->private; if (sc->lmi_channel1023) nglmi_startup_auto(sc); } else if (strcmp(name, NG_LMI_HOOK_AUTO1023) == 0) { /* Note this, and if A is already installed, we're complete */ sc->lmi_channel1023 = hook; sc->protoname = NAME_NONE; hook->private = node->private; if (sc->lmi_channel0) nglmi_startup_auto(sc); } else return (EINVAL); /* unknown hook */ return (0); } /* * We have just attached to a live (we hope) node. * Fire out a LMI inquiry, and then start up the timers. */ static void LMI_ticker(void *arg) { sc_p sc = arg; int s = splnet(); if (sc->flags & SCF_AUTO) { ngauto_state_machine(sc); sc->handle = timeout(LMI_ticker, sc, NG_LMI_POLL_RATE * hz); } else { if (sc->livs++ >= sc->liv_per_full) { nglmi_inquire(sc, 1); /* sc->livs = 0; *//* do this when we get the answer! */ } else { nglmi_inquire(sc, 0); } sc->handle = timeout(LMI_ticker, sc, sc->liv_rate * hz); } splx(s); } static void nglmi_startup_fixed(sc_p sc, hook_p hook) { sc->flags |= (SCF_FIXED | SCF_CONNECTED); sc->lmi_channel = hook; nglmi_startup(sc); } static void nglmi_startup_auto(sc_p sc) { sc->flags |= (SCF_AUTO | SCF_CONNECTED); sc->poll_state = 0; /* reset state machine */ sc->poll_count = 0; nglmi_startup(sc); } static void nglmi_startup(sc_p sc) { sc->remote_seq = 0; sc->local_seq = 1; sc->seq_retries = 0; sc->livs = sc->liv_per_full - 1; /* start off the ticker in 1 sec */ sc->handle = timeout(LMI_ticker, sc, hz); } #define META_PAD 16 static void nglmi_inquire(sc_p sc, int full) { struct mbuf *m; char *cptr, *start; int error; meta_p meta = NULL; if (sc->lmi_channel == NULL) return; MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) { log(LOG_ERR, "nglmi: unable to start up LMI processing\n"); return; } m->m_pkthdr.rcvif = NULL; /* Allocate a meta struct (and leave some slop for options to be * added by other modules). */ /* MALLOC(meta, meta_p, sizeof( struct ng_meta) + META_PAD, * M_NETGRAPH, M_NOWAIT); */ MALLOC(meta, meta_p, sizeof(*meta) + META_PAD, M_NETGRAPH, M_NOWAIT); if (meta != NULL) { /* if it failed, well, it was optional anyhow */ meta->used_len = (u_short) sizeof(struct ng_meta); meta->allocated_len = (u_short) sizeof(struct ng_meta) + META_PAD; meta->flags = 0; meta->priority = NG_LMI_LMI_PRIORITY; meta->discardability = -1; } m->m_data += 4; /* leave some room for a header */ cptr = start = mtod(m, char *); /* add in the header for an LMI inquiry. */ *cptr++ = 0x03; /* UI frame */ if (GROUP4(sc)) *cptr++ = 0x09; /* proto discriminator */ else *cptr++ = 0x08; /* proto discriminator */ *cptr++ = 0x00; /* call reference */ *cptr++ = 0x75; /* inquiry */ /* If we are Annex-D, there is this extra thing.. */ if (ANNEXD(sc)) *cptr++ = 0x95; /* ??? */ /* Add a request type */ if (ANNEXA(sc)) *cptr++ = 0x51; /* report type */ else *cptr++ = 0x01; /* report type */ *cptr++ = 0x01; /* size = 1 */ if (full) *cptr++ = 0x00; /* full */ else *cptr++ = 0x01; /* partial */ /* Add a link verification IE */ if (ANNEXA(sc)) *cptr++ = 0x53; /* verification IE */ else *cptr++ = 0x03; /* verification IE */ *cptr++ = 0x02; /* 2 extra bytes */ *cptr++ = sc->local_seq; *cptr++ = sc->remote_seq; sc->seq_retries++; /* Send it */ m->m_len = m->m_pkthdr.len = cptr - start; NG_SEND_DATA(error, sc->lmi_channel, m, meta); /* If we've been sending requests for long enough, and there has * been no response, then mark as DOWN, any DLCIs that are UP. */ if (sc->seq_retries == LMI_PATIENCE) { int count; for (count = 0; count < MAXDLCI; count++) if (sc->dlci_state[count] == DLCI_UP) sc->dlci_state[count] = DLCI_DOWN; } } /* * State machine for LMI auto-detect. The transitions are ordered * to try the more likely possibilities first. */ static void ngauto_state_machine(sc_p sc) { if ((sc->poll_count <= 0) || (sc->poll_count > LMIPOLLSIZE)) { /* time to change states in the auto probe machine */ /* capture wild values of poll_count while we are at it */ sc->poll_count = LMIPOLLSIZE; sc->poll_state++; } switch (sc->poll_state) { case 7: log(LOG_WARNING, "nglmi: no response from exchange\n"); default: /* capture bad states */ sc->poll_state = 1; case 1: sc->lmi_channel = sc->lmi_channel0; SETLMITYPE(sc, SCF_ANNEX_D); break; case 2: sc->lmi_channel = sc->lmi_channel1023; SETLMITYPE(sc, SCF_ANNEX_D); break; case 3: sc->lmi_channel = sc->lmi_channel0; SETLMITYPE(sc, SCF_ANNEX_A); break; case 4: sc->lmi_channel = sc->lmi_channel1023; SETLMITYPE(sc, SCF_GROUP4); break; case 5: sc->lmi_channel = sc->lmi_channel1023; SETLMITYPE(sc, SCF_ANNEX_A); break; case 6: sc->lmi_channel = sc->lmi_channel0; SETLMITYPE(sc, SCF_GROUP4); break; } /* send an inquirey encoded appropriatly */ nglmi_inquire(sc, 0); sc->poll_count--; } /* * Receive a netgraph control message. */ static int nglmi_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp, hook_p lasthook) { int error = 0; sc_p sc = node->private; switch (msg->header.typecookie) { case NGM_GENERIC_COOKIE: switch (msg->header.cmd) { case NGM_TEXT_STATUS: { char *arg; int pos, count; NG_MKRESPONSE(*resp, msg, NG_TEXTRESPONSE, M_NOWAIT); if (*resp == NULL) { error = ENOMEM; break; } arg = (*resp)->data; pos = sprintf(arg, "protocol %s ", sc->protoname); if (sc->flags & SCF_FIXED) pos += sprintf(arg + pos, "fixed\n"); else if (sc->flags & SCF_AUTO) pos += sprintf(arg + pos, "auto-detecting\n"); else pos += sprintf(arg + pos, "auto on dlci %d\n", (sc->lmi_channel == sc->lmi_channel0) ? 0 : 1023); pos += sprintf(arg + pos, "keepalive period: %d seconds\n", sc->liv_rate); pos += sprintf(arg + pos, "unacknowledged keepalives: %ld\n", sc->seq_retries); for (count = 0; ((count <= MAXDLCI) && (pos < (NG_TEXTRESPONSE - 20))); count++) { if (sc->dlci_state[count]) { pos += sprintf(arg + pos, "dlci %d %s\n", count, (sc->dlci_state[count] == DLCI_UP) ? "up" : "down"); } } (*resp)->header.arglen = pos + 1; break; } default: error = EINVAL; break; } break; case NGM_LMI_COOKIE: switch (msg->header.cmd) { case NGM_LMI_GET_STATUS: { struct nglmistat *stat; int k; NG_MKRESPONSE(*resp, msg, sizeof(*stat), M_NOWAIT); if (!*resp) { error = ENOMEM; break; } stat = (struct nglmistat *) (*resp)->data; strncpy(stat->proto, sc->protoname, sizeof(stat->proto) - 1); strncpy(stat->hook, sc->protoname, sizeof(stat->hook) - 1); stat->autod = !!(sc->flags & SCF_AUTO); stat->fixed = !!(sc->flags & SCF_FIXED); for (k = 0; k <= MAXDLCI; k++) { switch (sc->dlci_state[k]) { case DLCI_UP: stat->up[k / 8] |= (1 << (k % 8)); /* fall through */ case DLCI_DOWN: stat->seen[k / 8] |= (1 << (k % 8)); break; } } break; } default: error = EINVAL; break; } break; default: error = EINVAL; break; } FREE(msg, M_NETGRAPH); return (error); } #define STEPBY(stepsize) \ do { \ packetlen -= (stepsize); \ data += (stepsize); \ } while (0) /* * receive data, and use it to update our status. * Anything coming in on the debug port is discarded. */ static int nglmi_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, struct mbuf **ret_m, meta_p *ret_meta) { sc_p sc = hook->node->private; u_char *data; unsigned short dlci; u_short packetlen; int resptype_seen = 0; int seq_seen = 0; if (hook->private == NULL) { goto drop; } packetlen = m->m_hdr.mh_len; /* XXX what if it's more than 1 mbuf? */ if ((packetlen > MHLEN) && !(m->m_flags & M_EXT)) { log(LOG_WARNING, "nglmi: packetlen (%d) too big\n", packetlen); goto drop; } if (m->m_len < packetlen && (m = m_pullup(m, packetlen)) == NULL) { log(LOG_WARNING, "nglmi: m_pullup failed for %d bytes\n", packetlen); NG_FREE_META(meta); return (0); } if (nglmi_checkdata(hook, m, meta) == 0) return (0); /* pass the first 4 bytes (already checked in the nglmi_checkdata()) */ data = mtod(m, u_char *); STEPBY(4); /* Now check if there is a 'locking shift'. This is only seen in * Annex D frames. don't bother checking, we already did that. Don't * increment immediatly as it might not be there. */ if (ANNEXD(sc)) STEPBY(1); /* If we get this far we should consider that it is a legitimate * frame and we know what it is. */ if (sc->flags & SCF_AUTO) { /* note the hook that this valid channel came from and drop * out of auto probe mode. */ if (ANNEXA(sc)) sc->protoname = NAME_ANNEXA; else if (ANNEXD(sc)) sc->protoname = NAME_ANNEXD; else if (GROUP4(sc)) sc->protoname = NAME_GROUP4; else { log(LOG_ERR, "nglmi: No known type\n"); goto drop; } sc->lmi_channel = hook; sc->flags &= ~SCF_AUTO; log(LOG_INFO, "nglmi: auto-detected %s LMI on DLCI %d\n", sc->protoname, hook == sc->lmi_channel0 ? 0 : 1023); } /* While there is more data in the status packet, keep processing * status items. First make sure there is enough data for the * segment descriptor's length field. */ while (packetlen >= 2) { u_int segtype = data[0]; u_int segsize = data[1]; /* Now that we know how long it claims to be, make sure * there is enough data for the next seg. */ if (packetlen < segsize + 2) break; switch (segtype) { case 0x01: case 0x51: if (resptype_seen) { log(LOG_WARNING, "nglmi: dup MSGTYPE\n"); goto nextIE; } resptype_seen++; /* The remote end tells us what kind of response * this is. Only expect a type 0 or 1. if we are a * full status, invalidate a few DLCIs just to see * that they are still ok. */ if (segsize != 1) goto nextIE; switch (data[2]) { case 1: /* partial status, do no extra processing */ break; case 0: { int count = 0; int idx = sc->invalidx; for (count = 0; count < 10; count++) { if (idx > MAXDLCI) idx = 0; if (sc->dlci_state[idx] == DLCI_UP) sc->dlci_state[idx] = DLCI_DOWN; idx++; } sc->invalidx = idx; /* we got and we wanted one. relax * now.. but don't reset to 0 if it * was unrequested. */ if (sc->livs > sc->liv_per_full) sc->livs = 0; break; } } break; case 0x03: case 0x53: /* The remote tells us what it thinks the sequence * numbers are. If it's not size 2, it must be a * duplicate to have gotten this far, skip it. */ if (seq_seen != 0) /* already seen seq numbers */ goto nextIE; if (segsize != 2) goto nextIE; sc->remote_seq = data[2]; if (sc->local_seq == data[3]) { sc->local_seq++; sc->seq_retries = 0; /* Note that all 3 Frame protocols seem to * not like 0 as a sequence number. */ if (sc->local_seq == 0) sc->local_seq = 1; } break; case 0x07: case 0x57: /* The remote tells us about a DLCI that it knows * about. There may be many of these in a single * status response */ switch (segsize) { case 6:/* only on 'group of 4' */ dlci = ((u_short) data[2] & 0xff) << 8; dlci |= (data[3] & 0xff); if ((dlci < 1024) && (dlci > 0)) { /* XXX */ } break; case 3: dlci = ((u_short) data[2] & 0x3f) << 4; dlci |= ((data[3] & 0x78) >> 3); if ((dlci < 1024) && (dlci > 0)) { /* set up the bottom half of the * support for that dlci if it's not * already been done */ /* store this information somewhere */ } break; default: goto nextIE; } if (sc->dlci_state[dlci] != DLCI_UP) { /* bring new DLCI to life */ /* may do more here some day */ if (sc->dlci_state[dlci] != DLCI_DOWN) log(LOG_INFO, "nglmi: DLCI %d became active\n", dlci); sc->dlci_state[dlci] = DLCI_UP; } break; } nextIE: STEPBY(segsize + 2); } NG_FREE_DATA(m, meta); return (0); drop: NG_FREE_DATA(m, meta); return (EINVAL); } /* * Check that a packet is entirely kosha. * return 1 of ok, and 0 if not. * All data is discarded if a 0 is returned. */ static int nglmi_checkdata(hook_p hook, struct mbuf *m, meta_p meta) { sc_p sc = hook->node->private; u_char *data; u_short packetlen; unsigned short dlci; u_char type; u_char nextbyte; int seq_seen = 0; int resptype_seen = 0; /* 0 , 1 (partial) or 2 (full) */ int highest_dlci = 0; packetlen = m->m_hdr.mh_len; data = mtod(m, u_char *); if (*data != 0x03) { log(LOG_WARNING, "nglmi: unexpected value in LMI(%d)\n", 1); goto reject; } STEPBY(1); /* look at the protocol ID */ nextbyte = *data; if (sc->flags & SCF_AUTO) { SETLMITYPE(sc, SCF_NOLMI); /* start with a clean slate */ switch (nextbyte) { case 0x8: sc->protoID = 8; break; case 0x9: SETLMITYPE(sc, SCF_GROUP4); sc->protoID = 9; break; default: log(LOG_WARNING, "nglmi: bad Protocol ID(%d)\n", (int) nextbyte); goto reject; } } else { if (nextbyte != sc->protoID) { log(LOG_WARNING, "nglmi: unexpected Protocol ID(%d)\n", (int) nextbyte); goto reject; } } STEPBY(1); /* check call reference (always null in non ISDN frame relay) */ if (*data != 0x00) { log(LOG_WARNING, "nglmi: unexpected Call Reference (0x%x)\n", data[-1]); goto reject; } STEPBY(1); /* check message type */ switch ((type = *data)) { case 0x75: /* Status enquiry */ log(LOG_WARNING, "nglmi: unexpected message type(0x%x)\n", data[-1]); goto reject; case 0x7D: /* Status message */ break; default: log(LOG_WARNING, "nglmi: unexpected msg type(0x%x) \n", (int) type); goto reject; } STEPBY(1); /* Now check if there is a 'locking shift'. This is only seen in * Annex D frames. Don't increment immediately as it might not be * there. */ nextbyte = *data; if (sc->flags & SCF_AUTO) { if (!(GROUP4(sc))) { if (nextbyte == 0x95) { SETLMITYPE(sc, SCF_ANNEX_D); STEPBY(1); } else SETLMITYPE(sc, SCF_ANNEX_A); } else if (nextbyte == 0x95) { log(LOG_WARNING, "nglmi: locking shift seen in G4\n"); goto reject; } } else { if (ANNEXD(sc)) { if (*data == 0x95) STEPBY(1); else { log(LOG_WARNING, "nglmi: locking shift missing\n"); goto reject; } } else if (*data == 0x95) { log(LOG_WARNING, "nglmi: locking shift seen\n"); goto reject; } } /* While there is more data in the status packet, keep processing * status items. First make sure there is enough data for the * segment descriptor's length field. */ while (packetlen >= 2) { u_int segtype = data[0]; u_int segsize = data[1]; /* Now that we know how long it claims to be, make sure * there is enough data for the next seg. */ if (packetlen < (segsize + 2)) { log(LOG_WARNING, "nglmi: IE longer than packet\n"); break; } switch (segtype) { case 0x01: case 0x51: /* According to MCI's HP analyser, we should just * ignore if there is mor ethan one of these (?). */ if (resptype_seen) { log(LOG_WARNING, "nglmi: dup MSGTYPE\n"); goto nextIE; } if (segsize != 1) { log(LOG_WARNING, "nglmi: MSGTYPE wrong size\n"); goto reject; } /* The remote end tells us what kind of response * this is. Only expect a type 0 or 1. if it was a * full (type 0) check we just asked for a type * full. */ switch (data[2]) { case 1:/* partial */ if (sc->livs > sc->liv_per_full) { log(LOG_WARNING, "nglmi: LIV when FULL expected\n"); goto reject; /* need full */ } resptype_seen = 1; break; case 0:/* full */ /* Full response is always acceptable */ resptype_seen = 2; break; default: log(LOG_WARNING, "nglmi: Unknown report type %d\n", data[2]); goto reject; } break; case 0x03: case 0x53: /* The remote tells us what it thinks the sequence * numbers are. I would have thought that there * needs to be one and only one of these, but MCI * want us to just ignore extras. (?) */ if (resptype_seen == 0) { log(LOG_WARNING, "nglmi: no TYPE before SEQ\n"); goto reject; } if (seq_seen != 0) /* already seen seq numbers */ goto nextIE; if (segsize != 2) { log(LOG_WARNING, "nglmi: bad SEQ sts size\n"); goto reject; } if (sc->local_seq != data[3]) { log(LOG_WARNING, "nglmi: unexpected SEQ\n"); goto reject; } seq_seen = 1; break; case 0x07: case 0x57: /* The remote tells us about a DLCI that it knows * about. There may be many of these in a single * status response */ if (seq_seen != 1) { /* already seen seq numbers? */ log(LOG_WARNING, "nglmi: No sequence before DLCI\n"); goto reject; } if (resptype_seen != 2) { /* must be full */ log(LOG_WARNING, "nglmi: No resp type before DLCI\n"); goto reject; } if (GROUP4(sc)) { if (segsize != 6) { log(LOG_WARNING, "nglmi: wrong IE segsize\n"); goto reject; } dlci = ((u_short) data[2] & 0xff) << 8; dlci |= (data[3] & 0xff); } else { if (segsize != 3) { log(LOG_WARNING, "nglmi: DLCI headersize of %d" " not supported\n", segsize - 1); goto reject; } dlci = ((u_short) data[2] & 0x3f) << 4; dlci |= ((data[3] & 0x78) >> 3); } /* async can only have one of these */ #if 0 /* async not yet accepted */ if (async && highest_dlci) { log(LOG_WARNING, "nglmi: Async with > 1 DLCI\n"); goto reject; } #endif /* Annex D says these will always be Ascending, but * the HP test for G4 says we should accept * duplicates, so for now allow that. ( <= vs. < ) */ #if 0 /* MCI tests want us to accept out of order for AnxD */ if ((!GROUP4(sc)) && (dlci < highest_dlci)) { /* duplicate or mis-ordered dlci */ /* (spec says they will increase in number) */ log(LOG_WARNING, "nglmi: DLCI out of order\n"); goto reject; } #endif if (dlci > 1023) { log(LOG_WARNING, "nglmi: DLCI out of range\n"); goto reject; } highest_dlci = dlci; break; default: log(LOG_WARNING, "nglmi: unknown LMI segment type %d\n", segtype); } nextIE: STEPBY(segsize + 2); } if (packetlen != 0) { /* partial junk at end? */ log(LOG_WARNING, "nglmi: %d bytes extra at end of packet\n", packetlen); goto print; } if (resptype_seen == 0) { log(LOG_WARNING, "nglmi: No response type seen\n"); goto reject; /* had no response type */ } if (seq_seen == 0) { log(LOG_WARNING, "nglmi: No sequence numbers seen\n"); goto reject; /* had no sequence numbers */ } return (1); print: { int i, j, k, pos; char buf[100]; int loc; u_char *bp = mtod(m, u_char *); k = i = 0; loc = (m->m_hdr.mh_len - packetlen); log(LOG_WARNING, "nglmi: error at location %d\n", loc); while (k < m->m_hdr.mh_len) { pos = 0; j = 0; while ((j++ < 16) && k < m->m_hdr.mh_len) { pos += sprintf(buf + pos, "%c%02x", ((loc == k) ? '>' : ' '), bp[k]); k++; } if (i == 0) log(LOG_WARNING, "nglmi: packet data:%s\n", buf); else log(LOG_WARNING, "%04d :%s\n", k, buf); i++; } } return (1); reject: { int i, j, k, pos; char buf[100]; int loc; u_char *bp = mtod(m, u_char *); k = i = 0; loc = (m->m_hdr.mh_len - packetlen); log(LOG_WARNING, "nglmi: error at location %d\n", loc); while (k < m->m_hdr.mh_len) { pos = 0; j = 0; while ((j++ < 16) && k < m->m_hdr.mh_len) { pos += sprintf(buf + pos, "%c%02x", ((loc == k) ? '>' : ' '), bp[k]); k++; } if (i == 0) log(LOG_WARNING, "nglmi: packet data:%s\n", buf); else log(LOG_WARNING, "%04d :%s\n", k, buf); i++; } } NG_FREE_DATA(m, meta); return (0); } /* * Do local shutdown processing.. * Cut any remaining links and free our local resources. */ static int nglmi_rmnode(node_p node) { const sc_p sc = node->private; node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); node->private = NULL; ng_unref(sc->node); FREE(sc, M_NETGRAPH); return (0); } /* * Hook disconnection * For this type, removal of any link except "debug" destroys the node. */ static int nglmi_disconnect(hook_p hook) { const sc_p sc = hook->node->private; /* OK to remove debug hook(s) */ if (hook->private == NULL) return (0); /* Stop timer if it's currently active */ if (sc->flags & SCF_CONNECTED) untimeout(LMI_ticker, sc, sc->handle); /* Self-destruct */ ng_rmnode(hook->node); return (0); }