Index: head/sys/netgraph/netgraph.h =================================================================== --- head/sys/netgraph/netgraph.h (revision 52751) +++ head/sys/netgraph/netgraph.h (revision 52752) @@ -1,258 +1,257 @@ /* * netgraph.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: Julian Elischer * * $FreeBSD$ - * $Whistle: netgraph.h,v 1.24 1999/01/28 23:54:52 julian Exp $ + * $Whistle: netgraph.h,v 1.29 1999/11/01 07:56:13 julian Exp $ */ #ifndef _NETGRAPH_NETGRAPH_H_ #define _NETGRAPH_NETGRAPH_H_ 1 #include #include #include #ifndef KERNEL #error "This file should not be included in user level programs" #endif /* * Structure of a hook */ struct ng_hook { char *name; /* what this node knows this link as */ void *private; /* node dependant ID for this hook */ int flags; /* info about this hook/link */ int refs; /* dont actually free this till 0 */ struct ng_hook *peer; /* the other end of this link */ struct ng_node *node; /* The node this hook is attached to */ LIST_ENTRY(ng_hook) hooks; /* linked list of all hooks on node */ }; typedef struct ng_hook *hook_p; /* Flags for a hook */ #define HK_INVALID 0x0001 /* don't trust it! */ /* * Structure of a node */ struct ng_node { char *name; /* optional globally unique name */ struct ng_type *type; /* the installed 'type' */ int flags; /* see below for bit definitions */ int sleepers; /* #procs sleeping on this node */ int refs; /* number of references to this node */ int numhooks; /* number of hooks */ int colour; /* for graph colouring algorithms */ void *private; /* node type dependant node ID */ ng_ID_t ID; /* Unique per node */ LIST_HEAD(hooks, ng_hook) hooks; /* linked list of node hooks */ LIST_ENTRY(ng_node) nodes; /* linked list of all nodes */ LIST_ENTRY(ng_node) idnodes; /* ID hash collision list */ }; typedef struct ng_node *node_p; /* Flags for a node */ #define NG_INVALID 0x001 /* free when all sleepers and refs go to 0 */ #define NG_BUSY 0x002 /* callers should sleep or wait */ #define NG_TOUCHED 0x004 /* to avoid cycles when 'flooding' */ #define NGF_TYPE1 0x10000000 /* reserved for type specific storage */ #define NGF_TYPE2 0x20000000 /* reserved for type specific storage */ #define NGF_TYPE3 0x40000000 /* reserved for type specific storage */ #define NGF_TYPE4 0x80000000 /* reserved for type specific storage */ /* * The structure that holds meta_data about a data packet (e.g. priority) * Nodes might add or subtract options as needed if there is room. * They might reallocate the struct to make more room if they need to. * Meta-data is still experimental. */ struct meta_field_header { u_long cookie; /* cookie for the field. Skip fields you don't * know about (same cookie as in messgaes) */ u_short type; /* field ID */ u_short len; /* total len of this field including extra * data */ char data[0]; /* data starts here */ }; /* To zero out an option 'in place' set it's cookie to this */ #define INVALID_COOKIE 865455152 /* This part of the metadata is always present if the pointer is non NULL */ struct ng_meta { char priority; /* -ve is less priority, 0 is default */ char discardability; /* higher is less valuable.. discard first */ u_short allocated_len; /* amount malloc'd */ u_short used_len; /* sum of all fields, options etc. */ u_short flags; /* see below.. generic flags */ struct meta_field_header options[0]; /* add as (if) needed */ }; typedef struct ng_meta *meta_p; /* Flags for meta-data */ #define NGMF_TEST 0x01 /* discard at the last moment before sending */ #define NGMF_TRACE 0x02 /* trace when handing this data to a node */ +/* node method definitions */ +typedef int ng_constructor_t(node_p *node); +typedef int ng_rcvmsg_t(node_p node, struct ng_mesg *msg, + const char *retaddr, struct ng_mesg **resp); +typedef int ng_shutdown_t(node_p node); +typedef int ng_newhook_t(node_p node, hook_p hook, const char *name); +typedef hook_p ng_findhook_t(node_p node, const char *name); +typedef int ng_connect_t(hook_p hook); +typedef int ng_rcvdata_t(hook_p hook, struct mbuf *m, meta_p meta); +typedef int ng_disconnect_t(hook_p hook); + /* * Structure of a node type */ struct ng_type { - /* Netgraph version number (must equal NG_VERSION) */ - u_int32_t version; + u_int32_t version; /* must equal NG_VERSION */ + const char *name; /* Unique type name */ + modeventhand_t mod_event; /* Module event handler (optional) */ + ng_constructor_t *constructor; /* Node constructor */ + ng_rcvmsg_t *rcvmsg; /* control messages come here */ + ng_shutdown_t *shutdown; /* reset, and free resources */ + ng_newhook_t *newhook; /* first notification of new hook */ + ng_findhook_t *findhook; /* only if you have 23000 hooks */ + ng_connect_t *connect; /* final notification of new hook */ + ng_rcvdata_t *rcvdata; /* date comes here */ + ng_rcvdata_t *rcvdataq; /* or here if been queued */ + ng_disconnect_t *disconnect; /* notify on disconnect */ - /* Unique type name */ - const char *name; - - /* Module event handler (optional) */ - modeventhand_t mod_event; - - /* Node constructor */ - int (*constructor)(node_p *node); - - /* Calls using the node */ - int (*rcvmsg)(node_p node, struct ng_mesg *msg, - const char *retaddr, struct ng_mesg **resp); - int (*shutdown)(node_p node); - int (*newhook)(node_p node, hook_p hook, const char *name); - hook_p (*findhook)(node_p node, const char *name); - - /* Calls using the hook */ - int (*connect)(hook_p hook); /* already linked in */ - int (*rcvdata)(hook_p hook, struct mbuf *m, meta_p meta); - int (*rcvdataq)(hook_p hook, struct mbuf *m, meta_p meta); - int (*disconnect)(hook_p hook); /* notify on disconnect */ - - /* These are private to the base netgraph code */ + /* R/W data private to the base netgraph code DON'T TOUCH!*/ LIST_ENTRY(ng_type) types; /* linked list of all types */ int refs; /* number of instances */ }; /* Send data packet with meta-data */ #define NG_SEND_DATA(error, hook, m, a) \ do { \ (error) = ng_send_data((hook), (m), (a)); \ (m) = NULL; \ (a) = NULL; \ } while (0) /* Send queued data packet with meta-data */ #define NG_SEND_DATAQ(error, hook, m, a) \ do { \ (error) = ng_send_dataq((hook), (m), (a)); \ (m) = NULL; \ (a) = NULL; \ } while (0) /* Free metadata */ #define NG_FREE_META(a) \ do { \ if ((a)) { \ FREE((a), M_NETGRAPH); \ a = NULL; \ } \ } while (0) /* Free any data packet and/or meta-data */ #define NG_FREE_DATA(m, a) \ do { \ if ((m)) { \ m_freem((m)); \ m = NULL; \ } \ NG_FREE_META((a)); \ } while (0) /* * Use the NETGRAPH_INIT() macro to link a node type into the * netgraph system. This works for types compiled into the kernel * as well as KLD modules. The first argument should be the type * name (eg, echo) and the second a pointer to the type struct. * * If a different link time is desired, e.g., a device driver that * needs to install its netgraph type before probing, use the * NETGRAPH_INIT_ORDERED() macro instead. Deivce drivers probably * want to use SI_SUB_DRIVERS instead of SI_SUB_PSEUDO. */ #define NETGRAPH_INIT_ORDERED(typename, typestructp, sub, order) \ static moduledata_t ng_##typename##_mod = { \ "ng_" #typename, \ ng_mod_event, \ (typestructp) \ }; \ DECLARE_MODULE(ng_##typename, ng_##typename##_mod, sub, order) #define NETGRAPH_INIT(tn, tp) \ NETGRAPH_INIT_ORDERED(tn, tp, SI_SUB_PSEUDO, SI_ORDER_ANY) /* Special malloc() type for netgraph structs and ctrl messages */ MALLOC_DECLARE(M_NETGRAPH); int ng_bypass(hook_p hook1, hook_p hook2); void ng_cutlinks(node_p node); int ng_con_nodes(node_p node, const char *name, node_p node2, const char *name2); void ng_destroy_hook(hook_p hook); node_p ng_findname(node_p node, const char *name); struct ng_type *ng_findtype(const char *type); int ng_make_node(const char *type, node_p *nodepp); int ng_make_node_common(struct ng_type *typep, node_p *nodep); int ng_mkpeer(node_p node, const char *name, const char *name2, char *type); int ng_mod_event(module_t mod, int what, void *arg); int ng_name_node(node_p node, const char *name); int ng_newtype(struct ng_type *tp); ng_ID_t ng_node2ID(node_p node); int ng_path2node(node_p here, const char *path, node_p *dest, char **rtnp); int ng_path_parse(char *addr, char **node, char **path, char **hook); int ng_queue_data(hook_p hook, struct mbuf *m, meta_p meta); int ng_queue_msg(node_p here, struct ng_mesg *msg, int len, const char *address); void ng_release_node(node_p node); void ng_rmnode(node_p node); int ng_send_data(hook_p hook, struct mbuf *m, meta_p meta); int ng_send_dataq(hook_p hook, struct mbuf *m, meta_p meta); int ng_send_msg(node_p here, struct ng_mesg *msg, const char *address, struct ng_mesg **resp); void ng_unname(node_p node); void ng_unref(node_p node); int ng_wait_node(node_p node, char *msg); #endif /* _NETGRAPH_NETGRAPH_H_ */ Index: head/sys/netgraph/ng_UI.c =================================================================== --- head/sys/netgraph/ng_UI.c (revision 52751) +++ head/sys/netgraph/ng_UI.c (revision 52752) @@ -1,243 +1,242 @@ /* * 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 $ + * $Whistle: ng_UI.c,v 1.14 1999/11/01 09:24:51 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); +static ng_constructor_t ng_UI_constructor; +static ng_rcvmsg_t ng_UI_rcvmsg; +static ng_shutdown_t ng_UI_rmnode; +static ng_newhook_t ng_UI_newhook; +static ng_rcvdata_t ng_UI_rcvdata; +static ng_disconnect_t ng_UI_disconnect; /* 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_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); } Index: head/sys/netgraph/ng_async.c =================================================================== --- head/sys/netgraph/ng_async.c (revision 52751) +++ head/sys/netgraph/ng_async.c (revision 52752) @@ -1,586 +1,585 @@ /* * ng_async.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_async.c,v 1.15 1999/01/28 23:54:52 julian Exp $ + * $Whistle: ng_async.c,v 1.17 1999/11/01 09:24:51 julian Exp $ */ /* * This node type implements a PPP style sync <-> async converter. * See RFC 1661 for details of how asynchronous encoding works. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Optimize opening and closing flags into one? Set to max # seconds delay */ #define SYNC_OPT_TIME 1 /* one second maximum */ /* Async decode state */ #define MODE_HUNT 0 #define MODE_NORMAL 1 #define MODE_ESC 2 /* Private data structure */ struct private { node_p node; /* Our node */ hook_p async; /* Asynchronous side */ hook_p sync; /* Synchronous side */ hook_p sync2; /* Synchronous side, full escapes */ u_char amode; /* Async hunt/esape mode */ u_int16_t fcs; /* Decoded async FCS (so far) */ u_char *abuf; /* Buffer to encode sync into */ u_char *sbuf; /* Buffer to decode async into */ u_int slen; /* Length of data in sbuf */ #if SYNC_OPT_TIME long lasttime; /* Time of last async packet sent */ #endif struct ng_async_cfg cfg; /* Configuration */ struct ng_async_stat stats; /* Statistics */ }; typedef struct private *sc_p; /* Useful macros */ #define ASYNC_BUF_SIZE(smru) (2 * (smru) + 10) #define SYNC_BUF_SIZE(amru) ((amru) + 10) #define ERROUT(x) do { error = (x); goto done; } while (0) /* Netgraph methods */ -static int nga_constructor(node_p *node); -static int nga_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); -static int nga_rcvmsg(node_p node, struct ng_mesg *msg, - const char *rtn, struct ng_mesg **resp); -static int nga_shutdown(node_p node); -static int nga_newhook(node_p node, hook_p hook, const char *name); -static int nga_disconnect(hook_p hook); +static ng_constructor_t nga_constructor; +static ng_rcvdata_t nga_rcvdata; +static ng_rcvmsg_t nga_rcvmsg; +static ng_shutdown_t nga_shutdown; +static ng_newhook_t nga_newhook; +static ng_disconnect_t nga_disconnect; /* Helper stuff */ static int nga_rcv_sync(const sc_p sc, struct mbuf *m, meta_p meta); static int nga_rcv_async(const sc_p sc, struct mbuf *m, meta_p meta); /* Define the netgraph node type */ static struct ng_type typestruct = { NG_VERSION, NG_ASYNC_NODE_TYPE, NULL, nga_constructor, nga_rcvmsg, nga_shutdown, nga_newhook, NULL, NULL, nga_rcvdata, nga_rcvdata, nga_disconnect }; NETGRAPH_INIT(async, &typestruct); /* CRC table */ static const u_int16_t fcstab[]; /****************************************************************** NETGRAPH NODE METHODS ******************************************************************/ /* * Initialize a new node */ static int nga_constructor(node_p *nodep) { sc_p sc; int error; if ((error = ng_make_node_common(&typestruct, nodep))) return (error); MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_WAITOK); if (sc == NULL) return (ENOMEM); bzero(sc, sizeof(*sc)); sc->amode = MODE_HUNT; sc->cfg.accm = ~0; sc->cfg.amru = NG_ASYNC_DEFAULT_MRU; sc->cfg.smru = NG_ASYNC_DEFAULT_MRU; MALLOC(sc->abuf, u_char *, ASYNC_BUF_SIZE(sc->cfg.smru), M_NETGRAPH, M_WAITOK); if (sc->abuf == NULL) goto fail; MALLOC(sc->sbuf, u_char *, SYNC_BUF_SIZE(sc->cfg.amru), M_NETGRAPH, M_WAITOK); if (sc->sbuf == NULL) { FREE(sc->abuf, M_NETGRAPH); fail: FREE(sc, M_NETGRAPH); return (ENOMEM); } (*nodep)->private = sc; sc->node = *nodep; return (0); } /* * Reserve a hook for a pending connection */ static int nga_newhook(node_p node, hook_p hook, const char *name) { const sc_p sc = node->private; hook_p *hookp; if (!strcmp(name, NG_ASYNC_HOOK_ASYNC)) hookp = &sc->async; else if (!strcmp(name, NG_ASYNC_HOOK_SYNC)) hookp = &sc->sync; else if (!strcmp(name, NG_ASYNC_HOOK_SYNC2)) hookp = &sc->sync2; else return (EINVAL); if (*hookp) return (EISCONN); *hookp = hook; return (0); } /* * Receive incoming data */ static int nga_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { const sc_p sc = hook->node->private; if (hook == sc->sync) return (nga_rcv_sync(sc, m, meta)); else if (hook == sc->sync2) { const u_char acfcompSave = sc->cfg.acfcomp; const u_int32_t accmSave = sc->cfg.accm; int rtn; sc->cfg.acfcomp = 0; sc->cfg.accm = ~0; rtn = nga_rcv_sync(sc, m, meta); sc->cfg.acfcomp = acfcompSave; sc->cfg.accm = accmSave; return (rtn); } else if (hook == sc->async) return (nga_rcv_async(sc, m, meta)); panic(__FUNCTION__); } /* * Receive incoming control message */ static int nga_rcvmsg(node_p node, struct ng_mesg *msg, const char *rtn, struct ng_mesg **rptr) { const sc_p sc = (sc_p) node->private; struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_ASYNC_COOKIE: switch (msg->header.cmd) { case NGM_ASYNC_CMD_GET_STATS: NG_MKRESPONSE(resp, msg, sizeof(sc->stats), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); *((struct ng_async_stat *) resp->data) = sc->stats; break; case NGM_ASYNC_CMD_CLR_STATS: bzero(&sc->stats, sizeof(sc->stats)); break; case NGM_ASYNC_CMD_SET_CONFIG: { struct ng_async_cfg *const cfg = (struct ng_async_cfg *) msg->data; u_char *buf; if (msg->header.arglen != sizeof(*cfg)) ERROUT(EINVAL); if (cfg->amru < NG_ASYNC_MIN_MRU || cfg->amru > NG_ASYNC_MAX_MRU || cfg->smru < NG_ASYNC_MIN_MRU || cfg->smru > NG_ASYNC_MAX_MRU) ERROUT(EINVAL); cfg->enabled = !!cfg->enabled; /* normalize */ cfg->acfcomp = !!cfg->acfcomp; /* normalize */ if (cfg->smru > sc->cfg.smru) { /* reallocate buffer */ MALLOC(buf, u_char *, ASYNC_BUF_SIZE(cfg->smru), M_NETGRAPH, M_NOWAIT); if (!buf) ERROUT(ENOMEM); FREE(sc->abuf, M_NETGRAPH); sc->abuf = buf; } if (cfg->amru > sc->cfg.amru) { /* reallocate buffer */ MALLOC(buf, u_char *, SYNC_BUF_SIZE(cfg->amru), M_NETGRAPH, M_NOWAIT); if (!buf) ERROUT(ENOMEM); FREE(sc->sbuf, M_NETGRAPH); sc->sbuf = buf; sc->amode = MODE_HUNT; sc->slen = 0; } if (!cfg->enabled) { sc->amode = MODE_HUNT; sc->slen = 0; } sc->cfg = *cfg; break; } case NGM_ASYNC_CMD_GET_CONFIG: NG_MKRESPONSE(resp, msg, sizeof(sc->cfg), M_NOWAIT); if (!resp) ERROUT(ENOMEM); *((struct ng_async_cfg *) resp->data) = sc->cfg; break; default: ERROUT(EINVAL); } break; default: ERROUT(EINVAL); } if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); done: FREE(msg, M_NETGRAPH); return (error); } /* * Shutdown this node */ static int nga_shutdown(node_p node) { const sc_p sc = node->private; ng_cutlinks(node); ng_unname(node); FREE(sc->abuf, M_NETGRAPH); FREE(sc->sbuf, M_NETGRAPH); bzero(sc, sizeof(*sc)); FREE(sc, M_NETGRAPH); node->private = NULL; ng_unref(node); return (0); } /* * Lose a hook. When both hooks go away, we disappear. */ static int nga_disconnect(hook_p hook) { const sc_p sc = hook->node->private; hook_p *hookp; if (hook == sc->async) hookp = &sc->async; else if (hook == sc->sync) hookp = &sc->sync; else if (hook == sc->sync2) hookp = &sc->sync2; else panic(__FUNCTION__); if (!*hookp) panic(__FUNCTION__ "2"); *hookp = NULL; bzero(&sc->stats, sizeof(sc->stats)); #if SYNC_OPT_TIME sc->lasttime = 0; #endif if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } /****************************************************************** INTERNAL HELPER STUFF ******************************************************************/ /* * Encode a byte into the async buffer */ static __inline__ void nga_async_add(const sc_p sc, u_int16_t *fcs, u_int32_t accm, int *len, u_char x) { *fcs = PPP_FCS(*fcs, x); if ((x < 32 && ((1 << x) & accm)) || (x == PPP_ESCAPE) || (x == PPP_FLAG)) { sc->abuf[(*len)++] = PPP_ESCAPE; x ^= PPP_TRANS; } sc->abuf[(*len)++] = x; } /* * Receive incoming synchronous data. Any "meta" information means * for us to apply full ACCM to this frame. */ static int nga_rcv_sync(const sc_p sc, struct mbuf *m, meta_p meta) { struct ifnet *const rcvif = m->m_pkthdr.rcvif; u_int16_t fcs, fcs0; int alen, error = 0; #define ADD_BYTE(x) \ nga_async_add(sc, &fcs, meta ? ~0 : sc->cfg.accm, &alen, (x)) if (!sc->cfg.enabled) { NG_SEND_DATA(error, sc->async, m, meta); return (error); } if (m->m_pkthdr.len > sc->cfg.smru) { sc->stats.syncOverflows++; NG_FREE_DATA(m, meta); return (EMSGSIZE); } sc->stats.syncFrames++; sc->stats.syncOctets += m->m_pkthdr.len; /* Initialize async encoded version of input mbuf */ alen = 0; fcs = PPP_INITFCS; /* Add beginning sync flag if it's been long enough to need one */ #if SYNC_OPT_TIME { struct timeval time; getmicrotime(&time); if (time.tv_sec >= sc->lasttime + SYNC_OPT_TIME) { sc->abuf[alen++] = PPP_FLAG; sc->lasttime = time.tv_sec; } } #else sc->abuf[alen++] = PPP_FLAG; #endif /* Add option address and control fields, then packet payload */ if (!sc->cfg.acfcomp || meta) { ADD_BYTE(PPP_ALLSTATIONS); ADD_BYTE(PPP_UI); } while (m) { struct mbuf *n; while (m->m_len > 0) { u_char const ch = *mtod(m, u_char *); ADD_BYTE(ch); m->m_data++; m->m_len--; } MFREE(m, n); m = n; } /* Add checksum and final sync flag */ fcs0 = fcs; ADD_BYTE(~fcs0 & 0xff); ADD_BYTE(~fcs0 >> 8); sc->abuf[alen++] = PPP_FLAG; /* Put frame in an mbuf and ship it off */ NG_FREE_META(meta); if (!(m = m_devget(sc->abuf, alen, 0, rcvif, NULL))) error = ENOBUFS; else NG_SEND_DATA(error, sc->async, m, meta); return (error); } /* * Receive incoming asynchronous data * XXX technically, we should strip out supposedly escaped characters */ static int nga_rcv_async(const sc_p sc, struct mbuf * m, meta_p meta) { struct ifnet *const rcvif = m->m_pkthdr.rcvif; int error; if (!sc->cfg.enabled) { NG_SEND_DATA(error, sc->sync, m, meta); return (error); } NG_FREE_META(meta); while (m) { struct mbuf *n; for (; m->m_len > 0; m->m_data++, m->m_len--) { u_char ch = *mtod(m, u_char *); sc->stats.asyncOctets++; if (ch == PPP_FLAG) { /* Flag overrides everything */ int skip = 0; /* Check for runts */ if (sc->slen < 2) { if (sc->slen > 0) sc->stats.asyncRunts++; goto reset; } /* Verify CRC */ if (sc->fcs != PPP_GOODFCS) { sc->stats.asyncBadCheckSums++; goto reset; } sc->slen -= 2; /* Strip address and control fields */ if (sc->slen >= 2 && sc->sbuf[0] == PPP_ALLSTATIONS && sc->sbuf[1] == PPP_UI) skip = 2; /* Check for frame too big */ if (sc->slen - skip > sc->cfg.amru) { sc->stats.asyncOverflows++; goto reset; } /* OK, ship it out */ if ((n = m_devget(sc->sbuf + skip, sc->slen - skip, 0, rcvif, NULL))) NG_SEND_DATA(error, sc->sync, n, meta); sc->stats.asyncFrames++; reset: sc->amode = MODE_NORMAL; sc->fcs = PPP_INITFCS; sc->slen = 0; continue; } switch (sc->amode) { case MODE_NORMAL: if (ch == PPP_ESCAPE) { sc->amode = MODE_ESC; continue; } break; case MODE_ESC: ch ^= PPP_TRANS; sc->amode = MODE_NORMAL; break; case MODE_HUNT: default: continue; } /* Add byte to frame */ if (sc->slen >= SYNC_BUF_SIZE(sc->cfg.amru)) { sc->stats.asyncOverflows++; sc->amode = MODE_HUNT; sc->slen = 0; } else { sc->sbuf[sc->slen++] = ch; sc->fcs = PPP_FCS(sc->fcs, ch); } } MFREE(m, n); m = n; } return (0); } /* * CRC table * * Taken from RFC 1171 Appendix B */ static const u_int16_t fcstab[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; Index: head/sys/netgraph/ng_cisco.c =================================================================== --- head/sys/netgraph/ng_cisco.c (revision 52751) +++ head/sys/netgraph/ng_cisco.c (revision 52752) @@ -1,557 +1,555 @@ /* * ng_cisco.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_cisco.c,v 1.23 1999/01/28 23:54:53 julian Exp $ + * $Whistle: ng_cisco.c,v 1.25 1999/11/01 09:24:51 julian Exp $ */ #include "opt_inet.h" #include "opt_atalk.h" #include "opt_ipx.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CISCO_MULTICAST 0x8f /* Cisco multicast address */ #define CISCO_UNICAST 0x0f /* Cisco unicast address */ #define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */ #define CISCO_ADDR_REQ 0 /* Cisco address request */ #define CISCO_ADDR_REPLY 1 /* Cisco address reply */ #define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */ #define KEEPALIVE_SECS 10 struct cisco_header { u_char address; u_char control; u_short protocol; }; #define CISCO_HEADER_LEN sizeof (struct cisco_header) struct cisco_packet { u_long type; u_long par1; u_long par2; u_short rel; u_short time0; u_short time1; }; #define CISCO_PACKET_LEN (sizeof(struct cisco_packet)) struct protoent { hook_p hook; /* the hook for this proto */ u_short af; /* address family, -1 = downstream */ }; struct cisco_priv { u_long local_seq; u_long remote_seq; u_long seq_retries; /* how many times we've been here throwing out * the same sequence number without ack */ node_p node; struct callout_handle handle; struct protoent downstream; struct protoent inet; /* IP information */ struct in_addr localip; struct in_addr localmask; struct protoent atalk; /* AppleTalk information */ struct protoent ipx; /* IPX information */ }; typedef struct cisco_priv *sc_p; /* Netgraph methods */ -static int cisco_constructor(node_p *node); -static int cisco_rcvmsg(node_p node, struct ng_mesg *msg, - const char *retaddr, struct ng_mesg **resp); -static int cisco_rmnode(node_p node); -static int cisco_newhook(node_p node, hook_p hook, const char *name); - -static int cisco_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); -static int cisco_disconnect(hook_p hook); +static ng_constructor_t cisco_constructor; +static ng_rcvmsg_t cisco_rcvmsg; +static ng_shutdown_t cisco_rmnode; +static ng_newhook_t cisco_newhook; +static ng_rcvdata_t cisco_rcvdata; +static ng_disconnect_t cisco_disconnect; /* Other functions */ static int cisco_input(sc_p sc, struct mbuf *m, meta_p meta); static void cisco_keepalive(void *arg); static int cisco_send(sc_p sc, int type, long par1, long par2); /* Node type */ static struct ng_type typestruct = { NG_VERSION, NG_CISCO_NODE_TYPE, NULL, cisco_constructor, cisco_rcvmsg, cisco_rmnode, cisco_newhook, NULL, NULL, cisco_rcvdata, cisco_rcvdata, cisco_disconnect }; NETGRAPH_INIT(cisco, &typestruct); /* * Node constructor */ static int cisco_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(struct cisco_priv)); callout_handle_init(&sc->handle); if ((error = ng_make_node_common(&typestruct, nodep))) { FREE(sc, M_NETGRAPH); return (error); } (*nodep)->private = sc; sc->node = *nodep; /* Initialise the varous protocol hook holders */ sc->downstream.af = 0xffff; sc->inet.af = AF_INET; sc->atalk.af = AF_APPLETALK; sc->ipx.af = AF_IPX; return (0); } /* * Check new hook */ static int cisco_newhook(node_p node, hook_p hook, const char *name) { const sc_p sc = node->private; if (strcmp(name, NG_CISCO_HOOK_DOWNSTREAM) == 0) { sc->downstream.hook = hook; hook->private = &sc->downstream; /* Start keepalives */ sc->handle = timeout(cisco_keepalive, sc, hz * KEEPALIVE_SECS); } else if (strcmp(name, NG_CISCO_HOOK_INET) == 0) { sc->inet.hook = hook; hook->private = &sc->inet; } else if (strcmp(name, NG_CISCO_HOOK_APPLETALK) == 0) { sc->atalk.hook = hook; hook->private = &sc->atalk; } else if (strcmp(name, NG_CISCO_HOOK_IPX) == 0) { sc->ipx.hook = hook; hook->private = &sc->ipx; } else if (strcmp(name, NG_CISCO_HOOK_DEBUG) == 0) { hook->private = NULL; /* unimplemented */ } else return (EINVAL); return 0; } /* * Receive control message. */ static int cisco_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr) { const sc_p sc = node->private; struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_GENERIC_COOKIE: switch (msg->header.cmd) { case NGM_TEXT_STATUS: { char *arg; int pos; NG_MKRESPONSE(resp, msg, sizeof(struct ng_mesg) + NG_TEXTRESPONSE, M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } arg = (char *) resp->data; pos = sprintf(arg, "keepalive period: %d sec; ", KEEPALIVE_SECS); pos += sprintf(arg + pos, "unacknowledged keepalives: %ld", sc->seq_retries); resp->header.arglen = pos + 1; break; } default: error = EINVAL; break; } break; case NGM_CISCO_COOKIE: switch (msg->header.cmd) { case NGM_CISCO_GET_IPADDR: /* could be a late reply! */ if ((msg->header.flags & NGF_RESP) == 0) { struct in_addr *ips; NG_MKRESPONSE(resp, msg, 2 * sizeof(*ips), M_NOWAIT); if (!resp) { error = ENOMEM; break; } ips = (struct in_addr *) resp->data; ips[0] = sc->localip; ips[1] = sc->localmask; break; } /* FALLTHROUGH */ /* ...if it's a reply */ case NGM_CISCO_SET_IPADDR: { struct in_addr *const ips = (struct in_addr *)msg->data; if (msg->header.arglen < 2 * sizeof(*ips)) { error = EINVAL; break; } sc->localip = ips[0]; sc->localmask = ips[1]; break; } case NGM_CISCO_GET_STATUS: { struct ngciscostat *stat; NG_MKRESPONSE(resp, msg, sizeof(*stat), M_NOWAIT); if (!resp) { error = ENOMEM; break; } stat = (struct ngciscostat *) resp->data; stat->seq_retries = sc->seq_retries; stat->keepalive_period = KEEPALIVE_SECS; break; } default: error = EINVAL; break; } break; default: error = EINVAL; break; } if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); FREE(msg, M_NETGRAPH); return (error); } /* * Receive data */ static int cisco_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { const sc_p sc = hook->node->private; struct protoent *pep; struct cisco_header *h; int error = 0; if ((pep = hook->private) == NULL) goto out; /* If it came from our downlink, deal with it separately */ if (pep->af == 0xffff) return (cisco_input(sc, m, meta)); /* OK so it came from a protocol, heading out. Prepend general data packet header. For now, IP,IPX only */ M_PREPEND(m, CISCO_HEADER_LEN, M_DONTWAIT); if (!m) { error = ENOBUFS; goto out; } h = mtod(m, struct cisco_header *); h->address = CISCO_MULTICAST; /* broadcast address */ h->control = 0; switch (pep->af) { case AF_INET: /* Internet Protocol */ h->protocol = htons(ETHERTYPE_IP); break; case AF_APPLETALK: /* AppleTalk Protocol */ h->protocol = htons(ETHERTYPE_AT); break; case AF_IPX: /* Novell IPX Protocol */ h->protocol = htons(ETHERTYPE_IPX); break; default: error = EAFNOSUPPORT; goto out; } /* Send it */ NG_SEND_DATA(error, sc->downstream.hook, m, meta); return (error); out: NG_FREE_DATA(m, meta); return (error); } /* * Shutdown node */ static int cisco_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); } /* * Disconnection of a hook * * For this type, removal of the last link destroys the node */ static int cisco_disconnect(hook_p hook) { const sc_p sc = hook->node->private; struct protoent *pep; /* Check it's not the debug hook */ if ((pep = hook->private)) { pep->hook = NULL; if (pep->af == 0xffff) { /* If it is the downstream hook, stop the timers */ untimeout(cisco_keepalive, sc, sc->handle); } } /* If no more hooks, remove the node */ if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } /* * Receive data */ static int cisco_input(sc_p sc, struct mbuf *m, meta_p meta) { struct cisco_header *h; struct cisco_packet *p; struct protoent *pep; int error = 0; if (m->m_pkthdr.len <= CISCO_HEADER_LEN) goto drop; /* Strip off cisco header */ h = mtod(m, struct cisco_header *); m_adj(m, CISCO_HEADER_LEN); switch (h->address) { default: /* Invalid Cisco packet. */ goto drop; case CISCO_UNICAST: case CISCO_MULTICAST: /* Don't check the control field here (RFC 1547). */ switch (ntohs(h->protocol)) { default: goto drop; case CISCO_KEEPALIVE: p = mtod(m, struct cisco_packet *); switch (ntohl(p->type)) { default: log(LOG_WARNING, "cisco: unknown cisco packet type: 0x%lx\n", ntohl(p->type)); break; case CISCO_ADDR_REPLY: /* Reply on address request, ignore */ break; case CISCO_KEEPALIVE_REQ: sc->remote_seq = ntohl(p->par1); if (sc->local_seq == ntohl(p->par2)) { sc->local_seq++; sc->seq_retries = 0; } break; case CISCO_ADDR_REQ: { struct ng_mesg *msg, *resp; /* Ask inet peer for IP address information */ if (sc->inet.hook == NULL) goto nomsg; NG_MKMESSAGE(msg, NGM_CISCO_COOKIE, NGM_CISCO_GET_IPADDR, 0, M_NOWAIT); if (msg == NULL) goto nomsg; ng_send_msg(sc->node, msg, NG_CISCO_HOOK_INET, &resp); if (resp != NULL) cisco_rcvmsg(sc->node, resp, ".", NULL); nomsg: /* Send reply to peer device */ error = cisco_send(sc, CISCO_ADDR_REPLY, ntohl(sc->localip.s_addr), ntohl(sc->localmask.s_addr)); break; } } goto drop; case ETHERTYPE_IP: pep = &sc->inet; break; case ETHERTYPE_AT: pep = &sc->atalk; break; case ETHERTYPE_IPX: pep = &sc->ipx; break; } break; } /* Send it on */ if (pep->hook == NULL) goto drop; NG_SEND_DATA(error, pep->hook, m, meta); return (error); drop: NG_FREE_DATA(m, meta); return (error); } /* * Send keepalive packets, every 10 seconds. */ static void cisco_keepalive(void *arg) { const sc_p sc = arg; int s = splimp(); cisco_send(sc, CISCO_KEEPALIVE_REQ, sc->local_seq, sc->remote_seq); sc->seq_retries++; splx(s); sc->handle = timeout(cisco_keepalive, sc, hz * KEEPALIVE_SECS); } /* * Send Cisco keepalive packet. */ static int cisco_send(sc_p sc, int type, long par1, long par2) { struct cisco_header *h; struct cisco_packet *ch; struct mbuf *m; u_long t; int error = 0; meta_p meta = NULL; struct timeval time; getmicrotime(&time); MGETHDR(m, M_DONTWAIT, MT_DATA); if (!m) return (ENOBUFS); t = (time.tv_sec - boottime.tv_sec) * 1000; m->m_pkthdr.len = m->m_len = CISCO_HEADER_LEN + CISCO_PACKET_LEN; m->m_pkthdr.rcvif = 0; h = mtod(m, struct cisco_header *); h->address = CISCO_MULTICAST; h->control = 0; h->protocol = htons(CISCO_KEEPALIVE); ch = (struct cisco_packet *) (h + 1); ch->type = htonl(type); ch->par1 = htonl(par1); ch->par2 = htonl(par2); ch->rel = -1; ch->time0 = htons((u_short) (t >> 16)); ch->time1 = htons((u_short) t); NG_SEND_DATA(error, sc->downstream.hook, m, meta); return (error); } Index: head/sys/netgraph/ng_echo.c =================================================================== --- head/sys/netgraph/ng_echo.c (revision 52751) +++ head/sys/netgraph/ng_echo.c (revision 52752) @@ -1,121 +1,120 @@ /* * ng_echo.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_echo.c,v 1.11 1999/01/28 23:54:53 julian Exp $ + * $Whistle: ng_echo.c,v 1.13 1999/11/01 09:24:51 julian Exp $ */ /* * Netgraph "echo" node * * This node simply bounces data and messages back to whence they came. */ #include #include #include #include #include #include #include #include #include #include /* Netgraph methods */ -static int nge_rcvmsg(node_p node, struct ng_mesg *msg, - const char *retaddr, struct ng_mesg ** resp); -static int nge_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); -static int nge_disconnect(hook_p hook); +static ng_rcvmsg_t nge_rcvmsg; +static ng_rcvdata_t nge_rcvdata; +static ng_disconnect_t nge_disconnect; /* Netgraph type */ static struct ng_type typestruct = { NG_VERSION, NG_ECHO_NODE_TYPE, NULL, NULL, nge_rcvmsg, NULL, NULL, NULL, NULL, nge_rcvdata, nge_rcvdata, nge_disconnect }; NETGRAPH_INIT(echo, &typestruct); /* * Receive control message. We just bounce it back as a reply. */ static int nge_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr) { if (rptr) { msg->header.flags |= NGF_RESP; *rptr = msg; } else { FREE(msg, M_NETGRAPH); } return (0); } /* * Receive data */ static int nge_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { int error = 0; NG_SEND_DATA(error, hook, m, meta); return (error); } /* * Removal of the last link destroys the nodeo */ static int nge_disconnect(hook_p hook) { if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } Index: head/sys/netgraph/ng_frame_relay.c =================================================================== --- head/sys/netgraph/ng_frame_relay.c (revision 52751) +++ head/sys/netgraph/ng_frame_relay.c (revision 52752) @@ -1,525 +1,525 @@ /* * 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 $ + * $Whistle: ng_frame_relay.c,v 1.20 1999/11/01 09:24:51 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? */ 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); +static ng_constructor_t ngfrm_constructor; +static ng_shutdown_t ngfrm_rmnode; +static ng_newhook_t ngfrm_newhook; +static ng_rcvdata_t ngfrm_rcvdata; +static ng_disconnect_t ngfrm_disconnect; /* 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_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); } Index: head/sys/netgraph/ng_hole.c =================================================================== --- head/sys/netgraph/ng_hole.c (revision 52751) +++ head/sys/netgraph/ng_hole.c (revision 52752) @@ -1,97 +1,97 @@ /* * ng_hole.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_hole.c,v 1.8 1999/01/28 23:54:53 julian Exp $ + * $Whistle: ng_hole.c,v 1.10 1999/11/01 09:24:51 julian Exp $ */ /* * This node is a 'black hole' that simply discards everything it receives */ #include #include #include #include #include #include #include #include #include #include /* Netgraph methods */ -static int ngh_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); -static int ngh_disconnect(hook_p hook); +static ng_rcvdata_t ngh_rcvdata; +static ng_disconnect_t ngh_disconnect; static struct ng_type typestruct = { NG_VERSION, NG_HOLE_NODE_TYPE, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ngh_rcvdata, ngh_rcvdata, ngh_disconnect }; NETGRAPH_INIT(hole, &typestruct); /* * Receive data */ static int ngh_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { NG_FREE_DATA(m, meta); return 0; } /* * Hook disconnection */ static int ngh_disconnect(hook_p hook) { if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } Index: head/sys/netgraph/ng_iface.c =================================================================== --- head/sys/netgraph/ng_iface.c (revision 52751) +++ head/sys/netgraph/ng_iface.c (revision 52752) @@ -1,765 +1,764 @@ /* * ng_iface.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_iface.c,v 1.31 1999/02/02 22:27:28 archie Exp $ + * $Whistle: ng_iface.c,v 1.33 1999/11/01 09:24:51 julian Exp $ */ /* * This node is also a system networking interface. It has * a hook for each protocol (IP, AppleTalk, IPX, etc). Packets * are simply relayed between the interface and the hooks. * * Interfaces are named ng0, ng1, .... FreeBSD does not support * the removal of interfaces, so iface nodes are persistent. * * This node also includes Berkeley packet filter support. */ #include "opt_inet.h" #include "opt_atalk.h" #include "opt_ipx.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #include #include #endif #ifdef NETATALK #include #include #include #endif #ifdef IPX #include #include #endif #ifdef NS #include #include #endif #include /* This struct describes one address family */ struct iffam { char *hookname; /* Name for hook */ u_char af; /* Family number */ u_char netisr; /* or NETISR_NONE */ union { void *_dummy; /* avoid warning */ struct ifqueue *inq; /* if netisr */ void (*input)(struct mbuf *m); /* if direct input */ } u; }; typedef const struct iffam *iffam_p; #define NETISR_NONE 0xff /* List of address families supported by our interface. Each address family has a way to input packets to it, either by calling a function directly (such as ip_input()) or by adding the packet to a queue and setting a NETISR bit. */ const static struct iffam gFamilies[] = { #ifdef INET { NG_IFACE_HOOK_INET, AF_INET, NETISR_NONE, { ip_input } }, #endif #ifdef NETATALK { NG_IFACE_HOOK_ATALK, AF_APPLETALK, NETISR_ATALK, { &atintrq2 } }, #endif #ifdef IPX { NG_IFACE_HOOK_IPX, AF_IPX, NETISR_IPX, { &ipxintrq } }, #endif #ifdef NS { NG_IFACE_HOOK_NS, AF_NS, NETISR_NS, { &nsintrq } }, #endif }; #define NUM_FAMILIES (sizeof(gFamilies) / sizeof(*gFamilies)) /* Node private data */ struct private { struct ifnet *ifp; /* This interface */ node_p node; /* Our netgraph node */ hook_p hooks[NUM_FAMILIES]; /* Hook for each address family */ struct private *next; /* When hung on the free list */ }; typedef struct private *priv_p; /* Interface methods */ static void ng_iface_start(struct ifnet *ifp); static int ng_iface_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); static int ng_iface_output(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst, struct rtentry *rt0); static void ng_iface_bpftap(struct ifnet *ifp, struct mbuf *m, u_int af); #ifdef DEBUG static void ng_iface_print_ioctl(struct ifnet *ifp, int cmd, caddr_t data); #endif /* Netgraph methods */ -static int ng_iface_constructor(node_p *nodep); -static int ng_iface_rcvmsg(node_p node, struct ng_mesg *msg, - const char *retaddr, struct ng_mesg **resp); -static int ng_iface_rmnode(node_p node); -static int ng_iface_newhook(node_p node, hook_p hook, const char *name); -static int ng_iface_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); -static int ng_iface_disconnect(hook_p hook); +static ng_constructor_t ng_iface_constructor; +static ng_rcvmsg_t ng_iface_rcvmsg; +static ng_shutdown_t ng_iface_rmnode; +static ng_newhook_t ng_iface_newhook; +static ng_rcvdata_t ng_iface_rcvdata; +static ng_disconnect_t ng_iface_disconnect; /* Helper stuff */ static iffam_p get_iffam_from_af(int af); static iffam_p get_iffam_from_hook(priv_p priv, hook_p hook); static iffam_p get_iffam_from_name(const char *name); static hook_p *get_hook_from_iffam(priv_p priv, iffam_p iffam); /* Node type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_IFACE_NODE_TYPE, NULL, ng_iface_constructor, ng_iface_rcvmsg, ng_iface_rmnode, ng_iface_newhook, NULL, NULL, ng_iface_rcvdata, ng_iface_rcvdata, ng_iface_disconnect }; NETGRAPH_INIT(iface, &typestruct); static char ng_iface_ifname[] = NG_IFACE_IFACE_NAME; static int ng_iface_next_unit; /************************************************************************ HELPER STUFF ************************************************************************/ /* * Get the family descriptor from the family ID */ static __inline__ iffam_p get_iffam_from_af(int af) { iffam_p iffam; int k; for (k = 0; k < NUM_FAMILIES; k++) { iffam = &gFamilies[k]; if (iffam->af == af) return (iffam); } return (NULL); } /* * Get the family descriptor from the hook */ static __inline__ iffam_p get_iffam_from_hook(priv_p priv, hook_p hook) { int k; for (k = 0; k < NUM_FAMILIES; k++) if (priv->hooks[k] == hook) return (&gFamilies[k]); return (NULL); } /* * Get the hook from the iffam descriptor */ static __inline__ hook_p * get_hook_from_iffam(priv_p priv, iffam_p iffam) { return (&priv->hooks[iffam - gFamilies]); } /* * Get the iffam descriptor from the name */ static __inline__ iffam_p get_iffam_from_name(const char *name) { iffam_p iffam; int k; for (k = 0; k < NUM_FAMILIES; k++) { iffam = &gFamilies[k]; if (!strcmp(iffam->hookname, name)) return (iffam); } return (NULL); } /************************************************************************ INTERFACE STUFF ************************************************************************/ /* * Process an ioctl for the virtual interface */ static int ng_iface_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct ifreq *const ifr = (struct ifreq *) data; int s, error = 0; #ifdef DEBUG ng_iface_print_ioctl(ifp, command, data); #endif s = splimp(); switch (command) { /* These two are mostly handled at a higher layer */ case SIOCSIFADDR: ifp->if_flags |= (IFF_UP | IFF_RUNNING); ifp->if_flags &= ~(IFF_OACTIVE); break; case SIOCGIFADDR: break; /* Set flags */ case SIOCSIFFLAGS: /* * If the interface is marked up and stopped, then start it. * If it is marked down and running, then stop it. */ if (ifr->ifr_flags & IFF_UP) { if (!(ifp->if_flags & IFF_RUNNING)) { ifp->if_flags &= ~(IFF_OACTIVE); ifp->if_flags |= IFF_RUNNING; } } else { if (ifp->if_flags & IFF_RUNNING) ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); } break; /* Set the interface MTU */ case SIOCSIFMTU: if (ifr->ifr_mtu > NG_IFACE_MTU_MAX || ifr->ifr_mtu < NG_IFACE_MTU_MIN) error = EINVAL; else ifp->if_mtu = ifr->ifr_mtu; break; /* Stuff that's not supported */ case SIOCADDMULTI: case SIOCDELMULTI: case SIOCSIFPHYS: error = EOPNOTSUPP; break; default: error = EINVAL; break; } (void) splx(s); return (error); } /* * This routine is called to deliver a packet out the interface. * We simply look at the address family and relay the packet to * the corresponding hook, if it exists and is connected. */ static int ng_iface_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, struct rtentry *rt0) { const priv_p priv = (priv_p) ifp->if_softc; const iffam_p iffam = get_iffam_from_af(dst->sa_family); meta_p meta = NULL; int len, error = 0; /* Check interface flags */ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { m_freem(m); return (ENETDOWN); } /* Berkeley packet filter */ ng_iface_bpftap(ifp, m, dst->sa_family); /* Check address family to determine hook (if known) */ if (iffam == NULL) { m_freem(m); log(LOG_WARNING, "%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit, dst->sa_family); return (EAFNOSUPPORT); } /* Copy length before the mbuf gets invalidated */ len = m->m_pkthdr.len; /* Send packet; if hook is not connected, mbuf will get freed. */ NG_SEND_DATA(error, *get_hook_from_iffam(priv, iffam), m, meta); /* Update stats */ if (error == 0) { ifp->if_obytes += len; ifp->if_opackets++; } return (error); } /* * This routine should never be called */ static void ng_iface_start(struct ifnet *ifp) { printf("%s%d: %s called?", ifp->if_name, ifp->if_unit, __FUNCTION__); } /* * Flash a packet by the BPF (requires prepending 4 byte AF header) * Note the phoney mbuf; this is OK because BPF treats it read-only. */ static void ng_iface_bpftap(struct ifnet *ifp, struct mbuf *m, u_int af) { struct mbuf m2; if (ifp->if_bpf) { if (af == AF_UNSPEC) { af = *(mtod(m, int *)); m->m_len -= sizeof(int); m->m_pkthdr.len -= sizeof(int); m->m_data += sizeof(int); } if (!ifp->if_bpf) return; m2.m_next = m; m2.m_len = 4; m2.m_data = (char *) ⁡ bpf_mtap(ifp, &m2); } } #ifdef DEBUG /* * Display an ioctl to the virtual interface */ static void ng_iface_print_ioctl(struct ifnet *ifp, int command, caddr_t data) { char *str; switch (command & IOC_DIRMASK) { case IOC_VOID: str = "IO"; break; case IOC_OUT: str = "IOR"; break; case IOC_IN: str = "IOW"; break; case IOC_INOUT: str = "IORW"; break; default: str = "IO??"; } log(LOG_DEBUG, "%s%d: %s('%c', %d, char[%d])\n", ifp->if_name, ifp->if_unit, str, IOCGROUP(command), command & 0xff, IOCPARM_LEN(command)); } #endif /* DEBUG */ /************************************************************************ NETGRAPH NODE STUFF ************************************************************************/ /* * Constructor for a node */ static int ng_iface_constructor(node_p *nodep) { char ifname[NG_IFACE_IFACE_NAME_MAX + 1]; struct ifnet *ifp; node_p node; priv_p priv; int error = 0; /* Allocate node and interface private structures */ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK); if (priv == NULL) return (ENOMEM); bzero(priv, sizeof(*priv)); MALLOC(ifp, struct ifnet *, sizeof(*ifp), M_NETGRAPH, M_WAITOK); if (ifp == NULL) { FREE(priv, M_NETGRAPH); return (ENOMEM); } bzero(ifp, sizeof(*ifp)); /* Link them together */ ifp->if_softc = priv; priv->ifp = ifp; /* Call generic node constructor */ if ((error = ng_make_node_common(&typestruct, nodep))) { FREE(priv, M_NETGRAPH); FREE(ifp, M_NETGRAPH); return (error); } node = *nodep; /* Link together node and private info */ node->private = priv; priv->node = node; /* Initialize interface structure */ ifp->if_name = ng_iface_ifname; ifp->if_unit = ng_iface_next_unit++; ifp->if_output = ng_iface_output; ifp->if_start = ng_iface_start; ifp->if_ioctl = ng_iface_ioctl; ifp->if_watchdog = NULL; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; ifp->if_mtu = NG_IFACE_MTU_DEFAULT; ifp->if_flags = (IFF_SIMPLEX | IFF_POINTOPOINT | IFF_NOARP); ifp->if_type = IFT_PROPVIRTUAL; /* XXX */ ifp->if_addrlen = 0; /* XXX */ ifp->if_hdrlen = 0; /* XXX */ ifp->if_baudrate = 64000; /* XXX */ TAILQ_INIT(&ifp->if_addrhead); /* Give this node the same name as the interface (if possible) */ bzero(ifname, sizeof(ifname)); sprintf(ifname, "%s%d", ifp->if_name, ifp->if_unit); (void) ng_name_node(node, ifname); /* Attach the interface */ if_attach(ifp); bpfattach(ifp, DLT_NULL, sizeof(u_int)); /* Done */ return (0); } /* * Give our ok for a hook to be added */ static int ng_iface_newhook(node_p node, hook_p hook, const char *name) { const iffam_p iffam = get_iffam_from_name(name); hook_p *hookptr; if (iffam == NULL) return (EPFNOSUPPORT); hookptr = get_hook_from_iffam((priv_p) node->private, iffam); if (*hookptr != NULL) return (EISCONN); *hookptr = hook; return (0); } /* * Receive a control message */ static int ng_iface_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr) { const priv_p priv = node->private; struct ifnet *const ifp = priv->ifp; struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_IFACE_COOKIE: switch (msg->header.cmd) { case NGM_IFACE_GET_IFNAME: { struct ng_iface_ifname *arg; NG_MKRESPONSE(resp, msg, sizeof(*arg), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } arg = (struct ng_iface_ifname *) resp->data; sprintf(arg->ngif_name, "%s%d", ifp->if_name, ifp->if_unit); break; } case NGM_IFACE_GET_IFADDRS: { struct ifaddr *ifa; caddr_t ptr; int buflen; #define SA_SIZE(s) ((s)->sa_lensa_len) /* Determine size of response and allocate it */ buflen = 0; TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) buflen += SA_SIZE(ifa->ifa_addr); NG_MKRESPONSE(resp, msg, buflen, M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } /* Add addresses */ ptr = resp->data; TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { const int len = SA_SIZE(ifa->ifa_addr); if (buflen < len) { log(LOG_ERR, "%s%d: len changed?\n", ifp->if_name, ifp->if_unit); break; } bcopy(ifa->ifa_addr, ptr, len); ptr += len; buflen -= len; } break; #undef SA_SIZE } default: error = EINVAL; break; } break; case NGM_CISCO_COOKIE: switch (msg->header.cmd) { case NGM_CISCO_GET_IPADDR: /* we understand this too */ { struct ifaddr *ifa; /* Return the first configured IP address */ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { struct in_addr *ips; if (ifa->ifa_addr->sa_family != AF_INET) continue; NG_MKRESPONSE(resp, msg, 2 * sizeof(*ips), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } ips = (struct in_addr *) resp->data; ips[0] = ((struct sockaddr_in *) ifa->ifa_addr)->sin_addr; ips[1] = ((struct sockaddr_in *) ifa->ifa_netmask)->sin_addr; break; } /* No IP addresses on this interface? */ if (ifa == NULL) error = EADDRNOTAVAIL; break; } default: error = EINVAL; break; } break; default: error = EINVAL; break; } if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); FREE(msg, M_NETGRAPH); return (error); } /* * Recive data from a hook. Pass the packet to the correct input routine. */ static int ng_iface_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { const priv_p priv = hook->node->private; const iffam_p iffam = get_iffam_from_hook(priv, hook); struct ifnet *const ifp = priv->ifp; int s, error = 0; /* Sanity checks */ #ifdef DIAGNOSTIC if (iffam == NULL) panic(__FUNCTION__); if ((m->m_flags & M_PKTHDR) == 0) panic(__FUNCTION__); #endif if (m == NULL) return (EINVAL); if ((ifp->if_flags & IFF_UP) == 0) { NG_FREE_DATA(m, meta); return (ENETDOWN); } /* Update interface stats */ ifp->if_ipackets++; ifp->if_ibytes += m->m_pkthdr.len; /* Note receiving interface */ m->m_pkthdr.rcvif = ifp; /* Berkeley packet filter */ ng_iface_bpftap(ifp, m, iffam->af); /* Ignore any meta-data */ NG_FREE_META(meta); /* Send packet, either by NETISR or use a direct input function */ switch (iffam->netisr) { case NETISR_NONE: (*iffam->u.input)(m); break; default: s = splimp(); schednetisr(iffam->netisr); if (IF_QFULL(iffam->u.inq)) { IF_DROP(iffam->u.inq); m_freem(m); error = ENOBUFS; } else IF_ENQUEUE(iffam->u.inq, m); splx(s); break; } /* Done */ return (error); } /* * Because the BSD networking code doesn't support the removal of * networking interfaces, iface nodes (once created) are persistent. * So this method breaks all connections and marks the interface * down, but does not remove the node. */ static int ng_iface_rmnode(node_p node) { const priv_p priv = node->private; struct ifnet *const ifp = priv->ifp; ng_cutlinks(node); node->flags &= ~NG_INVALID; ifp->if_flags &= ~(IFF_UP | IFF_RUNNING | IFF_OACTIVE); return (0); } /* * Hook disconnection */ static int ng_iface_disconnect(hook_p hook) { const priv_p priv = hook->node->private; const iffam_p iffam = get_iffam_from_hook(priv, hook); if (iffam == NULL) panic(__FUNCTION__); *get_hook_from_iffam(priv, iffam) = NULL; return (0); } Index: head/sys/netgraph/ng_lmi.c =================================================================== --- head/sys/netgraph/ng_lmi.c (revision 52751) +++ head/sys/netgraph/ng_lmi.c (revision 52752) @@ -1,1091 +1,1090 @@ /* * 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 $ + * $Whistle: ng_lmi.c,v 1.38 1999/11/01 09:24:52 julian Exp $ */ /* * This node performs the frame relay LMI protocol. It knows how * to do ITU Annex A, ANSI Annex D, and "Group-of-Four" variants * of the protocol. * * A specific protocol can be forced by connecting the corresponding * hook to DLCI 0 or 1023 (as appropriate) of a frame relay link. * * Alternately, this node can do auto-detection of the LMI protocol * by connecting hook "auto0" to DLCI 0 and "auto1023" to DLCI 1023. */ #include #include #include #include #include #include #include #include #include #include /* * Human readable names for LMI */ #define NAME_ANNEXA NG_LMI_HOOK_ANNEXA #define NAME_ANNEXD NG_LMI_HOOK_ANNEXD #define NAME_GROUP4 NG_LMI_HOOK_GROUPOF4 #define NAME_NONE "None" #define MAX_DLCIS 128 #define MAXDLCI 1023 /* * DLCI states */ #define DLCI_NULL 0 #define DLCI_UP 1 #define DLCI_DOWN 2 /* * Any received LMI frame should be at least this long */ #define LMI_MIN_LENGTH 8 /* XXX verify */ /* * Netgraph node methods and type descriptor */ -static 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 ng_constructor_t nglmi_constructor; +static ng_rcvmsg_t nglmi_rcvmsg; +static ng_shutdown_t nglmi_rmnode; +static ng_newhook_t nglmi_newhook; +static ng_rcvdata_t nglmi_rcvdata; +static ng_disconnect_t nglmi_disconnect; static int nglmi_checkdata(hook_p hook, struct mbuf *m, meta_p meta); static struct ng_type typestruct = { NG_VERSION, NG_LMI_NODE_TYPE, NULL, nglmi_constructor, nglmi_rcvmsg, nglmi_rmnode, nglmi_newhook, NULL, NULL, nglmi_rcvdata, nglmi_rcvdata, nglmi_disconnect }; 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_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); } Index: head/sys/netgraph/ng_ppp.c =================================================================== --- head/sys/netgraph/ng_ppp.c (revision 52751) +++ head/sys/netgraph/ng_ppp.c (revision 52752) @@ -1,1404 +1,1403 @@ /* * ng_ppp.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_ppp.c,v 1.22 1999/01/28 23:54:53 julian Exp $ + * $Whistle: ng_ppp.c,v 1.24 1999/11/01 09:24:52 julian Exp $ */ /* * PPP node type. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define PROT_VALID(p) (((p) & 0x0101) == 0x0001) #define PROT_COMPRESSIBLE(p) (((p) & 0xff00) == 0x0000) /* Some PPP protocol numbers we're interested in */ #define PROT_APPLETALK 0x0029 #define PROT_COMPD 0x00fd #define PROT_CRYPTD 0x0053 #define PROT_IP 0x0021 #define PROT_IPX 0x002B #define PROT_MP 0x003d #define PROT_VJCOMP 0x002d #define PROT_VJUNCOMP 0x002f /* Multilink PPP definitions */ #define MP_MIN_MRRU 1500 /* per RFC 1990 */ #define MP_INITIAL_SEQ 0 /* per RFC 1990 */ #define MP_MIN_LINK_MRU 32 #define MP_MAX_SEQ_LINGER 64 /* max frags we will hold */ #define MP_INSANE_SEQ_JUMP 128 /* a sequence # jump too far */ #define MP_MIN_FRAG_LEN 6 /* don't frag smaller frames */ #define MP_SHORT_SEQ_MASK 0x00000fff /* short seq # mask */ #define MP_SHORT_SEQ_HIBIT 0x00000800 /* short seq # high bit */ #define MP_SHORT_FIRST_FLAG 0x00008000 /* first fragment in frame */ #define MP_SHORT_LAST_FLAG 0x00004000 /* last fragment in frame */ #define MP_LONG_SEQ_MASK 0x00ffffff /* long seq # mask */ #define MP_LONG_SEQ_HIBIT 0x00800000 /* long seq # high bit */ #define MP_LONG_FIRST_FLAG 0x80000000 /* first fragment in frame */ #define MP_LONG_LAST_FLAG 0x40000000 /* last fragment in frame */ #define MP_SEQ_MASK (priv->conf.recvShortSeq ? \ MP_SHORT_SEQ_MASK : MP_LONG_SEQ_MASK) /* Sign extension of MP sequence numbers */ #define MP_SHORT_EXTEND(s) (((s) & MP_SHORT_SEQ_HIBIT) ? \ ((s) | ~MP_SHORT_SEQ_MASK) : (s)) #define MP_LONG_EXTEND(s) (((s) & MP_LONG_SEQ_HIBIT) ? \ ((s) | ~MP_LONG_SEQ_MASK) : (s)) /* Comparision of MP sequence numbers */ #define MP_SHORT_SEQ_DIFF(x,y) (MP_SHORT_EXTEND(x) - MP_SHORT_EXTEND(y)) #define MP_LONG_SEQ_DIFF(x,y) (MP_LONG_EXTEND(x) - MP_LONG_EXTEND(y)) #define MP_SEQ_DIFF(x,y) (priv->conf.recvShortSeq ? \ MP_SHORT_SEQ_DIFF((x), (y)) : \ MP_LONG_SEQ_DIFF((x), (y))) /* We store incoming fragments this way */ struct ng_ppp_frag { int seq; u_char first; u_char last; struct mbuf *data; meta_p meta; CIRCLEQ_ENTRY(ng_ppp_frag) f_qent; }; /* We keep track of link queue status this way */ struct ng_ppp_link_qstat { struct timeval lastWrite; /* time of last write */ int bytesInQueue; /* bytes in the queue then */ }; /* We use integer indicies to refer to the non-link hooks */ static const char *const ng_ppp_hook_names[] = { NG_PPP_HOOK_ATALK, #define HOOK_INDEX_ATALK 0 NG_PPP_HOOK_BYPASS, #define HOOK_INDEX_BYPASS 1 NG_PPP_HOOK_COMPRESS, #define HOOK_INDEX_COMPRESS 2 NG_PPP_HOOK_ENCRYPT, #define HOOK_INDEX_ENCRYPT 3 NG_PPP_HOOK_DECOMPRESS, #define HOOK_INDEX_DECOMPRESS 4 NG_PPP_HOOK_DECRYPT, #define HOOK_INDEX_DECRYPT 5 NG_PPP_HOOK_INET, #define HOOK_INDEX_INET 6 NG_PPP_HOOK_IPX, #define HOOK_INDEX_IPX 7 NG_PPP_HOOK_VJC_COMP, #define HOOK_INDEX_VJC_COMP 8 NG_PPP_HOOK_VJC_IP, #define HOOK_INDEX_VJC_IP 9 NG_PPP_HOOK_VJC_UNCOMP, #define HOOK_INDEX_VJC_UNCOMP 10 NG_PPP_HOOK_VJC_VJIP, #define HOOK_INDEX_VJC_VJIP 11 NULL #define HOOK_INDEX_MAX 12 }; /* We store index numbers in the hook private pointer. The HOOK_INDEX() for a hook is either the index (above) for normal hooks, or the ones complement of the link number for link hooks. */ #define HOOK_INDEX(hook) (*((int16_t *) &(hook)->private)) /* Node private data */ struct private { struct ng_ppp_node_config conf; struct ng_ppp_link_stat bundleStats; struct ng_ppp_link_stat linkStats[NG_PPP_MAX_LINKS]; hook_p links[NG_PPP_MAX_LINKS]; hook_p hooks[HOOK_INDEX_MAX]; u_char vjCompHooked; u_char allLinksEqual; u_short activeLinks[NG_PPP_MAX_LINKS]; u_int numActiveLinks; u_int lastLink; /* for round robin */ struct ng_ppp_link_qstat qstat[NG_PPP_MAX_LINKS]; CIRCLEQ_HEAD(ng_ppp_fraglist, ng_ppp_frag) frags; /* incoming fragments */ int mpSeqOut; /* next out MP seq # */ }; typedef struct private *priv_p; /* Netgraph node methods */ -static int ng_ppp_constructor(node_p *nodep); -static int ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg, - const char *retaddr, struct ng_mesg **resp); -static int ng_ppp_rmnode(node_p node); -static int ng_ppp_newhook(node_p node, hook_p hook, const char *name); -static int ng_ppp_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); -static int ng_ppp_disconnect(hook_p hook); +static ng_constructor_t ng_ppp_constructor; +static ng_rcvmsg_t ng_ppp_rcvmsg; +static ng_shutdown_t ng_ppp_rmnode; +static ng_newhook_t ng_ppp_newhook; +static ng_rcvdata_t ng_ppp_rcvdata; +static ng_disconnect_t ng_ppp_disconnect; /* Helper functions */ static int ng_ppp_input(node_p node, int ln, struct mbuf *m, meta_p meta); static int ng_ppp_output(node_p node, int ln, struct mbuf *m, meta_p meta); static int ng_ppp_mp_input(node_p nd, int ln, struct mbuf *m, meta_p meta); static int ng_ppp_mp_output(node_p node, struct mbuf *m, meta_p meta); static void ng_ppp_mp_strategy(node_p node, int len, int *distrib); static int ng_ppp_intcmp(const void *v1, const void *v2); static struct mbuf *ng_ppp_addproto(struct mbuf *m, int proto, int compOK); static int ng_ppp_config_valid(node_p node, const struct ng_ppp_node_config *newConf); static void ng_ppp_update(node_p node, int newConf); static void ng_ppp_free_frags(node_p node); /* Node type descriptor */ static struct ng_type ng_ppp_typestruct = { NG_VERSION, NG_PPP_NODE_TYPE, NULL, ng_ppp_constructor, ng_ppp_rcvmsg, ng_ppp_rmnode, ng_ppp_newhook, NULL, NULL, ng_ppp_rcvdata, ng_ppp_rcvdata, ng_ppp_disconnect }; NETGRAPH_INIT(ppp, &ng_ppp_typestruct); static int *compareLatencies; /* hack for ng_ppp_intcmp() */ #define ERROUT(x) do { error = (x); goto done; } while (0) /************************************************************************ NETGRAPH NODE STUFF ************************************************************************/ /* * Node type constructor */ static int ng_ppp_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(&ng_ppp_typestruct, nodep))) { FREE(priv, M_NETGRAPH); return (error); } (*nodep)->private = priv; /* Initialize state */ CIRCLEQ_INIT(&priv->frags); /* Done */ return (0); } /* * Give our OK for a hook to be added */ static int ng_ppp_newhook(node_p node, hook_p hook, const char *name) { const priv_p priv = node->private; int linkNum = -1; hook_p *hookPtr = NULL; int hookIndex = -1; /* Figure out which hook it is */ if (strncmp(name, NG_PPP_HOOK_LINK_PREFIX, /* a link hook? */ strlen(NG_PPP_HOOK_LINK_PREFIX)) == 0) { int gotDigit = 0; const char *cp; for (cp = name + strlen(NG_PPP_HOOK_LINK_PREFIX); *cp >= '0' && *cp <= '9'; cp++) { if (!gotDigit) { if (*cp == '0') /* no leading zeros */ return (EINVAL); linkNum = *cp - '0'; gotDigit = 1; } else linkNum = (linkNum * 10) + (*cp - '0'); if (linkNum >= NG_PPP_MAX_LINKS) return (EINVAL); } if (!gotDigit || *cp != '\0') return (EINVAL); hookPtr = &priv->links[linkNum]; hookIndex = ~linkNum; } else { /* must be a non-link hook */ int i; for (i = 0; ng_ppp_hook_names[i] != NULL; i++) { if (strcmp(name, ng_ppp_hook_names[i]) == 0) { hookPtr = &priv->hooks[i]; hookIndex = i; break; } } if (ng_ppp_hook_names[i] == NULL) return (EINVAL); /* no such hook */ } /* See if hook is already connected */ if (*hookPtr != NULL) return (EISCONN); /* Disallow more than one link unless multilink is enabled */ if (linkNum != -1 && priv->conf.links[linkNum].enableLink && !priv->conf.enableMultilink && priv->numActiveLinks >= 1) return (ENODEV); /* OK */ *hookPtr = hook; HOOK_INDEX(hook) = hookIndex; ng_ppp_update(node, 0); return (0); } /* * Receive a control message */ static int ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg, const char *raddr, struct ng_mesg **rptr) { const priv_p priv = node->private; struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_PPP_COOKIE: switch (msg->header.cmd) { case NGM_PPP_SET_CONFIG: { struct ng_ppp_node_config *const newConf = (struct ng_ppp_node_config *) msg->data; /* Check for invalid or illegal config */ if (msg->header.arglen != sizeof(*newConf)) ERROUT(EINVAL); if (!ng_ppp_config_valid(node, newConf)) ERROUT(EINVAL); priv->conf = *newConf; ng_ppp_update(node, 1); break; } case NGM_PPP_GET_CONFIG: NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); bcopy(&priv->conf, resp->data, sizeof(priv->conf)); break; case NGM_PPP_GET_LINK_STATS: case NGM_PPP_CLR_LINK_STATS: { struct ng_ppp_link_stat *stats; u_int16_t linkNum; if (msg->header.arglen != sizeof(u_int16_t)) ERROUT(EINVAL); linkNum = *((u_int16_t *) msg->data); if (linkNum >= NG_PPP_MAX_LINKS && linkNum != NG_PPP_BUNDLE_LINKNUM) ERROUT(EINVAL); stats = (linkNum == NG_PPP_BUNDLE_LINKNUM) ? &priv->bundleStats : &priv->linkStats[linkNum]; if (msg->header.cmd == NGM_PPP_GET_LINK_STATS) { NG_MKRESPONSE(resp, msg, sizeof(struct ng_ppp_link_stat), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); bcopy(stats, resp->data, sizeof(*stats)); } else bzero(stats, sizeof(*stats)); break; } default: error = EINVAL; break; } break; case NGM_VJC_COOKIE: { char path[NG_PATHLEN + 1]; node_p origNode; if ((error = ng_path2node(node, raddr, &origNode, NULL)) != 0) ERROUT(error); snprintf(path, sizeof(path), "[%lx]:%s", (long) node, NG_PPP_HOOK_VJC_IP); return ng_send_msg(origNode, msg, path, rptr); 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 on a hook */ static int ng_ppp_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { const node_p node = hook->node; const priv_p priv = node->private; const int index = HOOK_INDEX(hook); u_int16_t linkNum = NG_PPP_BUNDLE_LINKNUM; hook_p outHook = NULL; int proto = 0, error; /* Did it come from a link hook? */ if (index < 0) { /* Is link active? */ linkNum = (u_int16_t)~index; KASSERT(linkNum < NG_PPP_MAX_LINKS, ("%s: bogus index 0x%x", __FUNCTION__, index)); if (!priv->conf.links[linkNum].enableLink) { NG_FREE_DATA(m, meta); return (ENXIO); } /* Stats */ priv->linkStats[linkNum].recvFrames++; priv->linkStats[linkNum].recvOctets += m->m_pkthdr.len; /* Dispatch incoming frame */ return ng_ppp_input(node, linkNum, m, meta); } /* Get protocol & check if data allowed from this hook */ switch (index) { /* Downwards flowing data */ case HOOK_INDEX_ATALK: if (!priv->conf.enableAtalk) { NG_FREE_DATA(m, meta); return (ENXIO); } proto = PROT_APPLETALK; break; case HOOK_INDEX_IPX: if (!priv->conf.enableIPX) { NG_FREE_DATA(m, meta); return (ENXIO); } proto = PROT_IPX; break; case HOOK_INDEX_INET: case HOOK_INDEX_VJC_VJIP: if (!priv->conf.enableIP) { NG_FREE_DATA(m, meta); return (ENXIO); } proto = PROT_IP; break; case HOOK_INDEX_VJC_COMP: if (!priv->conf.enableVJCompression) { NG_FREE_DATA(m, meta); return (ENXIO); } proto = PROT_VJCOMP; break; case HOOK_INDEX_VJC_UNCOMP: if (!priv->conf.enableVJCompression) { NG_FREE_DATA(m, meta); return (ENXIO); } proto = PROT_VJUNCOMP; break; case HOOK_INDEX_COMPRESS: if (!priv->conf.enableCompression) { NG_FREE_DATA(m, meta); return (ENXIO); } proto = PROT_COMPD; break; case HOOK_INDEX_ENCRYPT: if (!priv->conf.enableEncryption) { NG_FREE_DATA(m, meta); return (ENXIO); } proto = PROT_CRYPTD; break; case HOOK_INDEX_BYPASS: if (m->m_pkthdr.len < 4) { NG_FREE_META(meta); return (EINVAL); } if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } linkNum = ntohs(mtod(m, u_int16_t *)[0]); proto = ntohs(mtod(m, u_int16_t *)[1]); m_adj(m, 4); if (linkNum >= NG_PPP_MAX_LINKS && linkNum != NG_PPP_BUNDLE_LINKNUM) return (EINVAL); break; /* Upwards flowing data */ case HOOK_INDEX_VJC_IP: if (!priv->conf.enableVJDecompression) { NG_FREE_DATA(m, meta); return (ENXIO); } break; case HOOK_INDEX_DECOMPRESS: if (!priv->conf.enableDecompression) { NG_FREE_DATA(m, meta); return (ENXIO); } break; case HOOK_INDEX_DECRYPT: if (!priv->conf.enableDecryption) { NG_FREE_DATA(m, meta); return (ENXIO); } break; default: KASSERT(0, ("%s: bogus index 0x%x", __FUNCTION__, index)); } /* Now figure out what to do with the frame */ switch (index) { case HOOK_INDEX_INET: if (priv->conf.enableVJCompression && priv->vjCompHooked) { outHook = priv->hooks[HOOK_INDEX_VJC_IP]; break; } /* FALLTHROUGH */ case HOOK_INDEX_ATALK: case HOOK_INDEX_IPX: case HOOK_INDEX_VJC_COMP: case HOOK_INDEX_VJC_UNCOMP: case HOOK_INDEX_VJC_VJIP: if (priv->conf.enableCompression && priv->hooks[HOOK_INDEX_COMPRESS] != NULL) { if ((m = ng_ppp_addproto(m, proto, 1)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } outHook = priv->hooks[HOOK_INDEX_COMPRESS]; break; } /* FALLTHROUGH */ case HOOK_INDEX_COMPRESS: if (priv->conf.enableEncryption && priv->hooks[HOOK_INDEX_ENCRYPT] != NULL) { if ((m = ng_ppp_addproto(m, proto, 1)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } outHook = priv->hooks[HOOK_INDEX_ENCRYPT]; break; } /* FALLTHROUGH */ case HOOK_INDEX_ENCRYPT: case HOOK_INDEX_BYPASS: if ((m = ng_ppp_addproto(m, proto, linkNum == NG_PPP_BUNDLE_LINKNUM || priv->conf.links[linkNum].enableProtoComp)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } return ng_ppp_output(node, NG_PPP_BUNDLE_LINKNUM, m, meta); /* Incoming data */ case HOOK_INDEX_DECRYPT: case HOOK_INDEX_DECOMPRESS: case HOOK_INDEX_VJC_IP: return ng_ppp_input(node, NG_PPP_BUNDLE_LINKNUM, m, meta); } /* Send packet out hook */ NG_SEND_DATA(error, outHook, m, meta); return (error); } /* * Destroy node */ static int ng_ppp_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); /* let the node escape */ return (0); } /* * Hook disconnection */ static int ng_ppp_disconnect(hook_p hook) { if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } /************************************************************************ HELPER STUFF ************************************************************************/ /* * Handle an incoming frame. Extract the PPP protocol number * and dispatch accordingly. */ static int ng_ppp_input(node_p node, int linkNum, struct mbuf *m, meta_p meta) { const priv_p priv = node->private; hook_p outHook = NULL; int proto, error; /* Extract protocol number */ for (proto = 0; !PROT_VALID(proto) && m->m_pkthdr.len > 0; ) { if (m->m_len < 1 && (m = m_pullup(m, 1)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } proto = (proto << 8) + *mtod(m, u_char *); m_adj(m, 1); } if (!PROT_VALID(proto)) { if (linkNum == NG_PPP_BUNDLE_LINKNUM) priv->bundleStats.badProtos++; else priv->linkStats[linkNum].badProtos++; NG_FREE_DATA(m, meta); return (EINVAL); } /* Check protocol */ switch (proto) { case PROT_COMPD: if (priv->conf.enableDecompression) outHook = priv->hooks[HOOK_INDEX_DECOMPRESS]; break; case PROT_CRYPTD: if (priv->conf.enableDecryption) outHook = priv->hooks[HOOK_INDEX_DECRYPT]; break; case PROT_VJCOMP: if (priv->conf.enableVJDecompression && priv->vjCompHooked) outHook = priv->hooks[HOOK_INDEX_VJC_COMP]; break; case PROT_VJUNCOMP: if (priv->conf.enableVJDecompression && priv->vjCompHooked) outHook = priv->hooks[HOOK_INDEX_VJC_UNCOMP]; break; case PROT_MP: if (priv->conf.enableMultilink) { NG_FREE_META(meta); return ng_ppp_mp_input(node, linkNum, m, meta); } break; case PROT_APPLETALK: if (priv->conf.enableAtalk) outHook = priv->hooks[HOOK_INDEX_ATALK]; break; case PROT_IPX: if (priv->conf.enableIPX) outHook = priv->hooks[HOOK_INDEX_IPX]; break; case PROT_IP: if (priv->conf.enableIP) outHook = priv->hooks[HOOK_INDEX_INET]; break; } /* For unknown/inactive protocols, forward out the bypass hook */ if (outHook == NULL) { M_PREPEND(m, 4, M_NOWAIT); if (m == NULL || (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL)) return (ENOBUFS); mtod(m, u_int16_t *)[0] = htons(linkNum); mtod(m, u_int16_t *)[1] = htons(proto); outHook = priv->hooks[HOOK_INDEX_BYPASS]; } /* Forward frame */ NG_SEND_DATA(error, outHook, m, meta); return (error); } /* * Deliver a frame out a link, either a real one or NG_PPP_BUNDLE_LINKNUM */ static int ng_ppp_output(node_p node, int linkNum, struct mbuf *m, meta_p meta) { const priv_p priv = node->private; int error; /* Check for bundle virtual link */ if (linkNum == NG_PPP_BUNDLE_LINKNUM) { if (priv->conf.enableMultilink) return ng_ppp_mp_output(node, m, meta); linkNum = priv->activeLinks[0]; } /* Check link status */ if (!priv->conf.links[linkNum].enableLink) return (ENXIO); if (priv->links[linkNum] == NULL) { NG_FREE_DATA(m, meta); return (ENETDOWN); } /* Update stats XXX even if error? */ priv->linkStats[linkNum].xmitFrames++; priv->linkStats[linkNum].xmitOctets += m->m_pkthdr.len; /* Deliver frame */ NG_SEND_DATA(error, priv->links[linkNum], m, meta); return error; } /* * Handle an incoming multi-link fragment */ static int ng_ppp_mp_input(node_p node, int linkNum, struct mbuf *m, meta_p meta) { const priv_p priv = node->private; struct ng_ppp_frag frag0, *frag = &frag0; struct ng_ppp_frag *qent, *qnext; struct ng_ppp_frag *first = NULL, *last = NULL; int diff, highSeq, nextSeq; struct mbuf *tail; /* Extract fragment information from MP header */ if (priv->conf.recvShortSeq) { u_int16_t shdr; if (m->m_pkthdr.len < 2) { NG_FREE_DATA(m, meta); return (EINVAL); } if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } shdr = ntohs(*mtod(m, u_int16_t *)); frag->seq = shdr & MP_SHORT_SEQ_MASK; frag->first = (shdr & MP_SHORT_FIRST_FLAG) != 0; frag->last = (shdr & MP_SHORT_LAST_FLAG) != 0; highSeq = CIRCLEQ_EMPTY(&priv->frags) ? frag->seq : CIRCLEQ_FIRST(&priv->frags)->seq; diff = MP_SHORT_SEQ_DIFF(frag->seq, highSeq); m_adj(m, 2); } else { u_int32_t lhdr; if (m->m_pkthdr.len < 4) { NG_FREE_DATA(m, meta); return (EINVAL); } if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } lhdr = ntohl(*mtod(m, u_int32_t *)); frag->seq = lhdr & MP_LONG_SEQ_MASK; frag->first = (lhdr & MP_LONG_FIRST_FLAG) != 0; frag->last = (lhdr & MP_LONG_LAST_FLAG) != 0; highSeq = CIRCLEQ_EMPTY(&priv->frags) ? frag->seq : CIRCLEQ_FIRST(&priv->frags)->seq; diff = MP_LONG_SEQ_DIFF(frag->seq, highSeq); m_adj(m, 4); } frag->data = m; frag->meta = meta; /* If the sequence number makes a large jump, empty the queue */ if (diff <= -MP_INSANE_SEQ_JUMP || diff >= MP_INSANE_SEQ_JUMP) ng_ppp_free_frags(node); /* Optimization: handle a frame that's all in one fragment */ if (frag->first && frag->last) return ng_ppp_input(node, NG_PPP_BUNDLE_LINKNUM, m, meta); /* Allocate a new frag struct for the queue */ MALLOC(frag, struct ng_ppp_frag *, sizeof(*frag), M_NETGRAPH, M_NOWAIT); if (frag == NULL) { NG_FREE_DATA(m, meta); return (ENOMEM); } *frag = frag0; meta = NULL; m = NULL; /* Add fragment to queue, which is reverse sorted by sequence number */ CIRCLEQ_FOREACH(qent, &priv->frags, f_qent) { diff = MP_SEQ_DIFF(frag->seq, qent->seq); if (diff > 0) { CIRCLEQ_INSERT_BEFORE(&priv->frags, qent, frag, f_qent); break; } else if (diff == 0) { /* should never happen! */ log(LOG_ERR, "%s: rec'd dup MP fragment\n", node->name); if (linkNum == NG_PPP_BUNDLE_LINKNUM) priv->linkStats[linkNum].dupFragments++; else priv->bundleStats.dupFragments++; NG_FREE_DATA(frag->data, frag->meta); FREE(frag, M_NETGRAPH); return (EINVAL); } } if (qent == NULL) CIRCLEQ_INSERT_TAIL(&priv->frags, frag, f_qent); /* Find the first fragment in the possibly newly completed frame */ for (nextSeq = frag->seq, qent = frag; qent != (void *) &priv->frags; qent = CIRCLEQ_PREV(qent, f_qent)) { if (qent->seq != nextSeq) goto pruneQueue; if (qent->first) { first = qent; break; } nextSeq = (nextSeq + 1) & MP_SEQ_MASK; } /* Find the last fragment in the possibly newly completed frame */ for (nextSeq = frag->seq, qent = frag; qent != (void *) &priv->frags; qent = CIRCLEQ_NEXT(qent, f_qent)) { if (qent->seq != nextSeq) goto pruneQueue; if (qent->last) { last = qent; break; } nextSeq = (nextSeq - 1) & MP_SEQ_MASK; } /* We have a complete frame, extract it from the queue */ for (tail = NULL, qent = first; qent != NULL; qent = qnext) { qnext = CIRCLEQ_PREV(qent, f_qent); CIRCLEQ_REMOVE(&priv->frags, qent, f_qent); if (tail == NULL) { tail = m = qent->data; meta = qent->meta; /* inherit first frag's meta */ } else { m->m_pkthdr.len += qent->data->m_pkthdr.len; tail->m_next = qent->data; NG_FREE_META(qent->meta); /* drop other frag's metas */ } while (tail->m_next != NULL) tail = tail->m_next; if (qent == last) qnext = NULL; FREE(qent, M_NETGRAPH); } pruneQueue: /* Prune out stale entries in the queue */ for (qent = CIRCLEQ_LAST(&priv->frags); qent != (void *) &priv->frags; qent = qnext) { if (MP_SEQ_DIFF(highSeq, qent->seq) <= MP_MAX_SEQ_LINGER) break; qnext = CIRCLEQ_PREV(qent, f_qent); CIRCLEQ_REMOVE(&priv->frags, qent, f_qent); NG_FREE_DATA(qent->data, qent->meta); FREE(qent, M_NETGRAPH); } /* Deliver newly completed frame, if any */ return m ? ng_ppp_input(node, NG_PPP_BUNDLE_LINKNUM, m, meta) : 0; } /* * Deliver a frame out on the bundle, i.e., figure out how to fragment * the frame across the individual PPP links and do so. */ static int ng_ppp_mp_output(node_p node, struct mbuf *m, meta_p meta) { const priv_p priv = node->private; int distrib[NG_PPP_MAX_LINKS]; int firstFragment; int activeLinkNum; /* At least one link must be active */ if (priv->numActiveLinks == 0) { NG_FREE_DATA(m, meta); return (ENETDOWN); } /* Round-robin strategy */ if (priv->conf.enableRoundRobin || m->m_pkthdr.len < MP_MIN_FRAG_LEN) { activeLinkNum = priv->lastLink++ % priv->numActiveLinks; bzero(&distrib, priv->numActiveLinks * sizeof(distrib[0])); distrib[activeLinkNum] = m->m_pkthdr.len; goto deliver; } /* Strategy when all links are equivalent (optimize the common case) */ if (priv->allLinksEqual) { const int fraction = m->m_pkthdr.len / priv->numActiveLinks; int i, remain; for (i = 0; i < priv->numActiveLinks; i++) distrib[priv->lastLink++ % priv->numActiveLinks] = fraction; remain = m->m_pkthdr.len - (fraction * priv->numActiveLinks); while (remain > 0) { distrib[priv->lastLink++ % priv->numActiveLinks]++; remain--; } goto deliver; } /* Strategy when all links are not equivalent */ ng_ppp_mp_strategy(node, m->m_pkthdr.len, distrib); deliver: /* Update stats */ priv->bundleStats.xmitFrames++; priv->bundleStats.xmitOctets += m->m_pkthdr.len; /* Send alloted portions of frame out on the link(s) */ for (firstFragment = 1, activeLinkNum = priv->numActiveLinks - 1; activeLinkNum >= 0; activeLinkNum--) { const int linkNum = priv->activeLinks[activeLinkNum]; /* Deliver fragment(s) out the next link */ for ( ; distrib[activeLinkNum] > 0; firstFragment = 0) { int len, lastFragment, error; struct mbuf *m2; meta_p meta2; /* Calculate fragment length; don't exceed link MTU */ len = distrib[activeLinkNum]; if (len > priv->conf.links[linkNum].mru) len = priv->conf.links[linkNum].mru; distrib[activeLinkNum] -= len; lastFragment = (len == m->m_pkthdr.len); /* Split off next fragment as "m2" */ m2 = m; if (!lastFragment) { struct mbuf *n = m_split(m, len, M_NOWAIT); if (n == NULL) { NG_FREE_DATA(m, meta); return (ENOMEM); } m = n; } /* Prepend MP header */ if (priv->conf.xmitShortSeq) { u_int16_t shdr; M_PREPEND(m2, 2, M_NOWAIT); if (m2 == NULL || (m2->m_len < 2 && (m2 = m_pullup(m2, 2)) == NULL)) { if (!lastFragment) m_freem(m); NG_FREE_META(meta); return (ENOBUFS); } shdr = priv->mpSeqOut; priv->mpSeqOut = (priv->mpSeqOut + 1) % MP_SHORT_SEQ_MASK; if (firstFragment) shdr |= MP_SHORT_FIRST_FLAG; if (lastFragment) shdr |= MP_SHORT_LAST_FLAG; *mtod(m2, u_int16_t *) = htons(shdr); } else { u_int32_t lhdr; M_PREPEND(m2, 4, M_NOWAIT); if (m2 == NULL || (m2->m_len < 4 && (m2 = m_pullup(m2, 4)) == NULL)) { if (!lastFragment) m_freem(m); NG_FREE_META(meta); return (ENOBUFS); } lhdr = priv->mpSeqOut; priv->mpSeqOut = (priv->mpSeqOut + 1) % MP_LONG_SEQ_MASK; if (firstFragment) lhdr |= MP_LONG_FIRST_FLAG; if (lastFragment) lhdr |= MP_LONG_LAST_FLAG; *mtod(m2, u_int32_t *) = htonl(lhdr); } /* Add MP protocol number */ m2 = ng_ppp_addproto(m, PROT_MP, priv->conf.links[linkNum].enableProtoComp); if (m2 == NULL) { if (!lastFragment) m_freem(m); NG_FREE_META(meta); return (ENOBUFS); } /* Copy the meta information, if any */ if (meta != NULL && !lastFragment) { MALLOC(meta2, meta_p, meta->used_len, M_NETGRAPH, M_NOWAIT); if (meta2 == NULL) { m_freem(m2); NG_FREE_DATA(m, meta); return (ENOMEM); } meta2->allocated_len = meta->used_len; bcopy(meta, meta2, meta->used_len); } else meta2 = meta; /* Send fragment */ error = ng_ppp_output(node, linkNum, m2, meta2); /* Abort for error */ if (error != 0) { if (!lastFragment) NG_FREE_DATA(m, meta); return (error); } } } /* Done */ return (0); } /* * Computing the optimal fragmentation * ----------------------------------- * * This routine tries to compute the optimal fragmentation pattern based * on each link's latency, bandwidth, and calculated additional latency. * The latter quantity is the additional latency caused by previously * written data that has not been transmitted yet. * * This algorithm is only useful when not all of the links have the * same latency and bandwidth values. * * The essential idea is to make the last bit of each fragment of the * frame arrive at the opposite end at the exact same time. This greedy * algorithm is optimal, in that no other scheduling could result in any * packet arriving any sooner unless packets are delivered out of order. * * Suppose link i has bandwidth b_i (in tens of bytes per milisecond) and * latency l_i (in miliseconds). Consider the function function f_i(t) * which is equal to the number of bytes that will have arrived at * the peer after t miliseconds if we start writing continuously at * time t = 0. Then f_i(t) = b_i * (t - l_i) = ((b_i * t) - (l_i * b_i). * That is, f_i(t) is a line with slope b_i and y-intersect -(l_i * b_i). * Note that the y-intersect is always <= zero because latency can't be * negative. Note also that really the function is f_i(t) except when * f_i(t) is negative, in which case the function is zero. To take * care of this, let Q_i(t) = { if (f_i(t) > 0) return 1; else return 0; }. * So the actual number of bytes that will have arrived at the peer after * t miliseconds is f_i(t) * Q_i(t). * * At any given time, each link has some additional latency a_i >= 0 * due to previously written fragment(s) which are still in the queue. * This value is easily computed from the time since last transmission, * the previous latency value, the number of bytes written, and the * link's bandwidth. * * Assume that l_i includes any a_i already, and that the links are * sorted by latency, so that l_i <= l_{i+1}. * * Let N be the total number of bytes in the current frame we are sending. * * Suppose we were to start writing bytes at time t = 0 on all links * simultaneously, which is the most we can possibly do. Then let * F(t) be equal to the total number of bytes received by the peer * after t miliseconds. Then F(t) = Sum_i (f_i(t) * Q_i(t)). * * Our goal is simply this: fragment the frame across the links such * that the peer is able to reconstruct the completed frame as soon as * possible, i.e., at the least possible value of t. Call this value t_0. * * Then it follows that F(t_0) = N. Our strategy is first to find the value * of t_0, and then deduce how many bytes to write to each link. * * Rewriting F(t_0): * * t_0 = ( N + Sum_i ( l_i * b_i * Q_i(t_0) ) ) / Sum_i ( b_i * Q_i(t_0) ) * * Now, we note that Q_i(t) is constant for l_i <= t <= l_{i+1}. t_0 will * lie in one of these ranges. To find it, we just need to find the i such * that F(l_i) <= N <= F(l_{i+1}). Then we compute all the constant values * for Q_i() in this range, plug in the remaining values, solving for t_0. * * Once t_0 is known, then the number of bytes to send on link i is * just f_i(t_0) * Q_i(t_0). * * In other words, we start allocating bytes to the links one at a time. * We keep adding links until the frame is completely sent. Some links * may not get any bytes because their latency is too high. * * Is all this work really worth the trouble? Depends on the situation. * The bigger the ratio of computer speed to link speed, and the more * important total bundle latency is (e.g., for interactive response time), * the more it's worth it. There is however the cost of calling this * function for every frame. The running time is O(n^2) where n is the * number of links that receive a non-zero number of bytes. * * Since latency is measured in miliseconds, the "resolution" of this * algorithm is one milisecond. * * To avoid this algorithm altogether, configure all links to have the * same latency and bandwidth. */ static void ng_ppp_mp_strategy(node_p node, int len, int *distrib) { const priv_p priv = node->private; int latency[NG_PPP_MAX_LINKS]; int sortByLatency[NG_PPP_MAX_LINKS]; int activeLinkNum, linkNum; int t0, total, topSum, botSum; struct timeval now; int i, numFragments; /* If only one link, this gets real easy */ if (priv->numActiveLinks == 1) { distrib[0] = len; return; } /* Get current time */ getmicrotime(&now); /* Compute latencies for each link at this point in time */ for (activeLinkNum = 0; activeLinkNum < priv->numActiveLinks; activeLinkNum++) { struct timeval diff; int xmitBytes; /* Start with base latency value */ linkNum = priv->activeLinks[activeLinkNum]; latency[activeLinkNum] = priv->conf.links[linkNum].latency; sortByLatency[activeLinkNum] = activeLinkNum; /* see below */ /* Any additional latency? */ if (priv->qstat[activeLinkNum].bytesInQueue == 0) continue; /* Compute time delta since last write */ diff = now; timevalsub(&diff, &priv->qstat[activeLinkNum].lastWrite); if (now.tv_sec < 0 || diff.tv_sec >= 10) { /* sanity */ priv->qstat[activeLinkNum].bytesInQueue = 0; continue; } /* How many bytes could have transmitted since last write? */ xmitBytes = priv->conf.links[linkNum].bandwidth * diff.tv_sec + (priv->conf.links[linkNum].bandwidth * (diff.tv_usec / 1000)) / 100; priv->qstat[activeLinkNum].bytesInQueue -= xmitBytes; if (priv->qstat[activeLinkNum].bytesInQueue < 0) priv->qstat[activeLinkNum].bytesInQueue = 0; else latency[activeLinkNum] += (100 * priv->qstat[activeLinkNum].bytesInQueue) / priv->conf.links[linkNum].bandwidth; } /* Sort links by latency */ compareLatencies = latency; qsort(sortByLatency, priv->numActiveLinks, sizeof(*sortByLatency), ng_ppp_intcmp); compareLatencies = NULL; /* Find the interval we need (add links in sortByLatency[] order) */ for (numFragments = 1; numFragments < priv->numActiveLinks; numFragments++) { for (total = i = 0; i < numFragments; i++) { int flowTime; flowTime = latency[sortByLatency[numFragments]] - latency[sortByLatency[i]]; total += ((flowTime * priv->conf.links[ priv->activeLinks[sortByLatency[i]]].bandwidth) + 99) / 100; } if (total >= len) break; } /* Solve for t_0 in that interval */ for (topSum = botSum = i = 0; i < numFragments; i++) { int bw = priv->conf.links[ priv->activeLinks[sortByLatency[i]]].bandwidth; topSum += latency[sortByLatency[i]] * bw; /* / 100 */ botSum += bw; /* / 100 */ } t0 = ((len * 100) + topSum + botSum / 2) / botSum; /* Compute f_i(t_0) all i */ bzero(distrib, priv->numActiveLinks * sizeof(*distrib)); for (total = i = 0; i < numFragments; i++) { int bw = priv->conf.links[ priv->activeLinks[sortByLatency[i]]].bandwidth; distrib[sortByLatency[i]] = (bw * (t0 - latency[sortByLatency[i]]) + 50) / 100; total += distrib[sortByLatency[i]]; } /* Deal with any rounding error by adjusting fastest link */ if (total != len) { int fast = 0; for (i = 1; i < numFragments; i++) { if (priv->conf.links[ priv->activeLinks[sortByLatency[i]]].bandwidth > priv->conf.links[ priv->activeLinks[sortByLatency[fast]]].bandwidth) fast = i; } distrib[sortByLatency[fast]] += len - total; } /* Update bytes in queue counters */ for (i = 0; i < priv->numActiveLinks; i++) { priv->qstat[i].bytesInQueue += distrib[i]; priv->qstat[i].lastWrite = now; } } /* * Compare two integers */ static int ng_ppp_intcmp(const void *v1, const void *v2) { const int index1 = *((const int *) v1); const int index2 = *((const int *) v2); return compareLatencies[index1] - compareLatencies[index2]; } /* * Prepend a possibly compressed PPP protocol number in front of a frame */ static struct mbuf * ng_ppp_addproto(struct mbuf *m, int proto, int compOK) { int psize = (PROT_COMPRESSIBLE(proto) && compOK) ? 1 : 2; /* Add protocol number */ M_PREPEND(m, psize, M_NOWAIT); if (m == NULL || (m->m_len < psize && (m = m_pullup(m, psize)) == NULL)) return (NULL); if (psize == 1) *mtod(m, u_char *) = (u_char)proto; else *mtod(m, u_int16_t *) = htons((u_int16_t)proto); return (m); } /* * Update private information that is derived from other private information */ static void ng_ppp_update(node_p node, int newConf) { const priv_p priv = node->private; int i; /* Update active status for VJ Compression */ priv->vjCompHooked = priv->hooks[HOOK_INDEX_VJC_IP] != NULL && priv->hooks[HOOK_INDEX_VJC_COMP] != NULL && priv->hooks[HOOK_INDEX_VJC_UNCOMP] != NULL && priv->hooks[HOOK_INDEX_VJC_VJIP] != NULL; /* Increase latency for each link an amount equal to one MP header */ if (newConf) { for (i = 0; i < NG_PPP_MAX_LINKS; i++) { int hdrBytes; hdrBytes = (priv->conf.links[i].enableProtoComp ? 1 : 2) + (priv->conf.xmitShortSeq ? 2 : 4); priv->conf.links[i].latency += ((hdrBytes * priv->conf.links[i].bandwidth) + 50) / 100; } } /* Update list of active links */ bzero(&priv->activeLinks, sizeof(priv->activeLinks)); priv->numActiveLinks = 0; priv->allLinksEqual = 1; for (i = 0; i < NG_PPP_MAX_LINKS; i++) { if (priv->conf.links[i].enableLink && priv->links[i] != NULL) { priv->activeLinks[priv->numActiveLinks++] = i; if (priv->conf.links[i].latency != priv->conf.links[0].latency || priv->conf.links[i].bandwidth != priv->conf.links[0].bandwidth) priv->allLinksEqual = 0; } } /* Reset MP state if no longer active */ if (!priv->conf.enableMultilink || priv->numActiveLinks == 0) { ng_ppp_free_frags(node); priv->mpSeqOut = MP_INITIAL_SEQ; bzero(&priv->qstat, sizeof(priv->qstat)); } } /* * Determine if a new configuration would represent a valid change * from the current configuration and link activity status. */ static int ng_ppp_config_valid(node_p node, const struct ng_ppp_node_config *newConf) { const priv_p priv = node->private; int i, newNumLinksActive; /* Check per-link config and count how many links would be active */ for (newNumLinksActive = i = 0; i < NG_PPP_MAX_LINKS; i++) { if (newConf->links[i].mru < MP_MIN_LINK_MRU) return (0); if (newConf->links[i].bandwidth == 0) return (0); if (newConf->links[i].bandwidth > NG_PPP_MAX_BANDWIDTH) return (0); if (newConf->links[i].latency > NG_PPP_MAX_LATENCY) return (0); if (newConf->links[i].enableLink && priv->links[i] != NULL) newNumLinksActive++; } /* Check bundle parameters */ if (priv->conf.enableMultilink && newConf->mrru < MP_MIN_MRRU) return (0); /* At most one link can be active unless multi-link is enabled */ if (!newConf->enableMultilink && newNumLinksActive > 1) return (0); /* Disallow changes to multi-link configuration while MP is active */ if (priv->numActiveLinks > 0 && newNumLinksActive > 0) { if (!priv->conf.enableMultilink != !newConf->enableMultilink || !priv->conf.xmitShortSeq != !newConf->xmitShortSeq || !priv->conf.recvShortSeq != !newConf->recvShortSeq) return (0); } /* Configuration change is valid */ return (1); } /* * Free all entries in the fragment queue */ static void ng_ppp_free_frags(node_p node) { const priv_p priv = node->private; struct ng_ppp_frag *qent, *next; for (qent = CIRCLEQ_FIRST(&priv->frags); qent != (void *) &priv->frags; qent = next) { next = CIRCLEQ_NEXT(qent, f_qent); NG_FREE_DATA(qent->data, qent->meta); FREE(qent, M_NETGRAPH); } CIRCLEQ_INIT(&priv->frags); } Index: head/sys/netgraph/ng_pppoe.c =================================================================== --- head/sys/netgraph/ng_pppoe.c (revision 52751) +++ head/sys/netgraph/ng_pppoe.c (revision 52752) @@ -1,1482 +1,1481 @@ #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 $ + * $Whistle: ng_pppoe.c,v 1.10 1999/11/01 09:24:52 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); +static ng_constructor_t ng_pppoe_constructor; +static ng_rcvmsg_t ng_pppoe_rcvmsg; +static ng_shutdown_t ng_pppoe_rmnode; +static ng_newhook_t ng_pppoe_newhook; +static ng_connect_t ng_pppoe_connect; +static ng_rcvdata_t ng_pppoe_rcvdata; +static ng_disconnect_t ng_pppoe_disconnect; /* 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. * But first correct the length. */ sp->pkt_hdr.ph.length = htons((short)(m->m_pkthdr.len)); 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)); 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; 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; int hooks; AAA if (hook->private == &privp->debug_hook) { privp->debug_hook = NULL; } else if (hook->private == &privp->ethernet_hook) { privp->ethernet_hook = NULL; ng_rmnode(node); } 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; /* work out how many session hooks there are */ /* Node goes away on last session hook removal */ hooks = node->numhooks; /* this one already not counted */ if (privp->ethernet_hook) hooks -= 1; if (privp->debug_hook) hooks -= 1; if (hooks == 0) ng_rmnode(node); } 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); } Index: head/sys/netgraph/ng_rfc1490.c =================================================================== --- head/sys/netgraph/ng_rfc1490.c (revision 52751) +++ head/sys/netgraph/ng_rfc1490.c (revision 52752) @@ -1,348 +1,347 @@ /* * 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 $ + * $Whistle: ng_rfc1490.c,v 1.22 1999/11/01 09:24:52 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); +static ng_constructor_t ng_rfc1490_constructor; +static ng_rcvmsg_t ng_rfc1490_rcvmsg; +static ng_shutdown_t ng_rfc1490_rmnode; +static ng_newhook_t ng_rfc1490_newhook; +static ng_rcvdata_t ng_rfc1490_rcvdata; +static ng_disconnect_t ng_rfc1490_disconnect; /* 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_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); } Index: head/sys/netgraph/ng_sample.c =================================================================== --- head/sys/netgraph/ng_sample.c (revision 52751) +++ head/sys/netgraph/ng_sample.c (revision 52752) @@ -1,441 +1,440 @@ /* * ng_sample.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_sample.c,v 1.11 1999/01/28 23:54:54 julian Exp $ + * $Whistle: ng_sample.c,v 1.13 1999/11/01 09:24:52 julian Exp $ */ #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_xxx_constructor(node_p *node); -static int ng_xxx_rcvmsg(node_p node, struct ng_mesg *msg, - const char *retaddr, struct ng_mesg **resp); -static int ng_xxx_rmnode(node_p node); -static int ng_xxx_newhook(node_p node, hook_p hook, const char *name); -static int ng_xxx_connect(hook_p hook); -static int ng_xxx_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); -static int ng_xxx_rcvdataq(hook_p hook, struct mbuf *m, meta_p meta); -static int ng_xxx_disconnect(hook_p hook); +static ng_constructor_t ng_xxx_constructor; +static ng_rcvmsg_t ng_xxx_rcvmsg; +static ng_shutdown_t ng_xxx_rmnode; +static ng_newhook_t ng_xxx_newhook; +static ng_connect_t ng_xxx_connect; +static ng_rcvdata_t ng_xxx_rcvdata; /* note these are both ng_rcvdata_t */ +static ng_rcvdata_t ng_xxx_rcvdataq; /* note these are both ng_rcvdata_t */ +static ng_disconnect_t ng_xxx_disconnect; /* Netgraph node type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_XXX_NODE_TYPE, NULL, ng_xxx_constructor, ng_xxx_rcvmsg, ng_xxx_rmnode, ng_xxx_newhook, NULL, ng_xxx_connect, ng_xxx_rcvdata, ng_xxx_rcvdataq, ng_xxx_disconnect }; NETGRAPH_INIT(xxx, &typestruct); /* Information we store for each hook on each node */ struct XXX_hookinfo { int dlci; /* The DLCI it represents, -1 == downstream */ int channel; /* The channel representing this DLCI */ hook_p hook; }; /* Information we store for each node */ struct XXX { struct XXX_hookinfo channel[XXX_NUM_DLCIS]; struct XXX_hookinfo downstream_hook; node_p node; /* back pointer to node */ hook_p debughook; u_int packets_in; /* packets in from downstream */ u_int packets_out; /* packets out towards downstream */ u_int32_t flags; }; typedef struct XXX *xxx_p; /* * 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_xxx_constructor(node_p *nodep) { xxx_p privdata; int i, error; /* Initialize private descriptor */ MALLOC(privdata, xxx_p, sizeof(*privdata), M_NETGRAPH, M_WAITOK); if (privdata == NULL) return (ENOMEM); bzero(privdata, sizeof(struct XXX)); for (i = 0; i < XXX_NUM_DLCIS; i++) { privdata->channel[i].dlci = -2; privdata->channel[i].channel = i; } /* 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... * If we are not running this might kick a device into life. * Possibly decode information out of the hook name. * Add the hook's private info to the hook structure. * (if we had some). In this example, we assume that there is a * an array of structs, called 'channel' in the private info, * one for each active channel. The private * pointer of each hook points to the appropriate XXX_hookinfo struct * so that the source of an input packet is easily identified. * (a dlci is a frame relay channel) */ static int ng_xxx_newhook(node_p node, hook_p hook, const char *name) { const xxx_p xxxp = node->private; const char *cp; char c = '\0'; int digits = 0; int dlci = 0; int chan; #if 0 /* Possibly start up the device if it's not already going */ if ((xxxp->flags & SCF_RUNNING) == 0) { ng_xxx_start_hardware(xxxp); } #endif /* Example of how one might use hooks with embedded numbers: All * hooks start with 'dlci' and have a decimal trailing channel * number up to 4 digits Use the leadin defined int he associated .h * file. */ if (strncmp(name, NG_XXX_HOOK_DLCI_LEADIN, 4) == 0) { cp = name + sizeof(NG_XXX_HOOK_DLCI_LEADIN); 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 */ for (chan = 0; chan < XXX_NUM_DLCIS; chan++) if (xxxp->channel[chan].dlci == dlci) break; if (chan == XXX_NUM_DLCIS) { for (chan = 0; chan < XXX_NUM_DLCIS; chan++) if (xxxp->channel[chan].dlci != -2) continue; if (chan == XXX_NUM_DLCIS) return (ENOBUFS); } if (xxxp->channel[chan].hook != NULL) return (EADDRINUSE); hook->private = xxxp->channel + chan; xxxp->channel[chan].hook = hook; return (0); } else if (strcmp(name, NG_XXX_HOOK_DOWNSTREAM) == 0) { /* Example of simple predefined hooks. */ /* do something specific to the downstream connection */ xxxp->downstream_hook.hook = hook; hook->private = &xxxp->downstream_hook; } else if (strcmp(name, NG_XXX_HOOK_DEBUG) == 0) { /* do something specific to a debug connection */ xxxp->debughook = hook; hook->private = NULL; } else return (EINVAL); /* not a hook we know about */ return(0); } /* * Get a netgraph control message. * Check it is one we understand. If needed, send a response. * We could save the address for an async action later, but don't here. * Always free the message. * The response should be in a malloc'd region that the caller can 'free'. * A response is not required. * Theoretically you could respond defferently to old message types if * the cookie in the header didn't match what we consider to be current * (so that old userland programs could continue to work). */ static int ng_xxx_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr) { const xxx_p xxxp = node->private; struct ng_mesg *resp = NULL; int error = 0; /* Deal with message according to cookie and command */ switch (msg->header.typecookie) { case NGM_XXX_COOKIE: switch (msg->header.cmd) { case NGM_XXX_GET_STATUS: { struct ngxxxstat *stats; NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); if (!resp) { error = ENOMEM; break; } stats = (struct ngxxxstat *) resp->data; stats->packets_in = xxxp->packets_in; stats->packets_out = xxxp->packets_out; break; } case NGM_XXX_SET_FLAG: if (msg->header.arglen != sizeof(u_int32_t)) { error = EINVAL; break; } xxxp->flags = *((u_int32_t *) msg->data); break; default: error = EINVAL; /* unknown command */ break; } break; default: error = EINVAL; /* unknown cookie type */ break; } /* Take care of synchronous response, if any */ if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); /* Free the message and return */ FREE(msg, M_NETGRAPH); return(error); } /* * Receive data, and do something with it. * Possibly send it out on another link after processing. * Possibly do something different if it comes from different * hooks. the caller will never free m or meta, so * if we use up this data or abort we must free BOTH of these. * * If we want, we may decide to force this data to be queued and reprocessed * at the netgraph NETISR time. (at which time it will be entered using ng_xxx_rcvdataq(). */ static int ng_xxx_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { int dlci = -2; int error; if (hook->private) { /* * If it's dlci 1023, requeue it so that it's handled at a lower priority. * This is how a node decides to defer a data message. */ dlci = ((struct XXX_hookinfo *) hook->private)->dlci; if (dlci == 1023) { return(ng_queue_data(hook->peer, m, meta)); } } return(ng_xxx_rcvdataq(hook, m, meta)); } /* * Always accept the data. This version of rcvdata is called from the dequeueing routine. */ static int ng_xxx_rcvdataq(hook_p hook, struct mbuf *m, meta_p meta) { const xxx_p xxxp = hook->node->private; int chan = -2; int dlci = -2; int error; if (hook->private) { dlci = ((struct XXX_hookinfo *) hook->private)->dlci; chan = ((struct XXX_hookinfo *) hook->private)->channel; if (dlci != -1) { /* If received on a DLCI hook process for this * channel and pass it to the downstream module. * Normally one would add a multiplexing header at * the front here */ /* M_PREPEND(....) ; */ /* mtod(m, xxxxxx)->dlci = dlci; */ error = ng_send_data(xxxp->downstream_hook.hook, m, meta); xxxp->packets_out++; } else { /* data came from the multiplexed link */ dlci = 1; /* get dlci from header */ /* madjust(....) *//* chop off header */ for (chan = 0; chan < XXX_NUM_DLCIS; chan++) if (xxxp->channel[chan].dlci == dlci) break; if (chan == XXX_NUM_DLCIS) { NG_FREE_DATA(m, meta); return (ENETUNREACH); } /* If we were called at splnet, use the following: * NG_SEND_DATA(error, otherhook, m, meta); if this * node is running at some SPL other than SPLNET * then you should use instead: error = * ng_queueit(otherhook, m, meta); m = NULL: meta = * NULL; this queues the data using the standard * NETISR system and schedules the data to be picked * up again once the system has moved to SPLNET and * the processing of the data can continue. after * these are run 'm' and 'meta' should be considered * as invalid and NG_SEND_DATA actually zaps them. */ NG_SEND_DATA(error, xxxp->channel[chan].hook, m, meta); xxxp->packets_in++; } } else { /* It's the debug hook, throw it away.. */ if (hook == xxxp->downstream_hook.hook) NG_FREE_DATA(m, meta); } return 0; } #if 0 /* * If this were a device node, the data may have been received in response * to some interrupt. * in which case it would probably look as follows: */ devintr() { meta_p meta = NULL; /* whatever metadata we might imagine goes * here */ /* get packet from device and send on */ m = MGET(blah blah) error = ng_queueit(upstream, m, meta); /* see note above in * xxx_rcvdata() */ } #endif /* 0 */ /* * 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_xxx_rmnode(node_p node) { const xxx_p privdata = node->private; node->flags |= NG_INVALID; ng_cutlinks(node); #ifndef PERSISTANT_NODE ng_unname(node); node->private = NULL; ng_unref(privdata->node); FREE(privdata, M_NETGRAPH); #else privdata->packets_in = 0; /* reset stats */ privdata->packets_out = 0; node->flags &= ~NG_INVALID; /* reset invalid flag */ #endif /* PERSISTANT_NODE */ 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_xxx_connect(hook_p hook) { /* be really amiable and just say "YUP that's OK by me! " */ return (0); } /* * Dook disconnection * * For this type, removal of the last link destroys the node */ static int ng_xxx_disconnect(hook_p hook) { if (hook->private) ((struct XXX_hookinfo *) (hook->private))->hook == NULL; if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } Index: head/sys/netgraph/ng_socket.c =================================================================== --- head/sys/netgraph/ng_socket.c (revision 52751) +++ head/sys/netgraph/ng_socket.c (revision 52752) @@ -1,957 +1,956 @@ /* * ng_socket.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_socket.c,v 1.25 1999/01/28 23:54:54 julian Exp $ + * $Whistle: ng_socket.c,v 1.28 1999/11/01 09:24:52 julian Exp $ */ /* * Netgraph socket nodes * * There are two types of netgraph sockets, control and data. * Control sockets have a netgraph node, but data sockets are * parasitic on control sockets, and have no node of their own. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef NOTYET #include #endif #include #include #include #include /* * It's Ascii-art time! * +-------------+ +-------------+ * |socket (ctl)| |socket (data)| * +-------------+ +-------------+ * ^ ^ * | | * v v * +-----------+ +-----------+ * |pcb (ctl)| |pcb (data)| * +-----------+ +-----------+ * ^ ^ * | | * v v * +--------------------------+ * | Socket type private | * | data | * +--------------------------+ * ^ * | * v * +----------------+ * | struct ng_node | * +----------------+ */ /* Netgraph node methods */ -static int ngs_constructor(node_p *nodep); -static int ngs_rcvmsg(node_p node, struct ng_mesg *msg, - const char *retaddr, struct ng_mesg **resp); -static int ngs_rmnode(node_p node); -static int ngs_newhook(node_p node, hook_p hook, const char *name); -static int ngs_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); +static ng_constructor_t ngs_constructor; +static ng_rcvmsg_t ngs_rcvmsg; +static ng_shutdown_t ngs_rmnode; +static ng_newhook_t ngs_newhook; +static ng_rcvdata_t ngs_rcvdata; /* Internal methods */ static int ng_attach_data(struct socket *so); static int ng_attach_cntl(struct socket *so); static int ng_attach_common(struct socket *so, int type); static void ng_detach_common(struct ngpcb *pcbp, int type); /*static int ng_internalize(struct mbuf *m, struct proc *p); */ static int ng_connect_data(struct sockaddr *nam, struct ngpcb *pcbp); static int ng_connect_cntl(struct sockaddr *nam, struct ngpcb *pcbp); static int ng_bind(struct sockaddr *nam, struct ngpcb *pcbp); static int ngs_mod_event(module_t mod, int event, void *data); static int ship_msg(struct ngpcb *pcbp, struct ng_mesg *msg, struct sockaddr_ng *addr); /* Netgraph type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_SOCKET_NODE_TYPE, ngs_mod_event, ngs_constructor, ngs_rcvmsg, ngs_rmnode, ngs_newhook, NULL, NULL, ngs_rcvdata, ngs_rcvdata, NULL, }; NETGRAPH_INIT(socket, &typestruct); /* Buffer space */ static u_long ngpdg_sendspace = 2 * 1024; /* really max datagram size */ static u_long ngpdg_recvspace = 20 * 1024; /* List of all sockets */ LIST_HEAD(, ngpcb) ngsocklist; #define sotongpcb(so) ((struct ngpcb *)so->so_pcb) /* If getting unexplained errors returned, set this to "Debugger("X"); */ #ifndef TRAP_ERROR #define TRAP_ERROR #endif /*************************************************************** Control sockets ***************************************************************/ static int ngc_attach(struct socket *so, int proto, struct proc *p) { struct ngpcb *const pcbp = sotongpcb(so); if (pcbp != NULL) return (EISCONN); return (ng_attach_cntl(so)); } static int ngc_detach(struct socket *so) { struct ngpcb *const pcbp = sotongpcb(so); if (pcbp == NULL) return (EINVAL); ng_detach_common(pcbp, NG_CONTROL); return (0); } static int ngc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, struct mbuf *control, struct proc *p) { struct ngpcb *const pcbp = sotongpcb(so); struct sockaddr_ng *const sap = (struct sockaddr_ng *) addr; struct ng_mesg *resp; struct mbuf *m0; char *msg, *path = NULL; int len, error = 0; if (pcbp == NULL) { error = EINVAL; goto release; } #ifdef NOTYET if (control && (error = ng_internalize(control, p))) { if (pcbp->sockdata == NULL) { error = ENOTCONN; goto release; } } #else /* NOTYET */ if (control) { error = EINVAL; goto release; } #endif /* NOTYET */ /* Require destination as there may be >= 1 hooks on this node */ if (addr == NULL) { error = EDESTADDRREQ; goto release; } /* Allocate an expendable buffer for the path, chop off * the sockaddr header, and make sure it's NUL terminated */ len = sap->sg_len - 2; MALLOC(path, char *, len + 1, M_NETGRAPH, M_WAITOK); if (path == NULL) { error = ENOMEM; goto release; } bcopy(sap->sg_data, path, len); path[len] = '\0'; /* Move the actual message out of mbufs into a linear buffer. * Start by adding up the size of the data. (could use mh_len?) */ for (len = 0, m0 = m; m0 != NULL; m0 = m0->m_next) len += m0->m_len; /* Move the data into a linear buffer as well. Messages are not * delivered in mbufs. */ MALLOC(msg, char *, len + 1, M_NETGRAPH, M_WAITOK); if (msg == NULL) { error = ENOMEM; goto release; } m_copydata(m, 0, len, msg); /* The callee will free the msg when done. The addr is our business. */ error = ng_send_msg(pcbp->sockdata->node, (struct ng_mesg *) msg, path, &resp); /* If the callee responded with a synchronous response, then put it * back on the receive side of the socket; sap is source address. */ if (error == 0 && resp != NULL) error = ship_msg(pcbp, resp, sap); release: if (path != NULL) FREE(path, M_NETGRAPH); if (control != NULL) m_freem(control); if (m != NULL) m_freem(m); return (error); } static int ngc_bind(struct socket *so, struct sockaddr *nam, struct proc *p) { struct ngpcb *const pcbp = sotongpcb(so); if (pcbp == 0) return (EINVAL); return (ng_bind(nam, pcbp)); } static int ngc_connect(struct socket *so, struct sockaddr *nam, struct proc *p) { struct ngpcb *const pcbp = sotongpcb(so); if (pcbp == 0) return (EINVAL); return (ng_connect_cntl(nam, pcbp)); } /*************************************************************** Data sockets ***************************************************************/ static int ngd_attach(struct socket *so, int proto, struct proc *p) { struct ngpcb *const pcbp = sotongpcb(so); if (pcbp != NULL) return (EISCONN); return (ng_attach_data(so)); } static int ngd_detach(struct socket *so) { struct ngpcb *const pcbp = sotongpcb(so); if (pcbp == NULL) return (EINVAL); ng_detach_common(pcbp, NG_DATA); return (0); } static int ngd_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, struct mbuf *control, struct proc *p) { struct ngpcb *const pcbp = sotongpcb(so); struct sockaddr_ng *const sap = (struct sockaddr_ng *) addr; char *hookname = NULL; meta_p mp = NULL; int len, error; hook_p hook; if ((pcbp == NULL) || (control != NULL)) { error = EINVAL; goto release; } if (pcbp->sockdata == NULL) { error = ENOTCONN; goto release; } if (addr == NULL) { error = EDESTADDRREQ; goto release; } /* Allocate an expendable buffer for the hook name, chop off * the sockaddr header, and make sure it's NUL terminated */ len = sap->sg_len - 2; MALLOC(hookname, char *, len + 1, M_NETGRAPH, M_WAITOK); if (hookname == NULL) { error = ENOMEM; goto release; } bcopy(sap->sg_data, hookname, len); hookname[len] = '\0'; /* Find the correct hook from 'hookname' */ LIST_FOREACH(hook, &pcbp->sockdata->node->hooks, hooks) { if (strcmp(hookname, hook->name) == 0) break; } /* Send data (OK if hook is NULL) */ NG_SEND_DATA(error, hook, m, mp); /* makes m NULL */ release: if (hookname != NULL) FREE(hookname, M_NETGRAPH); if (control != NULL) m_freem(control); if (m != NULL) m_freem(m); return (error); } static int ngd_connect(struct socket *so, struct sockaddr *nam, struct proc *p) { struct ngpcb *const pcbp = sotongpcb(so); if (pcbp == 0) return (EINVAL); return (ng_connect_data(nam, pcbp)); } /* * Used for both data and control sockets */ static int ng_setsockaddr(struct socket *so, struct sockaddr **addr) { struct ngpcb *const pcbp = sotongpcb(so); struct sockaddr *sa; int namelen; if (pcbp == 0) return (EINVAL); if (pcbp->sockdata->node->name != NULL) { namelen = strlen(pcbp->sockdata->node->name) + 3; MALLOC(sa, struct sockaddr *, namelen, M_SONAME, M_WAITOK); if (sa == NULL) return (ENOMEM); sa->sa_family = AF_NETGRAPH; sa->sa_len = namelen; strcpy(sa->sa_data, pcbp->sockdata->node->name); *addr = sa; } else *addr = NULL; /* XXX check this makes sense */ return (0); } /* * Attach a socket to it's protocol specific partner. * For a control socket, actually create a netgraph node and attach * to it as well. */ static int ng_attach_cntl(struct socket *so) { struct ngsock *privdata; struct ngpcb *pcbp; int error; /* Setup protocol control block */ if ((error = ng_attach_common(so, NG_CONTROL)) != 0) return (error); pcbp = (struct ngpcb *) so->so_pcb; /* Allocate node private info */ MALLOC(privdata, struct ngsock *, sizeof(*privdata), M_NETGRAPH, M_WAITOK); if (privdata == NULL) { ng_detach_common(pcbp, NG_CONTROL); return (ENOMEM); } bzero(privdata, sizeof(*privdata)); /* Make the generic node components */ if ((error = ng_make_node_common(&typestruct, &privdata->node)) != 0) { FREE(privdata, M_NETGRAPH); ng_detach_common(pcbp, NG_CONTROL); return (error); } privdata->node->private = privdata; /* Link the pcb and the node private data */ privdata->ctlsock = pcbp; pcbp->sockdata = privdata; privdata->refs++; return (0); } static int ng_attach_data(struct socket *so) { return(ng_attach_common(so, NG_DATA)); } /* * Set up a socket protocol control block. * This code is shared between control and data sockets. */ static int ng_attach_common(struct socket *so, int type) { struct ngpcb *pcbp; int error; /* Standard socket setup stuff */ error = soreserve(so, ngpdg_sendspace, ngpdg_recvspace); if (error) return (error); /* Allocate the pcb */ MALLOC(pcbp, struct ngpcb *, sizeof(*pcbp), M_PCB, M_WAITOK); if (pcbp == NULL) return (ENOMEM); bzero(pcbp, sizeof(*pcbp)); pcbp->type = type; /* Link the pcb and the socket */ so->so_pcb = (caddr_t) pcbp; pcbp->ng_socket = so; /* Add the socket to linked list */ LIST_INSERT_HEAD(&ngsocklist, pcbp, socks); return (0); } /* * Disassociate the socket from it's protocol specific * partner. If it's attached to a node's private data structure, * then unlink from that too. If we were the last socket attached to it, * then shut down the entire node. Shared code for control and data sockets. */ static void ng_detach_common(struct ngpcb *pcbp, int which) { struct ngsock *sockdata; if (pcbp->sockdata) { sockdata = pcbp->sockdata; pcbp->sockdata = NULL; switch (which) { case NG_CONTROL: sockdata->ctlsock = NULL; break; case NG_DATA: sockdata->datasock = NULL; break; default: panic(__FUNCTION__); } if ((--sockdata->refs == 0) && (sockdata->node != NULL)) ng_rmnode(sockdata->node); } pcbp->ng_socket->so_pcb = NULL; pcbp->ng_socket = NULL; LIST_REMOVE(pcbp, socks); FREE(pcbp, M_PCB); } #ifdef NOTYET /* * File descriptors can be passed into a AF_NETGRAPH socket. * Note, that file descriptors cannot be passed OUT. * Only character device descriptors are accepted. * Character devices are useful to connect a graph to a device, * which after all is the purpose of this whole system. */ static int ng_internalize(struct mbuf *control, struct proc *p) { struct filedesc *fdp = p->p_fd; struct cmsghdr *cm = mtod(control, struct cmsghdr *); struct file *fp; struct vnode *vn; int oldfds; int fd; if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET || cm->cmsg_len != control->m_len) { TRAP_ERROR; return (EINVAL); } /* Check there is only one FD. XXX what would more than one signify? */ oldfds = (cm->cmsg_len - sizeof(*cm)) / sizeof(int); if (oldfds != 1) { TRAP_ERROR; return (EINVAL); } /* Check that the FD given is legit. and change it to a pointer to a * struct file. */ fd = *(int *) (cm + 1); if ((unsigned) fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) { return (EBADF); } /* Depending on what kind of resource it is, act differently. For * devices, we treat it as a file. For a AF_NETGRAPH socket, * shortcut straight to the node. */ switch (fp->f_type) { case DTYPE_VNODE: vn = (struct vnode *) fp->f_data; if (vn && (vn->v_type == VCHR)) { /* for a VCHR, actually reference the FILE */ fp->f_count++; /* XXX then what :) */ /* how to pass on to other modules? */ } else { TRAP_ERROR; return (EINVAL); } break; default: TRAP_ERROR; return (EINVAL); } return (0); } #endif /* NOTYET */ /* * Connect the data socket to a named control socket node. */ static int ng_connect_data(struct sockaddr *nam, struct ngpcb *pcbp) { struct sockaddr_ng *sap; node_p farnode; struct ngsock *sockdata; int error; /* If we are already connected, don't do it again */ if (pcbp->sockdata != NULL) return (EISCONN); /* Find the target (victim) and check it doesn't already have a data * socket. Also check it is a 'socket' type node. */ sap = (struct sockaddr_ng *) nam; if ((error = ng_path2node(NULL, sap->sg_data, &farnode, NULL))) return (error); if (strcmp(farnode->type->name, NG_SOCKET_NODE_TYPE) != 0) return (EINVAL); sockdata = farnode->private; if (sockdata->datasock != NULL) return (EADDRINUSE); /* Link the PCB and the private data struct. and note the extra * reference */ sockdata->datasock = pcbp; pcbp->sockdata = sockdata; sockdata->refs++; return (0); } /* * Connect the existing control socket node to a named node:hook. * The hook we use on this end is the same name as the remote node name. */ static int ng_connect_cntl(struct sockaddr *nam, struct ngpcb *pcbp) { struct ngsock *const sockdata = pcbp->sockdata; struct sockaddr_ng *sap; char *node, *hook; node_p farnode; int rtn, error; sap = (struct sockaddr_ng *) nam; rtn = ng_path_parse(sap->sg_data, &node, NULL, &hook); if (rtn < 0 || node == NULL || hook == NULL) { TRAP_ERROR; return (EINVAL); } farnode = ng_findname(sockdata->node, node); if (farnode == NULL) { TRAP_ERROR; return (EADDRNOTAVAIL); } /* Connect, using a hook name the same as the far node name. */ error = ng_con_nodes(sockdata->node, node, farnode, hook); return error; } /* * Binding a socket means giving the corresponding node a name */ static int ng_bind(struct sockaddr *nam, struct ngpcb *pcbp) { struct ngsock *const sockdata = pcbp->sockdata; struct sockaddr_ng *const sap = (struct sockaddr_ng *) nam; if (sockdata == NULL) { TRAP_ERROR; return (EINVAL); } if (sap->sg_len < 3 || sap->sg_data[sap->sg_len - 3] != '\0') { TRAP_ERROR; return (EINVAL); } return (ng_name_node(sockdata->node, sap->sg_data)); } /* * Take a message and pass it up to the control socket associated * with the node. */ static int ship_msg(struct ngpcb *pcbp, struct ng_mesg *msg, struct sockaddr_ng *addr) { struct socket *const so = pcbp->ng_socket; struct mbuf *mdata; int msglen; /* Copy the message itself into an mbuf chain */ msglen = sizeof(struct ng_mesg) + msg->header.arglen; mdata = m_devget((caddr_t) msg, msglen, 0, NULL, NULL); /* Here we free the message, as we are the end of the line. * We need to do that regardless of whether we got mbufs. */ FREE(msg, M_NETGRAPH); if (mdata == NULL) { TRAP_ERROR; return (ENOBUFS); } /* Send it up to the socket */ if (sbappendaddr(&so->so_rcv, (struct sockaddr *) addr, mdata, NULL) == 0) { TRAP_ERROR; m_freem(mdata); return (ENOBUFS); } sorwakeup(so); return (0); } /* * You can only create new nodes from the socket end of things. */ static int ngs_constructor(node_p *nodep) { return (EINVAL); } /* * We allow any hook to be connected to the node. * There is no per-hook private information though. */ static int ngs_newhook(node_p node, hook_p hook, const char *name) { hook->private = node->private; return (0); } /* * Incoming messages get passed up to the control socket. */ static int ngs_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp) { struct ngsock *const sockdata = node->private; struct ngpcb *const pcbp = sockdata->ctlsock; struct sockaddr_ng *addr; int addrlen; int error = 0; /* Only allow mesgs to be passed if we have the control socket. * Data sockets can only support the generic messages. */ if (pcbp == NULL) { TRAP_ERROR; return (EINVAL); } /* Get the return address into a sockaddr */ if ((retaddr == NULL) || (*retaddr == '\0')) retaddr = ""; addrlen = strlen(retaddr); MALLOC(addr, struct sockaddr_ng *, addrlen + 4, M_NETGRAPH, M_NOWAIT); if (addr == NULL) { TRAP_ERROR; return (ENOMEM); } addr->sg_len = addrlen + 3; addr->sg_family = AF_NETGRAPH; bcopy(retaddr, addr->sg_data, addrlen); addr->sg_data[addrlen] = '\0'; /* Send it up */ error = ship_msg(pcbp, msg, addr); FREE(addr, M_NETGRAPH); return (error); } /* * Receive data on a hook */ static int ngs_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { struct ngsock *const sockdata = hook->node->private; struct ngpcb *const pcbp = sockdata->datasock; struct socket *so; struct sockaddr_ng *addr; char *addrbuf[NG_HOOKLEN + 1 + 4]; int addrlen; /* If there is no data socket, black-hole it */ if (pcbp == NULL) { NG_FREE_DATA(m, meta); return (0); } so = pcbp->ng_socket; /* Get the return address into a sockaddr. */ addrlen = strlen(hook->name); /* <= NG_HOOKLEN */ addr = (struct sockaddr_ng *) addrbuf; addr->sg_len = addrlen + 3; addr->sg_family = AF_NETGRAPH; bcopy(hook->name, addr->sg_data, addrlen); addr->sg_data[addrlen] = '\0'; /* We have no use for the meta data, free/clear it now. */ NG_FREE_META(meta); /* Try to tell the socket which hook it came in on */ if (sbappendaddr(&so->so_rcv, (struct sockaddr *) addr, m, NULL) == 0) { m_freem(m); TRAP_ERROR; return (ENOBUFS); } sorwakeup(so); return (0); } /* * Do local shutdown processing. * In this case, that involves making sure the socket * knows we should be shutting down. */ static int ngs_rmnode(node_p node) { struct ngsock *const sockdata = node->private; struct ngpcb *const dpcbp = sockdata->datasock; struct ngpcb *const pcbp = sockdata->ctlsock; ng_cutlinks(node); ng_unname(node); if (dpcbp != NULL) { soisdisconnected(dpcbp->ng_socket); dpcbp->sockdata = NULL; sockdata->datasock = NULL; sockdata->refs--; } if (pcbp != NULL) { soisdisconnected(pcbp->ng_socket); pcbp->sockdata = NULL; sockdata->ctlsock = NULL; sockdata->refs--; } node->private = NULL; ng_unref(node); FREE(sockdata, M_NETGRAPH); return (0); } /* * Control and data socket type descriptors */ static struct pr_usrreqs ngc_usrreqs = { NULL, /* abort */ pru_accept_notsupp, ngc_attach, ngc_bind, ngc_connect, pru_connect2_notsupp, pru_control_notsupp, ngc_detach, NULL, /* disconnect */ pru_listen_notsupp, NULL, /* setpeeraddr */ pru_rcvd_notsupp, pru_rcvoob_notsupp, ngc_send, pru_sense_null, NULL, /* shutdown */ ng_setsockaddr, sosend, soreceive, sopoll }; static struct pr_usrreqs ngd_usrreqs = { NULL, /* abort */ pru_accept_notsupp, ngd_attach, NULL, /* bind */ ngd_connect, pru_connect2_notsupp, pru_control_notsupp, ngd_detach, NULL, /* disconnect */ pru_listen_notsupp, NULL, /* setpeeraddr */ pru_rcvd_notsupp, pru_rcvoob_notsupp, ngd_send, pru_sense_null, NULL, /* shutdown */ ng_setsockaddr, sosend, soreceive, sopoll }; /* * Definitions of protocols supported in the NETGRAPH domain. */ extern struct domain ngdomain; /* stop compiler warnings */ static struct protosw ngsw[] = { { SOCK_DGRAM, &ngdomain, NG_CONTROL, PR_ATOMIC | PR_ADDR /* | PR_RIGHTS */, 0, 0, 0, 0, NULL, 0, 0, 0, 0, &ngc_usrreqs }, { SOCK_DGRAM, &ngdomain, NG_DATA, PR_ATOMIC | PR_ADDR, 0, 0, 0, 0, NULL, 0, 0, 0, 0, &ngd_usrreqs } }; struct domain ngdomain = { AF_NETGRAPH, "netgraph", 0, NULL, NULL, ngsw, &ngsw[sizeof(ngsw) / sizeof(ngsw[0])], 0, NULL, 0, 0 }; /* * Handle loading and unloading for this node type * This is to handle auxiliary linkages (e.g protocol domain addition). */ static int ngs_mod_event(module_t mod, int event, void *data) { int error = 0; switch (event) { case MOD_LOAD: /* Register protocol domain */ net_add_domain(&ngdomain); break; case MOD_UNLOAD: /* Insure there are no open netgraph sockets */ if (!LIST_EMPTY(&ngsocklist)) { error = EBUSY; break; } #ifdef NOTYET /* Unregister protocol domain XXX can't do this yet.. */ if ((error = net_rm_domain(&ngdomain)) != 0) break; #else error = EBUSY; #endif break; default: error = EOPNOTSUPP; break; } return (error); } SYSCTL_NODE(_net, AF_NETGRAPH, graph, CTLFLAG_RW, 0, "netgraph Family"); SYSCTL_INT(_net_graph, OID_AUTO, family, CTLFLAG_RD, 0, AF_NETGRAPH, ""); SYSCTL_NODE(_net_graph, OID_AUTO, data, CTLFLAG_RW, 0, "DATA"); SYSCTL_INT(_net_graph_data, OID_AUTO, proto, CTLFLAG_RD, 0, NG_DATA, ""); SYSCTL_NODE(_net_graph, OID_AUTO, control, CTLFLAG_RW, 0, "CONTROL"); SYSCTL_INT(_net_graph_control, OID_AUTO, proto, CTLFLAG_RD, 0, NG_CONTROL, ""); Index: head/sys/netgraph/ng_tee.c =================================================================== --- head/sys/netgraph/ng_tee.c (revision 52751) +++ head/sys/netgraph/ng_tee.c (revision 52752) @@ -1,268 +1,267 @@ /* * ng_tee.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_tee.c,v 1.16 1999/01/28 23:54:54 julian Exp $ + * $Whistle: ng_tee.c,v 1.18 1999/11/01 09:24:52 julian Exp $ */ /* * This node is like the tee(1) command and is useful for ``snooping.'' * It has 4 hooks: left, right, left2right, and right2left. Data * entering from the right is passed to the left and duplicated on * right2left, and data entering from the left is passed to the right * and duplicated on left2right. Data entering from left2right is * sent to right, and data from right2left to left. */ #include #include #include #include #include #include #include #include #include /* Per hook info */ struct hookinfo { hook_p hook; int bytes; int packets; int flags; }; /* Per node info */ struct privdata { node_p node; int flags; struct hookinfo left; struct hookinfo right; struct hookinfo left2right; struct hookinfo right2left; }; typedef struct privdata *sc_p; /* Netgraph methods */ -static int ngt_constructor(node_p *node); -static int ngt_rcvmsg(node_p node, struct ng_mesg *msg, - const char *retaddr, struct ng_mesg **resp); -static int ngt_rmnode(node_p node); -static int ngt_newhook(node_p node, hook_p hook, const char *name); -static int ngt_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); -static int ngt_disconnect(hook_p hook); +static ng_constructor_t ngt_constructor; +static ng_rcvmsg_t ngt_rcvmsg; +static ng_shutdown_t ngt_rmnode; +static ng_newhook_t ngt_newhook; +static ng_rcvdata_t ngt_rcvdata; +static ng_disconnect_t ngt_disconnect; /* Netgraph type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_TEE_NODE_TYPE, NULL, ngt_constructor, ngt_rcvmsg, ngt_rmnode, ngt_newhook, NULL, NULL, ngt_rcvdata, ngt_rcvdata, ngt_disconnect }; NETGRAPH_INIT(tee, &typestruct); /* * Node constructor */ static int ngt_constructor(node_p *nodep) { sc_p privdata; int error = 0; MALLOC(privdata, sc_p, sizeof(*privdata), M_NETGRAPH, M_WAITOK); if (privdata == NULL) return (ENOMEM); bzero(privdata, sizeof(*privdata)); if ((error = ng_make_node_common(&typestruct, nodep))) { FREE(privdata, M_NETGRAPH); return (error); } (*nodep)->private = privdata; privdata->node = *nodep; return (0); } /* * Add a hook */ static int ngt_newhook(node_p node, hook_p hook, const char *name) { const sc_p sc = node->private; if (strcmp(name, NG_TEE_HOOK_RIGHT) == 0) { sc->right.hook = hook; sc->right.bytes = 0; sc->right.packets = 0; hook->private = &sc->right; } else if (strcmp(name, NG_TEE_HOOK_LEFT) == 0) { sc->left.hook = hook; sc->left.bytes = 0; sc->left.packets = 0; hook->private = &sc->left; } else if (strcmp(name, NG_TEE_HOOK_RIGHT2LEFT) == 0) { sc->right2left.hook = hook; sc->right2left.bytes = 0; sc->right2left.packets = 0; hook->private = &sc->right2left; } else if (strcmp(name, NG_TEE_HOOK_LEFT2RIGHT) == 0) { sc->left2right.hook = hook; sc->left2right.bytes = 0; sc->left2right.packets = 0; hook->private = &sc->left2right; } else return (EINVAL); return (0); } /* * We don't support any type-specific messages */ static int ngt_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp) { FREE(msg, M_NETGRAPH); return (EINVAL); } /* * Receive data on a hook * * If data comes in the right link send a copy out right2left, and then * send the original onwards out through the left link. * Do the opposite for data coming in from the left link. * Data coming in right2left or left2right is forwarded * on through the appropriate destination hook as if it had come * from the other side. */ static int ngt_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { const sc_p sc = hook->node->private; struct hookinfo *hi; struct hookinfo *dest; struct hookinfo *dup; struct mbuf *mdup; int error = 0; if ((hi = hook->private) != NULL) { if (hi == &sc->left) { dup = &sc->left2right; dest = &sc->right; } else if (hi == &sc->right) { dup = &sc->right2left; dest = &sc->left; } else if (hi == &sc->right2left) { dup = NULL; dest = &sc->left; } else if (hi == &sc->left2right) { dup = NULL; dest = &sc->right; } else goto out; if (dup) { mdup = m_copypacket(m, M_NOWAIT); if (mdup) { /* XXX should we duplicate meta? */ /* for now no. */ void *x = NULL; NG_SEND_DATA(error, dup->hook, mdup, x); } } NG_SEND_DATA(error, dest->hook, m, meta); } out: NG_FREE_DATA(m, meta); return (error); } /* * Shutdown processing * * This is tricky. If we have both a left and right hook, then we * probably want to extricate ourselves and leave the two peers * still linked to each other. Otherwise we should just shut down as * a normal node would. * * To keep the scope of info correct the routine to "extract" a node * from two links is in ng_base.c. */ static int ngt_rmnode(node_p node) { const sc_p privdata = node->private; node->flags |= NG_INVALID; if (privdata->left.hook && privdata->right.hook) ng_bypass(privdata->left.hook, privdata->right.hook); ng_cutlinks(node); ng_unname(node); node->private = NULL; ng_unref(privdata->node); FREE(privdata, M_NETGRAPH); return (0); } /* * Hook disconnection */ static int ngt_disconnect(hook_p hook) { struct hookinfo *hi; if ((hi = hook->private) != NULL) hi->hook = NULL; if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } Index: head/sys/netgraph/ng_tty.c =================================================================== --- head/sys/netgraph/ng_tty.c (revision 52751) +++ head/sys/netgraph/ng_tty.c (revision 52752) @@ -1,703 +1,702 @@ /* * ng_tty.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_tty.c,v 1.18 1999/01/28 23:54:54 julian Exp $ + * $Whistle: ng_tty.c,v 1.21 1999/11/01 09:24:52 julian Exp $ */ /* * This file implements a terminal line discipline that is also a * netgraph node. Installing this line discipline on a terminal device * instantiates a new netgraph node of this type, which allows access * to the device via the "hook" hook of the node. * * Once the line discipline is installed, you can find out the name * of the corresponding netgraph node via a NGIOCGINFO ioctl(). * * Incoming characters are delievered to the hook one at a time, each * in its own mbuf. You may optionally define a ``hotchar,'' which causes * incoming characters to be buffered up until either the hotchar is * seen or the mbuf is full (MHLEN bytes). Then all buffered characters * are immediately delivered. * * NOTE: This node operates at spltty(). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __i386__ /* fiddle with the spl locking */ #include #include #endif /* Misc defs */ #define MAX_MBUFQ 3 /* Max number of queued mbufs */ #define NGT_HIWATER 400 /* High water mark on output */ /* Per-node private info */ struct ngt_sc { struct tty *tp; /* Terminal device */ node_p node; /* Netgraph node */ hook_p hook; /* Netgraph hook */ struct mbuf *m; /* Incoming data buffer */ struct mbuf *qhead, **qtail; /* Queue of outgoing mbuf's */ short qlen; /* Length of queue */ short hotchar; /* Hotchar, or -1 if none */ u_int flags; /* Flags */ struct callout_handle chand; /* See man timeout(9) */ }; typedef struct ngt_sc *sc_p; /* Flags */ #define FLG_TIMEOUT 0x0001 /* A timeout is pending */ #define FLG_DEBUG 0x0002 /* Debugging */ #ifdef DIAGNOSTICS #define QUEUECHECK(sc) \ do { \ struct mbuf **mp; \ int k; \ \ for (k = 0, mp = &sc->qhead; \ k <= MAX_MBUFQ && *mp; \ k++, mp = &(*mp)->m_nextpkt); \ if (k != sc->qlen || k > MAX_MBUFQ || *mp || mp != sc->qtail) \ panic(__FUNCTION__ ": queue"); \ } while (0) #else #define QUEUECHECK(sc) do {} while (0) #endif /* Line discipline methods */ static int ngt_open(dev_t dev, struct tty *tp); static int ngt_close(struct tty *tp, int flag); static int ngt_read(struct tty *tp, struct uio *uio, int flag); static int ngt_write(struct tty *tp, struct uio *uio, int flag); static int ngt_tioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct proc *); static int ngt_input(int c, struct tty *tp); static int ngt_start(struct tty *tp); /* Netgraph methods */ -static int ngt_constructor(node_p *node); -static int ngt_rcvmsg(node_p node, struct ng_mesg *msg, - const char *retaddr, struct ng_mesg **resp); -static int ngt_shutdown(node_p node); -static int ngt_newhook(node_p node, hook_p hook, const char *name); -static int ngt_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); -static int ngt_disconnect(hook_p hook); +static ng_constructor_t ngt_constructor; +static ng_rcvmsg_t ngt_rcvmsg; +static ng_shutdown_t ngt_shutdown; +static ng_newhook_t ngt_newhook; +static ng_rcvdata_t ngt_rcvdata; +static ng_disconnect_t ngt_disconnect; static int ngt_mod_event(module_t mod, int event, void *data); /* Other stuff */ static void ngt_timeout(void *arg); #define ERROUT(x) do { error = (x); goto done; } while (0) /* Line discipline descriptor */ static struct linesw ngt_disc = { ngt_open, ngt_close, ngt_read, ngt_write, ngt_tioctl, ngt_input, ngt_start, ttymodem, NG_TTY_DFL_HOTCHAR /* XXX can't change this in serial driver */ }; /* Netgraph node type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_TTY_NODE_TYPE, ngt_mod_event, ngt_constructor, ngt_rcvmsg, ngt_shutdown, ngt_newhook, NULL, NULL, ngt_rcvdata, ngt_rcvdata, ngt_disconnect, }; NETGRAPH_INIT(tty, &typestruct); static int ngt_unit; static int ngt_nodeop_ok; /* OK to create/remove node */ static int ngt_ldisc; /****************************************************************** LINE DISCIPLINE METHODS ******************************************************************/ /* * Set our line discipline on the tty. * Called from device open routine or ttioctl() at >= splsofttty() */ static int ngt_open(dev_t dev, struct tty *tp) { struct proc *const p = curproc; /* XXX */ char name[sizeof(NG_TTY_NODE_TYPE) + 8]; sc_p sc; int s, error; /* Super-user only */ if ((error = suser(p))) return (error); s = splnet(); (void) spltty(); /* XXX is this necessary? */ /* Already installed? */ if (tp->t_line == NETGRAPHDISC) { sc = (sc_p) tp->t_sc; if (sc != NULL && sc->tp == tp) goto done; } /* Initialize private struct */ MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_WAITOK); if (sc == NULL) { error = ENOMEM; goto done; } bzero(sc, sizeof(*sc)); sc->tp = tp; sc->hotchar = NG_TTY_DFL_HOTCHAR; sc->qtail = &sc->qhead; QUEUECHECK(sc); callout_handle_init(&sc->chand); /* Setup netgraph node */ ngt_nodeop_ok = 1; error = ng_make_node_common(&typestruct, &sc->node); ngt_nodeop_ok = 0; if (error) { FREE(sc, M_NETGRAPH); goto done; } sprintf(name, "%s%d", typestruct.name, ngt_unit++); /* Set back pointers */ sc->node->private = sc; tp->t_sc = (caddr_t) sc; /* Assign node its name */ if ((error = ng_name_node(sc->node, name))) { log(LOG_ERR, "%s: node name exists?\n", name); ngt_nodeop_ok = 1; ng_rmnode(sc->node); ngt_nodeop_ok = 0; goto done; } /* * Pre-allocate cblocks to the an appropriate amount. * I'm not sure what is appropriate. */ ttyflush(tp, FREAD | FWRITE); clist_alloc_cblocks(&tp->t_canq, 0, 0); clist_alloc_cblocks(&tp->t_rawq, 0, 0); clist_alloc_cblocks(&tp->t_outq, MLEN + NGT_HIWATER, MLEN + NGT_HIWATER); done: /* Done */ splx(s); return (error); } /* * Line specific close routine, called from device close routine * and from ttioctl at >= splsofttty(). This causes the node to * be destroyed as well. */ static int ngt_close(struct tty *tp, int flag) { const sc_p sc = (sc_p) tp->t_sc; int s; s = spltty(); ttyflush(tp, FREAD | FWRITE); clist_free_cblocks(&tp->t_outq); tp->t_line = 0; if (sc != NULL) { if (sc->flags & FLG_TIMEOUT) { untimeout(ngt_timeout, sc, sc->chand); sc->flags &= ~FLG_TIMEOUT; } ngt_nodeop_ok = 1; ng_rmnode(sc->node); ngt_nodeop_ok = 0; tp->t_sc = NULL; } splx(s); return (0); } /* * Once the device has been turned into a node, we don't allow reading. */ static int ngt_read(struct tty *tp, struct uio *uio, int flag) { return (EIO); } /* * Once the device has been turned into a node, we don't allow writing. */ static int ngt_write(struct tty *tp, struct uio *uio, int flag) { return (EIO); } /* * We implement the NGIOCGINFO ioctl() defined in ng_message.h. */ static int ngt_tioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct proc *p) { const sc_p sc = (sc_p) tp->t_sc; int s, error = 0; s = spltty(); switch (cmd) { case NGIOCGINFO: { struct nodeinfo *const ni = (struct nodeinfo *) data; const node_p node = sc->node; bzero(ni, sizeof(*ni)); if (node->name) strncpy(ni->name, node->name, sizeof(ni->name) - 1); strncpy(ni->type, node->type->name, sizeof(ni->type) - 1); ni->id = (u_int32_t) node; ni->hooks = node->numhooks; break; } default: ERROUT(ENOIOCTL); } done: splx(s); return (error); } /* * Receive data coming from the device. We get one character at * a time, which is kindof silly. * Only guaranteed to be at splsofttty() or spltty(). */ static int ngt_input(int c, struct tty *tp) { const sc_p sc = (sc_p) tp->t_sc; const node_p node = sc->node; struct mbuf *m; int s, error = 0; if (!sc || tp != sc->tp) return (0); s = spltty(); if (!sc->hook) ERROUT(0); /* Check for error conditions */ if ((tp->t_state & TS_CONNECTED) == 0) { if (sc->flags & FLG_DEBUG) log(LOG_DEBUG, "%s: no carrier\n", node->name); ERROUT(0); } if (c & TTY_ERRORMASK) { /* framing error or overrun on this char */ if (sc->flags & FLG_DEBUG) log(LOG_DEBUG, "%s: line error %x\n", node->name, c & TTY_ERRORMASK); ERROUT(0); } c &= TTY_CHARMASK; /* Get a new header mbuf if we need one */ if (!(m = sc->m)) { MGETHDR(m, M_DONTWAIT, MT_DATA); if (!m) { if (sc->flags & FLG_DEBUG) log(LOG_ERR, "%s: can't get mbuf\n", node->name); ERROUT(ENOBUFS); } m->m_len = 0; m->m_pkthdr.len = 0; sc->m = m; } /* Add char to mbuf */ *mtod(m, u_char *) = c; m->m_data++; m->m_len++; m->m_pkthdr.len++; /* Ship off mbuf if it's time */ if (sc->hotchar == -1 || c == sc->hotchar || m->m_len >= MHLEN) { m->m_data = m->m_pktdat; error = ng_queue_data(sc->hook, m, NULL); sc->m = NULL; } done: splx(s); return (error); } /* * This is called when the device driver is ready for more output. * Called from tty system at splsofttty() or spltty(). * Also call from ngt_rcv_data() when a new mbuf is available for output. */ static int ngt_start(struct tty *tp) { const sc_p sc = (sc_p) tp->t_sc; int s; s = spltty(); while (tp->t_outq.c_cc < NGT_HIWATER) { /* XXX 2.2 specific ? */ struct mbuf *m = sc->qhead; /* Remove first mbuf from queue */ if (!m) break; if ((sc->qhead = m->m_nextpkt) == NULL) sc->qtail = &sc->qhead; sc->qlen--; QUEUECHECK(sc); /* Send as much of it as possible */ while (m) { struct mbuf *m2; int sent; sent = m->m_len - b_to_q(mtod(m, u_char *), m->m_len, &tp->t_outq); m->m_data += sent; m->m_len -= sent; if (m->m_len > 0) break; /* device can't take no more */ MFREE(m, m2); m = m2; } /* Put remainder of mbuf chain (if any) back on queue */ if (m) { m->m_nextpkt = sc->qhead; sc->qhead = m; if (sc->qtail == &sc->qhead) sc->qtail = &m->m_nextpkt; sc->qlen++; QUEUECHECK(sc); break; } } /* Call output process whether or not there is any output. We are * being called in lieu of ttstart and must do what it would. */ if (tp->t_oproc != NULL) (*tp->t_oproc) (tp); /* This timeout is needed for operation on a pseudo-tty, because the * pty code doesn't call pppstart after it has drained the t_outq. */ if (sc->qhead && (sc->flags & FLG_TIMEOUT) == 0) { sc->chand = timeout(ngt_timeout, sc, 1); sc->flags |= FLG_TIMEOUT; } splx(s); return (0); } /* * We still have data to output to the device, so try sending more. */ static void ngt_timeout(void *arg) { const sc_p sc = (sc_p) arg; int s; s = spltty(); sc->flags &= ~FLG_TIMEOUT; ngt_start(sc->tp); splx(s); } /****************************************************************** NETGRAPH NODE METHODS ******************************************************************/ /* * Initialize a new node of this type. * * We only allow nodes to be created as a result of setting * the line discipline on a tty, so always return an error if not. */ static int ngt_constructor(node_p *nodep) { if (!ngt_nodeop_ok) return (EOPNOTSUPP); return (ng_make_node_common(&typestruct, nodep)); } /* * Add a new hook. There can only be one. */ static int ngt_newhook(node_p node, hook_p hook, const char *name) { const sc_p sc = node->private; int s, error = 0; if (strcmp(name, NG_TTY_HOOK)) return (EINVAL); s = spltty(); if (sc->hook) ERROUT(EISCONN); sc->hook = hook; done: splx(s); return (error); } /* * Disconnect the hook */ static int ngt_disconnect(hook_p hook) { const sc_p sc = hook->node->private; int s; s = spltty(); if (hook != sc->hook) panic(__FUNCTION__); sc->hook = NULL; m_freem(sc->m); sc->m = NULL; splx(s); return (0); } /* * Remove this node. The does the netgraph portion of the shutdown. * This should only be called indirectly from ngt_close(). */ static int ngt_shutdown(node_p node) { const sc_p sc = node->private; if (!ngt_nodeop_ok) return (EOPNOTSUPP); ng_unname(node); ng_cutlinks(node); node->private = NULL; ng_unref(sc->node); m_freem(sc->qhead); m_freem(sc->m); bzero(sc, sizeof(*sc)); FREE(sc, M_NETGRAPH); return (0); } /* * Receive incoming data from netgraph system. Put it on our * output queue and start output if necessary. */ static int ngt_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { const sc_p sc = hook->node->private; int s, error = 0; if (hook != sc->hook) panic(__FUNCTION__); NG_FREE_META(meta); s = spltty(); if (sc->qlen >= MAX_MBUFQ) ERROUT(ENOBUFS); m->m_nextpkt = NULL; *sc->qtail = m; sc->qtail = &m->m_nextpkt; sc->qlen++; QUEUECHECK(sc); m = NULL; if (sc->qlen == 1) ngt_start(sc->tp); done: splx(s); if (m) m_freem(m); return (error); } /* * Receive control message */ static int ngt_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr) { const sc_p sc = (sc_p) node->private; struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_TTY_COOKIE: switch (msg->header.cmd) { case NGM_TTY_SET_HOTCHAR: { int hotchar; if (msg->header.arglen != sizeof(int)) ERROUT(EINVAL); hotchar = *((int *) msg->data); if (hotchar != (u_char) hotchar && hotchar != -1) ERROUT(EINVAL); sc->hotchar = hotchar; /* race condition is OK */ break; } case NGM_TTY_GET_HOTCHAR: NG_MKRESPONSE(resp, msg, sizeof(int), M_NOWAIT); if (!resp) ERROUT(ENOMEM); /* Race condition here is OK */ *((int *) resp->data) = sc->hotchar; break; default: ERROUT(EINVAL); } break; default: ERROUT(EINVAL); } if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); done: FREE(msg, M_NETGRAPH); return (error); } /****************************************************************** INITIALIZATION ******************************************************************/ /* * Handle loading and unloading for this node type */ static int ngt_mod_event(module_t mod, int event, void *data) { /* struct ng_type *const type = data;*/ int s, error = 0; switch (event) { case MOD_LOAD: #ifdef __i386__ /* Insure the soft net "engine" can't run during spltty code */ s = splhigh(); tty_imask |= softnet_imask; /* spltty() block spl[soft]net() */ net_imask |= softtty_imask; /* splimp() block splsofttty() */ net_imask |= tty_imask; /* splimp() block spltty() */ update_intr_masks(); splx(s); if (bootverbose) log(LOG_DEBUG, "new masks: bio %x, tty %x, net %x\n", bio_imask, tty_imask, net_imask); #endif /* Register line discipline */ s = spltty(); if ((ngt_ldisc = ldisc_register(NETGRAPHDISC, &ngt_disc)) < 0) { splx(s); log(LOG_ERR, "%s: can't register line discipline", __FUNCTION__); return (EIO); } splx(s); break; case MOD_UNLOAD: /* Unregister line discipline */ s = spltty(); ldisc_deregister(ngt_ldisc); splx(s); break; default: error = EOPNOTSUPP; break; } return (error); } Index: head/sys/netgraph/ng_vjc.c =================================================================== --- head/sys/netgraph/ng_vjc.c (revision 52751) +++ head/sys/netgraph/ng_vjc.c (revision 52752) @@ -1,446 +1,445 @@ /* * 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 $ + * $Whistle: ng_vjc.c,v 1.17 1999/11/01 09:24:52 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); +static ng_constructor_t ng_vjc_constructor; +static ng_rcvmsg_t ng_vjc_rcvmsg; +static ng_shutdown_t ng_vjc_rmnode; +static ng_newhook_t ng_vjc_newhook; +static ng_rcvdata_t ng_vjc_rcvdata; +static ng_disconnect_t ng_vjc_disconnect; /* Helper stuff */ 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_SET_CONFIG: { struct ngm_vjc_config *const c = (struct ngm_vjc_config *) msg->data; if (msg->header.arglen != sizeof(*c)) ERROUT(EINVAL); if ((priv->conf.enableComp || priv->conf.enableDecomp) && (c->enableComp || c->enableDecomp)) ERROUT(EALREADY); 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.enableComp) /* compression not enabled */ type = TYPE_IP; else { struct ip *ip; 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 decompressing? */ if (!priv->conf.enableDecomp) { m_freem(m); m = NULL; ERROUT(ENETDOWN); } /* Uncompress packet to reconstruct TCP/IP header */ 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_char *hdr; u_int hlen; /* Are we decompressing? */ if (!priv->conf.enableDecomp) { m_freem(m); m = NULL; ERROUT(ENETDOWN); } /* Run packet through uncompressor */ 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. If packet is not * a TCP packet, just pull up the IP header. */ static struct mbuf * ng_vjc_pulluphdrs(struct mbuf *m) { struct ip *ip; struct tcphdr *tcp; int ihlen, thlen; 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); 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); thlen = tcp->th_off << 2; if (m->m_len < ihlen + thlen) m = m_pullup(m, ihlen + thlen); return (m); }