diff --git a/sys/netgraph/ng_UI.c b/sys/netgraph/ng_UI.c index 7295b4ae73bb..48078d93e350 100644 --- a/sys/netgraph/ng_UI.c +++ b/sys/netgraph/ng_UI.c @@ -1,242 +1,243 @@ /* * ng_UI.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_UI.c,v 1.11 1999/01/28 23:54:52 julian Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * DEFINITIONS */ /* Everything, starting with sdlc on has defined UI as 0x03 */ #define HDLC_UI 0x03 /* Node private data */ struct private { hook_p downlink; hook_p uplink; }; typedef struct private *priv_p; /* Netgraph node methods */ static int ng_UI_constructor(node_p *nodep); static int ng_UI_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp); static int ng_UI_rmnode(node_p node); static int ng_UI_newhook(node_p node, hook_p hook, const char *name); static int ng_UI_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); static int ng_UI_disconnect(hook_p hook); /* Node type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_UI_NODE_TYPE, NULL, ng_UI_constructor, ng_UI_rcvmsg, ng_UI_rmnode, ng_UI_newhook, NULL, NULL, ng_UI_rcvdata, ng_UI_rcvdata, ng_UI_disconnect }; NETGRAPH_INIT(UI, &typestruct); /************************************************************************ NETGRAPH NODE STUFF ************************************************************************/ /* * Create a newborn node. We start with an implicit reference. */ static int ng_UI_constructor(node_p *nodep) { priv_p priv; int error; /* Allocate private structure */ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK); if (priv == NULL) return (ENOMEM); bzero(priv, sizeof(*priv)); /* Call generic node constructor */ if ((error = ng_make_node_common(&typestruct, nodep))) { FREE(priv, M_NETGRAPH); return (error); } (*nodep)->private = priv; /* Done */ return (0); } /* * Give our ok for a hook to be added */ static int ng_UI_newhook(node_p node, hook_p hook, const char *name) { const priv_p priv = node->private; if (!strcmp(name, NG_UI_HOOK_DOWNSTREAM)) { if (priv->downlink) return (EISCONN); priv->downlink = hook; } else if (!strcmp(name, NG_UI_HOOK_UPSTREAM)) { if (priv->uplink) return (EISCONN); priv->uplink = hook; } else return (EINVAL); return (0); } /* * Receive a control message */ static int ng_UI_rcvmsg(node_p node, struct ng_mesg *msg, const char *raddr, struct ng_mesg **rp) { FREE(msg, M_NETGRAPH); return (EINVAL); } #define MAX_ENCAPS_HDR 1 #define ERROUT(x) do { error = (x); goto done; } while (0) /* * Receive a data frame */ static int ng_UI_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { const node_p node = hook->node; const priv_p priv = node->private; int error = 0; if (hook == priv->downlink) { u_char *start, *ptr; - if (!m || !(m = m_pullup(m, MAX_ENCAPS_HDR))) + if (!m || (m->m_len < MAX_ENCAPS_HDR + && !(m = m_pullup(m, MAX_ENCAPS_HDR)))) ERROUT(ENOBUFS); ptr = start = mtod(m, u_char *); /* Must be UI frame */ if (*ptr++ != HDLC_UI) ERROUT(0); m_adj(m, ptr - start); NG_SEND_DATA(error, priv->uplink, m, meta); /* m -> NULL */ } else if (hook == priv->uplink) { M_PREPEND(m, 1, M_DONTWAIT); /* Prepend IP NLPID */ if (!m) ERROUT(ENOBUFS); mtod(m, u_char *)[0] = HDLC_UI; NG_SEND_DATA(error, priv->downlink, m, meta); /* m -> NULL */ } else panic(__FUNCTION__); done: NG_FREE_DATA(m, meta); /* does nothing if m == NULL */ return (error); } /* * Shutdown node */ static int ng_UI_rmnode(node_p node) { const priv_p priv = node->private; /* Take down netgraph node */ node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); bzero(priv, sizeof(*priv)); FREE(priv, M_NETGRAPH); node->private = NULL; ng_unref(node); return (0); } /* * Hook disconnection */ static int ng_UI_disconnect(hook_p hook) { const priv_p priv = hook->node->private; if (hook->node->numhooks == 0) ng_rmnode(hook->node); else if (hook == priv->downlink) priv->downlink = NULL; else if (hook == priv->uplink) priv->uplink = NULL; else panic(__FUNCTION__); return (0); } diff --git a/sys/netgraph/ng_frame_relay.c b/sys/netgraph/ng_frame_relay.c index 995b769a9fed..97ba1a5b6162 100644 --- a/sys/netgraph/ng_frame_relay.c +++ b/sys/netgraph/ng_frame_relay.c @@ -1,526 +1,526 @@ /* * ng_frame_relay.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 Elisher * * $FreeBSD$ * $Whistle: ng_frame_relay.c,v 1.16 1999/01/28 23:54:53 julian Exp $ */ /* * This node implements the frame relay protocol, not including * the LMI line management. This means basically keeping track * of which DLCI's are active, doing frame (de)multiplexing, etc. * * It has a 'downstream' hook that goes to the line, and a * hook for each DLCI (eg, 'dlci16'). */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Line info, and status per channel. */ struct ctxinfo { /* one per active hook */ u_int flags; #define CHAN_VALID 0x01 /* assigned to a channel */ #define CHAN_ACTIVE 0x02 /* bottom level active */ int dlci; /* the dlci assigned to this context */ hook_p hook; /* if there's a hook assigned.. */ }; #define MAX_CT 16 /* # of dlci's active at a time (POWER OF 2!) */ struct frmrel_softc { int unit; /* which card are we? */ char nodename[NG_NODELEN + 1]; /* store our node name */ int datahooks; /* number of data hooks attached */ node_p node; /* netgraph node */ int addrlen; /* address header length */ int flags; /* state */ int mtu; /* guess */ u_char remote_seq; /* sequence number the remote sent */ u_char local_seq; /* sequence number the remote rcvd */ u_short ALT[1024]; /* map DLCIs to CTX */ #define CTX_VALID 0x8000 /* this bit means it's a valid CTX */ #define CTX_VALUE (MAX_CT - 1) /* mask for context part */ struct ctxinfo channel[MAX_CT]; struct ctxinfo downstream; }; typedef struct frmrel_softc *sc_p; #define BYTEX_EA 0x01 /* End Address. Always 0 on byte1 */ #define BYTE1_C_R 0x02 #define BYTE2_FECN 0x08 /* forwards congestion notification */ #define BYTE2_BECN 0x04 /* Backward congestion notification */ #define BYTE2_DE 0x02 /* Discard elligability */ #define LASTBYTE_D_C 0x02 /* last byte is dl_core or dlci info */ /* Used to do headers */ static struct segment { u_char mask; u_char shift; u_char width; } makeup[] = { { 0xfc, 2, 6 }, { 0xf0, 4, 4 }, { 0xfe, 1, 7 }, { 0xfc, 2, 6 } }; #define SHIFTIN(segment, byte, dlci) \ { \ (dlci) <<= (segment)->width; \ (dlci) |= \ (((byte) & (segment)->mask) >> (segment)->shift); \ } #define SHIFTOUT(segment, byte, dlci) \ { \ (byte) |= (((dlci) << (segment)->shift) & (segment)->mask); \ (dlci) >>= (segment)->width; \ } /* Netgraph methods */ static int ngfrm_constructor(node_p *nodep); static int ngfrm_rmnode(node_p node); static int ngfrm_newhook(node_p node, hook_p hook, const char *name); static int ngfrm_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); static int ngfrm_disconnect(hook_p hook); /* Other internal functions */ static int ngfrm_decode(node_p node, struct mbuf * m, meta_p meta); static int ngfrm_addrlen(char *hdr); static int ngfrm_allocate_CTX(sc_p sc, int dlci); /* Netgraph type */ static struct ng_type typestruct = { NG_VERSION, NG_FRAMERELAY_NODE_TYPE, NULL, ngfrm_constructor, NULL, ngfrm_rmnode, ngfrm_newhook, NULL, NULL, ngfrm_rcvdata, ngfrm_rcvdata, ngfrm_disconnect }; NETGRAPH_INIT(framerelay, &typestruct); /* * Given a DLCI, return the index of the context table entry for it, * Allocating a new one if needs be, or -1 if none available. */ static int ngfrm_allocate_CTX(sc_p sc, int dlci) { u_int ctxnum = -1; /* what ctx number we are using */ volatile struct ctxinfo *CTXp = NULL; /* Sanity check the dlci value */ if (dlci > 1023) return (-1); /* Check to see if we already have an entry for this DLCI */ if (sc->ALT[dlci]) { if ((ctxnum = sc->ALT[dlci] & CTX_VALUE) < MAX_CT) { CTXp = sc->channel + ctxnum; } else { ctxnum = -1; sc->ALT[dlci] = 0; /* paranoid but... */ } } /* * If the index has no valid entry yet, then we need to allocate a * CTX number to it */ if (CTXp == NULL) { for (ctxnum = 0; ctxnum < MAX_CT; ctxnum++) { /* * If the VALID flag is empty it is unused */ if ((sc->channel[ctxnum].flags & CHAN_VALID) == 0) { bzero(sc->channel + ctxnum, sizeof(struct ctxinfo)); CTXp = sc->channel + ctxnum; sc->ALT[dlci] = ctxnum | CTX_VALID; sc->channel[ctxnum].dlci = dlci; sc->channel[ctxnum].flags = CHAN_VALID; break; } } } /* * If we still don't have a CTX pointer, then we never found a free * spot so give up now.. */ if (!CTXp) { log(LOG_ERR, "No CTX available for dlci %d\n", dlci); return (-1); } return (ctxnum); } /* * Node constructor */ static int ngfrm_constructor(node_p *nodep) { sc_p sc; int error = 0; MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT); if (!sc) return (ENOMEM); bzero(sc, sizeof(*sc)); if ((error = ng_make_node_common(&typestruct, nodep))) { FREE(sc, M_NETGRAPH); return (error); } sc->addrlen = 2; /* default */ /* Link the node and our private info */ (*nodep)->private = sc; sc->node = *nodep; return (0); } /* * Add a new hook * * We allow hooks called "debug", "downstream" and dlci[0-1023] * The hook's private info points to our stash of info about that * channel. A NULL pointer is debug and a DLCI of -1 means downstream. */ static int ngfrm_newhook(node_p node, hook_p hook, const char *name) { const sc_p sc = node->private; const char *cp; char c = '\0'; int digits = 0; int dlci = 0; int ctxnum; /* Check if it's our friend the control hook */ if (strcmp(name, NG_FRAMERELAY_HOOK_DEBUG) == 0) { hook->private = NULL; /* paranoid */ return (0); } /* * All other hooks either start with 'dlci' and have a decimal * trailing channel number up to 4 digits, or are the downstream * hook. */ if (strncmp(name, NG_FRAMERELAY_HOOK_DLCI, strlen(NG_FRAMERELAY_HOOK_DLCI)) != 0) { /* It must be the downstream connection */ if (strcmp(name, NG_FRAMERELAY_HOOK_DOWNSTREAM) != 0) return EINVAL; /* Make sure we haven't already got one (paranoid) */ if (sc->downstream.hook) return (EADDRINUSE); /* OK add it */ hook->private = &sc->downstream; sc->downstream.hook = hook; sc->downstream.dlci = -1; sc->downstream.flags |= CHAN_ACTIVE; sc->datahooks++; return (0); } /* Must be a dlci hook at this point */ cp = name + strlen(NG_FRAMERELAY_HOOK_DLCI); while ((digits < 5) && ((c = *cp++) >= '0') && (c <= '9')) { dlci *= 10; dlci += c - '0'; digits++; } if ((c != 0) || (digits == 5) || (dlci < 0) || (dlci > 1023)) return (EINVAL); /* * We have a dlci, now either find it, or allocate it. It's possible * that we might have seen packets for it already and made an entry * for it. */ ctxnum = ngfrm_allocate_CTX(sc, dlci); if (ctxnum == -1) return (ENOBUFS); /* * Be paranoid: if it's got a hook already, that dlci is in use . * Generic code can not catch all the synonyms (e.g. dlci016 vs * dlci16) */ if (sc->channel[ctxnum].hook != NULL) return (EADDRINUSE); /* * Put our hooks into it (pun not intended) */ sc->channel[ctxnum].flags |= CHAN_ACTIVE; hook->private = sc->channel + ctxnum; sc->channel[ctxnum].hook = hook; sc->datahooks++; return (0); } /* * Count up the size of the address header if we don't already know */ int ngfrm_addrlen(char *hdr) { if (hdr[0] & BYTEX_EA) return 0; if (hdr[1] & BYTEX_EA) return 2; if (hdr[2] & BYTEX_EA) return 3; if (hdr[3] & BYTEX_EA) return 4; return 0; } /* * Receive data packet */ static int ngfrm_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { struct ctxinfo *const ctxp = hook->private; int error = 0; int dlci; sc_p sc; int alen; char *data; /* Data doesn't come in from just anywhere (e.g debug hook) */ if (ctxp == NULL) { error = ENETDOWN; goto bad; } /* If coming from downstream, decode it to a channel */ dlci = ctxp->dlci; if (dlci == -1) return (ngfrm_decode(hook->node, m, meta)); /* Derive the softc we will need */ sc = hook->node->private; /* If there is no live channel, throw it away */ if ((sc->downstream.hook == NULL) || ((ctxp->flags & CHAN_ACTIVE) == 0)) { error = ENETDOWN; goto bad; } /* Store the DLCI on the front of the packet */ alen = sc->addrlen; if (alen == 0) alen = 2; /* default value for transmit */ M_PREPEND(m, alen, M_DONTWAIT); if (m == NULL) { error = ENOBUFS; goto bad; } data = mtod(m, char *); /* * Shift the lowest bits into the address field untill we are done. * First byte is MSBits of addr so work backwards. */ switch (alen) { case 2: data[0] = data[1] = '\0'; SHIFTOUT(makeup + 1, data[1], dlci); SHIFTOUT(makeup + 0, data[0], dlci); data[1] |= BYTEX_EA; break; case 3: data[0] = data[1] = data[2] = '\0'; SHIFTOUT(makeup + 3, data[2], dlci); /* 3 and 2 is correct */ SHIFTOUT(makeup + 1, data[1], dlci); SHIFTOUT(makeup + 0, data[0], dlci); data[2] |= BYTEX_EA; break; case 4: data[0] = data[1] = data[2] = data[3] = '\0'; SHIFTOUT(makeup + 3, data[3], dlci); SHIFTOUT(makeup + 2, data[2], dlci); SHIFTOUT(makeup + 1, data[1], dlci); SHIFTOUT(makeup + 0, data[0], dlci); data[3] |= BYTEX_EA; break; default: panic(__FUNCTION__); } /* Send it */ NG_SEND_DATA(error, sc->downstream.hook, m, meta); return (error); bad: NG_FREE_DATA(m, meta); return (error); } /* * Decode an incoming frame coming from the switch */ static int ngfrm_decode(node_p node, struct mbuf *m, meta_p meta) { const sc_p sc = node->private; char *data; int alen; u_int dlci = 0; int error = 0; int ctxnum; - if ((m = m_pullup(m, 4)) == NULL) { + if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) { error = ENOBUFS; goto out; } data = mtod(m, char *); if ((alen = sc->addrlen) == 0) { sc->addrlen = alen = ngfrm_addrlen(data); } switch (alen) { case 2: SHIFTIN(makeup + 0, data[0], dlci); SHIFTIN(makeup + 1, data[1], dlci); break; case 3: SHIFTIN(makeup + 0, data[0], dlci); SHIFTIN(makeup + 1, data[1], dlci); SHIFTIN(makeup + 3, data[2], dlci); /* 3 and 2 is correct */ break; case 4: SHIFTIN(makeup + 0, data[0], dlci); SHIFTIN(makeup + 1, data[1], dlci); SHIFTIN(makeup + 2, data[2], dlci); SHIFTIN(makeup + 3, data[3], dlci); break; default: error = EINVAL; goto out; } if (dlci > 1023) { error = EINVAL; goto out; } ctxnum = sc->ALT[dlci]; if ((ctxnum & CTX_VALID) && sc->channel[ctxnum &= CTX_VALUE].hook) { /* Send it */ m_adj(m, alen); NG_SEND_DATA(error, sc->channel[ctxnum].hook, m, meta); return (error); } else { error = ENETDOWN; } out: NG_FREE_DATA(m, meta); return (error); } /* * Shutdown node */ static int ngfrm_rmnode(node_p node) { const sc_p sc = node->private; node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); node->private = NULL; FREE(sc, M_NETGRAPH); ng_unref(sc->node); return (0); } /* * Hook disconnection * * Invalidate the private data associated with this dlci. * For this type, removal of the last link resets tries to destroy the node. */ static int ngfrm_disconnect(hook_p hook) { const sc_p sc = hook->node->private; struct ctxinfo *const cp = hook->private; int dlci; /* If it's a regular dlci hook, then free resources etc.. */ if (cp != NULL) { cp->hook = NULL; dlci = cp->dlci; if (dlci != -1) sc->ALT[dlci] = 0; cp->flags = 0; sc->datahooks--; } if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } diff --git a/sys/netgraph/ng_lmi.c b/sys/netgraph/ng_lmi.c index a4049986aa81..239bc57843f8 100644 --- a/sys/netgraph/ng_lmi.c +++ b/sys/netgraph/ng_lmi.c @@ -1,1090 +1,1091 @@ /* * 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.35 1999/01/28 23:54:53 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 int nglmi_constructor(node_p *node); static int nglmi_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp); static int nglmi_rmnode(node_p node); static int nglmi_newhook(node_p node, hook_p hook, const char *name); static int nglmi_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); static int nglmi_disconnect(hook_p hook); /* notify on 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 }; 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_WAITOK); if (sc == NULL) return (ENOMEM); bzero(sc, sizeof(*sc)); 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; } /* 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) { 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) { 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_pullup(m, packetlen)) == NULL) { - log(LOG_WARNING, "nglmi: m_pullup failed for %d bytes\n", packetlen); + 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); } diff --git a/sys/netgraph/ng_pppoe.c b/sys/netgraph/ng_pppoe.c index 65115ef68c75..3f0ff6888a90 100644 --- a/sys/netgraph/ng_pppoe.c +++ b/sys/netgraph/ng_pppoe.c @@ -1,1471 +1,1472 @@ #define SIGNOFF "session closed" /* * ng_pppoe.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_pppoe.c,v 1.7 1999/10/16 10:16:43 julian Exp $ */ #if 0 #define AAA printf("pppoe: %s\n", __FUNCTION__ ); #define BBB printf("-%d-", __LINE__ ); #else #define AAA #define BBB #endif #include #include #include #include #include #include #include #include #include #include #include /* * This section contains the netgraph method declarations for the * sample node. These methods define the netgraph 'type'. */ static int ng_PPPoE_constructor(node_p *node); static int ng_PPPoE_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp); static int ng_PPPoE_rmnode(node_p node); static int ng_PPPoE_newhook(node_p node, hook_p hook, const char *name); static int ng_PPPoE_connect(hook_p hook); static int ng_PPPoE_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); static int ng_PPPoE_disconnect(hook_p hook); /* Netgraph node type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_PPPOE_NODE_TYPE, NULL, ng_PPPoE_constructor, ng_PPPoE_rcvmsg, ng_PPPoE_rmnode, ng_PPPoE_newhook, NULL, ng_PPPoE_connect, ng_PPPoE_rcvdata, ng_PPPoE_rcvdata, ng_PPPoE_disconnect }; NETGRAPH_INIT(PPPoE, &typestruct); /* * States for the session state machine. * These have no meaning if there is no hook attached yet. */ enum state { PPPOE_SNONE=0, /* [both] Initial state */ PPPOE_SINIT, /* [Client] Sent discovery initiation */ PPPOE_PRIMED, /* [Server] Sent offer message */ PPPOE_SOFFER, /* [Server] Sent offer message */ PPPOE_SREQ, /* [Client] Sent a Request */ PPPOE_LISTENING, /* [Server] Listening for discover initiation msg */ PPPOE_NEWCONNECTED, /* [Both] Connection established, No data received */ PPPOE_CONNECTED, /* [Both] Connection established, Data received */ PPPOE_DEAD /* [Both] */ }; #define NUMTAGS 20 /* number of tags we are set up to work with */ /* * Information we store for each hook on each node for negotiating the * session. The mbuf and cluster are freed once negotiation has completed. * The whole negotiation block is then discarded. */ struct sess_neg { struct mbuf *m; /* holds cluster with last sent packet */ union packet *pkt; /* points within the above cluster */ struct callout_handle timeout_handle; /* see timeout(9) */ u_int timeout; /* 0,1,2,4,8,16 etc. seconds */ u_int numtags; struct pppoe_tag *tags[NUMTAGS]; u_int service_len; u_int ac_name_len; struct datatag service; struct datatag ac_name; }; typedef struct sess_neg *negp; /* * Session information that is needed after connection. */ struct session { hook_p hook; u_int16_t Session_ID; struct session *hash_next; /* not yet uesed */ enum state state; char creator[NG_NODELEN + 1]; /* who to notify */ struct pppoe_full_hdr pkt_hdr; /* used when connected */ negp neg; /* used when negotiating */ }; typedef struct session *sessp; /* * Information we store for each node */ struct PPPOE { node_p node; /* back pointer to node */ hook_p ethernet_hook; hook_p debug_hook; u_int packets_in; /* packets in from ethernet */ u_int packets_out; /* packets out towards ethernet */ u_int32_t flags; /*struct session *buckets[HASH_SIZE];*/ /* not yet used */ }; typedef struct PPPOE *priv_p; const struct ether_header eh_prototype = {{0xff,0xff,0xff,0xff,0xff,0xff}, {0x00,0x00,0x00,0x00,0x00,0x00}, ETHERTYPE_PPPOE_DISC}; union uniq { char bytes[sizeof(void *)]; void * pointer; }; #define LEAVE(x) do { error = x; goto quit; } while(0) static void pppoe_start(sessp sp); static void sendpacket(sessp sp); static void pppoe_ticker(void *arg); static struct pppoe_tag* scan_tags(sessp sp, struct pppoe_hdr* ph); static int pppoe_send_event(sessp sp, enum cmd cmdid); /************************************************************************* * Some basic utilities from the Linux version with author's permission.* * Author: Michal Ostrowski * ************************************************************************/ /* * Generate a new session id * XXX find out the freeBSD locking scheme. */ static u_int16_t get_new_sid(node_p node) { static int pppoe_sid = 10; sessp sp; hook_p hook; u_int16_t val; priv_p privp = node->private; AAA restart: val = pppoe_sid++; /* * Spec says 0xFFFF is reserved. * Also don't use 0x0000 */ if (val == 0xffff) { pppoe_sid = 20; goto restart; } /* Check it isn't already in use */ LIST_FOREACH(hook, &node->hooks, hooks) { /* don't check special hooks */ if ((hook->private == &privp->debug_hook) || (hook->private == &privp->ethernet_hook)) continue; sp = hook->private; if (sp->Session_ID == val) goto restart; } return val; } /* * Return the location where the next tag can be put */ static __inline struct pppoe_tag* next_tag(struct pppoe_hdr* ph) { return (struct pppoe_tag*)(((char*)&ph->tag[0]) + ntohs(ph->length)); } /* * Look for a tag of a specific type * Don't trust any length the other end says. * but assume we already sanity checked ph->length. */ static struct pppoe_tag* get_tag(struct pppoe_hdr* ph, u_int16_t idx) { char *end = (char *)next_tag(ph); char *ptn; struct pppoe_tag *pt = &ph->tag[0]; /* * Keep processing tags while a tag header will still fit. */ AAA while((char*)(pt + 1) <= end) { /* * If the tag data would go past the end of the packet, abort. */ ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len)); if(ptn > end) return NULL; if(pt->tag_type == idx) return pt; pt = (struct pppoe_tag*)ptn; } return NULL; } /************************************************************************** * inlines to initialise or add tags to a session's tag list, **************************************************************************/ /* * Initialise the session's tag list */ static void init_tags(sessp sp) { AAA if(sp->neg == NULL) { printf("pppoe: asked to init NULL neg pointer\n"); return; } sp->neg->numtags = 0; } static void insert_tag(sessp sp, struct pppoe_tag *tp) { int i; negp neg; AAA if((neg = sp->neg) == NULL) { printf("pppoe: asked to use NULL neg pointer\n"); return; } if ((i = neg->numtags++) < NUMTAGS) { neg->tags[i] = tp; } else { printf("pppoe: asked to add too many tags to packet\n"); } } /* * Make up a packet, using the tags filled out for the session. * * Assume that the actual pppoe header and ethernet header * are filled out externally to this routine. * Also assume that neg->wh points to the correct * location at the front of the buffer space. */ static void make_packet(sessp sp) { struct pppoe_full_hdr *wh = &sp->neg->pkt->pkt_header; struct pppoe_tag **tag; char *dp; int count; int tlen; u_int16_t length = 0; AAA if ((sp->neg == NULL) || (sp->neg->m == NULL)) { printf("pppoe: make_packet called from wrong state\n"); } dp = (char *)wh->ph.tag; for (count = 0, tag = sp->neg->tags; ((count < sp->neg->numtags) && (count < NUMTAGS)); tag++, count++) { tlen = ntohs((*tag)->tag_len) + sizeof(**tag); if ((length + tlen) > (ETHER_MAX_LEN - 4 - sizeof(*wh))) { printf("pppoe: tags too long\n"); sp->neg->numtags = count; break; /* XXX chop off what's too long */ } bcopy((char *)*tag, (char *)dp, tlen); length += tlen; dp += tlen; } wh->ph.length = htons(length); sp->neg->m->m_len = length + sizeof(*wh); sp->neg->m->m_pkthdr.len = length + sizeof(*wh); } /************************************************************************** * Routine to match a service offered * **************************************************************************/ /* * Find a hook that has a service string that matches that * we are seeking. for now use a simple string. * In the future we may need something like regexp(). * for testing allow a null string to match 1st found and a null service * to match all requests. Also make '*' do the same. */ static hook_p pppoe_match_svc(node_p node, char *svc_name, int svc_len) { sessp sp = NULL; negp neg = NULL; priv_p privp = node->private; hook_p hook; AAA LIST_FOREACH(hook, &node->hooks, hooks) { /* skip any hook that is debug or ethernet */ if ((hook->private == &privp->debug_hook) || (hook->private == &privp->ethernet_hook)) continue; sp = hook->private; /* Skip any sessions which are not in LISTEN mode. */ if ( sp->state != PPPOE_LISTENING) continue; neg = sp->neg; /* XXX check validity of this */ /* special case, NULL request. match 1st found. */ if (svc_len == 0) break; /* XXX check validity of this */ /* Special case for a blank or "*" service name (wildcard) */ if ((neg->service_len == 0) || ((neg->service_len == 1) && (neg->service.data[0] == '*'))) { break; } /* If the lengths don't match, that aint it. */ if (neg->service_len != svc_len) continue; /* An exact match? */ if (strncmp(svc_name, neg->service.data, svc_len) == 0) break; } return (hook); } /************************************************************************** * Routine to find a particular session that matches an incoming packet * **************************************************************************/ static hook_p pppoe_findsession(node_p node, struct pppoe_full_hdr *wh) { sessp sp = NULL; hook_p hook = NULL; priv_p privp = node->private; u_int16_t session = ntohs(wh->ph.sid); /* * find matching peer/session combination. */ AAA LIST_FOREACH(hook, &node->hooks, hooks) { /* don't check special hooks */ if ((hook->private == &privp->debug_hook) || (hook->private == &privp->ethernet_hook)) { continue; } sp = hook->private; if ( ( (sp->state == PPPOE_CONNECTED) || (sp->state == PPPOE_NEWCONNECTED) ) && (sp->Session_ID == session) && (bcmp(sp->pkt_hdr.eh.ether_dhost, wh->eh.ether_shost, ETHER_ADDR_LEN)) == 0) { break; } } return (hook); } static hook_p pppoe_finduniq(node_p node, struct pppoe_tag *tag) { hook_p hook = NULL; priv_p privp = node->private; union uniq uniq; AAA bcopy(tag->tag_data, uniq.bytes, sizeof(void *)); /* cycle through all known hooks */ LIST_FOREACH(hook, &node->hooks, hooks) { /* don't check special hooks */ if ((hook->private == &privp->debug_hook) || (hook->private == &privp->ethernet_hook)) continue; if (uniq.pointer == hook->private) break; } return (hook); } /************************************************************************** * start of Netgraph entrypoints * **************************************************************************/ /* * Allocate the private data structure and the generic node * and link them together. * * ng_make_node_common() returns with a generic node struct * with a single reference for us.. we transfer it to the * private structure.. when we free the private struct we must * unref the node so it gets freed too. * * If this were a device node than this work would be done in the attach() * routine and the constructor would return EINVAL as you should not be able * to creatednodes that depend on hardware (unless you can add the hardware :) */ static int ng_PPPoE_constructor(node_p *nodep) { priv_p privdata; int error; AAA /* Initialize private descriptor */ MALLOC(privdata, priv_p, sizeof(*privdata), M_NETGRAPH, M_WAITOK); if (privdata == NULL) return (ENOMEM); bzero(privdata, sizeof(*privdata)); /* Call the 'generic' (ie, superclass) node constructor */ if ((error = ng_make_node_common(&typestruct, nodep))) { FREE(privdata, M_NETGRAPH); return (error); } /* Link structs together; this counts as our one reference to *nodep */ (*nodep)->private = privdata; privdata->node = *nodep; return (0); } /* * Give our ok for a hook to be added... * point the hook's private info to the hook structure. * * The following hook names are special: * Ethernet: the hook that should be connected to a NIC. * debug: copies of data sent out here (when I write the code). */ static int ng_PPPoE_newhook(node_p node, hook_p hook, const char *name) { const priv_p privp = node->private; sessp sp; AAA if (strcmp(name, NG_PPPOE_HOOK_ETHERNET) == 0) { privp->ethernet_hook = hook; hook->private = &privp->ethernet_hook; } else if (strcmp(name, NG_PPPOE_HOOK_DEBUG) == 0) { privp->debug_hook = hook; hook->private = &privp->debug_hook; } else { /* * Any other unique name is OK. * The infrastructure has already checked that it's unique, * so just allocate it and hook it in. */ MALLOC(sp, sessp, sizeof(*sp), M_NETGRAPH, M_WAITOK); if (sp == NULL) { return (ENOMEM); } bzero(sp, sizeof(*sp)); hook->private = sp; sp->hook = hook; } return(0); } /* * Get a netgraph control message. * Check it is one we understand. If needed, send a response. * We sometimes save the address for an async action later. * Always free the message. */ static int ng_PPPoE_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr) { priv_p privp = node->private; struct ngPPPoE_init_data *ourmsg = NULL; struct ng_mesg *resp = NULL; int error = 0; hook_p hook = NULL; sessp sp = NULL; negp neg = NULL; AAA /* Deal with message according to cookie and command */ switch (msg->header.typecookie) { case NGM_PPPOE_COOKIE: switch (msg->header.cmd) { case NGM_PPPOE_CONNECT: case NGM_PPPOE_LISTEN: case NGM_PPPOE_OFFER: ourmsg = (struct ngPPPoE_init_data *)msg->data; if (( sizeof(*ourmsg) > msg->header.arglen) || ((sizeof(*ourmsg) + ourmsg->data_len) > msg->header.arglen)) { printf("PPPoE_rcvmsg: bad arg size"); LEAVE(EMSGSIZE); } if (ourmsg->data_len > PPPOE_SERVICE_NAME_SIZE) { printf("pppoe: init data too long (%d)\n", ourmsg->data_len); LEAVE(EMSGSIZE); } /* make sure strcmp will terminate safely */ ourmsg->hook[sizeof(ourmsg->hook) - 1] = '\0'; /* cycle through all known hooks */ LIST_FOREACH(hook, &node->hooks, hooks) { if (hook->name && strcmp(hook->name, ourmsg->hook) == 0) break; } if (hook == NULL) { LEAVE(ENOENT); } if ((hook->private == &privp->debug_hook) || (hook->private == &privp->ethernet_hook)) { LEAVE(EINVAL); } sp = hook->private; if (sp->state |= PPPOE_SNONE) { printf("pppoe: Session already active\n"); LEAVE(EISCONN); } /* * set up prototype header */ MALLOC(neg, negp, sizeof(*neg), M_NETGRAPH, M_WAITOK); if (neg == NULL) { printf("pppoe: Session out of memory\n"); LEAVE(ENOMEM); } bzero(neg, sizeof(*neg)); MGETHDR(neg->m, M_DONTWAIT, MT_DATA); if(neg->m == NULL) { printf("pppoe: Session out of mbufs\n"); FREE(neg, M_NETGRAPH); LEAVE(ENOBUFS); } neg->m->m_pkthdr.rcvif = NULL; MCLGET(neg->m, M_DONTWAIT); if ((neg->m->m_flags & M_EXT) == 0) { printf("pppoe: Session out of mcls\n"); m_freem(neg->m); FREE(neg, M_NETGRAPH); LEAVE(ENOBUFS); } sp->neg = neg; callout_handle_init( &neg->timeout_handle); neg->m->m_len = sizeof(struct pppoe_full_hdr); neg->pkt = mtod(neg->m, union packet*); neg->pkt->pkt_header.eh = eh_prototype; neg->pkt->pkt_header.ph.ver = 0x1; neg->pkt->pkt_header.ph.type = 0x1; neg->pkt->pkt_header.ph.sid = 0x0000; neg->timeout = 0; strncpy(sp->creator, retaddr, NG_NODELEN); sp->creator[NG_NODELEN] = '\0'; } switch (msg->header.cmd) { case NGM_PPPOE_GET_STATUS: { struct ngPPPoEstat *stats; NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); if (!resp) { LEAVE(ENOMEM); } stats = (struct ngPPPoEstat *) resp->data; stats->packets_in = privp->packets_in; stats->packets_out = privp->packets_out; break; } case NGM_PPPOE_CONNECT: /* * Check the hook exists and is Uninitialised. * Send a PADI request, and start the timeout logic. * Store the originator of this message so we can send * a success of fail message to them later. * Move the session to SINIT * Set up the session to the correct state and * start it. */ neg->service.hdr.tag_type = PTT_SRV_NAME; neg->service.hdr.tag_len = htons((u_int16_t)ourmsg->data_len); if (ourmsg->data_len) { bcopy(ourmsg->data, neg->service.data, ourmsg->data_len); } neg->service_len = ourmsg->data_len; pppoe_start(sp); break; case NGM_PPPOE_LISTEN: /* * Check the hook exists and is Uninitialised. * Install the service matching string. * Store the originator of this message so we can send * a success of fail message to them later. * Move the hook to 'LISTENING' */ neg->service.hdr.tag_type = PTT_SRV_NAME; neg->service.hdr.tag_len = htons((u_int16_t)ourmsg->data_len); if (ourmsg->data_len) { bcopy(ourmsg->data, neg->service.data, ourmsg->data_len); } neg->service_len = ourmsg->data_len; neg->pkt->pkt_header.ph.code = PADT_CODE; /* * wait for PADI packet coming from ethernet */ sp->state = PPPOE_LISTENING; break; case NGM_PPPOE_OFFER: /* * Check the hook exists and is Uninitialised. * Store the originator of this message so we can send * a success of fail message to them later. * Store the AC-Name given and go to PRIMED. */ neg->ac_name.hdr.tag_type = PTT_AC_NAME; neg->ac_name.hdr.tag_len = htons((u_int16_t)ourmsg->data_len); if (ourmsg->data_len) { bcopy(ourmsg->data, neg->ac_name.data, ourmsg->data_len); } neg->ac_name_len = ourmsg->data_len; neg->pkt->pkt_header.ph.code = PADO_CODE; /* * Wait for PADI packet coming from hook */ sp->state = PPPOE_PRIMED; break; default: LEAVE(EINVAL); } break; default: LEAVE(EINVAL); } /* Take care of synchronous response, if any */ if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); /* Free the message and return */ quit: FREE(msg, M_NETGRAPH); return(error); } /* * Start a client into the first state. A separate function because * it can be needed if the negotiation times out. */ static void pppoe_start(sessp sp) { struct { struct pppoe_tag hdr; union uniq data; } uniqtag; /* * kick the state machine into starting up */ AAA sp->state = PPPOE_SINIT; /* reset the packet header to broadcast */ sp->neg->pkt->pkt_header.eh = eh_prototype; sp->neg->pkt->pkt_header.ph.code = PADI_CODE; uniqtag.hdr.tag_type = PTT_HOST_UNIQ; uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(uniqtag.data)); uniqtag.data.pointer = sp; init_tags(sp); insert_tag(sp, &uniqtag.hdr); insert_tag(sp, &sp->neg->service.hdr); make_packet(sp); sendpacket(sp); } /* * Receive data, and do something with it. * The caller will never free m or meta, so * if we use up this data or abort we must free BOTH of these. */ static int ng_PPPoE_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { node_p node = hook->node; const priv_p privp = node->private; sessp sp = hook->private; struct pppoe_full_hdr *wh; struct pppoe_hdr *ph; int error = 0; u_int16_t session; u_int16_t length; u_int8_t code; struct pppoe_tag *tag = NULL; hook_p sendhook; struct { struct pppoe_tag hdr; union uniq data; } uniqtag; negp neg = NULL; AAA if (hook->private == &privp->debug_hook) { /* * Data from the debug hook gets sent without modification * straight to the ethernet. */ NG_SEND_DATA( error, privp->ethernet_hook, m, meta); privp->packets_out++; } else if (hook->private == &privp->ethernet_hook) { /* * Incoming data. * Dig out various fields from the packet. * use them to decide where to send it. */ privp->packets_in++; if( m->m_len < sizeof(*wh)) { m = m_pullup(m, sizeof(*wh)); /* Checks length */ if (m == NULL) { printf("couldn't m_pullup\n"); LEAVE(ENOBUFS); } } wh = mtod(m, struct pppoe_full_hdr *); ph = &wh->ph; session = ntohs(wh->ph.sid); length = ntohs(wh->ph.length); code = wh->ph.code; switch(wh->eh.ether_type) { case ETHERTYPE_PPPOE_DISC: /* * We need to try make sure that the tag area * is contiguous, or we could wander of the end * of a buffer and make a mess. * (Linux wouldn't have this problem). */ /*XXX fix this mess */ if (m->m_pkthdr.len <= MHLEN) { if( m->m_len < m->m_pkthdr.len) { m = m_pullup(m, m->m_pkthdr.len); if (m == NULL) { printf("couldn't m_pullup\n"); LEAVE(ENOBUFS); } } } if (m->m_len != m->m_pkthdr.len) { /* * It's not all in one piece. * We need to do extra work. */ printf("packet fragmented\n"); LEAVE(EMSGSIZE); } switch(code) { case PADI_CODE: /* * We are a server: * Look for a hook with the required service * and send the ENTIRE packet up there. * It should come back to a new hook in * PRIMED state. Look there for further * processing. */ tag = get_tag(ph, PTT_SRV_NAME); if (tag == NULL) { printf("no service tag\n"); LEAVE(ENETUNREACH); } sendhook = pppoe_match_svc(hook->node, tag->tag_data, ntohs(tag->tag_len)); if (sendhook) { NG_SEND_DATA(error, sendhook, m, meta); } else { printf("no such service\n"); LEAVE(ENETUNREACH); } break; case PADO_CODE: /* * We are a client: * Use the host_uniq tag to find the * hook this is in response to. * Received #2, now send #3 * For now simply accept the first we receive. */ tag = get_tag(ph, PTT_HOST_UNIQ); if ((tag == NULL) || (ntohs(tag->tag_len) != sizeof(sp))) { printf("no host unique field\n"); LEAVE(ENETUNREACH); } sendhook = pppoe_finduniq(node, tag); if (sendhook == NULL) { printf("no matching session\n"); LEAVE(ENETUNREACH); } /* * Check the session is in the right state. * It needs to be in PPPOE_SINIT. */ sp = sendhook->private; if (sp->state != PPPOE_SINIT) { printf("session in wrong state\n"); LEAVE(ENETUNREACH); } neg = sp->neg; untimeout(pppoe_ticker, sendhook, neg->timeout_handle); /* * This is the first time we hear * from the server, so note it's * unicast address, replacing the * broadcast address . */ bcopy(wh->eh.ether_shost, neg->pkt->pkt_header.eh.ether_dhost, ETHER_ADDR_LEN); neg->timeout = 0; neg->pkt->pkt_header.ph.code = PADR_CODE; init_tags(sp); insert_tag(sp, &neg->service.hdr); /* Service */ insert_tag(sp, tag); /* Host Unique */ tag = get_tag(ph, PTT_AC_COOKIE); if (tag) insert_tag(sp, tag); /* return cookie */ tag = get_tag(ph, PTT_AC_NAME); if (tag) insert_tag(sp, tag); /* return cookie */ scan_tags(sp, ph); make_packet(sp); sp->state = PPPOE_SREQ; sendpacket(sp); break; case PADR_CODE: /* * We are a server: * Use the ac_cookie tag to find the * hook this is in response to. */ tag = get_tag(ph, PTT_AC_COOKIE); if ((tag == NULL) || (ntohs(tag->tag_len) != sizeof(sp))) { LEAVE(ENETUNREACH); } sendhook = pppoe_finduniq(node, tag); if (sendhook == NULL) { LEAVE(ENETUNREACH); } /* * Check the session is in the right state. * It needs to be in PPPOE_SOFFER * or PPPOE_NEWCONNECTED. If the latter, * then this is a retry by the client. * so be nice, and resend. */ sp = sendhook->private; if (sp->state == PPPOE_NEWCONNECTED) { /* * Whoa! drop back to resend that * PADS packet. * We should still have a copy of it. */ sp->state = PPPOE_SOFFER; } if (sp->state != PPPOE_SOFFER) { LEAVE (ENETUNREACH); break; } neg = sp->neg; untimeout(pppoe_ticker, sendhook, neg->timeout_handle); neg->pkt->pkt_header.ph.code = PADS_CODE; if (sp->Session_ID == 0) neg->pkt->pkt_header.ph.sid = htons(sp->Session_ID = get_new_sid(node)); neg->timeout = 0; /* * start working out the tags to respond with. */ init_tags(sp); insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ insert_tag(sp, tag); /* ac_cookie */ tag = get_tag(ph, PTT_SRV_NAME); insert_tag(sp, tag); /* returned service */ tag = get_tag(ph, PTT_HOST_UNIQ); insert_tag(sp, tag); /* returned hostuniq */ scan_tags(sp, ph); make_packet(sp); sp->state = PPPOE_NEWCONNECTED; sendpacket(sp); pppoe_send_event(sp, NGM_PPPOE_SUCCESS); /* * Having sent the last Negotiation header, * Set up the stored packet header to * be correct for the actual session. * But keep the negotialtion stuff * around in case we need to resend this last * packet. We'll discard it when we move * from NEWCONNECTED to CONNECTED */ sp->pkt_hdr = neg->pkt->pkt_header; sp->pkt_hdr.eh.ether_type = ETHERTYPE_PPPOE_SESS; sp->pkt_hdr.ph.code = 0; pppoe_send_event(sp, NGM_PPPOE_SUCCESS); break; case PADS_CODE: /* * We are a client: * Use the host_uniq tag to find the * hook this is in response to. * take the session ID and store it away. * Also make sure the pre-made header is * correct and set us into Session mode. */ tag = get_tag(ph, PTT_HOST_UNIQ); if ((tag == NULL) || (ntohs(tag->tag_len) != sizeof(sp))) { LEAVE (ENETUNREACH); break; } sendhook = pppoe_finduniq(node, tag); if (sendhook == NULL) { LEAVE(ENETUNREACH); } /* * Check the session is in the right state. * It needs to be in PPPOE_SREQ. */ sp = sendhook->private; if (sp->state != PPPOE_SREQ) { LEAVE(ENETUNREACH); } neg = sp->neg; untimeout(pppoe_ticker, sendhook, neg->timeout_handle); neg->pkt->pkt_header.ph.sid = wh->ph.sid; sp->Session_ID = ntohs(wh->ph.sid); neg->timeout = 0; sp->state = PPPOE_CONNECTED; /* * Now we have gone to Connected mode, * Free all resources needed for * negotiation. * Keep a copy of the header we will be using. */ sp->pkt_hdr = neg->pkt->pkt_header; sp->pkt_hdr.eh.ether_type = ETHERTYPE_PPPOE_SESS; sp->pkt_hdr.ph.code = 0; m_freem(neg->m); FREE(sp->neg, M_NETGRAPH); sp->neg = NULL; pppoe_send_event(sp, NGM_PPPOE_SUCCESS); break; case PADT_CODE: /* * Send a 'close' message to the controlling * process (the one that set us up); * And then tear everything down. * * Find matching peer/session combination. */ sendhook = pppoe_findsession(node, wh); NG_FREE_DATA(m, meta); /* no longer needed */ if (sendhook == NULL) { LEAVE(ENETUNREACH); } /* send message to creator */ /* close hook */ if (sendhook) { ng_destroy_hook(sendhook); } break; default: LEAVE(EPFNOSUPPORT); } break; case ETHERTYPE_PPPOE_SESS: /* * find matching peer/session combination. */ sendhook = pppoe_findsession(node, wh); if (sendhook == NULL) { LEAVE (ENETUNREACH); break; } sp = sendhook->private; m_adj(m, sizeof(*wh)); if (m->m_pkthdr.len < length) { /* Packet too short, dump it */ LEAVE(EMSGSIZE); } if (m->m_pkthdr.len > length) { m_adj(m, -((int)(m->m_pkthdr.len - length))); } /* XXX also need to trim excess at end I should think */ if ( sp->state != PPPOE_CONNECTED) { if (sp->state == PPPOE_NEWCONNECTED) { sp->state = PPPOE_CONNECTED; /* * Now we have gone to Connected mode, * Free all resources needed for * negotiation. */ m_freem(sp->neg->m); FREE(sp->neg, M_NETGRAPH); sp->neg = NULL; } else { LEAVE (ENETUNREACH); break; } } NG_SEND_DATA( error, sendhook, m, meta); break; default: LEAVE(EPFNOSUPPORT); } } else { /* * Not ethernet or debug hook.. * * The packet has come in on a normal hook. * We need to find out what kind of hook, * So we can decide how to handle it. * Check the hook's state. */ sp = hook->private; switch (sp->state) { case PPPOE_NEWCONNECTED: case PPPOE_CONNECTED: { struct pppoe_full_hdr *wh; /* * Bang in a pre-made header, and set the length up * to be correct. Then send it to the ethernet driver. */ M_PREPEND(m, sizeof(*wh), M_DONTWAIT); if (m == NULL) { LEAVE(ENOBUFS); } wh = mtod(m, struct pppoe_full_hdr *); bcopy(&sp->pkt_hdr, wh, sizeof(*wh)); wh->ph.length = htons((short)(m->m_pkthdr.len)); NG_SEND_DATA( error, privp->ethernet_hook, m, meta); privp->packets_out++; break; } case PPPOE_PRIMED: /* * A PADI packet is being returned by the application * that has set up this hook. This indicates that it * wants us to offer service. */ neg = sp->neg; - m_pullup(m, sizeof(*wh)); /* Checks length */ + if (m->m_len < sizeof(*wh)) + m_pullup(m, sizeof(*wh)); if (m == NULL) { LEAVE(ENOBUFS); } wh = mtod(m, struct pppoe_full_hdr *); ph = &wh->ph; session = ntohs(wh->ph.sid); length = ntohs(wh->ph.length); code = wh->ph.code; if ( code != PADI_CODE) { LEAVE(EINVAL); }; untimeout(pppoe_ticker, hook, neg->timeout_handle); /* * This is the first time we hear * from the client, so note it's * unicast address, replacing the * broadcast address . */ bcopy(wh->eh.ether_shost, neg->pkt->pkt_header.eh.ether_dhost, ETHER_ADDR_LEN); sp->state = PPPOE_SOFFER; neg->timeout = 0; neg->pkt->pkt_header.ph.code = PADO_CODE; /* * start working out the tags to respond with. */ uniqtag.hdr.tag_type = PTT_AC_COOKIE; uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp)); uniqtag.data.pointer = sp; init_tags(sp); insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ insert_tag(sp, tag); /* returned hostunique */ insert_tag(sp, &uniqtag.hdr); /* AC cookie */ tag = get_tag(ph, PTT_SRV_NAME); insert_tag(sp, tag); /* returned service */ /* XXX maybe put the tag in the session store */ scan_tags(sp, ph); make_packet(sp); sendpacket(sp); break; /* * Packets coming from the hook make no sense * to sessions in these states. Throw them away. */ case PPPOE_SINIT: case PPPOE_SREQ: case PPPOE_SOFFER: case PPPOE_SNONE: case PPPOE_LISTENING: case PPPOE_DEAD: default: LEAVE(ENETUNREACH); } } quit: NG_FREE_DATA(m, meta); return error; } /* * Do local shutdown processing.. * If we are a persistant device, we might refuse to go away, and * we'd only remove our links and reset ourself. */ static int ng_PPPoE_rmnode(node_p node) { const priv_p privdata = node->private; AAA node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); node->private = NULL; ng_unref(privdata->node); FREE(privdata, M_NETGRAPH); return (0); } /* * This is called once we've already connected a new hook to the other node. * It gives us a chance to balk at the last minute. */ static int ng_PPPoE_connect(hook_p hook) { /* be really amiable and just say "YUP that's OK by me! " */ return (0); } /* * Hook disconnection * * Clean up all dangling links and infirmation about the session/hook. * For this type, removal of the last link destroys the node */ static int ng_PPPoE_disconnect(hook_p hook) { node_p node = hook->node; priv_p privp = node->private; sessp sp; AAA if (hook->private == &privp->debug_hook) { privp->debug_hook = NULL; } else if (hook->private == &privp->ethernet_hook) { privp->ethernet_hook = NULL; } else { sp = hook->private; if (sp->state != PPPOE_SNONE ) { pppoe_send_event(sp, NGM_PPPOE_CLOSE); } if ((privp->ethernet_hook) && ((sp->state == PPPOE_CONNECTED) || (sp->state == PPPOE_NEWCONNECTED))) { struct mbuf *m; struct pppoe_full_hdr *wh; struct pppoe_tag *tag; int msglen = strlen(SIGNOFF); void *dummy = NULL; int error = 0; /* revert the stored header to DISC/PADT mode */ wh = &sp->pkt_hdr; wh->ph.code = PADT_CODE; wh->eh.ether_type = ETHERTYPE_PPPOE_DISC; /* generate a packet of that type */ MGETHDR(m, M_DONTWAIT, MT_DATA); m->m_pkthdr.rcvif = NULL; m->m_pkthdr.len = m->m_len = sizeof(*wh); bcopy((caddr_t)wh, mtod(m, caddr_t), sizeof(*wh)); /* Add a General error message and adjust sizes */ wh = mtod(m, struct pppoe_full_hdr *); tag = wh->ph.tag; tag->tag_type = PTT_GEN_ERR; tag->tag_len = htons((u_int16_t)msglen); strncpy(tag->tag_data, SIGNOFF, msglen); m->m_pkthdr.len = (m->m_len += sizeof(*tag) + msglen); wh->ph.length = htons(sizeof(*tag) + msglen); NG_SEND_DATA(error, privp->ethernet_hook, m, dummy); } if (sp->neg) { untimeout(pppoe_ticker, hook, sp->neg->timeout_handle); if (sp->neg->m) m_freem(sp->neg->m); FREE(sp->neg, M_NETGRAPH); } FREE(sp, M_NETGRAPH); hook->private = NULL; } if (node->numhooks == 0) ng_rmnode(node); return (0); } /* * timeouts come here. */ static void pppoe_ticker(void *arg) { int s = splnet(); hook_p hook = arg; sessp sp = hook->private; negp neg = sp->neg; int error = 0; struct mbuf *m0 = NULL; priv_p privp = hook->node->private; meta_p dummy = NULL; AAA switch(sp->state) { /* * resend the last packet, using an exponential backoff. * After a period of time, stop growing the backoff, * and either leave it, or reverst to the start. */ case PPPOE_SINIT: case PPPOE_SREQ: /* timeouts on these produce resends */ m0 = m_copypacket(sp->neg->m, M_DONTWAIT); NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy); neg->timeout_handle = timeout(pppoe_ticker, hook, neg->timeout * hz); if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) { if (sp->state == PPPOE_SREQ) { /* revert to SINIT mode */ pppoe_start(sp); } else { neg->timeout = PPPOE_TIMEOUT_LIMIT; } } break; case PPPOE_PRIMED: case PPPOE_SOFFER: /* a timeout on these says "give up" */ ng_destroy_hook(hook); break; default: /* timeouts have no meaning in other states */ printf("pppoe: unexpected timeout\n"); } splx(s); } static void sendpacket(sessp sp) { int error = 0; struct mbuf *m0 = NULL; hook_p hook = sp->hook; negp neg = sp->neg; priv_p privp = hook->node->private; meta_p dummy = NULL; AAA switch(sp->state) { case PPPOE_LISTENING: case PPPOE_DEAD: case PPPOE_SNONE: case PPPOE_NEWCONNECTED: case PPPOE_CONNECTED: printf("pppoe: sendpacket: unexpected state\n"); break; case PPPOE_PRIMED: /* No packet to send, but set up the timeout */ neg->timeout_handle = timeout(pppoe_ticker, hook, PPPOE_OFFER_TIMEOUT * hz); break; case PPPOE_SOFFER: /* * send the offer but if they don't respond * in PPPOE_OFFER_TIMEOUT seconds, forget about it. */ m0 = m_copypacket(sp->neg->m, M_DONTWAIT); NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy); neg->timeout_handle = timeout(pppoe_ticker, hook, PPPOE_OFFER_TIMEOUT * hz); break; case PPPOE_SINIT: case PPPOE_SREQ: m0 = m_copypacket(sp->neg->m, M_DONTWAIT); NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy); neg->timeout_handle = timeout(pppoe_ticker, hook, hz); neg->timeout = 2; break; default: error = EINVAL; printf("pppoe: timeout: bad state\n"); } /* return (error); */ } /* * Parse an incoming packet to see if any tags should be copied to the * output packet. DOon't do any tags that are likely to have been * handles a the main state machine. */ static struct pppoe_tag* scan_tags(sessp sp, struct pppoe_hdr* ph) { char *end = (char *)next_tag(ph); char *ptn; struct pppoe_tag *pt = &ph->tag[0]; /* * Keep processing tags while a tag header will still fit. */ AAA while((char*)(pt + 1) <= end) { /* * If the tag data would go past the end of the packet, abort. */ ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len)); if(ptn > end) return NULL; switch (pt->tag_type) { case PTT_RELAY_SID: insert_tag(sp, pt); break; case PTT_EOL: return NULL; case PTT_SRV_NAME: case PTT_AC_NAME: case PTT_HOST_UNIQ: case PTT_AC_COOKIE: case PTT_VENDOR: case PTT_SRV_ERR: case PTT_SYS_ERR: case PTT_GEN_ERR: break; } pt = (struct pppoe_tag*)ptn; } return NULL; } static int pppoe_send_event(sessp sp, enum cmd cmdid) { int error; struct ng_mesg *msg; struct ngPPPoE_sts *sts; AAA NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, cmdid, sizeof(struct ngPPPoE_sts), M_NOWAIT); sts = (struct ngPPPoE_sts *)msg->data; strncpy(sts->hook, sp->hook->name, NG_HOOKLEN + 1); error = ng_send_msg(sp->hook->node, msg, sp->creator, NULL); return (error); } diff --git a/sys/netgraph/ng_rfc1490.c b/sys/netgraph/ng_rfc1490.c index 3649476f95cc..6bbdc41c6500 100644 --- a/sys/netgraph/ng_rfc1490.c +++ b/sys/netgraph/ng_rfc1490.c @@ -1,347 +1,348 @@ /* * ng_rfc1490.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_rfc1490.c,v 1.19 1999/01/28 23:54:53 julian Exp $ */ /* * This node does RFC 1490 multiplexing. * * NOTE: RFC 1490 is updated by RFC 2427. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * DEFINITIONS */ /* Q.922 stuff -- see RFC 1490 */ #define HDLC_UI 0x03 #define NLPID_IP 0xCC #define NLPID_PPP 0xCF #define NLPID_SNAP 0x80 #define NLPID_Q933 0x08 #define NLPID_CLNP 0x81 #define NLPID_ESIS 0x82 #define NLPID_ISIS 0x83 /* Node private data */ struct private { hook_p downlink; hook_p ppp; hook_p inet; }; typedef struct private *priv_p; /* Netgraph node methods */ static int ng_rfc1490_constructor(node_p * nodep); static int ng_rfc1490_rcvmsg(node_p node, struct ng_mesg * msg, const char *retaddr, struct ng_mesg **resp); static int ng_rfc1490_rmnode(node_p node); static int ng_rfc1490_newhook(node_p node, hook_p hook, const char *name); static int ng_rfc1490_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); static int ng_rfc1490_disconnect(hook_p hook); /* Node type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_RFC1490_NODE_TYPE, NULL, ng_rfc1490_constructor, ng_rfc1490_rcvmsg, ng_rfc1490_rmnode, ng_rfc1490_newhook, NULL, NULL, ng_rfc1490_rcvdata, ng_rfc1490_rcvdata, ng_rfc1490_disconnect }; NETGRAPH_INIT(rfc1490, &typestruct); /************************************************************************ NETGRAPH NODE STUFF ************************************************************************/ /* * Node constructor */ static int ng_rfc1490_constructor(node_p *nodep) { priv_p priv; int error; /* Allocate private structure */ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK); if (priv == NULL) return (ENOMEM); bzero(priv, sizeof(*priv)); /* Call generic node constructor */ if ((error = ng_make_node_common(&typestruct, nodep))) { FREE(priv, M_NETGRAPH); return (error); } (*nodep)->private = priv; /* Done */ return (0); } /* * Give our ok for a hook to be added */ static int ng_rfc1490_newhook(node_p node, hook_p hook, const char *name) { const priv_p priv = node->private; if (!strcmp(name, NG_RFC1490_HOOK_DOWNSTREAM)) { if (priv->downlink) return (EISCONN); priv->downlink = hook; } else if (!strcmp(name, NG_RFC1490_HOOK_PPP)) { if (priv->ppp) return (EISCONN); priv->ppp = hook; } else if (!strcmp(name, NG_RFC1490_HOOK_INET)) { if (priv->inet) return (EISCONN); priv->inet = hook; } else return (EINVAL); return (0); } /* * Receive a control message. We don't support any special ones. */ static int ng_rfc1490_rcvmsg(node_p node, struct ng_mesg *msg, const char *raddr, struct ng_mesg **rp) { FREE(msg, M_NETGRAPH); return (EINVAL); } /* * Receive data on a hook and encapsulate according to RFC 1490. * Only those nodes marked (*) are supported by this routine so far. * * Q.922 control * | * | * -------------------------------------------- * | 0x03 | * UI I Frame * | | * --------------------------------- -------------- * | 0x08 | 0x81 |0xCC |0xCF | 0x00 |..01.... |..10.... * | | | | | 0x80 | | * Q.933 CLNP IP(*) PPP(*) SNAP ISO 8208 ISO 8208 * | (rfc1973) | Modulo 8 Modulo 128 * | | * -------------------- OUI * | | | * L2 ID L3 ID ------------------------- * | User |00-80-C2 |00-00-00 * | specified | | * | 0x70 PID Ethertype * | | | * ------------------- --------------... ---------- * |0x51 |0x4E | |0x4C |0x1 |0xB | |0x806 | * | | | | | | | | | * 7776 Q.922 Others 802.2 802.3 802.6 Others ARP(*) Others * * */ #define MAX_ENCAPS_HDR 8 #define ERROUT(x) do { error = (x); goto done; } while (0) #define OUICMP(P,A,B,C) ((P)[0]==(A) && (P)[1]==(B) && (P)[2]==(C)) static int ng_rfc1490_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { const node_p node = hook->node; const priv_p priv = node->private; int error = 0; if (hook == priv->downlink) { u_char *start, *ptr; - if (!m || !(m = m_pullup(m, MAX_ENCAPS_HDR))) + if (!m || (m->m_len < MAX_ENCAPS_HDR + && !(m = m_pullup(m, MAX_ENCAPS_HDR)))) ERROUT(ENOBUFS); ptr = start = mtod(m, u_char *); /* Must be UI frame */ if (*ptr++ != HDLC_UI) ERROUT(0); /* Eat optional zero pad byte */ if (*ptr == 0x00) ptr++; /* Multiplex on NLPID */ switch (*ptr++) { case NLPID_SNAP: if (OUICMP(ptr, 0, 0, 0)) { /* It's an ethertype */ u_int16_t etype; ptr += 3; etype = ntohs(*((u_int16_t *) ptr)); ptr += 2; m_adj(m, ptr - start); switch (etype) { case ETHERTYPE_IP: NG_SEND_DATA(error, priv->inet, m, meta); break; case ETHERTYPE_ARP: case ETHERTYPE_REVARP: default: ERROUT(0); } } else if (OUICMP(ptr, 0x00, 0x80, 0xc2)) /* 802.1 bridging */ ERROUT(0); else /* Other weird stuff... */ ERROUT(0); break; case NLPID_IP: m_adj(m, ptr - start); NG_SEND_DATA(error, priv->inet, m, meta); break; case NLPID_PPP: m_adj(m, ptr - start); NG_SEND_DATA(error, priv->ppp, m, meta); break; case NLPID_Q933: case NLPID_CLNP: case NLPID_ESIS: case NLPID_ISIS: ERROUT(0); default: /* Try PPP (see RFC 1973) */ ptr--; /* NLPID becomes PPP proto */ if ((*ptr & 0x01) == 0x01) ERROUT(0); m_adj(m, ptr - start); NG_SEND_DATA(error, priv->ppp, m, meta); break; } } else if (hook == priv->ppp) { M_PREPEND(m, 2, M_DONTWAIT); /* Prepend PPP NLPID */ if (!m) ERROUT(ENOBUFS); mtod(m, u_char *)[0] = HDLC_UI; mtod(m, u_char *)[1] = NLPID_PPP; NG_SEND_DATA(error, priv->downlink, m, meta); } else if (hook == priv->inet) { M_PREPEND(m, 2, M_DONTWAIT); /* Prepend IP NLPID */ if (!m) ERROUT(ENOBUFS); mtod(m, u_char *)[0] = HDLC_UI; mtod(m, u_char *)[1] = NLPID_IP; NG_SEND_DATA(error, priv->downlink, m, meta); } else panic(__FUNCTION__); done: NG_FREE_DATA(m, meta); return (error); } /* * Nuke node */ static int ng_rfc1490_rmnode(node_p node) { const priv_p priv = node->private; /* Take down netgraph node */ node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); bzero(priv, sizeof(*priv)); node->private = NULL; ng_unref(node); /* let the node escape */ return (0); } /* * Hook disconnection */ static int ng_rfc1490_disconnect(hook_p hook) { const priv_p priv = hook->node->private; if (hook->node->numhooks == 0) ng_rmnode(hook->node); else if (hook == priv->downlink) priv->downlink = NULL; else if (hook == priv->inet) priv->inet = NULL; else if (hook == priv->ppp) priv->ppp = NULL; else panic(__FUNCTION__); return (0); } diff --git a/sys/netgraph/ng_vjc.c b/sys/netgraph/ng_vjc.c index 9755bd433348..a7f0ec231aea 100644 --- a/sys/netgraph/ng_vjc.c +++ b/sys/netgraph/ng_vjc.c @@ -1,439 +1,446 @@ /* * ng_vjc.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: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_vjc.c,v 1.14 1999/01/28 23:54:54 julian Exp $ */ /* * This node performs Van Jacobsen IP header (de)compression. * You must have included net/slcompress.c in your kernel compilation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Check agreement with slcompress.c */ #if MAX_STATES != NG_VJC_MAX_CHANNELS #error NG_VJC_MAX_CHANNELS must be the same as MAX_STATES #endif #define MAX_VJHEADER 16 /* Node private data */ struct private { struct ngm_vjc_config conf; struct slcompress slc; hook_p ip; hook_p vjcomp; hook_p vjuncomp; hook_p vjip; }; typedef struct private *priv_p; #define ERROUT(x) do { error = (x); goto done; } while (0) /* Netgraph node methods */ static int ng_vjc_constructor(node_p *nodep); static int ng_vjc_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp); static int ng_vjc_rmnode(node_p node); static int ng_vjc_newhook(node_p node, hook_p hook, const char *name); static int ng_vjc_rcvdata(hook_p hook, struct mbuf *m, meta_p t); static int ng_vjc_disconnect(hook_p hook); /* Helper stuff */ -static struct mbuf *pulluphdrs(struct mbuf *m); +static struct mbuf *ng_vjc_pulluphdrs(struct mbuf *m); /* Node type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_VJC_NODE_TYPE, NULL, ng_vjc_constructor, ng_vjc_rcvmsg, ng_vjc_rmnode, ng_vjc_newhook, NULL, NULL, ng_vjc_rcvdata, ng_vjc_rcvdata, ng_vjc_disconnect }; NETGRAPH_INIT(vjc, &typestruct); /************************************************************************ NETGRAPH NODE METHODS ************************************************************************/ /* * Create a new node */ static int ng_vjc_constructor(node_p *nodep) { priv_p priv; int error; /* Allocate private structure */ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK); if (priv == NULL) return (ENOMEM); bzero(priv, sizeof(*priv)); /* Call generic node constructor */ if ((error = ng_make_node_common(&typestruct, nodep))) { FREE(priv, M_NETGRAPH); return (error); } (*nodep)->private = priv; /* Done */ return (0); } /* * Add a new hook */ static int ng_vjc_newhook(node_p node, hook_p hook, const char *name) { const priv_p priv = (priv_p) node->private; hook_p *hookp; /* Get hook */ if (!strcmp(name, NG_VJC_HOOK_IP)) hookp = &priv->ip; else if (!strcmp(name, NG_VJC_HOOK_VJCOMP)) hookp = &priv->vjcomp; else if (!strcmp(name, NG_VJC_HOOK_VJUNCOMP)) hookp = &priv->vjuncomp; else if (!strcmp(name, NG_VJC_HOOK_VJIP)) hookp = &priv->vjip; else return (EINVAL); /* See if already connected */ if (*hookp) return (EISCONN); /* OK */ *hookp = hook; return (0); } /* * Receive a control message */ static int ng_vjc_rcvmsg(node_p node, struct ng_mesg *msg, const char *raddr, struct ng_mesg **rptr) { const priv_p priv = (priv_p) node->private; struct ng_mesg *resp = NULL; int error = 0; /* Check type cookie */ switch (msg->header.typecookie) { case NGM_VJC_COOKIE: switch (msg->header.cmd) { - case NGM_VJC_CONFIG: + case NGM_VJC_SET_CONFIG: { struct ngm_vjc_config *const c = (struct ngm_vjc_config *) msg->data; - if (msg->header.arglen != sizeof(*c) - || c->numChannels > NG_VJC_MAX_CHANNELS - || c->numChannels < NG_VJC_MIN_CHANNELS) + if (msg->header.arglen != sizeof(*c)) ERROUT(EINVAL); - if (priv->conf.enabled && c->enabled) + if ((priv->conf.enableComp || priv->conf.enableDecomp) + && (c->enableComp || c->enableDecomp)) ERROUT(EALREADY); - if (c->enabled != 0) { + if (c->enableComp) { + if (c->numChannels > NG_VJC_MAX_CHANNELS + || c->numChannels < NG_VJC_MIN_CHANNELS) + ERROUT(EINVAL); + } else { + c->numChannels = NG_VJC_MAX_CHANNELS; + } + if (c->enableComp != 0 || c->enableDecomp != 0) { bzero(&priv->slc, sizeof(priv->slc)); sl_compress_init(&priv->slc, c->numChannels); } priv->conf = *c; break; } case NGM_VJC_GET_STATE: NG_MKRESPONSE(resp, msg, sizeof(priv->slc), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); *((struct slcompress *) resp->data) = priv->slc; break; case NGM_VJC_CLR_STATS: priv->slc.sls_packets = 0; priv->slc.sls_compressed = 0; priv->slc.sls_searches = 0; priv->slc.sls_misses = 0; priv->slc.sls_uncompressedin = 0; priv->slc.sls_compressedin = 0; priv->slc.sls_errorin = 0; priv->slc.sls_tossed = 0; break; case NGM_VJC_RECV_ERROR: priv->slc.flags |= SLF_TOSS; break; default: error = EINVAL; break; } break; default: error = EINVAL; break; } if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); done: FREE(msg, M_NETGRAPH); return (error); } /* * Receive data */ static int ng_vjc_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { const node_p node = hook->node; const priv_p priv = (priv_p) node->private; int error = 0; if (hook == priv->ip) { /* outgoing packet */ u_int type; - if (!priv->conf.enabled) /* compression not enabled */ + if (!priv->conf.enableComp) /* compression not enabled */ type = TYPE_IP; else { struct ip *ip; - if ((m = pulluphdrs(m)) == NULL) + if ((m = ng_vjc_pulluphdrs(m)) == NULL) ERROUT(ENOBUFS); ip = mtod(m, struct ip *); type = (ip->ip_p == IPPROTO_TCP) ? sl_compress_tcp(m, ip, &priv->slc, priv->conf.compressCID) : TYPE_IP; } switch (type) { case TYPE_IP: hook = priv->vjip; break; case TYPE_UNCOMPRESSED_TCP: hook = priv->vjuncomp; break; case TYPE_COMPRESSED_TCP: hook = priv->vjcomp; break; default: panic(__FUNCTION__); } } else if (hook == priv->vjcomp) { /* incoming compressed packet */ int vjlen; u_int hlen; u_char *hdr; struct mbuf *mp; - /* Are we initialized? */ - if (!priv->conf.enabled) { + /* Are we decompressing? */ + if (!priv->conf.enableDecomp) { m_freem(m); m = NULL; ERROUT(ENETDOWN); } /* Uncompress packet to reconstruct TCP/IP header */ - if (!(m = m_pullup(m, MAX_VJHEADER))) + if (m->m_len < MAX_VJHEADER && !(m = m_pullup(m, MAX_VJHEADER))) ERROUT(ENOBUFS); vjlen = sl_uncompress_tcp_core(mtod(m, u_char *), m->m_len, m->m_pkthdr.len, TYPE_COMPRESSED_TCP, &priv->slc, &hdr, &hlen); if (vjlen <= 0) { m_freem(m); m = NULL; ERROUT(EINVAL); } /* Copy the reconstructed TCP/IP headers into a new mbuf */ MGETHDR(mp, M_DONTWAIT, MT_DATA); if (!mp) goto compfailmem; mp->m_len = 0; mp->m_next = NULL; if (hlen > MHLEN) { MCLGET(mp, M_DONTWAIT); if (M_TRAILINGSPACE(mp) < hlen) { m_freem(mp); /* can't get a cluster, drop */ compfailmem: m_freem(m); m = NULL; ERROUT(ENOBUFS); } } bcopy(hdr, mtod(mp, u_char *), hlen); mp->m_len = hlen; /* Stick header and rest of packet together */ m->m_data += vjlen; m->m_len -= vjlen; if (m->m_len <= M_TRAILINGSPACE(mp)) { bcopy(mtod(m, u_char *), mtod(mp, u_char *) + mp->m_len, m->m_len); mp->m_len += m->m_len; MFREE(m, mp->m_next); } else mp->m_next = m; m = mp; hook = priv->ip; } else if (hook == priv->vjuncomp) { /* incoming uncompressed pkt */ - u_int hlen; u_char *hdr; + u_int hlen; - /* Are we initialized? */ - if (!priv->conf.enabled) { + /* Are we decompressing? */ + if (!priv->conf.enableDecomp) { m_freem(m); m = NULL; ERROUT(ENETDOWN); } /* Run packet through uncompressor */ - if ((m = pulluphdrs(m)) == NULL) + if ((m = ng_vjc_pulluphdrs(m)) == NULL) ERROUT(ENOBUFS); if (sl_uncompress_tcp_core(mtod(m, u_char *), m->m_len, m->m_pkthdr.len, TYPE_UNCOMPRESSED_TCP, &priv->slc, &hdr, &hlen) < 0) { m_freem(m); m = NULL; ERROUT(EINVAL); } hook = priv->ip; } else if (hook == priv->vjip) /* incoming regular packet (bypass) */ hook = priv->ip; else panic(__FUNCTION__); done: if (m) NG_SEND_DATA(error, hook, m, meta); else NG_FREE_META(meta); return (error); } /* * Shutdown node */ static int ng_vjc_rmnode(node_p node) { const priv_p priv = (priv_p) node->private; node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); bzero(priv, sizeof(*priv)); FREE(priv, M_NETGRAPH); node->private = NULL; ng_unref(node); return (0); } /* * Hook disconnection */ static int ng_vjc_disconnect(hook_p hook) { if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } /************************************************************************ HELPER STUFF ************************************************************************/ /* - * Pull up the full IP and TCP headers of a packet. This is optimized - * for the common case of standard length headers. If packet is not + * Pull up the full IP and TCP headers of a packet. If packet is not * a TCP packet, just pull up the IP header. */ static struct mbuf * -pulluphdrs(struct mbuf *m) +ng_vjc_pulluphdrs(struct mbuf *m) { struct ip *ip; struct tcphdr *tcp; int ihlen, thlen; - if ((m = m_pullup(m, sizeof(*ip) + sizeof(*tcp))) == NULL) + if (m->m_len < sizeof(*ip) && !(m = m_pullup(m, sizeof(*ip)))) return (NULL); ip = mtod(m, struct ip *); if (ip->ip_p != IPPROTO_TCP) return (m); - if ((ihlen = (ip->ip_hl << 2)) != sizeof(*ip)) { + ihlen = ip->ip_hl << 2; + if (m->m_len < ihlen + sizeof(*tcp)) { if (!(m = m_pullup(m, ihlen + sizeof(*tcp)))) return (NULL); ip = mtod(m, struct ip *); } tcp = (struct tcphdr *) ((u_char *) ip + ihlen); - if ((thlen = (tcp->th_off << 2)) != sizeof(*tcp)) + thlen = tcp->th_off << 2; + if (m->m_len < ihlen + thlen) m = m_pullup(m, ihlen + thlen); return (m); } diff --git a/sys/netgraph/ng_vjc.h b/sys/netgraph/ng_vjc.h index 5067f6add843..5dc22974a3c5 100644 --- a/sys/netgraph/ng_vjc.h +++ b/sys/netgraph/ng_vjc.h @@ -1,75 +1,76 @@ /* * ng_vjc.h * * 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: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_vjc.h,v 1.6 1999/01/25 02:40:22 archie Exp $ */ #ifndef _NETGRAPH_VJC_H_ #define _NETGRAPH_VJC_H_ /* Node type name and magic cookie */ #define NG_VJC_NODE_TYPE "vjc" -#define NGM_VJC_COOKIE 868219207 +#define NGM_VJC_COOKIE 868219208 /* Hook names */ #define NG_VJC_HOOK_IP "ip" /* normal IP traffic */ #define NG_VJC_HOOK_VJCOMP "vjcomp" /* compressed TCP */ #define NG_VJC_HOOK_VJUNCOMP "vjuncomp" /* uncompressed TCP */ #define NG_VJC_HOOK_VJIP "vjip" /* uncompressed IP */ /* Minimum and maximum number of channels */ #define NG_VJC_MIN_CHANNELS 4 #define NG_VJC_MAX_CHANNELS 16 /* Configure struct */ struct ngm_vjc_config { - u_char enabled; /* Enable compression/decompression */ - u_char numChannels; /* Number of outgoing channels */ + u_char enableComp; /* Enable compression */ + u_char enableDecomp; /* Enable decompression */ + u_char numChannels; /* Number of compression channels */ u_char compressCID; /* OK to compress outgoing CID's */ }; /* Netgraph commands */ enum { - NGM_VJC_CONFIG, /* Supply a struct ngm_vjc_config */ + NGM_VJC_SET_CONFIG, /* Supply a struct ngm_vjc_config */ NGM_VJC_GET_STATE, /* Returns current struct slcompress */ NGM_VJC_CLR_STATS, /* Clears statistics counters */ NGM_VJC_RECV_ERROR, /* Indicate loss of incoming frame */ }; #endif /* _NETGRAPH_VJC_H_ */