diff options
author | julian <julian@FreeBSD.org> | 1999-10-21 09:06:11 +0000 |
---|---|---|
committer | julian <julian@FreeBSD.org> | 1999-10-21 09:06:11 +0000 |
commit | c5c63975d538cf48ceb99ba48c341293676d15c0 (patch) | |
tree | 722c03ee4d750dd89ed43b028c35302fbfd03bfd /sys/netgraph | |
parent | 028ec91c46f181b4be2318c3bba8d194b5583f87 (diff) | |
download | FreeBSD-src-c5c63975d538cf48ceb99ba48c341293676d15c0.zip FreeBSD-src-c5c63975d538cf48ceb99ba48c341293676d15c0.tar.gz |
Whistle's Netgraph link-layer (sometimes more) networking infrastructure.
Been in production for 3 years now. Gives Instant Frame relay to if_sr
and if_ar drivers, and PPPOE support soon. See:
ftp://ftp.whistle.com/pub/archie/netgraph/index.html
for on-line manual pages.
Reviewed by: Doug Rabson (dfr@freebsd.org)
Obtained from: Whistle CVS tree
Diffstat (limited to 'sys/netgraph')
38 files changed, 12430 insertions, 0 deletions
diff --git a/sys/netgraph/NOTES b/sys/netgraph/NOTES new file mode 100644 index 0000000..69d8b71 --- /dev/null +++ b/sys/netgraph/NOTES @@ -0,0 +1,81 @@ +$FreeBSD$ +Development ideas.. + +Archie's suggestions... :-) + + - There should be a new malloc type: M_NETGRAPH + [DONE] + - all mallocs/frees now changed to use this.. JRE + - might further split them out some time. + + - Use MALLOC and FREE macros instead of direct function calls + [DONE] + - They allow conditional compilation which keeps + statistics & counters on various memory allocation + (or so it seems) + + - In struct ng_mesg: at least make "header" into "hdr", if not + getting rid of it altogether. It doesn't seem necessary and + makes all my C code lines too long. + + - I understand.. one thought however.. consider.. + if char data[0] were not legal, so that data[1] needed to be + used instead, then the only way to get the size of the header + would be sizeof(msg.header) as sizeof(msg) would include the dummy + following bytes. this is a portability issue and I hope + it will be ported eventually :) + + - Baloney! you can use sizeof(msg) - 1 then.. or just + make it a macro, then its always portable: + + #ifdef __GNU_C__ + #define NG_MSG_HDR_SIZE (sizeof(struct ng_message)) + #else + #define NG_MSG_HDR_SIZE (sizeof(struct ng_message) - 1) + #endif + + - Have a user level program to print out and manipulate nodes, etc. + - [DONE] + see ngctl + + - "Netgraph global" flags to turn on tracing, etc. + + - ngctl needs to be rewritten using libnetgraph. Also it needs a + command to list all existing nodes (in case you don't know the + name of what you're looking for). + [DONE] + + - Need a way to get a list of ALL nodes. + [DONE] + - see NGM_LISTNODES + + - Enhance "netstat" to display all netgraph nodes -- or at least + all netgraph socket nodes. + [DONE] + + - BUG FIX: bind() on a socket should neither require nor allow a + colon character at the end of the name. Note ngctl allows you + to do it either way! + [DONE] (I think) + + - Need to implement passing meta information through socket nodes + using sendmsg() and recvmsg(). + + - Stuff needing to be added to manual: + + - Awareness of SPL level, use ng_queue*() functions when necessary. + - Malloc all memory with type M_NETGRAPH. + - Write code so it can be an LKM or built into the kernel.. this means + be careful with things like #ifdef INET. + - All nodes assume that all data mbufs have the M_PKTHDR flag set! + The ng_send_data() and related functions should have an + #ifdef DIAGNOSTICS check to check this assumption for every mbuf. + - More generally, netgraph code should make liberal use of the + #ifdef DIAGNOSTICS definition. + - Since data and messages are sent functionally, programmers need + to watch out for infinite feedback loops. Should ng_base.c detect + this automatically? + - I've been thinking about this. each node could have a 'colour' + which is set to the colour of the packet as you pass through. + hitting a node already of your colour would abort. Each packet + has another (incremented) colour. diff --git a/sys/netgraph/netgraph.h b/sys/netgraph/netgraph.h new file mode 100644 index 0000000..e5e04e6 --- /dev/null +++ b/sys/netgraph/netgraph.h @@ -0,0 +1,255 @@ + +/* + * 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 <julian@whistle.com> + * + * $FreeBSD$ + * $Whistle: netgraph.h,v 1.24 1999/01/28 23:54:52 julian Exp $ + */ + +#ifndef _NETGRAPH_NETGRAPH_H_ +#define _NETGRAPH_NETGRAPH_H_ 1 + +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/module.h> + +#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 */ + LIST_HEAD(hooks, ng_hook) hooks; /* linked list of node hooks */ + LIST_ENTRY(ng_node) nodes; /* linked list of all nodes */ +}; +typedef struct ng_node *node_p; + +/* Flags for a node */ +#define NG_INVALID 0x001 /* free when all sleepers and refs go to 0 */ +#define NG_BUSY 0x002 /* callers should sleep or wait */ +#define NG_TOUCHED 0x004 /* to avoid cycles when 'flooding' */ +#define NGF_TYPE1 0x10000000 /* reserved for type specific storage */ +#define NGF_TYPE2 0x20000000 /* reserved for type specific storage */ +#define NGF_TYPE3 0x40000000 /* reserved for type specific storage */ +#define NGF_TYPE4 0x80000000 /* reserved for type specific storage */ + +/* + * The structure that holds meta_data about a data packet (e.g. priority) + * Nodes might add or subtract options as needed if there is room. + * They might reallocate the struct to make more room if they need to. + * Meta-data is still experimental. + */ +struct meta_field_header { + u_long cookie; /* cookie for the field. Skip fields you don't + * know about (same cookie as in messgaes) */ + u_short type; /* field ID */ + u_short len; /* total len of this field including extra + * data */ + char data[0]; /* data starts here */ +}; + +/* To zero out an option 'in place' set it's cookie to this */ +#define INVALID_COOKIE 865455152 + +/* This part of the metadata is always present if the pointer is non NULL */ +struct ng_meta { + char priority; /* -ve is less priority, 0 is default */ + char discardability; /* higher is less valuable.. discard first */ + u_short allocated_len; /* amount malloc'd */ + u_short used_len; /* sum of all fields, options etc. */ + u_short flags; /* see below.. generic flags */ + struct meta_field_header options[0]; /* add as (if) needed */ +}; +typedef struct ng_meta *meta_p; + +/* Flags for meta-data */ +#define NGMF_TEST 0x01 /* discard at the last moment before sending */ +#define NGMF_TRACE 0x02 /* trace when handing this data to a node */ + +/* + * Structure of a node type + */ +struct ng_type { + + /* Netgraph version number (must equal NG_VERSION) */ + u_int32_t version; + + /* Unique type name */ + const char *name; + + /* Module event handler (optional) */ + modeventhand_t mod_event; + + /* Node constructor */ + int (*constructor)(node_p *node); + + /* Calls using the node */ + int (*rcvmsg)(node_p node, struct ng_mesg *msg, + const char *retaddr, struct ng_mesg **resp); + int (*shutdown)(node_p node); + int (*newhook)(node_p node, hook_p hook, const char *name); + hook_p (*findhook)(node_p node, const char *name); + + /* Calls using the hook */ + int (*connect)(hook_p hook); /* already linked in */ + int (*rcvdata)(hook_p hook, struct mbuf *m, meta_p meta); + int (*rcvdataq)(hook_p hook, struct mbuf *m, meta_p meta); + int (*disconnect)(hook_p hook); /* notify on disconnect */ + + /* These are private to the base netgraph code */ + 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); + +void ng_cutlinks(node_p node); +int ng_con_nodes(node_p node, + const char *name, node_p node2, const char *name2); +void ng_destroy_hook(hook_p hook); +node_p ng_findname(node_p node, const char *name); +struct ng_type *ng_findtype(const char *type); +int ng_make_node(const char *type, node_p *nodepp); +int ng_make_node_common(struct ng_type *typep, node_p *nodep); +int ng_mkpeer(node_p node, const char *name, const char *name2, char *type); +int ng_mod_event(module_t mod, int what, void *arg); +int ng_name_node(node_p node, const char *name); +int ng_newtype(struct ng_type *tp); +int ng_path2node(node_p here, const char *path, node_p *dest, char **rtnp); +int ng_path_parse(char *addr, char **node, char **path, char **hook); +int ng_queue_data(hook_p hook, struct mbuf *m, meta_p meta); +int ng_queue_msg(node_p here, struct ng_mesg *msg, int len, + const char *address); +void ng_release_node(node_p node); +void ng_rmnode(node_p node); +int ng_send_data(hook_p hook, struct mbuf *m, meta_p meta); +int ng_send_dataq(hook_p hook, struct mbuf *m, meta_p meta); +int ng_send_msg(node_p here, struct ng_mesg *msg, + const char *address, struct ng_mesg **resp); +void ng_unname(node_p node); +void ng_unref(node_p node); +int ng_bypass(hook_p hook1, hook_p hook2); +int ng_wait_node(node_p node, char *msg); + +#endif /* _NETGRAPH_NETGRAPH_H_ */ + diff --git a/sys/netgraph/ng_UI.c b/sys/netgraph/ng_UI.c new file mode 100644 index 0000000..7295b4a --- /dev/null +++ b/sys/netgraph/ng_UI.c @@ -0,0 +1,242 @@ + +/* + * ng_UI.c + * + * Copyright (c) 1996-1999 Whistle Communications, Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Whistle Communications; + * provided, however, that: + * 1. Any and all reproductions of the source or object code must include the + * copyright notice above and the following disclaimer of warranties; and + * 2. No rights are granted, in any manner or form, to use Whistle + * Communications, Inc. trademarks, including the mark "WHISTLE + * COMMUNICATIONS" on advertising, endorsements, or otherwise except as + * such appears in the above copyright notice or in the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY + * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS + * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. + * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Julian Elischer <julian@whistle.com> + * + * $FreeBSD$ + * $Whistle: ng_UI.c,v 1.11 1999/01/28 23:54:52 julian Exp $ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/conf.h> +#include <sys/errno.h> +#include <sys/socket.h> +#include <sys/syslog.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> +#include <netgraph/ng_UI.h> + +/* + * DEFINITIONS + */ + +/* Everything, starting with sdlc on has defined UI as 0x03 */ +#define HDLC_UI 0x03 + +/* Node private data */ +struct private { + hook_p downlink; + hook_p uplink; +}; +typedef struct private *priv_p; + +/* Netgraph node methods */ +static int ng_UI_constructor(node_p *nodep); +static int ng_UI_rcvmsg(node_p node, struct ng_mesg *msg, + const char *retaddr, struct ng_mesg **resp); +static int ng_UI_rmnode(node_p node); +static int ng_UI_newhook(node_p node, hook_p hook, const char *name); +static int ng_UI_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); +static int ng_UI_disconnect(hook_p hook); + +/* Node type descriptor */ +static struct ng_type typestruct = { + NG_VERSION, + NG_UI_NODE_TYPE, + NULL, + ng_UI_constructor, + ng_UI_rcvmsg, + ng_UI_rmnode, + ng_UI_newhook, + NULL, + NULL, + ng_UI_rcvdata, + ng_UI_rcvdata, + ng_UI_disconnect +}; +NETGRAPH_INIT(UI, &typestruct); + +/************************************************************************ + NETGRAPH NODE STUFF + ************************************************************************/ + +/* + * Create a newborn node. We start with an implicit reference. + */ + +static int +ng_UI_constructor(node_p *nodep) +{ + priv_p priv; + int error; + + /* Allocate private structure */ + MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK); + if (priv == NULL) + return (ENOMEM); + bzero(priv, sizeof(*priv)); + + /* Call generic node constructor */ + if ((error = ng_make_node_common(&typestruct, nodep))) { + FREE(priv, M_NETGRAPH); + return (error); + } + (*nodep)->private = priv; + + /* Done */ + return (0); +} + +/* + * Give our ok for a hook to be added + */ +static int +ng_UI_newhook(node_p node, hook_p hook, const char *name) +{ + const priv_p priv = node->private; + + if (!strcmp(name, NG_UI_HOOK_DOWNSTREAM)) { + if (priv->downlink) + return (EISCONN); + priv->downlink = hook; + } else if (!strcmp(name, NG_UI_HOOK_UPSTREAM)) { + if (priv->uplink) + return (EISCONN); + priv->uplink = hook; + } else + return (EINVAL); + return (0); +} + +/* + * Receive a control message + */ +static int +ng_UI_rcvmsg(node_p node, struct ng_mesg *msg, + const char *raddr, struct ng_mesg **rp) +{ + FREE(msg, M_NETGRAPH); + return (EINVAL); +} + +#define MAX_ENCAPS_HDR 1 +#define ERROUT(x) do { error = (x); goto done; } while (0) + +/* + * Receive a data frame + */ +static int +ng_UI_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) +{ + const node_p node = hook->node; + const priv_p priv = node->private; + int error = 0; + + if (hook == priv->downlink) { + u_char *start, *ptr; + + if (!m || !(m = m_pullup(m, MAX_ENCAPS_HDR))) + ERROUT(ENOBUFS); + ptr = start = mtod(m, u_char *); + + /* Must be UI frame */ + if (*ptr++ != HDLC_UI) + ERROUT(0); + + m_adj(m, ptr - start); + NG_SEND_DATA(error, priv->uplink, m, meta); /* m -> NULL */ + } else if (hook == priv->uplink) { + M_PREPEND(m, 1, M_DONTWAIT); /* Prepend IP NLPID */ + if (!m) + ERROUT(ENOBUFS); + mtod(m, u_char *)[0] = HDLC_UI; + NG_SEND_DATA(error, priv->downlink, m, meta); /* m -> NULL */ + } else + panic(__FUNCTION__); + +done: + NG_FREE_DATA(m, meta); /* does nothing if m == NULL */ + return (error); +} + +/* + * Shutdown node + */ +static int +ng_UI_rmnode(node_p node) +{ + const priv_p priv = node->private; + + /* Take down netgraph node */ + node->flags |= NG_INVALID; + ng_cutlinks(node); + ng_unname(node); + bzero(priv, sizeof(*priv)); + FREE(priv, M_NETGRAPH); + node->private = NULL; + ng_unref(node); + return (0); +} + +/* + * Hook disconnection + */ +static int +ng_UI_disconnect(hook_p hook) +{ + const priv_p priv = hook->node->private; + + if (hook->node->numhooks == 0) + ng_rmnode(hook->node); + else if (hook == priv->downlink) + priv->downlink = NULL; + else if (hook == priv->uplink) + priv->uplink = NULL; + else + panic(__FUNCTION__); + return (0); +} + diff --git a/sys/netgraph/ng_UI.h b/sys/netgraph/ng_UI.h new file mode 100644 index 0000000..f41e5a9 --- /dev/null +++ b/sys/netgraph/ng_UI.h @@ -0,0 +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 <julian@whistle.com> + * + * $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_NODE_COOKIE 884639499 + +/* Hook names */ +#define NG_UI_HOOK_DOWNSTREAM "downstream" +#define NG_UI_HOOK_UPSTREAM "upstream" + +#endif /* _NETGRAPH_UI_H_ */ + diff --git a/sys/netgraph/ng_async.c b/sys/netgraph/ng_async.c new file mode 100644 index 0000000..5dae651 --- /dev/null +++ b/sys/netgraph/ng_async.c @@ -0,0 +1,586 @@ + +/* + * 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 <archie@whistle.com> + * + * $FreeBSD$ + * $Whistle: ng_async.c,v 1.15 1999/01/28 23:54:52 julian Exp $ + */ + +/* + * This node type implements a PPP style sync <-> async converter. + * See RFC 1661 for details of how asynchronous encoding works. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/proc.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <sys/tty.h> +#include <sys/syslog.h> +#include <sys/errno.h> + +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> +#include <netgraph/ng_async.h> + +#include <net/ppp_defs.h> + +/* Optimize opening and closing flags into one? Set to max # seconds delay */ +#define SYNC_OPT_TIME 1 /* one second maximum */ + +/* Async decode state */ +#define MODE_HUNT 0 +#define MODE_NORMAL 1 +#define MODE_ESC 2 + +/* Private data structure */ +struct private { + node_p node; /* Our node */ + hook_p async; /* Asynchronous side */ + hook_p sync; /* Synchronous side */ + hook_p sync2; /* Synchronous side, full escapes */ + u_char amode; /* Async hunt/esape mode */ + u_int16_t fcs; /* Decoded async FCS (so far) */ + u_char *abuf; /* Buffer to encode sync into */ + u_char *sbuf; /* Buffer to decode async into */ + u_int slen; /* Length of data in sbuf */ +#if SYNC_OPT_TIME + long lasttime; /* Time of last async packet sent */ +#endif + struct ng_async_cfg cfg; /* Configuration */ + struct ng_async_stat stats; /* Statistics */ +}; +typedef struct private *sc_p; + +/* Useful macros */ +#define ASYNC_BUF_SIZE(smru) (2 * (smru) + 10) +#define SYNC_BUF_SIZE(amru) ((amru) + 10) +#define ERROUT(x) do { error = (x); goto done; } while (0) + +/* Netgraph methods */ +static int nga_constructor(node_p *node); +static int nga_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); +static int nga_rcvmsg(node_p node, struct ng_mesg *msg, + const char *rtn, struct ng_mesg **resp); +static int nga_shutdown(node_p node); +static int nga_newhook(node_p node, hook_p hook, const char *name); +static int nga_disconnect(hook_p hook); + +/* Helper stuff */ +static int nga_rcv_sync(const sc_p sc, struct mbuf *m, meta_p meta); +static int nga_rcv_async(const sc_p sc, struct mbuf *m, meta_p meta); + +/* Define the netgraph node type */ +static struct ng_type typestruct = { + NG_VERSION, + NG_ASYNC_NODE_TYPE, + NULL, + nga_constructor, + nga_rcvmsg, + nga_shutdown, + nga_newhook, + NULL, + NULL, + nga_rcvdata, + nga_rcvdata, + nga_disconnect +}; +NETGRAPH_INIT(async, &typestruct); + +/* CRC table */ +static const u_int16_t fcstab[]; + +/****************************************************************** + NETGRAPH NODE METHODS +******************************************************************/ + +/* + * Initialize a new node + */ +static int +nga_constructor(node_p *nodep) +{ + sc_p sc; + int error; + + if ((error = ng_make_node_common(&typestruct, nodep))) + return (error); + MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_WAITOK); + if (sc == NULL) + return (ENOMEM); + bzero(sc, sizeof(*sc)); + sc->amode = MODE_HUNT; + sc->cfg.accm = ~0; + sc->cfg.amru = NG_ASYNC_DEFAULT_MRU; + sc->cfg.smru = NG_ASYNC_DEFAULT_MRU; + MALLOC(sc->abuf, u_char *, + ASYNC_BUF_SIZE(sc->cfg.smru), M_NETGRAPH, M_WAITOK); + if (sc->abuf == NULL) + goto fail; + MALLOC(sc->sbuf, u_char *, + SYNC_BUF_SIZE(sc->cfg.amru), M_NETGRAPH, M_WAITOK); + if (sc->sbuf == NULL) { + FREE(sc->abuf, M_NETGRAPH); +fail: + FREE(sc, M_NETGRAPH); + return (ENOMEM); + } + (*nodep)->private = sc; + sc->node = *nodep; + return (0); +} + +/* + * Reserve a hook for a pending connection + */ +static int +nga_newhook(node_p node, hook_p hook, const char *name) +{ + const sc_p sc = node->private; + hook_p *hookp; + + if (!strcmp(name, NG_ASYNC_HOOK_ASYNC)) + hookp = &sc->async; + else if (!strcmp(name, NG_ASYNC_HOOK_SYNC)) + hookp = &sc->sync; + else if (!strcmp(name, NG_ASYNC_HOOK_SYNC2)) + hookp = &sc->sync2; + else + return (EINVAL); + if (*hookp) + return (EISCONN); + *hookp = hook; + return (0); +} + +/* + * Receive incoming data + */ +static int +nga_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) +{ + const sc_p sc = hook->node->private; + + if (hook == sc->sync) + return (nga_rcv_sync(sc, m, meta)); + else if (hook == sc->sync2) { + const u_char acfcompSave = sc->cfg.acfcomp; + const u_int32_t accmSave = sc->cfg.accm; + int rtn; + + sc->cfg.acfcomp = 0; + sc->cfg.accm = ~0; + rtn = nga_rcv_sync(sc, m, meta); + sc->cfg.acfcomp = acfcompSave; + sc->cfg.accm = accmSave; + return (rtn); + } else if (hook == sc->async) + return (nga_rcv_async(sc, m, meta)); + panic(__FUNCTION__); +} + +/* + * Receive incoming control message + */ +static int +nga_rcvmsg(node_p node, struct ng_mesg *msg, + const char *rtn, struct ng_mesg **rptr) +{ + const sc_p sc = (sc_p) node->private; + struct ng_mesg *resp = NULL; + int error = 0; + + switch (msg->header.typecookie) { + case NGM_ASYNC_COOKIE: + switch (msg->header.cmd) { + case NGM_ASYNC_CMD_GET_STATS: + NG_MKRESPONSE(resp, msg, sizeof(sc->stats), M_NOWAIT); + if (resp == NULL) + ERROUT(ENOMEM); + *((struct ng_async_stat *) resp->data) = sc->stats; + break; + case NGM_ASYNC_CMD_CLR_STATS: + bzero(&sc->stats, sizeof(sc->stats)); + break; + case NGM_ASYNC_CMD_SET_CONFIG: + { + struct ng_async_cfg *const cfg = + (struct ng_async_cfg *) msg->data; + u_char *buf; + + if (msg->header.arglen != sizeof(*cfg)) + ERROUT(EINVAL); + if (cfg->amru < NG_ASYNC_MIN_MRU + || cfg->amru > NG_ASYNC_MAX_MRU + || cfg->smru < NG_ASYNC_MIN_MRU + || cfg->smru > NG_ASYNC_MAX_MRU) + ERROUT(EINVAL); + cfg->enabled = !!cfg->enabled; /* normalize */ + cfg->acfcomp = !!cfg->acfcomp; /* normalize */ + if (cfg->smru > sc->cfg.smru) { /* reallocate buffer */ + MALLOC(buf, u_char *, ASYNC_BUF_SIZE(cfg->smru), + M_NETGRAPH, M_NOWAIT); + if (!buf) + ERROUT(ENOMEM); + FREE(sc->abuf, M_NETGRAPH); + sc->abuf = buf; + } + if (cfg->amru > sc->cfg.amru) { /* reallocate buffer */ + MALLOC(buf, u_char *, SYNC_BUF_SIZE(cfg->amru), + M_NETGRAPH, M_NOWAIT); + if (!buf) + ERROUT(ENOMEM); + FREE(sc->sbuf, M_NETGRAPH); + sc->sbuf = buf; + sc->amode = MODE_HUNT; + sc->slen = 0; + } + if (!cfg->enabled) { + sc->amode = MODE_HUNT; + sc->slen = 0; + } + sc->cfg = *cfg; + break; + } + case NGM_ASYNC_CMD_GET_CONFIG: + NG_MKRESPONSE(resp, msg, sizeof(sc->cfg), M_NOWAIT); + if (!resp) + ERROUT(ENOMEM); + *((struct ng_async_cfg *) resp->data) = sc->cfg; + break; + default: + ERROUT(EINVAL); + } + break; + default: + ERROUT(EINVAL); + } + if (rptr) + *rptr = resp; + else if (resp) + FREE(resp, M_NETGRAPH); + +done: + FREE(msg, M_NETGRAPH); + return (error); +} + +/* + * Shutdown this node + */ +static int +nga_shutdown(node_p node) +{ + const sc_p sc = node->private; + + ng_cutlinks(node); + ng_unname(node); + FREE(sc->abuf, M_NETGRAPH); + FREE(sc->sbuf, M_NETGRAPH); + bzero(sc, sizeof(*sc)); + FREE(sc, M_NETGRAPH); + node->private = NULL; + ng_unref(node); + return (0); +} + +/* + * Lose a hook. When both hooks go away, we disappear. + */ +static int +nga_disconnect(hook_p hook) +{ + const sc_p sc = hook->node->private; + hook_p *hookp; + + if (hook == sc->async) + hookp = &sc->async; + else if (hook == sc->sync) + hookp = &sc->sync; + else if (hook == sc->sync2) + hookp = &sc->sync2; + else + panic(__FUNCTION__); + if (!*hookp) + panic(__FUNCTION__ "2"); + *hookp = NULL; + bzero(&sc->stats, sizeof(sc->stats)); +#if SYNC_OPT_TIME + sc->lasttime = 0; +#endif + if (hook->node->numhooks == 0) + ng_rmnode(hook->node); + return (0); +} + +/****************************************************************** + INTERNAL HELPER STUFF +******************************************************************/ + +/* + * Encode a byte into the async buffer + */ +static __inline__ void +nga_async_add(const sc_p sc, u_int16_t *fcs, u_int32_t accm, int *len, u_char x) +{ + *fcs = PPP_FCS(*fcs, x); + if ((x < 32 && ((1 << x) & accm)) + || (x == PPP_ESCAPE) + || (x == PPP_FLAG)) { + sc->abuf[(*len)++] = PPP_ESCAPE; + x ^= PPP_TRANS; + } + sc->abuf[(*len)++] = x; +} + +/* + * Receive incoming synchronous data. Any "meta" information means + * for us to apply full ACCM to this frame. + */ +static int +nga_rcv_sync(const sc_p sc, struct mbuf *m, meta_p meta) +{ + struct ifnet *const rcvif = m->m_pkthdr.rcvif; + u_int16_t fcs, fcs0; + int alen, error = 0; + +#define ADD_BYTE(x) \ + nga_async_add(sc, &fcs, meta ? ~0 : sc->cfg.accm, &alen, (x)) + + if (!sc->cfg.enabled) { + NG_SEND_DATA(error, sc->async, m, meta); + return (error); + } + if (m->m_pkthdr.len > sc->cfg.smru) { + sc->stats.syncOverflows++; + NG_FREE_DATA(m, meta); + return (EMSGSIZE); + } + sc->stats.syncFrames++; + sc->stats.syncOctets += m->m_pkthdr.len; + + /* Initialize async encoded version of input mbuf */ + alen = 0; + fcs = PPP_INITFCS; + + /* Add beginning sync flag if it's been long enough to need one */ +#if SYNC_OPT_TIME + { + struct timeval time; + + getmicrotime(&time); + if (time.tv_sec >= sc->lasttime + SYNC_OPT_TIME) { + sc->abuf[alen++] = PPP_FLAG; + sc->lasttime = time.tv_sec; + } + } +#else + sc->abuf[alen++] = PPP_FLAG; +#endif + + /* Add option address and control fields, then packet payload */ + if (!sc->cfg.acfcomp || meta) { + ADD_BYTE(PPP_ALLSTATIONS); + ADD_BYTE(PPP_UI); + } + while (m) { + struct mbuf *n; + + while (m->m_len > 0) { + u_char const ch = *mtod(m, u_char *); + + ADD_BYTE(ch); + m->m_data++; + m->m_len--; + } + MFREE(m, n); + m = n; + } + + /* Add checksum and final sync flag */ + fcs0 = fcs; + ADD_BYTE(~fcs0 & 0xff); + ADD_BYTE(~fcs0 >> 8); + sc->abuf[alen++] = PPP_FLAG; + + /* Put frame in an mbuf and ship it off */ + NG_FREE_META(meta); + if (!(m = m_devget(sc->abuf, alen, 0, rcvif, NULL))) + error = ENOBUFS; + else + NG_SEND_DATA(error, sc->async, m, meta); + return (error); +} + +/* + * Receive incoming asynchronous data + * XXX technically, we should strip out supposedly escaped characters + */ +static int +nga_rcv_async(const sc_p sc, struct mbuf * m, meta_p meta) +{ + struct ifnet *const rcvif = m->m_pkthdr.rcvif; + int error; + + if (!sc->cfg.enabled) { + NG_SEND_DATA(error, sc->sync, m, meta); + return (error); + } + NG_FREE_META(meta); + while (m) { + struct mbuf *n; + + for (; m->m_len > 0; m->m_data++, m->m_len--) { + u_char ch = *mtod(m, u_char *); + + sc->stats.asyncOctets++; + if (ch == PPP_FLAG) { /* Flag overrides everything */ + int skip = 0; + + /* Check for runts */ + if (sc->slen < 2) { + if (sc->slen > 0) + sc->stats.asyncRunts++; + goto reset; + } + + /* Verify CRC */ + if (sc->fcs != PPP_GOODFCS) { + sc->stats.asyncBadCheckSums++; + goto reset; + } + sc->slen -= 2; + + /* Strip address and control fields */ + if (sc->slen >= 2 + && sc->sbuf[0] == PPP_ALLSTATIONS + && sc->sbuf[1] == PPP_UI) + skip = 2; + + /* Check for frame too big */ + if (sc->slen - skip > sc->cfg.amru) { + sc->stats.asyncOverflows++; + goto reset; + } + + /* OK, ship it out */ + if ((n = m_devget(sc->sbuf + skip, + sc->slen - skip, 0, rcvif, NULL))) + NG_SEND_DATA(error, sc->sync, n, meta); + sc->stats.asyncFrames++; +reset: + sc->amode = MODE_NORMAL; + sc->fcs = PPP_INITFCS; + sc->slen = 0; + continue; + } + switch (sc->amode) { + case MODE_NORMAL: + if (ch == PPP_ESCAPE) { + sc->amode = MODE_ESC; + continue; + } + break; + case MODE_ESC: + ch ^= PPP_TRANS; + sc->amode = MODE_NORMAL; + break; + case MODE_HUNT: + default: + continue; + } + + /* Add byte to frame */ + if (sc->slen >= SYNC_BUF_SIZE(sc->cfg.amru)) { + sc->stats.asyncOverflows++; + sc->amode = MODE_HUNT; + sc->slen = 0; + } else { + sc->sbuf[sc->slen++] = ch; + sc->fcs = PPP_FCS(sc->fcs, ch); + } + } + MFREE(m, n); + m = n; + } + return (0); +} + +/* + * CRC table + * + * Taken from RFC 1171 Appendix B + */ +static const u_int16_t fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; diff --git a/sys/netgraph/ng_async.h b/sys/netgraph/ng_async.h new file mode 100644 index 0000000..f08290f --- /dev/null +++ b/sys/netgraph/ng_async.h @@ -0,0 +1,89 @@ + +/* + * 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 <archie@whistle.com> + * + * $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 886473715 + +/* Hook names */ +#define NG_ASYNC_HOOK_SYNC "sync" /* Normal encoding */ +#define NG_ASYNC_HOOK_SYNC2 "sync2" /* Full ACCM, no ACF comp. */ +#define NG_ASYNC_HOOK_ASYNC "async" /* Normal decoding */ + +/* 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; +}; + +/* Configuration for this node */ +struct ng_async_cfg { + u_char enabled; /* Turn encoding on/off */ + u_char acfcomp; /* Address/control field compression */ + u_int16_t amru; /* Max receive async frame length */ + u_int16_t smru; /* Max receive sync frame length */ + u_int32_t accm; /* ACCM encoding */ +}; + +/* 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_ */ diff --git a/sys/netgraph/ng_base.c b/sys/netgraph/ng_base.c new file mode 100644 index 0000000..4234355 --- /dev/null +++ b/sys/netgraph/ng_base.c @@ -0,0 +1,1633 @@ + +/* + * 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 <julian@whistle.com> + * Archie Cobbs <archie@whistle.com> + * + * $FreeBSD$ + * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $ + */ + +/* + * This file implements the base netgraph code. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/syslog.h> +#include <sys/linker.h> +#include <sys/queue.h> +#include <sys/mbuf.h> +#include <sys/socketvar.h> + +#include <net/netisr.h> + +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> + +/* List of all nodes */ +static LIST_HEAD(, ng_node) nodelist; + +/* List of installed types */ +static LIST_HEAD(, ng_type) typelist; + +/* 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 node_p 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 + +/************************************************************************ + 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); + + /* 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); + 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 { +#ifdef DIAGNOSTIC + if (node->refs == 1) { + panic(__FUNCTION__); + error = ENXIO; + } +#endif + 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 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) != NULL) { + 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, temp; + + /* "." means "this node" */ + if (strcmp(name, ".") == 0) + return(this); + + /* Check for name-by-ID */ + if ((temp = ng_decodeidname(name)) != NULL) { + + /* Make sure the ID really points to a node */ + LIST_FOREACH(node, &nodelist, nodes) { + if (node == temp) + break; + } + return (node); + } + + /* 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 NULL if the + * string is not valid, otherwise returns the ID cast to a + * node pointer. + * + * NOTE: the returned pointer is not necessarily valid! + */ +static node_p +ng_decodeidname(const char *name) +{ + u_int32_t val; + int k, len; + + /* Basic checks */ + for (len = 0; name[len] != '\0'; len++) { + const char ch = name[len]; + + if (len == 0) { + if (ch != '[') + return (NULL); + } else if (name[len + 1] == '\0') { + if (ch != ']') + return (NULL); + } else if (!((ch >= '0' && ch <= '9') + || (ch >= 'a' && ch <= 'f') + || (ch >= 'A' && ch <= 'F'))) + return (NULL); + } + if (len < 3 || len > (sizeof(val) * 2) + 2 || name[1] == '0') + return (NULL); + + /* Convert number to binary */ + for (val = 0, k = 1; k < len - 1; k++) { + const char ch = name[k]; + + if (ch <= '9') + val = (val << 4) | ((ch - '0') & 0xf); + else + val = (val << 4) | (((ch & 0xdf) - 'A' + 10) & 0xf); + } + return ((node_p) 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); + } + LIST_FOREACH(hook, &node->hooks, hooks) { + if (strcmp(hook->name, name) == 0) { + 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); +} + +/* + * 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: <NODE:><PATH> + * + * 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 <NODE:> and <PATH> are optional. The <PATH> 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 <PATH>, 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 */ + LIST_FOREACH(hook, &node->hooks, hooks) { + if (hook->name && strcmp(hook->name, segment) == 0) + break; + } + + /* 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, "[%lx]:", (u_long) 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'; + LIST_FOREACH(hook, &here->hooks, hooks) { + if (hook->name && strcmp(hook->name, rmh->ourhook) == 0) + break; + } + if (hook) + 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 = (u_int32_t) 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 = (u_int32_t) 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 = (u_int32_t) 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 = (u_int32_t) 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->typename, type->name, NG_TYPELEN); + tp->numnodes = type->refs; + tl->numtypes++; + } + *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; + +#ifdef DIAGNOSTIC + if ((m->m_flags & M_PKTHDR) == 0) + panic(__FUNCTION__); +#endif + 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; + +#ifdef DIAGNOSTIC + if ((m->m_flags & M_PKTHDR) == 0) + panic(__FUNCTION__); +#endif + 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); +} + +/************************************************************************ + 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, int len, const char *address) +{ + register struct ng_queue_entry *q; + int s; + node_p dest = NULL; + char *retaddr = NULL; + int error; + + /* Find the target node. Note that this trashes address */ + 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); + } + } +} + + diff --git a/sys/netgraph/ng_cisco.c b/sys/netgraph/ng_cisco.c new file mode 100644 index 0000000..1d8ad13 --- /dev/null +++ b/sys/netgraph/ng_cisco.c @@ -0,0 +1,557 @@ + +/* + * 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 <julian@whistle.com> + * + * $FreeBSD$ + * $Whistle: ng_cisco.c,v 1.23 1999/01/28 23:54:53 julian Exp $ + */ + +#include "opt_inet.h" +#include "opt_atalk.h" +#include "opt_ipx.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/syslog.h> + +#include <net/if.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <netatalk/at.h> +#include <netatalk/at_var.h> +#include <netatalk/at_extern.h> + +#include <netipx/ipx.h> +#include <netipx/ipx_if.h> + +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> +#include <netgraph/ng_cisco.h> + +#define CISCO_MULTICAST 0x8f /* Cisco multicast address */ +#define CISCO_UNICAST 0x0f /* Cisco unicast address */ +#define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */ +#define CISCO_ADDR_REQ 0 /* Cisco address request */ +#define CISCO_ADDR_REPLY 1 /* Cisco address reply */ +#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */ + +#define KEEPALIVE_SECS 10 + +struct cisco_header { + u_char address; + u_char control; + u_short protocol; +}; + +#define CISCO_HEADER_LEN sizeof (struct cisco_header) + +struct cisco_packet { + u_long type; + u_long par1; + u_long par2; + u_short rel; + u_short time0; + u_short time1; +}; + +#define CISCO_PACKET_LEN (sizeof(struct cisco_packet)) + +struct protoent { + hook_p hook; /* the hook for this proto */ + u_short af; /* address family, -1 = downstream */ +}; + +struct cisco_priv { + u_long local_seq; + u_long remote_seq; + u_long seq_retries; /* how many times we've been here throwing out + * the same sequence number without ack */ + node_p node; + struct callout_handle handle; + struct protoent downstream; + struct protoent inet; /* IP information */ + struct in_addr localip; + struct in_addr localmask; + struct protoent atalk; /* AppleTalk information */ + struct protoent ipx; /* IPX information */ +}; +typedef struct cisco_priv *sc_p; + +/* Netgraph methods */ +static int cisco_constructor(node_p *node); +static int cisco_rcvmsg(node_p node, struct ng_mesg *msg, + const char *retaddr, struct ng_mesg **resp); +static int cisco_rmnode(node_p node); +static int cisco_newhook(node_p node, hook_p hook, const char *name); + +static int cisco_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); +static int cisco_disconnect(hook_p hook); + +/* Other functions */ +static int cisco_input(sc_p sc, struct mbuf *m, meta_p meta); +static void cisco_keepalive(void *arg); +static int cisco_send(sc_p sc, int type, long par1, long par2); + +/* Node type */ +static struct ng_type typestruct = { + NG_VERSION, + NG_CISCO_NODE_TYPE, + NULL, + cisco_constructor, + cisco_rcvmsg, + cisco_rmnode, + cisco_newhook, + NULL, + NULL, + cisco_rcvdata, + cisco_rcvdata, + cisco_disconnect +}; +NETGRAPH_INIT(cisco, &typestruct); + +/* + * Node constructor + */ +static int +cisco_constructor(node_p *nodep) +{ + sc_p sc; + int error = 0; + + MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_WAITOK); + if (sc == NULL) + return (ENOMEM); + bzero(sc, sizeof(struct cisco_priv)); + + callout_handle_init(&sc->handle); + if ((error = ng_make_node_common(&typestruct, nodep))) { + FREE(sc, M_NETGRAPH); + return (error); + } + (*nodep)->private = sc; + sc->node = *nodep; + + /* Initialise the varous protocol hook holders */ + sc->downstream.af = 0xffff; + sc->inet.af = AF_INET; + sc->atalk.af = AF_APPLETALK; + sc->ipx.af = AF_IPX; + return (0); +} + +/* + * Check new hook + */ +static int +cisco_newhook(node_p node, hook_p hook, const char *name) +{ + const sc_p sc = node->private; + + if (strcmp(name, NG_CISCO_HOOK_DOWNSTREAM) == 0) { + sc->downstream.hook = hook; + hook->private = &sc->downstream; + + /* Start keepalives */ + sc->handle = timeout(cisco_keepalive, sc, hz * KEEPALIVE_SECS); + } else if (strcmp(name, NG_CISCO_HOOK_INET) == 0) { + sc->inet.hook = hook; + hook->private = &sc->inet; + } else if (strcmp(name, NG_CISCO_HOOK_APPLETALK) == 0) { + sc->atalk.hook = hook; + hook->private = &sc->atalk; + } else if (strcmp(name, NG_CISCO_HOOK_IPX) == 0) { + sc->ipx.hook = hook; + hook->private = &sc->ipx; + } else if (strcmp(name, NG_CISCO_HOOK_DEBUG) == 0) { + hook->private = NULL; /* unimplemented */ + } else + return (EINVAL); + return 0; +} + +/* + * Receive control message. + */ +static int +cisco_rcvmsg(node_p node, struct ng_mesg *msg, + const char *retaddr, struct ng_mesg **rptr) +{ + const sc_p sc = node->private; + struct ng_mesg *resp = NULL; + int error = 0; + + switch (msg->header.typecookie) { + case NGM_GENERIC_COOKIE: + switch (msg->header.cmd) { + case NGM_TEXT_STATUS: + { + char *arg; + int pos; + + NG_MKRESPONSE(resp, msg, sizeof(struct ng_mesg) + + NG_TEXTRESPONSE, M_NOWAIT); + if (resp == NULL) { + error = ENOMEM; + break; + } + arg = (char *) resp->data; + pos = sprintf(arg, + "keepalive period: %d sec; ", KEEPALIVE_SECS); + pos += sprintf(arg + pos, + "unacknowledged keepalives: %ld", sc->seq_retries); + resp->header.arglen = pos + 1; + break; + } + default: + error = EINVAL; + break; + } + break; + case NGM_CISCO_COOKIE: + switch (msg->header.cmd) { + case NGM_CISCO_GET_IPADDR: /* could be a late reply! */ + if ((msg->header.flags & NGF_RESP) == 0) { + struct in_addr *ips; + + NG_MKRESPONSE(resp, msg, + 2 * sizeof(*ips), M_NOWAIT); + if (!resp) { + error = ENOMEM; + break; + } + ips = (struct in_addr *) resp->data; + ips[0] = sc->localip; + ips[1] = sc->localmask; + break; + } + /* FALLTHROUGH */ /* ...if it's a reply */ + case NGM_CISCO_SET_IPADDR: + { + struct in_addr *const ips = (struct in_addr *)msg->data; + + if (msg->header.arglen < 2 * sizeof(*ips)) { + error = EINVAL; + break; + } + sc->localip = ips[0]; + sc->localmask = ips[1]; + break; + } + case NGM_CISCO_GET_STATUS: + { + struct ngciscostat *stat; + + NG_MKRESPONSE(resp, msg, sizeof(*stat), M_NOWAIT); + if (!resp) { + error = ENOMEM; + break; + } + stat = (struct ngciscostat *) resp->data; + stat->seq_retries = sc->seq_retries; + stat->keepalive_period = KEEPALIVE_SECS; + break; + } + default: + error = EINVAL; + break; + } + break; + default: + error = EINVAL; + break; + } + if (rptr) + *rptr = resp; + else if (resp) + FREE(resp, M_NETGRAPH); + FREE(msg, M_NETGRAPH); + return (error); +} + +/* + * Receive data + */ +static int +cisco_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) +{ + const sc_p sc = hook->node->private; + struct protoent *pep; + struct cisco_header *h; + int error = 0; + + if ((pep = hook->private) == NULL) + goto out; + + /* If it came from our downlink, deal with it separately */ + if (pep->af == 0xffff) + return (cisco_input(sc, m, meta)); + + /* OK so it came from a protocol, heading out. Prepend general data + packet header. For now, IP,IPX only */ + M_PREPEND(m, CISCO_HEADER_LEN, M_DONTWAIT); + if (!m) { + error = ENOBUFS; + goto out; + } + h = mtod(m, struct cisco_header *); + h->address = CISCO_MULTICAST; /* broadcast address */ + h->control = 0; + + switch (pep->af) { + case AF_INET: /* Internet Protocol */ + h->protocol = htons(ETHERTYPE_IP); + break; + case AF_APPLETALK: /* AppleTalk Protocol */ + h->protocol = htons(ETHERTYPE_AT); + break; + case AF_IPX: /* Novell IPX Protocol */ + h->protocol = htons(ETHERTYPE_IPX); + break; + default: + error = EAFNOSUPPORT; + goto out; + } + + /* Send it */ + NG_SEND_DATA(error, sc->downstream.hook, m, meta); + return (error); + +out: + NG_FREE_DATA(m, meta); + return (error); +} + +/* + * Shutdown node + */ +static int +cisco_rmnode(node_p node) +{ + const sc_p sc = node->private; + + node->flags |= NG_INVALID; + ng_cutlinks(node); + ng_unname(node); + node->private = NULL; + ng_unref(sc->node); + FREE(sc, M_NETGRAPH); + return (0); +} + +/* + * Disconnection of a hook + * + * For this type, removal of the last link destroys the node + */ +static int +cisco_disconnect(hook_p hook) +{ + const sc_p sc = hook->node->private; + struct protoent *pep; + + /* Check it's not the debug hook */ + if ((pep = hook->private)) { + pep->hook = NULL; + if (pep->af == 0xffff) { + /* If it is the downstream hook, stop the timers */ + untimeout(cisco_keepalive, sc, sc->handle); + } + } + + /* If no more hooks, remove the node */ + if (hook->node->numhooks == 0) + ng_rmnode(hook->node); + return (0); +} + +/* + * Receive data + */ +static int +cisco_input(sc_p sc, struct mbuf *m, meta_p meta) +{ + struct cisco_header *h; + struct cisco_packet *p; + struct protoent *pep; + int error = 0; + + if (m->m_pkthdr.len <= CISCO_HEADER_LEN) + goto drop; + + /* Strip off cisco header */ + h = mtod(m, struct cisco_header *); + m_adj(m, CISCO_HEADER_LEN); + + switch (h->address) { + default: /* Invalid Cisco packet. */ + goto drop; + case CISCO_UNICAST: + case CISCO_MULTICAST: + /* Don't check the control field here (RFC 1547). */ + switch (ntohs(h->protocol)) { + default: + goto drop; + case CISCO_KEEPALIVE: + p = mtod(m, struct cisco_packet *); + switch (ntohl(p->type)) { + default: + log(LOG_WARNING, + "cisco: unknown cisco packet type: 0x%lx\n", + ntohl(p->type)); + break; + case CISCO_ADDR_REPLY: + /* Reply on address request, ignore */ + break; + case CISCO_KEEPALIVE_REQ: + sc->remote_seq = ntohl(p->par1); + if (sc->local_seq == ntohl(p->par2)) { + sc->local_seq++; + sc->seq_retries = 0; + } + break; + case CISCO_ADDR_REQ: + { + struct ng_mesg *msg, *resp; + + /* Ask inet peer for IP address information */ + if (sc->inet.hook == NULL) + goto nomsg; + NG_MKMESSAGE(msg, NGM_CISCO_COOKIE, + NGM_CISCO_GET_IPADDR, 0, M_NOWAIT); + if (msg == NULL) + goto nomsg; + ng_send_msg(sc->node, msg, + NG_CISCO_HOOK_INET, &resp); + if (resp != NULL) + cisco_rcvmsg(sc->node, resp, ".", NULL); + + nomsg: + /* Send reply to peer device */ + error = cisco_send(sc, CISCO_ADDR_REPLY, + ntohl(sc->localip.s_addr), + ntohl(sc->localmask.s_addr)); + break; + } + } + goto drop; + case ETHERTYPE_IP: + pep = &sc->inet; + break; + case ETHERTYPE_AT: + pep = &sc->atalk; + break; + case ETHERTYPE_IPX: + pep = &sc->ipx; + break; + } + break; + } + + /* Send it on */ + if (pep->hook == NULL) + goto drop; + NG_SEND_DATA(error, pep->hook, m, meta); + return (error); + +drop: + NG_FREE_DATA(m, meta); + return (error); +} + + +/* + * Send keepalive packets, every 10 seconds. + */ +static void +cisco_keepalive(void *arg) +{ + const sc_p sc = arg; + int s = splimp(); + + cisco_send(sc, CISCO_KEEPALIVE_REQ, sc->local_seq, sc->remote_seq); + sc->seq_retries++; + splx(s); + sc->handle = timeout(cisco_keepalive, sc, hz * KEEPALIVE_SECS); +} + +/* + * Send Cisco keepalive packet. + */ +static int +cisco_send(sc_p sc, int type, long par1, long par2) +{ + struct cisco_header *h; + struct cisco_packet *ch; + struct mbuf *m; + u_long t; + int error = 0; + meta_p meta = NULL; + struct timeval time; + + getmicrotime(&time); + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (!m) + return (ENOBUFS); + + t = (time.tv_sec - boottime.tv_sec) * 1000; + m->m_pkthdr.len = m->m_len = CISCO_HEADER_LEN + CISCO_PACKET_LEN; + m->m_pkthdr.rcvif = 0; + + h = mtod(m, struct cisco_header *); + h->address = CISCO_MULTICAST; + h->control = 0; + h->protocol = htons(CISCO_KEEPALIVE); + + ch = (struct cisco_packet *) (h + 1); + ch->type = htonl(type); + ch->par1 = htonl(par1); + ch->par2 = htonl(par2); + ch->rel = -1; + ch->time0 = htons((u_short) (t >> 16)); + ch->time1 = htons((u_short) t); + + NG_SEND_DATA(error, sc->downstream.hook, m, meta); + return (error); +} diff --git a/sys/netgraph/ng_cisco.h b/sys/netgraph/ng_cisco.h new file mode 100644 index 0000000..559cc14 --- /dev/null +++ b/sys/netgraph/ng_cisco.h @@ -0,0 +1,76 @@ + +/* + * 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 <archie@whistle.com> + * + * $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 { + /* This takes two struct in_addr's: the IP address and netmask */ + NGM_CISCO_SET_IPADDR = 1, + + /* This is both received and *sent* by this node (to the "inet" + peer). The reply contains the same info as NGM_CISCO_SET_IPADDR. */ + NGM_CISCO_GET_IPADDR, + + /* This returns a struct ngciscostat (see below) */ + NGM_CISCO_GET_STATUS, +}; + +struct ngciscostat { + u_int32_t seq_retries; /* # unack'd retries */ + u_int32_t keepalive_period; /* in seconds */ +}; + +#endif /* _NETGRAPH_CISCO_H_ */ + diff --git a/sys/netgraph/ng_echo.c b/sys/netgraph/ng_echo.c new file mode 100644 index 0000000..ed69f9c --- /dev/null +++ b/sys/netgraph/ng_echo.c @@ -0,0 +1,121 @@ + +/* + * 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 <julian@whistle.com> + * + * $FreeBSD$ + * $Whistle: ng_echo.c,v 1.11 1999/01/28 23:54:53 julian Exp $ + */ + +/* + * Netgraph "echo" node + * + * This node simply bounces data and messages back to whence they came. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/syslog.h> +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> +#include <netgraph/ng_echo.h> + +/* Netgraph methods */ +static int nge_rcvmsg(node_p node, struct ng_mesg *msg, + const char *retaddr, struct ng_mesg ** resp); +static int nge_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); +static int nge_disconnect(hook_p hook); + +/* Netgraph type */ +static struct ng_type typestruct = { + NG_VERSION, + NG_ECHO_NODE_TYPE, + NULL, + NULL, + nge_rcvmsg, + NULL, + NULL, + NULL, + NULL, + nge_rcvdata, + nge_rcvdata, + nge_disconnect +}; +NETGRAPH_INIT(echo, &typestruct); + +/* + * Receive control message. We just bounce it back as a reply. + */ +static int +nge_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, + struct ng_mesg **rptr) +{ + if (rptr) { + msg->header.flags |= NGF_RESP; + *rptr = msg; + } else { + FREE(msg, M_NETGRAPH); + } + return (0); +} + +/* + * Receive data + */ +static int +nge_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) +{ + int error = 0; + + NG_SEND_DATA(error, hook, m, meta); + return (error); +} + +/* + * Removal of the last link destroys the nodeo + */ +static int +nge_disconnect(hook_p hook) +{ + if (hook->node->numhooks == 0) + ng_rmnode(hook->node); + return (0); +} + diff --git a/sys/netgraph/ng_echo.h b/sys/netgraph/ng_echo.h new file mode 100644 index 0000000..ca8663f --- /dev/null +++ b/sys/netgraph/ng_echo.h @@ -0,0 +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 <archie@whistle.com> + * + * $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_ */ diff --git a/sys/netgraph/ng_ether.h b/sys/netgraph/ng_ether.h new file mode 100644 index 0000000..45625e4 --- /dev/null +++ b/sys/netgraph/ng_ether.h @@ -0,0 +1,55 @@ + +/* + * 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 <archie@whistle.com> + * + * $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" + +#endif /* _NETGRAPH_NG_ETHER_H_ */ + diff --git a/sys/netgraph/ng_frame_relay.c b/sys/netgraph/ng_frame_relay.c new file mode 100644 index 0000000..995b769 --- /dev/null +++ b/sys/netgraph/ng_frame_relay.c @@ -0,0 +1,526 @@ + +/* + * ng_frame_relay.c + * + * Copyright (c) 1996-1999 Whistle Communications, Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Whistle Communications; + * provided, however, that: + * 1. Any and all reproductions of the source or object code must include the + * copyright notice above and the following disclaimer of warranties; and + * 2. No rights are granted, in any manner or form, to use Whistle + * Communications, Inc. trademarks, including the mark "WHISTLE + * COMMUNICATIONS" on advertising, endorsements, or otherwise except as + * such appears in the above copyright notice or in the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY + * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS + * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. + * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Julian Elisher <julian@whistle.com> + * + * $FreeBSD$ + * $Whistle: ng_frame_relay.c,v 1.16 1999/01/28 23:54:53 julian Exp $ + */ + +/* + * This node implements the frame relay protocol, not including + * the LMI line management. This means basically keeping track + * of which DLCI's are active, doing frame (de)multiplexing, etc. + * + * It has a 'downstream' hook that goes to the line, and a + * hook for each DLCI (eg, 'dlci16'). + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/errno.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/syslog.h> +#include <machine/clock.h> + +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> +#include <netgraph/ng_frame_relay.h> + +/* + * Line info, and status per channel. + */ +struct ctxinfo { /* one per active hook */ + u_int flags; +#define CHAN_VALID 0x01 /* assigned to a channel */ +#define CHAN_ACTIVE 0x02 /* bottom level active */ + int dlci; /* the dlci assigned to this context */ + hook_p hook; /* if there's a hook assigned.. */ +}; + +#define MAX_CT 16 /* # of dlci's active at a time (POWER OF 2!) */ +struct frmrel_softc { + int unit; /* which card are we? */ + char nodename[NG_NODELEN + 1]; /* store our node name */ + int datahooks; /* number of data hooks attached */ + node_p node; /* netgraph node */ + int addrlen; /* address header length */ + int flags; /* state */ + int mtu; /* guess */ + u_char remote_seq; /* sequence number the remote sent */ + u_char local_seq; /* sequence number the remote rcvd */ + u_short ALT[1024]; /* map DLCIs to CTX */ +#define CTX_VALID 0x8000 /* this bit means it's a valid CTX */ +#define CTX_VALUE (MAX_CT - 1) /* mask for context part */ + struct ctxinfo channel[MAX_CT]; + struct ctxinfo downstream; +}; +typedef struct frmrel_softc *sc_p; + +#define BYTEX_EA 0x01 /* End Address. Always 0 on byte1 */ +#define BYTE1_C_R 0x02 +#define BYTE2_FECN 0x08 /* forwards congestion notification */ +#define BYTE2_BECN 0x04 /* Backward congestion notification */ +#define BYTE2_DE 0x02 /* Discard elligability */ +#define LASTBYTE_D_C 0x02 /* last byte is dl_core or dlci info */ + +/* Used to do headers */ +static struct segment { + u_char mask; + u_char shift; + u_char width; +} makeup[] = { + { 0xfc, 2, 6 }, + { 0xf0, 4, 4 }, + { 0xfe, 1, 7 }, + { 0xfc, 2, 6 } +}; + +#define SHIFTIN(segment, byte, dlci) \ + { \ + (dlci) <<= (segment)->width; \ + (dlci) |= \ + (((byte) & (segment)->mask) >> (segment)->shift); \ + } + +#define SHIFTOUT(segment, byte, dlci) \ + { \ + (byte) |= (((dlci) << (segment)->shift) & (segment)->mask); \ + (dlci) >>= (segment)->width; \ + } + +/* Netgraph methods */ +static int ngfrm_constructor(node_p *nodep); +static int ngfrm_rmnode(node_p node); +static int ngfrm_newhook(node_p node, hook_p hook, const char *name); +static int ngfrm_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); +static int ngfrm_disconnect(hook_p hook); + +/* Other internal functions */ +static int ngfrm_decode(node_p node, struct mbuf * m, meta_p meta); +static int ngfrm_addrlen(char *hdr); +static int ngfrm_allocate_CTX(sc_p sc, int dlci); + +/* Netgraph type */ +static struct ng_type typestruct = { + NG_VERSION, + NG_FRAMERELAY_NODE_TYPE, + NULL, + ngfrm_constructor, + NULL, + ngfrm_rmnode, + ngfrm_newhook, + NULL, + NULL, + ngfrm_rcvdata, + ngfrm_rcvdata, + ngfrm_disconnect +}; +NETGRAPH_INIT(framerelay, &typestruct); + +/* + * Given a DLCI, return the index of the context table entry for it, + * Allocating a new one if needs be, or -1 if none available. + */ +static int +ngfrm_allocate_CTX(sc_p sc, int dlci) +{ + u_int ctxnum = -1; /* what ctx number we are using */ + volatile struct ctxinfo *CTXp = NULL; + + /* Sanity check the dlci value */ + if (dlci > 1023) + return (-1); + + /* Check to see if we already have an entry for this DLCI */ + if (sc->ALT[dlci]) { + if ((ctxnum = sc->ALT[dlci] & CTX_VALUE) < MAX_CT) { + CTXp = sc->channel + ctxnum; + } else { + ctxnum = -1; + sc->ALT[dlci] = 0; /* paranoid but... */ + } + } + + /* + * If the index has no valid entry yet, then we need to allocate a + * CTX number to it + */ + if (CTXp == NULL) { + for (ctxnum = 0; ctxnum < MAX_CT; ctxnum++) { + /* + * If the VALID flag is empty it is unused + */ + if ((sc->channel[ctxnum].flags & CHAN_VALID) == 0) { + bzero(sc->channel + ctxnum, + sizeof(struct ctxinfo)); + CTXp = sc->channel + ctxnum; + sc->ALT[dlci] = ctxnum | CTX_VALID; + sc->channel[ctxnum].dlci = dlci; + sc->channel[ctxnum].flags = CHAN_VALID; + break; + } + } + } + + /* + * If we still don't have a CTX pointer, then we never found a free + * spot so give up now.. + */ + if (!CTXp) { + log(LOG_ERR, "No CTX available for dlci %d\n", dlci); + return (-1); + } + return (ctxnum); +} + +/* + * Node constructor + */ +static int +ngfrm_constructor(node_p *nodep) +{ + sc_p sc; + int error = 0; + + MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT); + if (!sc) + return (ENOMEM); + bzero(sc, sizeof(*sc)); + if ((error = ng_make_node_common(&typestruct, nodep))) { + FREE(sc, M_NETGRAPH); + return (error); + } + sc->addrlen = 2; /* default */ + + /* Link the node and our private info */ + (*nodep)->private = sc; + sc->node = *nodep; + return (0); +} + +/* + * Add a new hook + * + * We allow hooks called "debug", "downstream" and dlci[0-1023] + * The hook's private info points to our stash of info about that + * channel. A NULL pointer is debug and a DLCI of -1 means downstream. + */ +static int +ngfrm_newhook(node_p node, hook_p hook, const char *name) +{ + const sc_p sc = node->private; + const char *cp; + char c = '\0'; + int digits = 0; + int dlci = 0; + int ctxnum; + + /* Check if it's our friend the control hook */ + if (strcmp(name, NG_FRAMERELAY_HOOK_DEBUG) == 0) { + hook->private = NULL; /* paranoid */ + return (0); + } + + /* + * All other hooks either start with 'dlci' and have a decimal + * trailing channel number up to 4 digits, or are the downstream + * hook. + */ + if (strncmp(name, NG_FRAMERELAY_HOOK_DLCI, + strlen(NG_FRAMERELAY_HOOK_DLCI)) != 0) { + + /* It must be the downstream connection */ + if (strcmp(name, NG_FRAMERELAY_HOOK_DOWNSTREAM) != 0) + return EINVAL; + + /* Make sure we haven't already got one (paranoid) */ + if (sc->downstream.hook) + return (EADDRINUSE); + + /* OK add it */ + hook->private = &sc->downstream; + sc->downstream.hook = hook; + sc->downstream.dlci = -1; + sc->downstream.flags |= CHAN_ACTIVE; + sc->datahooks++; + return (0); + } + + /* Must be a dlci hook at this point */ + cp = name + strlen(NG_FRAMERELAY_HOOK_DLCI); + while ((digits < 5) && ((c = *cp++) >= '0') && (c <= '9')) { + dlci *= 10; + dlci += c - '0'; + digits++; + } + if ((c != 0) || (digits == 5) || (dlci < 0) || (dlci > 1023)) + return (EINVAL); + /* + * We have a dlci, now either find it, or allocate it. It's possible + * that we might have seen packets for it already and made an entry + * for it. + */ + ctxnum = ngfrm_allocate_CTX(sc, dlci); + if (ctxnum == -1) + return (ENOBUFS); + + /* + * Be paranoid: if it's got a hook already, that dlci is in use . + * Generic code can not catch all the synonyms (e.g. dlci016 vs + * dlci16) + */ + if (sc->channel[ctxnum].hook != NULL) + return (EADDRINUSE); + + /* + * Put our hooks into it (pun not intended) + */ + sc->channel[ctxnum].flags |= CHAN_ACTIVE; + hook->private = sc->channel + ctxnum; + sc->channel[ctxnum].hook = hook; + sc->datahooks++; + return (0); +} + +/* + * Count up the size of the address header if we don't already know + */ +int +ngfrm_addrlen(char *hdr) +{ + if (hdr[0] & BYTEX_EA) + return 0; + if (hdr[1] & BYTEX_EA) + return 2; + if (hdr[2] & BYTEX_EA) + return 3; + if (hdr[3] & BYTEX_EA) + return 4; + return 0; +} + +/* + * Receive data packet + */ +static int +ngfrm_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) +{ + struct ctxinfo *const ctxp = hook->private; + int error = 0; + int dlci; + sc_p sc; + int alen; + char *data; + + /* Data doesn't come in from just anywhere (e.g debug hook) */ + if (ctxp == NULL) { + error = ENETDOWN; + goto bad; + } + + /* If coming from downstream, decode it to a channel */ + dlci = ctxp->dlci; + if (dlci == -1) + return (ngfrm_decode(hook->node, m, meta)); + + /* Derive the softc we will need */ + sc = hook->node->private; + + /* If there is no live channel, throw it away */ + if ((sc->downstream.hook == NULL) + || ((ctxp->flags & CHAN_ACTIVE) == 0)) { + error = ENETDOWN; + goto bad; + } + + /* Store the DLCI on the front of the packet */ + alen = sc->addrlen; + if (alen == 0) + alen = 2; /* default value for transmit */ + M_PREPEND(m, alen, M_DONTWAIT); + if (m == NULL) { + error = ENOBUFS; + goto bad; + } + data = mtod(m, char *); + + /* + * Shift the lowest bits into the address field untill we are done. + * First byte is MSBits of addr so work backwards. + */ + switch (alen) { + case 2: + data[0] = data[1] = '\0'; + SHIFTOUT(makeup + 1, data[1], dlci); + SHIFTOUT(makeup + 0, data[0], dlci); + data[1] |= BYTEX_EA; + break; + case 3: + data[0] = data[1] = data[2] = '\0'; + SHIFTOUT(makeup + 3, data[2], dlci); /* 3 and 2 is correct */ + SHIFTOUT(makeup + 1, data[1], dlci); + SHIFTOUT(makeup + 0, data[0], dlci); + data[2] |= BYTEX_EA; + break; + case 4: + data[0] = data[1] = data[2] = data[3] = '\0'; + SHIFTOUT(makeup + 3, data[3], dlci); + SHIFTOUT(makeup + 2, data[2], dlci); + SHIFTOUT(makeup + 1, data[1], dlci); + SHIFTOUT(makeup + 0, data[0], dlci); + data[3] |= BYTEX_EA; + break; + default: + panic(__FUNCTION__); + } + + /* Send it */ + NG_SEND_DATA(error, sc->downstream.hook, m, meta); + return (error); + +bad: + NG_FREE_DATA(m, meta); + return (error); +} + +/* + * Decode an incoming frame coming from the switch + */ +static int +ngfrm_decode(node_p node, struct mbuf *m, meta_p meta) +{ + const sc_p sc = node->private; + char *data; + int alen; + u_int dlci = 0; + int error = 0; + int ctxnum; + + if ((m = m_pullup(m, 4)) == NULL) { + error = ENOBUFS; + goto out; + } + data = mtod(m, char *); + if ((alen = sc->addrlen) == 0) { + sc->addrlen = alen = ngfrm_addrlen(data); + } + switch (alen) { + case 2: + SHIFTIN(makeup + 0, data[0], dlci); + SHIFTIN(makeup + 1, data[1], dlci); + break; + case 3: + SHIFTIN(makeup + 0, data[0], dlci); + SHIFTIN(makeup + 1, data[1], dlci); + SHIFTIN(makeup + 3, data[2], dlci); /* 3 and 2 is correct */ + break; + case 4: + SHIFTIN(makeup + 0, data[0], dlci); + SHIFTIN(makeup + 1, data[1], dlci); + SHIFTIN(makeup + 2, data[2], dlci); + SHIFTIN(makeup + 3, data[3], dlci); + break; + default: + error = EINVAL; + goto out; + } + + if (dlci > 1023) { + error = EINVAL; + goto out; + } + ctxnum = sc->ALT[dlci]; + if ((ctxnum & CTX_VALID) && sc->channel[ctxnum &= CTX_VALUE].hook) { + /* Send it */ + m_adj(m, alen); + NG_SEND_DATA(error, sc->channel[ctxnum].hook, m, meta); + return (error); + } else { + error = ENETDOWN; + } +out: + NG_FREE_DATA(m, meta); + return (error); +} + +/* + * Shutdown node + */ +static int +ngfrm_rmnode(node_p node) +{ + const sc_p sc = node->private; + + node->flags |= NG_INVALID; + ng_cutlinks(node); + ng_unname(node); + node->private = NULL; + FREE(sc, M_NETGRAPH); + ng_unref(sc->node); + return (0); +} + +/* + * Hook disconnection + * + * Invalidate the private data associated with this dlci. + * For this type, removal of the last link resets tries to destroy the node. + */ +static int +ngfrm_disconnect(hook_p hook) +{ + const sc_p sc = hook->node->private; + struct ctxinfo *const cp = hook->private; + int dlci; + + /* If it's a regular dlci hook, then free resources etc.. */ + if (cp != NULL) { + cp->hook = NULL; + dlci = cp->dlci; + if (dlci != -1) + sc->ALT[dlci] = 0; + cp->flags = 0; + sc->datahooks--; + } + if (hook->node->numhooks == 0) + ng_rmnode(hook->node); + return (0); +} diff --git a/sys/netgraph/ng_frame_relay.h b/sys/netgraph/ng_frame_relay.h new file mode 100644 index 0000000..5c76421 --- /dev/null +++ b/sys/netgraph/ng_frame_relay.h @@ -0,0 +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 <archie@whistle.com> + * + * $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_ */ diff --git a/sys/netgraph/ng_hole.c b/sys/netgraph/ng_hole.c new file mode 100644 index 0000000..7baf91d --- /dev/null +++ b/sys/netgraph/ng_hole.c @@ -0,0 +1,97 @@ + +/* + * ng_hole.c + * + * Copyright (c) 1996-1999 Whistle Communications, Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Whistle Communications; + * provided, however, that: + * 1. Any and all reproductions of the source or object code must include the + * copyright notice above and the following disclaimer of warranties; and + * 2. No rights are granted, in any manner or form, to use Whistle + * Communications, Inc. trademarks, including the mark "WHISTLE + * COMMUNICATIONS" on advertising, endorsements, or otherwise except as + * such appears in the above copyright notice or in the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY + * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS + * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. + * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Julian Elisher <julian@whistle.com> + * + * $FreeBSD$ + * $Whistle: ng_hole.c,v 1.8 1999/01/28 23:54:53 julian Exp $ + */ + +/* + * This node is a 'black hole' that simply discards everything it receives + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/syslog.h> +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> +#include <netgraph/ng_hole.h> + +/* Netgraph methods */ +static int ngh_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); +static int ngh_disconnect(hook_p hook); + +static struct ng_type typestruct = { + NG_VERSION, + NG_HOLE_NODE_TYPE, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + ngh_rcvdata, + ngh_rcvdata, + ngh_disconnect +}; +NETGRAPH_INIT(hole, &typestruct); + +/* + * Receive data + */ +static int +ngh_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) +{ + NG_FREE_DATA(m, meta); + return 0; +} + +/* + * Hook disconnection + */ +static int +ngh_disconnect(hook_p hook) +{ + if (hook->node->numhooks == 0) + ng_rmnode(hook->node); + return (0); +} diff --git a/sys/netgraph/ng_hole.h b/sys/netgraph/ng_hole.h new file mode 100644 index 0000000..ec12f3f --- /dev/null +++ b/sys/netgraph/ng_hole.h @@ -0,0 +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 <archie@whistle.com> + * + * $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_ */ diff --git a/sys/netgraph/ng_iface.c b/sys/netgraph/ng_iface.c new file mode 100644 index 0000000..d487749 --- /dev/null +++ b/sys/netgraph/ng_iface.c @@ -0,0 +1,778 @@ + +/* + * 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 <archie@whistle.com> + * + * $FreeBSD$ + * $Whistle: ng_iface.c,v 1.31 1999/02/02 22:27:28 archie 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 <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/conf.h> +#include <sys/errno.h> +#include <sys/sockio.h> +#include <sys/socket.h> +#include <sys/syslog.h> + +#include <net/route.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/netisr.h> + +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> +#include <netgraph/ng_iface.h> +#include <netgraph/ng_cisco.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#include <netinet/in_var.h> +#endif + +#ifdef NETATALK +#include <netatalk/at.h> +#include <netatalk/at_var.h> +#include <netatalk/at_extern.h> +#endif + +#ifdef IPX +#include <netipx/ipx.h> +#include <netipx/ipx_if.h> +#endif + +#ifdef NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif + +#include "bpfilter.h" + +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#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 private { + struct ifnet *ifp; /* This interface */ + node_p node; /* Our netgraph node */ + hook_p hooks[NUM_FAMILIES]; /* Hook for each address family */ + struct private *next; /* When hung on the free list */ +}; +typedef struct private *priv_p; + +/* Interface methods */ +static void ng_iface_start(struct ifnet *ifp); +static int ng_iface_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); +static int ng_iface_output(struct ifnet *ifp, struct mbuf *m0, + struct sockaddr *dst, struct rtentry *rt0); +#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 int ng_iface_constructor(node_p *nodep); +static int ng_iface_rcvmsg(node_p node, struct ng_mesg *msg, + const char *retaddr, struct ng_mesg **resp); +static int ng_iface_rmnode(node_p node); +static int ng_iface_newhook(node_p node, hook_p hook, const char *name); +static int ng_iface_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); +static int ng_iface_disconnect(hook_p hook); + +/* Helper stuff */ +static iffam_p get_iffam_from_af(int af); +static iffam_p get_iffam_from_hook(priv_p priv, hook_p hook); +static iffam_p get_iffam_from_name(const char *name); +static hook_p *get_hook_from_iffam(priv_p priv, iffam_p iffam); + +/* Node type descriptor */ +static struct ng_type typestruct = { + NG_VERSION, + NG_IFACE_NODE_TYPE, + NULL, + ng_iface_constructor, + ng_iface_rcvmsg, + ng_iface_rmnode, + ng_iface_newhook, + NULL, + NULL, + ng_iface_rcvdata, + ng_iface_rcvdata, + ng_iface_disconnect +}; +NETGRAPH_INIT(iface, &typestruct); + +static char ng_iface_ifname[] = NG_IFACE_IFACE_NAME; +static int ng_iface_next_unit; + +/************************************************************************ + HELPER STUFF + ************************************************************************/ + +/* + * Get the family descriptor from the family ID + */ +static __inline__ iffam_p +get_iffam_from_af(int af) +{ + iffam_p iffam; + int k; + + for (k = 0; k < NUM_FAMILIES; k++) { + iffam = &gFamilies[k]; + if (iffam->af == af) + return (iffam); + } + return (NULL); +} + +/* + * Get the family descriptor from the hook + */ +static __inline__ iffam_p +get_iffam_from_hook(priv_p priv, hook_p hook) +{ + int k; + + for (k = 0; k < NUM_FAMILIES; k++) + if (priv->hooks[k] == hook) + return (&gFamilies[k]); + return (NULL); +} + +/* + * Get the hook from the iffam descriptor + */ + +static __inline__ hook_p * +get_hook_from_iffam(priv_p priv, iffam_p iffam) +{ + return (&priv->hooks[iffam - gFamilies]); +} + +/* + * Get the iffam descriptor from the name + */ +static __inline__ iffam_p +get_iffam_from_name(const char *name) +{ + iffam_p iffam; + int k; + + for (k = 0; k < NUM_FAMILIES; k++) { + iffam = &gFamilies[k]; + if (!strcmp(iffam->hookname, name)) + return (iffam); + } + return (NULL); +} + +/************************************************************************ + INTERFACE STUFF + ************************************************************************/ + +/* + * Process an ioctl for the virtual interface + */ +static int +ng_iface_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct ifreq *const ifr = (struct ifreq *) data; + int s, error = 0; + +#ifdef DEBUG + ng_iface_print_ioctl(ifp, command, data); +#endif + s = splimp(); + switch (command) { + + /* These two are mostly handled at a higher layer */ + case SIOCSIFADDR: + ifp->if_flags |= (IFF_UP | IFF_RUNNING); + ifp->if_flags &= ~(IFF_OACTIVE); + break; + case SIOCGIFADDR: + break; + + /* Set flags */ + case SIOCSIFFLAGS: + /* + * If the interface is marked up and stopped, then start it. + * If it is marked down and running, then stop it. + */ + if (ifr->ifr_flags & IFF_UP) { + if (!(ifp->if_flags & IFF_RUNNING)) { + ifp->if_flags &= ~(IFF_OACTIVE); + ifp->if_flags |= IFF_RUNNING; + } + } else { + if (ifp->if_flags & IFF_RUNNING) + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + } + break; + + /* Set the interface MTU */ + case SIOCSIFMTU: + if (ifr->ifr_mtu > NG_IFACE_MTU_MAX + || ifr->ifr_mtu < NG_IFACE_MTU_MIN) + error = EINVAL; + else + ifp->if_mtu = ifr->ifr_mtu; + break; + + /* Stuff that's not supported */ + case SIOCADDMULTI: + case SIOCDELMULTI: + case SIOCSIFPHYS: + error = EOPNOTSUPP; + break; + + default: + error = EINVAL; + break; + } + (void) splx(s); + return (error); +} + +/* + * This routine is called to deliver a packet out the interface. + * We simply look at the address family and relay the packet to + * the corresponding hook, if it exists and is connected. + */ + +static int +ng_iface_output(struct ifnet *ifp, struct mbuf *m, + struct sockaddr *dst, struct rtentry *rt0) +{ + const priv_p priv = (priv_p) ifp->if_softc; + const iffam_p iffam = get_iffam_from_af(dst->sa_family); + meta_p meta = NULL; + int len, error = 0; + + /* Check interface flags */ + if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { + m_freem(m); + return (ENETDOWN); + } + + /* Berkeley packet filter */ +#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); + 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_len<sizeof(*(s))? sizeof(*(s)):(s)->sa_len) + + /* Determine size of response and allocate it */ + buflen = 0; + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) + buflen += SA_SIZE(ifa->ifa_addr); + NG_MKRESPONSE(resp, msg, buflen, M_NOWAIT); + if (resp == NULL) { + error = ENOMEM; + break; + } + + /* Add addresses */ + ptr = resp->data; + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + const int len = SA_SIZE(ifa->ifa_addr); + + if (buflen < len) { + log(LOG_ERR, "%s%d: len changed?\n", + ifp->if_name, ifp->if_unit); + break; + } + bcopy(ifa->ifa_addr, ptr, len); + ptr += len; + buflen -= len; + } + break; +#undef SA_SIZE + } + + default: + error = EINVAL; + break; + } + break; + case NGM_CISCO_COOKIE: + switch (msg->header.cmd) { + case NGM_CISCO_GET_IPADDR: /* we understand this too */ + { + struct ifaddr *ifa; + + /* Return the first configured IP address */ + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + struct in_addr *ips; + + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + NG_MKRESPONSE(resp, msg, + 2 * sizeof(*ips), M_NOWAIT); + if (resp == NULL) { + error = ENOMEM; + break; + } + ips = (struct in_addr *) resp->data; + ips[0] = ((struct sockaddr_in *) + ifa->ifa_addr)->sin_addr; + ips[1] = ((struct sockaddr_in *) + ifa->ifa_netmask)->sin_addr; + break; + } + + /* No IP addresses on this interface? */ + if (ifa == NULL) + error = EADDRNOTAVAIL; + break; + } + default: + error = EINVAL; + break; + } + break; + default: + error = EINVAL; + break; + } + if (rptr) + *rptr = resp; + else if (resp) + FREE(resp, M_NETGRAPH); + FREE(msg, M_NETGRAPH); + return (error); +} + +/* + * Recive data from a hook. Pass the packet to the correct input routine. + */ +static int +ng_iface_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) +{ + const priv_p priv = hook->node->private; + const iffam_p iffam = get_iffam_from_hook(priv, hook); + struct ifnet *const ifp = priv->ifp; + int s, error = 0; + + /* Sanity checks */ +#ifdef DIAGNOSTIC + if (iffam == NULL) + panic(__FUNCTION__); + if ((m->m_flags & M_PKTHDR) == 0) + panic(__FUNCTION__); +#endif + if (m == NULL) + return (EINVAL); + if ((ifp->if_flags & IFF_UP) == 0) { + NG_FREE_DATA(m, meta); + return (ENETDOWN); + } + + /* Update interface stats */ + ifp->if_ipackets++; + ifp->if_ibytes += m->m_pkthdr.len; + + /* Note receiving interface */ + m->m_pkthdr.rcvif = ifp; + +#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); +} + diff --git a/sys/netgraph/ng_iface.h b/sys/netgraph/ng_iface.h new file mode 100644 index 0000000..946f06e --- /dev/null +++ b/sys/netgraph/ng_iface.h @@ -0,0 +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 <archie@whistle.com> + * + * $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_ */ diff --git a/sys/netgraph/ng_lmi.c b/sys/netgraph/ng_lmi.c new file mode 100644 index 0000000..a404998 --- /dev/null +++ b/sys/netgraph/ng_lmi.c @@ -0,0 +1,1090 @@ + +/* + * ng_lmi.c + * + * Copyright (c) 1996-1999 Whistle Communications, Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Whistle Communications; + * provided, however, that: + * 1. Any and all reproductions of the source or object code must include the + * copyright notice above and the following disclaimer of warranties; and + * 2. No rights are granted, in any manner or form, to use Whistle + * Communications, Inc. trademarks, including the mark "WHISTLE + * COMMUNICATIONS" on advertising, endorsements, or otherwise except as + * such appears in the above copyright notice or in the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY + * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS + * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. + * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Julian Elischer <julian@whistle.com> + * + * $FreeBSD$ + * $Whistle: ng_lmi.c,v 1.35 1999/01/28 23:54:53 julian Exp $ + */ + +/* + * This node performs the frame relay LMI protocol. It knows how + * to do ITU Annex A, ANSI Annex D, and "Group-of-Four" variants + * of the protocol. + * + * A specific protocol can be forced by connecting the corresponding + * hook to DLCI 0 or 1023 (as appropriate) of a frame relay link. + * + * Alternately, this node can do auto-detection of the LMI protocol + * by connecting hook "auto0" to DLCI 0 and "auto1023" to DLCI 1023. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/syslog.h> +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> +#include <netgraph/ng_lmi.h> + +/* + * Human readable names for LMI + */ +#define NAME_ANNEXA NG_LMI_HOOK_ANNEXA +#define NAME_ANNEXD NG_LMI_HOOK_ANNEXD +#define NAME_GROUP4 NG_LMI_HOOK_GROUPOF4 +#define NAME_NONE "None" + +#define MAX_DLCIS 128 +#define MAXDLCI 1023 + +/* + * DLCI states + */ +#define DLCI_NULL 0 +#define DLCI_UP 1 +#define DLCI_DOWN 2 + +/* + * Any received LMI frame should be at least this long + */ +#define LMI_MIN_LENGTH 8 /* XXX verify */ + +/* + * Netgraph node methods and type descriptor + */ +static int nglmi_constructor(node_p *node); +static int nglmi_rcvmsg(node_p node, struct ng_mesg *msg, + const char *retaddr, struct ng_mesg **resp); +static int nglmi_rmnode(node_p node); +static int nglmi_newhook(node_p node, hook_p hook, const char *name); +static int nglmi_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); +static int nglmi_disconnect(hook_p hook); /* notify on disconnect */ +static int nglmi_checkdata(hook_p hook, struct mbuf *m, meta_p meta); + +static struct ng_type typestruct = { + NG_VERSION, + NG_LMI_NODE_TYPE, + NULL, + nglmi_constructor, + nglmi_rcvmsg, + nglmi_rmnode, + nglmi_newhook, + NULL, + NULL, + nglmi_rcvdata, + nglmi_rcvdata, + nglmi_disconnect +}; +NETGRAPH_INIT(lmi, &typestruct); + +/* + * Info and status per node + */ +struct nglmi_softc { + node_p node; /* netgraph node */ + int flags; /* state */ + int poll_count; /* the count of times for autolmi */ + int poll_state; /* state of auto detect machine */ + u_char remote_seq; /* sequence number the remote sent */ + u_char local_seq; /* last sequence number we sent */ + u_char protoID; /* 9 for group of 4, 8 otherwise */ + u_long seq_retries; /* sent this how many time so far */ + struct callout_handle handle; /* see timeout(9) */ + int liv_per_full; + int liv_rate; + int livs; + int need_full; + hook_p lmi_channel; /* whatever we ended up using */ + hook_p lmi_annexA; + hook_p lmi_annexD; + hook_p lmi_group4; + hook_p lmi_channel0; /* auto-detect on DLCI 0 */ + hook_p lmi_channel1023;/* auto-detect on DLCI 1023 */ + char *protoname; /* cache protocol name */ + u_char dlci_state[MAXDLCI + 1]; + int invalidx; /* next dlci's to invalidate */ +}; +typedef struct nglmi_softc *sc_p; + +/* + * Other internal functions + */ +static void LMI_ticker(void *arg); +static void nglmi_startup_fixed(sc_p sc, hook_p hook); +static void nglmi_startup_auto(sc_p sc); +static void nglmi_startup(sc_p sc); +static void nglmi_inquire(sc_p sc, int full); +static void ngauto_state_machine(sc_p sc); + +/* + * Values for 'flags' field + * NB: the SCF_CONNECTED flag is set if and only if the timer is running. + */ +#define SCF_CONNECTED 0x01 /* connected to something */ +#define SCF_AUTO 0x02 /* we are auto-detecting */ +#define SCF_FIXED 0x04 /* we are fixed from the start */ + +#define SCF_LMITYPE 0x18 /* mask for determining Annex mode */ +#define SCF_NOLMI 0x00 /* no LMI type selected yet */ +#define SCF_ANNEX_A 0x08 /* running annex A mode */ +#define SCF_ANNEX_D 0x10 /* running annex D mode */ +#define SCF_GROUP4 0x18 /* running group of 4 */ + +#define SETLMITYPE(sc, annex) \ +do { \ + (sc)->flags &= ~SCF_LMITYPE; \ + (sc)->flags |= (annex); \ +} while (0) + +#define NOPROTO(sc) (((sc)->flags & SCF_LMITYPE) == SCF_NOLMI) +#define ANNEXA(sc) (((sc)->flags & SCF_LMITYPE) == SCF_ANNEX_A) +#define ANNEXD(sc) (((sc)->flags & SCF_LMITYPE) == SCF_ANNEX_D) +#define GROUP4(sc) (((sc)->flags & SCF_LMITYPE) == SCF_GROUP4) + +#define LMIPOLLSIZE 3 +#define LMI_PATIENCE 8 /* declare all DLCI DOWN after N LMI failures */ + +/* + * Node constructor + */ +static int +nglmi_constructor(node_p *nodep) +{ + sc_p sc; + int error = 0; + + MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_WAITOK); + if (sc == NULL) + return (ENOMEM); + bzero(sc, sizeof(*sc)); + + callout_handle_init(&sc->handle); + if ((error = ng_make_node_common(&typestruct, nodep))) { + FREE(sc, M_NETGRAPH); + return (error); + } + (*nodep)->private = sc; + sc->protoname = NAME_NONE; + sc->node = *nodep; + sc->liv_per_full = NG_LMI_SEQ_PER_FULL; /* make this dynamic */ + sc->liv_rate = NG_LMI_KEEPALIVE_RATE; + return (0); +} + +/* + * The LMI channel has a private pointer which is the same as the + * node private pointer. The debug channel has a NULL private pointer. + */ +static int +nglmi_newhook(node_p node, hook_p hook, const char *name) +{ + sc_p sc = node->private; + + if (strcmp(name, NG_LMI_HOOK_DEBUG) == 0) { + hook->private = NULL; + return (0); + } + if (sc->flags & SCF_CONNECTED) { + /* already connected, return an error */ + return (EINVAL); + } + if (strcmp(name, NG_LMI_HOOK_ANNEXA) == 0) { + sc->lmi_annexA = hook; + hook->private = node->private; + sc->protoID = 8; + SETLMITYPE(sc, SCF_ANNEX_A); + sc->protoname = NAME_ANNEXA; + nglmi_startup_fixed(sc, hook); + } else if (strcmp(name, NG_LMI_HOOK_ANNEXD) == 0) { + sc->lmi_annexD = hook; + hook->private = node->private; + sc->protoID = 8; + SETLMITYPE(sc, SCF_ANNEX_D); + sc->protoname = NAME_ANNEXD; + nglmi_startup_fixed(sc, hook); + } else if (strcmp(name, NG_LMI_HOOK_GROUPOF4) == 0) { + sc->lmi_group4 = hook; + hook->private = node->private; + sc->protoID = 9; + SETLMITYPE(sc, SCF_GROUP4); + sc->protoname = NAME_GROUP4; + nglmi_startup_fixed(sc, hook); + } else if (strcmp(name, NG_LMI_HOOK_AUTO0) == 0) { + /* Note this, and if B is already installed, we're complete */ + sc->lmi_channel0 = hook; + sc->protoname = NAME_NONE; + hook->private = node->private; + if (sc->lmi_channel1023) + nglmi_startup_auto(sc); + } else if (strcmp(name, NG_LMI_HOOK_AUTO1023) == 0) { + /* Note this, and if A is already installed, we're complete */ + sc->lmi_channel1023 = hook; + sc->protoname = NAME_NONE; + hook->private = node->private; + if (sc->lmi_channel0) + nglmi_startup_auto(sc); + } else + return (EINVAL); /* unknown hook */ + return (0); +} + +/* + * We have just attached to a live (we hope) node. + * Fire out a LMI inquiry, and then start up the timers. + */ +static void +LMI_ticker(void *arg) +{ + sc_p sc = arg; + int s = splnet(); + + if (sc->flags & SCF_AUTO) { + ngauto_state_machine(sc); + sc->handle = timeout(LMI_ticker, sc, NG_LMI_POLL_RATE * hz); + } else { + if (sc->livs++ >= sc->liv_per_full) { + nglmi_inquire(sc, 1); + /* sc->livs = 0; *//* do this when we get the answer! */ + } else { + nglmi_inquire(sc, 0); + } + sc->handle = timeout(LMI_ticker, sc, sc->liv_rate * hz); + } + splx(s); +} + +static void +nglmi_startup_fixed(sc_p sc, hook_p hook) +{ + sc->flags |= (SCF_FIXED | SCF_CONNECTED); + sc->lmi_channel = hook; + nglmi_startup(sc); +} + +static void +nglmi_startup_auto(sc_p sc) +{ + sc->flags |= (SCF_AUTO | SCF_CONNECTED); + sc->poll_state = 0; /* reset state machine */ + sc->poll_count = 0; + nglmi_startup(sc); +} + +static void +nglmi_startup(sc_p sc) +{ + sc->remote_seq = 0; + sc->local_seq = 1; + sc->seq_retries = 0; + sc->livs = sc->liv_per_full - 1; + /* start off the ticker in 1 sec */ + sc->handle = timeout(LMI_ticker, sc, hz); +} + +#define META_PAD 16 +static void +nglmi_inquire(sc_p sc, int full) +{ + struct mbuf *m; + char *cptr, *start; + int error; + meta_p meta = NULL; + + if (sc->lmi_channel == NULL) + return; + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + log(LOG_ERR, "nglmi: unable to start up LMI processing\n"); + return; + } + /* Allocate a meta struct (and leave some slop for options to be + * added by other modules). */ + /* MALLOC(meta, meta_p, sizeof( struct ng_meta) + META_PAD, + * M_NETGRAPH, M_NOWAIT); */ + MALLOC(meta, meta_p, sizeof(*meta) + META_PAD, M_NETGRAPH, M_NOWAIT); + if (meta != NULL) { /* if it failed, well, it was optional anyhow */ + meta->used_len = (u_short) sizeof(struct ng_meta); + meta->allocated_len + = (u_short) sizeof(struct ng_meta) + META_PAD; + meta->flags = 0; + meta->priority = NG_LMI_LMI_PRIORITY; + meta->discardability = -1; + } + m->m_data += 4; /* leave some room for a header */ + cptr = start = mtod(m, char *); + /* add in the header for an LMI inquiry. */ + *cptr++ = 0x03; /* UI frame */ + if (GROUP4(sc)) + *cptr++ = 0x09; /* proto discriminator */ + else + *cptr++ = 0x08; /* proto discriminator */ + *cptr++ = 0x00; /* call reference */ + *cptr++ = 0x75; /* inquiry */ + + /* If we are Annex-D, there is this extra thing.. */ + if (ANNEXD(sc)) + *cptr++ = 0x95; /* ??? */ + /* Add a request type */ + if (ANNEXA(sc)) + *cptr++ = 0x51; /* report type */ + else + *cptr++ = 0x01; /* report type */ + *cptr++ = 0x01; /* size = 1 */ + if (full) + *cptr++ = 0x00; /* full */ + else + *cptr++ = 0x01; /* partial */ + + /* Add a link verification IE */ + if (ANNEXA(sc)) + *cptr++ = 0x53; /* verification IE */ + else + *cptr++ = 0x03; /* verification IE */ + *cptr++ = 0x02; /* 2 extra bytes */ + *cptr++ = sc->local_seq; + *cptr++ = sc->remote_seq; + sc->seq_retries++; + + /* Send it */ + m->m_len = m->m_pkthdr.len = cptr - start; + NG_SEND_DATA(error, sc->lmi_channel, m, meta); + + /* If we've been sending requests for long enough, and there has + * been no response, then mark as DOWN, any DLCIs that are UP. */ + if (sc->seq_retries == LMI_PATIENCE) { + int count; + + for (count = 0; count < MAXDLCI; count++) + if (sc->dlci_state[count] == DLCI_UP) + sc->dlci_state[count] = DLCI_DOWN; + } +} + +/* + * State machine for LMI auto-detect. The transitions are ordered + * to try the more likely possibilities first. + */ +static void +ngauto_state_machine(sc_p sc) +{ + if ((sc->poll_count <= 0) || (sc->poll_count > LMIPOLLSIZE)) { + /* time to change states in the auto probe machine */ + /* capture wild values of poll_count while we are at it */ + sc->poll_count = LMIPOLLSIZE; + sc->poll_state++; + } + switch (sc->poll_state) { + case 7: + log(LOG_WARNING, "nglmi: no response from exchange\n"); + default: /* capture bad states */ + sc->poll_state = 1; + case 1: + sc->lmi_channel = sc->lmi_channel0; + SETLMITYPE(sc, SCF_ANNEX_D); + break; + case 2: + sc->lmi_channel = sc->lmi_channel1023; + SETLMITYPE(sc, SCF_ANNEX_D); + break; + case 3: + sc->lmi_channel = sc->lmi_channel0; + SETLMITYPE(sc, SCF_ANNEX_A); + break; + case 4: + sc->lmi_channel = sc->lmi_channel1023; + SETLMITYPE(sc, SCF_GROUP4); + break; + case 5: + sc->lmi_channel = sc->lmi_channel1023; + SETLMITYPE(sc, SCF_ANNEX_A); + break; + case 6: + sc->lmi_channel = sc->lmi_channel0; + SETLMITYPE(sc, SCF_GROUP4); + break; + } + + /* send an inquirey encoded appropriatly */ + nglmi_inquire(sc, 0); + sc->poll_count--; +} + +/* + * Receive a netgraph control message. + */ +static int +nglmi_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, + struct ng_mesg **resp) +{ + int error = 0; + sc_p sc = node->private; + + switch (msg->header.typecookie) { + case NGM_GENERIC_COOKIE: + switch (msg->header.cmd) { + case NGM_TEXT_STATUS: + { + char *arg; + int pos, count; + + NG_MKRESPONSE(*resp, msg, NG_TEXTRESPONSE, M_NOWAIT); + if (*resp == NULL) { + error = ENOMEM; + break; + } + arg = (*resp)->data; + pos = sprintf(arg, "protocol %s ", sc->protoname); + if (sc->flags & SCF_FIXED) + pos += sprintf(arg + pos, "fixed\n"); + else if (sc->flags & SCF_AUTO) + pos += sprintf(arg + pos, "auto-detecting\n"); + else + pos += sprintf(arg + pos, "auto on dlci %d\n", + (sc->lmi_channel == sc->lmi_channel0) ? + 0 : 1023); + pos += sprintf(arg + pos, + "keepalive period: %d seconds\n", sc->liv_rate); + pos += sprintf(arg + pos, + "unacknowledged keepalives: %ld\n", + sc->seq_retries); + for (count = 0; + ((count <= MAXDLCI) + && (pos < (NG_TEXTRESPONSE - 20))); + count++) { + if (sc->dlci_state[count]) { + pos += sprintf(arg + pos, + "dlci %d %s\n", count, + (sc->dlci_state[count] + == DLCI_UP) ? "up" : "down"); + } + } + (*resp)->header.arglen = pos + 1; + break; + } + default: + error = EINVAL; + break; + } + break; + case NGM_LMI_COOKIE: + switch (msg->header.cmd) { + case NGM_LMI_GET_STATUS: + { + struct nglmistat *stat; + int k; + + NG_MKRESPONSE(*resp, msg, sizeof(*stat), M_NOWAIT); + if (!*resp) { + error = ENOMEM; + break; + } + stat = (struct nglmistat *) (*resp)->data; + strncpy(stat->proto, + sc->protoname, sizeof(stat->proto) - 1); + strncpy(stat->hook, + sc->protoname, sizeof(stat->hook) - 1); + stat->autod = !!(sc->flags & SCF_AUTO); + stat->fixed = !!(sc->flags & SCF_FIXED); + for (k = 0; k <= MAXDLCI; k++) { + switch (sc->dlci_state[k]) { + case DLCI_UP: + stat->up[k / 8] |= (1 << (k % 8)); + /* fall through */ + case DLCI_DOWN: + stat->seen[k / 8] |= (1 << (k % 8)); + break; + } + } + break; + } + default: + error = EINVAL; + break; + } + break; + default: + error = EINVAL; + break; + } + FREE(msg, M_NETGRAPH); + return (error); +} + +#define STEPBY(stepsize) \ + do { \ + packetlen -= (stepsize); \ + data += (stepsize); \ + } while (0) + +/* + * receive data, and use it to update our status. + * Anything coming in on the debug port is discarded. + */ +static int +nglmi_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) +{ + sc_p sc = hook->node->private; + u_char *data; + unsigned short dlci; + u_short packetlen; + int resptype_seen = 0; + int seq_seen = 0; + + if (hook->private == NULL) { + goto drop; + } + packetlen = m->m_hdr.mh_len; + + /* XXX what if it's more than 1 mbuf? */ + if ((packetlen > MHLEN) && !(m->m_flags & M_EXT)) { + log(LOG_WARNING, "nglmi: packetlen (%d) too big\n", packetlen); + goto drop; + } + if ((m = m_pullup(m, packetlen)) == NULL) { + log(LOG_WARNING, "nglmi: m_pullup failed for %d bytes\n", packetlen); + NG_FREE_META(meta); + return (0); + } + if (nglmi_checkdata(hook, m, meta) == 0) + return (0); + + /* pass the first 4 bytes (already checked in the nglmi_checkdata()) */ + data = mtod(m, u_char *); + STEPBY(4); + + /* Now check if there is a 'locking shift'. This is only seen in + * Annex D frames. don't bother checking, we already did that. Don't + * increment immediatly as it might not be there. */ + if (ANNEXD(sc)) + STEPBY(1); + + /* If we get this far we should consider that it is a legitimate + * frame and we know what it is. */ + if (sc->flags & SCF_AUTO) { + /* note the hook that this valid channel came from and drop + * out of auto probe mode. */ + if (ANNEXA(sc)) + sc->protoname = NAME_ANNEXA; + else if (ANNEXD(sc)) + sc->protoname = NAME_ANNEXD; + else if (GROUP4(sc)) + sc->protoname = NAME_GROUP4; + else { + log(LOG_ERR, "nglmi: No known type\n"); + goto drop; + } + sc->lmi_channel = hook; + sc->flags &= ~SCF_AUTO; + log(LOG_INFO, "nglmi: auto-detected %s LMI on DLCI %d\n", + sc->protoname, hook == sc->lmi_channel0 ? 0 : 1023); + } + + /* While there is more data in the status packet, keep processing + * status items. First make sure there is enough data for the + * segment descriptor's length field. */ + while (packetlen >= 2) { + u_int segtype = data[0]; + u_int segsize = data[1]; + + /* Now that we know how long it claims to be, make sure + * there is enough data for the next seg. */ + if (packetlen < segsize + 2) + break; + switch (segtype) { + case 0x01: + case 0x51: + if (resptype_seen) { + log(LOG_WARNING, "nglmi: dup MSGTYPE\n"); + goto nextIE; + } + resptype_seen++; + /* The remote end tells us what kind of response + * this is. Only expect a type 0 or 1. if we are a + * full status, invalidate a few DLCIs just to see + * that they are still ok. */ + if (segsize != 1) + goto nextIE; + switch (data[2]) { + case 1: + /* partial status, do no extra processing */ + break; + case 0: + { + int count = 0; + int idx = sc->invalidx; + + for (count = 0; count < 10; count++) { + if (idx > MAXDLCI) + idx = 0; + if (sc->dlci_state[idx] == DLCI_UP) + sc->dlci_state[idx] = DLCI_DOWN; + idx++; + } + sc->invalidx = idx; + /* we got and we wanted one. relax + * now.. but don't reset to 0 if it + * was unrequested. */ + if (sc->livs > sc->liv_per_full) + sc->livs = 0; + break; + } + } + break; + case 0x03: + case 0x53: + /* The remote tells us what it thinks the sequence + * numbers are. If it's not size 2, it must be a + * duplicate to have gotten this far, skip it. */ + if (seq_seen != 0) /* already seen seq numbers */ + goto nextIE; + if (segsize != 2) + goto nextIE; + sc->remote_seq = data[2]; + if (sc->local_seq == data[3]) { + sc->local_seq++; + sc->seq_retries = 0; + /* Note that all 3 Frame protocols seem to + * not like 0 as a sequence number. */ + if (sc->local_seq == 0) + sc->local_seq = 1; + } + break; + case 0x07: + case 0x57: + /* The remote tells us about a DLCI that it knows + * about. There may be many of these in a single + * status response */ + switch (segsize) { + case 6:/* only on 'group of 4' */ + dlci = ((u_short) data[2] & 0xff) << 8; + dlci |= (data[3] & 0xff); + if ((dlci < 1024) && (dlci > 0)) { + /* XXX */ + } + break; + case 3: + dlci = ((u_short) data[2] & 0x3f) << 4; + dlci |= ((data[3] & 0x78) >> 3); + if ((dlci < 1024) && (dlci > 0)) { + /* set up the bottom half of the + * support for that dlci if it's not + * already been done */ + /* store this information somewhere */ + } + break; + default: + goto nextIE; + } + if (sc->dlci_state[dlci] != DLCI_UP) { + /* bring new DLCI to life */ + /* may do more here some day */ + if (sc->dlci_state[dlci] != DLCI_DOWN) + log(LOG_INFO, + "nglmi: DLCI %d became active\n", + dlci); + sc->dlci_state[dlci] = DLCI_UP; + } + break; + } +nextIE: + STEPBY(segsize + 2); + } + NG_FREE_DATA(m, meta); + return (0); + +drop: + NG_FREE_DATA(m, meta); + return (EINVAL); +} + +/* + * Check that a packet is entirely kosha. + * return 1 of ok, and 0 if not. + * All data is discarded if a 0 is returned. + */ +static int +nglmi_checkdata(hook_p hook, struct mbuf *m, meta_p meta) +{ + sc_p sc = hook->node->private; + u_char *data; + u_short packetlen; + unsigned short dlci; + u_char type; + u_char nextbyte; + int seq_seen = 0; + int resptype_seen = 0; /* 0 , 1 (partial) or 2 (full) */ + int highest_dlci = 0; + + packetlen = m->m_hdr.mh_len; + data = mtod(m, u_char *); + if (*data != 0x03) { + log(LOG_WARNING, "nglmi: unexpected value in LMI(%d)\n", 1); + goto reject; + } + STEPBY(1); + + /* look at the protocol ID */ + nextbyte = *data; + if (sc->flags & SCF_AUTO) { + SETLMITYPE(sc, SCF_NOLMI); /* start with a clean slate */ + switch (nextbyte) { + case 0x8: + sc->protoID = 8; + break; + case 0x9: + SETLMITYPE(sc, SCF_GROUP4); + sc->protoID = 9; + break; + default: + log(LOG_WARNING, "nglmi: bad Protocol ID(%d)\n", + (int) nextbyte); + goto reject; + } + } else { + if (nextbyte != sc->protoID) { + log(LOG_WARNING, "nglmi: unexpected Protocol ID(%d)\n", + (int) nextbyte); + goto reject; + } + } + STEPBY(1); + + /* check call reference (always null in non ISDN frame relay) */ + if (*data != 0x00) { + log(LOG_WARNING, "nglmi: unexpected Call Reference (0x%x)\n", + data[-1]); + goto reject; + } + STEPBY(1); + + /* check message type */ + switch ((type = *data)) { + case 0x75: /* Status enquiry */ + log(LOG_WARNING, "nglmi: unexpected message type(0x%x)\n", + data[-1]); + goto reject; + case 0x7D: /* Status message */ + break; + default: + log(LOG_WARNING, + "nglmi: unexpected msg type(0x%x) \n", (int) type); + goto reject; + } + STEPBY(1); + + /* Now check if there is a 'locking shift'. This is only seen in + * Annex D frames. Don't increment immediately as it might not be + * there. */ + nextbyte = *data; + if (sc->flags & SCF_AUTO) { + if (!(GROUP4(sc))) { + if (nextbyte == 0x95) { + SETLMITYPE(sc, SCF_ANNEX_D); + STEPBY(1); + } else + SETLMITYPE(sc, SCF_ANNEX_A); + } else if (nextbyte == 0x95) { + log(LOG_WARNING, "nglmi: locking shift seen in G4\n"); + goto reject; + } + } else { + if (ANNEXD(sc)) { + if (*data == 0x95) + STEPBY(1); + else { + log(LOG_WARNING, + "nglmi: locking shift missing\n"); + goto reject; + } + } else if (*data == 0x95) { + log(LOG_WARNING, "nglmi: locking shift seen\n"); + goto reject; + } + } + + /* While there is more data in the status packet, keep processing + * status items. First make sure there is enough data for the + * segment descriptor's length field. */ + while (packetlen >= 2) { + u_int segtype = data[0]; + u_int segsize = data[1]; + + /* Now that we know how long it claims to be, make sure + * there is enough data for the next seg. */ + if (packetlen < (segsize + 2)) { + log(LOG_WARNING, "nglmi: IE longer than packet\n"); + break; + } + switch (segtype) { + case 0x01: + case 0x51: + /* According to MCI's HP analyser, we should just + * ignore if there is mor ethan one of these (?). */ + if (resptype_seen) { + log(LOG_WARNING, "nglmi: dup MSGTYPE\n"); + goto nextIE; + } + if (segsize != 1) { + log(LOG_WARNING, "nglmi: MSGTYPE wrong size\n"); + goto reject; + } + /* The remote end tells us what kind of response + * this is. Only expect a type 0 or 1. if it was a + * full (type 0) check we just asked for a type + * full. */ + switch (data[2]) { + case 1:/* partial */ + if (sc->livs > sc->liv_per_full) { + log(LOG_WARNING, + "nglmi: LIV when FULL expected\n"); + goto reject; /* need full */ + } + resptype_seen = 1; + break; + case 0:/* full */ + /* Full response is always acceptable */ + resptype_seen = 2; + break; + default: + log(LOG_WARNING, + "nglmi: Unknown report type %d\n", data[2]); + goto reject; + } + break; + case 0x03: + case 0x53: + /* The remote tells us what it thinks the sequence + * numbers are. I would have thought that there + * needs to be one and only one of these, but MCI + * want us to just ignore extras. (?) */ + if (resptype_seen == 0) { + log(LOG_WARNING, "nglmi: no TYPE before SEQ\n"); + goto reject; + } + if (seq_seen != 0) /* already seen seq numbers */ + goto nextIE; + if (segsize != 2) { + log(LOG_WARNING, "nglmi: bad SEQ sts size\n"); + goto reject; + } + if (sc->local_seq != data[3]) { + log(LOG_WARNING, "nglmi: unexpected SEQ\n"); + goto reject; + } + seq_seen = 1; + break; + case 0x07: + case 0x57: + /* The remote tells us about a DLCI that it knows + * about. There may be many of these in a single + * status response */ + if (seq_seen != 1) { /* already seen seq numbers? */ + log(LOG_WARNING, + "nglmi: No sequence before DLCI\n"); + goto reject; + } + if (resptype_seen != 2) { /* must be full */ + log(LOG_WARNING, + "nglmi: No resp type before DLCI\n"); + goto reject; + } + if (GROUP4(sc)) { + if (segsize != 6) { + log(LOG_WARNING, + "nglmi: wrong IE segsize\n"); + goto reject; + } + dlci = ((u_short) data[2] & 0xff) << 8; + dlci |= (data[3] & 0xff); + } else { + if (segsize != 3) { + log(LOG_WARNING, + "nglmi: DLCI headersize of %d" + " not supported\n", segsize - 1); + goto reject; + } + dlci = ((u_short) data[2] & 0x3f) << 4; + dlci |= ((data[3] & 0x78) >> 3); + } + /* async can only have one of these */ +#if 0 /* async not yet accepted */ + if (async && highest_dlci) { + log(LOG_WARNING, + "nglmi: Async with > 1 DLCI\n"); + goto reject; + } +#endif + /* Annex D says these will always be Ascending, but + * the HP test for G4 says we should accept + * duplicates, so for now allow that. ( <= vs. < ) */ +#if 0 + /* MCI tests want us to accept out of order for AnxD */ + if ((!GROUP4(sc)) && (dlci < highest_dlci)) { + /* duplicate or mis-ordered dlci */ + /* (spec says they will increase in number) */ + log(LOG_WARNING, "nglmi: DLCI out of order\n"); + goto reject; + } +#endif + if (dlci > 1023) { + log(LOG_WARNING, "nglmi: DLCI out of range\n"); + goto reject; + } + highest_dlci = dlci; + break; + default: + log(LOG_WARNING, + "nglmi: unknown LMI segment type %d\n", segtype); + } +nextIE: + STEPBY(segsize + 2); + } + if (packetlen != 0) { /* partial junk at end? */ + log(LOG_WARNING, + "nglmi: %d bytes extra at end of packet\n", packetlen); + goto print; + } + if (resptype_seen == 0) { + log(LOG_WARNING, "nglmi: No response type seen\n"); + goto reject; /* had no response type */ + } + if (seq_seen == 0) { + log(LOG_WARNING, "nglmi: No sequence numbers seen\n"); + goto reject; /* had no sequence numbers */ + } + return (1); + +print: + { + int i, j, k, pos; + char buf[100]; + int loc; + u_char *bp = mtod(m, u_char *); + + k = i = 0; + loc = (m->m_hdr.mh_len - packetlen); + log(LOG_WARNING, "nglmi: error at location %d\n", loc); + while (k < m->m_hdr.mh_len) { + pos = 0; + j = 0; + while ((j++ < 16) && k < m->m_hdr.mh_len) { + pos += sprintf(buf + pos, "%c%02x", + ((loc == k) ? '>' : ' '), + bp[k]); + k++; + } + if (i == 0) + log(LOG_WARNING, "nglmi: packet data:%s\n", buf); + else + log(LOG_WARNING, "%04d :%s\n", k, buf); + i++; + } + } + return (1); +reject: + { + int i, j, k, pos; + char buf[100]; + int loc; + u_char *bp = mtod(m, u_char *); + + k = i = 0; + loc = (m->m_hdr.mh_len - packetlen); + log(LOG_WARNING, "nglmi: error at location %d\n", loc); + while (k < m->m_hdr.mh_len) { + pos = 0; + j = 0; + while ((j++ < 16) && k < m->m_hdr.mh_len) { + pos += sprintf(buf + pos, "%c%02x", + ((loc == k) ? '>' : ' '), + bp[k]); + k++; + } + if (i == 0) + log(LOG_WARNING, "nglmi: packet data:%s\n", buf); + else + log(LOG_WARNING, "%04d :%s\n", k, buf); + i++; + } + } + NG_FREE_DATA(m, meta); + return (0); +} + +/* + * Do local shutdown processing.. + * Cut any remaining links and free our local resources. + */ +static int +nglmi_rmnode(node_p node) +{ + const sc_p sc = node->private; + + node->flags |= NG_INVALID; + ng_cutlinks(node); + ng_unname(node); + node->private = NULL; + ng_unref(sc->node); + FREE(sc, M_NETGRAPH); + return (0); +} + +/* + * Hook disconnection + * For this type, removal of any link except "debug" destroys the node. + */ +static int +nglmi_disconnect(hook_p hook) +{ + const sc_p sc = hook->node->private; + + /* OK to remove debug hook(s) */ + if (hook->private == NULL) + return (0); + + /* Stop timer if it's currently active */ + if (sc->flags & SCF_CONNECTED) + untimeout(LMI_ticker, sc, sc->handle); + + /* Self-destruct */ + ng_rmnode(hook->node); + return (0); +} + diff --git a/sys/netgraph/ng_lmi.h b/sys/netgraph/ng_lmi.h new file mode 100644 index 0000000..16f2b5b --- /dev/null +++ b/sys/netgraph/ng_lmi.h @@ -0,0 +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 <archie@whistle.com> + * + * $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_ */ diff --git a/sys/netgraph/ng_message.h b/sys/netgraph/ng_message.h new file mode 100644 index 0000000..8ff46b2 --- /dev/null +++ b/sys/netgraph/ng_message.h @@ -0,0 +1,209 @@ + +/* + * 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 <julian@whistle.com> + * + * $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 */ +}; +#define NG_VERSION 1 +#define NGF_ORIG 0x0000 /* the msg is the original request */ +#define NGF_RESP 0x0001 /* the message is a response */ + +/* + * 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 0x0001 /* no args */ +#define NGM_MKPEER 0x0002 +#define NGM_CONNECT 0x0003 +#define NGM_NAME 0x0004 +#define NGM_RMHOOK 0x0005 +#define NGM_NODEINFO 0x0006 /* get nodeinfo for the target */ +#define NGM_LISTHOOKS 0x0007 /* get nodeinfo for the target + hook info */ +#define NGM_LISTNAMES 0x0008 /* list all globally named nodes */ +#define NGM_LISTNODES 0x0009 /* list all nodes, named and unnamed */ +#define NGM_LISTTYPES 0x000a /* list all installed node types */ +#define NGM_TEXT_STATUS 0x000b /* (optional) returns human readable status */ + +/* + * Args sections for generic NG commands. All strings are NUL-terminated. + */ +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 */ +}; + +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 */ +}; + +struct ngm_name { + char name[NG_NODELEN + 1]; /* node name */ +}; + +struct ngm_rmhook { + char ourhook[NG_HOOKLEN + 1]; /* hook name */ +}; + +/* Structures used in response to NGM_NODEINFO and NGM_LISTHOOKS */ +struct nodeinfo { + char name[NG_NODELEN + 1]; /* node name (if any) */ + char type[NG_TYPELEN + 1]; /* peer type */ + u_int32_t id; /* unique identifier */ + u_int32_t hooks; /* number of active hooks */ +}; + +struct linkinfo { + char ourhook[NG_HOOKLEN + 1]; /* hook name */ + char peerhook[NG_HOOKLEN + 1]; /* peer hook */ + struct nodeinfo nodeinfo; +}; + +struct hooklist { + struct nodeinfo nodeinfo; /* node information */ + struct linkinfo link[0]; /* info about each hook */ +}; + +/* Structure used for NGM_LISTNAMES/NGM_LISTNODES (not node specific) */ +struct namelist { + u_int32_t numnames; + struct nodeinfo nodeinfo[0]; +}; + +/* Structures used for NGM_LISTTYPES (not node specific) */ +struct typeinfo { + char typename[NG_TYPELEN + 1]; /* name of type */ + u_int32_t numnodes; /* number alive */ +}; + +struct typelist { + u_int32_t numtypes; + struct typeinfo typeinfo[0]; +}; + +/* + * 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 <sys/ioccom.h>. + */ + +#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_ */ + diff --git a/sys/netgraph/ng_ppp.c b/sys/netgraph/ng_ppp.c new file mode 100644 index 0000000..3377a80 --- /dev/null +++ b/sys/netgraph/ng_ppp.c @@ -0,0 +1,406 @@ + +/* + * ng_ppp.c + * + * Copyright (c) 1996-1999 Whistle Communications, Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Whistle Communications; + * provided, however, that: + * 1. Any and all reproductions of the source or object code must include the + * copyright notice above and the following disclaimer of warranties; and + * 2. No rights are granted, in any manner or form, to use Whistle + * Communications, Inc. trademarks, including the mark "WHISTLE + * COMMUNICATIONS" on advertising, endorsements, or otherwise except as + * such appears in the above copyright notice or in the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY + * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS + * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. + * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Archie Cobbs <archie@whistle.com> + * + * $FreeBSD$ + * $Whistle: ng_ppp.c,v 1.22 1999/01/28 23:54:53 julian Exp $ + */ + +/* + * This node does PPP protocol multiplexing based on PPP protocol + * ID numbers. This node does not add address and control fields, + * as that is considered a ``device layer'' issue. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/errno.h> +#include <sys/socket.h> +#include <sys/syslog.h> + +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> +#include <netgraph/ng_ppp.h> + +/* Protocol stuff */ +#define PROT_DOWNLINK 0xffff +#define PROT_BYPASS 0x0000 + +#define PROT_VALID(p) (((p) & 0x0101) == 0x0001) +#define PROT_COMPRESSIBLE(p) (((p) & 0xFF00) == 0x0000) + +/* Extract protocol from hook private pointer */ +#define HOOK_PROTO(hook) (*((u_int16_t *) &hook->private)) + +/* Node private data */ +struct private { + struct ng_ppp_stat stats; + u_int protocomp:1; +}; +typedef struct private *priv_p; + +/* Protocol aliases */ +struct protoalias { + char *name; + u_int16_t proto; +}; + +/* Netgraph node methods */ +static int ng_ppp_constructor(node_p *nodep); +static int ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg, + const char *retaddr, struct ng_mesg **resp); +static int ng_ppp_rmnode(node_p node); +static int ng_ppp_newhook(node_p node, hook_p hook, const char *name); +static int ng_ppp_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); +static int ng_ppp_disconnect(hook_p hook); + +/* Helper stuff */ +static int ng_ppp_decodehookname(const char *name); +static hook_p ng_ppp_findhook(node_p node, int proto); + +/* Node type descriptor */ +static struct ng_type typestruct = { + NG_VERSION, + NG_PPP_NODE_TYPE, + NULL, + ng_ppp_constructor, + ng_ppp_rcvmsg, + ng_ppp_rmnode, + ng_ppp_newhook, + NULL, + NULL, + ng_ppp_rcvdata, + ng_ppp_rcvdata, + ng_ppp_disconnect +}; +NETGRAPH_INIT(ppp, &typestruct); + +/* Protocol aliases */ +static const struct protoalias gAliases[] = +{ + { NG_PPP_HOOK_DOWNLINK, PROT_DOWNLINK }, + { NG_PPP_HOOK_BYPASS, PROT_BYPASS }, + { NG_PPP_HOOK_LCP, 0xc021 }, + { NG_PPP_HOOK_IPCP, 0x8021 }, + { NG_PPP_HOOK_ATCP, 0x8029 }, + { NG_PPP_HOOK_CCP, 0x80fd }, + { NG_PPP_HOOK_ECP, 0x8053 }, + { NG_PPP_HOOK_IP, 0x0021 }, + { NG_PPP_HOOK_VJCOMP, 0x002d }, + { NG_PPP_HOOK_VJUNCOMP, 0x002f }, + { NG_PPP_HOOK_MP, 0x003d }, + { NG_PPP_HOOK_COMPD, 0x00fd }, + { NG_PPP_HOOK_CRYPTD, 0x0053 }, + { NG_PPP_HOOK_PAP, 0xc023 }, + { NG_PPP_HOOK_CHAP, 0xc223 }, + { NG_PPP_HOOK_LQR, 0xc025 }, + { NULL, 0 } +}; + +#define ERROUT(x) do { error = (x); goto done; } while (0) + +/************************************************************************ + NETGRAPH NODE STUFF + ************************************************************************/ + +/* + * Node constructor + */ +static int +ng_ppp_constructor(node_p *nodep) +{ + priv_p priv; + int error; + + /* Allocate private structure */ + MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK); + if (priv == NULL) + return (ENOMEM); + bzero(priv, sizeof(*priv)); + + /* Call generic node constructor */ + if ((error = ng_make_node_common(&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_ppp_newhook(node_p node, hook_p hook, const char *name) +{ + const priv_p priv = node->private; + int proto; + + /* Decode protocol number */ + if ((proto = ng_ppp_decodehookname(name)) < 0) + return (EINVAL); + + /* See if already connected */ + if (ng_ppp_findhook(node, proto) != NULL) + return (EISCONN); + + /* Clear stats when downstream hook reconnected */ + if (proto == PROT_DOWNLINK) + bzero(&priv->stats, sizeof(priv->stats)); + + /* OK */ + HOOK_PROTO(hook) = proto; + 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_PROTOCOMP: + if (msg->header.arglen < sizeof(int)) + ERROUT(EINVAL); + priv->protocomp = !!*((int *) msg->data); + break; + case NGM_PPP_GET_STATS: + NG_MKRESPONSE(resp, msg, sizeof(priv->stats), M_NOWAIT); + if (resp == NULL) + ERROUT(ENOMEM); + *((struct ng_ppp_stat *) resp->data) = priv->stats; + break; + case NGM_PPP_CLR_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 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; + u_int16_t proto = HOOK_PROTO(hook); + int error = 0; + + switch (proto) { + + /* Prepend the (possibly compressed) protocol number */ + default: + { + int psize = (priv->protocomp + && PROT_COMPRESSIBLE(proto)) ? 1 : 2; + + M_PREPEND(m, psize, M_NOWAIT); + if (!m || !(m = m_pullup(m, psize))) + ERROUT(ENOBUFS); + if (psize == 1) + *mtod(m, u_char *) = proto; + else + *mtod(m, u_short *) = htons(proto); + hook = ng_ppp_findhook(node, PROT_DOWNLINK); + break; + } + + /* Extract the protocol number and direct to the corresponding hook */ + case PROT_DOWNLINK: + { + /* Stats */ + priv->stats.recvFrames++; + priv->stats.recvOctets += m->m_pkthdr.len; + + /* Extract protocol number */ + for (proto = 0; + !PROT_VALID(proto); + proto = (proto << 8) + *mtod(m, u_char *), m_adj(m, 1)) { + if (m == NULL) { + priv->stats.badProto++; + ERROUT(EINVAL); + } + if ((m = m_pullup(m, 1)) == NULL) + ERROUT(ENOBUFS); + } + + /* Find corresponding hook; if none, use the "unhooked" + hook and leave the two-byte protocol prepended */ + if ((hook = ng_ppp_findhook(node, proto)) == NULL) { + priv->stats.unknownProto++; + hook = ng_ppp_findhook(node, PROT_BYPASS); + M_PREPEND(m, 2, M_NOWAIT); + if (m == NULL || (m = m_pullup(m, 2)) == NULL) + ERROUT(ENOBUFS); + *mtod(m, u_short *) = htons(proto); + } + break; + } + + /* Send raw data from "unhooked" hook as-is; we assume the + protocol is already prepended */ + case PROT_BYPASS: + hook = ng_ppp_findhook(node, PROT_DOWNLINK); + break; + } + + /* Stats */ + if (m != NULL && hook != NULL && HOOK_PROTO(hook) == PROT_DOWNLINK) { + priv->stats.xmitFrames++; + priv->stats.xmitOctets += m->m_pkthdr.len; + } + + /* Forward packet on hook */ + NG_SEND_DATA(error, hook, m, meta); + return (error); + +done: + /* Something went wrong */ + NG_FREE_DATA(m, meta); + return (error); +} + +/* + * Destroy node + */ +static int +ng_ppp_rmnode(node_p node) +{ + const priv_p priv = node->private; + + /* Take down netgraph node */ + node->flags |= NG_INVALID; + ng_cutlinks(node); + ng_unname(node); + bzero(priv, sizeof(*priv)); + FREE(priv, M_NETGRAPH); + node->private = NULL; + ng_unref(node); /* let the node escape */ + return (0); +} + +/* + * Hook disconnection + */ +static int +ng_ppp_disconnect(hook_p hook) +{ + if (hook->node->numhooks == 0) + ng_rmnode(hook->node); + return (0); +} + +/************************************************************************ + HELPER STUFF + ************************************************************************/ + +/* + * Decode ASCII protocol name + */ +static int +ng_ppp_decodehookname(const char *name) +{ + int k, proto; + + for (k = 0; gAliases[k].name; k++) + if (!strcmp(gAliases[k].name, name)) + return (gAliases[k].proto); + if (strlen(name) != 6 || name[0] != '0' || name[1] != 'x') + return (-1); + for (proto = k = 2; k < 6; k++) { + const u_char ch = name[k] | 0x20; + int dig; + + if (ch >= '0' && ch <= '9') + dig = ch - '0'; + else if (ch >= 'a' && ch <= 'f') + dig = ch - 'a' + 10; + else + return (-1); + proto = (proto << 4) + dig; + } + if (!PROT_VALID(proto)) + return(-1); + return (proto); +} + +/* + * Find a hook by protocol number + */ +static hook_p +ng_ppp_findhook(node_p node, int proto) +{ + hook_p hook; + + LIST_FOREACH(hook, &node->hooks, hooks) { + if (HOOK_PROTO(hook) == proto) + return (hook); + } + return (NULL); +} + diff --git a/sys/netgraph/ng_ppp.h b/sys/netgraph/ng_ppp.h new file mode 100644 index 0000000..06a6ddd --- /dev/null +++ b/sys/netgraph/ng_ppp.h @@ -0,0 +1,91 @@ + +/* + * ng_ppp.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 <archie@whistle.com> + * + * $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 860635544 + +/* Hook names */ +#define NG_PPP_HOOK_DOWNLINK "downlink" /* downstream hook */ +#define NG_PPP_HOOK_BYPASS "bypass" /* any unhooked protocol */ + +/* Netgraph commands */ +enum { + NGM_PPP_SET_PROTOCOMP = 1, /* takes an integer 0 or 1 */ + NGM_PPP_GET_STATS, /* returns struct ng_ppp_stat */ + NGM_PPP_CLR_STATS, /* clear stats */ +}; + +/* Statistics struct */ +struct ng_ppp_stat { + u_int32_t xmitFrames; /* xmit frames on "downstream" */ + u_int32_t xmitOctets; /* xmit octets on "downstream" */ + u_int32_t recvFrames; /* recv frames on "downstream" */ + u_int32_t recvOctets; /* recv octets on "downstream" */ + u_int32_t badProto; /* frames with invalid protocol */ + u_int32_t unknownProto; /* frames sent to "unhooked" */ +}; + +/* + * We recognize these hook names for some various PPP protocols. But we + * always recognize the hook name "0xNNNN" for any protocol, including these. + * So these are really just alias hook names. + */ +#define NG_PPP_HOOK_LCP "lcp" /* 0xc021 */ +#define NG_PPP_HOOK_IPCP "ipcp" /* 0x8021 */ +#define NG_PPP_HOOK_ATCP "atcp" /* 0x8029 */ +#define NG_PPP_HOOK_CCP "ccp" /* 0x80fd */ +#define NG_PPP_HOOK_ECP "ecp" /* 0x8053 */ +#define NG_PPP_HOOK_IP "ip" /* 0x0021 */ +#define NG_PPP_HOOK_VJCOMP "vjcomp" /* 0x002d */ +#define NG_PPP_HOOK_VJUNCOMP "vjuncomp" /* 0x002f */ +#define NG_PPP_HOOK_MP "mp" /* 0x003d */ +#define NG_PPP_HOOK_COMPD "compd" /* 0x00fd */ +#define NG_PPP_HOOK_CRYPTD "cryptd" /* 0x0053 */ +#define NG_PPP_HOOK_PAP "pap" /* 0xc023 */ +#define NG_PPP_HOOK_CHAP "chap" /* 0xc223 */ +#define NG_PPP_HOOK_LQR "lqr" /* 0xc025 */ + +#endif /* _NETGRAPH_PPP_H_ */ diff --git a/sys/netgraph/ng_pppoe.c b/sys/netgraph/ng_pppoe.c new file mode 100644 index 0000000..b59dbb3 --- /dev/null +++ b/sys/netgraph/ng_pppoe.c @@ -0,0 +1,1343 @@ + +/* + * 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 <julian@whistle.com> + * + * $FreeBSD$ + * $Whistle: ng_pppoe.c,v 1.7 1999/10/16 10:16:43 julian Exp $ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/errno.h> +#include <sys/syslog.h> +#include <net/ethernet.h> + +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> +#include <netgraph/ng_pppoe.h> + +/* + * This section contains the netgraph method declarations for the + * sample node. These methods define the netgraph 'type'. + */ + +static int ng_PPPoE_constructor(node_p *node); +static int ng_PPPoE_rcvmsg(node_p node, struct ng_mesg *msg, + const char *retaddr, struct ng_mesg **resp); +static int ng_PPPoE_rmnode(node_p node); +static int ng_PPPoE_newhook(node_p node, hook_p hook, const char *name); +static int ng_PPPoE_connect(hook_p hook); +static int ng_PPPoE_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); +static int ng_PPPoE_disconnect(hook_p hook); + +/* Netgraph node type descriptor */ +static struct ng_type typestruct = { + NG_VERSION, + NG_PPPOE_NODE_TYPE, + NULL, + ng_PPPoE_constructor, + ng_PPPoE_rcvmsg, + ng_PPPoE_rmnode, + ng_PPPoE_newhook, + NULL, + ng_PPPoE_connect, + ng_PPPoE_rcvdata, + ng_PPPoE_rcvdata, + ng_PPPoE_disconnect +}; +NETGRAPH_INIT(PPPoE, &typestruct); + +/* + * States for the session state machine. + * These have no meaning if there is no hook attached yet. + */ +enum state { + PPPOE_SNONE=0, /* [both] Initial state */ + PPPOE_SINIT, /* [Client] Sent discovery initiation */ + PPPOE_PRIMED, /* [Server] Sent offer message */ + PPPOE_SOFFER, /* [Server] Sent offer message */ + PPPOE_SREQ, /* [Client] Sent a Request */ + PPPOE_LISTENING, /* [Server] Listening for discover initiation msg */ + PPPOE_NEWCONNECTED, /* [Both] Connection established, No data received */ + PPPOE_CONNECTED, /* [Both] Connection established, Data received */ + PPPOE_DEAD /* [Both] */ +}; +/* + * Events for the state machine + */ +enum event { + PPPOE_TIMEOUT, /* It's time to do something */ + PPPOE_PACKET, /* a packet has been received. */ + PPPOE_CLOSE /* start shutdown processing */ +}; + +#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); + +/************************************************************************* + * Some basic utilities from the Linux version with author's permission.* + * Author: Michal Ostrowski <mostrows@styx.uwaterloo.ca> * + ************************************************************************/ + +/* + * 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; + +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. + */ + 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) +{ + 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; + + if((neg = sp->neg) == NULL) { + printf("pppoe: asked to use NULL neg pointer\n"); + return; + } + if ((i = neg->numtags++) < NUMTAGS) { + neg->tags[i] = tp; + } else { + printf("pppoe: asked to add too many tags to packet\n"); + } +} + +/* + * Make up a packet, using the tags filled out for the session. + * + * Assume that the actual pppoe header and ethernet header + * are filled out externally to this routine. + * Also assume that neg->wh points to the correct + * location at the front of the buffer space. + */ +static void +make_packet(sessp sp) { + struct pppoe_full_hdr *wh = &sp->neg->pkt->pkt_header; + struct pppoe_tag **tag; + char *dp; + int count; + int tlen; + u_int16_t length = 0; + + 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; + + 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 = wh->ph.sid; + + /* + * find matching peer/session combination. + */ + 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; + + 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; + + /* 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; + + 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; + callout_handle_init( &sp->neg->timeout_handle); + } + 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; + + /* 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) { + 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) { + m_freem(neg->m); + FREE(neg, M_NETGRAPH); + LEAVE(ENOBUFS); + } + sp->neg = neg; + 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); + bcopy(ourmsg->data, + neg->service.data, ourmsg->data_len); + neg->service_len = ourmsg->data_len; + neg->pkt->pkt_header.ph.code = PADI_CODE; + 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); + 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); + 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); +} + +static void +pppoe_start(sessp sp) +{ + struct { + struct pppoe_tag hdr; + union uniq data; + } uniqtag; + + /* + * kick the state machine into starting up + */ + sp->state = PPPOE_SINIT; + uniqtag.hdr.tag_type = PTT_HOST_UNIQ; + uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(uniqtag.data)); + uniqtag.data.pointer = sp; + init_tags(sp); + insert_tag(sp, &uniqtag.hdr); + insert_tag(sp, &sp->neg->service.hdr); + make_packet(sp); + sendpacket(sp); +} + +/* + * Receive data, and do something with it. + * The caller will never free m or meta, so + * if we use up this data or abort we must free BOTH of these. + */ +static int +ng_PPPoE_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) +{ + node_p node = hook->node; + const priv_p privp = node->private; + sessp sp = hook->private; + struct pppoe_full_hdr *wh; + struct pppoe_hdr *ph; + int error = 0; + u_int16_t session; + u_int16_t length; + u_int8_t code; + struct pppoe_tag *tag = NULL; + hook_p sendhook; + struct { + struct pppoe_tag hdr; + union uniq data; + } uniqtag; + negp neg = NULL; + + 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++; + m_pullup(m, sizeof(*wh)); /* Checks length */ + 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; + switch(ntohs(wh->eh.ether_type)) { + case ETHERTYPE_PPPOE_DISC: + /* + * We need to try make sure that the tag area + * is contiguous, or we could wander of the end + * of a buffer and make a mess. + * (Linux wouldn't have this problem). + */ + 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"); + } + + 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) { + 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 { + LEAVE(ENETUNREACH); + } + break; + case PADO_CODE: + /* + * We are a client: + * Use the host_uniq tag to find the + * hook this is in response to. + * + * For now simply accept the first we receive. + */ + tag = get_tag(ph, PTT_HOST_UNIQ); + if ((tag == NULL) + || (ntohs(tag->tag_len) != sizeof(sp))) { + LEAVE(ENETUNREACH); + } + + sendhook = pppoe_finduniq(node, tag); + if (sendhook == NULL) { + LEAVE(ENETUNREACH); + } + + /* + * Check the session is in the right state. + * It needs to be in PPPOE_SINIT. + */ + sp = sendhook->private; + if (sp->state != PPPOE_SINIT) { + LEAVE(ENETUNREACH); + } + neg = sp->neg; + untimeout(pppoe_ticker, sendhook, + neg->timeout_handle); + + /* + * This is the first time we hear + * from the server, so note it's + * unicast address, replacing the + * broadcast address . + */ + bcopy(wh->eh.ether_shost, + neg->pkt->pkt_header.eh.ether_dhost, + ETHER_ADDR_LEN); + neg->timeout = 0; + neg->pkt->pkt_header.ph.code = PADR_CODE; + init_tags(sp); + insert_tag(sp,&neg->service.hdr); /* Service */ + insert_tag(sp, tag); /* Host Unique */ + tag = get_tag(ph, PTT_AC_COOKIE); + insert_tag(sp, tag); /* returned cookie */ + scan_tags(sp, ph); + make_packet(sp); + sp->state = PPPOE_SREQ; + sendpacket(sp); + break; + case PADR_CODE: + + /* + * We are a server: + * Use the ac_cookie tag to find the + * hook this is in response to. + */ + tag = get_tag(ph, PTT_AC_COOKIE); + if ((tag == NULL) + || (ntohs(tag->tag_len) != sizeof(sp))) { + LEAVE(ENETUNREACH); + } + + sendhook = pppoe_finduniq(node, tag); + if (sendhook == NULL) { + LEAVE(ENETUNREACH); + } + + /* + * Check the session is in the right state. + * It needs to be in PPPOE_SOFFER + * or PPPOE_NEWCONNECTED. If the latter, + * then this is a retry by the client. + * so be nice, and resend. + */ + sp = sendhook->private; + if (sp->state == PPPOE_NEWCONNECTED) { + /* + * Whoa! drop back to resend that + * PADS packet. + * We should still have a copy of it. + */ + sp->state = PPPOE_SOFFER; + } + if (sp->state != PPPOE_SOFFER) { + LEAVE (ENETUNREACH); + break; + } + neg = sp->neg; + untimeout(pppoe_ticker, sendhook, + neg->timeout_handle); + neg->pkt->pkt_header.ph.code = PADS_CODE; + if (sp->Session_ID == 0) + neg->pkt->pkt_header.ph.sid = + sp->Session_ID = get_new_sid(node); + neg->timeout = 0; + /* + * start working out the tags to respond with. + */ + init_tags(sp); + insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ + insert_tag(sp, tag); /* ac_cookie */ + tag = get_tag(ph, PTT_SRV_NAME); + insert_tag(sp, tag); /* returned service */ + tag = get_tag(ph, PTT_HOST_UNIQ); + insert_tag(sp, tag); /* returned hostuniq */ + scan_tags(sp, ph); + make_packet(sp); + sp->state = PPPOE_NEWCONNECTED; + sendpacket(sp); + /* + * 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; + break; + case PADS_CODE: + /* + * We are a client: + * Use the host_uniq tag to find the + * hook this is in response to. + * take the session ID and store it away. + * Also make sure the pre-made header is + * correct and set us into Session mode. + */ + tag = get_tag(ph, PTT_HOST_UNIQ); + if ((tag == NULL) + || (ntohs(tag->tag_len) != sizeof(sp))) { + LEAVE (ENETUNREACH); + break; + } + + sendhook = pppoe_finduniq(node, tag); + if (sendhook == NULL) { + LEAVE(ENETUNREACH); + } + + /* + * Check the session is in the right state. + * It needs to be in PPPOE_SREQ. + */ + sp = sendhook->private; + if (sp->state != PPPOE_SREQ) { + LEAVE(ENETUNREACH); + } + neg = sp->neg; + untimeout(pppoe_ticker, sendhook, + neg->timeout_handle); + sp->Session_ID = wh->ph.sid; + neg->timeout = 0; + sp->state = PPPOE_CONNECTED; + sendpacket(sp); + /* + * 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; + 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 */ + 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; + } + m_adj(m, sizeof(*wh)); + if (m->m_pkthdr.len < length) { + /* Packet too short, dump it */ + LEAVE(EMSGSIZE); + } + /* XXX also need to trim excess at end I should think */ + if ( sp->state != PPPOE_CONNECTED) { + if (sp->state == PPPOE_NEWCONNECTED) { + sp->state = PPPOE_CONNECTED; + /* + * Now we have gone to Connected mode, + * Free all resources needed for + * negotiation. + */ + m_freem(sp->neg->m); + FREE(sp->neg, M_NETGRAPH); + sp->neg = NULL; + } else { + LEAVE (ENETUNREACH); + break; + } + } + NG_SEND_DATA( error, sendhook, m, meta); + break; + default: + LEAVE(EPFNOSUPPORT); + } + } else { + /* + * Not ethernet or debug hook.. + * + * The packet has come in on a normal hook. + * We need to find out what kind of hook, + * So we can decide how to handle it. + * Check the hook's state. + */ + sp = hook->private; + switch (sp->state) { + case PPPOE_NEWCONNECTED: + case PPPOE_CONNECTED: { + struct pppoe_full_hdr *wh; + /* + * Bang in a pre-made header, and set the length up + * to be correct. Then send it to the ethernet driver. + */ + M_PREPEND(m, sizeof(*wh), M_DONTWAIT); + if (m == NULL) { + LEAVE(ENOBUFS); + } + wh = mtod(m, struct pppoe_full_hdr *); + bcopy(&sp->pkt_hdr, wh, sizeof(*wh)); + wh->ph.length = htons((short)(m->m_pkthdr.len)); + NG_SEND_DATA( error, privp->ethernet_hook, m, meta); + privp->packets_out++; + break; + } + case PPPOE_PRIMED: + /* + * A PADI packet is being returned by the application + * that has set up this hook. This indicates that it + * wants us to offer service. + */ + neg = sp->neg; + m_pullup(m, sizeof(*wh)); /* Checks length */ + if (m == 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; + + /* + * This is the first time we hear + * from the client, so note it's + * unicast address, replacing the + * broadcast address . + */ + bcopy(wh->eh.ether_shost, + neg->pkt->pkt_header.eh.ether_dhost, + ETHER_ADDR_LEN); + sp->state = PPPOE_SOFFER; + neg->timeout = 0; + neg->pkt->pkt_header.ph.code = PADO_CODE; + + /* + * start working out the tags to respond with. + */ + uniqtag.hdr.tag_type = PTT_AC_COOKIE; + uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp)); + uniqtag.data.pointer = sp; + init_tags(sp); + insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ + insert_tag(sp, tag); /* returned hostunique */ + insert_tag(sp, &uniqtag.hdr); /* AC cookie */ + tag = get_tag(ph, PTT_SRV_NAME); + insert_tag(sp, tag); /* returned service */ + /* XXX maybe put the tag in the session store */ + scan_tags(sp, ph); + make_packet(sp); + sendpacket(sp); + break; + + /* + * Packets coming from the hook make no sense + * to sessions in these states. Throw them away. + */ + case PPPOE_SINIT: + case PPPOE_SREQ: + case PPPOE_SOFFER: + case PPPOE_SNONE: + case PPPOE_LISTENING: + case PPPOE_DEAD: + default: + LEAVE(ENETUNREACH); + } + } +quit: + NG_FREE_DATA(m, meta); + return error; +} + +/* + * Do local shutdown processing.. + * If we are a persistant device, we might refuse to go away, and + * we'd only remove our links and reset ourself. + */ +static int +ng_PPPoE_rmnode(node_p node) +{ + const priv_p privdata = node->private; + + node->flags |= NG_INVALID; + ng_cutlinks(node); + ng_unname(node); + node->private = NULL; + ng_unref(privdata->node); + FREE(privdata, M_NETGRAPH); + return (0); +} + +/* + * This is called once we've already connected a new hook to the other node. + * It gives us a chance to balk at the last minute. + */ +static int +ng_PPPoE_connect(hook_p hook) +{ + /* be really amiable and just say "YUP that's OK by me! " */ + return (0); +} + +/* + * Hook disconnection + * + * Clean up all dangling links and infirmation about the session/hook. + * For this type, removal of the last link destroys the node + */ +static int +ng_PPPoE_disconnect(hook_p hook) +{ + node_p node = hook->node; + priv_p privp = node->private; + sessp sp; + + if (hook->private == &privp->debug_hook) { + privp->debug_hook = NULL; + } else if (hook->private == &privp->ethernet_hook) { + privp->ethernet_hook = NULL; + } else { + sp = hook->private; + untimeout(pppoe_ticker, hook, sp->neg->timeout_handle); + FREE(sp, M_NETGRAPH); + } + 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; + + switch(sp->state) { + /* + * resend the last packet, using an exponential backoff. + * After a period of time, stop growing the backoff, + * and either leave it, or reverst to the start. + */ + case PPPOE_SINIT: + case PPPOE_SREQ: + /* timeouts on these produce resends */ + m0 = m_copypacket(sp->neg->m, M_DONTWAIT); + NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy); + neg->timeout_handle = timeout(pppoe_ticker, + hook, neg->timeout * hz); + if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) { + if (sp->state == PPPOE_SREQ) { + /* revert to SINIT mode */ + } else { + neg->timeout = PPPOE_TIMEOUT_LIMIT; + } + } + break; + case PPPOE_PRIMED: + case PPPOE_SOFFER: + /* a timeout on these says "give up" */ + /* XXX should notify creator */ + 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; + + switch(sp->state) { + case PPPOE_LISTENING: + case PPPOE_DEAD: + case PPPOE_SNONE: + case PPPOE_NEWCONNECTED: + case PPPOE_CONNECTED: + printf("pppoe: timeout: unexpected state\n"); + break; + + case PPPOE_PRIMED: + /* No packet to send, but set up the timeout */ + neg->timeout_handle = timeout(pppoe_ticker, + hook, PPPOE_OFFER_TIMEOUT * hz); + break; + + case PPPOE_SOFFER: + /* + * send the offer but if they don't respond + * in PPPOE_OFFER_TIMEOUT seconds, forget about it. + */ + m0 = m_copypacket(sp->neg->m, M_DONTWAIT); + NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy); + neg->timeout_handle = timeout(pppoe_ticker, + hook, PPPOE_OFFER_TIMEOUT * hz); + break; + + case PPPOE_SINIT: + case PPPOE_SREQ: + m0 = m_copypacket(sp->neg->m, M_DONTWAIT); + NG_SEND_DATA( error, sp->hook, m0, dummy); + neg->timeout_handle = timeout(pppoe_ticker, hook, hz); + neg->timeout = 2; + break; + + default: + error = EINVAL; + printf("pppoe: timeout: bad state\n"); + } + /* return (error); */ +} + +/* + * Parse an incoming packet to see if any tags should be copied to the + * output packet. DOon't do any tags that are likely to have been + * handles a the main state machine. + */ +static struct pppoe_tag* +scan_tags(sessp sp, struct pppoe_hdr* ph) +{ + char *end = (char *)next_tag(ph); + char *ptn; + struct pppoe_tag *pt = &ph->tag[0]; + /* + * Keep processing tags while a tag header will still fit. + */ + 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; +} + diff --git a/sys/netgraph/ng_pppoe.h b/sys/netgraph/ng_pppoe.h new file mode 100644 index 0000000..5ee3e7d --- /dev/null +++ b/sys/netgraph/ng_pppoe.h @@ -0,0 +1,224 @@ + +/* + * 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 <julian@whistle.com> + * + * $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 { + 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 downstream */ + u_int packets_out; /* packets out towards downstream */ +}; + +/* + * 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 exporession, 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_req { + 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 + +/* 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_ */ + diff --git a/sys/netgraph/ng_rfc1490.c b/sys/netgraph/ng_rfc1490.c new file mode 100644 index 0000000..3649476 --- /dev/null +++ b/sys/netgraph/ng_rfc1490.c @@ -0,0 +1,347 @@ + +/* + * ng_rfc1490.c + * + * Copyright (c) 1996-1999 Whistle Communications, Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Whistle Communications; + * provided, however, that: + * 1. Any and all reproductions of the source or object code must include the + * copyright notice above and the following disclaimer of warranties; and + * 2. No rights are granted, in any manner or form, to use Whistle + * Communications, Inc. trademarks, including the mark "WHISTLE + * COMMUNICATIONS" on advertising, endorsements, or otherwise except as + * such appears in the above copyright notice or in the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY + * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS + * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. + * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Julian Elischer <julian@whistle.com> + * + * $FreeBSD$ + * $Whistle: ng_rfc1490.c,v 1.19 1999/01/28 23:54:53 julian Exp $ + */ + +/* + * This node does RFC 1490 multiplexing. + * + * NOTE: RFC 1490 is updated by RFC 2427. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/conf.h> +#include <sys/errno.h> +#include <sys/socket.h> +#include <sys/syslog.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> +#include <netgraph/ng_rfc1490.h> + +/* + * DEFINITIONS + */ + +/* Q.922 stuff -- see RFC 1490 */ +#define HDLC_UI 0x03 + +#define NLPID_IP 0xCC +#define NLPID_PPP 0xCF +#define NLPID_SNAP 0x80 +#define NLPID_Q933 0x08 +#define NLPID_CLNP 0x81 +#define NLPID_ESIS 0x82 +#define NLPID_ISIS 0x83 + +/* Node private data */ +struct private { + hook_p downlink; + hook_p ppp; + hook_p inet; +}; +typedef struct private *priv_p; + +/* Netgraph node methods */ +static int ng_rfc1490_constructor(node_p * nodep); +static int ng_rfc1490_rcvmsg(node_p node, struct ng_mesg * msg, + const char *retaddr, struct ng_mesg **resp); +static int ng_rfc1490_rmnode(node_p node); +static int ng_rfc1490_newhook(node_p node, hook_p hook, const char *name); +static int ng_rfc1490_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); +static int ng_rfc1490_disconnect(hook_p hook); + +/* Node type descriptor */ +static struct ng_type typestruct = { + NG_VERSION, + NG_RFC1490_NODE_TYPE, + NULL, + ng_rfc1490_constructor, + ng_rfc1490_rcvmsg, + ng_rfc1490_rmnode, + ng_rfc1490_newhook, + NULL, + NULL, + ng_rfc1490_rcvdata, + ng_rfc1490_rcvdata, + ng_rfc1490_disconnect +}; +NETGRAPH_INIT(rfc1490, &typestruct); + +/************************************************************************ + NETGRAPH NODE STUFF + ************************************************************************/ + +/* + * Node constructor + */ +static int +ng_rfc1490_constructor(node_p *nodep) +{ + priv_p priv; + int error; + + /* Allocate private structure */ + MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK); + if (priv == NULL) + return (ENOMEM); + bzero(priv, sizeof(*priv)); + + /* Call generic node constructor */ + if ((error = ng_make_node_common(&typestruct, nodep))) { + FREE(priv, M_NETGRAPH); + return (error); + } + (*nodep)->private = priv; + + /* Done */ + return (0); +} + +/* + * Give our ok for a hook to be added + */ +static int +ng_rfc1490_newhook(node_p node, hook_p hook, const char *name) +{ + const priv_p priv = node->private; + + if (!strcmp(name, NG_RFC1490_HOOK_DOWNSTREAM)) { + if (priv->downlink) + return (EISCONN); + priv->downlink = hook; + } else if (!strcmp(name, NG_RFC1490_HOOK_PPP)) { + if (priv->ppp) + return (EISCONN); + priv->ppp = hook; + } else if (!strcmp(name, NG_RFC1490_HOOK_INET)) { + if (priv->inet) + return (EISCONN); + priv->inet = hook; + } else + return (EINVAL); + return (0); +} + +/* + * Receive a control message. We don't support any special ones. + */ +static int +ng_rfc1490_rcvmsg(node_p node, struct ng_mesg *msg, + const char *raddr, struct ng_mesg **rp) +{ + FREE(msg, M_NETGRAPH); + return (EINVAL); +} + +/* + * Receive data on a hook and encapsulate according to RFC 1490. + * Only those nodes marked (*) are supported by this routine so far. + * + * Q.922 control + * | + * | + * -------------------------------------------- + * | 0x03 | + * UI I Frame + * | | + * --------------------------------- -------------- + * | 0x08 | 0x81 |0xCC |0xCF | 0x00 |..01.... |..10.... + * | | | | | 0x80 | | + * Q.933 CLNP IP(*) PPP(*) SNAP ISO 8208 ISO 8208 + * | (rfc1973) | Modulo 8 Modulo 128 + * | | + * -------------------- OUI + * | | | + * L2 ID L3 ID ------------------------- + * | User |00-80-C2 |00-00-00 + * | specified | | + * | 0x70 PID Ethertype + * | | | + * ------------------- --------------... ---------- + * |0x51 |0x4E | |0x4C |0x1 |0xB | |0x806 | + * | | | | | | | | | + * 7776 Q.922 Others 802.2 802.3 802.6 Others ARP(*) Others + * + * + */ + +#define MAX_ENCAPS_HDR 8 +#define ERROUT(x) do { error = (x); goto done; } while (0) +#define OUICMP(P,A,B,C) ((P)[0]==(A) && (P)[1]==(B) && (P)[2]==(C)) + +static int +ng_rfc1490_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) +{ + const node_p node = hook->node; + const priv_p priv = node->private; + int error = 0; + + if (hook == priv->downlink) { + u_char *start, *ptr; + + if (!m || !(m = m_pullup(m, MAX_ENCAPS_HDR))) + ERROUT(ENOBUFS); + ptr = start = mtod(m, u_char *); + + /* Must be UI frame */ + if (*ptr++ != HDLC_UI) + ERROUT(0); + + /* Eat optional zero pad byte */ + if (*ptr == 0x00) + ptr++; + + /* Multiplex on NLPID */ + switch (*ptr++) { + case NLPID_SNAP: + if (OUICMP(ptr, 0, 0, 0)) { /* It's an ethertype */ + u_int16_t etype; + + ptr += 3; + etype = ntohs(*((u_int16_t *) ptr)); + ptr += 2; + m_adj(m, ptr - start); + switch (etype) { + case ETHERTYPE_IP: + NG_SEND_DATA(error, + priv->inet, m, meta); + break; + case ETHERTYPE_ARP: + case ETHERTYPE_REVARP: + default: + ERROUT(0); + } + } else if (OUICMP(ptr, 0x00, 0x80, 0xc2)) /* 802.1 bridging */ + ERROUT(0); + else /* Other weird stuff... */ + ERROUT(0); + break; + case NLPID_IP: + m_adj(m, ptr - start); + NG_SEND_DATA(error, priv->inet, m, meta); + break; + case NLPID_PPP: + m_adj(m, ptr - start); + NG_SEND_DATA(error, priv->ppp, m, meta); + break; + case NLPID_Q933: + case NLPID_CLNP: + case NLPID_ESIS: + case NLPID_ISIS: + ERROUT(0); + default: /* Try PPP (see RFC 1973) */ + ptr--; /* NLPID becomes PPP proto */ + if ((*ptr & 0x01) == 0x01) + ERROUT(0); + m_adj(m, ptr - start); + NG_SEND_DATA(error, priv->ppp, m, meta); + break; + } + } else if (hook == priv->ppp) { + M_PREPEND(m, 2, M_DONTWAIT); /* Prepend PPP NLPID */ + if (!m) + ERROUT(ENOBUFS); + mtod(m, u_char *)[0] = HDLC_UI; + mtod(m, u_char *)[1] = NLPID_PPP; + NG_SEND_DATA(error, priv->downlink, m, meta); + } else if (hook == priv->inet) { + M_PREPEND(m, 2, M_DONTWAIT); /* Prepend IP NLPID */ + if (!m) + ERROUT(ENOBUFS); + mtod(m, u_char *)[0] = HDLC_UI; + mtod(m, u_char *)[1] = NLPID_IP; + NG_SEND_DATA(error, priv->downlink, m, meta); + } else + panic(__FUNCTION__); + +done: + NG_FREE_DATA(m, meta); + return (error); +} + +/* + * Nuke node + */ +static int +ng_rfc1490_rmnode(node_p node) +{ + const priv_p priv = node->private; + + /* Take down netgraph node */ + node->flags |= NG_INVALID; + ng_cutlinks(node); + ng_unname(node); + bzero(priv, sizeof(*priv)); + node->private = NULL; + ng_unref(node); /* let the node escape */ + return (0); +} + +/* + * Hook disconnection + */ +static int +ng_rfc1490_disconnect(hook_p hook) +{ + const priv_p priv = hook->node->private; + + if (hook->node->numhooks == 0) + ng_rmnode(hook->node); + else if (hook == priv->downlink) + priv->downlink = NULL; + else if (hook == priv->inet) + priv->inet = NULL; + else if (hook == priv->ppp) + priv->ppp = NULL; + else + panic(__FUNCTION__); + return (0); +} + diff --git a/sys/netgraph/ng_rfc1490.h b/sys/netgraph/ng_rfc1490.h new file mode 100644 index 0000000..98cdf63 --- /dev/null +++ b/sys/netgraph/ng_rfc1490.h @@ -0,0 +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 <archie@whistle.com> + * + * $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_NODE_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_ */ diff --git a/sys/netgraph/ng_sample.c b/sys/netgraph/ng_sample.c new file mode 100644 index 0000000..cef1977 --- /dev/null +++ b/sys/netgraph/ng_sample.c @@ -0,0 +1,441 @@ + +/* + * 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 <julian@whistle.com> + * + * $FreeBSD$ + * $Whistle: ng_sample.c,v 1.11 1999/01/28 23:54:54 julian Exp $ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/errno.h> +#include <sys/syslog.h> + +#include <netgraph/ng_message.h> +#include <netgraph/ng_sample.h> +#include <netgraph/netgraph.h> + +/* + * This section contains the netgraph method declarations for the + * sample node. These methods define the netgraph 'type'. + */ + +static int ng_xxx_constructor(node_p *node); +static int ng_xxx_rcvmsg(node_p node, struct ng_mesg *msg, + const char *retaddr, struct ng_mesg **resp); +static int ng_xxx_rmnode(node_p node); +static int ng_xxx_newhook(node_p node, hook_p hook, const char *name); +static int ng_xxx_connect(hook_p hook); +static int ng_xxx_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); +static int ng_xxx_rcvdataq(hook_p hook, struct mbuf *m, meta_p meta); +static int ng_xxx_disconnect(hook_p hook); + +/* Netgraph node type descriptor */ +static struct ng_type typestruct = { + NG_VERSION, + NG_XXX_NODE_TYPE, + NULL, + ng_xxx_constructor, + ng_xxx_rcvmsg, + ng_xxx_rmnode, + ng_xxx_newhook, + NULL, + ng_xxx_connect, + ng_xxx_rcvdata, + ng_xxx_rcvdataq, + ng_xxx_disconnect +}; +NETGRAPH_INIT(xxx, &typestruct); + +/* Information we store for each hook on each node */ +struct XXX_hookinfo { + int dlci; /* The DLCI it represents, -1 == downstream */ + int channel; /* The channel representing this DLCI */ + hook_p hook; +}; + +/* Information we store for each node */ +struct XXX { + struct XXX_hookinfo channel[XXX_NUM_DLCIS]; + struct XXX_hookinfo downstream_hook; + node_p node; /* back pointer to node */ + hook_p debughook; + u_int packets_in; /* packets in from downstream */ + u_int packets_out; /* packets out towards downstream */ + u_int32_t flags; +}; +typedef struct XXX *xxx_p; + +/* + * Allocate the private data structure and the generic node + * and link them together. + * + * ng_make_node_common() returns with a generic node struct + * with a single reference for us.. we transfer it to the + * private structure.. when we free the private struct we must + * unref the node so it gets freed too. + * + * If this were a device node than this work would be done in the attach() + * routine and the constructor would return EINVAL as you should not be able + * to creatednodes that depend on hardware (unless you can add the hardware :) + */ +static int +ng_xxx_constructor(node_p *nodep) +{ + xxx_p privdata; + int i, error; + + /* Initialize private descriptor */ + MALLOC(privdata, xxx_p, sizeof(*privdata), M_NETGRAPH, M_WAITOK); + if (privdata == NULL) + return (ENOMEM); + bzero(privdata, sizeof(struct XXX)); + for (i = 0; i < XXX_NUM_DLCIS; i++) { + privdata->channel[i].dlci = -2; + privdata->channel[i].channel = i; + } + + /* Call the 'generic' (ie, superclass) node constructor */ + if ((error = ng_make_node_common(&typestruct, nodep))) { + FREE(privdata, M_NETGRAPH); + return (error); + } + + /* Link structs together; this counts as our one reference to *nodep */ + (*nodep)->private = privdata; + privdata->node = *nodep; + return (0); +} + +/* + * Give our ok for a hook to be added... + * If we are not running this might kick a device into life. + * Possibly decode information out of the hook name. + * Add the hook's private info to the hook structure. + * (if we had some). In this example, we assume that there is a + * an array of structs, called 'channel' in the private info, + * one for each active channel. The private + * pointer of each hook points to the appropriate XXX_hookinfo struct + * so that the source of an input packet is easily identified. + * (a dlci is a frame relay channel) + */ +static int +ng_xxx_newhook(node_p node, hook_p hook, const char *name) +{ + const xxx_p xxxp = node->private; + const char *cp; + char c = '\0'; + int digits = 0; + int dlci = 0; + int chan; + +#if 0 + /* Possibly start up the device if it's not already going */ + if ((xxxp->flags & SCF_RUNNING) == 0) { + ng_xxx_start_hardware(xxxp); + } +#endif + + /* Example of how one might use hooks with embedded numbers: All + * hooks start with 'dlci' and have a decimal trailing channel + * number up to 4 digits Use the leadin defined int he associated .h + * file. */ + if (strncmp(name, NG_XXX_HOOK_DLCI_LEADIN, 4) == 0) { + cp = name + sizeof(NG_XXX_HOOK_DLCI_LEADIN); + while ((digits < 5) + && ((c = *cp++) > '0') && (c < '9')) { + dlci *= 10; + dlci += c - '0'; + digits++; + } + if ((c != 0) || (digits == 5) + || (dlci <= 0) || (dlci > 1023)) + return (EINVAL); + /* We have a dlci, now either find it, or allocate it */ + for (chan = 0; chan < XXX_NUM_DLCIS; chan++) + if (xxxp->channel[chan].dlci == dlci) + break; + if (chan == XXX_NUM_DLCIS) { + for (chan = 0; chan < XXX_NUM_DLCIS; chan++) + if (xxxp->channel[chan].dlci != -2) + continue; + if (chan == XXX_NUM_DLCIS) + return (ENOBUFS); + } + if (xxxp->channel[chan].hook != NULL) + return (EADDRINUSE); + hook->private = xxxp->channel + chan; + xxxp->channel[chan].hook = hook; + return (0); + } else if (strcmp(name, NG_XXX_HOOK_DOWNSTREAM) == 0) { + /* Example of simple predefined hooks. */ + /* do something specific to the downstream connection */ + xxxp->downstream_hook.hook = hook; + hook->private = &xxxp->downstream_hook; + } else if (strcmp(name, NG_XXX_HOOK_DEBUG) == 0) { + /* do something specific to a debug connection */ + xxxp->debughook = hook; + hook->private = NULL; + } else + return (EINVAL); /* not a hook we know about */ + return(0); +} + +/* + * Get a netgraph control message. + * Check it is one we understand. If needed, send a response. + * We could save the address for an async action later, but don't here. + * Always free the message. + * The response should be in a malloc'd region that the caller can 'free'. + * A response is not required. + * Theoretically you could respond defferently to old message types if + * the cookie in the header didn't match what we consider to be current + * (so that old userland programs could continue to work). + */ +static int +ng_xxx_rcvmsg(node_p node, + struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr) +{ + const xxx_p xxxp = node->private; + struct ng_mesg *resp = NULL; + int error = 0; + + /* Deal with message according to cookie and command */ + switch (msg->header.typecookie) { + case NGM_XXX_COOKIE: + switch (msg->header.cmd) { + case NGM_XXX_GET_STATUS: + { + struct ngxxxstat *stats; + + NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); + if (!resp) { + error = ENOMEM; + break; + } + stats = (struct ngxxxstat *) resp->data; + stats->packets_in = xxxp->packets_in; + stats->packets_out = xxxp->packets_out; + break; + } + case NGM_XXX_SET_FLAG: + if (msg->header.arglen != sizeof(u_int32_t)) { + error = EINVAL; + break; + } + xxxp->flags = *((u_int32_t *) msg->data); + break; + default: + error = EINVAL; /* unknown command */ + break; + } + break; + default: + error = EINVAL; /* unknown cookie type */ + break; + } + + /* Take care of synchronous response, if any */ + if (rptr) + *rptr = resp; + else if (resp) + FREE(resp, M_NETGRAPH); + + /* Free the message and return */ + FREE(msg, M_NETGRAPH); + return(error); +} + +/* + * Receive data, and do something with it. + * Possibly send it out on another link after processing. + * Possibly do something different if it comes from different + * hooks. the caller will never free m or meta, so + * if we use up this data or abort we must free BOTH of these. + * + * If we want, we may decide to force this data to be queued and reprocessed + * at the netgraph NETISR time. (at which time it will be entered using ng_xxx_rcvdataq(). + */ +static int +ng_xxx_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) +{ + int dlci = -2; + int error; + + if (hook->private) { + /* + * If it's dlci 1023, requeue it so that it's handled at a lower priority. + * This is how a node decides to defer a data message. + */ + dlci = ((struct XXX_hookinfo *) hook->private)->dlci; + if (dlci == 1023) { + ng_queue_data(hook->peer, m, meta); + } + } + 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); +} + diff --git a/sys/netgraph/ng_sample.h b/sys/netgraph/ng_sample.h new file mode 100644 index 0000000..5d2df45 --- /dev/null +++ b/sys/netgraph/ng_sample.h @@ -0,0 +1,75 @@ + +/* + * 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 <julian@whistle.com> + * + * $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 */ +}; + +#endif /* _NETGRAPH_SAMPLE_H_ */ diff --git a/sys/netgraph/ng_socket.c b/sys/netgraph/ng_socket.c new file mode 100644 index 0000000..b8ba80b --- /dev/null +++ b/sys/netgraph/ng_socket.c @@ -0,0 +1,957 @@ + +/* + * 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 <julian@whistle.com> + * + * $FreeBSD$ + * $Whistle: ng_socket.c,v 1.25 1999/01/28 23:54:54 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 <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/domain.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/file.h> +#include <sys/filedesc.h> +#include <sys/malloc.h> +#include <sys/queue.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> +#ifdef NOTYET +#include <sys/vnode.h> +#endif +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> +#include <netgraph/ng_socket.h> +#include <netgraph/ng_socketvar.h> + +/* + * It's Ascii-art time! + * +-------------+ +-------------+ + * |socket (ctl)| |socket (data)| + * +-------------+ +-------------+ + * ^ ^ + * | | + * v v + * +-----------+ +-----------+ + * |pcb (ctl)| |pcb (data)| + * +-----------+ +-----------+ + * ^ ^ + * | | + * v v + * +--------------------------+ + * | Socket type private | + * | data | + * +--------------------------+ + * ^ + * | + * v + * +----------------+ + * | struct ng_node | + * +----------------+ + */ + +/* Netgraph node methods */ +static int ngs_constructor(node_p *nodep); +static int ngs_rcvmsg(node_p node, struct ng_mesg *msg, + const char *retaddr, struct ng_mesg **resp); +static int ngs_rmnode(node_p node); +static int ngs_newhook(node_p node, hook_p hook, const char *name); +static int ngs_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); + +/* Internal methods */ +static int ng_attach_data(struct socket *so); +static int ng_attach_cntl(struct socket *so); +static int ng_attach_common(struct socket *so, int type); +static void ng_detach_common(struct ngpcb *pcbp, int type); +/*static int ng_internalize(struct mbuf *m, struct proc *p); */ + +static int ng_connect_data(struct sockaddr *nam, struct ngpcb *pcbp); +static int ng_connect_cntl(struct sockaddr *nam, struct ngpcb *pcbp); +static int ng_bind(struct sockaddr *nam, struct ngpcb *pcbp); + +static int ngs_mod_event(module_t mod, int event, void *data); +static int ship_msg(struct ngpcb *pcbp, struct ng_mesg *msg, + struct sockaddr_ng *addr); + +/* Netgraph type descriptor */ +static struct ng_type typestruct = { + NG_VERSION, + NG_SOCKET_NODE_TYPE, + ngs_mod_event, + ngs_constructor, + ngs_rcvmsg, + ngs_rmnode, + ngs_newhook, + NULL, + NULL, + ngs_rcvdata, + ngs_rcvdata, + NULL, +}; +NETGRAPH_INIT(socket, &typestruct); + +/* Buffer space */ +static u_long ngpdg_sendspace = 2 * 1024; /* really max datagram size */ +static u_long ngpdg_recvspace = 20 * 1024; + +/* List of all sockets */ +LIST_HEAD(, ngpcb) ngsocklist; + +#define sotongpcb(so) ((struct ngpcb *)so->so_pcb) + +/* If getting unexplained errors returned, set this to "Debugger("X"); */ +#ifndef TRAP_ERROR +#define TRAP_ERROR +#endif + +/*************************************************************** + Control sockets +***************************************************************/ + +static int +ngc_attach(struct socket *so, int proto, struct proc *p) +{ + struct ngpcb *const pcbp = sotongpcb(so); + + if (pcbp != NULL) + return (EISCONN); + return (ng_attach_cntl(so)); +} + +static int +ngc_detach(struct socket *so) +{ + struct ngpcb *const pcbp = sotongpcb(so); + + if (pcbp == NULL) + return (EINVAL); + ng_detach_common(pcbp, NG_CONTROL); + return (0); +} + +static int +ngc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, + struct mbuf *control, struct proc *p) +{ + struct ngpcb *const pcbp = sotongpcb(so); + struct sockaddr_ng *const sap = (struct sockaddr_ng *) addr; + struct ng_mesg *resp; + struct mbuf *m0; + char *msg, *path = NULL; + int len, error = 0; + + if (pcbp == NULL) { + error = EINVAL; + goto release; + } +#ifdef NOTYET + if (control && (error = ng_internalize(control, p))) { + if (pcbp->sockdata == NULL) { + error = ENOTCONN; + goto release; + } + } +#else /* NOTYET */ + if (control) { + error = EINVAL; + goto release; + } +#endif /* NOTYET */ + + /* Require destination as there may be >= 1 hooks on this node */ + if (addr == NULL) { + error = EDESTADDRREQ; + goto release; + } + + /* Allocate an expendable buffer for the path, chop off + * the sockaddr header, and make sure it's NUL terminated */ + len = sap->sg_len - 2; + MALLOC(path, char *, len + 1, M_NETGRAPH, M_WAITOK); + if (path == NULL) { + error = ENOMEM; + goto release; + } + bcopy(sap->sg_data, path, len); + path[len] = '\0'; + + /* Move the actual message out of mbufs into a linear buffer. + * Start by adding up the size of the data. (could use mh_len?) */ + for (len = 0, m0 = m; m0 != NULL; m0 = m0->m_next) + len += m0->m_len; + + /* Move the data into a linear buffer as well. Messages are not + * delivered in mbufs. */ + MALLOC(msg, char *, len + 1, M_NETGRAPH, M_WAITOK); + if (msg == NULL) { + error = ENOMEM; + goto release; + } + m_copydata(m, 0, len, msg); + + /* The callee will free the msg when done. The addr is our business. */ + error = ng_send_msg(pcbp->sockdata->node, + (struct ng_mesg *) msg, path, &resp); + + /* If the callee responded with a synchronous response, then put it + * back on the receive side of the socket; sap is source address. */ + if (error == 0 && resp != NULL) + error = ship_msg(pcbp, resp, sap); + +release: + if (path != NULL) + FREE(path, M_NETGRAPH); + if (control != NULL) + m_freem(control); + if (m != NULL) + m_freem(m); + return (error); +} + +static int +ngc_bind(struct socket *so, struct sockaddr *nam, struct proc *p) +{ + struct ngpcb *const pcbp = sotongpcb(so); + + if (pcbp == 0) + return (EINVAL); + return (ng_bind(nam, pcbp)); +} + +static int +ngc_connect(struct socket *so, struct sockaddr *nam, struct proc *p) +{ + struct ngpcb *const pcbp = sotongpcb(so); + + if (pcbp == 0) + return (EINVAL); + return (ng_connect_cntl(nam, pcbp)); +} + +/*************************************************************** + Data sockets +***************************************************************/ + +static int +ngd_attach(struct socket *so, int proto, struct proc *p) +{ + struct ngpcb *const pcbp = sotongpcb(so); + + if (pcbp != NULL) + return (EISCONN); + return (ng_attach_data(so)); +} + +static int +ngd_detach(struct socket *so) +{ + struct ngpcb *const pcbp = sotongpcb(so); + + if (pcbp == NULL) + return (EINVAL); + ng_detach_common(pcbp, NG_DATA); + return (0); +} + +static int +ngd_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, + struct mbuf *control, struct proc *p) +{ + struct ngpcb *const pcbp = sotongpcb(so); + struct sockaddr_ng *const sap = (struct sockaddr_ng *) addr; + char *hookname = NULL; + meta_p mp = NULL; + int len, error; + hook_p hook; + + if ((pcbp == NULL) || (control != NULL)) { + error = EINVAL; + goto release; + } + if (pcbp->sockdata == NULL) { + error = ENOTCONN; + goto release; + } + if (addr == NULL) { + error = EDESTADDRREQ; + goto release; + } + + /* Allocate an expendable buffer for the hook name, chop off + * the sockaddr header, and make sure it's NUL terminated */ + len = sap->sg_len - 2; + MALLOC(hookname, char *, len + 1, M_NETGRAPH, M_WAITOK); + if (hookname == NULL) { + error = ENOMEM; + goto release; + } + bcopy(sap->sg_data, hookname, len); + hookname[len] = '\0'; + + /* Find the correct hook from 'hookname' */ + LIST_FOREACH(hook, &pcbp->sockdata->node->hooks, hooks) { + if (strcmp(hookname, hook->name) == 0) + break; + } + + /* Send data (OK if hook is NULL) */ + NG_SEND_DATA(error, hook, m, mp); /* makes m NULL */ + +release: + if (hookname != NULL) + FREE(hookname, M_NETGRAPH); + if (control != NULL) + m_freem(control); + if (m != NULL) + m_freem(m); + return (error); +} + +static int +ngd_connect(struct socket *so, struct sockaddr *nam, struct proc *p) +{ + struct ngpcb *const pcbp = sotongpcb(so); + + if (pcbp == 0) + return (EINVAL); + return (ng_connect_data(nam, pcbp)); +} + +/* + * Used for both data and control sockets + */ +static int +ng_setsockaddr(struct socket *so, struct sockaddr **addr) +{ + struct ngpcb *const pcbp = sotongpcb(so); + struct sockaddr *sa; + int namelen; + + if (pcbp == 0) + return (EINVAL); + if (pcbp->sockdata->node->name != NULL) { + namelen = strlen(pcbp->sockdata->node->name) + 3; + MALLOC(sa, struct sockaddr *, namelen, M_SONAME, M_WAITOK); + if (sa == NULL) + return (ENOMEM); + sa->sa_family = AF_NETGRAPH; + sa->sa_len = namelen; + strcpy(sa->sa_data, pcbp->sockdata->node->name); + *addr = sa; + } else + *addr = NULL; /* XXX check this makes sense */ + return (0); +} + +/* + * Attach a socket to it's protocol specific partner. + * For a control socket, actually create a netgraph node and attach + * to it as well. + */ + +static int +ng_attach_cntl(struct socket *so) +{ + struct ngsock *privdata; + struct ngpcb *pcbp; + int error; + + /* Setup protocol control block */ + if ((error = ng_attach_common(so, NG_CONTROL)) != 0) + return (error); + pcbp = (struct ngpcb *) so->so_pcb; + + /* Allocate node private info */ + MALLOC(privdata, struct ngsock *, + sizeof(*privdata), M_NETGRAPH, M_WAITOK); + if (privdata == NULL) { + ng_detach_common(pcbp, NG_CONTROL); + return (ENOMEM); + } + bzero(privdata, sizeof(*privdata)); + + /* Make the generic node components */ + if ((error = ng_make_node_common(&typestruct, &privdata->node)) != 0) { + FREE(privdata, M_NETGRAPH); + ng_detach_common(pcbp, NG_CONTROL); + return (error); + } + privdata->node->private = privdata; + + /* Link the pcb and the node private data */ + privdata->ctlsock = pcbp; + pcbp->sockdata = privdata; + privdata->refs++; + return (0); +} + +static int +ng_attach_data(struct socket *so) +{ + return(ng_attach_common(so, NG_DATA)); +} + +/* + * Set up a socket protocol control block. + * This code is shared between control and data sockets. + */ +static int +ng_attach_common(struct socket *so, int type) +{ + struct ngpcb *pcbp; + int error; + + /* Standard socket setup stuff */ + error = soreserve(so, ngpdg_sendspace, ngpdg_recvspace); + if (error) + return (error); + + /* Allocate the pcb */ + MALLOC(pcbp, struct ngpcb *, sizeof(*pcbp), M_PCB, M_WAITOK); + if (pcbp == NULL) + return (ENOMEM); + bzero(pcbp, sizeof(*pcbp)); + pcbp->type = type; + + /* Link the pcb and the socket */ + so->so_pcb = (caddr_t) pcbp; + pcbp->ng_socket = so; + + /* Add the socket to linked list */ + LIST_INSERT_HEAD(&ngsocklist, pcbp, socks); + return (0); +} + +/* + * Disassociate the socket from it's protocol specific + * partner. If it's attached to a node's private data structure, + * then unlink from that too. If we were the last socket attached to it, + * then shut down the entire node. Shared code for control and data sockets. + */ +static void +ng_detach_common(struct ngpcb *pcbp, int which) +{ + struct ngsock *sockdata; + + if (pcbp->sockdata) { + sockdata = pcbp->sockdata; + pcbp->sockdata = NULL; + switch (which) { + case NG_CONTROL: + sockdata->ctlsock = NULL; + break; + case NG_DATA: + sockdata->datasock = NULL; + break; + default: + panic(__FUNCTION__); + } + if ((--sockdata->refs == 0) && (sockdata->node != NULL)) + ng_rmnode(sockdata->node); + } + pcbp->ng_socket->so_pcb = NULL; + pcbp->ng_socket = NULL; + LIST_REMOVE(pcbp, socks); + FREE(pcbp, M_PCB); +} + +#ifdef NOTYET +/* + * File descriptors can be passed into a AF_NETGRAPH socket. + * Note, that file descriptors cannot be passed OUT. + * Only character device descriptors are accepted. + * Character devices are useful to connect a graph to a device, + * which after all is the purpose of this whole system. + */ +static int +ng_internalize(struct mbuf *control, struct proc *p) +{ + struct filedesc *fdp = p->p_fd; + struct cmsghdr *cm = mtod(control, struct cmsghdr *); + struct file *fp; + struct vnode *vn; + int oldfds; + int fd; + + if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET || + cm->cmsg_len != control->m_len) { + TRAP_ERROR; + return (EINVAL); + } + + /* Check there is only one FD. XXX what would more than one signify? */ + oldfds = (cm->cmsg_len - sizeof(*cm)) / sizeof(int); + if (oldfds != 1) { + TRAP_ERROR; + return (EINVAL); + } + + /* Check that the FD given is legit. and change it to a pointer to a + * struct file. */ + fd = *(int *) (cm + 1); + if ((unsigned) fd >= fdp->fd_nfiles + || (fp = fdp->fd_ofiles[fd]) == NULL) { + return (EBADF); + } + + /* Depending on what kind of resource it is, act differently. For + * devices, we treat it as a file. For a AF_NETGRAPH socket, + * shortcut straight to the node. */ + switch (fp->f_type) { + case DTYPE_VNODE: + vn = (struct vnode *) fp->f_data; + if (vn && (vn->v_type == VCHR)) { + /* for a VCHR, actually reference the FILE */ + fp->f_count++; + /* XXX then what :) */ + /* how to pass on to other modules? */ + } else { + TRAP_ERROR; + return (EINVAL); + } + break; + default: + TRAP_ERROR; + return (EINVAL); + } + return (0); +} +#endif /* NOTYET */ + +/* + * Connect the data socket to a named control socket node. + */ +static int +ng_connect_data(struct sockaddr *nam, struct ngpcb *pcbp) +{ + struct sockaddr_ng *sap; + node_p farnode; + struct ngsock *sockdata; + int error; + + /* If we are already connected, don't do it again */ + if (pcbp->sockdata != NULL) + return (EISCONN); + + /* Find the target (victim) and check it doesn't already have a data + * socket. Also check it is a 'socket' type node. */ + sap = (struct sockaddr_ng *) nam; + if ((error = ng_path2node(NULL, sap->sg_data, &farnode, NULL))) + return (error); + + if (strcmp(farnode->type->name, NG_SOCKET_NODE_TYPE) != 0) + return (EINVAL); + sockdata = farnode->private; + if (sockdata->datasock != NULL) + return (EADDRINUSE); + + /* Link the PCB and the private data struct. and note the extra + * reference */ + sockdata->datasock = pcbp; + pcbp->sockdata = sockdata; + sockdata->refs++; + return (0); +} + +/* + * Connect the existing control socket node to a named node:hook. + * The hook we use on this end is the same name as the remote node name. + */ +static int +ng_connect_cntl(struct sockaddr *nam, struct ngpcb *pcbp) +{ + struct ngsock *const sockdata = pcbp->sockdata; + struct sockaddr_ng *sap; + char *node, *hook; + node_p farnode; + int rtn, error; + + sap = (struct sockaddr_ng *) nam; + rtn = ng_path_parse(sap->sg_data, &node, NULL, &hook); + if (rtn < 0 || node == NULL || hook == NULL) { + TRAP_ERROR; + return (EINVAL); + } + farnode = ng_findname(sockdata->node, node); + if (farnode == NULL) { + TRAP_ERROR; + return (EADDRNOTAVAIL); + } + + /* Connect, using a hook name the same as the far node name. */ + error = ng_con_nodes(sockdata->node, node, farnode, hook); + return error; +} + +/* + * Binding a socket means giving the corresponding node a name + */ +static int +ng_bind(struct sockaddr *nam, struct ngpcb *pcbp) +{ + struct ngsock *const sockdata = pcbp->sockdata; + struct sockaddr_ng *const sap = (struct sockaddr_ng *) nam; + + if (sockdata == NULL) { + TRAP_ERROR; + return (EINVAL); + } + if (sap->sg_len < 3 || sap->sg_data[sap->sg_len - 3] != '\0') { + TRAP_ERROR; + return (EINVAL); + } + return (ng_name_node(sockdata->node, sap->sg_data)); +} + +/* + * Take a message and pass it up to the control socket associated + * with the node. + */ +static int +ship_msg(struct ngpcb *pcbp, struct ng_mesg *msg, struct sockaddr_ng *addr) +{ + struct socket *const so = pcbp->ng_socket; + struct mbuf *mdata; + int msglen; + + /* Copy the message itself into an mbuf chain */ + msglen = sizeof(struct ng_mesg) + msg->header.arglen; + mdata = m_devget((caddr_t) msg, msglen, 0, NULL, NULL); + + /* Here we free the message, as we are the end of the line. + * We need to do that regardless of whether we got mbufs. */ + FREE(msg, M_NETGRAPH); + + if (mdata == NULL) { + TRAP_ERROR; + return (ENOBUFS); + } + + /* Send it up to the socket */ + if (sbappendaddr(&so->so_rcv, + (struct sockaddr *) addr, mdata, NULL) == 0) { + TRAP_ERROR; + m_freem(mdata); + return (ENOBUFS); + } + sorwakeup(so); + return (0); +} + +/* + * You can only create new nodes from the socket end of things. + */ +static int +ngs_constructor(node_p *nodep) +{ + return (EINVAL); +} + +/* + * We allow any hook to be connected to the node. + * There is no per-hook private information though. + */ +static int +ngs_newhook(node_p node, hook_p hook, const char *name) +{ + hook->private = node->private; + return (0); +} + +/* + * Incoming messages get passed up to the control socket. + */ +static int +ngs_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, + struct ng_mesg **resp) +{ + struct ngsock *const sockdata = node->private; + struct ngpcb *const pcbp = sockdata->ctlsock; + struct sockaddr_ng *addr; + int addrlen; + int error = 0; + + /* Only allow mesgs to be passed if we have the control socket. + * Data sockets can only support the generic messages. */ + if (pcbp == NULL) { + TRAP_ERROR; + return (EINVAL); + } + + /* Get the return address into a sockaddr */ + if ((retaddr == NULL) || (*retaddr == '\0')) + retaddr = ""; + addrlen = strlen(retaddr); + MALLOC(addr, struct sockaddr_ng *, addrlen + 4, M_NETGRAPH, M_NOWAIT); + if (addr == NULL) { + TRAP_ERROR; + return (ENOMEM); + } + addr->sg_len = addrlen + 3; + addr->sg_family = AF_NETGRAPH; + bcopy(retaddr, addr->sg_data, addrlen); + addr->sg_data[addrlen] = '\0'; + + /* Send it up */ + error = ship_msg(pcbp, msg, addr); + FREE(addr, M_NETGRAPH); + return (error); +} + +/* + * Receive data on a hook + */ +static int +ngs_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) +{ + struct ngsock *const sockdata = hook->node->private; + struct ngpcb *const pcbp = sockdata->datasock; + struct socket *so; + struct sockaddr_ng *addr; + char *addrbuf[NG_HOOKLEN + 1 + 4]; + int addrlen; + + /* If there is no data socket, black-hole it */ + if (pcbp == NULL) { + NG_FREE_DATA(m, meta); + return (0); + } + so = pcbp->ng_socket; + + /* Get the return address into a sockaddr. */ + addrlen = strlen(hook->name); /* <= NG_HOOKLEN */ + addr = (struct sockaddr_ng *) addrbuf; + addr->sg_len = addrlen + 3; + addr->sg_family = AF_NETGRAPH; + bcopy(hook->name, addr->sg_data, addrlen); + addr->sg_data[addrlen] = '\0'; + + /* We have no use for the meta data, free/clear it now. */ + NG_FREE_META(meta); + + /* Try to tell the socket which hook it came in on */ + if (sbappendaddr(&so->so_rcv, (struct sockaddr *) addr, m, NULL) == 0) { + m_freem(m); + TRAP_ERROR; + return (ENOBUFS); + } + sorwakeup(so); + return (0); +} + +/* + * Do local shutdown processing. + * In this case, that involves making sure the socket + * knows we should be shutting down. + */ +static int +ngs_rmnode(node_p node) +{ + struct ngsock *const sockdata = node->private; + struct ngpcb *const dpcbp = sockdata->datasock; + struct ngpcb *const pcbp = sockdata->ctlsock; + + ng_cutlinks(node); + ng_unname(node); + + if (dpcbp != NULL) { + soisdisconnected(dpcbp->ng_socket); + dpcbp->sockdata = NULL; + sockdata->datasock = NULL; + sockdata->refs--; + } + if (pcbp != NULL) { + soisdisconnected(pcbp->ng_socket); + pcbp->sockdata = NULL; + sockdata->ctlsock = NULL; + sockdata->refs--; + } + node->private = NULL; + ng_unref(node); + FREE(sockdata, M_NETGRAPH); + return (0); +} + +/* + * Control and data socket type descriptors + */ + +static struct pr_usrreqs ngc_usrreqs = { + NULL, /* abort */ + pru_accept_notsupp, + ngc_attach, + ngc_bind, + ngc_connect, + pru_connect2_notsupp, + pru_control_notsupp, + ngc_detach, + NULL, /* disconnect */ + pru_listen_notsupp, + NULL, /* setpeeraddr */ + pru_rcvd_notsupp, + pru_rcvoob_notsupp, + ngc_send, + pru_sense_null, + NULL, /* shutdown */ + ng_setsockaddr, + sosend, + soreceive, + sopoll +}; + +static struct pr_usrreqs ngd_usrreqs = { + NULL, /* abort */ + pru_accept_notsupp, + ngd_attach, + NULL, /* bind */ + ngd_connect, + pru_connect2_notsupp, + pru_control_notsupp, + ngd_detach, + NULL, /* disconnect */ + pru_listen_notsupp, + NULL, /* setpeeraddr */ + pru_rcvd_notsupp, + pru_rcvoob_notsupp, + ngd_send, + pru_sense_null, + NULL, /* shutdown */ + ng_setsockaddr, + sosend, + soreceive, + sopoll +}; + +/* + * Definitions of protocols supported in the NETGRAPH domain. + */ + +extern struct domain ngdomain; /* stop compiler warnings */ + +static struct protosw ngsw[] = { + { + SOCK_DGRAM, + &ngdomain, + NG_CONTROL, + PR_ATOMIC | PR_ADDR /* | PR_RIGHTS */, + 0, 0, 0, 0, + NULL, + 0, 0, 0, 0, + &ngc_usrreqs + }, + { + SOCK_DGRAM, + &ngdomain, + NG_DATA, + PR_ATOMIC | PR_ADDR, + 0, 0, 0, 0, + NULL, + 0, 0, 0, 0, + &ngd_usrreqs + } +}; + +struct domain ngdomain = { + AF_NETGRAPH, + "netgraph", + 0, + NULL, + NULL, + ngsw, + &ngsw[sizeof(ngsw) / sizeof(ngsw[0])], + 0, + NULL, + 0, + 0 +}; + +/* + * Handle loading and unloading for this node type + * This is to handle auxiliary linkages (e.g protocol domain addition). + */ +static int +ngs_mod_event(module_t mod, int event, void *data) +{ + int error = 0; + + switch (event) { + case MOD_LOAD: + /* Register protocol domain */ + net_add_domain(&ngdomain); + break; + case MOD_UNLOAD: + /* Insure there are no open netgraph sockets */ + if (!LIST_EMPTY(&ngsocklist)) { + error = EBUSY; + break; + } + +#ifdef NOTYET + /* Unregister protocol domain XXX can't do this yet.. */ + if ((error = net_rm_domain(&ngdomain)) != 0) + break; +#else + error = EBUSY; +#endif + break; + default: + error = EOPNOTSUPP; + break; + } + return (error); +} + +SYSCTL_NODE(_net, AF_NETGRAPH, graph, CTLFLAG_RW, 0, "netgraph Family"); +SYSCTL_INT(_net_graph, OID_AUTO, family, CTLFLAG_RD, 0, AF_NETGRAPH, ""); +SYSCTL_NODE(_net_graph, OID_AUTO, data, CTLFLAG_RW, 0, "DATA"); +SYSCTL_INT(_net_graph_data, OID_AUTO, proto, CTLFLAG_RD, 0, NG_DATA, ""); +SYSCTL_NODE(_net_graph, OID_AUTO, control, CTLFLAG_RW, 0, "CONTROL"); +SYSCTL_INT(_net_graph_control, OID_AUTO, proto, CTLFLAG_RD, 0, NG_CONTROL, ""); + diff --git a/sys/netgraph/ng_socket.h b/sys/netgraph/ng_socket.h new file mode 100644 index 0000000..a84d12a --- /dev/null +++ b/sys/netgraph/ng_socket.h @@ -0,0 +1,62 @@ + +/* + * 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 <julian@whistle.com> + * + * $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 + +/* 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_ */ + diff --git a/sys/netgraph/ng_socketvar.h b/sys/netgraph/ng_socketvar.h new file mode 100644 index 0000000..5451524 --- /dev/null +++ b/sys/netgraph/ng_socketvar.h @@ -0,0 +1,63 @@ + +/* + * 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 <julian@whistle.com> + * + * $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 refs; +}; + +#endif /* _NETGRAPH_NG_SOCKETVAR_H_ */ + diff --git a/sys/netgraph/ng_tee.c b/sys/netgraph/ng_tee.c new file mode 100644 index 0000000..476913c --- /dev/null +++ b/sys/netgraph/ng_tee.c @@ -0,0 +1,268 @@ + +/* + * 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 <julian@whistle.com> + * + * $FreeBSD$ + * $Whistle: ng_tee.c,v 1.16 1999/01/28 23:54:54 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 <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> +#include <netgraph/ng_tee.h> + +/* Per hook info */ +struct hookinfo { + hook_p hook; + int bytes; + int packets; + int flags; +}; + +/* Per node info */ +struct privdata { + node_p node; + int flags; + struct hookinfo left; + struct hookinfo right; + struct hookinfo left2right; + struct hookinfo right2left; +}; +typedef struct privdata *sc_p; + +/* Netgraph methods */ +static int ngt_constructor(node_p *node); +static int ngt_rcvmsg(node_p node, struct ng_mesg *msg, + const char *retaddr, struct ng_mesg **resp); +static int ngt_rmnode(node_p node); +static int ngt_newhook(node_p node, hook_p hook, const char *name); +static int ngt_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); +static int ngt_disconnect(hook_p hook); + +/* Netgraph type descriptor */ +static struct ng_type typestruct = { + NG_VERSION, + NG_TEE_NODE_TYPE, + NULL, + ngt_constructor, + ngt_rcvmsg, + ngt_rmnode, + ngt_newhook, + NULL, + NULL, + ngt_rcvdata, + ngt_rcvdata, + ngt_disconnect +}; +NETGRAPH_INIT(tee, &typestruct); + +/* + * Node constructor + */ +static int +ngt_constructor(node_p *nodep) +{ + sc_p privdata; + int error = 0; + + MALLOC(privdata, sc_p, sizeof(*privdata), M_NETGRAPH, M_WAITOK); + if (privdata == NULL) + return (ENOMEM); + bzero(privdata, sizeof(*privdata)); + + if ((error = ng_make_node_common(&typestruct, nodep))) { + FREE(privdata, M_NETGRAPH); + return (error); + } + (*nodep)->private = privdata; + privdata->node = *nodep; + return (0); +} + +/* + * Add a hook + */ +static int +ngt_newhook(node_p node, hook_p hook, const char *name) +{ + const sc_p sc = node->private; + + if (strcmp(name, NG_TEE_HOOK_RIGHT) == 0) { + sc->right.hook = hook; + sc->right.bytes = 0; + sc->right.packets = 0; + hook->private = &sc->right; + } else if (strcmp(name, NG_TEE_HOOK_LEFT) == 0) { + sc->left.hook = hook; + sc->left.bytes = 0; + sc->left.packets = 0; + hook->private = &sc->left; + } else if (strcmp(name, NG_TEE_HOOK_RIGHT2LEFT) == 0) { + sc->right2left.hook = hook; + sc->right2left.bytes = 0; + sc->right2left.packets = 0; + hook->private = &sc->right2left; + } else if (strcmp(name, NG_TEE_HOOK_LEFT2RIGHT) == 0) { + sc->left2right.hook = hook; + sc->left2right.bytes = 0; + sc->left2right.packets = 0; + hook->private = &sc->left2right; + } else + return (EINVAL); + return (0); +} + +/* + * We don't support any type-specific messages + */ +static int +ngt_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, + struct ng_mesg **resp) +{ + FREE(msg, M_NETGRAPH); + return (EINVAL); +} + +/* + * Receive data on a hook + * + * If data comes in the right link send a copy out right2left, and then + * send the original onwards out through the left link. + * Do the opposite for data coming in from the left link. + * Data coming in right2left or left2right is forwarded + * on through the appropriate destination hook as if it had come + * from the other side. + */ +static int +ngt_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) +{ + const sc_p sc = hook->node->private; + struct hookinfo *hi; + struct hookinfo *dest; + struct hookinfo *dup; + struct mbuf *mdup; + int error = 0; + + if ((hi = hook->private) != NULL) { + if (hi == &sc->left) { + dup = &sc->left2right; + dest = &sc->right; + } else if (hi == &sc->right) { + dup = &sc->right2left; + dest = &sc->left; + } else if (hi == &sc->right2left) { + dup = NULL; + dest = &sc->left; + } else if (hi == &sc->left2right) { + dup = NULL; + dest = &sc->right; + } else + goto out; + if (dup) { + mdup = m_copypacket(m, M_NOWAIT); + if (mdup) { + /* XXX should we duplicate meta? */ + /* for now no. */ + void *x = NULL; + + NG_SEND_DATA(error, dup->hook, mdup, x); + } + } + NG_SEND_DATA(error, dest->hook, m, meta); + } + +out: + NG_FREE_DATA(m, meta); + return (error); +} + +/* + * Shutdown processing + * + * This is tricky. If we have both a left and right hook, then we + * probably want to extricate ourselves and leave the two peers + * still linked to each other. Otherwise we should just shut down as + * a normal node would. + * + * To keep the scope of info correct the routine to "extract" a node + * from two links is in ng_base.c. + */ +static int +ngt_rmnode(node_p node) +{ + const sc_p privdata = node->private; + + node->flags |= NG_INVALID; + if (privdata->left.hook && privdata->right.hook) + ng_bypass(privdata->left.hook, privdata->right.hook); + ng_cutlinks(node); + ng_unname(node); + node->private = NULL; + ng_unref(privdata->node); + FREE(privdata, M_NETGRAPH); + return (0); +} + +/* + * Hook disconnection + */ +static int +ngt_disconnect(hook_p hook) +{ + struct hookinfo *hi; + + if ((hi = hook->private) != NULL) + hi->hook = NULL; + if (hook->node->numhooks == 0) + ng_rmnode(hook->node); + return (0); +} + diff --git a/sys/netgraph/ng_tee.h b/sys/netgraph/ng_tee.h new file mode 100644 index 0000000..96f2380 --- /dev/null +++ b/sys/netgraph/ng_tee.h @@ -0,0 +1,56 @@ + +/* + * 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 <archie@whistle.com> + * + * $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" + +#endif /* _NETGRAPH_TEE_H_ */ diff --git a/sys/netgraph/ng_tty.c b/sys/netgraph/ng_tty.c new file mode 100644 index 0000000..4ec7bf9 --- /dev/null +++ b/sys/netgraph/ng_tty.c @@ -0,0 +1,706 @@ + +/* + * 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 <archie@whistle.com> + * + * $FreeBSD$ + * $Whistle: ng_tty.c,v 1.18 1999/01/28 23:54:54 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 <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/proc.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/socket.h> +#include <sys/fcntl.h> +#include <sys/file.h> +#include <sys/tty.h> +#include <sys/syslog.h> +#include <sys/errno.h> +#include <sys/ioccom.h> + +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> +#include <netgraph/ng_tty.h> + +#ifdef __i386__ /* fiddle with the spl locking */ +#include <machine/ipl.h> +#include <i386/isa/intr_machdep.h> +#endif + +/* Misc defs */ +#define MAX_MBUFQ 3 /* Max number of queued mbufs */ +#define NGT_HIWATER 400 /* High water mark on output */ + +/* Per-node private info */ +struct ngt_sc { + struct tty *tp; /* Terminal device */ + node_p node; /* Netgraph node */ + hook_p hook; /* Netgraph hook */ + struct mbuf *m; /* Incoming data buffer */ + struct mbuf *qhead, **qtail; /* Queue of outgoing mbuf's */ + short qlen; /* Length of queue */ + short hotchar; /* Hotchar, or -1 if none */ + u_int flags; /* Flags */ + struct callout_handle chand; /* See man timeout(9) */ +}; +typedef struct ngt_sc *sc_p; + +/* Flags */ +#define FLG_TIMEOUT 0x0001 /* A timeout is pending */ +#define FLG_DEBUG 0x0002 + +/* Debugging */ +#ifdef DIAGNOSTICS +#define QUEUECHECK(sc) \ + do { \ + struct mbuf **mp; \ + int k; \ + \ + for (k = 0, mp = &sc->qhead; \ + k <= MAX_MBUFQ && *mp; \ + k++, mp = &(*mp)->m_nextpkt); \ + if (k != sc->qlen || k > MAX_MBUFQ || *mp || mp != sc->qtail) \ + panic(__FUNCTION__ ": queue"); \ + } while (0) +#else +#define QUEUECHECK(sc) do {} while (0) +#endif + +/* Line discipline methods */ +static int ngt_open(dev_t dev, struct tty *tp); +static int ngt_close(struct tty *tp, int flag); +static int ngt_read(struct tty *tp, struct uio *uio, int flag); +static int ngt_write(struct tty *tp, struct uio *uio, int flag); +static int ngt_tioctl(struct tty *tp, + u_long cmd, caddr_t data, int flag, struct proc *); +static int ngt_input(int c, struct tty *tp); +static int ngt_start(struct tty *tp); + +/* Netgraph methods */ +static int ngt_constructor(node_p *node); +static int ngt_rcvmsg(node_p node, struct ng_mesg *msg, + const char *retaddr, struct ng_mesg **resp); +static int ngt_shutdown(node_p node); +static int ngt_newhook(node_p node, hook_p hook, const char *name); +static int ngt_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); +static int ngt_disconnect(hook_p hook); +static 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 */ +}; +static int ngt_ldisc = -1; + +/* Netgraph node type descriptor */ +static struct ng_type typestruct = { + NG_VERSION, + NG_TTY_NODE_TYPE, + ngt_mod_event, + ngt_constructor, + ngt_rcvmsg, + ngt_shutdown, + ngt_newhook, + NULL, + NULL, + ngt_rcvdata, + ngt_rcvdata, + ngt_disconnect, +}; +NETGRAPH_INIT(tty, &typestruct); + +static int ngt_unit; +static int ngt_nodeop_ok; /* OK to create/remove node */ + +/****************************************************************** + LINE DISCIPLINE METHODS +******************************************************************/ + +/* + * Set our line discipline on the tty. + * Called from device open routine or ttioctl() at >= splsofttty() + */ +static int +ngt_open(dev_t dev, struct tty *tp) +{ + struct proc *const p = curproc; /* XXX */ + char name[sizeof(NG_TTY_NODE_TYPE) + 8]; + sc_p sc; + int s, error; + + /* Super-user only */ + if ((error = suser(p))) + return (error); + s = splnet(); + (void) spltty(); /* XXX is this necessary? */ + + /* Already installed? */ + if (tp->t_line == ngt_ldisc) { + sc = (sc_p) tp->t_sc; + if (sc != NULL && sc->tp == tp) + goto done; + } + + /* Initialize private struct */ + MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_WAITOK); + if (sc == NULL) { + error = ENOMEM; + goto done; + } + bzero(sc, sizeof(*sc)); + sc->tp = tp; + sc->hotchar = NG_TTY_DFL_HOTCHAR; + sc->qtail = &sc->qhead; + QUEUECHECK(sc); + callout_handle_init(&sc->chand); + + /* Setup netgraph node */ + ngt_nodeop_ok = 1; + error = ng_make_node_common(&typestruct, &sc->node); + ngt_nodeop_ok = 0; + if (error) { + FREE(sc, M_NETGRAPH); + goto done; + } + sprintf(name, "%s%d", typestruct.name, ngt_unit++); + + /* Set back pointers */ + sc->node->private = sc; + tp->t_sc = (caddr_t) sc; + + /* Assign node its name */ + if ((error = ng_name_node(sc->node, name))) { + log(LOG_ERR, "%s: node name exists?\n", name); + ngt_nodeop_ok = 1; + ng_rmnode(sc->node); + ngt_nodeop_ok = 0; + goto done; + } + + /* + * Pre-allocate cblocks to the an appropriate amount. + * I'm not sure what is appropriate. + */ + ttyflush(tp, FREAD | FWRITE); + clist_alloc_cblocks(&tp->t_canq, 0, 0); + clist_alloc_cblocks(&tp->t_rawq, 0, 0); + clist_alloc_cblocks(&tp->t_outq, + MLEN + NGT_HIWATER, MLEN + NGT_HIWATER); + +done: + /* Done */ + splx(s); + return (error); +} + +/* + * Line specific close routine, called from device close routine + * and from ttioctl at >= splsofttty(). This causes the node to + * be destroyed as well. + */ +static int +ngt_close(struct tty *tp, int flag) +{ + const sc_p sc = (sc_p) tp->t_sc; + int s; + + s = spltty(); + ttyflush(tp, FREAD | FWRITE); + clist_free_cblocks(&tp->t_outq); + tp->t_line = 0; + if (sc != NULL) { + if (sc->flags & FLG_TIMEOUT) { + untimeout(ngt_timeout, sc, sc->chand); + sc->flags &= ~FLG_TIMEOUT; + } + ngt_nodeop_ok = 1; + ng_rmnode(sc->node); + ngt_nodeop_ok = 0; + tp->t_sc = NULL; + } + splx(s); + return (0); +} + +/* + * Once the device has been turned into a node, we don't allow reading. + */ +static int +ngt_read(struct tty *tp, struct uio *uio, int flag) +{ + return (EIO); +} + +/* + * Once the device has been turned into a node, we don't allow writing. + */ +static int +ngt_write(struct tty *tp, struct uio *uio, int flag) +{ + return (EIO); +} + +/* + * We implement the NGIOCGINFO ioctl() defined in ng_message.h. + */ +static int +ngt_tioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + const sc_p sc = (sc_p) tp->t_sc; + int s, error = 0; + + s = spltty(); + switch (cmd) { + case NGIOCGINFO: + { + struct nodeinfo *const ni = (struct nodeinfo *) data; + const node_p node = sc->node; + + bzero(ni, sizeof(*ni)); + if (node->name) + strncpy(ni->name, node->name, sizeof(ni->name) - 1); + strncpy(ni->type, node->type->name, sizeof(ni->type) - 1); + ni->id = (u_int32_t) node; + ni->hooks = node->numhooks; + break; + } + default: + ERROUT(ENOIOCTL); + } +done: + splx(s); + return (error); +} + +/* + * Receive data coming from the device. We get one character at + * a time, which is kindof silly. + * Only guaranteed to be at splsofttty() or spltty(). + */ +static int +ngt_input(int c, struct tty *tp) +{ + const sc_p sc = (sc_p) tp->t_sc; + const node_p node = sc->node; + struct mbuf *m; + int s, error = 0; + + if (!sc || tp != sc->tp) + return (0); + s = spltty(); + if (!sc->hook) + ERROUT(0); + + /* Check for error conditions */ + if ((tp->t_state & TS_CONNECTED) == 0) { + if (sc->flags & FLG_DEBUG) + log(LOG_DEBUG, "%s: no carrier\n", node->name); + ERROUT(0); + } + if (c & TTY_ERRORMASK) { + /* framing error or overrun on this char */ + if (sc->flags & FLG_DEBUG) + log(LOG_DEBUG, "%s: line error %x\n", + node->name, c & TTY_ERRORMASK); + ERROUT(0); + } + c &= TTY_CHARMASK; + + /* Get a new header mbuf if we need one */ + if (!(m = sc->m)) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (!m) { + if (sc->flags & FLG_DEBUG) + log(LOG_ERR, + "%s: can't get mbuf\n", node->name); + ERROUT(ENOBUFS); + } + m->m_len = 0; + m->m_pkthdr.len = 0; + sc->m = m; + } + + /* Add char to mbuf */ + *mtod(m, u_char *) = c; + m->m_data++; + m->m_len++; + m->m_pkthdr.len++; + + /* Ship off mbuf if it's time */ + if (sc->hotchar == -1 || c == sc->hotchar || m->m_len >= MHLEN) { + m->m_data = m->m_pktdat; + error = ng_queue_data(sc->hook, m, NULL); + sc->m = NULL; + } +done: + splx(s); + return (error); +} + +/* + * This is called when the device driver is ready for more output. + * Called from tty system at splsofttty() or spltty(). + * Also call from ngt_rcv_data() when a new mbuf is available for output. + */ +static int +ngt_start(struct tty *tp) +{ + const sc_p sc = (sc_p) tp->t_sc; + int s; + + s = spltty(); + while (tp->t_outq.c_cc < NGT_HIWATER) { /* XXX 2.2 specific ? */ + struct mbuf *m = sc->qhead; + + /* Remove first mbuf from queue */ + if (!m) + break; + if ((sc->qhead = m->m_nextpkt) == NULL) + sc->qtail = &sc->qhead; + sc->qlen--; + QUEUECHECK(sc); + + /* Send as much of it as possible */ + while (m) { + struct mbuf *m2; + int sent; + + sent = m->m_len + - b_to_q(mtod(m, u_char *), m->m_len, &tp->t_outq); + m->m_data += sent; + m->m_len -= sent; + if (m->m_len > 0) + break; /* device can't take no more */ + MFREE(m, m2); + m = m2; + } + + /* Put remainder of mbuf chain (if any) back on queue */ + if (m) { + m->m_nextpkt = sc->qhead; + sc->qhead = m; + if (sc->qtail == &sc->qhead) + sc->qtail = &m->m_nextpkt; + sc->qlen++; + QUEUECHECK(sc); + break; + } + } + + /* Call output process whether or not there is any output. We are + * being called in lieu of ttstart and must do what it would. */ + if (tp->t_oproc != NULL) + (*tp->t_oproc) (tp); + + /* This timeout is needed for operation on a pseudo-tty, because the + * pty code doesn't call pppstart after it has drained the t_outq. */ + if (sc->qhead && (sc->flags & FLG_TIMEOUT) == 0) { + sc->chand = timeout(ngt_timeout, sc, 1); + sc->flags |= FLG_TIMEOUT; + } + splx(s); + return (0); +} + +/* + * We still have data to output to the device, so try sending more. + */ +static void +ngt_timeout(void *arg) +{ + const sc_p sc = (sc_p) arg; + int s; + + s = spltty(); + sc->flags &= ~FLG_TIMEOUT; + ngt_start(sc->tp); + splx(s); +} + +/****************************************************************** + NETGRAPH NODE METHODS +******************************************************************/ + +/* + * Initialize a new node of this type. + * + * We only allow nodes to be created as a result of setting + * the line discipline on a tty, so always return an error if not. + */ +static int +ngt_constructor(node_p *nodep) +{ + if (!ngt_nodeop_ok) + return (EOPNOTSUPP); + return (ng_make_node_common(&typestruct, nodep)); +} + +/* + * Add a new hook. There can only be one. + */ +static int +ngt_newhook(node_p node, hook_p hook, const char *name) +{ + const sc_p sc = node->private; + int s, error = 0; + + if (strcmp(name, NG_TTY_HOOK)) + return (EINVAL); + s = spltty(); + if (sc->hook) + ERROUT(EISCONN); + sc->hook = hook; +done: + splx(s); + return (error); +} + +/* + * Disconnect the hook + */ +static int +ngt_disconnect(hook_p hook) +{ + const sc_p sc = hook->node->private; + int s; + + s = spltty(); + if (hook != sc->hook) + panic(__FUNCTION__); + sc->hook = NULL; + m_freem(sc->m); + sc->m = NULL; + splx(s); + return (0); +} + +/* + * Remove this node. The does the netgraph portion of the shutdown. + * This should only be called indirectly from ngt_close(). + */ +static int +ngt_shutdown(node_p node) +{ + const sc_p sc = node->private; + + if (!ngt_nodeop_ok) + return (EOPNOTSUPP); + ng_unname(node); + ng_cutlinks(node); + node->private = NULL; + ng_unref(sc->node); + m_freem(sc->qhead); + m_freem(sc->m); + bzero(sc, sizeof(*sc)); + FREE(sc, M_NETGRAPH); + return (0); +} + +/* + * Receive incoming data from netgraph system. Put it on our + * output queue and start output if necessary. + */ +static int +ngt_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) +{ + const sc_p sc = hook->node->private; + int s, error = 0; + + if (hook != sc->hook) + panic(__FUNCTION__); + NG_FREE_META(meta); + s = spltty(); + if (sc->qlen >= MAX_MBUFQ) + ERROUT(ENOBUFS); + m->m_nextpkt = NULL; + *sc->qtail = m; + sc->qtail = &m->m_nextpkt; + sc->qlen++; + QUEUECHECK(sc); + m = NULL; + if (sc->qlen == 1) + ngt_start(sc->tp); +done: + splx(s); + if (m) + m_freem(m); + return (error); +} + +/* + * Receive control message + */ +static int +ngt_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, + struct ng_mesg **rptr) +{ + const sc_p sc = (sc_p) node->private; + struct ng_mesg *resp = NULL; + int error = 0; + + switch (msg->header.typecookie) { + case NGM_TTY_COOKIE: + switch (msg->header.cmd) { + case NGM_TTY_SET_HOTCHAR: + { + int hotchar; + + if (msg->header.arglen != sizeof(int)) + ERROUT(EINVAL); + hotchar = *((int *) msg->data); + if (hotchar != (u_char) hotchar && hotchar != -1) + ERROUT(EINVAL); + sc->hotchar = hotchar; /* race condition is OK */ + break; + } + case NGM_TTY_GET_HOTCHAR: + NG_MKRESPONSE(resp, msg, sizeof(int), M_NOWAIT); + if (!resp) + ERROUT(ENOMEM); + /* Race condition here is OK */ + *((int *) resp->data) = sc->hotchar; + break; + default: + ERROUT(EINVAL); + } + break; + default: + ERROUT(EINVAL); + } + if (rptr) + *rptr = resp; + else if (resp) + FREE(resp, M_NETGRAPH); + +done: + FREE(msg, M_NETGRAPH); + return (error); +} + +/****************************************************************** + INITIALIZATION +******************************************************************/ + +/* + * Handle loading and unloading for this node type + */ +static int +ngt_mod_event(module_t mod, int event, void *data) +{ + struct ng_type *const type = data; + int s, error = 0; + + switch (event) { + case MOD_LOAD: +#ifdef __i386__ + /* Insure the soft net "engine" can't run during spltty code */ + s = splhigh(); + tty_imask |= softnet_imask; /* spltty() block spl[soft]net() */ + net_imask |= softtty_imask; /* splimp() block splsofttty() */ + net_imask |= tty_imask; /* splimp() block spltty() */ + update_intr_masks(); + splx(s); + + if (bootverbose) + log(LOG_DEBUG, "new masks: bio %x, tty %x, net %x\n", + bio_imask, tty_imask, net_imask); +#endif + + /* Register line discipline */ + s = spltty(); + if ((ngt_ldisc = ldisc_register(LDISC_LOAD, &ngt_disc)) < 0) { + splx(s); + log(LOG_ERR, "%s: can't register line discipline", + __FUNCTION__); + return (EIO); + } + splx(s); + + /* OK */ + log(LOG_INFO, "line discipline #%d registered to" + " netgraph node type \"%s\"\n", ngt_ldisc, type->name); + break; + + case MOD_UNLOAD: + + /* Unregister line discipline */ + s = spltty(); + ldisc_deregister(ngt_ldisc); + splx(s); + break; + + default: + error = EOPNOTSUPP; + break; + } + return (error); +} + diff --git a/sys/netgraph/ng_tty.h b/sys/netgraph/ng_tty.h new file mode 100644 index 0000000..ad7dc4e --- /dev/null +++ b/sys/netgraph/ng_tty.h @@ -0,0 +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 <archie@whistle.com> + * + * $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_ */ diff --git a/sys/netgraph/ng_vjc.c b/sys/netgraph/ng_vjc.c new file mode 100644 index 0000000..9755bd4 --- /dev/null +++ b/sys/netgraph/ng_vjc.c @@ -0,0 +1,439 @@ + +/* + * 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 <archie@whistle.com> + * + * $FreeBSD$ + * $Whistle: ng_vjc.c,v 1.14 1999/01/28 23:54:54 julian Exp $ + */ + +/* + * This node performs Van Jacobsen IP header (de)compression. + * You must have included net/slcompress.c in your kernel compilation. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/conf.h> +#include <sys/errno.h> +#include <sys/socket.h> +#include <sys/syslog.h> + +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> +#include <netgraph/ng_vjc.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> + +#include <net/slcompress.h> + +/* Check agreement with slcompress.c */ +#if MAX_STATES != NG_VJC_MAX_CHANNELS +#error NG_VJC_MAX_CHANNELS must be the same as MAX_STATES +#endif + +#define MAX_VJHEADER 16 + +/* Node private data */ +struct private { + struct ngm_vjc_config conf; + struct slcompress slc; + hook_p ip; + hook_p vjcomp; + hook_p vjuncomp; + hook_p vjip; +}; +typedef struct private *priv_p; + +#define ERROUT(x) do { error = (x); goto done; } while (0) + +/* Netgraph node methods */ +static int ng_vjc_constructor(node_p *nodep); +static int ng_vjc_rcvmsg(node_p node, struct ng_mesg *msg, + const char *retaddr, struct ng_mesg **resp); +static int ng_vjc_rmnode(node_p node); +static int ng_vjc_newhook(node_p node, hook_p hook, const char *name); +static int ng_vjc_rcvdata(hook_p hook, struct mbuf *m, meta_p t); +static int ng_vjc_disconnect(hook_p hook); + +/* Helper stuff */ +static struct mbuf *pulluphdrs(struct mbuf *m); + +/* Node type descriptor */ +static struct ng_type typestruct = { + NG_VERSION, + NG_VJC_NODE_TYPE, + NULL, + ng_vjc_constructor, + ng_vjc_rcvmsg, + ng_vjc_rmnode, + ng_vjc_newhook, + NULL, + NULL, + ng_vjc_rcvdata, + ng_vjc_rcvdata, + ng_vjc_disconnect +}; +NETGRAPH_INIT(vjc, &typestruct); + +/************************************************************************ + NETGRAPH NODE METHODS + ************************************************************************/ + +/* + * Create a new node + */ +static int +ng_vjc_constructor(node_p *nodep) +{ + priv_p priv; + int error; + + /* Allocate private structure */ + MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK); + if (priv == NULL) + return (ENOMEM); + bzero(priv, sizeof(*priv)); + + /* Call generic node constructor */ + if ((error = ng_make_node_common(&typestruct, nodep))) { + FREE(priv, M_NETGRAPH); + return (error); + } + (*nodep)->private = priv; + + /* Done */ + return (0); +} + +/* + * Add a new hook + */ +static int +ng_vjc_newhook(node_p node, hook_p hook, const char *name) +{ + const priv_p priv = (priv_p) node->private; + hook_p *hookp; + + /* Get hook */ + if (!strcmp(name, NG_VJC_HOOK_IP)) + hookp = &priv->ip; + else if (!strcmp(name, NG_VJC_HOOK_VJCOMP)) + hookp = &priv->vjcomp; + else if (!strcmp(name, NG_VJC_HOOK_VJUNCOMP)) + hookp = &priv->vjuncomp; + else if (!strcmp(name, NG_VJC_HOOK_VJIP)) + hookp = &priv->vjip; + else + return (EINVAL); + + /* See if already connected */ + if (*hookp) + return (EISCONN); + + /* OK */ + *hookp = hook; + return (0); +} + +/* + * Receive a control message + */ +static int +ng_vjc_rcvmsg(node_p node, struct ng_mesg *msg, + const char *raddr, struct ng_mesg **rptr) +{ + const priv_p priv = (priv_p) node->private; + struct ng_mesg *resp = NULL; + int error = 0; + + /* Check type cookie */ + switch (msg->header.typecookie) { + case NGM_VJC_COOKIE: + switch (msg->header.cmd) { + case NGM_VJC_CONFIG: + { + struct ngm_vjc_config *const c = + (struct ngm_vjc_config *) msg->data; + + if (msg->header.arglen != sizeof(*c) + || c->numChannels > NG_VJC_MAX_CHANNELS + || c->numChannels < NG_VJC_MIN_CHANNELS) + ERROUT(EINVAL); + if (priv->conf.enabled && c->enabled) + ERROUT(EALREADY); + if (c->enabled != 0) { + bzero(&priv->slc, sizeof(priv->slc)); + sl_compress_init(&priv->slc, c->numChannels); + } + priv->conf = *c; + break; + } + case NGM_VJC_GET_STATE: + NG_MKRESPONSE(resp, msg, sizeof(priv->slc), M_NOWAIT); + if (resp == NULL) + ERROUT(ENOMEM); + *((struct slcompress *) resp->data) = priv->slc; + break; + case NGM_VJC_CLR_STATS: + priv->slc.sls_packets = 0; + priv->slc.sls_compressed = 0; + priv->slc.sls_searches = 0; + priv->slc.sls_misses = 0; + priv->slc.sls_uncompressedin = 0; + priv->slc.sls_compressedin = 0; + priv->slc.sls_errorin = 0; + priv->slc.sls_tossed = 0; + break; + case NGM_VJC_RECV_ERROR: + priv->slc.flags |= SLF_TOSS; + break; + default: + error = EINVAL; + break; + } + break; + default: + error = EINVAL; + break; + } + if (rptr) + *rptr = resp; + else if (resp) + FREE(resp, M_NETGRAPH); + +done: + FREE(msg, M_NETGRAPH); + return (error); +} + +/* + * Receive data + */ +static int +ng_vjc_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) +{ + const node_p node = hook->node; + const priv_p priv = (priv_p) node->private; + int error = 0; + + if (hook == priv->ip) { /* outgoing packet */ + u_int type; + + if (!priv->conf.enabled) /* compression not enabled */ + type = TYPE_IP; + else { + struct ip *ip; + + if ((m = pulluphdrs(m)) == NULL) + ERROUT(ENOBUFS); + ip = mtod(m, struct ip *); + type = (ip->ip_p == IPPROTO_TCP) ? + sl_compress_tcp(m, ip, + &priv->slc, priv->conf.compressCID) : TYPE_IP; + } + switch (type) { + case TYPE_IP: + hook = priv->vjip; + break; + case TYPE_UNCOMPRESSED_TCP: + hook = priv->vjuncomp; + break; + case TYPE_COMPRESSED_TCP: + hook = priv->vjcomp; + break; + default: + panic(__FUNCTION__); + } + } else if (hook == priv->vjcomp) { /* incoming compressed packet */ + int vjlen; + u_int hlen; + u_char *hdr; + struct mbuf *mp; + + /* Are we initialized? */ + if (!priv->conf.enabled) { + m_freem(m); + m = NULL; + ERROUT(ENETDOWN); + } + + /* Uncompress packet to reconstruct TCP/IP header */ + if (!(m = m_pullup(m, MAX_VJHEADER))) + ERROUT(ENOBUFS); + vjlen = sl_uncompress_tcp_core(mtod(m, u_char *), + m->m_len, m->m_pkthdr.len, TYPE_COMPRESSED_TCP, + &priv->slc, &hdr, &hlen); + if (vjlen <= 0) { + m_freem(m); + m = NULL; + ERROUT(EINVAL); + } + + /* Copy the reconstructed TCP/IP headers into a new mbuf */ + MGETHDR(mp, M_DONTWAIT, MT_DATA); + if (!mp) + goto compfailmem; + mp->m_len = 0; + mp->m_next = NULL; + if (hlen > MHLEN) { + MCLGET(mp, M_DONTWAIT); + if (M_TRAILINGSPACE(mp) < hlen) { + m_freem(mp); /* can't get a cluster, drop */ +compfailmem: + m_freem(m); + m = NULL; + ERROUT(ENOBUFS); + } + } + bcopy(hdr, mtod(mp, u_char *), hlen); + mp->m_len = hlen; + + /* Stick header and rest of packet together */ + m->m_data += vjlen; + m->m_len -= vjlen; + if (m->m_len <= M_TRAILINGSPACE(mp)) { + bcopy(mtod(m, u_char *), + mtod(mp, u_char *) + mp->m_len, m->m_len); + mp->m_len += m->m_len; + MFREE(m, mp->m_next); + } else + mp->m_next = m; + m = mp; + hook = priv->ip; + } else if (hook == priv->vjuncomp) { /* incoming uncompressed pkt */ + u_int hlen; + u_char *hdr; + + /* Are we initialized? */ + if (!priv->conf.enabled) { + m_freem(m); + m = NULL; + ERROUT(ENETDOWN); + } + + /* Run packet through uncompressor */ + if ((m = pulluphdrs(m)) == NULL) + ERROUT(ENOBUFS); + if (sl_uncompress_tcp_core(mtod(m, u_char *), + m->m_len, m->m_pkthdr.len, TYPE_UNCOMPRESSED_TCP, + &priv->slc, &hdr, &hlen) < 0) { + m_freem(m); + m = NULL; + ERROUT(EINVAL); + } + hook = priv->ip; + } else if (hook == priv->vjip) /* incoming regular packet (bypass) */ + hook = priv->ip; + else + panic(__FUNCTION__); + +done: + if (m) + NG_SEND_DATA(error, hook, m, meta); + else + NG_FREE_META(meta); + return (error); +} + +/* + * Shutdown node + */ +static int +ng_vjc_rmnode(node_p node) +{ + const priv_p priv = (priv_p) node->private; + + node->flags |= NG_INVALID; + ng_cutlinks(node); + ng_unname(node); + bzero(priv, sizeof(*priv)); + FREE(priv, M_NETGRAPH); + node->private = NULL; + ng_unref(node); + return (0); +} + +/* + * Hook disconnection + */ +static int +ng_vjc_disconnect(hook_p hook) +{ + if (hook->node->numhooks == 0) + ng_rmnode(hook->node); + return (0); +} + +/************************************************************************ + HELPER STUFF + ************************************************************************/ + +/* + * Pull up the full IP and TCP headers of a packet. This is optimized + * for the common case of standard length headers. If packet is not + * a TCP packet, just pull up the IP header. + */ +static struct mbuf * +pulluphdrs(struct mbuf *m) +{ + struct ip *ip; + struct tcphdr *tcp; + int ihlen, thlen; + + if ((m = m_pullup(m, sizeof(*ip) + sizeof(*tcp))) == NULL) + return (NULL); + ip = mtod(m, struct ip *); + if (ip->ip_p != IPPROTO_TCP) + return (m); + if ((ihlen = (ip->ip_hl << 2)) != sizeof(*ip)) { + if (!(m = m_pullup(m, ihlen + sizeof(*tcp)))) + return (NULL); + ip = mtod(m, struct ip *); + } + tcp = (struct tcphdr *) ((u_char *) ip + ihlen); + if ((thlen = (tcp->th_off << 2)) != sizeof(*tcp)) + m = m_pullup(m, ihlen + thlen); + return (m); +} + diff --git a/sys/netgraph/ng_vjc.h b/sys/netgraph/ng_vjc.h new file mode 100644 index 0000000..5067f6a --- /dev/null +++ b/sys/netgraph/ng_vjc.h @@ -0,0 +1,75 @@ + +/* + * 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 <archie@whistle.com> + * + * $FreeBSD$ + * $Whistle: ng_vjc.h,v 1.6 1999/01/25 02:40:22 archie Exp $ + */ + +#ifndef _NETGRAPH_VJC_H_ +#define _NETGRAPH_VJC_H_ + + /* Node type name and magic cookie */ +#define NG_VJC_NODE_TYPE "vjc" +#define NGM_VJC_COOKIE 868219207 + + /* Hook names */ +#define NG_VJC_HOOK_IP "ip" /* normal IP traffic */ +#define NG_VJC_HOOK_VJCOMP "vjcomp" /* compressed TCP */ +#define NG_VJC_HOOK_VJUNCOMP "vjuncomp" /* uncompressed TCP */ +#define NG_VJC_HOOK_VJIP "vjip" /* uncompressed IP */ + + /* Minimum and maximum number of channels */ +#define NG_VJC_MIN_CHANNELS 4 +#define NG_VJC_MAX_CHANNELS 16 + + /* Configure struct */ +struct ngm_vjc_config { + u_char enabled; /* Enable compression/decompression */ + u_char numChannels; /* Number of outgoing channels */ + u_char compressCID; /* OK to compress outgoing CID's */ +}; + + /* Netgraph commands */ +enum { + NGM_VJC_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_ */ |