Index: stable/3/sys/netgraph/netgraph.h =================================================================== --- stable/3/sys/netgraph/netgraph.h (revision 67531) +++ stable/3/sys/netgraph/netgraph.h (revision 67532) @@ -1,274 +1,274 @@ /* * 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 + * Author: Julian Elischer * * $FreeBSD$ * $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 NGM_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); /* * Command list -- each node type specifies the command that it knows * how to convert between ASCII and binary using an array of these. * The last element in the array must be a terminator with cookie=0. */ struct ng_cmdlist { u_int32_t cookie; /* command typecookie */ int cmd; /* command number */ const char *name; /* command name */ const struct ng_parse_type *mesgType; /* args if !NGF_RESP */ const struct ng_parse_type *respType; /* args if NGF_RESP */ }; /* * Structure of a node type */ struct ng_type { 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 lots of hooks */ ng_connect_t *connect; /* final notification of new hook */ ng_rcvdata_t *rcvdata; /* date comes here */ ng_rcvdata_t *rcvdataq; /* or here if being queued */ ng_disconnect_t *disconnect; /* notify on disconnect */ const struct ng_cmdlist *cmdlist; /* commands we can convert */ /* 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); meta_p ng_copy_meta(meta_p meta); void ng_destroy_hook(hook_p hook); hook_p ng_findhook(node_p node, const char *name); 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, 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: stable/3/sys/netgraph/ng_UI.c =================================================================== --- stable/3/sys/netgraph/ng_UI.c (revision 67531) +++ stable/3/sys/netgraph/ng_UI.c (revision 67532) @@ -1,237 +1,237 @@ /* * 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 + * Author: Julian Elischer * * $FreeBSD$ * $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 /* * DEFINITIONS */ /* Everything, starting with sdlc on has defined UI as 0x03 */ #define HDLC_UI 0x03 /* Node private data */ struct ng_UI_private { hook_p downlink; hook_p uplink; }; typedef struct ng_UI_private *priv_p; /* Netgraph node methods */ 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, NULL }; 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: stable/3/sys/netgraph/ng_UI.h =================================================================== --- stable/3/sys/netgraph/ng_UI.h (revision 67531) +++ stable/3/sys/netgraph/ng_UI.h (revision 67532) @@ -1,55 +1,55 @@ /* * ng_UI.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 + * Author: Julian Elischer * * $FreeBSD$ * $Whistle: ng_UI.h,v 1.6 1999/01/20 00:54:15 archie Exp $ */ #ifndef _NETGRAPH_UI_H_ #define _NETGRAPH_UI_H_ /* Node type name and cookie */ #define NG_UI_NODE_TYPE "UI" #define NGM_UI_COOKIE 884639499 /* Hook names */ #define NG_UI_HOOK_DOWNSTREAM "downstream" #define NG_UI_HOOK_UPSTREAM "upstream" #endif /* _NETGRAPH_UI_H_ */ Index: stable/3/sys/netgraph/ng_async.c =================================================================== --- stable/3/sys/netgraph/ng_async.c (revision 67531) +++ stable/3/sys/netgraph/ng_async.c (revision 67532) @@ -1,616 +1,616 @@ /* * 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 + * Author: Archie Cobbs * * $FreeBSD$ * $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 /* Async decode state */ #define MODE_HUNT 0 #define MODE_NORMAL 1 #define MODE_ESC 2 /* Private data structure */ struct ng_async_private { node_p node; /* Our node */ hook_p async; /* Asynchronous side */ hook_p sync; /* Synchronous side */ 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 */ long lasttime; /* Time of last async packet sent */ struct ng_async_cfg cfg; /* Configuration */ struct ng_async_stat stats; /* Statistics */ }; typedef struct ng_async_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 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); /* Parse type for struct ng_async_cfg */ static const struct ng_parse_struct_info nga_config_type_info = NG_ASYNC_CONFIG_TYPE_INFO; static const struct ng_parse_type nga_config_type = { &ng_parse_struct_type, &nga_config_type_info }; /* Parse type for struct ng_async_stat */ static const struct ng_parse_struct_info nga_stats_type_info = NG_ASYNC_STATS_TYPE_INFO; static const struct ng_parse_type nga_stats_type = { &ng_parse_struct_type, &nga_stats_type_info, }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist nga_cmdlist[] = { { NGM_ASYNC_COOKIE, NGM_ASYNC_CMD_SET_CONFIG, "setconfig", &nga_config_type, NULL }, { NGM_ASYNC_COOKIE, NGM_ASYNC_CMD_GET_CONFIG, "getconfig", NULL, &nga_config_type }, { NGM_ASYNC_COOKIE, NGM_ASYNC_CMD_GET_STATS, "getstats", NULL, &nga_stats_type }, { NGM_ASYNC_COOKIE, NGM_ASYNC_CMD_CLR_STATS, "clrstats", &nga_stats_type, NULL }, { 0 } }; /* 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, nga_cmdlist }; 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 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)); 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 */ 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 panic(__FUNCTION__); if (!*hookp) panic(__FUNCTION__ "2"); *hookp = NULL; bzero(&sc->stats, sizeof(sc->stats)); sc->lasttime = 0; 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. */ static int nga_rcv_sync(const sc_p sc, struct mbuf *m, meta_p meta) { struct ifnet *const rcvif = m->m_pkthdr.rcvif; int alen, error = 0; struct timeval time; u_int16_t fcs, fcs0; u_int32_t accm; #define ADD_BYTE(x) nga_async_add(sc, &fcs, accm, &alen, (x)) /* Check for bypass mode */ if (!sc->cfg.enabled) { NG_SEND_DATA(error, sc->async, m, meta); return (error); } /* Get ACCM; special case LCP frames, which use full ACCM */ accm = sc->cfg.accm; if (m->m_pkthdr.len >= 4) { static const u_char lcphdr[4] = { PPP_ALLSTATIONS, PPP_UI, (u_char)(PPP_LCP >> 8), (u_char)(PPP_LCP & 0xff) }; u_char buf[4]; m_copydata(m, 0, 4, (caddr_t)buf); if (bcmp(buf, &lcphdr, 4) == 0) accm = ~0; } /* Check for overflow */ if (m->m_pkthdr.len > sc->cfg.smru) { sc->stats.syncOverflows++; NG_FREE_DATA(m, meta); return (EMSGSIZE); } /* Update stats */ 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 */ getmicrotime(&time); if (time.tv_sec >= sc->lasttime + 1) { sc->abuf[alen++] = PPP_FLAG; sc->lasttime = time.tv_sec; } /* Add packet payload */ while (m != NULL) { struct mbuf *n; while (m->m_len > 0) { ADD_BYTE(*mtod(m, u_char *)); 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 */ if (!(m = m_devget(sc->abuf, alen, 0, rcvif, NULL))) { NG_FREE_META(meta); error = ENOBUFS; } else NG_SEND_DATA(error, sc->async, m, meta); return (error); } /* * Receive incoming asynchronous data * XXX Technically, we should strip out incoming characters * that are in our ACCM. Not sure if this is good or not. */ 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: stable/3/sys/netgraph/ng_async.h =================================================================== --- stable/3/sys/netgraph/ng_async.h (revision 67531) +++ stable/3/sys/netgraph/ng_async.h (revision 67532) @@ -1,113 +1,113 @@ /* * ng_async.h * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * - * Author: Archie Cobbs + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_async.h,v 1.5 1999/01/25 01:17:14 archie Exp $ */ #ifndef _NETGRAPH_ASYNC_H_ #define _NETGRAPH_ASYNC_H_ /* Type name and cookie */ #define NG_ASYNC_NODE_TYPE "async" #define NGM_ASYNC_COOKIE 886473717 /* Hook names */ #define NG_ASYNC_HOOK_SYNC "sync" /* Sync frames */ #define NG_ASYNC_HOOK_ASYNC "async" /* Async-encoded frames */ /* Maximum receive size bounds (for both sync and async sides) */ #define NG_ASYNC_MIN_MRU 1 #define NG_ASYNC_MAX_MRU 8192 #define NG_ASYNC_DEFAULT_MRU 1600 /* Frame statistics */ struct ng_async_stat { u_int32_t syncOctets; u_int32_t syncFrames; u_int32_t syncOverflows; u_int32_t asyncOctets; u_int32_t asyncFrames; u_int32_t asyncRunts; u_int32_t asyncOverflows; u_int32_t asyncBadCheckSums; }; /* Keep this in sync with the above structure definition */ #define NG_ASYNC_STATS_TYPE_INFO { \ { \ { "syncOctets", &ng_parse_int32_type }, \ { "syncFrames", &ng_parse_int32_type }, \ { "syncOverflows", &ng_parse_int32_type }, \ { "asyncOctets", &ng_parse_int32_type }, \ { "asyncFrames", &ng_parse_int32_type }, \ { "asyncRunts", &ng_parse_int32_type }, \ { "asyncOverflows", &ng_parse_int32_type }, \ { "asyncBadCheckSums",&ng_parse_int32_type }, \ { NULL }, \ } \ } /* Configuration for this node */ struct ng_async_cfg { u_char enabled; /* Turn encoding on/off */ u_int16_t amru; /* Max receive async frame length */ u_int16_t smru; /* Max receive sync frame length */ u_int32_t accm; /* ACCM encoding */ }; /* Keep this in sync with the above structure definition */ #define NG_ASYNC_CONFIG_TYPE_INFO { \ { \ { "enabled", &ng_parse_int8_type }, \ { "amru", &ng_parse_int16_type }, \ { "smru", &ng_parse_int16_type }, \ { "accm", &ng_parse_int32_type }, \ { NULL }, \ } \ } /* Commands */ enum { NGM_ASYNC_CMD_GET_STATS = 1, /* returns struct ng_async_stat */ NGM_ASYNC_CMD_CLR_STATS, NGM_ASYNC_CMD_SET_CONFIG, /* takes struct ng_async_cfg */ NGM_ASYNC_CMD_GET_CONFIG, /* returns struct ng_async_cfg */ }; #endif /* _NETGRAPH_ASYNC_H_ */ Index: stable/3/sys/netgraph/ng_base.c =================================================================== --- stable/3/sys/netgraph/ng_base.c (revision 67531) +++ stable/3/sys/netgraph/ng_base.c (revision 67532) @@ -1,2000 +1,2000 @@ /* * ng_base.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. * - * Authors: Julian Elischer - * Archie Cobbs + * Authors: Julian Elischer + * Archie Cobbs * * $FreeBSD$ * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $ */ /* * This file implements the base netgraph code. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* List of all nodes */ static LIST_HEAD(, ng_node) nodelist; /* List of installed types */ static LIST_HEAD(, ng_type) typelist; /* Hash releted definitions */ #define ID_HASH_SIZE 32 /* most systems wont need even this many */ static LIST_HEAD(, ng_node) ID_hash[ID_HASH_SIZE]; /* Don't nead to initialise them because it's a LIST */ /* Internal functions */ static int ng_add_hook(node_p node, const char *name, hook_p * hookp); static int ng_connect(hook_p hook1, hook_p hook2); static void ng_disconnect_hook(hook_p hook); static int ng_generic_msg(node_p here, struct ng_mesg *msg, const char *retaddr, struct ng_mesg ** resp); static ng_ID_t ng_decodeidname(const char *name); static int ngb_mod_event(module_t mod, int event, void *data); static void ngintr(void); /* Our own netgraph malloc type */ MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages"); /* Set this to Debugger("X") to catch all errors as they occur */ #ifndef TRAP_ERROR #define TRAP_ERROR #endif static ng_ID_t nextID = 1; #ifdef INVARIANTS #define CHECK_DATA_MBUF(m) do { \ struct mbuf *n; \ int total; \ \ if (((m)->m_flags & M_PKTHDR) == 0) \ panic("%s: !PKTHDR", __FUNCTION__); \ for (total = 0, n = (m); n != NULL; n = n->m_next) \ total += n->m_len; \ if ((m)->m_pkthdr.len != total) { \ panic("%s: %d != %d", \ __FUNCTION__, (m)->m_pkthdr.len, total); \ } \ } while (0) #else #define CHECK_DATA_MBUF(m) #endif /************************************************************************ Parse type definitions for generic messages ************************************************************************/ /* Handy structure parse type defining macro */ #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args) \ static const struct ng_parse_struct_info \ ng_ ## lo ## _type_info = NG_GENERIC_ ## up ## _INFO args; \ static const struct ng_parse_type ng_generic_ ## lo ## _type = { \ &ng_parse_struct_type, \ &ng_ ## lo ## _type_info \ } DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ()); DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ()); DEFINE_PARSE_STRUCT_TYPE(name, NAME, ()); DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ()); DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ()); DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ()); DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type)); /* Get length of an array when the length is stored as a 32 bit value immediately preceeding the array -- as with struct namelist and struct typelist. */ static int ng_generic_list_getLength(const struct ng_parse_type *type, const u_char *start, const u_char *buf) { return *((const u_int32_t *)(buf - 4)); } /* Get length of the array of struct linkinfo inside a struct hooklist */ static int ng_generic_linkinfo_getLength(const struct ng_parse_type *type, const u_char *start, const u_char *buf) { const struct hooklist *hl = (const struct hooklist *)start; return hl->nodeinfo.hooks; } /* Array type for a variable length array of struct namelist */ static const struct ng_parse_array_info ng_nodeinfoarray_type_info = { &ng_generic_nodeinfo_type, &ng_generic_list_getLength }; static const struct ng_parse_type ng_generic_nodeinfoarray_type = { &ng_parse_array_type, &ng_nodeinfoarray_type_info }; /* Array type for a variable length array of struct typelist */ static const struct ng_parse_array_info ng_typeinfoarray_type_info = { &ng_generic_typeinfo_type, &ng_generic_list_getLength }; static const struct ng_parse_type ng_generic_typeinfoarray_type = { &ng_parse_array_type, &ng_typeinfoarray_type_info }; /* Array type for array of struct linkinfo in struct hooklist */ static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = { &ng_generic_linkinfo_type, &ng_generic_linkinfo_getLength }; static const struct ng_parse_type ng_generic_linkinfo_array_type = { &ng_parse_array_type, &ng_generic_linkinfo_array_type_info }; DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type)); DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST, (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type)); DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES, (&ng_generic_nodeinfoarray_type)); /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_generic_cmds[] = { { NGM_GENERIC_COOKIE, NGM_SHUTDOWN, "shutdown", NULL, NULL }, { NGM_GENERIC_COOKIE, NGM_MKPEER, "mkpeer", &ng_generic_mkpeer_type, NULL }, { NGM_GENERIC_COOKIE, NGM_CONNECT, "connect", &ng_generic_connect_type, NULL }, { NGM_GENERIC_COOKIE, NGM_NAME, "name", &ng_generic_name_type, NULL }, { NGM_GENERIC_COOKIE, NGM_RMHOOK, "rmhook", &ng_generic_rmhook_type, NULL }, { NGM_GENERIC_COOKIE, NGM_NODEINFO, "nodeinfo", NULL, &ng_generic_nodeinfo_type }, { NGM_GENERIC_COOKIE, NGM_LISTHOOKS, "listhooks", NULL, &ng_generic_hooklist_type }, { NGM_GENERIC_COOKIE, NGM_LISTNAMES, "listnames", NULL, &ng_generic_listnodes_type /* same as NGM_LISTNODES */ }, { NGM_GENERIC_COOKIE, NGM_LISTNODES, "listnodes", NULL, &ng_generic_listnodes_type }, { NGM_GENERIC_COOKIE, NGM_LISTTYPES, "listtypes", NULL, &ng_generic_typeinfo_type }, { NGM_GENERIC_COOKIE, NGM_TEXT_STATUS, "textstatus", NULL, &ng_parse_string_type }, { NGM_GENERIC_COOKIE, NGM_ASCII2BINARY, "ascii2binary", &ng_parse_ng_mesg_type, &ng_parse_ng_mesg_type }, { NGM_GENERIC_COOKIE, NGM_BINARY2ASCII, "binary2ascii", &ng_parse_ng_mesg_type, &ng_parse_ng_mesg_type }, { 0 } }; /************************************************************************ Node routines ************************************************************************/ /* * Instantiate a node of the requested type */ int ng_make_node(const char *typename, node_p *nodepp) { struct ng_type *type; /* Check that the type makes sense */ if (typename == NULL) { TRAP_ERROR; return (EINVAL); } /* Locate the node type */ if ((type = ng_findtype(typename)) == NULL) { char *path, filename[NG_TYPELEN + 4]; linker_file_t lf; int error; /* Not found, try to load it as a loadable module */ snprintf(filename, sizeof(filename), "ng_%s.ko", typename); if ((path = linker_search_path(filename)) == NULL) return (ENXIO); error = linker_load_file(path, &lf); FREE(path, M_LINKER); if (error != 0) return (error); lf->userrefs++; /* pretend loaded by the syscall */ /* Try again, as now the type should have linked itself in */ if ((type = ng_findtype(typename)) == NULL) return (ENXIO); } /* Call the constructor */ if (type->constructor != NULL) return ((*type->constructor)(nodepp)); else return (ng_make_node_common(type, nodepp)); } /* * Generic node creation. Called by node constructors. * The returned node has a reference count of 1. */ int ng_make_node_common(struct ng_type *type, node_p *nodepp) { node_p node; /* Require the node type to have been already installed */ if (ng_findtype(type->name) == NULL) { TRAP_ERROR; return (EINVAL); } /* Make a node and try attach it to the type */ MALLOC(node, node_p, sizeof(*node), M_NETGRAPH, M_WAITOK); if (node == NULL) { TRAP_ERROR; return (ENOMEM); } bzero(node, sizeof(*node)); node->type = type; node->refs++; /* note reference */ type->refs++; /* Link us into the node linked list */ LIST_INSERT_HEAD(&nodelist, node, nodes); /* Initialize hook list for new node */ LIST_INIT(&node->hooks); /* get an ID and put us in the hash chain */ node->ID = nextID++; /* 137 per second for 1 year before wrap */ LIST_INSERT_HEAD(&ID_hash[node->ID % ID_HASH_SIZE], node, idnodes); /* Done */ *nodepp = node; return (0); } /* * Forceably start the shutdown process on a node. Either call * it's shutdown method, or do the default shutdown if there is * no type-specific method. * * Persistent nodes must have a type-specific method which * resets the NG_INVALID flag. */ void ng_rmnode(node_p node) { /* Check if it's already shutting down */ if ((node->flags & NG_INVALID) != 0) return; /* Add an extra reference so it doesn't go away during this */ node->refs++; /* Mark it invalid so any newcomers know not to try use it */ node->flags |= NG_INVALID; /* Ask the type if it has anything to do in this case */ if (node->type && node->type->shutdown) (*node->type->shutdown)(node); else { /* do the default thing */ ng_unname(node); ng_cutlinks(node); ng_unref(node); } /* Remove extra reference, possibly the last */ ng_unref(node); } /* * Called by the destructor to remove any STANDARD external references */ void ng_cutlinks(node_p node) { hook_p hook; /* Make sure that this is set to stop infinite loops */ node->flags |= NG_INVALID; /* If we have sleepers, wake them up; they'll see NG_INVALID */ if (node->sleepers) wakeup(node); /* Notify all remaining connected nodes to disconnect */ while ((hook = LIST_FIRST(&node->hooks)) != NULL) ng_destroy_hook(hook); } /* * Remove a reference to the node, possibly the last */ void ng_unref(node_p node) { if (--node->refs <= 0) { node->type->refs--; LIST_REMOVE(node, nodes); LIST_REMOVE(node, idnodes); FREE(node, M_NETGRAPH); } } /* * Wait for a node to come ready. Returns a node with a reference count; * don't forget to drop it when we are done with it using ng_release_node(). */ int ng_wait_node(node_p node, char *msg) { int s, error = 0; if (msg == NULL) msg = "netgraph"; s = splnet(); node->sleepers++; node->refs++; /* the sleeping process counts as a reference */ while ((node->flags & (NG_BUSY | NG_INVALID)) == NG_BUSY) error = tsleep(node, (PZERO + 1) | PCATCH, msg, 0); node->sleepers--; if (node->flags & NG_INVALID) { TRAP_ERROR; error = ENXIO; } else { KASSERT(node->refs > 1, ("%s: refs=%d", __FUNCTION__, node->refs)); node->flags |= NG_BUSY; } splx(s); /* Release the reference we had on it */ if (error != 0) ng_unref(node); return error; } /* * Release a node acquired via ng_wait_node() */ void ng_release_node(node_p node) { /* Declare that we don't want it */ node->flags &= ~NG_BUSY; /* If we have sleepers, then wake them up */ if (node->sleepers) wakeup(node); /* We also have a reference.. drop it too */ ng_unref(node); } /************************************************************************ Node ID handling ************************************************************************/ static node_p ng_ID2node(ng_ID_t ID) { node_p np; LIST_FOREACH(np, &ID_hash[ID % ID_HASH_SIZE], idnodes) { if (np->ID == ID) break; } return(np); } ng_ID_t ng_node2ID(node_p node) { return (node->ID); } /************************************************************************ Node name handling ************************************************************************/ /* * Assign a node a name. Once assigned, the name cannot be changed. */ int ng_name_node(node_p node, const char *name) { int i; /* Check the name is valid */ for (i = 0; i < NG_NODELEN + 1; i++) { if (name[i] == '\0' || name[i] == '.' || name[i] == ':') break; } if (i == 0 || name[i] != '\0') { TRAP_ERROR; return (EINVAL); } if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */ TRAP_ERROR; return (EINVAL); } /* Check the node isn't already named */ if (node->name != NULL) { TRAP_ERROR; return (EISCONN); } /* Check the name isn't already being used */ if (ng_findname(node, name) != NULL) { TRAP_ERROR; return (EADDRINUSE); } /* Allocate space and copy it */ MALLOC(node->name, char *, strlen(name) + 1, M_NETGRAPH, M_WAITOK); if (node->name == NULL) { TRAP_ERROR; return (ENOMEM); } strcpy(node->name, name); /* The name counts as a reference */ node->refs++; return (0); } /* * Find a node by absolute name. The name should NOT end with ':' * The name "." means "this node" and "[xxx]" means "the node * with ID (ie, at address) xxx". * * Returns the node if found, else NULL. */ node_p ng_findname(node_p this, const char *name) { node_p node; ng_ID_t temp; /* "." means "this node" */ if (strcmp(name, ".") == 0) return(this); /* Check for name-by-ID */ if ((temp = ng_decodeidname(name)) != 0) { return (ng_ID2node(temp)); } /* Find node by name */ LIST_FOREACH(node, &nodelist, nodes) { if (node->name != NULL && strcmp(node->name, name) == 0) break; } return (node); } /* * Decode a ID name, eg. "[f03034de]". Returns 0 if the * string is not valid, otherwise returns the value. */ static ng_ID_t ng_decodeidname(const char *name) { const int len = strlen(name); char *eptr; u_long val; /* Check for proper length, brackets, no leading junk */ if (len < 3 || name[0] != '[' || name[len - 1] != ']' || !isxdigit(name[1])) return (0); /* Decode number */ val = strtoul(name + 1, &eptr, 16); if (eptr - name != len - 1 || val == ULONG_MAX || val == 0) return ((ng_ID_t)0); return (ng_ID_t)val; } /* * Remove a name from a node. This should only be called * when shutting down and removing the node. */ void ng_unname(node_p node) { if (node->name) { FREE(node->name, M_NETGRAPH); node->name = NULL; ng_unref(node); } } /************************************************************************ Hook routines Names are not optional. Hooks are always connected, except for a brief moment within these routines. ************************************************************************/ /* * Remove a hook reference */ static void ng_unref_hook(hook_p hook) { if (--hook->refs == 0) FREE(hook, M_NETGRAPH); } /* * Add an unconnected hook to a node. Only used internally. */ static int ng_add_hook(node_p node, const char *name, hook_p *hookp) { hook_p hook; int error = 0; /* Check that the given name is good */ if (name == NULL) { TRAP_ERROR; return (EINVAL); } if (ng_findhook(node, name) != NULL) { TRAP_ERROR; return (EEXIST); } /* Allocate the hook and link it up */ MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH, M_WAITOK); if (hook == NULL) { TRAP_ERROR; return (ENOMEM); } bzero(hook, sizeof(*hook)); hook->refs = 1; hook->flags = HK_INVALID; hook->node = node; node->refs++; /* each hook counts as a reference */ /* Check if the node type code has something to say about it */ if (node->type->newhook != NULL) if ((error = (*node->type->newhook)(node, hook, name)) != 0) goto fail; /* * The 'type' agrees so far, so go ahead and link it in. * We'll ask again later when we actually connect the hooks. */ LIST_INSERT_HEAD(&node->hooks, hook, hooks); node->numhooks++; /* Set hook name */ MALLOC(hook->name, char *, strlen(name) + 1, M_NETGRAPH, M_WAITOK); if (hook->name == NULL) { error = ENOMEM; LIST_REMOVE(hook, hooks); node->numhooks--; fail: hook->node = NULL; ng_unref(node); ng_unref_hook(hook); /* this frees the hook */ return (error); } strcpy(hook->name, name); if (hookp) *hookp = hook; return (error); } /* * Connect a pair of hooks. Only used internally. */ static int ng_connect(hook_p hook1, hook_p hook2) { int error; hook1->peer = hook2; hook2->peer = hook1; /* Give each node the opportunity to veto the impending connection */ if (hook1->node->type->connect) { if ((error = (*hook1->node->type->connect) (hook1))) { ng_destroy_hook(hook1); /* also zaps hook2 */ return (error); } } if (hook2->node->type->connect) { if ((error = (*hook2->node->type->connect) (hook2))) { ng_destroy_hook(hook2); /* also zaps hook1 */ return (error); } } hook1->flags &= ~HK_INVALID; hook2->flags &= ~HK_INVALID; return (0); } /* * Find a hook * * Node types may supply their own optimized routines for finding * hooks. If none is supplied, we just do a linear search. */ hook_p ng_findhook(node_p node, const char *name) { hook_p hook; if (node->type->findhook != NULL) return (*node->type->findhook)(node, name); LIST_FOREACH(hook, &node->hooks, hooks) { if (hook->name != NULL && strcmp(hook->name, name) == 0) return (hook); } return (NULL); } /* * Destroy a hook * * As hooks are always attached, this really destroys two hooks. * The one given, and the one attached to it. Disconnect the hooks * from each other first. */ void ng_destroy_hook(hook_p hook) { hook_p peer = hook->peer; hook->flags |= HK_INVALID; /* as soon as possible */ if (peer) { peer->flags |= HK_INVALID; /* as soon as possible */ hook->peer = NULL; peer->peer = NULL; ng_disconnect_hook(peer); } ng_disconnect_hook(hook); } /* * Notify the node of the hook's demise. This may result in more actions * (e.g. shutdown) but we don't do that ourselves and don't know what * happens there. If there is no appropriate handler, then just remove it * (and decrement the reference count of it's node which in turn might * make something happen). */ static void ng_disconnect_hook(hook_p hook) { node_p node = hook->node; /* * Remove the hook from the node's list to avoid possible recursion * in case the disconnection results in node shutdown. */ LIST_REMOVE(hook, hooks); node->numhooks--; if (node->type->disconnect) { /* * The type handler may elect to destroy the peer so don't * trust its existance after this point. */ (*node->type->disconnect) (hook); } ng_unref(node); /* might be the last reference */ if (hook->name) FREE(hook->name, M_NETGRAPH); hook->node = NULL; /* may still be referenced elsewhere */ ng_unref_hook(hook); } /* * Take two hooks on a node and merge the connection so that the given node * is effectively bypassed. */ int ng_bypass(hook_p hook1, hook_p hook2) { if (hook1->node != hook2->node) return (EINVAL); hook1->peer->peer = hook2->peer; hook2->peer->peer = hook1->peer; /* XXX If we ever cache methods on hooks update them as well */ hook1->peer = NULL; hook2->peer = NULL; ng_destroy_hook(hook1); ng_destroy_hook(hook2); return (0); } /* * Install a new netgraph type */ int ng_newtype(struct ng_type *tp) { const size_t namelen = strlen(tp->name); /* Check version and type name fields */ if (tp->version != NG_VERSION || namelen == 0 || namelen > NG_TYPELEN) { TRAP_ERROR; return (EINVAL); } /* Check for name collision */ if (ng_findtype(tp->name) != NULL) { TRAP_ERROR; return (EEXIST); } /* Link in new type */ LIST_INSERT_HEAD(&typelist, tp, types); tp->refs = 0; return (0); } /* * Look for a type of the name given */ struct ng_type * ng_findtype(const char *typename) { struct ng_type *type; LIST_FOREACH(type, &typelist, types) { if (strcmp(type->name, typename) == 0) break; } return (type); } /************************************************************************ Composite routines ************************************************************************/ /* * Make a peer and connect. The order is arranged to minimise * the work needed to back out in case of error. */ int ng_mkpeer(node_p node, const char *name, const char *name2, char *type) { node_p node2; hook_p hook; hook_p hook2; int error; if ((error = ng_add_hook(node, name, &hook))) return (error); if ((error = ng_make_node(type, &node2))) { ng_destroy_hook(hook); return (error); } if ((error = ng_add_hook(node2, name2, &hook2))) { ng_rmnode(node2); ng_destroy_hook(hook); return (error); } /* * Actually link the two hooks together.. on failure they are * destroyed so we don't have to do that here. */ if ((error = ng_connect(hook, hook2))) ng_rmnode(node2); return (error); } /* * Connect two nodes using the specified hooks */ int ng_con_nodes(node_p node, const char *name, node_p node2, const char *name2) { int error; hook_p hook; hook_p hook2; if ((error = ng_add_hook(node, name, &hook))) return (error); if ((error = ng_add_hook(node2, name2, &hook2))) { ng_destroy_hook(hook); return (error); } return (ng_connect(hook, hook2)); } /* * Parse and verify a string of the form: * * Such a string can refer to a specific node or a specific hook * on a specific node, depending on how you look at it. In the * latter case, the PATH component must not end in a dot. * * Both and are optional. The is a string * of hook names separated by dots. This breaks out the original * string, setting *nodep to "NODE" (or NULL if none) and *pathp * to "PATH" (or NULL if degenerate). Also, *hookp will point to * the final hook component of , if any, otherwise NULL. * * This returns -1 if the path is malformed. The char ** are optional. */ int ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) { char *node, *path, *hook; int k; /* * Extract absolute NODE, if any */ for (path = addr; *path && *path != ':'; path++); if (*path) { node = addr; /* Here's the NODE */ *path++ = '\0'; /* Here's the PATH */ /* Node name must not be empty */ if (!*node) return -1; /* A name of "." is OK; otherwise '.' not allowed */ if (strcmp(node, ".") != 0) { for (k = 0; node[k]; k++) if (node[k] == '.') return -1; } } else { node = NULL; /* No absolute NODE */ path = addr; /* Here's the PATH */ } /* Snoop for illegal characters in PATH */ for (k = 0; path[k]; k++) if (path[k] == ':') return -1; /* Check for no repeated dots in PATH */ for (k = 0; path[k]; k++) if (path[k] == '.' && path[k + 1] == '.') return -1; /* Remove extra (degenerate) dots from beginning or end of PATH */ if (path[0] == '.') path++; if (*path && path[strlen(path) - 1] == '.') path[strlen(path) - 1] = 0; /* If PATH has a dot, then we're not talking about a hook */ if (*path) { for (hook = path, k = 0; path[k]; k++) if (path[k] == '.') { hook = NULL; break; } } else path = hook = NULL; /* Done */ if (nodep) *nodep = node; if (pathp) *pathp = path; if (hookp) *hookp = hook; return (0); } /* * Given a path, which may be absolute or relative, and a starting node, * return the destination node. Compute the "return address" if desired. */ int ng_path2node(node_p here, const char *address, node_p *destp, char **rtnp) { const node_p start = here; char fullpath[NG_PATHLEN + 1]; char *nodename, *path, pbuf[2]; node_p node; char *cp; /* Initialize */ if (rtnp) *rtnp = NULL; if (destp == NULL) return EINVAL; *destp = NULL; /* Make a writable copy of address for ng_path_parse() */ strncpy(fullpath, address, sizeof(fullpath) - 1); fullpath[sizeof(fullpath) - 1] = '\0'; /* Parse out node and sequence of hooks */ if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { TRAP_ERROR; return EINVAL; } if (path == NULL) { pbuf[0] = '.'; /* Needs to be writable */ pbuf[1] = '\0'; path = pbuf; } /* For an absolute address, jump to the starting node */ if (nodename) { node = ng_findname(here, nodename); if (node == NULL) { TRAP_ERROR; return (ENOENT); } } else node = here; /* Now follow the sequence of hooks */ for (cp = path; node != NULL && *cp != '\0'; ) { hook_p hook; char *segment; /* * Break out the next path segment. Replace the dot we just * found with a NUL; "cp" points to the next segment (or the * NUL at the end). */ for (segment = cp; *cp != '\0'; cp++) { if (*cp == '.') { *cp++ = '\0'; break; } } /* Empty segment */ if (*segment == '\0') continue; /* We have a segment, so look for a hook by that name */ hook = ng_findhook(node, segment); /* Can't get there from here... */ if (hook == NULL || hook->peer == NULL || (hook->flags & HK_INVALID) != 0) { TRAP_ERROR; return (ENOENT); } /* Hop on over to the next node */ node = hook->peer->node; } /* If node somehow missing, fail here (probably this is not needed) */ if (node == NULL) { TRAP_ERROR; return (ENXIO); } /* Now compute return address, i.e., the path to the sender */ if (rtnp != NULL) { MALLOC(*rtnp, char *, NG_NODELEN + 2, M_NETGRAPH, M_WAITOK); if (*rtnp == NULL) { TRAP_ERROR; return (ENOMEM); } if (start->name != NULL) sprintf(*rtnp, "%s:", start->name); else sprintf(*rtnp, "[%x]:", ng_node2ID(start)); } /* Done */ *destp = node; return (0); } /* * Call the appropriate message handler for the object. * It is up to the message handler to free the message. * If it's a generic message, handle it generically, otherwise * call the type's message handler (if it exists) */ #define CALL_MSG_HANDLER(error, node, msg, retaddr, resp) \ do { \ if((msg)->header.typecookie == NGM_GENERIC_COOKIE) { \ (error) = ng_generic_msg((node), (msg), \ (retaddr), (resp)); \ } else { \ if ((node)->type->rcvmsg != NULL) { \ (error) = (*(node)->type->rcvmsg)((node), \ (msg), (retaddr), (resp)); \ } else { \ TRAP_ERROR; \ FREE((msg), M_NETGRAPH); \ (error) = EINVAL; \ } \ } \ } while (0) /* * Send a control message to a node */ int ng_send_msg(node_p here, struct ng_mesg *msg, const char *address, struct ng_mesg **rptr) { node_p dest = NULL; char *retaddr = NULL; int error; /* Find the target node */ error = ng_path2node(here, address, &dest, &retaddr); if (error) { FREE(msg, M_NETGRAPH); return error; } /* Make sure the resp field is null before we start */ if (rptr != NULL) *rptr = NULL; CALL_MSG_HANDLER(error, dest, msg, retaddr, rptr); /* Make sure that if there is a response, it has the RESP bit set */ if ((error == 0) && rptr && *rptr) (*rptr)->header.flags |= NGF_RESP; /* * If we had a return address it is up to us to free it. They should * have taken a copy if they needed to make a delayed response. */ if (retaddr) FREE(retaddr, M_NETGRAPH); return (error); } /* * Implement the 'generic' control messages */ static int ng_generic_msg(node_p here, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp) { int error = 0; if (msg->header.typecookie != NGM_GENERIC_COOKIE) { TRAP_ERROR; FREE(msg, M_NETGRAPH); return (EINVAL); } switch (msg->header.cmd) { case NGM_SHUTDOWN: ng_rmnode(here); break; case NGM_MKPEER: { struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; if (msg->header.arglen != sizeof(*mkp)) { TRAP_ERROR; return (EINVAL); } mkp->type[sizeof(mkp->type) - 1] = '\0'; mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); break; } case NGM_CONNECT: { struct ngm_connect *const con = (struct ngm_connect *) msg->data; node_p node2; if (msg->header.arglen != sizeof(*con)) { TRAP_ERROR; return (EINVAL); } con->path[sizeof(con->path) - 1] = '\0'; con->ourhook[sizeof(con->ourhook) - 1] = '\0'; con->peerhook[sizeof(con->peerhook) - 1] = '\0'; error = ng_path2node(here, con->path, &node2, NULL); if (error) break; error = ng_con_nodes(here, con->ourhook, node2, con->peerhook); break; } case NGM_NAME: { struct ngm_name *const nam = (struct ngm_name *) msg->data; if (msg->header.arglen != sizeof(*nam)) { TRAP_ERROR; return (EINVAL); } nam->name[sizeof(nam->name) - 1] = '\0'; error = ng_name_node(here, nam->name); break; } case NGM_RMHOOK: { struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; hook_p hook; if (msg->header.arglen != sizeof(*rmh)) { TRAP_ERROR; return (EINVAL); } rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) ng_destroy_hook(hook); break; } case NGM_NODEINFO: { struct nodeinfo *ni; struct ng_mesg *rp; /* Get response struct */ if (resp == NULL) { error = EINVAL; break; } NG_MKRESPONSE(rp, msg, sizeof(*ni), M_NOWAIT); if (rp == NULL) { error = ENOMEM; break; } /* Fill in node info */ ni = (struct nodeinfo *) rp->data; if (here->name != NULL) strncpy(ni->name, here->name, NG_NODELEN); strncpy(ni->type, here->type->name, NG_TYPELEN); ni->id = ng_node2ID(here); ni->hooks = here->numhooks; *resp = rp; break; } case NGM_LISTHOOKS: { const int nhooks = here->numhooks; struct hooklist *hl; struct nodeinfo *ni; struct ng_mesg *rp; hook_p hook; /* Get response struct */ if (resp == NULL) { error = EINVAL; break; } NG_MKRESPONSE(rp, msg, sizeof(*hl) + (nhooks * sizeof(struct linkinfo)), M_NOWAIT); if (rp == NULL) { error = ENOMEM; break; } hl = (struct hooklist *) rp->data; ni = &hl->nodeinfo; /* Fill in node info */ if (here->name) strncpy(ni->name, here->name, NG_NODELEN); strncpy(ni->type, here->type->name, NG_TYPELEN); ni->id = ng_node2ID(here); /* Cycle through the linked list of hooks */ ni->hooks = 0; LIST_FOREACH(hook, &here->hooks, hooks) { struct linkinfo *const link = &hl->link[ni->hooks]; if (ni->hooks >= nhooks) { log(LOG_ERR, "%s: number of %s changed\n", __FUNCTION__, "hooks"); break; } if ((hook->flags & HK_INVALID) != 0) continue; strncpy(link->ourhook, hook->name, NG_HOOKLEN); strncpy(link->peerhook, hook->peer->name, NG_HOOKLEN); if (hook->peer->node->name != NULL) strncpy(link->nodeinfo.name, hook->peer->node->name, NG_NODELEN); strncpy(link->nodeinfo.type, hook->peer->node->type->name, NG_TYPELEN); link->nodeinfo.id = ng_node2ID(hook->peer->node); link->nodeinfo.hooks = hook->peer->node->numhooks; ni->hooks++; } *resp = rp; break; } case NGM_LISTNAMES: case NGM_LISTNODES: { const int unnamed = (msg->header.cmd == NGM_LISTNODES); struct namelist *nl; struct ng_mesg *rp; node_p node; int num = 0; if (resp == NULL) { error = EINVAL; break; } /* Count number of nodes */ LIST_FOREACH(node, &nodelist, nodes) { if (unnamed || node->name != NULL) num++; } /* Get response struct */ if (resp == NULL) { error = EINVAL; break; } NG_MKRESPONSE(rp, msg, sizeof(*nl) + (num * sizeof(struct nodeinfo)), M_NOWAIT); if (rp == NULL) { error = ENOMEM; break; } nl = (struct namelist *) rp->data; /* Cycle through the linked list of nodes */ nl->numnames = 0; LIST_FOREACH(node, &nodelist, nodes) { struct nodeinfo *const np = &nl->nodeinfo[nl->numnames]; if (nl->numnames >= num) { log(LOG_ERR, "%s: number of %s changed\n", __FUNCTION__, "nodes"); break; } if ((node->flags & NG_INVALID) != 0) continue; if (!unnamed && node->name == NULL) continue; if (node->name != NULL) strncpy(np->name, node->name, NG_NODELEN); strncpy(np->type, node->type->name, NG_TYPELEN); np->id = ng_node2ID(node); np->hooks = node->numhooks; nl->numnames++; } *resp = rp; break; } case NGM_LISTTYPES: { struct typelist *tl; struct ng_mesg *rp; struct ng_type *type; int num = 0; if (resp == NULL) { error = EINVAL; break; } /* Count number of types */ LIST_FOREACH(type, &typelist, types) num++; /* Get response struct */ if (resp == NULL) { error = EINVAL; break; } NG_MKRESPONSE(rp, msg, sizeof(*tl) + (num * sizeof(struct typeinfo)), M_NOWAIT); if (rp == NULL) { error = ENOMEM; break; } tl = (struct typelist *) rp->data; /* Cycle through the linked list of types */ tl->numtypes = 0; LIST_FOREACH(type, &typelist, types) { struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; if (tl->numtypes >= num) { log(LOG_ERR, "%s: number of %s changed\n", __FUNCTION__, "types"); break; } strncpy(tp->type_name, type->name, NG_TYPELEN); tp->numnodes = type->refs; tl->numtypes++; } *resp = rp; break; } case NGM_BINARY2ASCII: { int bufSize = 2000; /* XXX hard coded constant */ const struct ng_parse_type *argstype; const struct ng_cmdlist *c; struct ng_mesg *rp, *binary, *ascii; /* Data area must contain a valid netgraph message */ binary = (struct ng_mesg *)msg->data; if (msg->header.arglen < sizeof(struct ng_mesg) || msg->header.arglen - sizeof(struct ng_mesg) < binary->header.arglen) { error = EINVAL; break; } /* Get a response message with lots of room */ NG_MKRESPONSE(rp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); if (rp == NULL) { error = ENOMEM; break; } ascii = (struct ng_mesg *)rp->data; /* Copy binary message header to response message payload */ bcopy(binary, ascii, sizeof(*binary)); /* Find command by matching typecookie and command number */ for (c = here->type->cmdlist; c != NULL && c->name != NULL; c++) { if (binary->header.typecookie == c->cookie && binary->header.cmd == c->cmd) break; } if (c == NULL || c->name == NULL) { for (c = ng_generic_cmds; c->name != NULL; c++) { if (binary->header.typecookie == c->cookie && binary->header.cmd == c->cmd) break; } if (c->name == NULL) { FREE(rp, M_NETGRAPH); error = ENOSYS; break; } } /* Convert command name to ASCII */ snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), "%s", c->name); /* Convert command arguments to ASCII */ argstype = (binary->header.flags & NGF_RESP) ? c->respType : c->mesgType; if (argstype == NULL) *ascii->data = '\0'; else { if ((error = ng_unparse(argstype, (u_char *)binary->data, ascii->data, bufSize)) != 0) { FREE(rp, M_NETGRAPH); break; } } /* Return the result as struct ng_mesg plus ASCII string */ bufSize = strlen(ascii->data) + 1; ascii->header.arglen = bufSize; rp->header.arglen = sizeof(*ascii) + bufSize; *resp = rp; break; } case NGM_ASCII2BINARY: { int bufSize = 2000; /* XXX hard coded constant */ const struct ng_cmdlist *c; const struct ng_parse_type *argstype; struct ng_mesg *rp, *ascii, *binary; int off = 0; /* Data area must contain at least a struct ng_mesg + '\0' */ ascii = (struct ng_mesg *)msg->data; if (msg->header.arglen < sizeof(*ascii) + 1 || ascii->header.arglen < 1 || msg->header.arglen < sizeof(*ascii) + ascii->header.arglen) { error = EINVAL; break; } ascii->data[ascii->header.arglen - 1] = '\0'; /* Get a response message with lots of room */ NG_MKRESPONSE(rp, msg, sizeof(*binary) + bufSize, M_NOWAIT); if (rp == NULL) { error = ENOMEM; break; } binary = (struct ng_mesg *)rp->data; /* Copy ASCII message header to response message payload */ bcopy(ascii, binary, sizeof(*ascii)); /* Find command by matching ASCII command string */ for (c = here->type->cmdlist; c != NULL && c->name != NULL; c++) { if (strcmp(ascii->header.cmdstr, c->name) == 0) break; } if (c == NULL || c->name == NULL) { for (c = ng_generic_cmds; c->name != NULL; c++) { if (strcmp(ascii->header.cmdstr, c->name) == 0) break; } if (c->name == NULL) { FREE(rp, M_NETGRAPH); error = ENOSYS; break; } } /* Convert command name to binary */ binary->header.cmd = c->cmd; binary->header.typecookie = c->cookie; /* Convert command arguments to binary */ argstype = (binary->header.flags & NGF_RESP) ? c->respType : c->mesgType; if (argstype == NULL) bufSize = 0; else { if ((error = ng_parse(argstype, ascii->data, &off, (u_char *)binary->data, &bufSize)) != 0) { FREE(rp, M_NETGRAPH); break; } } /* Return the result */ binary->header.arglen = bufSize; rp->header.arglen = sizeof(*binary) + bufSize; *resp = rp; break; } case NGM_TEXT_STATUS: /* * This one is tricky as it passes the command down to the * actual node, even though it is a generic type command. * This means we must assume that the msg is already freed * when control passes back to us. */ if (resp == NULL) { error = EINVAL; break; } if (here->type->rcvmsg != NULL) return((*here->type->rcvmsg)(here, msg, retaddr, resp)); /* Fall through if rcvmsg not supported */ default: TRAP_ERROR; error = EINVAL; } FREE(msg, M_NETGRAPH); return (error); } /* * Send a data packet to a node. If the recipient has no * 'receive data' method, then silently discard the packet. */ int ng_send_data(hook_p hook, struct mbuf *m, meta_p meta) { int (*rcvdata)(hook_p, struct mbuf *, meta_p); int error; CHECK_DATA_MBUF(m); if (hook && (hook->flags & HK_INVALID) == 0) { rcvdata = hook->peer->node->type->rcvdata; if (rcvdata != NULL) error = (*rcvdata)(hook->peer, m, meta); else { error = 0; NG_FREE_DATA(m, meta); } } else { TRAP_ERROR; error = ENOTCONN; NG_FREE_DATA(m, meta); } return (error); } /* * Send a queued data packet to a node. If the recipient has no * 'receive queued data' method, then try the 'receive data' method above. */ int ng_send_dataq(hook_p hook, struct mbuf *m, meta_p meta) { int (*rcvdataq)(hook_p, struct mbuf *, meta_p); int error; CHECK_DATA_MBUF(m); if (hook && (hook->flags & HK_INVALID) == 0) { rcvdataq = hook->peer->node->type->rcvdataq; if (rcvdataq != NULL) error = (*rcvdataq)(hook->peer, m, meta); else { error = ng_send_data(hook, m, meta); } } else { TRAP_ERROR; error = ENOTCONN; NG_FREE_DATA(m, meta); } return (error); } /* * Copy a 'meta'. * * Returns new meta, or NULL if original meta is NULL or ENOMEM. */ meta_p ng_copy_meta(meta_p meta) { meta_p meta2; if (meta == NULL) return (NULL); MALLOC(meta2, meta_p, meta->used_len, M_NETGRAPH, M_NOWAIT); if (meta2 == NULL) return (NULL); meta2->allocated_len = meta->used_len; bcopy(meta, meta2, meta->used_len); return (meta2); } /************************************************************************ Module routines ************************************************************************/ /* * Handle the loading/unloading of a netgraph node type module */ int ng_mod_event(module_t mod, int event, void *data) { struct ng_type *const type = data; int s, error = 0; switch (event) { case MOD_LOAD: /* Register new netgraph node type */ s = splnet(); if ((error = ng_newtype(type)) != 0) { splx(s); break; } /* Call type specific code */ if (type->mod_event != NULL) if ((error = (*type->mod_event)(mod, event, data)) != 0) LIST_REMOVE(type, types); splx(s); break; case MOD_UNLOAD: s = splnet(); if (type->refs != 0) /* make sure no nodes exist! */ error = EBUSY; else { if (type->mod_event != NULL) { /* check with type */ error = (*type->mod_event)(mod, event, data); if (error != 0) { /* type refuses.. */ splx(s); break; } } LIST_REMOVE(type, types); } splx(s); break; default: if (type->mod_event != NULL) error = (*type->mod_event)(mod, event, data); else error = 0; /* XXX ? */ break; } return (error); } /* * Handle loading and unloading for this code. * The only thing we need to link into is the NETISR strucure. */ static int ngb_mod_event(module_t mod, int event, void *data) { int s, error = 0; switch (event) { case MOD_LOAD: /* Register line discipline */ s = splimp(); error = register_netisr(NETISR_NETGRAPH, ngintr); splx(s); break; case MOD_UNLOAD: /* You cant unload it because an interface may be using it. */ error = EBUSY; break; default: error = EOPNOTSUPP; break; } return (error); } static moduledata_t netgraph_mod = { "netgraph", ngb_mod_event, (NULL) }; DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); /************************************************************************ Queueing routines ************************************************************************/ /* The structure for queueing across ISR switches */ struct ng_queue_entry { u_long flags; struct ng_queue_entry *next; union { struct { hook_p da_hook; /* target hook */ struct mbuf *da_m; meta_p da_meta; } data; struct { struct ng_mesg *msg_msg; node_p msg_node; void *msg_retaddr; } msg; } body; }; #define NGQF_DATA 0x01 /* the queue element is data */ #define NGQF_MESG 0x02 /* the queue element is a message */ static struct ng_queue_entry *ngqbase; /* items to be unqueued */ static struct ng_queue_entry *ngqlast; /* last item queued */ static const int ngqroom = 64; /* max items to queue */ static int ngqsize; /* number of items in queue */ static struct ng_queue_entry *ngqfree; /* free ones */ static const int ngqfreemax = 16;/* cache at most this many */ static int ngqfreesize; /* number of cached entries */ /* * Get a queue entry */ static struct ng_queue_entry * ng_getqblk(void) { register struct ng_queue_entry *q; int s; /* Could be guarding against tty ints or whatever */ s = splhigh(); /* Try get a cached queue block, or else allocate a new one */ if ((q = ngqfree) == NULL) { splx(s); if (ngqsize < ngqroom) { /* don't worry about races */ MALLOC(q, struct ng_queue_entry *, sizeof(*q), M_NETGRAPH, M_NOWAIT); } } else { ngqfree = q->next; ngqfreesize--; splx(s); } return (q); } /* * Release a queue entry */ #define RETURN_QBLK(q) \ do { \ int s; \ if (ngqfreesize < ngqfreemax) { /* don't worry about races */ \ s = splhigh(); \ (q)->next = ngqfree; \ ngqfree = (q); \ ngqfreesize++; \ splx(s); \ } else { \ FREE((q), M_NETGRAPH); \ } \ } while (0) /* * Running at a raised (but we don't know which) processor priority level, * put the data onto a queue to be picked up by another PPL (probably splnet) */ int ng_queue_data(hook_p hook, struct mbuf *m, meta_p meta) { struct ng_queue_entry *q; int s; if (hook == NULL) { NG_FREE_DATA(m, meta); return (0); } if ((q = ng_getqblk()) == NULL) { NG_FREE_DATA(m, meta); return (ENOBUFS); } /* Fill out the contents */ q->flags = NGQF_DATA; q->next = NULL; q->body.data.da_hook = hook; q->body.data.da_m = m; q->body.data.da_meta = meta; hook->refs++; /* don't let it go away while on the queue */ /* Put it on the queue */ s = splhigh(); if (ngqbase) { ngqlast->next = q; } else { ngqbase = q; } ngqlast = q; ngqsize++; splx(s); /* Schedule software interrupt to handle it later */ schednetisr(NETISR_NETGRAPH); return (0); } /* * Running at a raised (but we don't know which) processor priority level, * put the msg onto a queue to be picked up by another PPL (probably splnet) */ int ng_queue_msg(node_p here, struct ng_mesg *msg, const char *address) { register struct ng_queue_entry *q; int s; node_p dest = NULL; char *retaddr = NULL; int error; /* Find the target node. */ error = ng_path2node(here, address, &dest, &retaddr); if (error) { FREE(msg, M_NETGRAPH); return (error); } if ((q = ng_getqblk()) == NULL) { FREE(msg, M_NETGRAPH); if (retaddr) FREE(retaddr, M_NETGRAPH); return (ENOBUFS); } /* Fill out the contents */ q->flags = NGQF_MESG; q->next = NULL; q->body.msg.msg_node = dest; q->body.msg.msg_msg = msg; q->body.msg.msg_retaddr = retaddr; dest->refs++; /* don't let it go away while on the queue */ /* Put it on the queue */ s = splhigh(); if (ngqbase) { ngqlast->next = q; } else { ngqbase = q; } ngqlast = q; ngqsize++; splx(s); /* Schedule software interrupt to handle it later */ schednetisr(NETISR_NETGRAPH); return (0); } /* * Pick an item off the queue, process it, and dispose of the queue entry. * Should be running at splnet. */ static void ngintr(void) { hook_p hook; struct ng_queue_entry *ngq; struct mbuf *m; meta_p meta; void *retaddr; struct ng_mesg *msg; node_p node; int error = 0; int s; while (1) { s = splhigh(); if ((ngq = ngqbase)) { ngqbase = ngq->next; ngqsize--; } splx(s); if (ngq == NULL) return; switch (ngq->flags) { case NGQF_DATA: hook = ngq->body.data.da_hook; m = ngq->body.data.da_m; meta = ngq->body.data.da_meta; RETURN_QBLK(ngq); NG_SEND_DATAQ(error, hook, m, meta); ng_unref_hook(hook); break; case NGQF_MESG: node = ngq->body.msg.msg_node; msg = ngq->body.msg.msg_msg; retaddr = ngq->body.msg.msg_retaddr; RETURN_QBLK(ngq); if (node->flags & NG_INVALID) { FREE(msg, M_NETGRAPH); } else { CALL_MSG_HANDLER(error, node, msg, retaddr, NULL); } ng_unref(node); if (retaddr) FREE(retaddr, M_NETGRAPH); break; default: RETURN_QBLK(ngq); } } } Index: stable/3/sys/netgraph/ng_bpf.c =================================================================== --- stable/3/sys/netgraph/ng_bpf.c (revision 67531) +++ stable/3/sys/netgraph/ng_bpf.c (revision 67532) @@ -1,502 +1,502 @@ /* * ng_bpf.c * * Copyright (c) 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, GUARANBPF, 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 + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_bpf.c,v 1.3 1999/12/03 20:30:23 archie Exp $ */ /* * BPF NETGRAPH NODE TYPE * * This node type accepts any number of hook connections. With each hook * is associated a bpf(4) filter program, and two hook names (each possibly * the empty string). Incoming packets are compared against the filter; * matching packets are delivered out the first named hook (or dropped if * the empty string), and non-matching packets are delivered out the second * named hook (or dropped if the empty string). * * Each hook also keeps statistics about how many packets have matched, etc. */ #include #include #include #include #include #include #include #include #include #include #include #define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0)) #define ERROUT(x) do { error = (x); goto done; } while (0) /* Per hook private info */ struct ng_bpf_hookinfo { node_p node; hook_p hook; struct ng_bpf_hookprog *prog; struct ng_bpf_hookstat stats; }; typedef struct ng_bpf_hookinfo *hinfo_p; /* Netgraph methods */ static ng_constructor_t ng_bpf_constructor; static ng_rcvmsg_t ng_bpf_rcvmsg; static ng_shutdown_t ng_bpf_rmnode; static ng_newhook_t ng_bpf_newhook; static ng_rcvdata_t ng_bpf_rcvdata; static ng_disconnect_t ng_bpf_disconnect; /* Internal helper functions */ static int ng_bpf_setprog(hook_p hook, const struct ng_bpf_hookprog *hp); /* Parse type for one struct bfp_insn */ static const struct ng_parse_struct_info ng_bpf_insn_type_info = { { { "code", &ng_parse_int16_type }, { "jt", &ng_parse_int8_type }, { "jf", &ng_parse_int8_type }, { "k", &ng_parse_int32_type }, { NULL } } }; static const struct ng_parse_type ng_bpf_insn_type = { &ng_parse_struct_type, &ng_bpf_insn_type_info }; /* Parse type for the field 'bpf_prog' in struct ng_bpf_hookprog */ static int ng_bpf_hookprogary_getLength(const struct ng_parse_type *type, const u_char *start, const u_char *buf) { const struct ng_bpf_hookprog *hp; hp = (const struct ng_bpf_hookprog *) (buf - OFFSETOF(struct ng_bpf_hookprog, bpf_prog)); return hp->bpf_prog_len; } static const struct ng_parse_array_info ng_bpf_hookprogary_info = { &ng_bpf_insn_type, &ng_bpf_hookprogary_getLength, NULL }; static const struct ng_parse_type ng_bpf_hookprogary_type = { &ng_parse_array_type, &ng_bpf_hookprogary_info }; /* Parse type for struct ng_bpf_hookprog */ static const struct ng_parse_struct_info ng_bpf_hookprog_type_info = NG_BPF_HOOKPROG_TYPE_INFO(&ng_bpf_hookprogary_type); static const struct ng_parse_type ng_bpf_hookprog_type = { &ng_parse_struct_type, &ng_bpf_hookprog_type_info }; /* Parse type for struct ng_bpf_hookstat */ static const struct ng_parse_struct_info ng_bpf_hookstat_type_info = NG_BPF_HOOKSTAT_TYPE_INFO; static const struct ng_parse_type ng_bpf_hookstat_type = { &ng_parse_struct_type, &ng_bpf_hookstat_type_info }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_bpf_cmdlist[] = { { NGM_BPF_COOKIE, NGM_BPF_SET_PROGRAM, "setprogram", &ng_bpf_hookprog_type, NULL }, { NGM_BPF_COOKIE, NGM_BPF_GET_PROGRAM, "getprogram", &ng_parse_hookbuf_type, &ng_bpf_hookprog_type }, { NGM_BPF_COOKIE, NGM_BPF_GET_STATS, "getstats", &ng_parse_hookbuf_type, &ng_bpf_hookstat_type }, { NGM_BPF_COOKIE, NGM_BPF_CLR_STATS, "clrstats", &ng_parse_hookbuf_type, NULL }, { NGM_BPF_COOKIE, NGM_BPF_GETCLR_STATS, "getclrstats", &ng_parse_hookbuf_type, &ng_bpf_hookstat_type }, { 0 } }; /* Netgraph type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_BPF_NODE_TYPE, NULL, ng_bpf_constructor, ng_bpf_rcvmsg, ng_bpf_rmnode, ng_bpf_newhook, NULL, NULL, ng_bpf_rcvdata, ng_bpf_rcvdata, ng_bpf_disconnect, ng_bpf_cmdlist }; NETGRAPH_INIT(bpf, &typestruct); /* Default BPF program for a hook that matches nothing */ static const struct ng_bpf_hookprog ng_bpf_default_prog = { { '\0' }, /* to be filled in at hook creation time */ { '\0' }, { '\0' }, 1, { BPF_STMT(BPF_RET+BPF_K, 0) } }; /* * Node constructor * * We don't keep any per-node private data */ static int ng_bpf_constructor(node_p *nodep) { int error = 0; if ((error = ng_make_node_common(&typestruct, nodep))) return (error); (*nodep)->private = NULL; return (0); } /* * Add a hook */ static int ng_bpf_newhook(node_p node, hook_p hook, const char *name) { hinfo_p hip; int error; /* Create hook private structure */ MALLOC(hip, hinfo_p, sizeof(*hip), M_NETGRAPH, M_WAITOK); if (hip == NULL) return (ENOMEM); bzero(hip, sizeof(*hip)); hip->hook = hook; hook->private = hip; hip->node = node; /* Attach the default BPF program */ if ((error = ng_bpf_setprog(hook, &ng_bpf_default_prog)) != 0) { FREE(hip, M_NETGRAPH); hook->private = NULL; return (error); } /* Set hook name */ strncpy(hip->prog->thisHook, name, sizeof(hip->prog->thisHook) - 1); hip->prog->thisHook[sizeof(hip->prog->thisHook) - 1] = '\0'; return (0); } /* * Receive a control message */ static int ng_bpf_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr) { struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_BPF_COOKIE: switch (msg->header.cmd) { case NGM_BPF_SET_PROGRAM: { struct ng_bpf_hookprog *const hp = (struct ng_bpf_hookprog *)msg->data; hook_p hook; /* Sanity check */ if (msg->header.arglen < sizeof(*hp) || msg->header.arglen != NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len)) ERROUT(EINVAL); /* Find hook */ if ((hook = ng_findhook(node, hp->thisHook)) == NULL) ERROUT(ENOENT); /* Set new program */ if ((error = ng_bpf_setprog(hook, hp)) != 0) ERROUT(error); break; } case NGM_BPF_GET_PROGRAM: { struct ng_bpf_hookprog *hp; hook_p hook; /* Sanity check */ if (msg->header.arglen == 0) ERROUT(EINVAL); msg->data[msg->header.arglen - 1] = '\0'; /* Find hook */ if ((hook = ng_findhook(node, msg->data)) == NULL) ERROUT(ENOENT); /* Build response */ hp = ((hinfo_p)hook->private)->prog; NG_MKRESPONSE(resp, msg, NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); bcopy(hp, resp->data, NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len)); break; } case NGM_BPF_GET_STATS: case NGM_BPF_CLR_STATS: case NGM_BPF_GETCLR_STATS: { struct ng_bpf_hookstat *stats; hook_p hook; /* Sanity check */ if (msg->header.arglen == 0) ERROUT(EINVAL); msg->data[msg->header.arglen - 1] = '\0'; /* Find hook */ if ((hook = ng_findhook(node, msg->data)) == NULL) ERROUT(ENOENT); stats = &((hinfo_p)hook->private)->stats; /* Build response (if desired) */ if (msg->header.cmd != NGM_BPF_CLR_STATS) { NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); bcopy(stats, resp->data, sizeof(*stats)); } /* Clear stats (if desired) */ if (msg->header.cmd != NGM_BPF_GET_STATS) bzero(stats, sizeof(*stats)); 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 on a hook * * Apply the filter, and then drop or forward packet as appropriate. */ static int ng_bpf_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { const hinfo_p hip = hook->private; int totlen = m->m_pkthdr.len; int needfree = 0, error = 0; u_char *data, buf[256]; hinfo_p dhip; hook_p dest; u_int len; /* Update stats on incoming hook */ hip->stats.recvFrames++; hip->stats.recvOctets += totlen; /* Need to put packet in contiguous memory for bpf */ if (m->m_next != NULL) { if (totlen > sizeof(buf)) { MALLOC(data, u_char *, totlen, M_NETGRAPH, M_NOWAIT); if (data == NULL) { NG_FREE_DATA(m, meta); return (ENOMEM); } needfree = 1; } else data = buf; m_copydata(m, 0, totlen, (caddr_t)data); } else data = mtod(m, u_char *); /* Run packet through filter */ len = bpf_filter(hip->prog->bpf_prog, data, totlen, totlen); if (needfree) FREE(data, M_NETGRAPH); /* See if we got a match and find destination hook */ if (len > 0) { /* Update stats */ hip->stats.recvMatchFrames++; hip->stats.recvMatchOctets += totlen; /* Truncate packet length if required by the filter */ if (len < totlen) { m_adj(m, -(totlen - len)); totlen -= len; } dest = ng_findhook(hip->node, hip->prog->ifMatch); } else dest = ng_findhook(hip->node, hip->prog->ifNotMatch); if (dest == NULL) { NG_FREE_DATA(m, meta); return (0); } /* Deliver frame out destination hook */ dhip = (hinfo_p)dest->private; dhip->stats.xmitOctets += totlen; dhip->stats.xmitFrames++; NG_SEND_DATA(error, dest, m, meta); return (error); } /* * Shutdown processing */ static int ng_bpf_rmnode(node_p node) { node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); ng_unref(node); return (0); } /* * Hook disconnection */ static int ng_bpf_disconnect(hook_p hook) { const hinfo_p hip = hook->private; KASSERT(hip != NULL, ("%s: null info", __FUNCTION__)); FREE(hip->prog, M_NETGRAPH); bzero(hip, sizeof(*hip)); FREE(hip, M_NETGRAPH); hook->private = NULL; /* for good measure */ if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } /************************************************************************ HELPER STUFF ************************************************************************/ /* * Set the BPF program associated with a hook */ static int ng_bpf_setprog(hook_p hook, const struct ng_bpf_hookprog *hp0) { const hinfo_p hip = hook->private; struct ng_bpf_hookprog *hp; int size; /* Check program for validity */ if (!bpf_validate(hp0->bpf_prog, hp0->bpf_prog_len)) return (EINVAL); /* Make a copy of the program */ size = NG_BPF_HOOKPROG_SIZE(hp0->bpf_prog_len); MALLOC(hp, struct ng_bpf_hookprog *, size, M_NETGRAPH, M_WAITOK); if (hp == NULL) return (ENOMEM); bcopy(hp0, hp, size); /* Free previous program, if any, and assign new one */ if (hip->prog != NULL) FREE(hip->prog, M_NETGRAPH); hip->prog = hp; return (0); } Index: stable/3/sys/netgraph/ng_bpf.h =================================================================== --- stable/3/sys/netgraph/ng_bpf.h (revision 67531) +++ stable/3/sys/netgraph/ng_bpf.h (revision 67532) @@ -1,106 +1,106 @@ /* * ng_bpf.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, GUARANBPF, 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 + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_bpf.h,v 1.3 1999/12/03 20:30:23 archie Exp $ */ #ifndef _NETGRAPH_BPF_H_ #define _NETGRAPH_BPF_H_ /* Node type name and magic cookie */ #define NG_BPF_NODE_TYPE "bpf" #define NGM_BPF_COOKIE 944100792 /* Program structure for one hook */ struct ng_bpf_hookprog { char thisHook[NG_HOOKLEN+1]; /* name of hook */ char ifMatch[NG_HOOKLEN+1]; /* match dest hook */ char ifNotMatch[NG_HOOKLEN+1]; /* !match dest hook */ int32_t bpf_prog_len; /* #isns in program */ struct bpf_insn bpf_prog[0]; /* bpf program */ }; #define NG_BPF_HOOKPROG_SIZE(numInsn) \ (sizeof(struct ng_bpf_hookprog) + (numInsn) * sizeof(struct bpf_insn)) /* Keep this in sync with the above structure definition */ #define NG_BPF_HOOKPROG_TYPE_INFO(bptype) { \ { \ { "thisHook", &ng_parse_hookbuf_type }, \ { "ifMatch", &ng_parse_hookbuf_type }, \ { "ifNotMatch", &ng_parse_hookbuf_type }, \ { "bpf_prog_len", &ng_parse_int32_type }, \ { "bpf_prog", (bptype) }, \ { NULL }, \ } \ } /* Statistics structure for one hook */ struct ng_bpf_hookstat { u_int64_t recvFrames; u_int64_t recvOctets; u_int64_t recvMatchFrames; u_int64_t recvMatchOctets; u_int64_t xmitFrames; u_int64_t xmitOctets; }; /* Keep this in sync with the above structure definition */ #define NG_BPF_HOOKSTAT_TYPE_INFO { \ { \ { "recvFrames", &ng_parse_int64_type }, \ { "recvOctets", &ng_parse_int64_type }, \ { "recvMatchFrames", &ng_parse_int64_type }, \ { "recvMatchOctets", &ng_parse_int64_type }, \ { "xmitFrames", &ng_parse_int64_type }, \ { "xmitOctets", &ng_parse_int64_type }, \ { NULL }, \ } \ } /* Netgraph commands */ enum { NGM_BPF_SET_PROGRAM = 1, /* supply a struct ng_bpf_hookprog */ NGM_BPF_GET_PROGRAM, /* returns a struct ng_bpf_hookprog */ NGM_BPF_GET_STATS, /* supply name as char[NG_HOOKLEN+1] */ NGM_BPF_CLR_STATS, /* supply name as char[NG_HOOKLEN+1] */ NGM_BPF_GETCLR_STATS, /* supply name as char[NG_HOOKLEN+1] */ }; #endif /* _NETGRAPH_BPF_H_ */ Index: stable/3/sys/netgraph/ng_cisco.c =================================================================== --- stable/3/sys/netgraph/ng_cisco.c (revision 67531) +++ stable/3/sys/netgraph/ng_cisco.c (revision 67532) @@ -1,597 +1,597 @@ /* * 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 + * Author: Julian Elischer * * $FreeBSD$ * $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 #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 seqRetries; /* 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 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); /* Parse type for struct ng_cisco_ipaddr */ static const struct ng_parse_struct_info ng_cisco_ipaddr_type_info = NG_CISCO_IPADDR_TYPE_INFO; static const struct ng_parse_type ng_cisco_ipaddr_type = { &ng_parse_struct_type, &ng_cisco_ipaddr_type_info }; /* Parse type for struct ng_async_stat */ static const struct ng_parse_struct_info ng_cisco_stats_type_info = NG_CISCO_STATS_TYPE_INFO; static const struct ng_parse_type ng_cisco_stats_type = { &ng_parse_struct_type, &ng_cisco_stats_type_info, }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_cisco_cmdlist[] = { { NGM_CISCO_COOKIE, NGM_CISCO_SET_IPADDR, "setipaddr", &ng_cisco_ipaddr_type, NULL }, { NGM_CISCO_COOKIE, NGM_CISCO_GET_IPADDR, "getipaddr", NULL, &ng_cisco_ipaddr_type }, { NGM_CISCO_COOKIE, NGM_CISCO_GET_STATUS, "getstats", NULL, &ng_cisco_stats_type }, { 0 } }; /* 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, ng_cisco_cmdlist }; 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->seqRetries); 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 ng_cisco_stats *stat; NG_MKRESPONSE(resp, msg, sizeof(*stat), M_NOWAIT); if (!resp) { error = ENOMEM; break; } stat = (struct ng_cisco_stats *)resp->data; stat->seqRetries = sc->seqRetries; stat->keepAlivePeriod = 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_UNICAST; 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->seqRetries = 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->seqRetries++; 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: stable/3/sys/netgraph/ng_cisco.h =================================================================== --- stable/3/sys/netgraph/ng_cisco.h (revision 67531) +++ stable/3/sys/netgraph/ng_cisco.h (revision 67532) @@ -1,93 +1,93 @@ /* * ng_cisco.h * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * - * Author: Archie Cobbs + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_cisco.h,v 1.6 1999/01/25 01:21:48 archie Exp $ */ #ifndef _NETGRAPH_CISCO_H_ #define _NETGRAPH_CISCO_H_ /* Node type name and magic cookie */ #define NG_CISCO_NODE_TYPE "cisco" #define NGM_CISCO_COOKIE 860707227 /* Hook names */ #define NG_CISCO_HOOK_DOWNSTREAM "downstream" #define NG_CISCO_HOOK_INET "inet" #define NG_CISCO_HOOK_APPLETALK "atalk" #define NG_CISCO_HOOK_IPX "ipx" #define NG_CISCO_HOOK_DEBUG "debug" /* Netgraph commands */ enum { NGM_CISCO_SET_IPADDR = 1, /* requires a struct ng_cisco_ipaddr */ NGM_CISCO_GET_IPADDR, /* returns a struct ng_cisco_ipaddr */ NGM_CISCO_GET_STATUS, /* returns a struct ng_cisco_stat */ }; struct ng_cisco_ipaddr { struct in_addr ipaddr; /* IP address */ struct in_addr netmask; /* Netmask */ }; /* Keep this in sync with the above structure definition */ #define NG_CISCO_IPADDR_TYPE_INFO { \ { \ { "ipaddr", &ng_parse_ipaddr_type }, \ { "netmask", &ng_parse_ipaddr_type }, \ { NULL }, \ } \ } struct ng_cisco_stats { u_int32_t seqRetries; /* # unack'd retries */ u_int32_t keepAlivePeriod; /* in seconds */ }; /* Keep this in sync with the above structure definition */ #define NG_CISCO_STATS_TYPE_INFO { \ { \ { "seqRetries", &ng_parse_int32_type }, \ { "keepAlivePeriod", &ng_parse_int32_type }, \ { NULL }, \ } \ } #endif /* _NETGRAPH_CISCO_H_ */ Index: stable/3/sys/netgraph/ng_echo.c =================================================================== --- stable/3/sys/netgraph/ng_echo.c (revision 67531) +++ stable/3/sys/netgraph/ng_echo.c (revision 67532) @@ -1,118 +1,118 @@ /* * 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 + * Author: Julian Elisher * * $FreeBSD$ * $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 /* Netgraph methods */ 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, NULL }; 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: stable/3/sys/netgraph/ng_echo.h =================================================================== --- stable/3/sys/netgraph/ng_echo.h (revision 67531) +++ stable/3/sys/netgraph/ng_echo.h (revision 67532) @@ -1,50 +1,50 @@ /* * ng_echo.h * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * - * Author: Archie Cobbs + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_echo.h,v 1.3 1999/01/20 00:22:12 archie Exp $ */ #ifndef _NETGRAPH_ECHO_H_ #define _NETGRAPH_ECHO_H_ /* Node type name and magic cookie */ #define NG_ECHO_NODE_TYPE "echo" #define NGM_ECHO_COOKIE 884298942 #endif /* _NETGRAPH_ECHO_H_ */ Index: stable/3/sys/netgraph/ng_ether.h =================================================================== --- stable/3/sys/netgraph/ng_ether.h (revision 67531) +++ stable/3/sys/netgraph/ng_ether.h (revision 67532) @@ -1,63 +1,63 @@ /* * ng_ether.h * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * - * Author: Archie Cobbs + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_ether.h,v 1.1 1999/02/02 03:17:22 julian Exp $ */ #ifndef _NETGRAPH_NG_ETHER_H_ #define _NETGRAPH_NG_ETHER_H_ /* Node type name and magic cookie */ #define NG_ETHER_NODE_TYPE "ether" #define NGM_ETHER_COOKIE 917786904 /* Hook names */ #define NG_ETHER_HOOK_ORPHAN "orphans" #define NG_ETHER_HOOK_DIVERT "divert" /* For adding/removing Ethernet multicast addresses */ enum { NGM_ETHER_ADD_MULTICAST = 1, /* supply struct ether_addr */ NGM_ETHER_DEL_MULTICAST, /* supply struct ether_addr */ NGM_ETHER_GET_MULTICAST, /* returns array of struct ether_addr */ NGM_ETHER_CLR_MULTICAST, /* clears all multicast addresses */ }; #endif /* _NETGRAPH_NG_ETHER_H_ */ Index: stable/3/sys/netgraph/ng_frame_relay.c =================================================================== --- stable/3/sys/netgraph/ng_frame_relay.c (revision 67531) +++ stable/3/sys/netgraph/ng_frame_relay.c (revision 67532) @@ -1,523 +1,523 @@ /* * 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 + * Author: Julian Elisher * * $FreeBSD$ * $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 /* * 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 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, NULL }; 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 *eptr; 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); if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) return (EINVAL); dlci = (int)strtoul(cp, &eptr, 10); if (*eptr != '\0' || 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: stable/3/sys/netgraph/ng_frame_relay.h =================================================================== --- stable/3/sys/netgraph/ng_frame_relay.h (revision 67531) +++ stable/3/sys/netgraph/ng_frame_relay.h (revision 67532) @@ -1,55 +1,55 @@ /* * ng_frame_relay.h * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * - * Author: Archie Cobbs + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_frame_relay.h,v 1.7 1999/01/20 00:22:13 archie Exp $ */ #ifndef _NETGRAPH_FRAME_RELAY_H_ #define _NETGRAPH_FRAME_RELAY_H_ /* Node type name and magic cookie */ #define NG_FRAMERELAY_NODE_TYPE "frame_relay" #define NGM_FRAMERELAY_COOKIE 872148478 /* Hook names */ #define NG_FRAMERELAY_HOOK_DEBUG "debug" #define NG_FRAMERELAY_HOOK_DOWNSTREAM "downstream" #define NG_FRAMERELAY_HOOK_DLCI "dlci" /* really just the prefix */ #endif /* _NETGRAPH_FRAME_RELAY_H_ */ Index: stable/3/sys/netgraph/ng_hole.c =================================================================== --- stable/3/sys/netgraph/ng_hole.c (revision 67531) +++ stable/3/sys/netgraph/ng_hole.c (revision 67532) @@ -1,95 +1,95 @@ /* * 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 + * Author: Julian Elisher * * $FreeBSD$ * $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 /* Netgraph methods */ 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, NULL }; 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: stable/3/sys/netgraph/ng_hole.h =================================================================== --- stable/3/sys/netgraph/ng_hole.h (revision 67531) +++ stable/3/sys/netgraph/ng_hole.h (revision 67532) @@ -1,50 +1,50 @@ /* * ng_hole.h * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * - * Author: Archie Cobbs + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_hole.h,v 1.3 1999/01/20 00:22:13 archie Exp $ */ #ifndef _NETGRAPH_HOLE_H_ #define _NETGRAPH_HOLE_H_ /* Node type name and magic cookie */ #define NG_HOLE_NODE_TYPE "hole" #define NGM_HOLE_COOKIE 915433206 #endif /* _NETGRAPH_HOLE_H_ */ Index: stable/3/sys/netgraph/ng_iface.c =================================================================== --- stable/3/sys/netgraph/ng_iface.c (revision 67531) +++ stable/3/sys/netgraph/ng_iface.c (revision 67532) @@ -1,771 +1,771 @@ /* * 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 + * Author: Archie Cobbs * * $FreeBSD$ * $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 #ifdef INET #include #include #include #endif #ifdef NETATALK #include #include #endif #ifdef IPX #include #include #endif #ifdef NS #include #include #endif #include "bpfilter.h" #if NBPFILTER > 0 #include #include #endif /* 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 ng_iface_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 ng_iface_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); #if NBPFILTER > 0 static void ng_iface_bpftap(struct ifnet *ifp, struct mbuf *m, u_int af); #endif #ifdef DEBUG static void ng_iface_print_ioctl(struct ifnet *ifp, int cmd, caddr_t data); #endif /* Netgraph methods */ 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, NULL }; 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: error = 0; break; 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 */ #if NBPFILTER > 0 ng_iface_bpftap(ifp, m, dst->sa_family); #endif /* 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__); } #if NBPFILTER > 0 /* * 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 (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); } #endif /* NBPFILTER > 0 */ #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 | IFF_MULTICAST); 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); #if NBPFILTER > 0 bpfattach(ifp, DLT_NULL, sizeof(u_int)); #endif /* 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 */ KASSERT(iffam != NULL, ("%s: iffam", __FUNCTION__)); KASSERT(m->m_flags & M_PKTHDR, ("%s: not pkthdr", __FUNCTION__)); 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; #if NBPFILTER > 0 /* Berkeley packet filter */ ng_iface_bpftap(ifp, m, iffam->af); #endif /* 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: stable/3/sys/netgraph/ng_iface.h =================================================================== --- stable/3/sys/netgraph/ng_iface.h (revision 67531) +++ stable/3/sys/netgraph/ng_iface.h (revision 67532) @@ -1,75 +1,75 @@ /* * ng_iface.h * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * - * Author: Archie Cobbs + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_iface.h,v 1.5 1999/01/20 00:22:13 archie Exp $ */ #ifndef _NETGRAPH_IFACE_H_ #define _NETGRAPH_IFACE_H_ /* Node type name and magic cookie */ #define NG_IFACE_NODE_TYPE "iface" #define NGM_IFACE_COOKIE 858821772 /* Interface base name */ #define NG_IFACE_IFACE_NAME "ng" #define NG_IFACE_IFACE_NAME_MAX 15 /* My hook names */ #define NG_IFACE_HOOK_INET "inet" #define NG_IFACE_HOOK_ATALK "atalk" /* AppleTalk phase 2 */ #define NG_IFACE_HOOK_IPX "ipx" #define NG_IFACE_HOOK_NS "ns" /* MTU bounds */ #define NG_IFACE_MTU_MIN 72 #define NG_IFACE_MTU_MAX 65535 #define NG_IFACE_MTU_DEFAULT 1500 /* Netgraph commands */ enum { NGM_IFACE_GET_IFNAME = 1, /* returns struct ng_iface_ifname */ NGM_IFACE_GET_IFADDRS, /* returns list of addresses */ }; struct ng_iface_ifname { char ngif_name[NG_IFACE_IFACE_NAME_MAX + 1]; }; #endif /* _NETGRAPH_IFACE_H_ */ Index: stable/3/sys/netgraph/ng_ksocket.c =================================================================== --- stable/3/sys/netgraph/ng_ksocket.c (revision 67531) +++ stable/3/sys/netgraph/ng_ksocket.c (revision 67532) @@ -1,910 +1,910 @@ /* * ng_ksocket.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 + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_ksocket.c,v 1.1 1999/11/16 20:04:40 archie Exp $ */ /* * Kernel socket node type. This node type is basically a kernel-mode * version of a socket... kindof like the reverse of the socket node type. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0)) #define SADATA_OFFSET (OFFSETOF(struct sockaddr, sa_data)) /* Node private data */ struct ng_ksocket_private { hook_p hook; struct socket *so; }; typedef struct ng_ksocket_private *priv_p; /* Netgraph node methods */ static ng_constructor_t ng_ksocket_constructor; static ng_rcvmsg_t ng_ksocket_rcvmsg; static ng_shutdown_t ng_ksocket_rmnode; static ng_newhook_t ng_ksocket_newhook; static ng_rcvdata_t ng_ksocket_rcvdata; static ng_disconnect_t ng_ksocket_disconnect; /* Alias structure */ struct ng_ksocket_alias { const char *name; const int value; const int family; }; /* Protocol family aliases */ static const struct ng_ksocket_alias ng_ksocket_families[] = { { "local", PF_LOCAL }, { "inet", PF_INET }, { "inet6", PF_INET6 }, { "atalk", PF_APPLETALK }, { "ipx", PF_IPX }, { "atm", PF_ATM }, { NULL, -1 }, }; /* Socket type aliases */ static const struct ng_ksocket_alias ng_ksocket_types[] = { { "stream", SOCK_STREAM }, { "dgram", SOCK_DGRAM }, { "raw", SOCK_RAW }, { "rdm", SOCK_RDM }, { "seqpacket", SOCK_SEQPACKET }, { NULL, -1 }, }; /* Protocol aliases */ static const struct ng_ksocket_alias ng_ksocket_protos[] = { { "ip", IPPROTO_IP, PF_INET }, { "raw", IPPROTO_IP, PF_INET }, { "icmp", IPPROTO_ICMP, PF_INET }, { "igmp", IPPROTO_IGMP, PF_INET }, { "tcp", IPPROTO_TCP, PF_INET }, { "udp", IPPROTO_UDP, PF_INET }, { "gre", IPPROTO_GRE, PF_INET }, { "esp", IPPROTO_ESP, PF_INET }, { "ah", IPPROTO_AH, PF_INET }, { "swipe", IPPROTO_SWIPE, PF_INET }, { "encap", IPPROTO_ENCAP, PF_INET }, { "divert", IPPROTO_DIVERT, PF_INET }, { "ddp", ATPROTO_DDP, PF_APPLETALK }, { "aarp", ATPROTO_AARP, PF_APPLETALK }, { NULL, -1 }, }; /* Helper functions */ static void ng_ksocket_incoming(struct socket *so, void *arg, int waitflag); static int ng_ksocket_parse(const struct ng_ksocket_alias *aliases, const char *s, int family); /************************************************************************ STRUCT SOCKADDR PARSE TYPE ************************************************************************/ /* Get the length of the data portion of a generic struct sockaddr */ static int ng_parse_generic_sockdata_getLength(const struct ng_parse_type *type, const u_char *start, const u_char *buf) { const struct sockaddr *sa; sa = (const struct sockaddr *)(buf - SADATA_OFFSET); return sa->sa_len - SADATA_OFFSET; } /* Type for the variable length data portion of a generic struct sockaddr */ static const struct ng_parse_type ng_ksocket_generic_sockdata_type = { &ng_parse_bytearray_type, &ng_parse_generic_sockdata_getLength }; /* Type for a generic struct sockaddr */ static const struct ng_parse_struct_info ng_parse_generic_sockaddr_type_info = { { { "len", &ng_parse_int8_type }, { "family", &ng_parse_int8_type }, { "data", &ng_ksocket_generic_sockdata_type }, { NULL } } }; static const struct ng_parse_type ng_ksocket_generic_sockaddr_type = { &ng_parse_struct_type, &ng_parse_generic_sockaddr_type_info }; /* Convert a struct sockaddr from ASCII to binary. If its a protocol family that we specially handle, do that, otherwise defer to the generic parse type ng_ksocket_generic_sockaddr_type. */ static int ng_ksocket_sockaddr_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { struct sockaddr *const sa = (struct sockaddr *)buf; enum ng_parse_token tok; char fambuf[32]; int family, len; char *t; /* If next token is a left curly brace, use generic parse type */ if ((tok = ng_parse_get_token(s, off, &len)) == T_LBRACE) { return (*ng_ksocket_generic_sockaddr_type.supertype->parse) (&ng_ksocket_generic_sockaddr_type, s, off, start, buf, buflen); } /* Get socket address family followed by a slash */ while (isspace(s[*off])) (*off)++; if ((t = index(s + *off, '/')) == NULL) return (EINVAL); if ((len = t - (s + *off)) > sizeof(fambuf) - 1) return (EINVAL); strncpy(fambuf, s + *off, len); fambuf[len] = '\0'; *off += len + 1; if ((family = ng_ksocket_parse(ng_ksocket_families, fambuf, 0)) == -1) return (EINVAL); /* Set family */ if (*buflen < SADATA_OFFSET) return (ERANGE); sa->sa_family = family; /* Set family-specific data and length */ switch (sa->sa_family) { case PF_LOCAL: /* Get pathname */ { const int pathoff = OFFSETOF(struct sockaddr_un, sun_path); struct sockaddr_un *const sun = (struct sockaddr_un *)sa; int toklen, pathlen; char *path; if ((path = ng_get_string_token(s, off, &toklen)) == NULL) return (EINVAL); pathlen = strlen(path); if (pathlen > SOCK_MAXADDRLEN) { FREE(path, M_NETGRAPH); return (E2BIG); } if (*buflen < pathoff + pathlen) { FREE(path, M_NETGRAPH); return (ERANGE); } *off += toklen; bcopy(path, sun->sun_path, pathlen); sun->sun_len = pathoff + pathlen; FREE(path, M_NETGRAPH); break; } case PF_INET: /* Get an IP address with optional port */ { struct sockaddr_in *const sin = (struct sockaddr_in *)sa; int i; /* Parse this: [:port] */ for (i = 0; i < 4; i++) { u_long val; char *eptr; val = strtoul(s + *off, &eptr, 10); if (val > 0xff || eptr == s + *off) return (EINVAL); *off += (eptr - (s + *off)); ((u_char *)&sin->sin_addr)[i] = (u_char)val; if (i < 3) { if (s[*off] != '.') return (EINVAL); (*off)++; } else if (s[*off] == ':') { (*off)++; val = strtoul(s + *off, &eptr, 10); if (val > 0xffff || eptr == s + *off) return (EINVAL); *off += (eptr - (s + *off)); sin->sin_port = htons(val); } else sin->sin_port = 0; } bzero(&sin->sin_zero, sizeof(sin->sin_zero)); sin->sin_len = sizeof(*sin); break; } #if 0 case PF_APPLETALK: /* XXX implement these someday */ case PF_INET6: case PF_IPX: #endif default: return (EINVAL); } /* Done */ *buflen = sa->sa_len; return (0); } /* Convert a struct sockaddr from binary to ASCII */ static int ng_ksocket_sockaddr_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { const struct sockaddr *sa = (const struct sockaddr *)(data + *off); int slen = 0; /* Output socket address, either in special or generic format */ switch (sa->sa_family) { case PF_LOCAL: { const int pathoff = OFFSETOF(struct sockaddr_un, sun_path); const struct sockaddr_un *sun = (const struct sockaddr_un *)sa; const int pathlen = sun->sun_len - pathoff; char pathbuf[SOCK_MAXADDRLEN + 1]; char *pathtoken; bcopy(sun->sun_path, pathbuf, pathlen); pathbuf[pathlen] = '\0'; if ((pathtoken = ng_encode_string(pathbuf)) == NULL) return (ENOMEM); slen += snprintf(cbuf, cbuflen, "local/%s", pathtoken); FREE(pathtoken, M_NETGRAPH); if (slen >= cbuflen) return (ERANGE); *off += sun->sun_len; return (0); } case PF_INET: { const struct sockaddr_in *sin = (const struct sockaddr_in *)sa; slen += snprintf(cbuf, cbuflen, "inet/%d.%d.%d.%d", ((const u_char *)&sin->sin_addr)[0], ((const u_char *)&sin->sin_addr)[1], ((const u_char *)&sin->sin_addr)[2], ((const u_char *)&sin->sin_addr)[3]); if (sin->sin_port != 0) { slen += snprintf(cbuf + strlen(cbuf), cbuflen - strlen(cbuf), ":%d", (u_int)ntohs(sin->sin_port)); } if (slen >= cbuflen) return (ERANGE); *off += sizeof(*sin); return(0); } #if 0 case PF_APPLETALK: /* XXX implement these someday */ case PF_INET6: case PF_IPX: #endif default: return (*ng_ksocket_generic_sockaddr_type.supertype->unparse) (&ng_ksocket_generic_sockaddr_type, data, off, cbuf, cbuflen); } } /* Parse type for struct sockaddr */ static const struct ng_parse_type ng_ksocket_sockaddr_type = { NULL, NULL, NULL, &ng_ksocket_sockaddr_parse, &ng_ksocket_sockaddr_unparse, NULL /* no such thing as a default struct sockaddr */ }; /************************************************************************ STRUCT NG_KSOCKET_SOCKOPT PARSE TYPE ************************************************************************/ /* Get length of the struct ng_ksocket_sockopt value field, which is the just the excess of the message argument portion over the length of the struct ng_ksocket_sockopt. */ static int ng_parse_sockoptval_getLength(const struct ng_parse_type *type, const u_char *start, const u_char *buf) { static const int offset = OFFSETOF(struct ng_ksocket_sockopt, value); const struct ng_ksocket_sockopt *sopt; const struct ng_mesg *msg; sopt = (const struct ng_ksocket_sockopt *)(buf - offset); msg = (const struct ng_mesg *)((const u_char *)sopt - sizeof(*msg)); return msg->header.arglen - sizeof(*sopt); } /* Parse type for the option value part of a struct ng_ksocket_sockopt XXX Eventually, we should handle the different socket options specially. XXX This would avoid byte order problems, eg an integer value of 1 is XXX going to be "[1]" for little endian or "[3=1]" for big endian. */ static const struct ng_parse_type ng_ksocket_sockoptval_type = { &ng_parse_bytearray_type, &ng_parse_sockoptval_getLength }; /* Parse type for struct ng_ksocket_sockopt */ static const struct ng_parse_struct_info ng_ksocket_sockopt_type_info = NG_KSOCKET_SOCKOPT_INFO(&ng_ksocket_sockoptval_type); static const struct ng_parse_type ng_ksocket_sockopt_type = { &ng_parse_struct_type, &ng_ksocket_sockopt_type_info, }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_ksocket_cmds[] = { { NGM_KSOCKET_COOKIE, NGM_KSOCKET_BIND, "bind", &ng_ksocket_sockaddr_type, NULL }, { NGM_KSOCKET_COOKIE, NGM_KSOCKET_LISTEN, "listen", &ng_parse_int32_type, NULL }, { NGM_KSOCKET_COOKIE, NGM_KSOCKET_ACCEPT, "accept", NULL, &ng_ksocket_sockaddr_type }, { NGM_KSOCKET_COOKIE, NGM_KSOCKET_CONNECT, "connect", &ng_ksocket_sockaddr_type, NULL }, { NGM_KSOCKET_COOKIE, NGM_KSOCKET_GETNAME, "getname", NULL, &ng_ksocket_sockaddr_type }, { NGM_KSOCKET_COOKIE, NGM_KSOCKET_GETPEERNAME, "getpeername", NULL, &ng_ksocket_sockaddr_type }, { NGM_KSOCKET_COOKIE, NGM_KSOCKET_SETOPT, "setopt", &ng_ksocket_sockopt_type, NULL }, { NGM_KSOCKET_COOKIE, NGM_KSOCKET_GETOPT, "getopt", &ng_ksocket_sockopt_type, &ng_ksocket_sockopt_type }, { 0 } }; /* Node type descriptor */ static struct ng_type ng_ksocket_typestruct = { NG_VERSION, NG_KSOCKET_NODE_TYPE, NULL, ng_ksocket_constructor, ng_ksocket_rcvmsg, ng_ksocket_rmnode, ng_ksocket_newhook, NULL, NULL, ng_ksocket_rcvdata, ng_ksocket_rcvdata, ng_ksocket_disconnect, ng_ksocket_cmds }; NETGRAPH_INIT(ksocket, &ng_ksocket_typestruct); #define ERROUT(x) do { error = (x); goto done; } while (0) /************************************************************************ NETGRAPH NODE STUFF ************************************************************************/ /* * Node type constructor */ static int ng_ksocket_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_ksocket_typestruct, nodep))) { FREE(priv, M_NETGRAPH); return (error); } (*nodep)->private = priv; /* Done */ return (0); } /* * Give our OK for a hook to be added. The hook name is of the * form "::" where the three components may * be decimal numbers or else aliases from the above lists. * * Connecting a hook amounts to opening the socket. Disconnecting * the hook closes the socket and destroys the node as well. */ static int ng_ksocket_newhook(node_p node, hook_p hook, const char *name0) { struct proc *p = curproc ? curproc : &proc0; /* XXX broken */ const priv_p priv = node->private; char *s1, *s2, name[NG_HOOKLEN+1]; int family, type, protocol, error; /* Check if we're already connected */ if (priv->hook != NULL) return (EISCONN); /* Extract family, type, and protocol from hook name */ snprintf(name, sizeof(name), "%s", name0); s1 = name; if ((s2 = index(s1, '/')) == NULL) return (EINVAL); *s2++ = '\0'; if ((family = ng_ksocket_parse(ng_ksocket_families, s1, 0)) == -1) return (EINVAL); s1 = s2; if ((s2 = index(s1, '/')) == NULL) return (EINVAL); *s2++ = '\0'; if ((type = ng_ksocket_parse(ng_ksocket_types, s1, 0)) == -1) return (EINVAL); s1 = s2; if ((protocol = ng_ksocket_parse(ng_ksocket_protos, s1, family)) == -1) return (EINVAL); /* Create the socket */ if ((error = socreate(family, &priv->so, type, protocol, p)) != 0) return (error); /* XXX call soreserve() ? */ /* Add our hook for incoming data */ priv->so->so_upcallarg = (caddr_t)node; priv->so->so_upcall = ng_ksocket_incoming; priv->so->so_rcv.sb_flags |= SB_UPCALL; /* OK */ priv->hook = hook; return (0); } /* * Receive a control message */ static int ng_ksocket_rcvmsg(node_p node, struct ng_mesg *msg, const char *raddr, struct ng_mesg **rptr) { struct proc *p = curproc ? curproc : &proc0; /* XXX broken */ const priv_p priv = node->private; struct socket *const so = priv->so; struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_KSOCKET_COOKIE: switch (msg->header.cmd) { case NGM_KSOCKET_BIND: { struct sockaddr *const sa = (struct sockaddr *)msg->data; /* Sanity check */ if (msg->header.arglen < SADATA_OFFSET || msg->header.arglen < sa->sa_len) ERROUT(EINVAL); if (so == NULL) ERROUT(ENXIO); /* Bind */ error = sobind(so, sa, p); break; } case NGM_KSOCKET_LISTEN: { /* Sanity check */ if (msg->header.arglen != sizeof(int)) ERROUT(EINVAL); if (so == NULL) ERROUT(ENXIO); /* Listen */ if ((error = solisten(so, *((int *)msg->data), p)) != 0) break; /* Notify sender when we get a connection attempt */ /* XXX implement me */ error = ENODEV; break; } case NGM_KSOCKET_ACCEPT: { /* Sanity check */ if (msg->header.arglen != 0) ERROUT(EINVAL); if (so == NULL) ERROUT(ENXIO); /* Accept on the socket in a non-blocking way */ /* Create a new ksocket node for the new connection */ /* Return a response with the peer's sockaddr and the absolute name of the newly created node */ /* XXX implement me */ error = ENODEV; break; } case NGM_KSOCKET_CONNECT: { struct sockaddr *const sa = (struct sockaddr *)msg->data; /* Sanity check */ if (msg->header.arglen < SADATA_OFFSET || msg->header.arglen < sa->sa_len) ERROUT(EINVAL); if (so == NULL) ERROUT(ENXIO); /* Do connect */ if ((so->so_state & SS_ISCONNECTING) != 0) ERROUT(EALREADY); if ((error = soconnect(so, sa, p)) != 0) { so->so_state &= ~SS_ISCONNECTING; ERROUT(error); } if ((so->so_state & SS_ISCONNECTING) != 0) /* Notify sender when we connect */ /* XXX implement me */ ERROUT(EINPROGRESS); break; } case NGM_KSOCKET_GETNAME: case NGM_KSOCKET_GETPEERNAME: { int (*func)(struct socket *so, struct sockaddr **nam); struct sockaddr *sa = NULL; int len; /* Sanity check */ if (msg->header.arglen != 0) ERROUT(EINVAL); if (so == NULL) ERROUT(ENXIO); /* Get function */ if (msg->header.cmd == NGM_KSOCKET_GETPEERNAME) { if ((so->so_state & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0) ERROUT(ENOTCONN); func = so->so_proto->pr_usrreqs->pru_peeraddr; } else func = so->so_proto->pr_usrreqs->pru_sockaddr; /* Get local or peer address */ if ((error = (*func)(so, &sa)) != 0) goto bail; len = (sa == NULL) ? 0 : sa->sa_len; /* Send it back in a response */ NG_MKRESPONSE(resp, msg, len, M_NOWAIT); if (resp == NULL) { error = ENOMEM; goto bail; } bcopy(sa, resp->data, len); bail: /* Cleanup */ if (sa != NULL) FREE(sa, M_SONAME); break; } case NGM_KSOCKET_GETOPT: { struct ng_ksocket_sockopt *ksopt = (struct ng_ksocket_sockopt *)msg->data; struct sockopt sopt; /* Sanity check */ if (msg->header.arglen != sizeof(*ksopt)) ERROUT(EINVAL); if (so == NULL) ERROUT(ENXIO); /* Get response with room for option value */ NG_MKRESPONSE(resp, msg, sizeof(*ksopt) + NG_KSOCKET_MAX_OPTLEN, M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); /* Get socket option, and put value in the response */ sopt.sopt_dir = SOPT_GET; sopt.sopt_level = ksopt->level; sopt.sopt_name = ksopt->name; sopt.sopt_p = p; sopt.sopt_valsize = NG_KSOCKET_MAX_OPTLEN; ksopt = (struct ng_ksocket_sockopt *)resp->data; sopt.sopt_val = ksopt->value; if ((error = sogetopt(so, &sopt)) != 0) { FREE(resp, M_NETGRAPH); break; } /* Set actual value length */ resp->header.arglen = sizeof(*ksopt) + sopt.sopt_valsize; break; } case NGM_KSOCKET_SETOPT: { struct ng_ksocket_sockopt *const ksopt = (struct ng_ksocket_sockopt *)msg->data; const int valsize = msg->header.arglen - sizeof(*ksopt); struct sockopt sopt; /* Sanity check */ if (valsize < 0) ERROUT(EINVAL); if (so == NULL) ERROUT(ENXIO); /* Set socket option */ sopt.sopt_dir = SOPT_SET; sopt.sopt_level = ksopt->level; sopt.sopt_name = ksopt->name; sopt.sopt_val = ksopt->value; sopt.sopt_valsize = valsize; sopt.sopt_p = p; error = sosetopt(so, &sopt); 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 incoming data on our hook. Send it out the socket. */ static int ng_ksocket_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { struct proc *p = curproc ? curproc : &proc0; /* XXX broken */ const node_p node = hook->node; const priv_p priv = node->private; struct socket *const so = priv->so; int error; NG_FREE_META(meta); error = (*so->so_proto->pr_usrreqs->pru_sosend)(so, 0, 0, m, 0, 0, p); return (error); } /* * Destroy node */ static int ng_ksocket_rmnode(node_p node) { const priv_p priv = node->private; /* Close our socket (if any) */ if (priv->so != NULL) { priv->so->so_upcall = NULL; priv->so->so_rcv.sb_flags &= ~SB_UPCALL; soclose(priv->so); priv->so = NULL; } /* 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_ksocket_disconnect(hook_p hook) { KASSERT(hook->node->numhooks == 0, ("%s: numhooks=%d?", __FUNCTION__, hook->node->numhooks)); ng_rmnode(hook->node); return (0); } /************************************************************************ HELPER STUFF ************************************************************************/ /* * When incoming data is appended to the socket, we get notified here. */ static void ng_ksocket_incoming(struct socket *so, void *arg, int waitflag) { const node_p node = arg; const priv_p priv = node->private; meta_p meta = NULL; struct sockaddr *nam; struct mbuf *m; struct uio auio; int s, flags, error; s = splnet(); /* Sanity check */ if ((node->flags & NG_INVALID) != 0) { splx(s); return; } KASSERT(so == priv->so, ("%s: wrong socket", __FUNCTION__)); KASSERT(priv->hook != NULL, ("%s: no hook", __FUNCTION__)); /* Read and forward available mbuf's */ auio.uio_procp = NULL; auio.uio_resid = 1000000000; flags = MSG_DONTWAIT; do { if ((error = (*so->so_proto->pr_usrreqs->pru_soreceive) (so, &nam, &auio, &m, (struct mbuf **)0, &flags)) == 0 && m != NULL) { struct mbuf *n; /* Don't trust the various socket layers to get the packet header and length correct (eg. kern/15175) */ for (n = m, m->m_pkthdr.len = 0; n; n = n->m_next) m->m_pkthdr.len += n->m_len; NG_SEND_DATA(error, priv->hook, m, meta); } } while (error == 0 && m != NULL); splx(s); } /* * Parse out either an integer value or an alias. */ static int ng_ksocket_parse(const struct ng_ksocket_alias *aliases, const char *s, int family) { int k, val; char *eptr; /* Try aliases */ for (k = 0; aliases[k].name != NULL; k++) { if (strcmp(s, aliases[k].name) == 0 && aliases[k].family == family) return aliases[k].value; } /* Try parsing as a number */ val = (int)strtoul(s, &eptr, 10); if (val < 0 || *eptr != '\0') return (-1); return (val); } Index: stable/3/sys/netgraph/ng_ksocket.h =================================================================== --- stable/3/sys/netgraph/ng_ksocket.h (revision 67531) +++ stable/3/sys/netgraph/ng_ksocket.h (revision 67532) @@ -1,84 +1,84 @@ /* * ng_ksocket.h * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * - * Author: Archie Cobbs + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_ksocket.h,v 1.1 1999/11/16 20:04:40 archie Exp $ */ #ifndef _NETGRAPH_KSOCKET_H_ #define _NETGRAPH_KSOCKET_H_ /* Node type name and magic cookie */ #define NG_KSOCKET_NODE_TYPE "ksocket" #define NGM_KSOCKET_COOKIE 942710669 /* For NGM_KSOCKET_SETOPT and NGM_KSOCKET_GETOPT control messages */ struct ng_ksocket_sockopt { u_int32_t level; /* second arg of [gs]etsockopt() */ u_int32_t name; /* third arg of [gs]etsockopt() */ u_char value[0]; /* fourth arg of [gs]etsockopt() */ }; /* Max length socket option we can return via NGM_KSOCKET_GETOPT XXX This should not be necessary, we should dynamically size XXX the response. Until then.. */ #define NG_KSOCKET_MAX_OPTLEN 1024 /* Keep this in sync with the above structure definition */ #define NG_KSOCKET_SOCKOPT_INFO(svtype) { \ { \ { "level", &ng_parse_int32_type }, \ { "name", &ng_parse_int32_type }, \ { "value", (svtype) }, \ { NULL }, \ } \ } /* Netgraph commands */ enum { NGM_KSOCKET_BIND = 1, NGM_KSOCKET_LISTEN, NGM_KSOCKET_ACCEPT, NGM_KSOCKET_CONNECT, NGM_KSOCKET_GETNAME, NGM_KSOCKET_GETPEERNAME, NGM_KSOCKET_SETOPT, NGM_KSOCKET_GETOPT, }; #endif /* _NETGRAPH_KSOCKET_H_ */ Index: stable/3/sys/netgraph/ng_lmi.c =================================================================== --- stable/3/sys/netgraph/ng_lmi.c (revision 67531) +++ stable/3/sys/netgraph/ng_lmi.c (revision 67532) @@ -1,1092 +1,1092 @@ /* * 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 + * Author: Julian Elischer * * $FreeBSD$ * $Whistle: ng_lmi.c,v 1.38 1999/11/01 09:24:52 julian Exp $ */ /* * This node performs the frame relay LMI protocol. It knows how * to do ITU Annex A, ANSI Annex D, and "Group-of-Four" variants * of the protocol. * * A specific protocol can be forced by connecting the corresponding * hook to DLCI 0 or 1023 (as appropriate) of a frame relay link. * * Alternately, this node can do auto-detection of the LMI protocol * by connecting hook "auto0" to DLCI 0 and "auto1023" to DLCI 1023. */ #include #include #include #include #include #include #include #include #include #include /* * Human readable names for LMI */ #define NAME_ANNEXA NG_LMI_HOOK_ANNEXA #define NAME_ANNEXD NG_LMI_HOOK_ANNEXD #define NAME_GROUP4 NG_LMI_HOOK_GROUPOF4 #define NAME_NONE "None" #define MAX_DLCIS 128 #define MAXDLCI 1023 /* * DLCI states */ #define DLCI_NULL 0 #define DLCI_UP 1 #define DLCI_DOWN 2 /* * Any received LMI frame should be at least this long */ #define LMI_MIN_LENGTH 8 /* XXX verify */ /* * Netgraph node methods and type descriptor */ static ng_constructor_t nglmi_constructor; static ng_rcvmsg_t nglmi_rcvmsg; static ng_shutdown_t nglmi_rmnode; static ng_newhook_t nglmi_newhook; static ng_rcvdata_t nglmi_rcvdata; static ng_disconnect_t nglmi_disconnect; static int nglmi_checkdata(hook_p hook, struct mbuf *m, meta_p meta); static struct ng_type typestruct = { NG_VERSION, NG_LMI_NODE_TYPE, NULL, nglmi_constructor, nglmi_rcvmsg, nglmi_rmnode, nglmi_newhook, NULL, NULL, nglmi_rcvdata, nglmi_rcvdata, nglmi_disconnect, NULL }; NETGRAPH_INIT(lmi, &typestruct); /* * Info and status per node */ struct nglmi_softc { node_p node; /* netgraph node */ int flags; /* state */ int poll_count; /* the count of times for autolmi */ int poll_state; /* state of auto detect machine */ u_char remote_seq; /* sequence number the remote sent */ u_char local_seq; /* last sequence number we sent */ u_char protoID; /* 9 for group of 4, 8 otherwise */ u_long seq_retries; /* sent this how many time so far */ struct callout_handle handle; /* see timeout(9) */ int liv_per_full; int liv_rate; int livs; int need_full; hook_p lmi_channel; /* whatever we ended up using */ hook_p lmi_annexA; hook_p lmi_annexD; hook_p lmi_group4; hook_p lmi_channel0; /* auto-detect on DLCI 0 */ hook_p lmi_channel1023;/* auto-detect on DLCI 1023 */ char *protoname; /* cache protocol name */ u_char dlci_state[MAXDLCI + 1]; int invalidx; /* next dlci's to invalidate */ }; typedef struct nglmi_softc *sc_p; /* * Other internal functions */ static void LMI_ticker(void *arg); static void nglmi_startup_fixed(sc_p sc, hook_p hook); static void nglmi_startup_auto(sc_p sc); static void nglmi_startup(sc_p sc); static void nglmi_inquire(sc_p sc, int full); static void ngauto_state_machine(sc_p sc); /* * Values for 'flags' field * NB: the SCF_CONNECTED flag is set if and only if the timer is running. */ #define SCF_CONNECTED 0x01 /* connected to something */ #define SCF_AUTO 0x02 /* we are auto-detecting */ #define SCF_FIXED 0x04 /* we are fixed from the start */ #define SCF_LMITYPE 0x18 /* mask for determining Annex mode */ #define SCF_NOLMI 0x00 /* no LMI type selected yet */ #define SCF_ANNEX_A 0x08 /* running annex A mode */ #define SCF_ANNEX_D 0x10 /* running annex D mode */ #define SCF_GROUP4 0x18 /* running group of 4 */ #define SETLMITYPE(sc, annex) \ do { \ (sc)->flags &= ~SCF_LMITYPE; \ (sc)->flags |= (annex); \ } while (0) #define NOPROTO(sc) (((sc)->flags & SCF_LMITYPE) == SCF_NOLMI) #define ANNEXA(sc) (((sc)->flags & SCF_LMITYPE) == SCF_ANNEX_A) #define ANNEXD(sc) (((sc)->flags & SCF_LMITYPE) == SCF_ANNEX_D) #define GROUP4(sc) (((sc)->flags & SCF_LMITYPE) == SCF_GROUP4) #define LMIPOLLSIZE 3 #define LMI_PATIENCE 8 /* declare all DLCI DOWN after N LMI failures */ /* * Node constructor */ static int nglmi_constructor(node_p *nodep) { sc_p sc; int error = 0; MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_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; } m->m_pkthdr.rcvif = NULL; /* Allocate a meta struct (and leave some slop for options to be * added by other modules). */ /* MALLOC(meta, meta_p, sizeof( struct ng_meta) + META_PAD, * M_NETGRAPH, M_NOWAIT); */ MALLOC(meta, meta_p, sizeof(*meta) + META_PAD, M_NETGRAPH, M_NOWAIT); if (meta != NULL) { /* if it failed, well, it was optional anyhow */ meta->used_len = (u_short) sizeof(struct ng_meta); meta->allocated_len = (u_short) sizeof(struct ng_meta) + META_PAD; meta->flags = 0; meta->priority = NG_LMI_LMI_PRIORITY; meta->discardability = -1; } m->m_data += 4; /* leave some room for a header */ cptr = start = mtod(m, char *); /* add in the header for an LMI inquiry. */ *cptr++ = 0x03; /* UI frame */ if (GROUP4(sc)) *cptr++ = 0x09; /* proto discriminator */ else *cptr++ = 0x08; /* proto discriminator */ *cptr++ = 0x00; /* call reference */ *cptr++ = 0x75; /* inquiry */ /* If we are Annex-D, there is this extra thing.. */ if (ANNEXD(sc)) *cptr++ = 0x95; /* ??? */ /* Add a request type */ if (ANNEXA(sc)) *cptr++ = 0x51; /* report type */ else *cptr++ = 0x01; /* report type */ *cptr++ = 0x01; /* size = 1 */ if (full) *cptr++ = 0x00; /* full */ else *cptr++ = 0x01; /* partial */ /* Add a link verification IE */ if (ANNEXA(sc)) *cptr++ = 0x53; /* verification IE */ else *cptr++ = 0x03; /* verification IE */ *cptr++ = 0x02; /* 2 extra bytes */ *cptr++ = sc->local_seq; *cptr++ = sc->remote_seq; sc->seq_retries++; /* Send it */ m->m_len = m->m_pkthdr.len = cptr - start; NG_SEND_DATA(error, sc->lmi_channel, m, meta); /* If we've been sending requests for long enough, and there has * been no response, then mark as DOWN, any DLCIs that are UP. */ if (sc->seq_retries == LMI_PATIENCE) { int count; for (count = 0; count < MAXDLCI; count++) if (sc->dlci_state[count] == DLCI_UP) sc->dlci_state[count] = DLCI_DOWN; } } /* * State machine for LMI auto-detect. The transitions are ordered * to try the more likely possibilities first. */ static void ngauto_state_machine(sc_p sc) { if ((sc->poll_count <= 0) || (sc->poll_count > LMIPOLLSIZE)) { /* time to change states in the auto probe machine */ /* capture wild values of poll_count while we are at it */ sc->poll_count = LMIPOLLSIZE; sc->poll_state++; } switch (sc->poll_state) { case 7: log(LOG_WARNING, "nglmi: no response from exchange\n"); default: /* capture bad states */ sc->poll_state = 1; case 1: sc->lmi_channel = sc->lmi_channel0; SETLMITYPE(sc, SCF_ANNEX_D); break; case 2: sc->lmi_channel = sc->lmi_channel1023; SETLMITYPE(sc, SCF_ANNEX_D); break; case 3: sc->lmi_channel = sc->lmi_channel0; SETLMITYPE(sc, SCF_ANNEX_A); break; case 4: sc->lmi_channel = sc->lmi_channel1023; SETLMITYPE(sc, SCF_GROUP4); break; case 5: sc->lmi_channel = sc->lmi_channel1023; SETLMITYPE(sc, SCF_ANNEX_A); break; case 6: sc->lmi_channel = sc->lmi_channel0; SETLMITYPE(sc, SCF_GROUP4); break; } /* send an inquirey encoded appropriatly */ nglmi_inquire(sc, 0); sc->poll_count--; } /* * Receive a netgraph control message. */ static int nglmi_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp) { 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: stable/3/sys/netgraph/ng_lmi.h =================================================================== --- stable/3/sys/netgraph/ng_lmi.h (revision 67531) +++ stable/3/sys/netgraph/ng_lmi.h (revision 67532) @@ -1,80 +1,80 @@ /* * ng_lmi.h * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * - * Author: Archie Cobbs + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_lmi.h,v 1.9 1999/01/20 00:22:13 archie Exp $ */ #ifndef _NETGRAPH_LMI_H_ #define _NETGRAPH_LMI_H_ /* Node type name and magic cookie */ #define NG_LMI_NODE_TYPE "lmi" #define NGM_LMI_COOKIE 867184133 /* My hook names */ #define NG_LMI_HOOK_DEBUG "debug" #define NG_LMI_HOOK_ANNEXA "annexA" #define NG_LMI_HOOK_ANNEXD "annexD" #define NG_LMI_HOOK_GROUPOF4 "group4" #define NG_LMI_HOOK_AUTO0 "auto0" #define NG_LMI_HOOK_AUTO1023 "auto1023" /* Netgraph commands */ enum { NGM_LMI_GET_STATUS = 1, }; #define NGM_LMI_STAT_ARYSIZE (1024/8) struct nglmistat { u_char proto[12]; /* Active proto (same as hook name) */ u_char hook[12]; /* Active hook */ u_char fixed; /* Set to fixed LMI mode */ u_char autod; /* Currently auto-detecting */ u_char seen[NGM_LMI_STAT_ARYSIZE]; /* DLCIs ever seen */ u_char up[NGM_LMI_STAT_ARYSIZE]; /* DLCIs currently up */ }; /* Some default values */ #define NG_LMI_KEEPALIVE_RATE 10 /* seconds per keepalive */ #define NG_LMI_POLL_RATE 3 /* faster when AUTO polling */ #define NG_LMI_SEQ_PER_FULL 5 /* keepalives per full status */ #define NG_LMI_LMI_PRIORITY 64 /* priority for LMI data */ #endif /* _NETGRAPH_LMI_H_ */ Index: stable/3/sys/netgraph/ng_message.h =================================================================== --- stable/3/sys/netgraph/ng_message.h (revision 67531) +++ stable/3/sys/netgraph/ng_message.h (revision 67532) @@ -1,329 +1,329 @@ /* * ng_message.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 + * Author: Julian Elischer * * $FreeBSD$ * $Whistle: ng_message.h,v 1.12 1999/01/25 01:17:44 archie Exp $ */ #ifndef _NETGRAPH_NG_MESSAGE_H_ #define _NETGRAPH_NG_MESSAGE_H_ 1 /* ASCII string size limits */ #define NG_TYPELEN 15 /* max type name len (16 with null) */ #define NG_HOOKLEN 15 /* max hook name len (16 with null) */ #define NG_NODELEN 15 /* max node name len (16 with null) */ #define NG_PATHLEN 511 /* max path len (512 with null) */ #define NG_CMDSTRLEN 15 /* max command string (16 with null) */ #define NG_TEXTRESPONSE 1024 /* allow this length for a text response */ /* A netgraph message */ struct ng_mesg { struct ng_msghdr { u_char version; /* must == NG_VERSION */ u_char spare; /* pad to 2 bytes */ u_int16_t arglen; /* length of data */ u_int32_t flags; /* message status */ u_int32_t token; /* match with reply */ u_int32_t typecookie; /* node's type cookie */ u_int32_t cmd; /* command identifier */ u_char cmdstr[NG_CMDSTRLEN+1]; /* cmd string + \0 */ } header; char data[0]; /* placeholder for actual data */ }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_NG_MESG_INFO(dtype) { \ { \ { "version", &ng_parse_int8_type }, \ { "spare", &ng_parse_int8_type }, \ { "arglen", &ng_parse_int16_type }, \ { "flags", &ng_parse_int32_type }, \ { "token", &ng_parse_int32_type }, \ { "typecookie", &ng_parse_int32_type }, \ { "cmd", &ng_parse_int32_type }, \ { "cmdstr", &ng_parse_cmdbuf_type }, \ { "data", (dtype) }, \ { NULL }, \ } \ } /* Negraph type binary compatibility field */ #define NG_VERSION 2 /* Flags field flags */ #define NGF_ORIG 0x0000 /* the msg is the original request */ #define NGF_RESP 0x0001 /* the message is a response */ /* Type of a unique node ID */ #define ng_ID_t unsigned int /* * Here we describe the "generic" messages that all nodes inherently * understand. With the exception of NGM_TEXT_STATUS, these are handled * automatically by the base netgraph code. */ /* Generic message type cookie */ #define NGM_GENERIC_COOKIE 851672668 /* Generic messages defined for this type cookie */ #define NGM_SHUTDOWN 1 /* shut down node */ #define NGM_MKPEER 2 /* create and attach a peer node */ #define NGM_CONNECT 3 /* connect two nodes */ #define NGM_NAME 4 /* give a node a name */ #define NGM_RMHOOK 5 /* break a connection btw. two nodes */ #define NGM_NODEINFO 6 /* get nodeinfo for the target */ #define NGM_LISTHOOKS 7 /* get list of hooks on node */ #define NGM_LISTNAMES 8 /* list all globally named nodes */ #define NGM_LISTNODES 9 /* list all nodes, named and unnamed */ #define NGM_LISTTYPES 10 /* list all installed node types */ #define NGM_TEXT_STATUS 11 /* (optional) get text status report */ #define NGM_BINARY2ASCII 12 /* convert struct ng_mesg to ascii */ #define NGM_ASCII2BINARY 13 /* convert ascii to struct ng_mesg */ /* Structure used for NGM_MKPEER */ struct ngm_mkpeer { char type[NG_TYPELEN + 1]; /* peer type */ char ourhook[NG_HOOKLEN + 1]; /* hook name */ char peerhook[NG_HOOKLEN + 1]; /* peer hook name */ }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_MKPEER_INFO() { \ { \ { "type", &ng_parse_typebuf_type }, \ { "ourhook", &ng_parse_hookbuf_type }, \ { "peerhook", &ng_parse_hookbuf_type }, \ { NULL }, \ } \ } /* Structure used for NGM_CONNECT */ struct ngm_connect { char path[NG_PATHLEN + 1]; /* peer path */ char ourhook[NG_HOOKLEN + 1]; /* hook name */ char peerhook[NG_HOOKLEN + 1]; /* peer hook name */ }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_CONNECT_INFO() { \ { \ { "path", &ng_parse_pathbuf_type }, \ { "ourhook", &ng_parse_hookbuf_type }, \ { "peerhook", &ng_parse_hookbuf_type }, \ { NULL }, \ } \ } /* Structure used for NGM_NAME */ struct ngm_name { char name[NG_NODELEN + 1]; /* node name */ }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_NAME_INFO() { \ { \ { "name", &ng_parse_nodebuf_type }, \ { NULL }, \ } \ } /* Structure used for NGM_RMHOOK */ struct ngm_rmhook { char ourhook[NG_HOOKLEN + 1]; /* hook name */ }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_RMHOOK_INFO() { \ { \ { "hook", &ng_parse_hookbuf_type }, \ { NULL }, \ } \ } /* Structure used for NGM_NODEINFO */ struct nodeinfo { char name[NG_NODELEN + 1]; /* node name (if any) */ char type[NG_TYPELEN + 1]; /* peer type */ ng_ID_t id; /* unique identifier */ u_int32_t hooks; /* number of active hooks */ }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_NODEINFO_INFO() { \ { \ { "name", &ng_parse_nodebuf_type }, \ { "type", &ng_parse_typebuf_type }, \ { "id", &ng_parse_int32_type }, \ { "hooks", &ng_parse_int32_type }, \ { NULL }, \ } \ } /* Structure used for NGM_LISTHOOKS */ struct linkinfo { char ourhook[NG_HOOKLEN + 1]; /* hook name */ char peerhook[NG_HOOKLEN + 1]; /* peer hook */ struct nodeinfo nodeinfo; }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_LINKINFO_INFO(nitype) { \ { \ { "ourhook", &ng_parse_hookbuf_type }, \ { "peerhook", &ng_parse_hookbuf_type }, \ { "nodeinfo", (nitype) }, \ { NULL }, \ } \ } struct hooklist { struct nodeinfo nodeinfo; /* node information */ struct linkinfo link[0]; /* info about each hook */ }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_HOOKLIST_INFO(nitype,litype) { \ { \ { "nodeinfo", (nitype) }, \ { "linkinfo", (litype) }, \ { NULL }, \ } \ } /* Structure used for NGM_LISTNAMES/NGM_LISTNODES */ struct namelist { u_int32_t numnames; struct nodeinfo nodeinfo[0]; }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_LISTNODES_INFO(niarraytype) { \ { \ { "numnames", &ng_parse_int32_type }, \ { "nodeinfo", (niarraytype) }, \ { NULL }, \ } \ } /* Structure used for NGM_LISTTYPES */ struct typeinfo { char type_name[NG_TYPELEN + 1]; /* name of type */ u_int32_t numnodes; /* number alive */ }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_TYPEINFO_INFO() { \ { \ { "typename", &ng_parse_typebuf_type }, \ { "typeinfo", &ng_parse_int32_type }, \ { NULL }, \ } \ } struct typelist { u_int32_t numtypes; struct typeinfo typeinfo[0]; }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_TYPELIST_INFO(tiarraytype) { \ { \ { "numtypes", &ng_parse_int32_type }, \ { "typeinfo", (tiarraytype) }, \ { NULL }, \ } \ } /* * For netgraph nodes that are somehow associated with file descriptors * (e.g., a device that has a /dev entry and is also a netgraph node), * we define a generic ioctl for requesting the corresponding nodeinfo * structure and for assigning a name (if there isn't one already). * * For these to you need to also #include . */ #define NGIOCGINFO _IOR('N', 40, struct nodeinfo) /* get node info */ #define NGIOCSETNAME _IOW('N', 41, struct ngm_name) /* set node name */ #ifdef KERNEL /* * Allocate and initialize a netgraph message "msg" with "len" * extra bytes of argument. Sets "msg" to NULL if fails. * Does not initialize token. */ #define NG_MKMESSAGE(msg, cookie, cmdid, len, how) \ do { \ MALLOC((msg), struct ng_mesg *, sizeof(struct ng_mesg) \ + (len), M_NETGRAPH, (how)); \ if ((msg) == NULL) \ break; \ bzero((msg), sizeof(struct ng_mesg) + (len)); \ (msg)->header.version = NG_VERSION; \ (msg)->header.typecookie = (cookie); \ (msg)->header.cmd = (cmdid); \ (msg)->header.arglen = (len); \ strncpy((msg)->header.cmdstr, #cmdid, \ sizeof((msg)->header.cmdstr) - 1); \ } while (0) /* * Allocate and initialize a response "rsp" to a message "msg" * with "len" extra bytes of argument. Sets "rsp" to NULL if fails. */ #define NG_MKRESPONSE(rsp, msg, len, how) \ do { \ MALLOC((rsp), struct ng_mesg *, sizeof(struct ng_mesg) \ + (len), M_NETGRAPH, (how)); \ if ((rsp) == NULL) \ break; \ bzero((rsp), sizeof(struct ng_mesg) + (len)); \ (rsp)->header.version = NG_VERSION; \ (rsp)->header.arglen = (len); \ (rsp)->header.token = (msg)->header.token; \ (rsp)->header.typecookie = (msg)->header.typecookie; \ (rsp)->header.cmd = (msg)->header.cmd; \ bcopy((msg)->header.cmdstr, (rsp)->header.cmdstr, \ sizeof((rsp)->header.cmdstr)); \ (rsp)->header.flags |= NGF_RESP; \ } while (0) #endif /* KERNEL */ #endif /* _NETGRAPH_NG_MESSAGE_H_ */ Index: stable/3/sys/netgraph/ng_parse.c =================================================================== --- stable/3/sys/netgraph/ng_parse.c (revision 67531) +++ stable/3/sys/netgraph/ng_parse.c (revision 67532) @@ -1,1603 +1,1603 @@ /* * ng_parse.c * * Copyright (c) 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 + * Author: Archie Cobbs * * $Whistle: ng_parse.c,v 1.3 1999/11/29 01:43:48 archie Exp $ * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include /* Compute alignment for primitive integral types */ struct int16_temp { char x; int16_t y; }; struct int32_temp { char x; int32_t y; }; struct int64_temp { char x; int64_t y; }; #define INT8_ALIGNMENT 1 #define INT16_ALIGNMENT ((int)&((struct int16_temp *)0)->y) #define INT32_ALIGNMENT ((int)&((struct int32_temp *)0)->y) #define INT64_ALIGNMENT ((int)&((struct int64_temp *)0)->y) /* Type of composite object: struct, array, or fixedarray */ enum comptype { CT_STRUCT, CT_ARRAY, CT_FIXEDARRAY, }; /* Composite types helper functions */ static int ng_parse_composite(const struct ng_parse_type *type, const char *s, int *off, const u_char *start, u_char *const buf, int *buflen, enum comptype ctype); static int ng_unparse_composite(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen, enum comptype ctype); static int ng_get_composite_elem_default(const struct ng_parse_type *type, int index, const u_char *start, u_char *buf, int *buflen, enum comptype ctype); static int ng_get_composite_len(const struct ng_parse_type *type, const u_char *start, const u_char *buf, enum comptype ctype); static const struct ng_parse_type *ng_get_composite_etype(const struct ng_parse_type *type, int index, enum comptype ctype); static int ng_parse_get_elem_pad(const struct ng_parse_type *type, int index, enum comptype ctype, int posn); /* Parsing helper functions */ static int ng_parse_skip_value(const char *s, int off, int *lenp); /* Poor man's virtual method calls */ #define METHOD(t,m) (ng_get_ ## m ## _method(t)) #define INVOKE(t,m) (*METHOD(t,m)) static ng_parse_t *ng_get_parse_method(const struct ng_parse_type *t); static ng_unparse_t *ng_get_unparse_method(const struct ng_parse_type *t); static ng_getDefault_t *ng_get_getDefault_method(const struct ng_parse_type *t); static ng_getAlign_t *ng_get_getAlign_method(const struct ng_parse_type *t); #define ALIGNMENT(t) (METHOD(t, getAlign) == NULL ? \ 0 : INVOKE(t, getAlign)(t)) /* For converting binary to string */ #define NG_PARSE_APPEND(fmt, args...) \ do { \ int len; \ \ len = snprintf((cbuf), (cbuflen), \ fmt , ## args); \ if (len >= (cbuflen)) \ return (ERANGE); \ (cbuf) += len; \ (cbuflen) -= len; \ } while (0) /************************************************************************ PUBLIC FUNCTIONS ************************************************************************/ /* * Convert an ASCII string to binary according to the supplied type descriptor */ int ng_parse(const struct ng_parse_type *type, const char *string, int *off, u_char *buf, int *buflen) { return INVOKE(type, parse)(type, string, off, buf, buf, buflen); } /* * Convert binary to an ASCII string according to the supplied type descriptor */ int ng_unparse(const struct ng_parse_type *type, const u_char *data, char *cbuf, int cbuflen) { int off = 0; return INVOKE(type, unparse)(type, data, &off, cbuf, cbuflen); } /* * Fill in the default value according to the supplied type descriptor */ int ng_parse_getDefault(const struct ng_parse_type *type, u_char *buf, int *buflen) { ng_getDefault_t *const func = METHOD(type, getDefault); if (func == NULL) return (EOPNOTSUPP); return (*func)(type, buf, buf, buflen); } /************************************************************************ STRUCTURE TYPE ************************************************************************/ static int ng_struct_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { return ng_parse_composite(type, s, off, start, buf, buflen, CT_STRUCT); } static int ng_struct_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { return ng_unparse_composite(type, data, off, cbuf, cbuflen, CT_STRUCT); } static int ng_struct_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { int off = 0; return ng_parse_composite(type, "{}", &off, start, buf, buflen, CT_STRUCT); } static int ng_struct_getAlign(const struct ng_parse_type *type) { const struct ng_parse_struct_info *si = type->info; const struct ng_parse_struct_field *field; int align = 0; for (field = si->fields; field->name != NULL; field++) { int falign = ALIGNMENT(field->type); if (falign > align) align = falign; } return align; } const struct ng_parse_type ng_parse_struct_type = { NULL, NULL, NULL, ng_struct_parse, ng_struct_unparse, ng_struct_getDefault, ng_struct_getAlign }; /************************************************************************ FIXED LENGTH ARRAY TYPE ************************************************************************/ static int ng_fixedarray_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { return ng_parse_composite(type, s, off, start, buf, buflen, CT_FIXEDARRAY); } static int ng_fixedarray_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { return ng_unparse_composite(type, data, off, cbuf, cbuflen, CT_FIXEDARRAY); } static int ng_fixedarray_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { int off = 0; return ng_parse_composite(type, "[]", &off, start, buf, buflen, CT_FIXEDARRAY); } static int ng_fixedarray_getAlign(const struct ng_parse_type *type) { const struct ng_parse_fixedarray_info *fi = type->info; return ALIGNMENT(fi->elementType); } const struct ng_parse_type ng_parse_fixedarray_type = { NULL, NULL, NULL, ng_fixedarray_parse, ng_fixedarray_unparse, ng_fixedarray_getDefault, ng_fixedarray_getAlign }; /************************************************************************ VARIABLE LENGTH ARRAY TYPE ************************************************************************/ static int ng_array_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { return ng_parse_composite(type, s, off, start, buf, buflen, CT_ARRAY); } static int ng_array_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { return ng_unparse_composite(type, data, off, cbuf, cbuflen, CT_ARRAY); } static int ng_array_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { int off = 0; return ng_parse_composite(type, "[]", &off, start, buf, buflen, CT_ARRAY); } static int ng_array_getAlign(const struct ng_parse_type *type) { const struct ng_parse_array_info *ai = type->info; return ALIGNMENT(ai->elementType); } const struct ng_parse_type ng_parse_array_type = { NULL, NULL, NULL, ng_array_parse, ng_array_unparse, ng_array_getDefault, ng_array_getAlign }; /************************************************************************ INT8 TYPE ************************************************************************/ static int ng_int8_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { long val; int8_t val8; char *eptr; val = strtol(s + *off, &eptr, 0); if (val < -0x80 || val > 0xff || eptr == s + *off) return (EINVAL); *off = eptr - s; val8 = (int8_t)val; bcopy(&val8, buf, sizeof(int8_t)); *buflen = sizeof(int8_t); return (0); } static int ng_int8_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { int8_t val; bcopy(data + *off, &val, sizeof(int8_t)); NG_PARSE_APPEND("%d", (int)val); *off += sizeof(int8_t); return (0); } static int ng_int8_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { int8_t val; if (*buflen < sizeof(int8_t)) return (ERANGE); val = 0; bcopy(&val, buf, sizeof(int8_t)); *buflen = sizeof(int8_t); return (0); } static int ng_int8_getAlign(const struct ng_parse_type *type) { return INT8_ALIGNMENT; } const struct ng_parse_type ng_parse_int8_type = { NULL, NULL, NULL, ng_int8_parse, ng_int8_unparse, ng_int8_getDefault, ng_int8_getAlign }; /************************************************************************ INT16 TYPE ************************************************************************/ static int ng_int16_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { long val; int16_t val16; char *eptr; val = strtol(s + *off, &eptr, 0); if (val < -0x8000 || val > 0xffff || eptr == s + *off) return (EINVAL); *off = eptr - s; val16 = (int16_t)val; bcopy(&val16, buf, sizeof(int16_t)); *buflen = sizeof(int16_t); return (0); } static int ng_int16_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { int16_t val; bcopy(data + *off, &val, sizeof(int16_t)); NG_PARSE_APPEND("%d", (int)val); *off += sizeof(int16_t); return (0); } static int ng_int16_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { int16_t val; if (*buflen < sizeof(int16_t)) return (ERANGE); val = 0; bcopy(&val, buf, sizeof(int16_t)); *buflen = sizeof(int16_t); return (0); } static int ng_int16_getAlign(const struct ng_parse_type *type) { return INT16_ALIGNMENT; } const struct ng_parse_type ng_parse_int16_type = { NULL, NULL, NULL, ng_int16_parse, ng_int16_unparse, ng_int16_getDefault, ng_int16_getAlign }; /************************************************************************ INT32 TYPE ************************************************************************/ static int ng_int32_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { long val; /* assumes long is at least 32 bits */ int32_t val32; char *eptr; val = strtol(s + *off, &eptr, 0); if (val < (long)-0x80000000 || val > (u_long)0xffffffff || eptr == s + *off) return (EINVAL); *off = eptr - s; val32 = (int32_t)val; bcopy(&val32, buf, sizeof(int32_t)); *buflen = sizeof(int32_t); return (0); } static int ng_int32_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { int32_t val; bcopy(data + *off, &val, sizeof(int32_t)); NG_PARSE_APPEND("%ld", (long)val); *off += sizeof(int32_t); return (0); } static int ng_int32_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { int32_t val; if (*buflen < sizeof(int32_t)) return (ERANGE); val = 0; bcopy(&val, buf, sizeof(int32_t)); *buflen = sizeof(int32_t); return (0); } static int ng_int32_getAlign(const struct ng_parse_type *type) { return INT32_ALIGNMENT; } const struct ng_parse_type ng_parse_int32_type = { NULL, NULL, NULL, ng_int32_parse, ng_int32_unparse, ng_int32_getDefault, ng_int32_getAlign }; /************************************************************************ INT64 TYPE ************************************************************************/ static int ng_int64_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { quad_t val; int64_t val64; char *eptr; val = strtoq(s + *off, &eptr, 0); if (eptr == s + *off) return (EINVAL); *off = eptr - s; val64 = (int64_t)val; bcopy(&val64, buf, sizeof(int64_t)); *buflen = sizeof(int64_t); return (0); } static int ng_int64_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { int64_t val; bcopy(data + *off, &val, sizeof(int64_t)); NG_PARSE_APPEND("%lld", (long long)val); *off += sizeof(int64_t); return (0); } static int ng_int64_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { int64_t val; if (*buflen < sizeof(int64_t)) return (ERANGE); val = 0; bcopy(&val, buf, sizeof(int64_t)); *buflen = sizeof(int64_t); return (0); } static int ng_int64_getAlign(const struct ng_parse_type *type) { return INT64_ALIGNMENT; } const struct ng_parse_type ng_parse_int64_type = { NULL, NULL, NULL, ng_int64_parse, ng_int64_unparse, ng_int64_getDefault, ng_int64_getAlign }; /************************************************************************ STRING TYPE ************************************************************************/ static int ng_string_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { char *sval; int len; if ((sval = ng_get_string_token(s, off, &len)) == NULL) return (EINVAL); *off += len; len = strlen(sval) + 1; bcopy(sval, buf, len); FREE(sval, M_NETGRAPH); *buflen = len; return (0); } static int ng_string_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { const char *const raw = (const char *)data + *off; char *const s = ng_encode_string(raw); if (s == NULL) return (ENOMEM); NG_PARSE_APPEND("%s", s); *off += strlen(raw) + 1; FREE(s, M_NETGRAPH); return (0); } static int ng_string_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { if (*buflen < 1) return (ERANGE); buf[0] = (u_char)'\0'; *buflen = 1; return (0); } const struct ng_parse_type ng_parse_string_type = { NULL, NULL, NULL, ng_string_parse, ng_string_unparse, ng_string_getDefault, NULL }; /************************************************************************ FIXED BUFFER STRING TYPE ************************************************************************/ static int ng_fixedstring_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { const struct ng_parse_fixedstring_info *const fi = type->info; char *sval; int len; if ((sval = ng_get_string_token(s, off, &len)) == NULL) return (EINVAL); if (strlen(sval) + 1 > fi->bufSize) return (E2BIG); *off += len; len = strlen(sval) + 1; bcopy(sval, buf, len); FREE(sval, M_NETGRAPH); bzero(buf + len, fi->bufSize - len); *buflen = fi->bufSize; return (0); } static int ng_fixedstring_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { const struct ng_parse_fixedstring_info *const fi = type->info; int error, temp = *off; if ((error = ng_string_unparse(type, data, &temp, cbuf, cbuflen)) != 0) return (error); *off += fi->bufSize; return (0); } static int ng_fixedstring_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { const struct ng_parse_fixedstring_info *const fi = type->info; if (*buflen < fi->bufSize) return (ERANGE); bzero(buf, fi->bufSize); *buflen = fi->bufSize; return (0); } const struct ng_parse_type ng_parse_fixedstring_type = { NULL, NULL, NULL, ng_fixedstring_parse, ng_fixedstring_unparse, ng_fixedstring_getDefault, NULL }; const struct ng_parse_fixedstring_info ng_parse_nodebuf_info = { NG_NODELEN + 1 }; const struct ng_parse_type ng_parse_nodebuf_type = { &ng_parse_fixedstring_type, &ng_parse_nodebuf_info }; const struct ng_parse_fixedstring_info ng_parse_hookbuf_info = { NG_HOOKLEN + 1 }; const struct ng_parse_type ng_parse_hookbuf_type = { &ng_parse_fixedstring_type, &ng_parse_hookbuf_info }; const struct ng_parse_fixedstring_info ng_parse_pathbuf_info = { NG_PATHLEN + 1 }; const struct ng_parse_type ng_parse_pathbuf_type = { &ng_parse_fixedstring_type, &ng_parse_pathbuf_info }; const struct ng_parse_fixedstring_info ng_parse_typebuf_info = { NG_TYPELEN + 1 }; const struct ng_parse_type ng_parse_typebuf_type = { &ng_parse_fixedstring_type, &ng_parse_typebuf_info }; const struct ng_parse_fixedstring_info ng_parse_cmdbuf_info = { NG_CMDSTRLEN + 1 }; const struct ng_parse_type ng_parse_cmdbuf_type = { &ng_parse_fixedstring_type, &ng_parse_cmdbuf_info }; /************************************************************************ IP ADDRESS TYPE ************************************************************************/ static int ng_ipaddr_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { int i, error; for (i = 0; i < 4; i++) { if ((error = ng_int8_parse(&ng_parse_int8_type, s, off, start, buf + i, buflen)) != 0) return (error); if (i < 3 && s[*off] != '.') return (EINVAL); (*off)++; } *buflen = 4; return (0); } static int ng_ipaddr_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { struct in_addr ip; bcopy(data + *off, &ip, sizeof(ip)); NG_PARSE_APPEND("%d.%d.%d.%d", ((u_char *)&ip)[0], ((u_char *)&ip)[1], ((u_char *)&ip)[2], ((u_char *)&ip)[3]); *off += sizeof(ip); return (0); } static int ng_ipaddr_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { struct in_addr ip = { 0 }; if (*buflen < sizeof(ip)) return (ERANGE); bcopy(&ip, buf, sizeof(ip)); *buflen = sizeof(ip); return (0); } const struct ng_parse_type ng_parse_ipaddr_type = { NULL, NULL, NULL, ng_ipaddr_parse, ng_ipaddr_unparse, ng_ipaddr_getDefault, ng_int32_getAlign }; /************************************************************************ BYTE ARRAY TYPE ************************************************************************/ /* Get the length of a byte array */ static int ng_parse_bytearray_subtype_getLength(const struct ng_parse_type *type, const u_char *start, const u_char *buf) { ng_parse_array_getLength_t *const getLength = type->private; return (*getLength)(type, start, buf); } static int ng_bytearray_elem_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { int8_t val; bcopy(data + *off, &val, sizeof(int8_t)); NG_PARSE_APPEND("0x%02x", (int)val & 0xff); /* always hex format */ *off += sizeof(int8_t); return (0); } /* Byte array element type is int8, but always output in hex format */ const struct ng_parse_type ng_parse_bytearray_elem_type = { &ng_parse_int8_type, NULL, NULL, NULL, ng_bytearray_elem_unparse, NULL, NULL }; static const struct ng_parse_array_info ng_parse_bytearray_subtype_info = { &ng_parse_bytearray_elem_type, &ng_parse_bytearray_subtype_getLength, NULL }; static const struct ng_parse_type ng_parse_bytearray_subtype = { &ng_parse_array_type, &ng_parse_bytearray_subtype_info }; static int ng_bytearray_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { char *str; int toklen; /* We accept either an array of bytes or a string constant */ if ((str = ng_get_string_token(s, off, &toklen)) != NULL) { ng_parse_array_getLength_t *const getLength = type->info; int arraylen, slen; arraylen = (*getLength)(type, start, buf); if (arraylen > *buflen) { FREE(str, M_NETGRAPH); return (ERANGE); } slen = strlen(str) + 1; if (slen > arraylen) { FREE(str, M_NETGRAPH); return (E2BIG); } bcopy(str, buf, slen); bzero(buf + slen, arraylen - slen); FREE(str, M_NETGRAPH); *off += toklen; *buflen = arraylen; return (0); } else { struct ng_parse_type subtype; subtype = ng_parse_bytearray_subtype; (const void *)subtype.private = type->info; return ng_array_parse(&subtype, s, off, start, buf, buflen); } } static int ng_bytearray_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { struct ng_parse_type subtype; subtype = ng_parse_bytearray_subtype; (const void *)subtype.private = type->info; return ng_array_unparse(&subtype, data, off, cbuf, cbuflen); } static int ng_bytearray_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { struct ng_parse_type subtype; subtype = ng_parse_bytearray_subtype; (const void *)subtype.private = type->info; return ng_array_getDefault(&subtype, start, buf, buflen); } const struct ng_parse_type ng_parse_bytearray_type = { NULL, NULL, NULL, ng_bytearray_parse, ng_bytearray_unparse, ng_bytearray_getDefault, NULL }; /************************************************************************ STRUCT NG_MESG TYPE ************************************************************************/ /* Get msg->header.arglen when "buf" is pointing to msg->data */ static int ng_parse_ng_mesg_getLength(const struct ng_parse_type *type, const u_char *start, const u_char *buf) { const struct ng_mesg *msg; msg = (const struct ng_mesg *)(buf - sizeof(*msg)); return msg->header.arglen; } /* Type for the variable length data portion of a struct ng_mesg */ static const struct ng_parse_type ng_msg_data_type = { &ng_parse_bytearray_type, &ng_parse_ng_mesg_getLength }; /* Type for the entire struct ng_mesg header with data section */ static const struct ng_parse_struct_info ng_parse_ng_mesg_type_info = NG_GENERIC_NG_MESG_INFO(&ng_msg_data_type); const struct ng_parse_type ng_parse_ng_mesg_type = { &ng_parse_struct_type, &ng_parse_ng_mesg_type_info, }; /************************************************************************ COMPOSITE HELPER ROUTINES ************************************************************************/ /* * Convert a structure or array from ASCII to binary */ static int ng_parse_composite(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen, const enum comptype ctype) { const int num = ng_get_composite_len(type, start, buf, ctype); int nextIndex = 0; /* next implicit array index */ u_int index; /* field or element index */ int *foff; /* field value offsets in string */ int align, len, blen, error = 0; /* Initialize */ MALLOC(foff, int *, num * sizeof(*foff), M_NETGRAPH, M_NOWAIT); if (foff == NULL) { error = ENOMEM; goto done; } bzero(foff, num * sizeof(*foff)); /* Get opening brace/bracket */ if (ng_parse_get_token(s, off, &len) != (ctype == CT_STRUCT ? T_LBRACE : T_LBRACKET)) { error = EINVAL; goto done; } *off += len; /* Get individual element value positions in the string */ for (;;) { enum ng_parse_token tok; /* Check for closing brace/bracket */ tok = ng_parse_get_token(s, off, &len); if (tok == (ctype == CT_STRUCT ? T_RBRACE : T_RBRACKET)) { *off += len; break; } /* For arrays, the 'name' (ie, index) is optional, so distinguish name from values by seeing if the next token is an equals sign */ if (ctype != CT_STRUCT) { int len2, off2; char *eptr; /* If an opening brace/bracket, index is implied */ if (tok == T_LBRACE || tok == T_LBRACKET) { index = nextIndex++; goto gotIndex; } /* Might be an index, might be a value, either way... */ if (tok != T_WORD) { error = EINVAL; goto done; } /* If no equals sign follows, index is implied */ off2 = *off + len; if (ng_parse_get_token(s, &off2, &len2) != T_EQUALS) { index = nextIndex++; goto gotIndex; } /* Index was specified explicitly; parse it */ index = (u_int)strtoul(s + *off, &eptr, 0); if (index < 0 || eptr - (s + *off) != len) { error = EINVAL; goto done; } nextIndex = index + 1; *off += len + len2; gotIndex: } else { /* a structure field */ const struct ng_parse_struct_field *field = NULL; const struct ng_parse_struct_info *si = type->info; /* Find the field by name (required) in field list */ if (tok != T_WORD) { error = EINVAL; goto done; } for (index = 0; index < num; index++) { field = &si->fields[index]; if (strncmp(&s[*off], field->name, len) == 0 && field->name[len] == '\0') break; } if (index == num) { error = ENOENT; goto done; } *off += len; /* Get equals sign */ if (ng_parse_get_token(s, off, &len) != T_EQUALS) { error = EINVAL; goto done; } *off += len; } /* Check array index */ if (index >= num) { error = E2BIG; goto done; } /* Save value's position and skip over it for now */ if (foff[index] != 0) { error = EALREADY; /* duplicate */ goto done; } while (isspace(s[*off])) (*off)++; foff[index] = *off; if ((error = ng_parse_skip_value(s, *off, &len)) != 0) goto done; *off += len; } /* Now build binary structure from supplied values and defaults */ for (blen = index = 0; index < num; index++) { const struct ng_parse_type *const etype = ng_get_composite_etype(type, index, ctype); int k, pad, vlen; /* Zero-pad any alignment bytes */ pad = ng_parse_get_elem_pad(type, index, ctype, blen); for (k = 0; k < pad; k++) { if (blen >= *buflen) { error = ERANGE; goto done; } buf[blen++] = 0; } /* Get value */ vlen = *buflen - blen; if (foff[index] == 0) { /* use default value */ error = ng_get_composite_elem_default(type, index, start, buf + blen, &vlen, ctype); } else { /* parse given value */ *off = foff[index]; error = INVOKE(etype, parse)(etype, s, off, start, buf + blen, &vlen); } if (error != 0) goto done; blen += vlen; } /* Make total composite structure size a multiple of its alignment */ if ((align = ALIGNMENT(type)) != 0) { while (blen % align != 0) { if (blen >= *buflen) { error = ERANGE; goto done; } buf[blen++] = 0; } } /* Done */ *buflen = blen; done: FREE(foff, M_NETGRAPH); return (error); } /* * Convert an array or structure from binary to ASCII */ static int ng_unparse_composite(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen, const enum comptype ctype) { const int num = ng_get_composite_len(type, data, data + *off, ctype); int nextIndex = 0, didOne = 0; int error, index; /* Opening brace/bracket */ NG_PARSE_APPEND("%c", (ctype == CT_STRUCT) ? '{' : '['); /* Do each item */ for (index = 0; index < num; index++) { const struct ng_parse_type *const etype = ng_get_composite_etype(type, index, ctype); u_char temp[1024]; /* Skip any alignment pad bytes */ *off += ng_parse_get_elem_pad(type, index, ctype, *off); /* See if element is equal to its default value; skip if so */ if (*off < sizeof(temp)) { int tempsize = sizeof(temp) - *off; bcopy(data, temp, *off); if (ng_get_composite_elem_default(type, index, temp, temp + *off, &tempsize, ctype) == 0 && bcmp(temp + *off, data + *off, tempsize) == 0) { *off += tempsize; continue; } } /* Print name= */ NG_PARSE_APPEND(" "); if (ctype != CT_STRUCT) { if (index != nextIndex) { nextIndex = index; NG_PARSE_APPEND("%d=", index); } nextIndex++; } else { const struct ng_parse_struct_info *si = type->info; NG_PARSE_APPEND("%s=", si->fields[index].name); } /* Print value */ if ((error = INVOKE(etype, unparse) (etype, data, off, cbuf, cbuflen)) != 0) return (error); cbuflen -= strlen(cbuf); cbuf += strlen(cbuf); didOne = 1; } /* Closing brace/bracket */ NG_PARSE_APPEND("%s%c", didOne ? " " : "", (ctype == CT_STRUCT) ? '}' : ']'); return (0); } /* * Generate the default value for an element of an array or structure * Returns EOPNOTSUPP if default value is unspecified. */ static int ng_get_composite_elem_default(const struct ng_parse_type *type, int index, const u_char *const start, u_char *buf, int *buflen, const enum comptype ctype) { const struct ng_parse_type *etype; ng_getDefault_t *func; switch (ctype) { case CT_STRUCT: break; case CT_ARRAY: { const struct ng_parse_array_info *const ai = type->info; if (ai->getDefault != NULL) { return (*ai->getDefault)(type, index, start, buf, buflen); } break; } case CT_FIXEDARRAY: { const struct ng_parse_fixedarray_info *const fi = type->info; if (*fi->getDefault != NULL) { return (*fi->getDefault)(type, index, start, buf, buflen); } break; } default: panic("%s", __FUNCTION__); } /* Default to element type default */ etype = ng_get_composite_etype(type, index, ctype); func = METHOD(etype, getDefault); if (func == NULL) return (EOPNOTSUPP); return (*func)(etype, start, buf, buflen); } /* * Get the number of elements in a struct, variable or fixed array. */ static int ng_get_composite_len(const struct ng_parse_type *type, const u_char *const start, const u_char *buf, const enum comptype ctype) { switch (ctype) { case CT_STRUCT: { const struct ng_parse_struct_info *const si = type->info; int numFields = 0; for (numFields = 0; ; numFields++) { const struct ng_parse_struct_field *const fi = &si->fields[numFields]; if (fi->name == NULL) break; } return (numFields); } case CT_ARRAY: { const struct ng_parse_array_info *const ai = type->info; return (*ai->getLength)(type, start, buf); } case CT_FIXEDARRAY: { const struct ng_parse_fixedarray_info *const fi = type->info; return fi->length; } default: panic("%s", __FUNCTION__); } return (0); } /* * Return the type of the index'th element of a composite structure */ static const struct ng_parse_type * ng_get_composite_etype(const struct ng_parse_type *type, int index, const enum comptype ctype) { const struct ng_parse_type *etype = NULL; switch (ctype) { case CT_STRUCT: { const struct ng_parse_struct_info *const si = type->info; etype = si->fields[index].type; break; } case CT_ARRAY: { const struct ng_parse_array_info *const ai = type->info; etype = ai->elementType; break; } case CT_FIXEDARRAY: { const struct ng_parse_fixedarray_info *const fi = type->info; etype = fi->elementType; break; } default: panic("%s", __FUNCTION__); } return (etype); } /* * Get the number of bytes to skip to align for the next * element in a composite structure. */ static int ng_parse_get_elem_pad(const struct ng_parse_type *type, int index, enum comptype ctype, int posn) { const struct ng_parse_type *const etype = ng_get_composite_etype(type, index, ctype); int align; /* Get element's alignment, and possibly override */ align = ALIGNMENT(etype); if (ctype == CT_STRUCT) { const struct ng_parse_struct_info *si = type->info; if (si->fields[index].alignment != 0) align = si->fields[index].alignment; } /* Return number of bytes to skip to align */ return (align ? (align - (posn % align)) % align : 0); } /************************************************************************ PARSING HELPER ROUTINES ************************************************************************/ /* * Skip over a value */ static int ng_parse_skip_value(const char *s, int off0, int *lenp) { int len, nbracket, nbrace; int off = off0; len = nbracket = nbrace = 0; do { switch (ng_parse_get_token(s, &off, &len)) { case T_LBRACKET: nbracket++; break; case T_LBRACE: nbrace++; break; case T_RBRACKET: if (nbracket-- == 0) return (EINVAL); break; case T_RBRACE: if (nbrace-- == 0) return (EINVAL); break; case T_EOF: return (EINVAL); default: break; } off += len; } while (nbracket > 0 || nbrace > 0); *lenp = off - off0; return (0); } /* * Find the next token in the string, starting at offset *startp. * Returns the token type, with *startp pointing to the first char * and *lenp the length. */ enum ng_parse_token ng_parse_get_token(const char *s, int *startp, int *lenp) { char *t; int i; while (isspace(s[*startp])) (*startp)++; switch (s[*startp]) { case '\0': *lenp = 0; return T_EOF; case '{': *lenp = 1; return T_LBRACE; case '}': *lenp = 1; return T_RBRACE; case '[': *lenp = 1; return T_LBRACKET; case ']': *lenp = 1; return T_RBRACKET; case '=': *lenp = 1; return T_EQUALS; case '"': if ((t = ng_get_string_token(s, startp, lenp)) == NULL) return T_ERROR; FREE(t, M_NETGRAPH); return T_STRING; default: for (i = *startp + 1; s[i] != '\0' && !isspace(s[i]) && s[i] != '{' && s[i] != '}' && s[i] != '[' && s[i] != ']' && s[i] != '=' && s[i] != '"'; i++) ; *lenp = i - *startp; return T_WORD; } } /* * Get a string token, which must be enclosed in double quotes. * The normal C backslash escapes are recognized. */ char * ng_get_string_token(const char *s, int *startp, int *lenp) { char *cbuf, *p; int start, off; while (isspace(s[*startp])) (*startp)++; start = *startp; if (s[*startp] != '"') return (NULL); MALLOC(cbuf, char *, strlen(s + start), M_NETGRAPH, M_NOWAIT); if (cbuf == NULL) return (NULL); strcpy(cbuf, s + start + 1); for (off = 1, p = cbuf; *p != '\0'; off++, p++) { if (*p == '"') { *p = '\0'; *lenp = off + 1; return (cbuf); } else if (p[0] == '\\' && p[1] != '\0') { int x, k; char *v; strcpy(p, p + 1); v = p; switch (*p) { case 't': *v = '\t'; off++; continue; case 'n': *v = '\n'; off++; continue; case 'r': *v = '\r'; off++; continue; case 'v': *v = '\v'; off++; continue; case 'f': *v = '\f'; off++; continue; case '"': *v = '"'; off++; continue; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': for (x = k = 0; k < 3 && *v >= '0' && *v <= '7'; v++) { x = (x << 3) + (*v - '0'); off++; } *--v = (char)x; break; case 'x': for (v++, x = k = 0; k < 2 && isxdigit(*v); v++) { x = (x << 4) + (isdigit(*v) ? (*v - '0') : (tolower(*v) - 'a' + 10)); off++; } *--v = (char)x; break; default: continue; } strcpy(p, v); } } return (NULL); /* no closing quote */ } /* * Encode a string so it can be safely put in double quotes. * Caller must free the result. */ char * ng_encode_string(const char *raw) { char *cbuf; int off = 0; MALLOC(cbuf, char *, strlen(raw) * 4 + 3, M_NETGRAPH, M_NOWAIT); if (cbuf == NULL) return (NULL); cbuf[off++] = '"'; for ( ; *raw != '\0'; raw++) { switch (*raw) { case '\t': cbuf[off++] = '\\'; cbuf[off++] = 't'; break; case '\f': cbuf[off++] = '\\'; cbuf[off++] = 'f'; break; case '\n': cbuf[off++] = '\\'; cbuf[off++] = 'n'; break; case '\r': cbuf[off++] = '\\'; cbuf[off++] = 'r'; break; case '\v': cbuf[off++] = '\\'; cbuf[off++] = 'v'; break; case '"': case '\\': cbuf[off++] = '\\'; cbuf[off++] = *raw; break; default: if (*raw < 0x20 || *raw > 0x7e) { off += sprintf(cbuf + off, "\\x%02x", (u_char)*raw); break; } cbuf[off++] = *raw; break; } } cbuf[off++] = '"'; cbuf[off] = '\0'; return (cbuf); } /************************************************************************ VIRTUAL METHOD LOOKUP ************************************************************************/ static ng_parse_t * ng_get_parse_method(const struct ng_parse_type *t) { while (t != NULL && t->parse == NULL) t = t->supertype; return (t ? t->parse : NULL); } static ng_unparse_t * ng_get_unparse_method(const struct ng_parse_type *t) { while (t != NULL && t->unparse == NULL) t = t->supertype; return (t ? t->unparse : NULL); } static ng_getDefault_t * ng_get_getDefault_method(const struct ng_parse_type *t) { while (t != NULL && t->getDefault == NULL) t = t->supertype; return (t ? t->getDefault : NULL); } static ng_getAlign_t * ng_get_getAlign_method(const struct ng_parse_type *t) { while (t != NULL && t->getAlign == NULL) t = t->supertype; return (t ? t->getAlign : NULL); } Index: stable/3/sys/netgraph/ng_parse.h =================================================================== --- stable/3/sys/netgraph/ng_parse.h (revision 67531) +++ stable/3/sys/netgraph/ng_parse.h (revision 67532) @@ -1,507 +1,507 @@ /* * ng_parse.h * * Copyright (c) 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 + * Author: Archie Cobbs * * $Whistle: ng_parse.h,v 1.2 1999/11/29 01:43:48 archie Exp $ * $FreeBSD$ */ #ifndef _NETGRAPH_PARSE_H_ #define _NETGRAPH_PARSE_H_ /* This defines a library of routines for converting between various C language types in binary form and ASCII strings. Types are user definable. Several pre-defined types are supplied, for some common C types: structures, variable and fixed length arrays, integer types, variable and fixed length strings, IP addresses, etc. A netgraph node type may provide a list of types that correspond to the structures it expects to send and receive in the arguments field of a control message. This allows these messages to be converted between their native binary form and the corresponding ASCII form. A future use of the ASCII form may be for inter-machine communication of control messages, because the ASCII form is machine independent whereas the native binary form is not. Syntax ------ Structures: '{' [ = ... ] '}' Omitted fields have their default values by implication. The order in which the fields are specified does not matter. Arrays: '[' [ [index=] ... ] ']' Element value may be specified with or without the "=" prefix; If omitted, the index after the previous element is used. Omitted fields have their default values by implication. Strings: "foo bar blah\r\n" That is, strings are specified just like C strings. The usual backslash escapes are accepted. Other simple types (integers, IP addresses) have their obvious forms. Example ------- Suppose we have a netgraph command that takes as an argument a 'struct foo' shown below. Here is an example of a possible value for the structure, and the corresponding ASCII encoding of that value: Structure Binary value --------- ------------ struct foo { struct in_addr ip; 01 02 03 04 int bar; 00 00 00 00 char label[8]; 61 62 63 0a 00 00 00 00 u_char alen; 03 00 short ary[0]; 05 00 00 00 0a 00 }; ASCII value ----------- { ip=1.2.3.4 label="abc\n" alen=3 ary=[ 5 2=10 ] } Note that omitted fields and array elements get their default values ("bar" and ary[2]), and that the alignment is handled automatically (the extra 00 byte after "num"). Also, since byte order and alignment are inherently machine dependent, so is this conversion process. The above example shows an x86 (little endian) encoding. Also the above example is tricky because the structure is variable length, depending on 'alen', the number of elements in the array 'ary'. Here is how one would define a parse type for the above structure, subclassing the pre-defined types below. We construct the type in a 'bottom up' fashion, defining each field's type first, then the type for the whole structure ('//' comments used to avoid breakage). // Super-type info for 'label' field struct ng_parse_fixedstring_info foo_label_info = { 8 }; // Parse type for 'label' field struct ng_parse_type foo_label_type = { &ng_parse_fixedstring_type // super-type &foo_label_info // super-type info }; #define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0)) // Function to compute the length of the array 'ary', which // is variable length, depending on the previous field 'alen'. // Upon entry 'buf' will be pointing at &ary[0]. int foo_ary_getLength(const struct ng_parse_type *type, const u_char *start, const u_char *buf) { const struct foo *f; f = (const struct foo *)(buf - OFFSETOF(struct foo, ary)); return f->alen; } // Super-type info for 'ary' field struct ng_parse_array_info foo_ary_info = { &ng_parse_int16_type, // element type &foo_ary_getLength // func to get array length } // Parse type for 'ary' field struct ng_parse_type foo_ary_type = { &ng_parse_array_type, // super-type &foo_ary_info // super-type info }; // Super-type info for struct foo struct ng_parse_struct_info foo_fields = { { "ip", &ng_parse_ipaddr_type }, { "bar", &ng_parse_int32_type }, { "label", &foo_label_type }, { "alen", &ng_parse_int8_type }, { "ary", &foo_ary_type }, { NULL } }; // Parse type for struct foo struct ng_parse_type foo_type = { &ng_parse_struct_type, // super-type &foo_fields // super-type info }; To define a type, you can define it as a sub-type of a predefined type as shown above, possibly overriding some of the predefined type's methods, or define an entirely new syntax, with the restriction that the ASCII representation of your type's value must not contain any whitespace or any of these characters: { } [ ] = " See ng_ksocket.c for an example of how to do this for 'struct sockaddr'. See ng_parse.c to see implementations of the pre-defined types below. */ /************************************************************************ METHODS REQUIRED BY A TYPE ************************************************************************/ /* * Three methods are required for a type. These may be given explicitly * or, if NULL, inherited from the super-type. The 'getDefault' method * is always optional; the others are required if there is no super-type. */ struct ng_parse_type; /* * Convert ASCII to binary according to the supplied type. * * The ASCII characters begin at offset *off in 'string'. The binary * representation is put into 'buf', which has at least *buflen bytes. * 'start' points to the first byte output by ng_parse() (ie, start <= buf). * * Upon return, *buflen contains the length of the new binary data, and * *off is updated to point just past the end of the parsed range of * characters, or, in the case of an error, to the offending character(s). * * Return values: * 0 Success; *buflen holds the length of the data * and *off points just past the last char parsed. * EALREADY Field specified twice * ENOENT Unknown field * E2BIG Array or character string overflow * ERANGE Output was longer than *buflen bytes * EINVAL Parse failure or other invalid content * ENOMEM Out of memory * EOPNOTSUPP Mandatory array/structure element missing */ typedef int ng_parse_t(const struct ng_parse_type *type, const char *string, int *off, const u_char *start, u_char *buf, int *buflen); /* * Convert binary to ASCII according to the supplied type. * * The results are put into 'buf', which is at least buflen bytes long. * *off points to the current byte in 'data' and should be updated * before return to point just past the last byte unparsed. * * Returns: * 0 Success * ERANGE Output was longer than buflen bytes */ typedef int ng_unparse_t(const struct ng_parse_type *type, const u_char *data, int *off, char *buf, int buflen); /* * Compute the default value according to the supplied type. * * Store the result in 'buf', which is at least *buflen bytes long. * Upon return *buflen contains the length of the output. * * Returns: * 0 Success * ERANGE Output was longer than *buflen bytes * EOPNOTSUPP Default value is not specified for this type */ typedef int ng_getDefault_t(const struct ng_parse_type *type, const u_char *start, u_char *buf, int *buflen); /* * Return the alignment requirement of this type. Zero is same as one. */ typedef int ng_getAlign_t(const struct ng_parse_type *type); /************************************************************************ TYPE DEFINITION ************************************************************************/ /* * This structure describes a type, which may be a sub-type of another * type by pointing to it with 'supertype' and possibly omitting methods. * Typically the super-type requires some type-specific info, which is * supplied by the 'info' field. * * The 'private' field is ignored by all of the pre-defined types. * Sub-types may use it as they see fit. * * The 'getDefault' method may always be omitted (even if there is no * super-type), which means the value for any item of this type must * always be explicitly given. */ struct ng_parse_type { const struct ng_parse_type *supertype; /* super-type, if any */ const void *info; /* type-specific info */ void *private; /* client private info */ ng_parse_t *parse; /* parse method */ ng_unparse_t *unparse; /* unparse method */ ng_getDefault_t *getDefault; /* get default value method */ ng_getAlign_t *getAlign; /* get alignment */ }; /************************************************************************ PRE-DEFINED TYPES ************************************************************************/ /* * STRUCTURE TYPE * * This type supports arbitrary C structures. The normal field alignment * rules for the local machine are applied. Fields are always parsed in * field order, no matter what order they are listed in the ASCII string. * * Default value: Determined on a per-field basis * Additional info: struct ng_parse_struct_info * */ extern const struct ng_parse_type ng_parse_struct_type; /* Each field has a name, type, and optional alignment override. If the override is non-zero, the alignment is determined from the field type. Note: add an extra struct ng_parse_struct_field with name == NULL to indicate the end of the list. */ struct ng_parse_struct_info { struct ng_parse_struct_field { const char *name; /* field name */ const struct ng_parse_type *type; /* field type */ int alignment; /* override alignment */ } fields[0]; }; /* * FIXED LENGTH ARRAY TYPE * * This type supports fixed length arrays, having any element type. * * Default value: As returned by getDefault for each index * Additional info: struct ng_parse_fixedarray_info * */ extern const struct ng_parse_type ng_parse_fixedarray_type; /* * Get the default value for the element at index 'index'. This method * may be NULL, in which case the default value is computed from the * element type. Otherwise, it should fill in the default value at *buf * (having size *buflen) and update *buflen to the length of the filled-in * value before return. If there is not enough routine return ERANGE. */ typedef int ng_parse_array_getDefault_t(const struct ng_parse_type *type, int index, const u_char *start, u_char *buf, int *buflen); struct ng_parse_fixedarray_info { const struct ng_parse_type *elementType; int length; ng_parse_array_getDefault_t *getDefault; }; /* * VARIABLE LENGTH ARRAY TYPE * * Same as fixed length arrays, except that the length is determined * by a function instead of a constant value. * * Default value: Same as with fixed length arrays * Additional info: struct ng_parse_array_info * */ extern const struct ng_parse_type ng_parse_array_type; /* * Return the length of the array. If the array is a field in a structure, * all prior fields are guaranteed to be filled in already. Upon entry, * 'start' is equal to the first byte parsed in this run, while 'buf' points * to the first element of the array to be filled in. */ typedef int ng_parse_array_getLength_t(const struct ng_parse_type *type, const u_char *start, const u_char *buf); struct ng_parse_array_info { const struct ng_parse_type *elementType; ng_parse_array_getLength_t *getLength; ng_parse_array_getDefault_t *getDefault; }; /* * ARBITRARY LENGTH STRING TYPE * * For arbirary length, NUL-terminated strings. * * Default value: Empty string * Additional info: None required */ extern const struct ng_parse_type ng_parse_string_type; /* * BOUNDED LENGTH STRING TYPE * * These are strings that have a fixed-size buffer, and always include * a terminating NUL character. * * Default value: Empty string * Additional info: struct ng_parse_fixedstring_info * */ extern const struct ng_parse_type ng_parse_fixedstring_type; struct ng_parse_fixedstring_info { int bufSize; /* size of buffer (including NUL) */ }; /* * COMMONLY USED BOUNDED LENGTH STRING TYPES */ extern const struct ng_parse_type ng_parse_nodebuf_type; /* NG_NODELEN + 1 */ extern const struct ng_parse_type ng_parse_hookbuf_type; /* NG_HOOKLEN + 1 */ extern const struct ng_parse_type ng_parse_pathbuf_type; /* NG_PATHLEN + 1 */ extern const struct ng_parse_type ng_parse_typebuf_type; /* NG_TYPELEN + 1 */ extern const struct ng_parse_type ng_parse_cmdbuf_type; /* NG_CMDSTRLEN + 1 */ /* * INTEGER TYPES * * Default value: 0 * Additional info: None required */ extern const struct ng_parse_type ng_parse_int8_type; extern const struct ng_parse_type ng_parse_int16_type; extern const struct ng_parse_type ng_parse_int32_type; extern const struct ng_parse_type ng_parse_int64_type; /* * IP ADDRESS TYPE * * Default value: 0.0.0.0 * Additional info: None required */ extern const struct ng_parse_type ng_parse_ipaddr_type; /* * VARIABLE LENGTH BYTE ARRAY TYPE * * The bytes are displayed in hex. The ASCII form may be either an * array of bytes or a string constant, in which case the array is * zero-filled after the string bytes. * * Default value: All bytes are zero * Additional info: ng_parse_array_getLength_t * */ extern const struct ng_parse_type ng_parse_bytearray_type; /* * NETGRAPH CONTROL MESSAGE TYPE * * This is the parse type for a struct ng_mesg. * * Default value: All fields zero * Additional info: None required */ extern const struct ng_parse_type ng_parse_ng_mesg_type; /************************************************************************ CONVERSTION AND PARSING ROUTINES ************************************************************************/ /* Tokens for parsing structs and arrays */ enum ng_parse_token { T_LBRACE, /* '{' */ T_RBRACE, /* '}' */ T_LBRACKET, /* '[' */ T_RBRACKET, /* ']' */ T_EQUALS, /* '=' */ T_STRING, /* string in double quotes */ T_ERROR, /* error parsing string in double quotes */ T_WORD, /* anything else containing no whitespace */ T_EOF, /* end of string reached */ }; /* * See typedef ng_parse_t for definition */ extern int ng_parse(const struct ng_parse_type *type, const char *string, int *off, u_char *buf, int *buflen); /* * See typedef ng_unparse_t for definition (*off assumed to be zero). */ extern int ng_unparse(const struct ng_parse_type *type, const u_char *data, char *buf, int buflen); /* * See typedef ng_getDefault_t for definition */ extern int ng_parse_getDefault(const struct ng_parse_type *type, u_char *buf, int *buflen); /* * Parse a token: '*startp' is the offset to start looking. Upon * successful return, '*startp' equals the beginning of the token * and '*lenp' the length. If error, '*startp' points at the * offending character(s). */ extern enum ng_parse_token ng_parse_get_token(const char *s, int *startp, int *lenp); /* * Like above, but specifically for getting a string token and returning * the string value. The string token must be enclosed in double quotes * and the normal C backslash escapes are recognized. The caller must * eventually free() the returned result. Returns NULL if token is * not a string token, or parse or other error. */ extern char *ng_get_string_token(const char *s, int *startp, int *lenp); /* * Convert a raw string into a doubly-quoted string including any * necessary backslash escapes. Caller must free the result. * Returns NULL if ENOMEM. */ extern char *ng_encode_string(const char *s); #endif /* _NETGRAPH_PARSE_H_ */ Index: stable/3/sys/netgraph/ng_ppp.c =================================================================== --- stable/3/sys/netgraph/ng_ppp.c (revision 67531) +++ stable/3/sys/netgraph/ng_ppp.c (revision 67532) @@ -1,1986 +1,1986 @@ /* * ng_ppp.c * * Copyright (c) 1996-2000 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 + * Author: Archie Cobbs * * $FreeBSD$ * $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 #include #include #define PROT_VALID(p) (((p) & 0x0101) == 0x0001) #define PROT_COMPRESSABLE(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_IPV6 0x0057 #define PROT_IPX 0x002b #define PROT_LCP 0xc021 #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_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_NOSEQ INT_MAX /* impossible sequence number */ #define MP_SEQ_MASK(priv) ((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(priv,x,y) ((priv)->conf.recvShortSeq ? \ MP_SHORT_SEQ_DIFF((x), (y)) : \ MP_LONG_SEQ_DIFF((x), (y))) #define MP_NEXT_SEQ(priv,seq) (((seq) + 1) & MP_SEQ_MASK(priv)) #define MP_PREV_SEQ(priv,seq) (((seq) - 1) & MP_SEQ_MASK(priv)) /* Don't fragment transmitted packets smaller than this */ #define MP_MIN_FRAG_LEN 6 /* Maximum fragment reasssembly queue length */ #define MP_MAX_QUEUE_LEN 128 /* Fragment queue scanner period */ #define MP_FRAGTIMER_INTERVAL (hz/2) /* We store incoming fragments this way */ struct ng_ppp_frag { int seq; /* fragment seq# */ u_char first; /* First in packet? */ u_char last; /* Last in packet? */ struct timeval timestamp; /* time of reception */ struct mbuf *data; /* Fragment data */ meta_p meta; /* Fragment meta */ CIRCLEQ_ENTRY(ng_ppp_frag) f_qent; /* Fragment queue */ }; /* 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 NG_PPP_HOOK_IPV6, #define HOOK_INDEX_IPV6 12 NULL #define HOOK_INDEX_MAX 13 }; /* 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)) /* Per-link private information */ struct ng_ppp_link { struct ng_ppp_link_conf conf; /* link configuration */ hook_p hook; /* connection to link data */ int seq; /* highest rec'd seq# - MSEQ */ struct timeval lastWrite; /* time of last write */ int bytesInQueue; /* bytes in the output queue */ struct ng_ppp_link_stat stats; /* Link stats */ }; /* Total per-node private information */ struct ng_ppp_private { struct ng_ppp_bund_conf conf; /* bundle config */ struct ng_ppp_link_stat bundleStats; /* bundle stats */ struct ng_ppp_link links[NG_PPP_MAX_LINKS];/* per-link info */ int xseq; /* next out MP seq # */ int mseq; /* min links[i].seq */ u_char vjCompHooked; /* VJ comp hooked up? */ u_char allLinksEqual; /* all xmit the same? */ u_char timerActive; /* frag timer active? */ u_int numActiveLinks; /* how many links up */ int activeLinks[NG_PPP_MAX_LINKS]; /* indicies */ u_int lastLink; /* for round robin */ hook_p hooks[HOOK_INDEX_MAX]; /* non-link hooks */ CIRCLEQ_HEAD(ng_ppp_fraglist, ng_ppp_frag) /* fragment queue */ frags; int qlen; /* fraq queue length */ struct callout_handle fragTimer; /* fraq queue check */ }; typedef struct ng_ppp_private *priv_p; /* Netgraph node methods */ 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 bypass, int linkNum, struct mbuf *m, meta_p meta); static int ng_ppp_output(node_p node, int bypass, int proto, int linkNum, struct mbuf *m, meta_p meta); static int ng_ppp_mp_input(node_p node, int linkNum, struct mbuf *m, meta_p meta); static int ng_ppp_check_packet(node_p node); static void ng_ppp_get_packet(node_p node, struct mbuf **mp, meta_p *metap); static int ng_ppp_frag_process(node_p node); static int ng_ppp_frag_trim(node_p node); static void ng_ppp_frag_timeout(void *arg); static void ng_ppp_frag_checkstale(node_p node); static void ng_ppp_frag_reset(node_p node); 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 struct mbuf *ng_ppp_prepend(struct mbuf *m, const void *buf, int len); static int ng_ppp_config_valid(node_p node, const struct ng_ppp_node_conf *newConf); static void ng_ppp_update(node_p node, int newConf); static void ng_ppp_start_frag_timer(node_p node); static void ng_ppp_stop_frag_timer(node_p node); /* Parse type for struct ng_ppp_link_conf */ static const struct ng_parse_struct_info ng_ppp_link_type_info = NG_PPP_LINK_TYPE_INFO; static const struct ng_parse_type ng_ppp_link_type = { &ng_parse_struct_type, &ng_ppp_link_type_info, }; /* Parse type for struct ng_ppp_bund_conf */ static const struct ng_parse_struct_info ng_ppp_bund_type_info = NG_PPP_BUND_TYPE_INFO; static const struct ng_parse_type ng_ppp_bund_type = { &ng_parse_struct_type, &ng_ppp_bund_type_info, }; /* Parse type for struct ng_ppp_node_conf */ struct ng_parse_fixedarray_info ng_ppp_array_info = { &ng_ppp_link_type, NG_PPP_MAX_LINKS }; static const struct ng_parse_type ng_ppp_link_array_type = { &ng_parse_fixedarray_type, &ng_ppp_array_info, }; static const struct ng_parse_struct_info ng_ppp_conf_type_info = NG_PPP_CONFIG_TYPE_INFO(&ng_ppp_bund_type, &ng_ppp_link_array_type); static const struct ng_parse_type ng_ppp_conf_type = { &ng_parse_struct_type, &ng_ppp_conf_type_info }; /* Parse type for struct ng_ppp_link_stat */ static const struct ng_parse_struct_info ng_ppp_stats_type_info = NG_PPP_STATS_TYPE_INFO; static const struct ng_parse_type ng_ppp_stats_type = { &ng_parse_struct_type, &ng_ppp_stats_type_info }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_ppp_cmds[] = { { NGM_PPP_COOKIE, NGM_PPP_SET_CONFIG, "setconfig", &ng_ppp_conf_type, NULL }, { NGM_PPP_COOKIE, NGM_PPP_GET_CONFIG, "getconfig", NULL, &ng_ppp_conf_type }, { NGM_PPP_COOKIE, NGM_PPP_GET_LINK_STATS, "getstats", &ng_parse_int16_type, &ng_ppp_stats_type }, { NGM_PPP_COOKIE, NGM_PPP_CLR_LINK_STATS, "clrstats", &ng_parse_int16_type, NULL }, { NGM_PPP_COOKIE, NGM_PPP_GETCLR_LINK_STATS, "getclrstats", &ng_parse_int16_type, &ng_ppp_stats_type }, { 0 } }; /* 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, ng_ppp_cmds }; NETGRAPH_INIT(ppp, &ng_ppp_typestruct); static int *compareLatencies; /* hack for ng_ppp_intcmp() */ /* Address and control field header */ static const u_char ng_ppp_acf[2] = { 0xff, 0x03 }; /* Maximum time we'll let a complete incoming packet sit in the queue */ static const struct timeval ng_ppp_max_staleness = { 2, 0 }; /* 2 seconds */ #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 i, 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); for (i = 0; i < NG_PPP_MAX_LINKS; i++) priv->links[i].seq = MP_NOSEQ; callout_handle_init(&priv->fragTimer); /* 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) { const char *cp; char *eptr; cp = name + strlen(NG_PPP_HOOK_LINK_PREFIX); if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) return (EINVAL); linkNum = (int)strtoul(cp, &eptr, 10); if (*eptr != '\0' || linkNum < 0 || linkNum >= NG_PPP_MAX_LINKS) return (EINVAL); hookPtr = &priv->links[linkNum].hook; 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->links[linkNum].conf.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_conf *const conf = (struct ng_ppp_node_conf *)msg->data; int i; /* Check for invalid or illegal config */ if (msg->header.arglen != sizeof(*conf)) ERROUT(EINVAL); if (!ng_ppp_config_valid(node, conf)) ERROUT(EINVAL); /* Copy config */ priv->conf = conf->bund; for (i = 0; i < NG_PPP_MAX_LINKS; i++) priv->links[i].conf = conf->links[i]; ng_ppp_update(node, 1); break; } case NGM_PPP_GET_CONFIG: { struct ng_ppp_node_conf *conf; int i; NG_MKRESPONSE(resp, msg, sizeof(*conf), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); conf = (struct ng_ppp_node_conf *)resp->data; conf->bund = priv->conf; for (i = 0; i < NG_PPP_MAX_LINKS; i++) conf->links[i] = priv->links[i].conf; break; } case NGM_PPP_GET_LINK_STATS: case NGM_PPP_CLR_LINK_STATS: case NGM_PPP_GETCLR_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->links[linkNum].stats; if (msg->header.cmd != NGM_PPP_CLR_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)); } if (msg->header.cmd != NGM_PPP_GET_LINK_STATS) 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); } 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) { struct ng_ppp_link *link; /* Convert index into a link number */ linkNum = (u_int16_t)~index; KASSERT(linkNum < NG_PPP_MAX_LINKS, ("%s: bogus index 0x%x", __FUNCTION__, index)); link = &priv->links[linkNum]; /* Stats */ link->stats.recvFrames++; link->stats.recvOctets += m->m_pkthdr.len; /* Strip address and control fields, if present */ if (m->m_pkthdr.len >= 2) { if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { NG_FREE_DATA(m, meta); return (ENOBUFS); } if (bcmp(mtod(m, u_char *), &ng_ppp_acf, 2) == 0) m_adj(m, 2); } /* Dispatch incoming frame (if not enabled, to bypass) */ return ng_ppp_input(node, !link->conf.enableLink, linkNum, m, meta); } /* Get protocol & check if data allowed from this hook */ switch (index) { /* Outgoing 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_IPV6: if (!priv->conf.enableIPv6) { NG_FREE_DATA(m, meta); return (ENXIO); } proto = PROT_IPV6; 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_DATA(m, 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) { NG_FREE_DATA(m, meta); return (EINVAL); } break; /* Incoming data */ case HOOK_INDEX_VJC_IP: if (!priv->conf.enableIP || !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: panic("%s: bogus index 0x%x", __FUNCTION__, index); } /* Now figure out what to do with the frame */ switch (index) { /* Outgoing data */ 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_IPV6: 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: return ng_ppp_output(node, 0, proto, NG_PPP_BUNDLE_LINKNUM, m, meta); case HOOK_INDEX_BYPASS: return ng_ppp_output(node, 1, proto, linkNum, m, meta); /* Incoming data */ case HOOK_INDEX_DECRYPT: case HOOK_INDEX_DECOMPRESS: return ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, m, meta); case HOOK_INDEX_VJC_IP: outHook = priv->hooks[HOOK_INDEX_INET]; break; } /* 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; /* Stop fragment queue timer */ ng_ppp_stop_frag_timer(node); /* Take down netgraph node */ node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); ng_ppp_frag_reset(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) { const node_p node = hook->node; const priv_p priv = node->private; const int index = HOOK_INDEX(hook); /* Zero out hook pointer */ if (index < 0) priv->links[~index].hook = NULL; else priv->hooks[index] = NULL; /* Update derived info (or go away if no hooks left) */ if (node->numhooks > 0) ng_ppp_update(node, 0); else ng_rmnode(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 bypass, 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->links[linkNum].stats.badProtos++; NG_FREE_DATA(m, meta); return (EINVAL); } /* Bypass frame? */ if (bypass) goto bypass; /* 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 && linkNum != NG_PPP_BUNDLE_LINKNUM) 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; case PROT_IPV6: if (priv->conf.enableIPv6) outHook = priv->hooks[HOOK_INDEX_IPV6]; break; } bypass: /* For unknown/inactive protocols, forward out the bypass hook */ if (outHook == NULL) { u_int16_t hdr[2]; hdr[0] = htons(linkNum); hdr[1] = htons((u_int16_t)proto); if ((m = ng_ppp_prepend(m, &hdr, 4)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } 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 * If the link is not enabled then ENXIO is returned, unless "bypass" is != 0. */ static int ng_ppp_output(node_p node, int bypass, int proto, int linkNum, struct mbuf *m, meta_p meta) { const priv_p priv = node->private; struct ng_ppp_link *link; int len, error; /* If not doing MP, map bundle virtual link to (the only) link */ if (linkNum == NG_PPP_BUNDLE_LINKNUM && !priv->conf.enableMultilink) linkNum = priv->activeLinks[0]; /* Get link pointer (optimization) */ link = (linkNum != NG_PPP_BUNDLE_LINKNUM) ? &priv->links[linkNum] : NULL; /* Check link status (if real) */ if (linkNum != NG_PPP_BUNDLE_LINKNUM) { if (!bypass && !link->conf.enableLink) { NG_FREE_DATA(m, meta); return (ENXIO); } if (link->hook == NULL) { NG_FREE_DATA(m, meta); return (ENETDOWN); } } /* Prepend protocol number, possibly compressed */ if ((m = ng_ppp_addproto(m, proto, linkNum == NG_PPP_BUNDLE_LINKNUM || link->conf.enableProtoComp)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } /* Special handling for the MP virtual link */ if (linkNum == NG_PPP_BUNDLE_LINKNUM) return ng_ppp_mp_output(node, m, meta); /* Prepend address and control field (unless compressed) */ if (proto == PROT_LCP || !link->conf.enableACFComp) { if ((m = ng_ppp_prepend(m, &ng_ppp_acf, 2)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } } /* Deliver frame */ len = m->m_pkthdr.len; NG_SEND_DATA(error, link->hook, m, meta); /* Update stats and 'bytes in queue' counter */ if (error == 0) { link->stats.xmitFrames++; link->stats.xmitOctets += len; link->bytesInQueue += len; getmicrouptime(&link->lastWrite); } return error; } /* * Handle an incoming multi-link fragment * * The fragment reassembly algorithm is somewhat complex. This is mainly * because we are required not to reorder the reconstructed packets, yet * fragments are only guaranteed to arrive in order on a per-link basis. * In other words, when we have a complete packet ready, but the previous * packet is still incomplete, we have to decide between delivering the * complete packet and throwing away the incomplete one, or waiting to * see if the remainder of the incomplete one arrives, at which time we * can deliver both packets, in order. * * This problem is exacerbated by "sequence number slew", which is when * the sequence numbers coming in from different links are far apart from * each other. In particular, certain unnamed equipment (*cough* Ascend) * has been seen to generate sequence number slew of up to 10 on an ISDN * 2B-channel MP link. There is nothing invalid about sequence number slew * but it makes the reasssembly process have to work harder. * * However, the peer is required to transmit fragments in order on each * link. That means if we define MSEQ as the minimum over all links of * the highest sequence number received on that link, then we can always * give up any hope of receiving a fragment with sequence number < MSEQ in * the future (all of this using 'wraparound' sequence number space). * Therefore we can always immediately throw away incomplete packets * missing fragments with sequence numbers < MSEQ. * * Here is an overview of our algorithm: * * o Received fragments are inserted into a queue, for which we * maintain these invariants between calls to this function: * * - Fragments are ordered in the queue by sequence number * - If a complete packet is at the head of the queue, then * the first fragment in the packet has seq# > MSEQ + 1 * (otherwise, we could deliver it immediately) * - If any fragments have seq# < MSEQ, then they are necessarily * part of a packet whose missing seq#'s are all > MSEQ (otherwise, * we can throw them away because they'll never be completed) * - The queue contains at most MP_MAX_QUEUE_LEN fragments * * o We have a periodic timer that checks the queue for the first * complete packet that has been sitting in the queue "too long". * When one is detected, all previous (incomplete) fragments are * discarded, their missing fragments are declared lost and MSEQ * is increased. * * o If we recieve a fragment with seq# < MSEQ, we throw it away * because we've already delcared it lost. * * This assumes linkNum != NG_PPP_BUNDLE_LINKNUM. */ 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_link *const link = &priv->links[linkNum]; struct ng_ppp_frag frag0, *frag = &frag0; struct ng_ppp_frag *qent; int i, diff, inserted; /* Extract fragment information from MP header */ if (priv->conf.recvShortSeq) { u_int16_t shdr; if (m->m_pkthdr.len < 2) { link->stats.runts++; 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; diff = MP_SHORT_SEQ_DIFF(frag->seq, priv->mseq); m_adj(m, 2); } else { u_int32_t lhdr; if (m->m_pkthdr.len < 4) { link->stats.runts++; 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; diff = MP_LONG_SEQ_DIFF(frag->seq, priv->mseq); m_adj(m, 4); } frag->data = m; frag->meta = meta; getmicrouptime(&frag->timestamp); /* If sequence number is < MSEQ, we've already declared this fragment as lost, so we have no choice now but to drop it */ if (diff < 0) { link->stats.dropFragments++; NG_FREE_DATA(m, meta); return (0); } /* Update highest received sequence number on this link and MSEQ */ priv->mseq = link->seq = frag->seq; for (i = 0; i < priv->numActiveLinks; i++) { struct ng_ppp_link *const alink = &priv->links[priv->activeLinks[i]]; if (MP_SEQ_DIFF(priv, alink->seq, priv->mseq) < 0) priv->mseq = alink->seq; } /* 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); ng_ppp_frag_process(node); return (ENOMEM); } *frag = frag0; /* Add fragment to queue, which is sorted by sequence number */ inserted = 0; CIRCLEQ_FOREACH_REVERSE(qent, &priv->frags, f_qent) { diff = MP_SEQ_DIFF(priv, frag->seq, qent->seq); if (diff > 0) { CIRCLEQ_INSERT_AFTER(&priv->frags, qent, frag, f_qent); inserted = 1; break; } else if (diff == 0) { /* should never happen! */ link->stats.dupFragments++; NG_FREE_DATA(frag->data, frag->meta); FREE(frag, M_NETGRAPH); return (EINVAL); } } if (!inserted) CIRCLEQ_INSERT_HEAD(&priv->frags, frag, f_qent); priv->qlen++; /* Process the queue */ return ng_ppp_frag_process(node); } /* * Examine our list of fragments, and determine if there is a * complete and deliverable packet at the head of the list. * Return 1 if so, zero otherwise. */ static int ng_ppp_check_packet(node_p node) { const priv_p priv = node->private; struct ng_ppp_frag *qent, *qnext; /* Check for empty queue */ if (CIRCLEQ_EMPTY(&priv->frags)) return (0); /* Check first fragment is the start of a deliverable packet */ qent = CIRCLEQ_FIRST(&priv->frags); if (!qent->first || MP_SEQ_DIFF(priv, qent->seq, priv->mseq) > 1) return (0); /* Check that all the fragments are there */ while (!qent->last) { qnext = CIRCLEQ_NEXT(qent, f_qent); if (qnext == (void *)&priv->frags) /* end of queue */ return (0); if (qnext->seq != MP_NEXT_SEQ(priv, qent->seq)) return (0); qent = qnext; } /* Got one */ return (1); } /* * Pull a completed packet off the head of the incoming fragment queue. * This assumes there is a completed packet there to pull off. */ static void ng_ppp_get_packet(node_p node, struct mbuf **mp, meta_p *metap) { const priv_p priv = node->private; struct ng_ppp_frag *qent, *qnext; struct mbuf *m = NULL, *tail; qent = CIRCLEQ_FIRST(&priv->frags); KASSERT(!CIRCLEQ_EMPTY(&priv->frags) && qent->first, ("%s: no packet", __FUNCTION__)); for (tail = NULL; qent != NULL; qent = qnext) { qnext = CIRCLEQ_NEXT(qent, f_qent); KASSERT(!CIRCLEQ_EMPTY(&priv->frags), ("%s: empty q", __FUNCTION__)); CIRCLEQ_REMOVE(&priv->frags, qent, f_qent); if (tail == NULL) { tail = m = qent->data; *metap = 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 frags' metas */ } while (tail->m_next != NULL) tail = tail->m_next; if (qent->last) qnext = NULL; FREE(qent, M_NETGRAPH); priv->qlen--; } *mp = m; } /* * Trim fragments from the queue whose packets can never be completed. * This assumes a complete packet is NOT at the beginning of the queue. * Returns 1 if fragments were removed, zero otherwise. */ static int ng_ppp_frag_trim(node_p node) { const priv_p priv = node->private; struct ng_ppp_frag *qent, *qnext = NULL; int removed = 0; /* Scan for "dead" fragments and remove them */ while (1) { int dead = 0; /* If queue is empty, we're done */ if (CIRCLEQ_EMPTY(&priv->frags)) break; /* Determine whether first fragment can ever be completed */ CIRCLEQ_FOREACH(qent, &priv->frags, f_qent) { if (MP_SEQ_DIFF(priv, qent->seq, priv->mseq) >= 0) break; qnext = CIRCLEQ_NEXT(qent, f_qent); KASSERT(qnext != (void*)&priv->frags, ("%s: last frag < MSEQ?", __FUNCTION__)); if (qnext->seq != MP_NEXT_SEQ(priv, qent->seq) || qent->last || qnext->first) { dead = 1; break; } } if (!dead) break; /* Remove fragment and all others in the same packet */ while ((qent = CIRCLEQ_FIRST(&priv->frags)) != qnext) { KASSERT(!CIRCLEQ_EMPTY(&priv->frags), ("%s: empty q", __FUNCTION__)); priv->bundleStats.dropFragments++; CIRCLEQ_REMOVE(&priv->frags, qent, f_qent); NG_FREE_DATA(qent->data, qent->meta); FREE(qent, M_NETGRAPH); priv->qlen--; removed = 1; } } return (removed); } /* * Run the queue, restoring the queue invariants */ static int ng_ppp_frag_process(node_p node) { const priv_p priv = node->private; struct mbuf *m; meta_p meta; /* Deliver any deliverable packets */ while (ng_ppp_check_packet(node)) { ng_ppp_get_packet(node, &m, &meta); ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, m, meta); } /* Delete dead fragments and try again */ if (ng_ppp_frag_trim(node)) { while (ng_ppp_check_packet(node)) { ng_ppp_get_packet(node, &m, &meta); ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, m, meta); } } /* Check for stale fragments while we're here */ ng_ppp_frag_checkstale(node); /* Check queue length */ if (priv->qlen > MP_MAX_QUEUE_LEN) { struct ng_ppp_frag *qent; int i; /* Get oldest fragment */ KASSERT(!CIRCLEQ_EMPTY(&priv->frags), ("%s: empty q", __FUNCTION__)); qent = CIRCLEQ_FIRST(&priv->frags); /* Bump MSEQ if necessary */ if (MP_SEQ_DIFF(priv, priv->mseq, qent->seq) < 0) { priv->mseq = qent->seq; for (i = 0; i < priv->numActiveLinks; i++) { struct ng_ppp_link *const alink = &priv->links[priv->activeLinks[i]]; if (MP_SEQ_DIFF(priv, alink->seq, priv->mseq) < 0) alink->seq = priv->mseq; } } /* Drop it */ priv->bundleStats.dropFragments++; CIRCLEQ_REMOVE(&priv->frags, qent, f_qent); NG_FREE_DATA(qent->data, qent->meta); FREE(qent, M_NETGRAPH); priv->qlen--; /* Process queue again */ return ng_ppp_frag_process(node); } /* Done */ return (0); } /* * Check for 'stale' completed packets that need to be delivered * * If a link goes down or has a temporary failure, MSEQ can get * "stuck", because no new incoming fragments appear on that link. * This can cause completed packets to never get delivered if * their sequence numbers are all > MSEQ + 1. * * This routine checks how long all of the completed packets have * been sitting in the queue, and if too long, removes fragments * from the queue and increments MSEQ to allow them to be delivered. */ static void ng_ppp_frag_checkstale(node_p node) { const priv_p priv = node->private; struct ng_ppp_frag *qent, *beg, *end; struct timeval now, age; struct mbuf *m; meta_p meta; int i, seq; now.tv_sec = 0; /* uninitialized state */ while (1) { /* If queue is empty, we're done */ if (CIRCLEQ_EMPTY(&priv->frags)) break; /* Find the first complete packet in the queue */ beg = end = NULL; seq = CIRCLEQ_FIRST(&priv->frags)->seq; CIRCLEQ_FOREACH(qent, &priv->frags, f_qent) { if (qent->first) beg = qent; else if (qent->seq != seq) beg = NULL; if (beg != NULL && qent->last) { end = qent; break; } seq = MP_NEXT_SEQ(priv, seq); } /* If none found, exit */ if (end == NULL) break; /* Get current time (we assume we've been up for >= 1 second) */ if (now.tv_sec == 0) getmicrouptime(&now); /* Check if packet has been queued too long */ age = now; timevalsub(&age, &beg->timestamp); if (timevalcmp(&age, &ng_ppp_max_staleness, < )) break; /* Throw away junk fragments in front of the completed packet */ while ((qent = CIRCLEQ_FIRST(&priv->frags)) != beg) { KASSERT(!CIRCLEQ_EMPTY(&priv->frags), ("%s: empty q", __FUNCTION__)); priv->bundleStats.dropFragments++; CIRCLEQ_REMOVE(&priv->frags, qent, f_qent); NG_FREE_DATA(qent->data, qent->meta); FREE(qent, M_NETGRAPH); priv->qlen--; } /* Extract completed packet */ ng_ppp_get_packet(node, &m, &meta); /* Bump MSEQ if necessary */ if (MP_SEQ_DIFF(priv, priv->mseq, end->seq) < 0) { priv->mseq = end->seq; for (i = 0; i < priv->numActiveLinks; i++) { struct ng_ppp_link *const alink = &priv->links[priv->activeLinks[i]]; if (MP_SEQ_DIFF(priv, alink->seq, priv->mseq) < 0) alink->seq = priv->mseq; } } /* Deliver packet */ ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, m, meta); } } /* * Periodically call ng_ppp_frag_checkstale() */ static void ng_ppp_frag_timeout(void *arg) { const node_p node = arg; const priv_p priv = node->private; int s = splnet(); /* Handle the race where shutdown happens just before splnet() above */ if ((node->flags & NG_INVALID) != 0) { ng_unref(node); splx(s); return; } /* Reset timer state after timeout */ KASSERT(priv->timerActive, ("%s: !timerActive", __FUNCTION__)); priv->timerActive = 0; KASSERT(node->refs > 1, ("%s: refs=%d", __FUNCTION__, node->refs)); ng_unref(node); /* Start timer again */ ng_ppp_start_frag_timer(node); /* Scan the fragment queue */ ng_ppp_frag_checkstale(node); splx(s); } /* * 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]; struct ng_ppp_link *const link = &priv->links[linkNum]; /* 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 > link->conf.mru) len = link->conf.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; shdr = priv->xseq; priv->xseq = (priv->xseq + 1) % MP_SHORT_SEQ_MASK; if (firstFragment) shdr |= MP_SHORT_FIRST_FLAG; if (lastFragment) shdr |= MP_SHORT_LAST_FLAG; shdr = htons(shdr); m2 = ng_ppp_prepend(m2, &shdr, 2); } else { u_int32_t lhdr; lhdr = priv->xseq; priv->xseq = (priv->xseq + 1) % MP_LONG_SEQ_MASK; if (firstFragment) lhdr |= MP_LONG_FIRST_FLAG; if (lastFragment) lhdr |= MP_LONG_LAST_FLAG; lhdr = htonl(lhdr); m2 = ng_ppp_prepend(m2, &lhdr, 4); } if (m2 == NULL) { if (!lastFragment) m_freem(m); NG_FREE_META(meta); return (ENOBUFS); } /* Copy the meta information, if any */ meta2 = lastFragment ? meta : ng_copy_meta(meta); /* Send fragment */ error = ng_ppp_output(node, 0, PROT_MP, linkNum, m2, meta2); 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; 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 */ getmicrouptime(&now); /* Compute latencies for each link at this point in time */ for (activeLinkNum = 0; activeLinkNum < priv->numActiveLinks; activeLinkNum++) { struct ng_ppp_link *alink; struct timeval diff; int xmitBytes; /* Start with base latency value */ alink = &priv->links[priv->activeLinks[activeLinkNum]]; latency[activeLinkNum] = alink->conf.latency; sortByLatency[activeLinkNum] = activeLinkNum; /* see below */ /* Any additional latency? */ if (alink->bytesInQueue == 0) continue; /* Compute time delta since last write */ diff = now; timevalsub(&diff, &alink->lastWrite); if (now.tv_sec < 0 || diff.tv_sec >= 10) { /* sanity */ alink->bytesInQueue = 0; continue; } /* How many bytes could have transmitted since last write? */ xmitBytes = (alink->conf.bandwidth * diff.tv_sec) + (alink->conf.bandwidth * (diff.tv_usec / 1000)) / 100; alink->bytesInQueue -= xmitBytes; if (alink->bytesInQueue < 0) alink->bytesInQueue = 0; else latency[activeLinkNum] += (100 * alink->bytesInQueue) / alink->conf.bandwidth; } /* Sort active 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->links[ priv->activeLinks[sortByLatency[i]]].conf.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->links[ priv->activeLinks[sortByLatency[i]]].conf.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->links[ priv->activeLinks[sortByLatency[i]]].conf.bandwidth; distrib[sortByLatency[i]] = (bw * (t0 - latency[sortByLatency[i]]) + 50) / 100; total += distrib[sortByLatency[i]]; } /* Deal with any rounding error */ if (total < len) { struct ng_ppp_link *fastLink = &priv->links[priv->activeLinks[sortByLatency[0]]]; int fast = 0; /* Find the fastest link */ for (i = 1; i < numFragments; i++) { struct ng_ppp_link *const link = &priv->links[priv->activeLinks[sortByLatency[i]]]; if (link->conf.bandwidth > fastLink->conf.bandwidth) { fast = i; fastLink = link; } } distrib[sortByLatency[fast]] += len - total; } else while (total > len) { struct ng_ppp_link *slowLink = &priv->links[priv->activeLinks[sortByLatency[0]]]; int delta, slow = 0; /* Find the slowest link that still has bytes to remove */ for (i = 1; i < numFragments; i++) { struct ng_ppp_link *const link = &priv->links[priv->activeLinks[sortByLatency[i]]]; if (distrib[sortByLatency[slow]] == 0 || (distrib[sortByLatency[i]] > 0 && link->conf.bandwidth < slowLink->conf.bandwidth)) { slow = i; slowLink = link; } } delta = total - len; if (delta > distrib[sortByLatency[slow]]) delta = distrib[sortByLatency[slow]]; distrib[sortByLatency[slow]] -= delta; total -= delta; } } /* * 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) { if (compOK && PROT_COMPRESSABLE(proto)) { u_char pbyte = (u_char)proto; return ng_ppp_prepend(m, &pbyte, 1); } else { u_int16_t pword = htons((u_int16_t)proto); return ng_ppp_prepend(m, &pword, 2); } } /* * Prepend some bytes to an mbuf */ static struct mbuf * ng_ppp_prepend(struct mbuf *m, const void *buf, int len) { M_PREPEND(m, len, M_NOWAIT); if (m == NULL || (m->m_len < len && (m = m_pullup(m, len)) == NULL)) return (NULL); bcopy(buf, mtod(m, u_char *), len); 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->links[i].conf.enableACFComp ? 0 : 2) + (priv->links[i].conf.enableProtoComp ? 1 : 2) + (priv->conf.xmitShortSeq ? 2 : 4); priv->links[i].conf.latency += ((hdrBytes * priv->links[i].conf.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++) { struct ng_ppp_link *const link = &priv->links[i]; /* Is link active? */ if (link->conf.enableLink && link->hook != NULL) { struct ng_ppp_link *link0; /* Add link to list of active links */ priv->activeLinks[priv->numActiveLinks++] = i; link0 = &priv->links[priv->activeLinks[0]]; /* Determine if all links are still equal */ if (link->conf.latency != link0->conf.latency || link->conf.bandwidth != link0->conf.bandwidth) priv->allLinksEqual = 0; /* Initialize rec'd sequence number */ if (link->seq == MP_NOSEQ) { link->seq = (link == link0) ? MP_INITIAL_SEQ : link0->seq; } } else link->seq = MP_NOSEQ; } /* Update MP state as multi-link is active or not */ if (priv->conf.enableMultilink && priv->numActiveLinks > 0) ng_ppp_start_frag_timer(node); else { ng_ppp_stop_frag_timer(node); ng_ppp_frag_reset(node); priv->xseq = MP_INITIAL_SEQ; priv->mseq = MP_INITIAL_SEQ; for (i = 0; i < NG_PPP_MAX_LINKS; i++) { struct ng_ppp_link *const link = &priv->links[i]; bzero(&link->lastWrite, sizeof(link->lastWrite)); link->bytesInQueue = 0; link->seq = MP_NOSEQ; } } } /* * 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_conf *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].enableLink && priv->links[i].hook != NULL) newNumLinksActive++; if (!newConf->links[i].enableLink) continue; 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); } /* Check bundle parameters */ if (newConf->bund.enableMultilink && newConf->bund.mrru < MP_MIN_MRRU) return (0); /* Disallow changes to multi-link configuration while MP is active */ if (priv->numActiveLinks > 0 && newNumLinksActive > 0) { if (!priv->conf.enableMultilink != !newConf->bund.enableMultilink || !priv->conf.xmitShortSeq != !newConf->bund.xmitShortSeq || !priv->conf.recvShortSeq != !newConf->bund.recvShortSeq) return (0); } /* At most one link can be active unless multi-link is enabled */ if (!newConf->bund.enableMultilink && newNumLinksActive > 1) return (0); /* Configuration change would be valid */ return (1); } /* * Free all entries in the fragment queue */ static void ng_ppp_frag_reset(node_p node) { const priv_p priv = node->private; struct ng_ppp_frag *qent, *qnext; for (qent = CIRCLEQ_FIRST(&priv->frags); qent != (void *)&priv->frags; qent = qnext) { qnext = CIRCLEQ_NEXT(qent, f_qent); NG_FREE_DATA(qent->data, qent->meta); FREE(qent, M_NETGRAPH); } CIRCLEQ_INIT(&priv->frags); priv->qlen = 0; } /* * Start fragment queue timer */ static void ng_ppp_start_frag_timer(node_p node) { const priv_p priv = node->private; if (!priv->timerActive) { priv->fragTimer = timeout(ng_ppp_frag_timeout, node, MP_FRAGTIMER_INTERVAL); priv->timerActive = 1; node->refs++; } } /* * Stop fragment queue timer */ static void ng_ppp_stop_frag_timer(node_p node) { const priv_p priv = node->private; if (priv->timerActive) { untimeout(ng_ppp_frag_timeout, node, priv->fragTimer); priv->timerActive = 0; KASSERT(node->refs > 1, ("%s: refs=%d", __FUNCTION__, node->refs)); ng_unref(node); } } Index: stable/3/sys/netgraph/ng_ppp.h =================================================================== --- stable/3/sys/netgraph/ng_ppp.h (revision 67531) +++ stable/3/sys/netgraph/ng_ppp.h (revision 67532) @@ -1,192 +1,192 @@ /* * ng_ppp.h * * Copyright (c) 1996-2000 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 + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_ppp.h,v 1.8 1999/01/25 02:40:02 archie Exp $ */ #ifndef _NETGRAPH_PPP_H_ #define _NETGRAPH_PPP_H_ /* Node type name and magic cookie */ #define NG_PPP_NODE_TYPE "ppp" #define NGM_PPP_COOKIE 940897794 /* Maximum number of supported links */ #define NG_PPP_MAX_LINKS 16 /* Pseudo-link number representing the multi-link bundle */ #define NG_PPP_BUNDLE_LINKNUM 0xffff /* Max allowable link latency (miliseconds) and bandwidth (bytes/second/10) */ #define NG_PPP_MAX_LATENCY 1000 /* 1 second */ #define NG_PPP_MAX_BANDWIDTH 125000 /* 10 Mbits / second */ /* Hook names */ #define NG_PPP_HOOK_BYPASS "bypass" /* unknown protocols */ #define NG_PPP_HOOK_COMPRESS "compress" /* outgoing compression */ #define NG_PPP_HOOK_DECOMPRESS "decompress" /* incoming decompression */ #define NG_PPP_HOOK_ENCRYPT "encrypt" /* outgoing encryption */ #define NG_PPP_HOOK_DECRYPT "decrypt" /* incoming decryption */ #define NG_PPP_HOOK_VJC_IP "vjc_ip" /* VJC raw IP */ #define NG_PPP_HOOK_VJC_COMP "vjc_vjcomp" /* VJC compressed TCP */ #define NG_PPP_HOOK_VJC_UNCOMP "vjc_vjuncomp" /* VJC uncompressed TCP */ #define NG_PPP_HOOK_VJC_VJIP "vjc_vjip" /* VJC uncompressed IP */ #define NG_PPP_HOOK_INET "inet" /* IP packet data */ #define NG_PPP_HOOK_ATALK "atalk" /* AppleTalk packet data */ #define NG_PPP_HOOK_IPX "ipx" /* IPX packet data */ #define NG_PPP_HOOK_IPV6 "ipv6" /* IPv6 packet data */ #define NG_PPP_HOOK_LINK_PREFIX "link" /* append decimal link number */ /* Netgraph commands */ enum { NGM_PPP_SET_CONFIG = 1, /* takes struct ng_ppp_node_conf */ NGM_PPP_GET_CONFIG, /* returns ng_ppp_node_conf */ NGM_PPP_GET_LINK_STATS, /* takes link #, returns stats struct */ NGM_PPP_CLR_LINK_STATS, /* takes link #, clears link stats */ NGM_PPP_GETCLR_LINK_STATS, /* takes link #, returns & clrs stats */ }; /* Per-link config structure */ struct ng_ppp_link_conf { u_char enableLink; /* enable this link */ u_char enableProtoComp;/* enable protocol field compression */ u_char enableACFComp; /* enable addr/ctrl field compression */ u_int16_t mru; /* peer MRU */ u_int32_t latency; /* link latency (in milliseconds) */ u_int32_t bandwidth; /* link bandwidth (in bytes/second) */ }; /* Keep this in sync with the above structure definition */ #define NG_PPP_LINK_TYPE_INFO { \ { \ { "enableLink", &ng_parse_int8_type }, \ { "enableProtoComp", &ng_parse_int8_type }, \ { "enableACFComp", &ng_parse_int8_type }, \ { "mru", &ng_parse_int16_type }, \ { "latency", &ng_parse_int32_type }, \ { "bandwidth", &ng_parse_int32_type }, \ { NULL }, \ } \ } /* Bundle config structure */ struct ng_ppp_bund_conf { u_int16_t mrru; /* multilink peer MRRU */ u_char enableMultilink; /* enable multilink */ u_char recvShortSeq; /* recv multilink short seq # */ u_char xmitShortSeq; /* xmit multilink short seq # */ u_char enableRoundRobin; /* xmit whole packets */ u_char enableIP; /* enable IP data flow */ u_char enableIPv6; /* enable IPv6 data flow */ u_char enableAtalk; /* enable AppleTalk data flow */ u_char enableIPX; /* enable IPX data flow */ u_char enableCompression; /* enable PPP compression */ u_char enableDecompression; /* enable PPP decompression */ u_char enableEncryption; /* enable PPP encryption */ u_char enableDecryption; /* enable PPP decryption */ u_char enableVJCompression; /* enable VJ compression */ u_char enableVJDecompression; /* enable VJ decompression */ }; /* Keep this in sync with the above structure definition */ #define NG_PPP_BUND_TYPE_INFO { \ { \ { "mrru", &ng_parse_int16_type }, \ { "enableMultilink", &ng_parse_int8_type }, \ { "recvShortSeq", &ng_parse_int8_type }, \ { "xmitShortSeq", &ng_parse_int8_type }, \ { "enableRoundRobin", &ng_parse_int8_type }, \ { "enableIP", &ng_parse_int8_type }, \ { "enableIPv6", &ng_parse_int8_type }, \ { "enableAtalk", &ng_parse_int8_type }, \ { "enableIPX", &ng_parse_int8_type }, \ { "enableCompression", &ng_parse_int8_type }, \ { "enableDecompression", &ng_parse_int8_type }, \ { "enableEncryption", &ng_parse_int8_type }, \ { "enableDecryption", &ng_parse_int8_type }, \ { "enableVJCompression", &ng_parse_int8_type }, \ { "enableVJDecompression", &ng_parse_int8_type }, \ { NULL } \ } \ } /* Total node config structure */ struct ng_ppp_node_conf { struct ng_ppp_bund_conf bund; struct ng_ppp_link_conf links[NG_PPP_MAX_LINKS]; }; /* Keep this in sync with the above structure definition */ #define NG_PPP_CONFIG_TYPE_INFO(bctype, arytype) { \ { \ { "bund", (bctype) }, \ { "links", (arytype) }, \ { NULL } \ } \ } /* Statistics struct for a link (or the bundle if NG_PPP_BUNDLE_LINKNUM) */ struct ng_ppp_link_stat { u_int32_t xmitFrames; /* xmit frames on link */ u_int32_t xmitOctets; /* xmit octets on link */ u_int32_t recvFrames; /* recv frames on link */ u_int32_t recvOctets; /* recv octets on link */ u_int32_t badProtos; /* frames rec'd with bogus protocol */ u_int32_t runts; /* Too short MP fragments */ u_int32_t dupFragments; /* MP frames with duplicate seq # */ u_int32_t dropFragments; /* MP fragments we had to drop */ }; /* Keep this in sync with the above structure definition */ #define NG_PPP_STATS_TYPE_INFO { \ { \ { "xmitFrames", &ng_parse_int32_type }, \ { "xmitOctets", &ng_parse_int32_type }, \ { "recvFrames", &ng_parse_int32_type }, \ { "recvOctets", &ng_parse_int32_type }, \ { "badProtos", &ng_parse_int32_type }, \ { "runts", &ng_parse_int32_type }, \ { "dupFragments", &ng_parse_int32_type }, \ { "dropFragments", &ng_parse_int32_type }, \ { NULL } \ } \ } #endif /* _NETGRAPH_PPP_H_ */ Index: stable/3/sys/netgraph/ng_pppoe.c =================================================================== --- stable/3/sys/netgraph/ng_pppoe.c (revision 67531) +++ stable/3/sys/netgraph/ng_pppoe.c (revision 67532) @@ -1,1498 +1,1498 @@ /* * 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 + * Author: Julian Elischer * * $FreeBSD$ * $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 #define SIGNOFF "session closed" /* * This section contains the netgraph method declarations for the * sample node. These methods define the netgraph 'type'. */ 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, NULL }; 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_LISTENING, /* [Daemon] Listening for discover initiation pkt */ PPPOE_SINIT, /* [Client] Sent discovery initiation */ PPPOE_PRIMED, /* [Server] Awaiting PADI from daemon */ PPPOE_SOFFER, /* [Server] Sent offer message (got PADI)*/ PPPOE_SREQ, /* [Client] Sent a Request */ PPPOE_NEWCONNECTED, /* [Server] 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"); neg->numtags--; } } /* * 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, &sp->neg->service.hdr); insert_tag(sp, &uniqtag.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 *utag = NULL, *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 to make sure that the tag area * is contiguous, or we could wander off 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. */ utag = get_tag(ph, PTT_HOST_UNIQ); if ((utag == NULL) || (ntohs(utag->tag_len) != sizeof(sp))) { printf("no host unique field\n"); LEAVE(ENETUNREACH); } sendhook = pppoe_finduniq(node, utag); 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 */ if ((tag = get_tag(ph, PTT_AC_COOKIE))) insert_tag(sp, tag); /* return cookie */ if ((tag = get_tag(ph, PTT_AC_NAME))) insert_tag(sp, tag); /* return it */ insert_tag(sp, utag); /* Host Unique */ 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. */ utag = get_tag(ph, PTT_AC_COOKIE); if ((utag == NULL) || (ntohs(utag->tag_len) != sizeof(sp))) { LEAVE(ENETUNREACH); } sendhook = pppoe_finduniq(node, utag); 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 */ if ((tag = get_tag(ph, PTT_SRV_NAME))) insert_tag(sp, tag);/* return service */ if ((tag = get_tag(ph, PTT_HOST_UNIQ))) insert_tag(sp, tag); /* return it */ insert_tag(sp, utag); /* ac_cookie */ scan_tags(sp, ph); make_packet(sp); sp->state = PPPOE_NEWCONNECTED; sendpacket(sp); /* * 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. */ utag = get_tag(ph, PTT_HOST_UNIQ); if ((utag == NULL) || (ntohs(utag->tag_len) != sizeof(sp))) { LEAVE (ENETUNREACH); break; } sendhook = pppoe_finduniq(node, utag); 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); } /* Also need to trim excess at the end */ if (m->m_pkthdr.len > length) { m_adj(m, -((int)(m->m_pkthdr.len - length))); } 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 = 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 */ if ((tag = get_tag(ph, PTT_SRV_NAME))) insert_tag(sp, tag); /* return service */ if ((tag = get_tag(ph, PTT_HOST_UNIQ))) insert_tag(sp, tag); /* returned hostunique */ insert_tag(sp, &uniqtag.hdr); /* 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 information 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); if(m == NULL) printf("pppoe: Session out of mbufs\n"); else { 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 revert 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_CONNECTED: printf("pppoe: sendpacket: unexpected state\n"); break; case PPPOE_NEWCONNECTED: /* send the PADS without a timeout - we're now connected */ m0 = m_copypacket(sp->neg->m, M_DONTWAIT); NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy); 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 * PPPOE_INITIAL_TIMEOUT)); neg->timeout = PPPOE_INITIAL_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. Don't do any tags that have been handled in 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: stable/3/sys/netgraph/ng_pppoe.h =================================================================== --- stable/3/sys/netgraph/ng_pppoe.h (revision 67531) +++ stable/3/sys/netgraph/ng_pppoe.h (revision 67532) @@ -1,225 +1,225 @@ /* * ng_pppoe.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 + * Author: Julian Elischer * * $FreeBSD$ * $Whistle: ng_pppoe.h,v 1.7 1999/10/16 10:16:43 julian Exp $ */ #ifndef _NETGRAPH_PPPOE_H_ #define _NETGRAPH_PPPOE_H_ /******************************************************************** * Netgraph hook constants etc. ********************************************************************/ /* Node type name. This should be unique among all netgraph node types */ #define NG_PPPOE_NODE_TYPE "pppoe" #define NGM_PPPOE_COOKIE 939032003 /* Number of active sessions we can handle */ #define PPPOE_NUM_SESSIONS 16 /* for now */ #define PPPOE_SERVICE_NAME_SIZE 64 /* for now */ /* Hook names */ #define NG_PPPOE_HOOK_ETHERNET "ethernet" #define NG_PPPOE_HOOK_PADI "PADI" /* default PADI requests come here */ #define NG_PPPOE_HOOK_S_LEADIN "service" /* PADO responses from PADI */ #define NG_PPPOE_HOOK_C_LEADIN "client" /* Connect message starts this */ #define NG_PPPOE_HOOK_DEBUG "debug" /********************************************************************** * Netgraph commands understood by this node type. * FAIL, SUCCESS and CLOSE are sent by the node rather than received. ********************************************************************/ enum cmd { NGM_PPPOE_SET_FLAG = 1, NGM_PPPOE_CONNECT = 2, /* Client, Try find this service */ NGM_PPPOE_LISTEN = 3, /* Server, Await a request for this service */ NGM_PPPOE_OFFER = 4, /* Server, hook X should respond (*) */ NGM_PPPOE_SUCCESS = 5, /* State machine connected */ NGM_PPPOE_FAIL = 6, /* State machine could not connect */ NGM_PPPOE_CLOSE = 7, /* Session closed down */ NGM_PPPOE_GET_STATUS }; /*********************** * Structures passed in the various netgraph command messages. ***********************/ /* This structure is returned by the NGM_PPPOE_GET_STATUS command */ struct ngpppoestat { u_int packets_in; /* packets in from ethernet */ u_int packets_out; /* packets out towards ethernet */ }; /* * When this structure is accepted by the NGM_PPPOE_CONNECT command : * The data field is MANDATORY. * The session sends out a PADI request for the named service. * * * When this structure is accepted by the NGM_PPPOE_WAIT command. * If no service is given this is assumed to accept ALL PADI requests. * This may at some time take a regexp expression, but not yet. * Matching PADI requests will be passed up the named hook. * * * When this structure is accepted by the NGM_PPPOE_OFFER command: * The AC-NAme field is set from that given and a PADI * packet is expected to arrive from the session control daemon, on the * named hook. The session will then issue the appropriate PADO * and begin negotiation. */ struct ngpppoe_init_data { char hook[NG_HOOKLEN + 1]; /* hook to monitor on */ u_int16_t data_len; /* Length of the service name */ char data[0]; /* init data goes here */ }; /* * This structure is used by the asychronous success and failure messages. * (to report which hook has failed or connected). The message is sent * to whoever requested the connection. (close may use this too). */ struct ngpppoe_sts { char hook[NG_HOOKLEN + 1]; /* hook associated with event session */ }; /******************************************************************** * Constants and definitions specific to pppoe ********************************************************************/ #define PPPOE_TIMEOUT_LIMIT 64 #define PPPOE_OFFER_TIMEOUT 16 #define PPPOE_INITIAL_TIMEOUT 2 /* Codes to identify message types */ #define PADI_CODE 0x09 #define PADO_CODE 0x07 #define PADR_CODE 0x19 #define PADS_CODE 0x65 #define PADT_CODE 0xa7 /* Tag identifiers */ #if BYTE_ORDER == BIG_ENDIAN #define PTT_EOL (0x0000) #define PTT_SRV_NAME (0x0101) #define PTT_AC_NAME (0x0102) #define PTT_HOST_UNIQ (0x0103) #define PTT_AC_COOKIE (0x0104) #define PTT_VENDOR (0x0105) #define PTT_RELAY_SID (0x0106) #define PTT_SRV_ERR (0x0201) #define PTT_SYS_ERR (0x0202) #define PTT_GEN_ERR (0x0203) #define ETHERTYPE_PPPOE_DISC 0x8863 /* pppoe discovery packets */ #define ETHERTYPE_PPPOE_SESS 0x8864 /* pppoe session packets */ #else #define PTT_EOL (0x0000) #define PTT_SRV_NAME (0x0101) #define PTT_AC_NAME (0x0201) #define PTT_HOST_UNIQ (0x0301) #define PTT_AC_COOKIE (0x0401) #define PTT_VENDOR (0x0501) #define PTT_RELAY_SID (0x0601) #define PTT_SRV_ERR (0x0102) #define PTT_SYS_ERR (0x0202) #define PTT_GEN_ERR (0x0302) #define ETHERTYPE_PPPOE_DISC 0x6388 /* pppoe discovery packets */ #define ETHERTYPE_PPPOE_SESS 0x6488 /* pppoe session packets */ #endif struct pppoe_tag { u_int16_t tag_type; u_int16_t tag_len; char tag_data[0]; }__attribute ((packed)); struct pppoe_hdr{ u_int8_t ver:4; u_int8_t type:4; u_int8_t code; u_int16_t sid; u_int16_t length; struct pppoe_tag tag[0]; }__attribute__ ((packed)); struct pppoe_full_hdr { struct ether_header eh; struct pppoe_hdr ph; }__attribute__ ((packed)); union packet { struct pppoe_full_hdr pkt_header; u_int8_t bytes[2048]; }; struct datatag { struct pppoe_tag hdr; u_int8_t data[PPPOE_SERVICE_NAME_SIZE]; }; /* * Define the order in which we will place tags in packets * this may be ignored */ /* for PADI */ #define TAGI_SVC 0 #define TAGI_HUNIQ 1 /* for PADO */ #define TAGO_ACNAME 0 #define TAGO_SVC 1 #define TAGO_COOKIE 2 #define TAGO_HUNIQ 3 /* for PADR */ #define TAGR_SVC 0 #define TAGR_HUNIQ 1 #define TAGR_COOKIE 2 /* for PADS */ #define TAGS_ACNAME 0 #define TAGS_SVC 1 #define TAGS_COOKIE 2 #define TAGS_HUNIQ 3 /* for PADT */ #endif /* _NETGRAPH_PPPOE_H_ */ Index: stable/3/sys/netgraph/ng_pptpgre.c =================================================================== --- stable/3/sys/netgraph/ng_pptpgre.c (revision 67531) +++ stable/3/sys/netgraph/ng_pptpgre.c (revision 67532) @@ -1,931 +1,931 @@ /* * ng_pptpgre.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 + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_pptpgre.c,v 1.7 1999/12/08 00:10:06 archie Exp $ */ /* * PPTP/GRE netgraph node type. * * This node type does the GRE encapsulation as specified for the PPTP * protocol (RFC 2637, section 4). This includes sequencing and * retransmission of frames, but not the actual packet delivery nor * any of the TCP control stream protocol. * * The "upper" hook of this node is suitable for attaching to a "ppp" * node link hook. The "lower" hook of this node is suitable for attaching * to a "ksocket" node on hook "inet/raw/gre". */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* GRE packet format, as used by PPTP */ struct greheader { #if BYTE_ORDER == LITTLE_ENDIAN u_char recursion:3; /* recursion control */ u_char ssr:1; /* strict source route */ u_char hasSeq:1; /* sequence number present */ u_char hasKey:1; /* key present */ u_char hasRoute:1; /* routing present */ u_char hasSum:1; /* checksum present */ u_char vers:3; /* version */ u_char flags:4; /* flags */ u_char hasAck:1; /* acknowlege number present */ #elif BYTE_ORDER == BIG_ENDIAN u_char hasSum:1; /* checksum present */ u_char hasRoute:1; /* routing present */ u_char hasKey:1; /* key present */ u_char hasSeq:1; /* sequence number present */ u_char ssr:1; /* strict source route */ u_char recursion:3; /* recursion control */ u_char hasAck:1; /* acknowlege number present */ u_char flags:4; /* flags */ u_char vers:3; /* version */ #else #error BYTE_ORDER is not defined properly #endif u_int16_t proto; /* protocol (ethertype) */ u_int16_t length; /* payload length */ u_int16_t cid; /* call id */ u_int32_t data[0]; /* opt. seq, ack, then data */ }; /* The PPTP protocol ID used in the GRE 'proto' field */ #define PPTP_GRE_PROTO 0x880b /* Bits that must be set a certain way in all PPTP/GRE packets */ #define PPTP_INIT_VALUE ((0x2001 << 16) | PPTP_GRE_PROTO) #define PPTP_INIT_MASK 0xef7fffff /* Min and max packet length */ #define PPTP_MAX_PAYLOAD (0xffff - sizeof(struct greheader) - 8) /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */ #define PPTP_TIME_SCALE 1000 typedef u_int32_t pptptime_t; /* Acknowledgment timeout parameters and functions */ #define PPTP_XMIT_WIN 8 /* max xmit window */ #define PPTP_MIN_RTT (PPTP_TIME_SCALE / 10) /* 1/10 second */ #define PPTP_MAX_TIMEOUT (10 * PPTP_TIME_SCALE) /* 10 seconds */ #define PPTP_ACK_ALPHA(x) ((x) >> 3) /* alpha = 0.125 */ #define PPTP_ACK_BETA(x) ((x) >> 2) /* beta = 0.25 */ #define PPTP_ACK_CHI(x) ((x) << 2) /* chi = 4 */ #define PPTP_ACK_DELTA(x) ((x) << 1) /* delta = 2 */ #define PPTP_SEQ_DIFF(x,y) ((int32_t)(x) - (int32_t)(y)) /* We keep packet retransmit and acknowlegement state in this struct */ struct ng_pptpgre_ackp { int32_t ato; /* adaptive time-out value */ int32_t rtt; /* round trip time estimate */ int32_t dev; /* deviation estimate */ u_int16_t xmitWin; /* size of xmit window */ u_char sackTimerRunning;/* send ack timer is running */ u_char rackTimerRunning;/* recv ack timer is running */ u_int32_t winAck; /* seq when xmitWin will grow */ struct callout_handle sackTimer; /* send ack timer */ struct callout_handle rackTimer; /* recv ack timer */ pptptime_t timeSent[PPTP_XMIT_WIN]; }; /* When we recieve a packet, we wait to see if there's an outgoing packet we can piggy-back the ACK off of. These parameters determine the mimimum and maxmimum length of time we're willing to wait in order to do that. */ #define PPTP_MAX_ACK_DELAY ((int) (0.25 * PPTP_TIME_SCALE)) /* Node private data */ struct ng_pptpgre_private { hook_p upper; /* hook to upper layers */ hook_p lower; /* hook to lower layers */ struct ng_pptpgre_conf conf; /* configuration info */ struct ng_pptpgre_ackp ackp; /* packet transmit ack state */ u_int32_t recvSeq; /* last seq # we rcv'd */ u_int32_t xmitSeq; /* last seq # we sent */ u_int32_t recvAck; /* last seq # peer ack'd */ u_int32_t xmitAck; /* last seq # we ack'd */ struct timeval startTime; /* time node was created */ struct ng_pptpgre_stats stats; /* node statistics */ }; typedef struct ng_pptpgre_private *priv_p; /* Netgraph node methods */ static ng_constructor_t ng_pptpgre_constructor; static ng_rcvmsg_t ng_pptpgre_rcvmsg; static ng_shutdown_t ng_pptpgre_rmnode; static ng_newhook_t ng_pptpgre_newhook; static ng_rcvdata_t ng_pptpgre_rcvdata; static ng_disconnect_t ng_pptpgre_disconnect; /* Helper functions */ static int ng_pptpgre_xmit(node_p node, struct mbuf *m, meta_p meta); static int ng_pptpgre_recv(node_p node, struct mbuf *m, meta_p meta); static void ng_pptpgre_start_send_ack_timer(node_p node, long ackTimeout); static void ng_pptpgre_start_recv_ack_timer(node_p node); static void ng_pptpgre_stop_send_ack_timer(node_p node); static void ng_pptpgre_stop_recv_ack_timer(node_p node); static void ng_pptpgre_recv_ack_timeout(void *arg); static void ng_pptpgre_send_ack_timeout(void *arg); static void ng_pptpgre_reset(node_p node); static pptptime_t ng_pptpgre_time(node_p node); /* Parse type for struct ng_pptpgre_conf */ static const struct ng_parse_struct_info ng_pptpgre_conf_type_info = NG_PPTPGRE_CONF_TYPE_INFO; static const struct ng_parse_type ng_pptpgre_conf_type = { &ng_parse_struct_type, &ng_pptpgre_conf_type_info, }; /* Parse type for struct ng_pptpgre_stats */ static const struct ng_parse_struct_info ng_pptpgre_stats_type_info = NG_PPTPGRE_STATS_TYPE_INFO; static const struct ng_parse_type ng_pptp_stats_type = { &ng_parse_struct_type, &ng_pptpgre_stats_type_info }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_pptpgre_cmdlist[] = { { NGM_PPTPGRE_COOKIE, NGM_PPTPGRE_SET_CONFIG, "setconfig", &ng_pptpgre_conf_type, NULL }, { NGM_PPTPGRE_COOKIE, NGM_PPTPGRE_GET_CONFIG, "getconfig", NULL, &ng_pptpgre_conf_type }, { NGM_PPTPGRE_COOKIE, NGM_PPTPGRE_GET_STATS, "getstats", NULL, &ng_pptp_stats_type }, { NGM_PPTPGRE_COOKIE, NGM_PPTPGRE_CLR_STATS, "clrstats", NULL, NULL }, { NGM_PPTPGRE_COOKIE, NGM_PPTPGRE_GETCLR_STATS, "getclrstats", NULL, &ng_pptp_stats_type }, { 0 } }; /* Node type descriptor */ static struct ng_type ng_pptpgre_typestruct = { NG_VERSION, NG_PPTPGRE_NODE_TYPE, NULL, ng_pptpgre_constructor, ng_pptpgre_rcvmsg, ng_pptpgre_rmnode, ng_pptpgre_newhook, NULL, NULL, ng_pptpgre_rcvdata, ng_pptpgre_rcvdata, ng_pptpgre_disconnect, ng_pptpgre_cmdlist }; NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct); #define ERROUT(x) do { error = (x); goto done; } while (0) /************************************************************************ NETGRAPH NODE STUFF ************************************************************************/ /* * Node type constructor */ static int ng_pptpgre_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_pptpgre_typestruct, nodep))) { FREE(priv, M_NETGRAPH); return (error); } (*nodep)->private = priv; /* Initialize state */ callout_handle_init(&priv->ackp.sackTimer); callout_handle_init(&priv->ackp.rackTimer); /* Done */ return (0); } /* * Give our OK for a hook to be added. */ static int ng_pptpgre_newhook(node_p node, hook_p hook, const char *name) { const priv_p priv = node->private; hook_p *hookPtr; /* Check hook name */ if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) hookPtr = &priv->upper; else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) hookPtr = &priv->lower; else return (EINVAL); /* See if already connected */ if (*hookPtr != NULL) return (EISCONN); /* OK */ *hookPtr = hook; return (0); } /* * Receive a control message. */ static int ng_pptpgre_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_PPTPGRE_COOKIE: switch (msg->header.cmd) { case NGM_PPTPGRE_SET_CONFIG: { struct ng_pptpgre_conf *const newConf = (struct ng_pptpgre_conf *) msg->data; /* Check for invalid or illegal config */ if (msg->header.arglen != sizeof(*newConf)) ERROUT(EINVAL); ng_pptpgre_reset(node); /* reset on configure */ priv->conf = *newConf; break; } case NGM_PPTPGRE_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_PPTPGRE_GET_STATS: case NGM_PPTPGRE_CLR_STATS: case NGM_PPTPGRE_GETCLR_STATS: { if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) { NG_MKRESPONSE(resp, msg, sizeof(priv->stats), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); bcopy(&priv->stats, resp->data, sizeof(priv->stats)); } if (msg->header.cmd != NGM_PPTPGRE_GET_STATS) bzero(&priv->stats, sizeof(priv->stats)); 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 incoming data on a hook. */ static int ng_pptpgre_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { const node_p node = hook->node; const priv_p priv = node->private; /* If not configured, reject */ if (!priv->conf.enabled) { NG_FREE_DATA(m, meta); return (ENXIO); } /* Treat as xmit or recv data */ if (hook == priv->upper) return ng_pptpgre_xmit(node, m, meta); if (hook == priv->lower) return ng_pptpgre_recv(node, m, meta); panic("%s: weird hook", __FUNCTION__); } /* * Destroy node */ static int ng_pptpgre_rmnode(node_p node) { const priv_p priv = node->private; /* Cancel timers */ ng_pptpgre_reset(node); /* 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_pptpgre_disconnect(hook_p hook) { const node_p node = hook->node; const priv_p priv = node->private; /* Zero out hook pointer */ if (hook == priv->upper) priv->upper = NULL; else if (hook == priv->lower) priv->lower = NULL; else panic("%s: unknown hook", __FUNCTION__); /* Go away if no longer connected to anything */ if (node->numhooks == 0) ng_rmnode(node); return (0); } /************************************************************************* TRANSMIT AND RECEIVE FUNCTIONS *************************************************************************/ /* * Transmit an outgoing frame, or just an ack if m is NULL. */ static int ng_pptpgre_xmit(node_p node, struct mbuf *m, meta_p meta) { const priv_p priv = node->private; struct ng_pptpgre_ackp *const a = &priv->ackp; u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)]; struct greheader *const gre = (struct greheader *)buf; int grelen, error; /* Check if there's data */ if (m != NULL) { /* Is our transmit window full? */ if ((u_int32_t)PPTP_SEQ_DIFF(priv->xmitSeq, priv->recvAck) >= a->xmitWin) { priv->stats.xmitDrops++; NG_FREE_DATA(m, meta); return (ENOBUFS); } /* Sanity check frame length */ if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) { priv->stats.xmitTooBig++; NG_FREE_DATA(m, meta); return (EMSGSIZE); } } else priv->stats.xmitLoneAcks++; /* Build GRE header */ ((u_int32_t *) gre)[0] = htonl(PPTP_INIT_VALUE); gre->length = (m != NULL) ? htons((u_short)m->m_pkthdr.len) : 0; gre->cid = htons(priv->conf.peerCid); /* Include sequence number if packet contains any data */ if (m != NULL) { gre->hasSeq = 1; a->timeSent[priv->xmitSeq - priv->recvAck] = ng_pptpgre_time(node); priv->xmitSeq++; gre->data[0] = htonl(priv->xmitSeq); if (priv->xmitSeq == priv->recvAck + 1) ng_pptpgre_start_recv_ack_timer(node); } /* Include acknowledgement (and stop send ack timer) if needed */ if (PPTP_SEQ_DIFF(priv->xmitAck, priv->recvSeq) < 0) { gre->hasAck = 1; priv->xmitAck = priv->recvSeq; gre->data[gre->hasSeq] = htonl(priv->xmitAck); ng_pptpgre_stop_send_ack_timer(node); } /* Prepend GRE header to outgoing frame */ grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); if (m == NULL) { MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) { NG_FREE_META(meta); return (ENOBUFS); } m->m_len = m->m_pkthdr.len = grelen; m->m_pkthdr.rcvif = NULL; } else { M_PREPEND(m, grelen, M_NOWAIT); if (m == NULL || (m->m_len < grelen && (m = m_pullup(m, grelen)) == NULL)) { NG_FREE_META(meta); return (ENOBUFS); } } bcopy(gre, mtod(m, u_char *), grelen); /* Update stats */ priv->stats.xmitPackets++; priv->stats.xmitOctets += m->m_pkthdr.len; /* Deliver packet */ NG_SEND_DATA(error, priv->lower, m, meta); return (error); } /* * Handle an incoming packet. The packet includes the IP header. */ static int ng_pptpgre_recv(node_p node, struct mbuf *m, meta_p meta) { const priv_p priv = node->private; int iphlen, grelen, extralen; struct greheader *gre; struct ip *ip; int error = 0; /* Update stats */ priv->stats.recvPackets++; priv->stats.recvOctets += m->m_pkthdr.len; /* Sanity check packet length */ if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) { priv->stats.recvRunts++; bad: NG_FREE_DATA(m, meta); return (EINVAL); } /* Safely pull up the complete IP+GRE headers */ if (m->m_len < sizeof(*ip) + sizeof(*gre) && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } ip = mtod(m, struct ip *); iphlen = ip->ip_hl << 2; if (m->m_len < iphlen + sizeof(*gre)) { if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } ip = mtod(m, struct ip *); } gre = (struct greheader *)((u_char *)ip + iphlen); grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); if (m->m_pkthdr.len < iphlen + grelen) { priv->stats.recvRunts++; goto bad; } if (m->m_len < iphlen + grelen) { if ((m = m_pullup(m, iphlen + grelen)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } ip = mtod(m, struct ip *); gre = (struct greheader *)((u_char *)ip + iphlen); } /* Sanity check packet length and GRE header bits */ extralen = m->m_pkthdr.len - (iphlen + grelen + (u_int16_t)ntohs(gre->length)); if (extralen < 0) { priv->stats.recvBadGRE++; goto bad; } if ((ntohl(*((u_int32_t *)gre)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) { priv->stats.recvBadGRE++; goto bad; } if (ntohs(gre->cid) != priv->conf.cid) { priv->stats.recvBadCID++; goto bad; } /* Look for peer ack */ if (gre->hasAck) { struct ng_pptpgre_ackp *const a = &priv->ackp; const u_int32_t ack = ntohl(gre->data[gre->hasSeq]); const int index = ack - priv->recvAck - 1; const long sample = ng_pptpgre_time(node) - a->timeSent[index]; long diff; /* Sanity check ack value */ if (PPTP_SEQ_DIFF(ack, priv->xmitSeq) > 0) { priv->stats.recvBadAcks++; goto badAck; /* we never sent it! */ } if (PPTP_SEQ_DIFF(ack, priv->recvAck) <= 0) goto badAck; /* ack already timed out */ priv->recvAck = ack; /* Update adaptive timeout stuff */ diff = sample - a->rtt; a->rtt += PPTP_ACK_ALPHA(diff); if (diff < 0) diff = -diff; a->dev += PPTP_ACK_BETA(diff - a->dev); a->ato = a->rtt + (u_int) (PPTP_ACK_CHI(a->dev)); if (a->ato > PPTP_MAX_TIMEOUT) a->ato = PPTP_MAX_TIMEOUT; ovbcopy(a->timeSent + index + 1, a->timeSent, sizeof(*a->timeSent) * (PPTP_XMIT_WIN - (index + 1))); if (PPTP_SEQ_DIFF(ack, a->winAck) >= 0 && a->xmitWin < PPTP_XMIT_WIN) { a->xmitWin++; a->winAck = ack + a->xmitWin; } /* Stop/(re)start receive ACK timer as necessary */ ng_pptpgre_start_recv_ack_timer(node); } badAck: /* See if frame contains any data */ if (gre->hasSeq) { struct ng_pptpgre_ackp *const a = &priv->ackp; const u_int32_t seq = ntohl(gre->data[0]); /* Sanity check sequence number */ if (PPTP_SEQ_DIFF(seq, priv->recvSeq) <= 0) { if (seq == priv->recvSeq) priv->stats.recvDuplicates++; else priv->stats.recvOutOfOrder++; goto bad; /* out-of-order or dup */ } priv->recvSeq = seq; /* We need to acknowledge this packet; do it soon... */ if (!a->sackTimerRunning) { long ackTimeout; /* Take half of the estimated round trip time */ ackTimeout = (a->rtt >> 1); /* If too soon, just send one right now */ if (!priv->conf.enableDelayedAck) ng_pptpgre_xmit(node, NULL, NULL); else { /* send the ack later */ if (ackTimeout > PPTP_MAX_ACK_DELAY) ackTimeout = PPTP_MAX_ACK_DELAY; ng_pptpgre_start_send_ack_timer(node, ackTimeout); } } /* Trim mbuf down to internal payload */ m_adj(m, iphlen + grelen); if (extralen > 0) m_adj(m, -extralen); /* Deliver frame to upper layers */ NG_SEND_DATA(error, priv->upper, m, meta); } else { priv->stats.recvLoneAcks++; NG_FREE_DATA(m, meta); /* no data to deliver */ } return (error); } /************************************************************************* TIMER RELATED FUNCTIONS *************************************************************************/ /* * Start a timer for the peer's acknowledging our oldest unacknowledged * sequence number. If we get an ack for this sequence number before * the timer goes off, we cancel the timer. Resets currently running * recv ack timer, if any. */ static void ng_pptpgre_start_recv_ack_timer(node_p node) { const priv_p priv = node->private; struct ng_pptpgre_ackp *const a = &priv->ackp; int remain; /* Stop current recv ack timer, if any */ if (a->rackTimerRunning) ng_pptpgre_stop_recv_ack_timer(node); /* Are we waiting for an acknowlegement? */ if (priv->recvAck == priv->xmitSeq) return; /* Compute how long until oldest unack'd packet times out, and reset the timer to that time. */ remain = (a->timeSent[0] + a->ato) - ng_pptpgre_time(node); if (remain < 0) remain = 0; /* Start timer */ a->rackTimer = timeout(ng_pptpgre_recv_ack_timeout, node, remain * hz / PPTP_TIME_SCALE); node->refs++; a->rackTimerRunning = 1; } /* * Stop the recv ack timer, if running. */ static void ng_pptpgre_stop_recv_ack_timer(node_p node) { const priv_p priv = node->private; struct ng_pptpgre_ackp *const a = &priv->ackp; if (a->rackTimerRunning) { untimeout(ng_pptpgre_recv_ack_timeout, node, a->rackTimer); KASSERT(node->refs > 1, ("%s: no refs", __FUNCTION__)); ng_unref(node); a->rackTimerRunning = 0; } } /* * The peer has failed to acknowledge the oldest unacknowledged sequence * number within the time allotted. Update our adaptive timeout parameters * and reset/restart the recv ack timer. */ static void ng_pptpgre_recv_ack_timeout(void *arg) { int s = splnet(); const node_p node = arg; const priv_p priv = node->private; struct ng_pptpgre_ackp *const a = &priv->ackp; /* Avoid shutdown race condition */ if ((node->flags & NG_INVALID) != 0) { ng_unref(node); splx(s); return; } /* Release timer reference */ KASSERT(a->rackTimerRunning, ("%s: !rackTimer", __FUNCTION__)); a->rackTimerRunning = 0; KASSERT(node->refs > 1, ("%s: no refs", __FUNCTION__)); ng_unref(node); /* Update adaptive timeout stuff */ priv->stats.recvAckTimeouts++; a->rtt = PPTP_ACK_DELTA(a->rtt); a->ato = a->rtt + PPTP_ACK_CHI(a->dev); if (a->ato > PPTP_MAX_TIMEOUT) a->ato = PPTP_MAX_TIMEOUT; priv->recvAck++; /* assume packet was lost */ a->winAck = priv->recvAck + a->xmitWin; /* reset win expand time */ ovbcopy(a->timeSent + 1, a->timeSent, /* shift xmit window times */ sizeof(*a->timeSent) * (PPTP_XMIT_WIN - 1)); a->xmitWin = (a->xmitWin + 1) / 2; /* shrink transmit window */ /* Restart timer if there are any more outstanding frames */ if (priv->recvAck != priv->xmitSeq) ng_pptpgre_start_recv_ack_timer(node); splx(s); } /* * Start the send ack timer. This assumes the timer is not * already running. */ static void ng_pptpgre_start_send_ack_timer(node_p node, long ackTimeout) { const priv_p priv = node->private; struct ng_pptpgre_ackp *const a = &priv->ackp; KASSERT(!a->sackTimerRunning, ("%s: sackTimer", __FUNCTION__)); a->sackTimer = timeout(ng_pptpgre_send_ack_timeout, node, ackTimeout * hz / PPTP_TIME_SCALE); node->refs++; a->sackTimerRunning = 1; } /* * Stop the send ack timer, if running. */ static void ng_pptpgre_stop_send_ack_timer(node_p node) { const priv_p priv = node->private; struct ng_pptpgre_ackp *const a = &priv->ackp; if (a->sackTimerRunning) { untimeout(ng_pptpgre_send_ack_timeout, node, a->sackTimer); KASSERT(node->refs > 1, ("%s: no refs", __FUNCTION__)); ng_unref(node); a->sackTimerRunning = 0; } } /* * We've waited as long as we're willing to wait before sending an * acknowledgement to the peer for received frames. We had hoped to * be able to piggy back our acknowledgement on an outgoing data frame, * but apparently there haven't been any since. So send the ack now. */ static void ng_pptpgre_send_ack_timeout(void *arg) { int s = splnet(); const node_p node = arg; const priv_p priv = node->private; struct ng_pptpgre_ackp *const a = &priv->ackp; /* Avoid shutdown race condition */ if ((node->flags & NG_INVALID) != 0) { ng_unref(node); splx(s); return; } /* Release timer reference */ KASSERT(a->sackTimerRunning, ("%s: !sackTimer", __FUNCTION__)); a->sackTimerRunning = 0; KASSERT(node->refs > 1, ("%s: no refs", __FUNCTION__)); ng_unref(node); /* Send a frame with an ack but no payload */ ng_pptpgre_xmit(node, NULL, NULL); splx(s); } /************************************************************************* MISC FUNCTIONS *************************************************************************/ /* * Reset state */ static void ng_pptpgre_reset(node_p node) { const priv_p priv = node->private; struct ng_pptpgre_ackp *const a = &priv->ackp; /* Reset adaptive timeout state */ a->ato = PPTP_MAX_TIMEOUT; a->rtt = priv->conf.peerPpd * PPTP_TIME_SCALE / 10; /* ppd in 10ths */ if (a->rtt < PPTP_MIN_RTT) a->rtt = PPTP_MIN_RTT; a->dev = 0; a->xmitWin = (priv->conf.recvWin + 1) / 2; if (a->xmitWin < 1) a->xmitWin = 1; if (a->xmitWin > PPTP_XMIT_WIN) a->xmitWin = PPTP_XMIT_WIN; a->winAck = a->xmitWin; /* Reset sequence numbers */ priv->recvSeq = 0; priv->recvAck = 0; priv->xmitSeq = 0; priv->xmitAck = 0; /* Reset start time */ getmicrouptime(&priv->startTime); /* Reset stats */ bzero(&priv->stats, sizeof(priv->stats)); /* Stop timers */ ng_pptpgre_stop_send_ack_timer(node); ng_pptpgre_stop_recv_ack_timer(node); } /* * Return the current time scaled & translated to our internally used format. */ static pptptime_t ng_pptpgre_time(node_p node) { const priv_p priv = node->private; struct timeval tv; getmicrouptime(&tv); if (tv.tv_sec < priv->startTime.tv_sec || (tv.tv_sec == priv->startTime.tv_sec && tv.tv_usec < priv->startTime.tv_usec)) return (0); timevalsub(&tv, &priv->startTime); tv.tv_sec *= PPTP_TIME_SCALE; tv.tv_usec /= 1000000 / PPTP_TIME_SCALE; return(tv.tv_sec + tv.tv_usec); } Index: stable/3/sys/netgraph/ng_pptpgre.h =================================================================== --- stable/3/sys/netgraph/ng_pptpgre.h (revision 67531) +++ stable/3/sys/netgraph/ng_pptpgre.h (revision 67532) @@ -1,128 +1,128 @@ /* * ng_pptpgre.h * * Copyright (c) 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 + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_pptpgre.h,v 1.3 1999/12/08 00:11:36 archie Exp $ */ #ifndef _NETGRAPH_PPTPGRE_H_ #define _NETGRAPH_PPTPGRE_H_ /* Node type name and magic cookie */ #define NG_PPTPGRE_NODE_TYPE "pptpgre" #define NGM_PPTPGRE_COOKIE 942783546 /* Hook names */ #define NG_PPTPGRE_HOOK_UPPER "upper" /* to upper layers */ #define NG_PPTPGRE_HOOK_LOWER "lower" /* to lower layers */ /* Configuration for a session */ struct ng_pptpgre_conf { u_char enabled; /* enables traffic flow */ u_char enableDelayedAck;/* enables delayed acks */ u_int16_t cid; /* my call id */ u_int16_t peerCid; /* peer call id */ u_int16_t recvWin; /* peer recv window size */ u_int16_t peerPpd; /* peer packet processing delay (in units of 1/10 of a second) */ }; /* Keep this in sync with the above structure definition */ #define NG_PPTPGRE_CONF_TYPE_INFO { \ { \ { "enabled", &ng_parse_int8_type }, \ { "enableDelayedAck", &ng_parse_int8_type }, \ { "cid", &ng_parse_int16_type }, \ { "peerCid", &ng_parse_int16_type }, \ { "recvWin", &ng_parse_int16_type }, \ { "peerPpd", &ng_parse_int16_type }, \ { NULL }, \ } \ } /* Statistics struct */ struct ng_pptpgre_stats { u_int32_t xmitPackets; /* number of GRE packets xmit */ u_int32_t xmitOctets; /* number of GRE octets xmit */ u_int32_t xmitLoneAcks; /* ack-only packets transmitted */ u_int32_t xmitDrops; /* xmits dropped due to full window */ u_int32_t xmitTooBig; /* xmits dropped because too big */ u_int32_t recvPackets; /* number of GRE packets rec'd */ u_int32_t recvOctets; /* number of GRE octets rec'd */ u_int32_t recvRunts; /* too short packets rec'd */ u_int32_t recvBadGRE; /* bogus packets rec'd (bad GRE hdr) */ u_int32_t recvBadAcks; /* bogus ack's rec'd in GRE header */ u_int32_t recvBadCID; /* pkts with unknown call ID rec'd */ u_int32_t recvOutOfOrder; /* packets rec'd out of order */ u_int32_t recvDuplicates; /* packets rec'd with duplicate seq # */ u_int32_t recvLoneAcks; /* ack-only packets rec'd */ u_int32_t recvAckTimeouts; /* times peer failed to ack in time */ }; /* Keep this in sync with the above structure definition */ #define NG_PPTPGRE_STATS_TYPE_INFO { \ { \ { "xmitPackets", &ng_parse_int32_type }, \ { "xmitOctets", &ng_parse_int32_type }, \ { "xmitLoneAcks", &ng_parse_int32_type }, \ { "xmitDrops", &ng_parse_int32_type }, \ { "xmitTooBig", &ng_parse_int32_type }, \ { "recvPackets", &ng_parse_int32_type }, \ { "recvOctets", &ng_parse_int32_type }, \ { "recvRunts", &ng_parse_int32_type }, \ { "recvBadGRE", &ng_parse_int32_type }, \ { "recvBadAcks", &ng_parse_int32_type }, \ { "recvBadCID", &ng_parse_int32_type }, \ { "recvOutOfOrder", &ng_parse_int32_type }, \ { "recvDuplicates", &ng_parse_int32_type }, \ { "recvLoneAcks", &ng_parse_int32_type }, \ { "recvAckTimeouts", &ng_parse_int32_type }, \ { NULL } \ } \ } /* Netgraph commands */ enum { NGM_PPTPGRE_SET_CONFIG = 1, /* supply a struct ng_pptpgre_conf */ NGM_PPTPGRE_GET_CONFIG, /* returns a struct ng_pptpgre_conf */ NGM_PPTPGRE_GET_STATS, /* returns struct ng_pptpgre_stats */ NGM_PPTPGRE_CLR_STATS, /* clears stats */ NGM_PPTPGRE_GETCLR_STATS, /* returns & clears stats */ }; #endif /* _NETGRAPH_PPTPGRE_H_ */ Index: stable/3/sys/netgraph/ng_rfc1490.c =================================================================== --- stable/3/sys/netgraph/ng_rfc1490.c (revision 67531) +++ stable/3/sys/netgraph/ng_rfc1490.c (revision 67532) @@ -1,346 +1,346 @@ /* * 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 + * Author: Julian Elischer * * $FreeBSD$ * $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 /* * 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 ng_rfc1490_private { hook_p downlink; hook_p ppp; hook_p inet; }; typedef struct ng_rfc1490_private *priv_p; /* Netgraph node methods */ 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, NULL }; 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: stable/3/sys/netgraph/ng_rfc1490.h =================================================================== --- stable/3/sys/netgraph/ng_rfc1490.h (revision 67531) +++ stable/3/sys/netgraph/ng_rfc1490.h (revision 67532) @@ -1,55 +1,55 @@ /* * ng_rfc1490.h * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * - * Author: Archie Cobbs + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_rfc1490.h,v 1.7 1999/01/20 00:54:15 archie Exp $ */ #ifndef _NETGRAPH_RFC1490_H_ #define _NETGRAPH_RFC1490_H_ /* Node type name */ #define NG_RFC1490_NODE_TYPE "rfc1490" #define NGM_RFC1490_COOKIE 861060632 /* Hook names */ #define NG_RFC1490_HOOK_DOWNSTREAM "downstream" #define NG_RFC1490_HOOK_INET "inet" #define NG_RFC1490_HOOK_PPP "ppp" #endif /* _NETGRAPH_RFC1490_H_ */ Index: stable/3/sys/netgraph/ng_sample.c =================================================================== --- stable/3/sys/netgraph/ng_sample.c (revision 67531) +++ stable/3/sys/netgraph/ng_sample.c (revision 67532) @@ -1,468 +1,468 @@ /* * 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 + * Author: Julian Elischer * * $FreeBSD$ * $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 #include #include /* * This section contains the netgraph method declarations for the * sample node. These methods define the netgraph 'type'. */ 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; /* Parse type for struct ngxxxstat */ static const struct ng_parse_struct_info ng_xxx_stat_type_info = NG_XXX_STATS_TYPE_INFO; static const struct ng_parse_type ng_xxx_stat_type = { &ng_parse_struct_type, &ng_xxx_stat_type_info }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_xxx_cmdlist[] = { { NGM_XXX_COOKIE, NGM_XXX_GET_STATUS, "getstatus", NULL, &ng_xxx_stat_type, }, { NGM_XXX_COOKIE, NGM_XXX_SET_FLAG, "setflag", &ng_parse_int32_type, NULL }, { 0 } }; /* 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, ng_xxx_cmdlist }; 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; 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, strlen(NG_XXX_HOOK_DLCI_LEADIN)) == 0) { char *eptr; cp = name + sizeof(NG_XXX_HOOK_DLCI_LEADIN); if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) return (EINVAL); dlci = (int)strtoul(cp, &eptr, 10); if (*eptr != '\0' || 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; 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: stable/3/sys/netgraph/ng_sample.h =================================================================== --- stable/3/sys/netgraph/ng_sample.h (revision 67531) +++ stable/3/sys/netgraph/ng_sample.h (revision 67532) @@ -1,90 +1,90 @@ /* * ng_sample.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 + * Author: Julian Elischer * * $FreeBSD$ * $Whistle: ng_sample.h,v 1.3 1999/01/20 00:22:14 archie Exp $ */ #ifndef _NETGRAPH_SAMPLE_H_ #define _NETGRAPH_SAMPLE_H_ /* Node type name. This should be unique among all netgraph node types */ #define NG_XXX_NODE_TYPE "sample" /* Node type cookie. Should also be unique. This value MUST change whenever an incompatible change is made to this header file, to insure consistency. The de facto method for generating cookies is to take the output of the date command: date -u +'%s' */ #define NGM_XXX_COOKIE 915491374 /* Number of active DLCI's we can handle */ #define XXX_NUM_DLCIS 16 /* Hook names */ #define NG_XXX_HOOK_DLCI_LEADIN "dlci" #define NG_XXX_HOOK_DOWNSTREAM "downstream" #define NG_XXX_HOOK_DEBUG "debug" /* Netgraph commands understood by this node type */ enum { NGM_XXX_SET_FLAG = 1, NGM_XXX_GET_STATUS, }; /* This structure is returned by the NGM_XXX_GET_STATUS command */ struct ngxxxstat { u_int packets_in; /* packets in from downstream */ u_int packets_out; /* packets out towards downstream */ }; /* * This is used to define the 'parse type' for a struct ngxxxstat, which * is bascially a description of how to convert a binary struct ngxxxstat * to an ASCII string and back. See ng_parse.h for more info. * * This needs to be kept in sync with the above structure definition */ #define NG_XXX_STATS_TYPE_INFO { \ { \ { "packets_in", &ng_parse_int32_type }, \ { "packets_out", &ng_parse_int32_type }, \ { NULL }, \ } \ } #endif /* _NETGRAPH_SAMPLE_H_ */ Index: stable/3/sys/netgraph/ng_socket.c =================================================================== --- stable/3/sys/netgraph/ng_socket.c (revision 67531) +++ stable/3/sys/netgraph/ng_socket.c (revision 67532) @@ -1,1021 +1,1021 @@ /* * 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 + * Author: Julian Elischer * * $FreeBSD$ * $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 #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 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; static ng_disconnect_t ngs_disconnect; /* 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, ngs_disconnect, 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 (suser(p->p_ucred, &p->p_acflag)) return (EPERM); 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; meta_p mp = NULL; int len, error; hook_p hook = NULL; char hookname[NG_HOOKLEN + 1]; if ((pcbp == NULL) || (control != NULL)) { error = EINVAL; goto release; } if (pcbp->sockdata == NULL) { error = ENOTCONN; goto release; } /* * If the user used any of these ways to not specify an address * then handle specially. */ if ((sap == NULL) || ((len = sap->sg_len) <= 2) || (*sap->sg_data == '\0')) { if (pcbp->sockdata->node->numhooks != 1) { error = EDESTADDRREQ; goto release; } /* * if exactly one hook exists, just use it. * Special case to allow write(2) to work on an ng_socket. */ hook = LIST_FIRST(&pcbp->sockdata->node->hooks); } else { if (len > NG_HOOKLEN) { error = EINVAL; goto release; } /* * chop off the sockaddr header, and make sure it's NUL * terminated */ 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; } if (hook == NULL) error = EHOSTUNREACH; } /* Send data (OK if hook is NULL) */ NG_SEND_DATA(error, hook, m, mp); /* makes m NULL */ release: 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 *pcbp; struct sockaddr_ng *sg; int sg_len, namelen, s; /* Why isn't sg_data a `char[1]' ? :-( */ sg_len = sizeof(struct sockaddr_ng) - sizeof(sg->sg_data) + 1; s = splnet(); pcbp = sotongpcb(so); if (pcbp == 0) { splx(s); return (EINVAL); } namelen = 0; /* silence compiler ! */ if (pcbp->sockdata->node->name != NULL) sg_len += namelen = strlen(pcbp->sockdata->node->name); MALLOC(sg, struct sockaddr_ng *, sg_len, M_SONAME, M_WAITOK); bzero(sg, sg_len); if (pcbp->sockdata->node->name != NULL) bcopy(pcbp->sockdata->node->name, sg->sg_data, namelen); splx(s); sg->sg_len = sg_len; sg->sg_family = AF_NETGRAPH; *addr = (struct sockaddr *)sg; 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 = sotongpcb(so); /* 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. * Unless they are for us specifically (socket_type) */ 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); } if (msg->header.typecookie == NGM_SOCKET_COOKIE) { switch (msg->header.cmd) { case NGM_SOCK_CMD_NOLINGER: sockdata->flags |= NGS_FLAG_NOLINGER; break; case NGM_SOCK_CMD_LINGER: sockdata->flags &= ~NGS_FLAG_NOLINGER; break; default: error = EINVAL; /* unknown command */ } /* Free the message and return */ FREE(msg, M_NETGRAPH); return(error); } /* 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); } /* * Hook disconnection * * For this type, removal of the last link destroys the node * if the NOLINGER flag is set. */ static int ngs_disconnect(hook_p hook) { struct ngsock *const sockdata = hook->node->private; if ((sockdata->flags & NGS_FLAG_NOLINGER ) && (hook->node->numhooks == 0)) { ng_rmnode(hook->node); } 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 */ error = 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: stable/3/sys/netgraph/ng_socket.h =================================================================== --- stable/3/sys/netgraph/ng_socket.h (revision 67531) +++ stable/3/sys/netgraph/ng_socket.h (revision 67532) @@ -1,70 +1,70 @@ /* * ng_socket.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 + * Author: Julian Elischer * * $FreeBSD$ * $Whistle: ng_socket.h,v 1.5 1999/01/20 00:22:14 archie Exp $ */ #ifndef _NETGRAPH_NG_SOCKET_H_ #define _NETGRAPH_NG_SOCKET_H_ 1 /* Netgraph node type name and cookie */ #define NG_SOCKET_NODE_TYPE "socket" #define NGM_SOCKET_COOKIE 851601233 /* Netgraph socket(2) constants */ #define NG_DATA 1 #define NG_CONTROL 2 /* Commands */ enum { NGM_SOCK_CMD_NOLINGER = 1, /* close the socket with last hook */ NGM_SOCK_CMD_LINGER /* Keep socket even if 0 hooks */ }; /* Netgraph version of struct sockaddr */ struct sockaddr_ng { u_char sg_len; /* total length */ u_char sg_family; /* address family */ char sg_data[14]; /* actually longer; address value */ }; #endif /* _NETGRAPH_NG_SOCKET_H_ */ Index: stable/3/sys/netgraph/ng_socketvar.h =================================================================== --- stable/3/sys/netgraph/ng_socketvar.h (revision 67531) +++ stable/3/sys/netgraph/ng_socketvar.h (revision 67532) @@ -1,65 +1,65 @@ /* * 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 + * Author: Julian Elischer * * $FreeBSD$ * $Whistle: ng_socketvar.h,v 1.1 1999/01/20 21:35:39 archie Exp $ */ #ifndef _NETGRAPH_NG_SOCKETVAR_H_ #define _NETGRAPH_NG_SOCKETVAR_H_ 1 /* Netgraph protocol control block for each socket */ struct ngpcb { struct socket *ng_socket; /* the socket */ struct ngsock *sockdata; /* netgraph info */ LIST_ENTRY(ngpcb) socks; /* linked list of sockets */ int type; /* NG_CONTROL or NG_DATA */ }; /* Per-node private data */ struct ngsock { struct ng_node *node; /* the associated netgraph node */ struct ngpcb *datasock; /* optional data socket */ struct ngpcb *ctlsock; /* optional control socket */ int flags; int refs; }; #define NGS_FLAG_NOLINGER 1 /* close with last hook */ #endif /* _NETGRAPH_NG_SOCKETVAR_H_ */ Index: stable/3/sys/netgraph/ng_tee.c =================================================================== --- stable/3/sys/netgraph/ng_tee.c (revision 67531) +++ stable/3/sys/netgraph/ng_tee.c (revision 67532) @@ -1,373 +1,373 @@ /* * 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 + * Author: Julian Elischer * * $FreeBSD$ * $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 #include /* Per hook info */ struct hookinfo { hook_p hook; struct ng_tee_hookstat stats; }; /* Per node info */ struct privdata { node_p node; struct hookinfo left; struct hookinfo right; struct hookinfo left2right; struct hookinfo right2left; }; typedef struct privdata *sc_p; /* Netgraph methods */ 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; /* Parse type for struct ng_tee_hookstat */ static const struct ng_parse_struct_info ng_tee_hookstat_type_info = NG_TEE_HOOKSTAT_INFO; static const struct ng_parse_type ng_tee_hookstat_type = { &ng_parse_struct_type, &ng_tee_hookstat_type_info, }; /* Parse type for struct ng_tee_stats */ static const struct ng_parse_struct_info ng_tee_stats_type_info = NG_TEE_STATS_INFO(&ng_tee_hookstat_type); static const struct ng_parse_type ng_tee_stats_type = { &ng_parse_struct_type, &ng_tee_stats_type_info, }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_tee_cmds[] = { { NGM_TEE_COOKIE, NGM_TEE_GET_STATS, "getstats", NULL, &ng_tee_stats_type }, { NGM_TEE_COOKIE, NGM_TEE_CLR_STATS, "clrstats", NULL, NULL }, { 0 } }; /* Netgraph type descriptor */ static struct ng_type ng_tee_typestruct = { NG_VERSION, NG_TEE_NODE_TYPE, NULL, ngt_constructor, ngt_rcvmsg, ngt_rmnode, ngt_newhook, NULL, NULL, ngt_rcvdata, ngt_rcvdata, ngt_disconnect, ng_tee_cmds }; NETGRAPH_INIT(tee, &ng_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(&ng_tee_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; bzero(&sc->right.stats, sizeof(sc->right.stats)); hook->private = &sc->right; } else if (strcmp(name, NG_TEE_HOOK_LEFT) == 0) { sc->left.hook = hook; bzero(&sc->left.stats, sizeof(sc->left.stats)); hook->private = &sc->left; } else if (strcmp(name, NG_TEE_HOOK_RIGHT2LEFT) == 0) { sc->right2left.hook = hook; bzero(&sc->right2left.stats, sizeof(sc->right2left.stats)); hook->private = &sc->right2left; } else if (strcmp(name, NG_TEE_HOOK_LEFT2RIGHT) == 0) { sc->left2right.hook = hook; bzero(&sc->left2right.stats, sizeof(sc->left2right.stats)); hook->private = &sc->left2right; } else return (EINVAL); return (0); } /* * Receive a control message */ static int ngt_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_TEE_COOKIE: switch (msg->header.cmd) { case NGM_TEE_GET_STATS: { struct ng_tee_stats *stats; NG_MKRESPONSE(resp, msg, sizeof(struct ng_tee_stats), M_NOWAIT); if (resp == NULL) { error = ENOMEM; goto done; } stats = (struct ng_tee_stats *) resp->data; bcopy(&sc->right.stats, &stats->right, sizeof(stats->right)); bcopy(&sc->left.stats, &stats->left, sizeof(stats->left)); bcopy(&sc->right2left.stats, &stats->right2left, sizeof(stats->right2left)); bcopy(&sc->left2right.stats, &stats->left2right, sizeof(stats->left2right)); break; } case NGM_TEE_CLR_STATS: bzero(&sc->right.stats, sizeof(sc->right.stats)); bzero(&sc->left.stats, sizeof(sc->left.stats)); bzero(&sc->right2left.stats, sizeof(sc->right2left.stats)); bzero(&sc->left2right.stats, sizeof(sc->left2right.stats)); 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 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 *const hinfo = (struct hookinfo *) hook->private; struct hookinfo *dest; struct hookinfo *dup; int error = 0; /* Which hook? */ if (hinfo == &sc->left) { dup = &sc->left2right; dest = &sc->right; } else if (hinfo == &sc->right) { dup = &sc->right2left; dest = &sc->left; } else if (hinfo == &sc->right2left) { dup = NULL; dest = &sc->left; } else if (hinfo == &sc->left2right) { dup = NULL; dest = &sc->right; } else panic("%s: no hook!", __FUNCTION__); /* Update stats on incoming hook */ hinfo->stats.inOctets += m->m_pkthdr.len; hinfo->stats.inFrames++; /* Duplicate packet and meta info if requried */ if (dup != NULL) { struct mbuf *m2; meta_p meta2; /* Copy packet */ m2 = m_dup(m, M_NOWAIT); if (m2 == NULL) { NG_FREE_DATA(m, meta); return (ENOBUFS); } /* Copy meta info */ if (meta != NULL) { MALLOC(meta2, meta_p, meta->used_len, M_NETGRAPH, M_NOWAIT); if (meta2 == NULL) { m_freem(m2); NG_FREE_DATA(m, meta); return (ENOMEM); } bcopy(meta, meta2, meta->used_len); meta2->allocated_len = meta->used_len; } else meta2 = NULL; /* Deliver duplicate */ dup->stats.outOctets += m->m_pkthdr.len; dup->stats.outFrames++; NG_SEND_DATA(error, dup->hook, m2, meta2); } /* Deliver frame out destination hook */ dest->stats.outOctets += m->m_pkthdr.len; dest->stats.outFrames++; NG_SEND_DATA(error, dest->hook, m, meta); return (0); } /* * 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 *const hinfo = (struct hookinfo *) hook->private; KASSERT(hinfo != NULL, ("%s: null info", __FUNCTION__)); hinfo->hook = NULL; if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } Index: stable/3/sys/netgraph/ng_tee.h =================================================================== --- stable/3/sys/netgraph/ng_tee.h (revision 67531) +++ stable/3/sys/netgraph/ng_tee.h (revision 67532) @@ -1,100 +1,100 @@ /* * ng_tee.h * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * - * Author: Archie Cobbs + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_tee.h,v 1.2 1999/01/20 00:22:14 archie Exp $ */ #ifndef _NETGRAPH_TEE_H_ #define _NETGRAPH_TEE_H_ /* Node type name and magic cookie */ #define NG_TEE_NODE_TYPE "tee" #define NGM_TEE_COOKIE 916107047 /* Hook names */ #define NG_TEE_HOOK_RIGHT "right" #define NG_TEE_HOOK_LEFT "left" #define NG_TEE_HOOK_RIGHT2LEFT "right2left" #define NG_TEE_HOOK_LEFT2RIGHT "left2right" /* Statistics structure for one hook */ struct ng_tee_hookstat { u_int64_t inOctets; u_int64_t inFrames; u_int64_t outOctets; u_int64_t outFrames; }; /* Keep this in sync with the above structure definition */ #define NG_TEE_HOOKSTAT_INFO { \ { \ { "inOctets", &ng_parse_int64_type }, \ { "inFrames", &ng_parse_int64_type }, \ { "outOctets", &ng_parse_int64_type }, \ { "outFrames", &ng_parse_int64_type }, \ { NULL }, \ } \ } /* Statistics structure returned by NGM_TEE_GET_STATS */ struct ng_tee_stats { struct ng_tee_hookstat right; struct ng_tee_hookstat left; struct ng_tee_hookstat right2left; struct ng_tee_hookstat left2right; }; /* Keep this in sync with the above structure definition */ #define NG_TEE_STATS_INFO(hstype) { \ { \ { "right", (hstype) }, \ { "left", (hstype) }, \ { "right2left", (hstype) }, \ { "left2right", (hstype) }, \ { NULL }, \ } \ } /* Netgraph commands */ enum { NGM_TEE_GET_STATS = 1, /* get stats */ NGM_TEE_CLR_STATS, /* clear stats */ }; #endif /* _NETGRAPH_TEE_H_ */ Index: stable/3/sys/netgraph/ng_tty.c =================================================================== --- stable/3/sys/netgraph/ng_tty.c (revision 67531) +++ stable/3/sys/netgraph/ng_tty.c (revision 67532) @@ -1,701 +1,701 @@ /* * 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 + * Author: Archie Cobbs * * $FreeBSD$ * $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 #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 INVARIANTS #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 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, NULL }; 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->p_ucred, &p->p_acflag))) 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; } snprintf(name, sizeof(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 = m->m_pkthdr.len = 0; m->m_pkthdr.rcvif = NULL; 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: stable/3/sys/netgraph/ng_tty.h =================================================================== --- stable/3/sys/netgraph/ng_tty.h (revision 67531) +++ stable/3/sys/netgraph/ng_tty.h (revision 67532) @@ -1,62 +1,62 @@ /* * ng_tty.h * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * - * Author: Archie Cobbs + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_tty.h,v 1.7 1999/01/20 00:22:15 archie Exp $ */ #ifndef _NETGRAPH_TTY_H_ #define _NETGRAPH_TTY_H_ /* Node type name and magic cookie */ #define NG_TTY_NODE_TYPE "tty" #define NGM_TTY_COOKIE 886279262 /* Default hot char */ #define NG_TTY_DFL_HOTCHAR 0x7e /* PPP flag byte */ /* Hook names */ #define NG_TTY_HOOK "hook" /* Netgraph commands */ enum { NGM_TTY_GET_HOTCHAR = 1, NGM_TTY_SET_HOTCHAR, }; #endif /* _NETGRAPH_TTY_H_ */ Index: stable/3/sys/netgraph/ng_vjc.c =================================================================== --- stable/3/sys/netgraph/ng_vjc.c (revision 67531) +++ stable/3/sys/netgraph/ng_vjc.c (revision 67532) @@ -1,468 +1,468 @@ /* * 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 + * Author: Archie Cobbs * * $FreeBSD$ * $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 /* 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 /* Maximum length of a compressed TCP VJ header */ #define MAX_VJHEADER 19 /* Node private data */ struct ng_vjc_private { struct ngm_vjc_config conf; struct slcompress slc; hook_p ip; hook_p vjcomp; hook_p vjuncomp; hook_p vjip; }; typedef struct ng_vjc_private *priv_p; #define ERROUT(x) do { error = (x); goto done; } while (0) /* Netgraph node methods */ 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, int knownTCP); /* 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, NULL }; 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) == 0) hookp = &priv->ip; else if (strcmp(name, NG_VJC_HOOK_VJCOMP) == 0) hookp = &priv->vjcomp; else if (strcmp(name, NG_VJC_HOOK_VJUNCOMP) == 0) hookp = &priv->vjuncomp; else if (strcmp(name, NG_VJC_HOOK_VJIP) == 0) 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->maxChannel > NG_VJC_MAX_CHANNELS - 1 || c->maxChannel < NG_VJC_MIN_CHANNELS - 1) ERROUT(EINVAL); } else c->maxChannel = NG_VJC_MAX_CHANNELS - 1; if (c->enableComp != 0 || c->enableDecomp != 0) { bzero(&priv->slc, sizeof(priv->slc)); sl_compress_init(&priv->slc, c->maxChannel); } 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: sl_uncompress_tcp(NULL, 0, TYPE_ERROR, &priv->slc); 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 = TYPE_IP; /* Compress packet if enabled and proto is TCP */ if (priv->conf.enableComp) { struct ip *ip; if ((m = ng_vjc_pulluphdrs(m, 0)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } ip = mtod(m, struct ip *); if (ip->ip_p == IPPROTO_TCP) { const int origLen = m->m_len; type = sl_compress_tcp(m, ip, &priv->slc, priv->conf.compressCID); m->m_pkthdr.len += m->m_len - origLen; } } /* Dispatch to the appropriate outgoing hook */ 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("%s: type=%d", __FUNCTION__, type); } } else if (hook == priv->vjcomp) { /* incoming compressed packet */ int vjlen, need2pullup; struct mbuf *hm; u_char *hdr; u_int hlen; /* Are we decompressing? */ if (!priv->conf.enableDecomp) { NG_FREE_DATA(m, meta); return (ENXIO); } /* Pull up the necessary amount from the mbuf */ need2pullup = MAX_VJHEADER; if (need2pullup > m->m_pkthdr.len) need2pullup = m->m_pkthdr.len; if (m->m_len < need2pullup && (m = m_pullup(m, need2pullup)) == NULL) { priv->slc.sls_errorin++; NG_FREE_META(meta); return (ENOBUFS); } /* Uncompress packet to reconstruct TCP/IP header */ 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) { NG_FREE_DATA(m, meta); return (EINVAL); } m_adj(m, vjlen); /* Copy the reconstructed TCP/IP headers into a new mbuf */ MGETHDR(hm, M_DONTWAIT, MT_DATA); if (hm == NULL) { priv->slc.sls_errorin++; NG_FREE_DATA(m, meta); return (ENOBUFS); } hm->m_len = 0; hm->m_pkthdr.rcvif = NULL; if (hlen > MHLEN) { /* unlikely, but can happen */ MCLGET(hm, M_DONTWAIT); if ((hm->m_flags & M_EXT) == 0) { m_freem(hm); priv->slc.sls_errorin++; NG_FREE_DATA(m, meta); return (ENOBUFS); } } bcopy(hdr, mtod(hm, u_char *), hlen); hm->m_len = hlen; /* Glue TCP/IP headers and rest of packet together */ hm->m_next = m; hm->m_pkthdr.len = hlen + m->m_pkthdr.len; m = hm; hook = priv->ip; } else if (hook == priv->vjuncomp) { /* incoming uncompressed pkt */ u_char *hdr; u_int hlen; /* Are we decompressing? */ if (!priv->conf.enableDecomp) { NG_FREE_DATA(m, meta); return (ENXIO); } /* Pull up IP+TCP headers */ if ((m = ng_vjc_pulluphdrs(m, 1)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } /* Run packet through uncompressor */ if (sl_uncompress_tcp_core(mtod(m, u_char *), m->m_len, m->m_pkthdr.len, TYPE_UNCOMPRESSED_TCP, &priv->slc, &hdr, &hlen) < 0) { NG_FREE_DATA(m, meta); return (EINVAL); } hook = priv->ip; } else if (hook == priv->vjip) /* incoming regular packet (bypass) */ hook = priv->ip; else panic("%s: unknown hook", __FUNCTION__); /* Send result back out */ NG_SEND_DATA(error, hook, m, 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) { const node_p node = hook->node; const priv_p priv = node->private; /* Zero out hook pointer */ if (hook == priv->ip) priv->ip = NULL; else if (hook == priv->vjcomp) priv->vjcomp = NULL; else if (hook == priv->vjuncomp) priv->vjuncomp = NULL; else if (hook == priv->vjip) priv->vjip = NULL; else panic("%s: unknown hook", __FUNCTION__); /* Go away if no hooks left */ if (node->numhooks == 0) ng_rmnode(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, int knownTCP) { struct ip *ip; struct tcphdr *tcp; int ihlen, thlen; if (m->m_len < sizeof(*ip) && (m = m_pullup(m, sizeof(*ip))) == NULL) return (NULL); ip = mtod(m, struct ip *); if (!knownTCP && 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))) == NULL) 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); } Index: stable/3/sys/netgraph/ng_vjc.h =================================================================== --- stable/3/sys/netgraph/ng_vjc.h (revision 67531) +++ stable/3/sys/netgraph/ng_vjc.h (revision 67532) @@ -1,76 +1,76 @@ /* * ng_vjc.h * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * - * Author: Archie Cobbs + * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_vjc.h,v 1.6 1999/01/25 02:40:22 archie Exp $ */ #ifndef _NETGRAPH_VJC_H_ #define _NETGRAPH_VJC_H_ /* Node type name and magic cookie */ #define NG_VJC_NODE_TYPE "vjc" #define NGM_VJC_COOKIE 868219209 /* Hook names */ #define NG_VJC_HOOK_IP "ip" /* normal IP traffic */ #define NG_VJC_HOOK_VJCOMP "vjcomp" /* compressed TCP */ #define NG_VJC_HOOK_VJUNCOMP "vjuncomp" /* uncompressed TCP */ #define NG_VJC_HOOK_VJIP "vjip" /* uncompressed IP */ /* Minimum and maximum number of compression channels */ #define NG_VJC_MIN_CHANNELS 4 #define NG_VJC_MAX_CHANNELS 16 /* Configure struct */ struct ngm_vjc_config { u_char enableComp; /* Enable compression */ u_char enableDecomp; /* Enable decompression */ u_char maxChannel; /* Number of compression channels - 1 */ u_char compressCID; /* OK to compress outgoing CID's */ }; /* Netgraph commands */ enum { NGM_VJC_SET_CONFIG, /* Supply a struct ngm_vjc_config */ NGM_VJC_GET_STATE, /* Returns current struct slcompress */ NGM_VJC_CLR_STATS, /* Clears statistics counters */ NGM_VJC_RECV_ERROR, /* Indicate loss of incoming frame */ }; #endif /* _NETGRAPH_VJC_H_ */