diff options
Diffstat (limited to 'sys')
37 files changed, 3433 insertions, 155 deletions
diff --git a/sys/conf/files b/sys/conf/files index ea7c95d..fbda398 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -572,6 +572,7 @@ netatm/uni/unisig_subr.c optional atm_uni atm_core netatm/uni/unisig_util.c optional atm_uni atm_core netatm/uni/unisig_vc_state.c optional atm_uni atm_core netgraph/ng_base.c optional netgraph +netgraph/ng_parse.c optional netgraph netgraph/ng_async.c optional netgraph_async netgraph/ng_cisco.c optional netgraph_cisco netgraph/ng_echo.c optional netgraph_echo diff --git a/sys/modules/netgraph/cisco/ng_cisco.4 b/sys/modules/netgraph/cisco/ng_cisco.4 index 452252f..26370cd 100644 --- a/sys/modules/netgraph/cisco/ng_cisco.4 +++ b/sys/modules/netgraph/cisco/ng_cisco.4 @@ -42,6 +42,7 @@ .Nm ng_cisco .Nd Cisco HDLC protocol netgraph node type .Sh SYNOPSIS +.Fd #include <netinet/in.h> .Fd #include <netgraph/ng_cisco.h> .Sh DESCRIPTION The diff --git a/sys/modules/netgraph/cisco/ng_cisco.8 b/sys/modules/netgraph/cisco/ng_cisco.8 index 452252f..26370cd 100644 --- a/sys/modules/netgraph/cisco/ng_cisco.8 +++ b/sys/modules/netgraph/cisco/ng_cisco.8 @@ -42,6 +42,7 @@ .Nm ng_cisco .Nd Cisco HDLC protocol netgraph node type .Sh SYNOPSIS +.Fd #include <netinet/in.h> .Fd #include <netgraph/ng_cisco.h> .Sh DESCRIPTION The diff --git a/sys/modules/netgraph/netgraph/Makefile b/sys/modules/netgraph/netgraph/Makefile index eb37f19..eaaf4ac 100644 --- a/sys/modules/netgraph/netgraph/Makefile +++ b/sys/modules/netgraph/netgraph/Makefile @@ -2,7 +2,7 @@ # $Whistle: Makefile,v 1.2 1999/01/19 19:39:22 archie Exp $ KMOD= netgraph -SRCS= ng_base.c +SRCS= ng_base.c ng_parse.c MAN4= netgraph.4 .include <bsd.kmod.mk> diff --git a/sys/modules/netgraph/netgraph/netgraph.4 b/sys/modules/netgraph/netgraph/netgraph.4 index e85e22e..f0d0e85 100644 --- a/sys/modules/netgraph/netgraph/netgraph.4 +++ b/sys/modules/netgraph/netgraph/netgraph.4 @@ -57,7 +57,7 @@ Nodes communicate along the edges to process data, implement protocols, etc. The aim of .Nm is to supplement rather than replace the existing kernel networking -infrastructure. It provides: +infrastructure. It provides: .Pp .Bl -bullet -compact -offset 2n .It @@ -132,8 +132,19 @@ in the graph, one edge at a time. The first mbuf in a chain must have the .Dv M_PKTHDR flag set. Each node decides how to handle data coming in on its hooks. .Pp -Control messages are type-specific structures sent from one node directly -to an arbitrary other node. There are two ways to address such a message. If +Control messages are type-specific C structures sent from one node +directly to some arbitrary other node. Control messages have a common +header format, followed by type-specific data, and are binary structures +for efficiency. However, node types also may support conversion of the +type specific data between binary and +ASCII for debugging and human interface purposes (see the +.Dv NGM_ASCII2BINARY +and +.Dv NGM_BINARY2ASCII +generic control messages below). Nodes are not required to support +these conversions. +.Pp +There are two ways to address a control message. If there is a sequence of edges connecting the two nodes, the message may be ``source routed'' by specifying the corresponding sequence of hooks as the destination address for the message (relative @@ -448,8 +459,17 @@ incrementing From a hook you can obtain the corresponding node, and from a node the list of all active hooks. .Pp -Node types are described by this structure: +Node types are described by these structures: .Bd -literal +/** How to convert a control message from binary <-> ASCII */ +struct ng_cmdlist { + u_int32_t cookie; /* typecookie */ + int cmd; /* command number */ + const char *name; /* command name */ + const struct ng_parse_type *mesgType; /* args if !NGF_RESP */ + const struct ng_parse_type *respType; /* args if NGF_RESP */ +}; + struct ng_type { u_int32_t version; /* Must equal NG_VERSION */ const char *name; /* Unique type name */ @@ -476,6 +496,9 @@ struct ng_type { struct mbuf *m, /* The data in an mbuf */ meta_p meta); /* Meta-data, if any */ int (*disconnect)(hook_p hook); /* Notify disconnection of hook */ + + /** How to convert control messages binary <-> ASCII */ + const struct ng_cmdlist *cmdlist; /* Optional; may be NULL */ }; .Ed .Pp @@ -554,6 +577,58 @@ purposes only). .Pp Some modules may choose to implement messages from more than one of the header files and thus recognize more than one type cookie. +.Sh Control Message ASCII Form +Control messages are in binary format for efficiency. However, for +debugging and human interface purposes, and if the node type supports +it, control messages may be converted to and from an equivalent ASCII +form. The ASCII form is similar to the binary form, with two exceptions: +.Pp +.Bl -tag -compact -width xxx +.It o +The +.Dv cmdstr +header field must contain the ASCII name of the command, corresponding to the +.Dv cmd +header field. +.It o +The +.Dv args +field contains a NUL-terminated ASCII string version of the message arguments. +.El +.Pp +In general, the arguments field of a control messgage can be any +arbitrary C data type. Netgraph includes parsing routines to support +some pre-defined datatypes in ASCII with this simple syntax: +.Pp +.Bl -tag -compact -width xxx +.It o +Integer types are represented by base 8, 10, or 16 numbers. +.It o +Strings are enclosed in double quotes and respect the normal +C language backslash escapes. +.It o +IP addresses have the obvious form. +.It o +Arrays are enclosed in square brackets, with the elements listed +consecutively starting at index zero. An element may have an optional +index and equals sign preceeding it. Whenever an element +does not have an explicit index, the index is implicitly the previous +element's index plus one. +.It o +Structures are enclosed in curly braces, and each field is specified +in the form ``fieldname=value''. +.It o +Any array element or structure field whose value is equal to its +``default value'' may be omitted. For integer types, the default value +is usually zero; for string types, the empty string. +.It o +Array elements and structure fields may be specified in any order. +.El +.Pp +Each node type may define its own arbitrary types by providing +the necessary routines to parse and unparse. ASCII forms defined +for a specific node type are documented in the documentation for +that node type. .Sh Generic Control Messages There are a number of standard predefined messages that will work for any node, as they are supported directly by the framework itself. @@ -612,6 +687,32 @@ elect to not support this message. The text response must be less than .Dv NG_TEXTRESPONSE bytes in length (presently 1024). This can be used to return general status information in human readable form. +.It Dv NGM_BINARY2ASCII +This generic converts a binary control message to its ASCII form. +The entire control message to be converted is contained within the +arguments field of the +.Dv Dv NGM_BINARY2ASCII +message itself. If successful, the reply will contain the same control +message in ASCII form. +A node will typically only know how to translate messages that it +itself understands, so +the target node of the +.Dv NGM_BINARY2ASCII +is often the same node that would actually receive that message. +.It Dv NGM_ASCII2BINARY +The opposite of +.Dv NGM_BINARY2ASCII . +The entire control message to be converted, in ASCII form, is contained +in the arguments section of the +.Dv NGM_ASCII2BINARY +and need only have the +.Dv flags , +.Dv cmdstr , +and +.Dv arglen +header fields filled in, plus the NUL-terminated string version of +the arguments in the arguments field. If successful, the reply +contains the binary version of the control message. .El .Sh Metadata Data moving through the diff --git a/sys/modules/netgraph/pppoe/ng_pppoe.4 b/sys/modules/netgraph/pppoe/ng_pppoe.4 index c0b0209..69d0016 100644 --- a/sys/modules/netgraph/pppoe/ng_pppoe.4 +++ b/sys/modules/netgraph/pppoe/ng_pppoe.4 @@ -42,6 +42,7 @@ .Nm ng_pppoe .Nd RFC 2516 PPPOE protocol netgraph node type .Sh SYNOPSIS +.Fd #include <net/ethernet.h> .Fd #include <netgraph/ng_pppoe.h> .Sh DESCRIPTION The diff --git a/sys/modules/netgraph/pppoe/ng_pppoe.8 b/sys/modules/netgraph/pppoe/ng_pppoe.8 index c0b0209..69d0016 100644 --- a/sys/modules/netgraph/pppoe/ng_pppoe.8 +++ b/sys/modules/netgraph/pppoe/ng_pppoe.8 @@ -42,6 +42,7 @@ .Nm ng_pppoe .Nd RFC 2516 PPPOE protocol netgraph node type .Sh SYNOPSIS +.Fd #include <net/ethernet.h> .Fd #include <netgraph/ng_pppoe.h> .Sh DESCRIPTION The diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index dc55d54..ec0f5cf 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -150,7 +150,8 @@ static struct ng_type typestruct = { ngether_connect, ngether_rcvdata, ngether_rcvdata, - ngether_disconnect + ngether_disconnect, + NULL }; #define AC2NG(AC) ((node_p)((AC)->ac_ng)) diff --git a/sys/netgraph/netgraph.h b/sys/netgraph/netgraph.h index 94649e6..d693aa5 100644 --- a/sys/netgraph/netgraph.h +++ b/sys/netgraph/netgraph.h @@ -112,7 +112,7 @@ struct meta_field_header { }; /* To zero out an option 'in place' set it's cookie to this */ -#define INVALID_COOKIE 865455152 +#define NGM_INVALID_COOKIE 865455152 /* This part of the metadata is always present if the pointer is non NULL */ struct ng_meta { @@ -132,7 +132,7 @@ typedef struct ng_meta *meta_p; /* node method definitions */ typedef int ng_constructor_t(node_p *node); typedef int ng_rcvmsg_t(node_p node, struct ng_mesg *msg, - const char *retaddr, struct ng_mesg **resp); + const char *retaddr, struct ng_mesg **resp); typedef int ng_shutdown_t(node_p node); typedef int ng_newhook_t(node_p node, hook_p hook, const char *name); typedef hook_p ng_findhook_t(node_p node, const char *name); @@ -141,12 +141,26 @@ typedef int ng_rcvdata_t(hook_p hook, struct mbuf *m, meta_p meta); typedef int ng_disconnect_t(hook_p hook); /* + * Command list -- each node type specifies the command that it knows + * how to convert between ASCII and binary using an array of these. + * The last element in the array must be a terminator with cookie=0. + */ + +struct ng_cmdlist { + u_int32_t cookie; /* command typecookie */ + int cmd; /* command number */ + const char *name; /* command name */ + const struct ng_parse_type *mesgType; /* args if !NGF_RESP */ + const struct ng_parse_type *respType; /* args if NGF_RESP */ +}; + +/* * Structure of a node type */ struct ng_type { - u_int32_t version; /* must equal NG_VERSION */ - const char *name; /* Unique type name */ + u_int32_t version; /* must equal NG_VERSION */ + const char *name; /* Unique type name */ modeventhand_t mod_event; /* Module event handler (optional) */ ng_constructor_t *constructor; /* Node constructor */ ng_rcvmsg_t *rcvmsg; /* control messages come here */ @@ -158,7 +172,9 @@ struct ng_type { ng_rcvdata_t *rcvdataq; /* or here if been queued */ ng_disconnect_t *disconnect; /* notify on disconnect */ - /* R/W data private to the base netgraph code DON'T TOUCH!*/ + const struct ng_cmdlist *cmdlist; /* commands we can convert */ + + /* R/W data private to the base netgraph code DON'T TOUCH! */ LIST_ENTRY(ng_type) types; /* linked list of all types */ int refs; /* number of instances */ }; diff --git a/sys/netgraph/ng_UI.c b/sys/netgraph/ng_UI.c index d30005f..21a10f5 100644 --- a/sys/netgraph/ng_UI.c +++ b/sys/netgraph/ng_UI.c @@ -94,7 +94,8 @@ static struct ng_type typestruct = { NULL, ng_UI_rcvdata, ng_UI_rcvdata, - ng_UI_disconnect + ng_UI_disconnect, + NULL }; NETGRAPH_INIT(UI, &typestruct); diff --git a/sys/netgraph/ng_UI.h b/sys/netgraph/ng_UI.h index f41e5a9..52fc362 100644 --- a/sys/netgraph/ng_UI.h +++ b/sys/netgraph/ng_UI.h @@ -45,7 +45,7 @@ /* Node type name and cookie */ #define NG_UI_NODE_TYPE "UI" -#define NGM_UI_NODE_COOKIE 884639499 +#define NGM_UI_COOKIE 884639499 /* Hook names */ #define NG_UI_HOOK_DOWNSTREAM "downstream" diff --git a/sys/netgraph/ng_async.c b/sys/netgraph/ng_async.c index ca96dbe..4c7e3ab 100644 --- a/sys/netgraph/ng_async.c +++ b/sys/netgraph/ng_async.c @@ -61,6 +61,7 @@ #include <netgraph/ng_message.h> #include <netgraph/netgraph.h> #include <netgraph/ng_async.h> +#include <netgraph/ng_parse.h> #include <net/ppp_defs.h> @@ -91,7 +92,7 @@ typedef struct ng_async_private *sc_p; #define ERROUT(x) do { error = (x); goto done; } while (0) /* Netgraph methods */ -static ng_constructor_t nga_constructor; +static ng_constructor_t nga_constructor; static ng_rcvdata_t nga_rcvdata; static ng_rcvmsg_t nga_rcvmsg; static ng_shutdown_t nga_shutdown; @@ -102,6 +103,55 @@ static ng_disconnect_t nga_disconnect; static int nga_rcv_sync(const sc_p sc, struct mbuf *m, meta_p meta); static int nga_rcv_async(const sc_p sc, struct mbuf *m, meta_p meta); +/* Parse type for struct ng_async_cfg */ +static const struct ng_parse_struct_info + nga_config_type_info = NG_ASYNC_CONFIG_TYPE_INFO; +static const struct ng_parse_type nga_config_type = { + &ng_parse_struct_type, + &nga_config_type_info +}; + +/* Parse type for struct ng_async_stat */ +static const struct ng_parse_struct_info + nga_stats_type_info = NG_ASYNC_STATS_TYPE_INFO; +static const struct ng_parse_type nga_stats_type = { + &ng_parse_struct_type, + &nga_stats_type_info, +}; + +/* List of commands and how to convert arguments to/from ASCII */ +static const struct ng_cmdlist nga_cmdlist[] = { + { + NGM_ASYNC_COOKIE, + NGM_ASYNC_CMD_SET_CONFIG, + "setconfig", + &nga_config_type, + NULL + }, + { + NGM_ASYNC_COOKIE, + NGM_ASYNC_CMD_GET_CONFIG, + "getconfig", + NULL, + &nga_config_type + }, + { + NGM_ASYNC_COOKIE, + NGM_ASYNC_CMD_GET_STATS, + "getstats", + NULL, + &nga_stats_type + }, + { + NGM_ASYNC_COOKIE, + NGM_ASYNC_CMD_CLR_STATS, + "clrstats", + &nga_stats_type, + NULL + }, + { 0 } +}; + /* Define the netgraph node type */ static struct ng_type typestruct = { NG_VERSION, @@ -115,7 +165,8 @@ static struct ng_type typestruct = { NULL, nga_rcvdata, nga_rcvdata, - nga_disconnect + nga_disconnect, + nga_cmdlist }; NETGRAPH_INIT(async, &typestruct); diff --git a/sys/netgraph/ng_async.h b/sys/netgraph/ng_async.h index a91ef16..63f6e43 100644 --- a/sys/netgraph/ng_async.h +++ b/sys/netgraph/ng_async.h @@ -45,7 +45,7 @@ /* Type name and cookie */ #define NG_ASYNC_NODE_TYPE "async" -#define NGM_ASYNC_COOKIE 886473718 +#define NGM_ASYNC_COOKIE 886473717 /* Hook names */ #define NG_ASYNC_HOOK_SYNC "sync" /* Sync frames */ @@ -68,6 +68,21 @@ struct ng_async_stat { u_int32_t asyncBadCheckSums; }; +/* Keep this in sync with the above structure definition */ +#define NG_ASYNC_STATS_TYPE_INFO { \ + { \ + { "syncOctets", &ng_parse_int32_type }, \ + { "syncFrames", &ng_parse_int32_type }, \ + { "syncOverflows", &ng_parse_int32_type }, \ + { "asyncOctets", &ng_parse_int32_type }, \ + { "asyncFrames", &ng_parse_int32_type }, \ + { "asyncRunts", &ng_parse_int32_type }, \ + { "asyncOverflows", &ng_parse_int32_type }, \ + { "asyncBadCheckSums",&ng_parse_int32_type }, \ + { NULL }, \ + } \ +} + /* Configuration for this node */ struct ng_async_cfg { u_char enabled; /* Turn encoding on/off */ @@ -76,6 +91,17 @@ struct ng_async_cfg { u_int32_t accm; /* ACCM encoding */ }; +/* Keep this in sync with the above structure definition */ +#define NG_ASYNC_CONFIG_TYPE_INFO { \ + { \ + { "enabled", &ng_parse_int8_type }, \ + { "amru", &ng_parse_int16_type }, \ + { "smru", &ng_parse_int16_type }, \ + { "accm", &ng_parse_int32_type }, \ + { NULL }, \ + } \ +} + /* Commands */ enum { NGM_ASYNC_CMD_GET_STATS = 1, /* returns struct ng_async_stat */ diff --git a/sys/netgraph/ng_base.c b/sys/netgraph/ng_base.c index 84cd696..7d20e80 100644 --- a/sys/netgraph/ng_base.c +++ b/sys/netgraph/ng_base.c @@ -62,6 +62,7 @@ #include <netgraph/ng_message.h> #include <netgraph/netgraph.h> +#include <netgraph/ng_parse.h> /* List of all nodes */ static LIST_HEAD(, ng_node) nodelist; @@ -114,6 +115,179 @@ static ng_ID_t nextID = 1; /************************************************************************ + Parse type definitions for generic messages +************************************************************************/ + +/* Handy structure parse type defining macro */ +#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args) \ +static const struct ng_parse_struct_info \ + ng_ ## lo ## _type_info = NG_GENERIC_ ## up ## _INFO args; \ +static const struct ng_parse_type ng_generic_ ## lo ## _type = { \ + &ng_parse_struct_type, \ + &ng_ ## lo ## _type_info \ +} + +DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ()); +DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ()); +DEFINE_PARSE_STRUCT_TYPE(name, NAME, ()); +DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ()); +DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ()); +DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ()); +DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type)); + +/* Get length of an array when the length is stored as a 32 bit + value immediately preceeding the array -- as with struct namelist + and struct typelist. */ +static int +ng_generic_list_getLength(const struct ng_parse_type *type, + const u_char *start, const u_char *buf) +{ + return *((const u_int32_t *)(buf - 4)); +} + +/* Get length of the array of struct linkinfo inside a struct hooklist */ +static int +ng_generic_linkinfo_getLength(const struct ng_parse_type *type, + const u_char *start, const u_char *buf) +{ + const struct hooklist *hl = (const struct hooklist *)start; + + return hl->nodeinfo.hooks; +} + +/* Array type for a variable length array of struct namelist */ +static const struct ng_parse_array_info ng_nodeinfoarray_type_info = { + &ng_generic_nodeinfo_type, + &ng_generic_list_getLength +}; +static const struct ng_parse_type ng_generic_nodeinfoarray_type = { + &ng_parse_array_type, + &ng_nodeinfoarray_type_info +}; + +/* Array type for a variable length array of struct typelist */ +static const struct ng_parse_array_info ng_typeinfoarray_type_info = { + &ng_generic_typeinfo_type, + &ng_generic_list_getLength +}; +static const struct ng_parse_type ng_generic_typeinfoarray_type = { + &ng_parse_array_type, + &ng_typeinfoarray_type_info +}; + +/* Array type for array of struct linkinfo in struct hooklist */ +static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = { + &ng_generic_linkinfo_type, + &ng_generic_linkinfo_getLength +}; +static const struct ng_parse_type ng_generic_linkinfo_array_type = { + &ng_parse_array_type, + &ng_generic_linkinfo_array_type_info +}; + +DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type)); +DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST, + (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type)); +DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES, + (&ng_generic_nodeinfoarray_type)); + +/* List of commands and how to convert arguments to/from ASCII */ +static const struct ng_cmdlist ng_generic_cmds[] = { + { + NGM_GENERIC_COOKIE, + NGM_SHUTDOWN, + "shutdown", + NULL, + NULL + }, + { + NGM_GENERIC_COOKIE, + NGM_MKPEER, + "mkpeer", + &ng_generic_mkpeer_type, + NULL + }, + { + NGM_GENERIC_COOKIE, + NGM_CONNECT, + "connect", + &ng_generic_connect_type, + NULL + }, + { + NGM_GENERIC_COOKIE, + NGM_NAME, + "name", + &ng_generic_name_type, + NULL + }, + { + NGM_GENERIC_COOKIE, + NGM_RMHOOK, + "rmhook", + &ng_generic_rmhook_type, + NULL + }, + { + NGM_GENERIC_COOKIE, + NGM_NODEINFO, + "nodeinfo", + NULL, + &ng_generic_nodeinfo_type + }, + { + NGM_GENERIC_COOKIE, + NGM_LISTHOOKS, + "listhooks", + NULL, + &ng_generic_hooklist_type + }, + { + NGM_GENERIC_COOKIE, + NGM_LISTNAMES, + "listnames", + NULL, + &ng_generic_listnodes_type /* same as NGM_LISTNODES */ + }, + { + NGM_GENERIC_COOKIE, + NGM_LISTNODES, + "listnodes", + NULL, + &ng_generic_listnodes_type + }, + { + NGM_GENERIC_COOKIE, + NGM_LISTTYPES, + "listtypes", + NULL, + &ng_generic_typeinfo_type + }, + { + NGM_GENERIC_COOKIE, + NGM_TEXT_STATUS, + "textstatus", + NULL, + &ng_parse_string_type + }, + { + NGM_GENERIC_COOKIE, + NGM_ASCII2BINARY, + "ascii2binary", + &ng_parse_ng_mesg_type, + &ng_parse_ng_mesg_type + }, + { + NGM_GENERIC_COOKIE, + NGM_BINARY2ASCII, + "binary2ascii", + &ng_parse_ng_mesg_type, + &ng_parse_ng_mesg_type + }, + { 0 } +}; + +/************************************************************************ Node routines ************************************************************************/ @@ -1256,6 +1430,151 @@ ng_generic_msg(node_p here, struct ng_mesg *msg, const char *retaddr, break; } + case NGM_BINARY2ASCII: + { + int bufSize = 2000; /* XXX hard coded constant */ + const struct ng_parse_type *argstype; + const struct ng_cmdlist *c; + struct ng_mesg *rp, *binary, *ascii; + + /* Data area must contain a valid netgraph message */ + binary = (struct ng_mesg *)msg->data; + if (msg->header.arglen < sizeof(struct ng_mesg) + || msg->header.arglen - sizeof(struct ng_mesg) + < binary->header.arglen) { + error = EINVAL; + break; + } + + /* Get a response message with lots of room */ + NG_MKRESPONSE(rp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); + if (rp == NULL) { + error = ENOMEM; + break; + } + ascii = (struct ng_mesg *)rp->data; + + /* Copy binary message header to response message payload */ + bcopy(binary, ascii, sizeof(*binary)); + + /* Find command by matching typecookie and command number */ + for (c = here->type->cmdlist; + c != NULL && c->name != NULL; c++) { + if (binary->header.typecookie == c->cookie + && binary->header.cmd == c->cmd) + break; + } + if (c == NULL || c->name == NULL) { + for (c = ng_generic_cmds; c->name != NULL; c++) { + if (binary->header.typecookie == c->cookie + && binary->header.cmd == c->cmd) + break; + } + if (c->name == NULL) { + FREE(rp, M_NETGRAPH); + error = ENOSYS; + break; + } + } + + /* Convert command name to ASCII */ + snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), + "%s", c->name); + + /* Convert command arguments to ASCII */ + argstype = (binary->header.flags & NGF_RESP) ? + c->respType : c->mesgType; + if (argstype == NULL) + *ascii->data = '\0'; + else { + if ((error = ng_unparse(argstype, + (u_char *)binary->data, + ascii->data, bufSize)) != 0) { + FREE(rp, M_NETGRAPH); + break; + } + } + + /* Return the result as struct ng_mesg plus ASCII string */ + bufSize = strlen(ascii->data) + 1; + ascii->header.arglen = bufSize; + rp->header.arglen = sizeof(*ascii) + bufSize; + *resp = rp; + break; + } + + case NGM_ASCII2BINARY: + { + int bufSize = 2000; /* XXX hard coded constant */ + const struct ng_cmdlist *c; + const struct ng_parse_type *argstype; + struct ng_mesg *rp, *ascii, *binary; + int off; + + /* Data area must contain at least a struct ng_mesg + '\0' */ + ascii = (struct ng_mesg *)msg->data; + if (msg->header.arglen < sizeof(*ascii) + 1 + || ascii->header.arglen < 1 + || msg->header.arglen + < sizeof(*ascii) + ascii->header.arglen) { + error = EINVAL; + break; + } + ascii->data[ascii->header.arglen - 1] = '\0'; + + /* Get a response message with lots of room */ + NG_MKRESPONSE(rp, msg, sizeof(*binary) + bufSize, M_NOWAIT); + if (rp == NULL) { + error = ENOMEM; + break; + } + binary = (struct ng_mesg *)rp->data; + + /* Copy ASCII message header to response message payload */ + bcopy(ascii, binary, sizeof(*ascii)); + + /* Find command by matching ASCII command string */ + for (c = here->type->cmdlist; + c != NULL && c->name != NULL; c++) { + if (strcmp(ascii->header.cmdstr, c->name) == 0) + break; + } + if (c == NULL || c->name == NULL) { + for (c = ng_generic_cmds; c->name != NULL; c++) { + if (strcmp(ascii->header.cmdstr, c->name) == 0) + break; + } + if (c->name == NULL) { + FREE(rp, M_NETGRAPH); + error = ENOSYS; + break; + } + } + + /* Convert command name to binary */ + binary->header.cmd = c->cmd; + binary->header.typecookie = c->cookie; + + /* Convert command arguments to binary */ + argstype = (binary->header.flags & NGF_RESP) ? + c->respType : c->mesgType; + if (argstype == NULL) + bufSize = 0; + else { + if ((error = ng_parse(argstype, ascii->data, + &off, (u_char *)binary->data, &bufSize)) != 0) { + FREE(rp, M_NETGRAPH); + break; + } + } + + /* Return the result */ + binary->header.arglen = bufSize; + rp->header.arglen = sizeof(*binary) + bufSize; + *resp = rp; + break; + } + case NGM_TEXT_STATUS: /* * This one is tricky as it passes the command down to the diff --git a/sys/netgraph/ng_cisco.c b/sys/netgraph/ng_cisco.c index cc30d3d..26898ce 100644 --- a/sys/netgraph/ng_cisco.c +++ b/sys/netgraph/ng_cisco.c @@ -67,6 +67,7 @@ #include <netgraph/ng_message.h> #include <netgraph/netgraph.h> +#include <netgraph/ng_parse.h> #include <netgraph/ng_cisco.h> #define CISCO_MULTICAST 0x8f /* Cisco multicast address */ @@ -105,7 +106,7 @@ struct protoent { struct cisco_priv { u_long local_seq; u_long remote_seq; - u_long seq_retries; /* how many times we've been here throwing out + u_long seqRetries; /* how many times we've been here throwing out * the same sequence number without ack */ node_p node; struct callout_handle handle; @@ -131,6 +132,48 @@ static int cisco_input(sc_p sc, struct mbuf *m, meta_p meta); static void cisco_keepalive(void *arg); static int cisco_send(sc_p sc, int type, long par1, long par2); +/* Parse type for struct ng_cisco_ipaddr */ +static const struct ng_parse_struct_info + ng_cisco_ipaddr_type_info = NG_CISCO_IPADDR_TYPE_INFO; +static const struct ng_parse_type ng_cisco_ipaddr_type = { + &ng_parse_struct_type, + &ng_cisco_ipaddr_type_info +}; + +/* Parse type for struct ng_async_stat */ +static const struct ng_parse_struct_info + ng_cisco_stats_type_info = NG_CISCO_STATS_TYPE_INFO; +static const struct ng_parse_type ng_cisco_stats_type = { + &ng_parse_struct_type, + &ng_cisco_stats_type_info, +}; + +/* List of commands and how to convert arguments to/from ASCII */ +static const struct ng_cmdlist ng_cisco_cmdlist[] = { + { + NGM_CISCO_COOKIE, + NGM_CISCO_SET_IPADDR, + "setipaddr", + &ng_cisco_ipaddr_type, + NULL + }, + { + NGM_CISCO_COOKIE, + NGM_CISCO_GET_IPADDR, + "getipaddr", + NULL, + &ng_cisco_ipaddr_type + }, + { + NGM_CISCO_COOKIE, + NGM_CISCO_GET_STATUS, + "getstats", + NULL, + &ng_cisco_stats_type + }, + { 0 } +}; + /* Node type */ static struct ng_type typestruct = { NG_VERSION, @@ -144,7 +187,8 @@ static struct ng_type typestruct = { NULL, cisco_rcvdata, cisco_rcvdata, - cisco_disconnect + cisco_disconnect, + ng_cisco_cmdlist }; NETGRAPH_INIT(cisco, &typestruct); @@ -237,7 +281,7 @@ cisco_rcvmsg(node_p node, struct ng_mesg *msg, pos = sprintf(arg, "keepalive period: %d sec; ", KEEPALIVE_SECS); pos += sprintf(arg + pos, - "unacknowledged keepalives: %ld", sc->seq_retries); + "unacknowledged keepalives: %ld", sc->seqRetries); resp->header.arglen = pos + 1; break; } @@ -278,16 +322,16 @@ cisco_rcvmsg(node_p node, struct ng_mesg *msg, } case NGM_CISCO_GET_STATUS: { - struct ngciscostat *stat; + struct ng_cisco_stats *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; + stat = (struct ng_cisco_stats *)resp->data; + stat->seqRetries = sc->seqRetries; + stat->keepAlivePeriod = KEEPALIVE_SECS; break; } default: @@ -445,7 +489,7 @@ cisco_input(sc_p sc, struct mbuf *m, meta_p meta) sc->remote_seq = ntohl(p->par1); if (sc->local_seq == ntohl(p->par2)) { sc->local_seq++; - sc->seq_retries = 0; + sc->seqRetries = 0; } break; case CISCO_ADDR_REQ: @@ -508,7 +552,7 @@ cisco_keepalive(void *arg) int s = splimp(); cisco_send(sc, CISCO_KEEPALIVE_REQ, sc->local_seq, sc->remote_seq); - sc->seq_retries++; + sc->seqRetries++; splx(s); sc->handle = timeout(cisco_keepalive, sc, hz * KEEPALIVE_SECS); } diff --git a/sys/netgraph/ng_cisco.h b/sys/netgraph/ng_cisco.h index 559cc14..a51dbfa 100644 --- a/sys/netgraph/ng_cisco.h +++ b/sys/netgraph/ng_cisco.h @@ -56,21 +56,38 @@ /* 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, + NGM_CISCO_SET_IPADDR = 1, /* requires a struct ng_cisco_ipaddr */ + NGM_CISCO_GET_IPADDR, /* returns a struct ng_cisco_ipaddr */ + NGM_CISCO_GET_STATUS, /* returns a struct ng_cisco_stat */ +}; - /* This returns a struct ngciscostat (see below) */ - NGM_CISCO_GET_STATUS, +struct ng_cisco_ipaddr { + struct in_addr ipaddr; /* IP address */ + struct in_addr netmask; /* Netmask */ }; -struct ngciscostat { - u_int32_t seq_retries; /* # unack'd retries */ - u_int32_t keepalive_period; /* in seconds */ +/* Keep this in sync with the above structure definition */ +#define NG_CISCO_IPADDR_TYPE_INFO { \ + { \ + { "ipaddr", &ng_parse_ipaddr_type }, \ + { "netmask", &ng_parse_ipaddr_type }, \ + { NULL }, \ + } \ +} + +struct ng_cisco_stats { + u_int32_t seqRetries; /* # unack'd retries */ + u_int32_t keepAlivePeriod; /* in seconds */ }; +/* Keep this in sync with the above structure definition */ +#define NG_CISCO_STATS_TYPE_INFO { \ + { \ + { "seqRetries", &ng_parse_int32_type }, \ + { "keepAlivePeriod", &ng_parse_int32_type }, \ + { NULL }, \ + } \ +} + #endif /* _NETGRAPH_CISCO_H_ */ diff --git a/sys/netgraph/ng_echo.c b/sys/netgraph/ng_echo.c index a834659..e5c35b5 100644 --- a/sys/netgraph/ng_echo.c +++ b/sys/netgraph/ng_echo.c @@ -75,7 +75,8 @@ static struct ng_type typestruct = { NULL, nge_rcvdata, nge_rcvdata, - nge_disconnect + nge_disconnect, + NULL }; NETGRAPH_INIT(echo, &typestruct); diff --git a/sys/netgraph/ng_ether.h b/sys/netgraph/ng_ether.h index 45625e4..f492361 100644 --- a/sys/netgraph/ng_ether.h +++ b/sys/netgraph/ng_ether.h @@ -45,11 +45,19 @@ /* Node type name and magic cookie */ #define NG_ETHER_NODE_TYPE "ether" -#define NGM_ETHER_COOKIE 917786904 +#define NGM_ETHER_COOKIE 917786904 /* Hook names */ #define NG_ETHER_HOOK_ORPHAN "orphans" #define NG_ETHER_HOOK_DIVERT "divert" +/* For adding/removing Ethernet multicast addresses */ +enum { + NGM_ETHER_ADD_MULTICAST = 1, /* supply struct ether_addr */ + NGM_ETHER_DEL_MULTICAST, /* supply struct ether_addr */ + NGM_ETHER_GET_MULTICAST, /* returns array of struct ether_addr */ + NGM_ETHER_CLR_MULTICAST, /* clears all multicast addresses */ +}; + #endif /* _NETGRAPH_NG_ETHER_H_ */ diff --git a/sys/netgraph/ng_frame_relay.c b/sys/netgraph/ng_frame_relay.c index 7923ad2..92437e9 100644 --- a/sys/netgraph/ng_frame_relay.c +++ b/sys/netgraph/ng_frame_relay.c @@ -151,7 +151,8 @@ static struct ng_type typestruct = { NULL, ngfrm_rcvdata, ngfrm_rcvdata, - ngfrm_disconnect + ngfrm_disconnect, + NULL }; NETGRAPH_INIT(framerelay, &typestruct); @@ -246,7 +247,7 @@ ngfrm_constructor(node_p *nodep) static int ngfrm_newhook(node_p node, hook_p hook, const char *name) { - const sc_p sc = node->private; + const sc_p sc = node->private; const char *cp; char *eptr; int dlci = 0; diff --git a/sys/netgraph/ng_hole.c b/sys/netgraph/ng_hole.c index 6b3768c..c0b5dcc 100644 --- a/sys/netgraph/ng_hole.c +++ b/sys/netgraph/ng_hole.c @@ -71,7 +71,8 @@ static struct ng_type typestruct = { NULL, ngh_rcvdata, ngh_rcvdata, - ngh_disconnect + ngh_disconnect, + NULL }; NETGRAPH_INIT(hole, &typestruct); diff --git a/sys/netgraph/ng_iface.c b/sys/netgraph/ng_iface.c index 6a6cbdf3..d450d2e 100644 --- a/sys/netgraph/ng_iface.c +++ b/sys/netgraph/ng_iface.c @@ -73,13 +73,14 @@ #include <net/if_types.h> #include <net/netisr.h> +#include <netinet/in.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> @@ -206,7 +207,8 @@ static struct ng_type typestruct = { NULL, ng_iface_rcvdata, ng_iface_rcvdata, - ng_iface_disconnect + ng_iface_disconnect, + NULL }; NETGRAPH_INIT(iface, &typestruct); @@ -683,12 +685,8 @@ ng_iface_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) int s, error = 0; /* Sanity checks */ -#ifdef DIAGNOSTIC - if (iffam == NULL) - panic(__FUNCTION__); - if ((m->m_flags & M_PKTHDR) == 0) - panic(__FUNCTION__); -#endif + KASSERT(iffam != NULL, ("%s: iffam", __FUNCTION__)); + KASSERT(m->m_flags & M_PKTHDR, ("%s: not pkthdr", __FUNCTION__)); if (m == NULL) return (EINVAL); if ((ifp->if_flags & IFF_UP) == 0) { diff --git a/sys/netgraph/ng_ksocket.c b/sys/netgraph/ng_ksocket.c index 485eeb5..2ce2b4e 100644 --- a/sys/netgraph/ng_ksocket.c +++ b/sys/netgraph/ng_ksocket.c @@ -52,20 +52,26 @@ #include <sys/mbuf.h> #include <sys/proc.h> #include <sys/malloc.h> +#include <sys/ctype.h> #include <sys/protosw.h> #include <sys/errno.h> #include <sys/socket.h> #include <sys/socketvar.h> #include <sys/syslog.h> #include <sys/uio.h> +#include <sys/un.h> #include <netgraph/ng_message.h> #include <netgraph/netgraph.h> +#include <netgraph/ng_parse.h> #include <netgraph/ng_ksocket.h> #include <netinet/in.h> #include <netatalk/at.h> +#define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0)) +#define SADATA_OFFSET (OFFSETOF(struct sockaddr, sa_data)) + /* Node private data */ struct ng_ksocket_private { hook_p hook; @@ -88,28 +94,6 @@ struct ng_ksocket_alias { const int family; }; -/* Helper functions */ -static void ng_ksocket_incoming(struct socket *so, void *arg, int waitflag); -static int ng_ksocket_parse(const struct ng_ksocket_alias *aliases, - const char *s, int family); - -/* Node type descriptor */ -static struct ng_type ng_ksocket_typestruct = { - NG_VERSION, - NG_KSOCKET_NODE_TYPE, - NULL, - ng_ksocket_constructor, - ng_ksocket_rcvmsg, - ng_ksocket_rmnode, - ng_ksocket_newhook, - NULL, - NULL, - ng_ksocket_rcvdata, - ng_ksocket_rcvdata, - ng_ksocket_disconnect -}; -NETGRAPH_INIT(ksocket, &ng_ksocket_typestruct); - /* Protocol family aliases */ static const struct ng_ksocket_alias ng_ksocket_families[] = { { "local", PF_LOCAL }, @@ -150,6 +134,350 @@ static const struct ng_ksocket_alias ng_ksocket_protos[] = { { NULL, -1 }, }; +/* Helper functions */ +static void ng_ksocket_incoming(struct socket *so, void *arg, int waitflag); +static int ng_ksocket_parse(const struct ng_ksocket_alias *aliases, + const char *s, int family); + +/************************************************************************ + STRUCT SOCKADDR PARSE TYPE + ************************************************************************/ + +/* Get the length of the data portion of a generic struct sockaddr */ +static int +ng_parse_generic_sockdata_getLength(const struct ng_parse_type *type, + const u_char *start, const u_char *buf) +{ + const struct sockaddr *sa; + + sa = (const struct sockaddr *)(buf - SADATA_OFFSET); + return sa->sa_len - SADATA_OFFSET; +} + +/* Type for the variable length data portion of a generic struct sockaddr */ +static const struct ng_parse_type ng_ksocket_generic_sockdata_type = { + &ng_parse_bytearray_type, + &ng_parse_generic_sockdata_getLength +}; + +/* Type for a generic struct sockaddr */ +static const struct ng_parse_struct_info ng_parse_generic_sockaddr_type_info = { + { + { "len", &ng_parse_int8_type }, + { "family", &ng_parse_int8_type }, + { "data", &ng_ksocket_generic_sockdata_type }, + { NULL } + } +}; +static const struct ng_parse_type ng_ksocket_generic_sockaddr_type = { + &ng_parse_struct_type, + &ng_parse_generic_sockaddr_type_info +}; + +/* Convert a struct sockaddr from ASCII to binary. If its a protocol + family that we specially handle, do that, otherwise defer to the + generic parse type ng_ksocket_generic_sockaddr_type. */ +static int +ng_ksocket_sockaddr_parse(const struct ng_parse_type *type, + const char *s, int *off, const u_char *const start, + u_char *const buf, int *buflen) +{ + struct sockaddr *const sa = (struct sockaddr *)buf; + enum ng_parse_token tok; + char fambuf[32]; + int family, len; + char *t; + + /* If next token is a left curly brace, use generic parse type */ + if ((tok = ng_parse_get_token(s, off, &len)) == T_LBRACE) { + return (*ng_ksocket_generic_sockaddr_type.supertype->parse) + (&ng_ksocket_generic_sockaddr_type, + s, off, start, buf, buflen); + } + + /* Get socket address family followed by a slash */ + while (isspace(s[*off])) + (*off)++; + if ((t = index(s + *off, '/')) == NULL) + return (EINVAL); + if ((len = t - (s + *off)) > sizeof(fambuf) - 1) + return (EINVAL); + strncpy(fambuf, s + *off, len); + fambuf[len] = '\0'; + *off += len + 1; + if ((family = ng_ksocket_parse(ng_ksocket_families, fambuf, 0)) == -1) + return (EINVAL); + + /* Set family */ + if (*buflen < SADATA_OFFSET) + return (ERANGE); + sa->sa_family = family; + + /* Set family-specific data and length */ + switch (sa->sa_family) { + case PF_LOCAL: /* Get pathname */ + { + const int pathoff = OFFSETOF(struct sockaddr_un, sun_path); + struct sockaddr_un *const sun = (struct sockaddr_un *)sa; + int toklen, pathlen; + char *path; + + if ((path = ng_get_string_token(s, off, &toklen)) == NULL) + return (EINVAL); + pathlen = strlen(path); + if (pathlen > SOCK_MAXADDRLEN) { + FREE(path, M_NETGRAPH); + return (E2BIG); + } + if (*buflen < pathoff + pathlen) { + FREE(path, M_NETGRAPH); + return (ERANGE); + } + *off += toklen; + bcopy(path, sun->sun_path, pathlen); + sun->sun_len = pathoff + pathlen; + FREE(path, M_NETGRAPH); + break; + } + + case PF_INET: /* Get an IP address with optional port */ + { + struct sockaddr_in *const sin = (struct sockaddr_in *)sa; + int i; + + /* Parse this: <ipaddress>[:port] */ + for (i = 0; i < 4; i++) { + u_long val; + char *eptr; + + val = strtoul(s + *off, &eptr, 10); + if (val > 0xff || eptr == s + *off) + return (EINVAL); + *off += (eptr - (s + *off)); + ((u_char *)&sin->sin_addr)[i] = (u_char)val; + if (i < 3) { + if (s[*off] != '.') + return (EINVAL); + (*off)++; + } else if (s[*off] == ':') { + (*off)++; + val = strtoul(s + *off, &eptr, 10); + if (val > 0xffff || eptr == s + *off) + return (EINVAL); + *off += (eptr - (s + *off)); + sin->sin_port = htons(val); + } else + sin->sin_port = 0; + } + bzero(&sin->sin_zero, sizeof(sin->sin_zero)); + sin->sin_len = sizeof(*sin); + break; + } + +#if 0 + case PF_APPLETALK: /* XXX implement these someday */ + case PF_INET6: + case PF_IPX: +#endif + + default: + return (EINVAL); + } + + /* Done */ + *buflen = sa->sa_len; + return (0); +} + +/* Convert a struct sockaddr from binary to ASCII */ +static int +ng_ksocket_sockaddr_unparse(const struct ng_parse_type *type, + const u_char *data, int *off, char *cbuf, int cbuflen) +{ + const struct sockaddr *sa = (const struct sockaddr *)(data + *off); + int slen = 0; + + /* Output socket address, either in special or generic format */ + switch (sa->sa_family) { + case PF_LOCAL: + { + const int pathoff = OFFSETOF(struct sockaddr_un, sun_path); + const struct sockaddr_un *sun = (const struct sockaddr_un *)sa; + const int pathlen = sun->sun_len - pathoff; + char pathbuf[SOCK_MAXADDRLEN + 1]; + char *pathtoken; + + bcopy(sun->sun_path, pathbuf, pathlen); + pathbuf[pathlen] = '\0'; + if ((pathtoken = ng_encode_string(pathbuf)) == NULL) + return (ENOMEM); + slen += snprintf(cbuf, cbuflen, "local/%s", pathtoken); + FREE(pathtoken, M_NETGRAPH); + if (slen >= cbuflen) + return (ERANGE); + *off += sun->sun_len; + return (0); + } + + case PF_INET: + { + const struct sockaddr_in *sin = (const struct sockaddr_in *)sa; + + slen += snprintf(cbuf, cbuflen, "inet/%d.%d.%d.%d", + ((const u_char *)&sin->sin_addr)[0], + ((const u_char *)&sin->sin_addr)[1], + ((const u_char *)&sin->sin_addr)[2], + ((const u_char *)&sin->sin_addr)[3]); + if (sin->sin_port != 0) { + slen += snprintf(cbuf + strlen(cbuf), + cbuflen - strlen(cbuf), ":%d", + (u_int)ntohs(sin->sin_port)); + } + if (slen >= cbuflen) + return (ERANGE); + *off += sizeof(*sin); + return(0); + } + +#if 0 + case PF_APPLETALK: /* XXX implement these someday */ + case PF_INET6: + case PF_IPX: +#endif + + default: + return (*ng_ksocket_generic_sockaddr_type.supertype->unparse) + (&ng_ksocket_generic_sockaddr_type, + data, off, cbuf, cbuflen); + } +} + +/* Parse type for struct sockaddr */ +static const struct ng_parse_type ng_ksocket_sockaddr_type = { + NULL, + NULL, + NULL, + &ng_ksocket_sockaddr_parse, + &ng_ksocket_sockaddr_unparse, + NULL /* no such thing as a default struct sockaddr */ +}; + +/************************************************************************ + STRUCT NG_KSOCKET_SOCKOPT PARSE TYPE + ************************************************************************/ + +/* Get length of the struct ng_ksocket_sockopt value field, which is the + just the excess of the message argument portion over the length of + the struct ng_ksocket_sockopt. */ +static int +ng_parse_sockoptval_getLength(const struct ng_parse_type *type, + const u_char *start, const u_char *buf) +{ + static const int offset = OFFSETOF(struct ng_ksocket_sockopt, value); + const struct ng_ksocket_sockopt *sopt; + const struct ng_mesg *msg; + + sopt = (const struct ng_ksocket_sockopt *)(buf - offset); + msg = (const struct ng_mesg *)((const u_char *)sopt - sizeof(*msg)); + return msg->header.arglen - sizeof(*sopt); +} + +/* Parse type for the option value part of a struct ng_ksocket_sockopt + XXX Eventually, we should handle the different socket options specially. + XXX This would avoid byte order problems, eg an integer value of 1 is + XXX going to be "[1]" for little endian or "[3=1]" for big endian. */ +static const struct ng_parse_type ng_ksocket_sockoptval_type = { + &ng_parse_bytearray_type, + &ng_parse_sockoptval_getLength +}; + +/* Parse type for struct ng_ksocket_sockopt */ +static const struct ng_parse_struct_info ng_ksocket_sockopt_type_info + = NG_KSOCKET_SOCKOPT_INFO(&ng_ksocket_sockoptval_type); +static const struct ng_parse_type ng_ksocket_sockopt_type = { + &ng_parse_struct_type, + &ng_ksocket_sockopt_type_info, +}; + +/* List of commands and how to convert arguments to/from ASCII */ +static const struct ng_cmdlist ng_ksocket_cmds[] = { + { + NGM_KSOCKET_COOKIE, + NGM_KSOCKET_BIND, + "bind", + &ng_ksocket_sockaddr_type, + NULL + }, + { + NGM_KSOCKET_COOKIE, + NGM_KSOCKET_LISTEN, + "listen", + &ng_parse_int32_type, + NULL + }, + { + NGM_KSOCKET_COOKIE, + NGM_KSOCKET_ACCEPT, + "accept", + NULL, + &ng_ksocket_sockaddr_type + }, + { + NGM_KSOCKET_COOKIE, + NGM_KSOCKET_CONNECT, + "connect", + &ng_ksocket_sockaddr_type, + NULL + }, + { + NGM_KSOCKET_COOKIE, + NGM_KSOCKET_GETNAME, + "getname", + NULL, + &ng_ksocket_sockaddr_type + }, + { + NGM_KSOCKET_COOKIE, + NGM_KSOCKET_GETPEERNAME, + "getpeername", + NULL, + &ng_ksocket_sockaddr_type + }, + { + NGM_KSOCKET_COOKIE, + NGM_KSOCKET_SETOPT, + "setopt", + &ng_ksocket_sockopt_type, + NULL + }, + { + NGM_KSOCKET_COOKIE, + NGM_KSOCKET_GETOPT, + "getopt", + &ng_ksocket_sockopt_type, + &ng_ksocket_sockopt_type + }, + { 0 } +}; + +/* Node type descriptor */ +static struct ng_type ng_ksocket_typestruct = { + NG_VERSION, + NG_KSOCKET_NODE_TYPE, + NULL, + ng_ksocket_constructor, + ng_ksocket_rcvmsg, + ng_ksocket_rmnode, + ng_ksocket_newhook, + NULL, + NULL, + ng_ksocket_rcvdata, + ng_ksocket_rcvdata, + ng_ksocket_disconnect, + ng_ksocket_cmds +}; +NETGRAPH_INIT(ksocket, &ng_ksocket_typestruct); + #define ERROUT(x) do { error = (x); goto done; } while (0) /************************************************************************ @@ -193,10 +521,10 @@ ng_ksocket_constructor(node_p *nodep) static int ng_ksocket_newhook(node_p node, hook_p hook, const char *name0) { + struct proc *p = curproc ? curproc : &proc0; /* XXX broken */ const priv_p priv = node->private; char *s1, *s2, name[NG_HOOKLEN+1]; int family, type, protocol, error; - struct proc *p = &proc0; /* XXX help what to do here */ /* Check if we're already connected */ if (priv->hook != NULL) @@ -243,9 +571,10 @@ static int ng_ksocket_rcvmsg(node_p node, struct ng_mesg *msg, const char *raddr, struct ng_mesg **rptr) { + struct proc *p = curproc ? curproc : &proc0; /* XXX broken */ const priv_p priv = node->private; + struct socket *const so = priv->so; struct ng_mesg *resp = NULL; - struct proc *p = &proc0; int error = 0; switch (msg->header.typecookie) { @@ -253,62 +582,68 @@ ng_ksocket_rcvmsg(node_p node, struct ng_mesg *msg, switch (msg->header.cmd) { case NGM_KSOCKET_BIND: { - struct sockaddr *sa = (struct sockaddr *)msg->data; - struct socket *const so = priv->so; + struct sockaddr *const sa + = (struct sockaddr *)msg->data; - /* Must have a connected hook first */ - if (priv->hook == NULL) - ERROUT(ENETDOWN); + /* Sanity check */ + if (msg->header.arglen < SADATA_OFFSET + || msg->header.arglen < sa->sa_len) + ERROUT(EINVAL); + if (so == NULL) + ERROUT(ENXIO); - /* Set and sanity check sockaddr length */ - if (msg->header.arglen > SOCK_MAXADDRLEN) - ERROUT(ENAMETOOLONG); - sa->sa_len = msg->header.arglen; + /* Bind */ error = sobind(so, sa, p); break; } case NGM_KSOCKET_LISTEN: { - struct socket *const so = priv->so; - int backlog; - - /* Must have a connected hook first */ - if (priv->hook == NULL) - ERROUT(ENETDOWN); - - /* Get backlog argument */ + /* Sanity check */ if (msg->header.arglen != sizeof(int)) ERROUT(EINVAL); - backlog = *((int *)msg->data); + if (so == NULL) + ERROUT(ENXIO); - /* Do listen */ - if ((error = solisten(so, backlog, p)) != 0) + /* Listen */ + if ((error = solisten(so, *((int *)msg->data), p)) != 0) break; /* Notify sender when we get a connection attempt */ /* XXX implement me */ + error = ENODEV; break; } case NGM_KSOCKET_ACCEPT: { - ERROUT(ENODEV); /* XXX implement me */ + /* Sanity check */ + if (msg->header.arglen != 0) + ERROUT(EINVAL); + if (so == NULL) + ERROUT(ENXIO); + + /* Accept on the socket in a non-blocking way */ + /* Create a new ksocket node for the new connection */ + /* Return a response with the peer's sockaddr and + the absolute name of the newly created node */ + + /* XXX implement me */ + + error = ENODEV; break; } case NGM_KSOCKET_CONNECT: { - struct socket *const so = priv->so; - struct sockaddr *sa = (struct sockaddr *)msg->data; - - /* Must have a connected hook first */ - if (priv->hook == NULL) - ERROUT(ENETDOWN); + struct sockaddr *const sa + = (struct sockaddr *)msg->data; - /* Set and sanity check sockaddr length */ - if (msg->header.arglen > SOCK_MAXADDRLEN) - ERROUT(ENAMETOOLONG); - sa->sa_len = msg->header.arglen; + /* Sanity check */ + if (msg->header.arglen < SADATA_OFFSET + || msg->header.arglen < sa->sa_len) + ERROUT(EINVAL); + if (so == NULL) + ERROUT(ENXIO); /* Do connect */ if ((so->so_state & SS_ISCONNECTING) != 0) @@ -325,26 +660,105 @@ ng_ksocket_rcvmsg(node_p node, struct ng_mesg *msg, } case NGM_KSOCKET_GETNAME: - { - ERROUT(ENODEV); /* XXX implement me */ - break; - } - case NGM_KSOCKET_GETPEERNAME: { - ERROUT(ENODEV); /* XXX implement me */ + int (*func)(struct socket *so, struct sockaddr **nam); + struct sockaddr *sa = NULL; + int len; + + /* Sanity check */ + if (msg->header.arglen != 0) + ERROUT(EINVAL); + if (so == NULL) + ERROUT(ENXIO); + + /* Get function */ + if (msg->header.cmd == NGM_KSOCKET_GETPEERNAME) { + if ((so->so_state + & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0) + ERROUT(ENOTCONN); + func = so->so_proto->pr_usrreqs->pru_peeraddr; + } else + func = so->so_proto->pr_usrreqs->pru_sockaddr; + + /* Get local or peer address */ + if ((error = (*func)(so, &sa)) != 0) + goto bail; + len = (sa == NULL) ? 0 : sa->sa_len; + + /* Send it back in a response */ + NG_MKRESPONSE(resp, msg, len, M_NOWAIT); + if (resp == NULL) { + error = ENOMEM; + goto bail; + } + bcopy(sa, resp->data, len); + + bail: + /* Cleanup */ + if (sa != NULL) + FREE(sa, M_SONAME); break; } case NGM_KSOCKET_GETOPT: { - ERROUT(ENODEV); /* XXX implement me */ + struct ng_ksocket_sockopt *ksopt = + (struct ng_ksocket_sockopt *)msg->data; + struct sockopt sopt; + + /* Sanity check */ + if (msg->header.arglen != sizeof(*ksopt)) + ERROUT(EINVAL); + if (so == NULL) + ERROUT(ENXIO); + + /* Get response with room for option value */ + NG_MKRESPONSE(resp, msg, sizeof(*ksopt) + + NG_KSOCKET_MAX_OPTLEN, M_NOWAIT); + if (resp == NULL) + ERROUT(ENOMEM); + + /* Get socket option, and put value in the response */ + sopt.sopt_dir = SOPT_GET; + sopt.sopt_level = ksopt->level; + sopt.sopt_name = ksopt->name; + sopt.sopt_p = p; + sopt.sopt_valsize = NG_KSOCKET_MAX_OPTLEN; + ksopt = (struct ng_ksocket_sockopt *)resp->data; + sopt.sopt_val = ksopt->value; + if ((error = sogetopt(so, &sopt)) != 0) { + FREE(resp, M_NETGRAPH); + break; + } + + /* Set actual value length */ + resp->header.arglen = sizeof(*ksopt) + + sopt.sopt_valsize; break; } case NGM_KSOCKET_SETOPT: { - ERROUT(ENODEV); /* XXX implement me */ + struct ng_ksocket_sockopt *const ksopt = + (struct ng_ksocket_sockopt *)msg->data; + const int valsize = msg->header.arglen - sizeof(*ksopt); + struct sockopt sopt; + + /* Sanity check */ + if (valsize < 0) + ERROUT(EINVAL); + if (so == NULL) + ERROUT(ENXIO); + + /* Set socket option */ + sopt.sopt_dir = SOPT_SET; + sopt.sopt_level = ksopt->level; + sopt.sopt_name = ksopt->name; + sopt.sopt_val = ksopt->value; + sopt.sopt_valsize = valsize; + sopt.sopt_p = p; + error = sosetopt(so, &sopt); break; } @@ -373,10 +787,10 @@ done: static int ng_ksocket_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { + struct proc *p = curproc ? curproc : &proc0; /* XXX broken */ const node_p node = hook->node; const priv_p priv = node->private; struct socket *const so = priv->so; - struct proc *p = &proc0; int error; NG_FREE_META(meta); @@ -394,6 +808,9 @@ ng_ksocket_rmnode(node_p node) /* Close our socket (if any) */ if (priv->so != NULL) { + + priv->so->so_upcall = NULL; + priv->so->so_rcv.sb_flags &= ~SB_UPCALL; soclose(priv->so); priv->so = NULL; } @@ -455,8 +872,16 @@ ng_ksocket_incoming(struct socket *so, void *arg, int waitflag) flags = MSG_DONTWAIT; do { if ((error = (*so->so_proto->pr_usrreqs->pru_soreceive) - (so, &nam, &auio, &m, (struct mbuf **)0, &flags)) == 0) + (so, &nam, &auio, &m, (struct mbuf **)0, &flags)) == 0 + && m != NULL) { + struct mbuf *n; + + /* Don't trust the various socket layers to get the + packet header and length correct (eg. kern/15175) */ + for (n = m, m->m_pkthdr.len = 0; n; n = n->m_next) + m->m_pkthdr.len += n->m_len; NG_SEND_DATA(error, priv->hook, m, meta); + } } while (error == 0 && m != NULL); splx(s); } @@ -480,7 +905,7 @@ ng_ksocket_parse(const struct ng_ksocket_alias *aliases, /* Try parsing as a number */ val = (int)strtoul(s, &eptr, 10); - if (val <= 0 || *eptr != '\0') + if (val < 0 || *eptr != '\0') return (-1); return (val); } diff --git a/sys/netgraph/ng_ksocket.h b/sys/netgraph/ng_ksocket.h index 93f3752..48cb41f 100644 --- a/sys/netgraph/ng_ksocket.h +++ b/sys/netgraph/ng_ksocket.h @@ -47,6 +47,28 @@ #define NG_KSOCKET_NODE_TYPE "ksocket" #define NGM_KSOCKET_COOKIE 942710669 +/* For NGM_KSOCKET_SETOPT and NGM_KSOCKET_GETOPT control messages */ +struct ng_ksocket_sockopt { + u_int32_t level; /* second arg of [gs]etsockopt() */ + u_int32_t name; /* third arg of [gs]etsockopt() */ + u_char value[0]; /* fourth arg of [gs]etsockopt() */ +}; + +/* Max length socket option we can return via NGM_KSOCKET_GETOPT + XXX This should not be necessary, we should dynamically size + XXX the response. Until then.. */ +#define NG_KSOCKET_MAX_OPTLEN 1024 + +/* Keep this in sync with the above structure definition */ +#define NG_KSOCKET_SOCKOPT_INFO(svtype) { \ + { \ + { "level", &ng_parse_int32_type }, \ + { "name", &ng_parse_int32_type }, \ + { "value", (svtype) }, \ + { NULL }, \ + } \ +} + /* Netgraph commands */ enum { NGM_KSOCKET_BIND = 1, diff --git a/sys/netgraph/ng_lmi.c b/sys/netgraph/ng_lmi.c index 9dcfa38..3a3afc9 100644 --- a/sys/netgraph/ng_lmi.c +++ b/sys/netgraph/ng_lmi.c @@ -109,7 +109,8 @@ static struct ng_type typestruct = { NULL, nglmi_rcvdata, nglmi_rcvdata, - nglmi_disconnect + nglmi_disconnect, + NULL }; NETGRAPH_INIT(lmi, &typestruct); diff --git a/sys/netgraph/ng_message.h b/sys/netgraph/ng_message.h index 66c7fba..aab7314 100644 --- a/sys/netgraph/ng_message.h +++ b/sys/netgraph/ng_message.h @@ -65,10 +65,33 @@ struct ng_mesg { } header; char data[0]; /* placeholder for actual data */ }; -#define NG_VERSION 1 + +/* Keep this in sync with the above structure definition */ +#define NG_GENERIC_NG_MESG_INFO(dtype) { \ + { \ + { "version", &ng_parse_int8_type }, \ + { "spare", &ng_parse_int8_type }, \ + { "arglen", &ng_parse_int16_type }, \ + { "flags", &ng_parse_int32_type }, \ + { "token", &ng_parse_int32_type }, \ + { "typecookie", &ng_parse_int32_type }, \ + { "cmd", &ng_parse_int32_type }, \ + { "cmdstr", &ng_parse_cmdbuf_type }, \ + { "data", (dtype) }, \ + { NULL }, \ + } \ +} + +/* Negraph type binary compatibility field */ +#define NG_VERSION 2 + +/* Flags field flags */ #define NGF_ORIG 0x0000 /* the msg is the original request */ #define NGF_RESP 0x0001 /* the message is a response */ +/* Type of a unique node ID */ +#define ng_ID_t unsigned int + /* * Here we describe the "generic" messages that all nodes inherently * understand. With the exception of NGM_TEXT_STATUS, these are handled @@ -76,46 +99,84 @@ struct ng_mesg { */ /* Generic message type cookie */ -#define NGM_GENERIC_COOKIE 851672668 +#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 */ +#define NGM_SHUTDOWN 1 /* shut down node */ +#define NGM_MKPEER 2 /* create and attach a peer node */ +#define NGM_CONNECT 3 /* connect two nodes */ +#define NGM_NAME 4 /* give a node a name */ +#define NGM_RMHOOK 5 /* break a connection btw. two nodes */ +#define NGM_NODEINFO 6 /* get nodeinfo for the target */ +#define NGM_LISTHOOKS 7 /* get list of hooks on node */ +#define NGM_LISTNAMES 8 /* list all globally named nodes */ +#define NGM_LISTNODES 9 /* list all nodes, named and unnamed */ +#define NGM_LISTTYPES 10 /* list all installed node types */ +#define NGM_TEXT_STATUS 11 /* (optional) get text status report */ +#define NGM_BINARY2ASCII 12 /* convert struct ng_mesg to ascii */ +#define NGM_ASCII2BINARY 13 /* convert ascii to struct ng_mesg */ -/* - * Args sections for generic NG commands. All strings are NUL-terminated. - */ +/* Structure used for NGM_MKPEER */ struct ngm_mkpeer { char type[NG_TYPELEN + 1]; /* peer type */ char ourhook[NG_HOOKLEN + 1]; /* hook name */ char peerhook[NG_HOOKLEN + 1]; /* peer hook name */ }; +/* Keep this in sync with the above structure definition */ +#define NG_GENERIC_MKPEER_INFO() { \ + { \ + { "type", &ng_parse_typebuf_type }, \ + { "ourhook", &ng_parse_hookbuf_type }, \ + { "peerhook", &ng_parse_hookbuf_type }, \ + { NULL }, \ + } \ +} + +/* Structure used for NGM_CONNECT */ struct ngm_connect { char path[NG_PATHLEN + 1]; /* peer path */ char ourhook[NG_HOOKLEN + 1]; /* hook name */ char peerhook[NG_HOOKLEN + 1]; /* peer hook name */ }; +/* Keep this in sync with the above structure definition */ +#define NG_GENERIC_CONNECT_INFO() { \ + { \ + { "path", &ng_parse_pathbuf_type }, \ + { "ourhook", &ng_parse_hookbuf_type }, \ + { "peerhook", &ng_parse_hookbuf_type }, \ + { NULL }, \ + } \ +} + +/* Structure used for NGM_NAME */ struct ngm_name { char name[NG_NODELEN + 1]; /* node name */ }; +/* Keep this in sync with the above structure definition */ +#define NG_GENERIC_NAME_INFO() { \ + { \ + { "name", &ng_parse_nodebuf_type }, \ + { NULL }, \ + } \ +} + +/* Structure used for NGM_RMHOOK */ struct ngm_rmhook { char ourhook[NG_HOOKLEN + 1]; /* hook name */ }; -#define ng_ID_t unsigned int -/* Structures used in response to NGM_NODEINFO and NGM_LISTHOOKS */ +/* Keep this in sync with the above structure definition */ +#define NG_GENERIC_RMHOOK_INFO() { \ + { \ + { "hook", &ng_parse_hookbuf_type }, \ + { NULL }, \ + } \ +} + +/* Structure used for NGM_NODEINFO */ struct nodeinfo { char name[NG_NODELEN + 1]; /* node name (if any) */ char type[NG_TYPELEN + 1]; /* peer type */ @@ -123,34 +184,92 @@ struct nodeinfo { u_int32_t hooks; /* number of active hooks */ }; +/* Keep this in sync with the above structure definition */ +#define NG_GENERIC_NODEINFO_INFO() { \ + { \ + { "name", &ng_parse_nodebuf_type }, \ + { "type", &ng_parse_typebuf_type }, \ + { "id", &ng_parse_int32_type }, \ + { "hooks", &ng_parse_int32_type }, \ + { NULL }, \ + } \ +} + +/* Structure used for NGM_LISTHOOKS */ struct linkinfo { char ourhook[NG_HOOKLEN + 1]; /* hook name */ char peerhook[NG_HOOKLEN + 1]; /* peer hook */ struct nodeinfo nodeinfo; }; +/* Keep this in sync with the above structure definition */ +#define NG_GENERIC_LINKINFO_INFO(nitype) { \ + { \ + { "ourhook", &ng_parse_hookbuf_type }, \ + { "peerhook", &ng_parse_hookbuf_type }, \ + { "nodeinfo", (nitype) }, \ + { NULL }, \ + } \ +} + struct hooklist { struct nodeinfo nodeinfo; /* node information */ struct linkinfo link[0]; /* info about each hook */ }; -/* Structure used for NGM_LISTNAMES/NGM_LISTNODES (not node specific) */ +/* Keep this in sync with the above structure definition */ +#define NG_GENERIC_HOOKLIST_INFO(nitype,litype) { \ + { \ + { "nodeinfo", (nitype) }, \ + { "linkinfo", (litype) }, \ + { NULL }, \ + } \ +} + +/* Structure used for NGM_LISTNAMES/NGM_LISTNODES */ struct namelist { u_int32_t numnames; struct nodeinfo nodeinfo[0]; }; -/* Structures used for NGM_LISTTYPES (not node specific) */ +/* Keep this in sync with the above structure definition */ +#define NG_GENERIC_LISTNODES_INFO(niarraytype) { \ + { \ + { "numnames", &ng_parse_int32_type }, \ + { "nodeinfo", (niarraytype) }, \ + { NULL }, \ + } \ +} + +/* Structure used for NGM_LISTTYPES */ struct typeinfo { char typename[NG_TYPELEN + 1]; /* name of type */ u_int32_t numnodes; /* number alive */ }; +/* Keep this in sync with the above structure definition */ +#define NG_GENERIC_TYPEINFO_INFO() { \ + { \ + { "typename", &ng_parse_typebuf_type }, \ + { "typeinfo", &ng_parse_int32_type }, \ + { NULL }, \ + } \ +} + struct typelist { u_int32_t numtypes; struct typeinfo typeinfo[0]; }; +/* Keep this in sync with the above structure definition */ +#define NG_GENERIC_TYPELIST_INFO(tiarraytype) { \ + { \ + { "numtypes", &ng_parse_int32_type }, \ + { "typeinfo", (tiarraytype) }, \ + { NULL }, \ + } \ +} + /* * For netgraph nodes that are somehow associated with file descriptors * (e.g., a device that has a /dev entry and is also a netgraph node), diff --git a/sys/netgraph/ng_parse.c b/sys/netgraph/ng_parse.c new file mode 100644 index 0000000..795d1c1 --- /dev/null +++ b/sys/netgraph/ng_parse.c @@ -0,0 +1,1604 @@ + +/* + * ng_parse.c + * + * Copyright (c) 1999 Whistle Communications, Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Whistle Communications; + * provided, however, that: + * 1. Any and all reproductions of the source or object code must include the + * copyright notice above and the following disclaimer of warranties; and + * 2. No rights are granted, in any manner or form, to use Whistle + * Communications, Inc. trademarks, including the mark "WHISTLE + * COMMUNICATIONS" on advertising, endorsements, or otherwise except as + * such appears in the above copyright notice or in the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY + * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS + * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. + * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Archie Cobbs <archie@whistle.com> + * + * $Whistle: ng_parse.c,v 1.3 1999/11/29 01:43:48 archie Exp $ + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/ctype.h> + +#include <netinet/in.h> + +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> +#include <netgraph/ng_parse.h> + +/* Compute alignment for primitive integral types */ +struct int16_temp { + char x; + int16_t y; +}; + +struct int32_temp { + char x; + int32_t y; +}; + +struct int64_temp { + char x; + int64_t y; +}; + +#define INT8_ALIGNMENT 1 +#define INT16_ALIGNMENT ((int)&((struct int16_temp *)0)->y) +#define INT32_ALIGNMENT ((int)&((struct int32_temp *)0)->y) +#define INT64_ALIGNMENT ((int)&((struct int64_temp *)0)->y) + +/* Type of composite object: struct, array, or fixedarray */ +enum comptype { + CT_STRUCT, + CT_ARRAY, + CT_FIXEDARRAY, +}; + +/* Composite types helper functions */ +static int ng_parse_composite(const struct ng_parse_type *type, + const char *s, int *off, const u_char *start, + u_char *const buf, int *buflen, enum comptype ctype); +static int ng_unparse_composite(const struct ng_parse_type *type, + const u_char *data, int *off, char *cbuf, int cbuflen, + enum comptype ctype); +static int ng_get_composite_elem_default(const struct ng_parse_type *type, + int index, const u_char *start, u_char *buf, + int *buflen, enum comptype ctype); +static int ng_get_composite_len(const struct ng_parse_type *type, + const u_char *start, const u_char *buf, + enum comptype ctype); +static const struct ng_parse_type *ng_get_composite_etype(const struct + ng_parse_type *type, int index, enum comptype ctype); +static int ng_parse_get_elem_pad(const struct ng_parse_type *type, + int index, enum comptype ctype, int posn); + +/* Parsing helper functions */ +static int ng_parse_skip_value(const char *s, int off, int *lenp); + +/* Poor man's virtual method calls */ +#define METHOD(t,m) (ng_get_ ## m ## _method(t)) +#define INVOKE(t,m) (*METHOD(t,m)) + +static ng_parse_t *ng_get_parse_method(const struct ng_parse_type *t); +static ng_unparse_t *ng_get_unparse_method(const struct ng_parse_type *t); +static ng_getDefault_t *ng_get_getDefault_method(const + struct ng_parse_type *t); +static ng_getAlign_t *ng_get_getAlign_method(const struct ng_parse_type *t); + +#define ALIGNMENT(t) (METHOD(t, getAlign) == NULL ? \ + 0 : INVOKE(t, getAlign)(t)) + +/* For converting binary to string */ +#define NG_PARSE_APPEND(fmt, args...) \ + do { \ + int len; \ + \ + len = snprintf((cbuf), (cbuflen), \ + fmt , ## args); \ + if (len >= (cbuflen)) \ + return (ERANGE); \ + (cbuf) += len; \ + (cbuflen) -= len; \ + } while (0) + +/************************************************************************ + PUBLIC FUNCTIONS + ************************************************************************/ + +/* + * Convert an ASCII string to binary according to the supplied type descriptor + */ +int +ng_parse(const struct ng_parse_type *type, + const char *string, int *off, u_char *buf, int *buflen) +{ + return INVOKE(type, parse)(type, string, off, buf, buf, buflen); +} + +/* + * Convert binary to an ASCII string according to the supplied type descriptor + */ +int +ng_unparse(const struct ng_parse_type *type, + const u_char *data, char *cbuf, int cbuflen) +{ + int off = 0; + + return INVOKE(type, unparse)(type, data, &off, cbuf, cbuflen); +} + +/* + * Fill in the default value according to the supplied type descriptor + */ +int +ng_parse_getDefault(const struct ng_parse_type *type, u_char *buf, int *buflen) +{ + ng_getDefault_t *const func = METHOD(type, getDefault); + + if (func == NULL) + return (EOPNOTSUPP); + return (*func)(type, buf, buf, buflen); +} + + +/************************************************************************ + STRUCTURE TYPE + ************************************************************************/ + +static int +ng_struct_parse(const struct ng_parse_type *type, + const char *s, int *off, const u_char *const start, + u_char *const buf, int *buflen) +{ + return ng_parse_composite(type, s, off, start, buf, buflen, CT_STRUCT); +} + +static int +ng_struct_unparse(const struct ng_parse_type *type, + const u_char *data, int *off, char *cbuf, int cbuflen) +{ + return ng_unparse_composite(type, data, off, cbuf, cbuflen, CT_STRUCT); +} + +static int +ng_struct_getDefault(const struct ng_parse_type *type, + const u_char *const start, u_char *buf, int *buflen) +{ + int off = 0; + + return ng_parse_composite(type, + "{}", &off, start, buf, buflen, CT_STRUCT); +} + +static int +ng_struct_getAlign(const struct ng_parse_type *type) +{ + const struct ng_parse_struct_info *si = type->info; + const struct ng_parse_struct_field *field; + int align = 0; + + for (field = si->fields; field->name != NULL; field++) { + int falign = ALIGNMENT(field->type); + + if (falign > align) + align = falign; + } + return align; +} + +const struct ng_parse_type ng_parse_struct_type = { + NULL, + NULL, + NULL, + ng_struct_parse, + ng_struct_unparse, + ng_struct_getDefault, + ng_struct_getAlign +}; + +/************************************************************************ + FIXED LENGTH ARRAY TYPE + ************************************************************************/ + +static int +ng_fixedarray_parse(const struct ng_parse_type *type, + const char *s, int *off, const u_char *const start, + u_char *const buf, int *buflen) +{ + return ng_parse_composite(type, + s, off, start, buf, buflen, CT_FIXEDARRAY); +} + +static int +ng_fixedarray_unparse(const struct ng_parse_type *type, + const u_char *data, int *off, char *cbuf, int cbuflen) +{ + return ng_unparse_composite(type, + data, off, cbuf, cbuflen, CT_FIXEDARRAY); +} + +static int +ng_fixedarray_getDefault(const struct ng_parse_type *type, + const u_char *const start, u_char *buf, int *buflen) +{ + int off = 0; + + return ng_parse_composite(type, + "[]", &off, start, buf, buflen, CT_FIXEDARRAY); +} + +static int +ng_fixedarray_getAlign(const struct ng_parse_type *type) +{ + const struct ng_parse_fixedarray_info *fi = type->info; + + return ALIGNMENT(fi->elementType); +} + +const struct ng_parse_type ng_parse_fixedarray_type = { + NULL, + NULL, + NULL, + ng_fixedarray_parse, + ng_fixedarray_unparse, + ng_fixedarray_getDefault, + ng_fixedarray_getAlign +}; + +/************************************************************************ + VARIABLE LENGTH ARRAY TYPE + ************************************************************************/ + +static int +ng_array_parse(const struct ng_parse_type *type, + const char *s, int *off, const u_char *const start, + u_char *const buf, int *buflen) +{ + return ng_parse_composite(type, s, off, start, buf, buflen, CT_ARRAY); +} + +static int +ng_array_unparse(const struct ng_parse_type *type, + const u_char *data, int *off, char *cbuf, int cbuflen) +{ + return ng_unparse_composite(type, data, off, cbuf, cbuflen, CT_ARRAY); +} + +static int +ng_array_getDefault(const struct ng_parse_type *type, + const u_char *const start, u_char *buf, int *buflen) +{ + int off = 0; + + return ng_parse_composite(type, + "[]", &off, start, buf, buflen, CT_ARRAY); +} + +static int +ng_array_getAlign(const struct ng_parse_type *type) +{ + const struct ng_parse_array_info *ai = type->info; + + return ALIGNMENT(ai->elementType); +} + +const struct ng_parse_type ng_parse_array_type = { + NULL, + NULL, + NULL, + ng_array_parse, + ng_array_unparse, + ng_array_getDefault, + ng_array_getAlign +}; + +/************************************************************************ + INT8 TYPE + ************************************************************************/ + +static int +ng_int8_parse(const struct ng_parse_type *type, + const char *s, int *off, const u_char *const start, + u_char *const buf, int *buflen) +{ + long val; + int8_t val8; + char *eptr; + + val = strtol(s + *off, &eptr, 0); + if (val < -0x80 || val > 0xff || eptr == s + *off) + return (EINVAL); + *off = eptr - s; + val8 = (int8_t)val; + bcopy(&val8, buf, sizeof(int8_t)); + *buflen = sizeof(int8_t); + return (0); +} + +static int +ng_int8_unparse(const struct ng_parse_type *type, + const u_char *data, int *off, char *cbuf, int cbuflen) +{ + int8_t val; + + bcopy(data + *off, &val, sizeof(int8_t)); + NG_PARSE_APPEND("%d", (int)val); + *off += sizeof(int8_t); + return (0); +} + +static int +ng_int8_getDefault(const struct ng_parse_type *type, + const u_char *const start, u_char *buf, int *buflen) +{ + int8_t val; + + if (*buflen < sizeof(int8_t)) + return (ERANGE); + val = 0; + bcopy(&val, buf, sizeof(int8_t)); + *buflen = sizeof(int8_t); + return (0); +} + +static int +ng_int8_getAlign(const struct ng_parse_type *type) +{ + return INT8_ALIGNMENT; +} + +const struct ng_parse_type ng_parse_int8_type = { + NULL, + NULL, + NULL, + ng_int8_parse, + ng_int8_unparse, + ng_int8_getDefault, + ng_int8_getAlign +}; + +/************************************************************************ + INT16 TYPE + ************************************************************************/ + +static int +ng_int16_parse(const struct ng_parse_type *type, + const char *s, int *off, const u_char *const start, + u_char *const buf, int *buflen) +{ + long val; + int16_t val16; + char *eptr; + + val = strtol(s + *off, &eptr, 0); + if (val < -0x8000 || val > 0xffff || eptr == s + *off) + return (EINVAL); + *off = eptr - s; + val16 = (int16_t)val; + bcopy(&val16, buf, sizeof(int16_t)); + *buflen = sizeof(int16_t); + return (0); +} + +static int +ng_int16_unparse(const struct ng_parse_type *type, + const u_char *data, int *off, char *cbuf, int cbuflen) +{ + int16_t val; + + bcopy(data + *off, &val, sizeof(int16_t)); + NG_PARSE_APPEND("%d", (int)val); + *off += sizeof(int16_t); + return (0); +} + +static int +ng_int16_getDefault(const struct ng_parse_type *type, + const u_char *const start, u_char *buf, int *buflen) +{ + int16_t val; + + if (*buflen < sizeof(int16_t)) + return (ERANGE); + val = 0; + bcopy(&val, buf, sizeof(int16_t)); + *buflen = sizeof(int16_t); + return (0); +} + +static int +ng_int16_getAlign(const struct ng_parse_type *type) +{ + return INT16_ALIGNMENT; +} + +const struct ng_parse_type ng_parse_int16_type = { + NULL, + NULL, + NULL, + ng_int16_parse, + ng_int16_unparse, + ng_int16_getDefault, + ng_int16_getAlign +}; + +/************************************************************************ + INT32 TYPE + ************************************************************************/ + +static int +ng_int32_parse(const struct ng_parse_type *type, + const char *s, int *off, const u_char *const start, + u_char *const buf, int *buflen) +{ + long val; /* assumes long is at least 32 bits */ + int32_t val32; + char *eptr; + + val = strtol(s + *off, &eptr, 0); + if (val < -0x80000000 || val > 0xffffffff || eptr == s + *off) + return (EINVAL); + *off = eptr - s; + val32 = (int32_t)val; + bcopy(&val32, buf, sizeof(int32_t)); + *buflen = sizeof(int32_t); + return (0); +} + +static int +ng_int32_unparse(const struct ng_parse_type *type, + const u_char *data, int *off, char *cbuf, int cbuflen) +{ + int32_t val; + + bcopy(data + *off, &val, sizeof(int32_t)); + NG_PARSE_APPEND("%ld", (long)val); + *off += sizeof(int32_t); + return (0); +} + +static int +ng_int32_getDefault(const struct ng_parse_type *type, + const u_char *const start, u_char *buf, int *buflen) +{ + int32_t val; + + if (*buflen < sizeof(int32_t)) + return (ERANGE); + val = 0; + bcopy(&val, buf, sizeof(int32_t)); + *buflen = sizeof(int32_t); + return (0); +} + +static int +ng_int32_getAlign(const struct ng_parse_type *type) +{ + return INT32_ALIGNMENT; +} + +const struct ng_parse_type ng_parse_int32_type = { + NULL, + NULL, + NULL, + ng_int32_parse, + ng_int32_unparse, + ng_int32_getDefault, + ng_int32_getAlign +}; + +/************************************************************************ + INT64 TYPE + ************************************************************************/ + +static int +ng_int64_parse(const struct ng_parse_type *type, + const char *s, int *off, const u_char *const start, + u_char *const buf, int *buflen) +{ + quad_t val; + int64_t val64; + char *eptr; + + val = strtoq(s + *off, &eptr, 0); + if (eptr == s + *off) + return (EINVAL); + *off = eptr - s; + val64 = (int64_t)val; + bcopy(&val64, buf, sizeof(int64_t)); + *buflen = sizeof(int64_t); + return (0); +} + +static int +ng_int64_unparse(const struct ng_parse_type *type, + const u_char *data, int *off, char *cbuf, int cbuflen) +{ + int64_t val; + + bcopy(data + *off, &val, sizeof(int64_t)); + NG_PARSE_APPEND("%lld", (long long)val); + *off += sizeof(int64_t); + return (0); +} + +static int +ng_int64_getDefault(const struct ng_parse_type *type, + const u_char *const start, u_char *buf, int *buflen) +{ + int64_t val; + + if (*buflen < sizeof(int64_t)) + return (ERANGE); + val = 0; + bcopy(&val, buf, sizeof(int64_t)); + *buflen = sizeof(int64_t); + return (0); +} + +static int +ng_int64_getAlign(const struct ng_parse_type *type) +{ + return INT64_ALIGNMENT; +} + +const struct ng_parse_type ng_parse_int64_type = { + NULL, + NULL, + NULL, + ng_int64_parse, + ng_int64_unparse, + ng_int64_getDefault, + ng_int64_getAlign +}; + +/************************************************************************ + STRING TYPE + ************************************************************************/ + +static int +ng_string_parse(const struct ng_parse_type *type, + const char *s, int *off, const u_char *const start, + u_char *const buf, int *buflen) +{ + char *sval; + int len; + + if ((sval = ng_get_string_token(s, off, &len)) == NULL) + return (EINVAL); + *off += len; + len = strlen(sval) + 1; + bcopy(sval, buf, len); + FREE(sval, M_NETGRAPH); + *buflen = len; + return (0); +} + +static int +ng_string_unparse(const struct ng_parse_type *type, + const u_char *data, int *off, char *cbuf, int cbuflen) +{ + const char *const raw = (const char *)data + *off; + char *const s = ng_encode_string(raw); + + if (s == NULL) + return (ENOMEM); + NG_PARSE_APPEND("%s", s); + *off += strlen(raw) + 1; + FREE(s, M_NETGRAPH); + return (0); +} + +static int +ng_string_getDefault(const struct ng_parse_type *type, + const u_char *const start, u_char *buf, int *buflen) +{ + + if (*buflen < 1) + return (ERANGE); + buf[0] = (u_char)'\0'; + *buflen = 1; + return (0); +} + +const struct ng_parse_type ng_parse_string_type = { + NULL, + NULL, + NULL, + ng_string_parse, + ng_string_unparse, + ng_string_getDefault, + NULL +}; + +/************************************************************************ + FIXED BUFFER STRING TYPE + ************************************************************************/ + +static int +ng_fixedstring_parse(const struct ng_parse_type *type, + const char *s, int *off, const u_char *const start, + u_char *const buf, int *buflen) +{ + const struct ng_parse_fixedsstring_info *const fi = type->info; + char *sval; + int len; + + if ((sval = ng_get_string_token(s, off, &len)) == NULL) + return (EINVAL); + if (strlen(sval) + 1 > fi->bufSize) + return (E2BIG); + *off += len; + len = strlen(sval) + 1; + bcopy(sval, buf, len); + FREE(sval, M_NETGRAPH); + bzero(buf + len, fi->bufSize - len); + *buflen = fi->bufSize; + return (0); +} + +static int +ng_fixedstring_unparse(const struct ng_parse_type *type, + const u_char *data, int *off, char *cbuf, int cbuflen) +{ + const struct ng_parse_fixedsstring_info *const fi = type->info; + int error, temp = *off; + + if ((error = ng_string_unparse(type, data, &temp, cbuf, cbuflen)) != 0) + return (error); + *off += fi->bufSize; + return (0); +} + +static int +ng_fixedstring_getDefault(const struct ng_parse_type *type, + const u_char *const start, u_char *buf, int *buflen) +{ + const struct ng_parse_fixedsstring_info *const fi = type->info; + + if (*buflen < fi->bufSize) + return (ERANGE); + bzero(buf, fi->bufSize); + *buflen = fi->bufSize; + return (0); +} + +const struct ng_parse_type ng_parse_fixedstring_type = { + NULL, + NULL, + NULL, + ng_fixedstring_parse, + ng_fixedstring_unparse, + ng_fixedstring_getDefault, + NULL +}; + +const struct ng_parse_fixedsstring_info ng_parse_nodebuf_info = { + NG_NODELEN + 1 +}; +const struct ng_parse_type ng_parse_nodebuf_type = { + &ng_parse_fixedstring_type, + &ng_parse_nodebuf_info +}; + +const struct ng_parse_fixedsstring_info ng_parse_hookbuf_info = { + NG_HOOKLEN + 1 +}; +const struct ng_parse_type ng_parse_hookbuf_type = { + &ng_parse_fixedstring_type, + &ng_parse_hookbuf_info +}; + +const struct ng_parse_fixedsstring_info ng_parse_pathbuf_info = { + NG_PATHLEN + 1 +}; +const struct ng_parse_type ng_parse_pathbuf_type = { + &ng_parse_fixedstring_type, + &ng_parse_pathbuf_info +}; + +const struct ng_parse_fixedsstring_info ng_parse_typebuf_info = { + NG_TYPELEN + 1 +}; +const struct ng_parse_type ng_parse_typebuf_type = { + &ng_parse_fixedstring_type, + &ng_parse_typebuf_info +}; + +const struct ng_parse_fixedsstring_info ng_parse_cmdbuf_info = { + NG_CMDSTRLEN + 1 +}; +const struct ng_parse_type ng_parse_cmdbuf_type = { + &ng_parse_fixedstring_type, + &ng_parse_cmdbuf_info +}; + +/************************************************************************ + IP ADDRESS TYPE + ************************************************************************/ + +static int +ng_ipaddr_parse(const struct ng_parse_type *type, + const char *s, int *off, const u_char *const start, + u_char *const buf, int *buflen) +{ + int i, error; + + for (i = 0; i < 4; i++) { + if ((error = ng_int8_parse(&ng_parse_int8_type, + s, off, start, buf + i, buflen)) != 0) + return (error); + if (i < 3 && s[*off] != '.') + return (EINVAL); + (*off)++; + } + *buflen = 4; + return (0); +} + +static int +ng_ipaddr_unparse(const struct ng_parse_type *type, + const u_char *data, int *off, char *cbuf, int cbuflen) +{ + struct in_addr ip; + + bcopy(data + *off, &ip, sizeof(ip)); + NG_PARSE_APPEND("%d.%d.%d.%d", ((u_char *)&ip)[0], + ((u_char *)&ip)[1], ((u_char *)&ip)[2], ((u_char *)&ip)[3]); + *off += sizeof(ip); + return (0); +} + +static int +ng_ipaddr_getDefault(const struct ng_parse_type *type, + const u_char *const start, u_char *buf, int *buflen) +{ + struct in_addr ip = { 0 }; + + if (*buflen < sizeof(ip)) + return (ERANGE); + bcopy(&ip, buf, sizeof(ip)); + *buflen = sizeof(ip); + return (0); +} + +const struct ng_parse_type ng_parse_ipaddr_type = { + NULL, + NULL, + NULL, + ng_ipaddr_parse, + ng_ipaddr_unparse, + ng_ipaddr_getDefault, + ng_int32_getAlign +}; + +/************************************************************************ + BYTE ARRAY TYPE + ************************************************************************/ + +/* Get the length of a byte array */ +static int +ng_parse_bytearray_subtype_getLength(const struct ng_parse_type *type, + const u_char *start, const u_char *buf) +{ + ng_parse_array_getLength_t *const getLength = type->private; + + return (*getLength)(type, start, buf); +} + +static int +ng_bytearray_elem_unparse(const struct ng_parse_type *type, + const u_char *data, int *off, char *cbuf, int cbuflen) +{ + int8_t val; + + bcopy(data + *off, &val, sizeof(int8_t)); + NG_PARSE_APPEND("0x%02x", (int)val & 0xff); /* always hex format */ + *off += sizeof(int8_t); + return (0); +} + +/* Byte array element type is int8, but always output in hex format */ +const struct ng_parse_type ng_parse_bytearray_elem_type = { + &ng_parse_int8_type, + NULL, + NULL, + NULL, + ng_bytearray_elem_unparse, + NULL, + NULL +}; + +static const struct ng_parse_array_info ng_parse_bytearray_subtype_info = { + &ng_parse_bytearray_elem_type, + &ng_parse_bytearray_subtype_getLength, + NULL +}; +static const struct ng_parse_type ng_parse_bytearray_subtype = { + &ng_parse_array_type, + &ng_parse_bytearray_subtype_info +}; + +static int +ng_bytearray_parse(const struct ng_parse_type *type, + const char *s, int *off, const u_char *const start, + u_char *const buf, int *buflen) +{ + char *str; + int toklen; + + /* We accept either an array of bytes or a string constant */ + if ((str = ng_get_string_token(s, off, &toklen)) != NULL) { + ng_parse_array_getLength_t *const getLength = type->info; + int arraylen, slen; + + arraylen = (*getLength)(type, start, buf); + if (arraylen > *buflen) { + FREE(str, M_NETGRAPH); + return (ERANGE); + } + slen = strlen(str) + 1; + if (slen > arraylen) { + FREE(str, M_NETGRAPH); + return (E2BIG); + } + bcopy(str, buf, slen); + bzero(buf + slen, arraylen - slen); + FREE(str, M_NETGRAPH); + *off += toklen; + *buflen = arraylen; + return (0); + } else { + struct ng_parse_type subtype; + + subtype = ng_parse_bytearray_subtype; + (const void *)subtype.private = type->info; + return ng_array_parse(&subtype, s, off, start, buf, buflen); + } +} + +static int +ng_bytearray_unparse(const struct ng_parse_type *type, + const u_char *data, int *off, char *cbuf, int cbuflen) +{ + struct ng_parse_type subtype; + + subtype = ng_parse_bytearray_subtype; + (const void *)subtype.private = type->info; + return ng_array_unparse(&subtype, data, off, cbuf, cbuflen); +} + +static int +ng_bytearray_getDefault(const struct ng_parse_type *type, + const u_char *const start, u_char *buf, int *buflen) +{ + struct ng_parse_type subtype; + + subtype = ng_parse_bytearray_subtype; + (const void *)subtype.private = type->info; + return ng_array_getDefault(&subtype, start, buf, buflen); +} + +const struct ng_parse_type ng_parse_bytearray_type = { + NULL, + NULL, + NULL, + ng_bytearray_parse, + ng_bytearray_unparse, + ng_bytearray_getDefault, + NULL +}; + +/************************************************************************ + STRUCT NG_MESG TYPE + ************************************************************************/ + +/* Get msg->header.arglen when "buf" is pointing to msg->data */ +static int +ng_parse_ng_mesg_getLength(const struct ng_parse_type *type, + const u_char *start, const u_char *buf) +{ + const struct ng_mesg *msg; + + msg = (const struct ng_mesg *)(buf - sizeof(*msg)); + return msg->header.arglen; +} + +/* Type for the variable length data portion of a struct ng_mesg */ +static const struct ng_parse_type ng_msg_data_type = { + &ng_parse_bytearray_type, + &ng_parse_ng_mesg_getLength +}; + +/* Type for the entire struct ng_mesg header with data section */ +static const struct ng_parse_struct_info + ng_parse_ng_mesg_type_info = NG_GENERIC_NG_MESG_INFO(&ng_msg_data_type); +const struct ng_parse_type ng_parse_ng_mesg_type = { + &ng_parse_struct_type, + &ng_parse_ng_mesg_type_info, +}; + +/************************************************************************ + COMPOSITE HELPER ROUTINES + ************************************************************************/ + +/* + * Convert a structure or array from ASCII to binary + */ +static int +ng_parse_composite(const struct ng_parse_type *type, const char *s, + int *off, const u_char *const start, u_char *const buf, int *buflen, + const enum comptype ctype) +{ + const int num = ng_get_composite_len(type, start, buf, ctype); + int nextIndex = 0; /* next implicit array index */ + u_int index; /* field or element index */ + int *foff; /* field value offsets in string */ + int align, len, blen, error = 0; + + /* Initialize */ + MALLOC(foff, int *, num * sizeof(*foff), M_NETGRAPH, M_NOWAIT); + if (foff == NULL) { + error = ENOMEM; + goto done; + } + bzero(foff, num * sizeof(*foff)); + + /* Get opening brace/bracket */ + if (ng_parse_get_token(s, off, &len) + != (ctype == CT_STRUCT ? T_LBRACE : T_LBRACKET)) { + error = EINVAL; + goto done; + } + *off += len; + + /* Get individual element value positions in the string */ + for (;;) { + enum ng_parse_token tok; + + /* Check for closing brace/bracket */ + tok = ng_parse_get_token(s, off, &len); + if (tok == (ctype == CT_STRUCT ? T_RBRACE : T_RBRACKET)) { + *off += len; + break; + } + + /* For arrays, the 'name' (ie, index) is optional, so + distinguish name from values by seeing if the next + token is an equals sign */ + if (ctype != CT_STRUCT) { + int len2, off2; + char *eptr; + + /* If an opening brace/bracket, index is implied */ + if (tok == T_LBRACE || tok == T_LBRACKET) { + index = nextIndex++; + goto gotIndex; + } + + /* Might be an index, might be a value, either way... */ + if (tok != T_WORD) { + error = EINVAL; + goto done; + } + + /* If no equals sign follows, index is implied */ + off2 = *off + len; + if (ng_parse_get_token(s, &off2, &len2) != T_EQUALS) { + index = nextIndex++; + goto gotIndex; + } + + /* Index was specified explicitly; parse it */ + index = (u_int)strtoul(s + *off, &eptr, 0); + if (index < 0 || eptr - (s + *off) != len) { + error = EINVAL; + goto done; + } + nextIndex = index + 1; + *off += len + len2; +gotIndex: + } else { /* a structure field */ + const struct ng_parse_struct_field *field = NULL; + const struct ng_parse_struct_info *si = type->info; + + /* Find the field by name (required) in field list */ + if (tok != T_WORD) { + error = EINVAL; + goto done; + } + for (index = 0; index < num; index++) { + field = &si->fields[index]; + if (strncmp(&s[*off], field->name, len) == 0 + && field->name[len] == '\0') + break; + } + if (index == num) { + error = ENOENT; + goto done; + } + *off += len; + + /* Get equals sign */ + if (ng_parse_get_token(s, off, &len) != T_EQUALS) { + error = EINVAL; + goto done; + } + *off += len; + } + + /* Check array index */ + if (index >= num) { + error = E2BIG; + goto done; + } + + /* Save value's position and skip over it for now */ + if (foff[index] != 0) { + error = EALREADY; /* duplicate */ + goto done; + } + while (isspace(s[*off])) + (*off)++; + foff[index] = *off; + if ((error = ng_parse_skip_value(s, *off, &len)) != 0) + goto done; + *off += len; + } + + /* Now build binary structure from supplied values and defaults */ + for (blen = index = 0; index < num; index++) { + const struct ng_parse_type *const + etype = ng_get_composite_etype(type, index, ctype); + int k, pad, vlen; + + /* Zero-pad any alignment bytes */ + pad = ng_parse_get_elem_pad(type, index, ctype, blen); + for (k = 0; k < pad; k++) { + if (blen >= *buflen) { + error = ERANGE; + goto done; + } + buf[blen++] = 0; + } + + /* Get value */ + vlen = *buflen - blen; + if (foff[index] == 0) { /* use default value */ + error = ng_get_composite_elem_default(type, index, + start, buf + blen, &vlen, ctype); + } else { /* parse given value */ + *off = foff[index]; + error = INVOKE(etype, parse)(etype, + s, off, start, buf + blen, &vlen); + } + if (error != 0) + goto done; + blen += vlen; + } + + /* Make total composite structure size a multiple of its alignment */ + if ((align = ALIGNMENT(type)) != 0) { + while (blen % align != 0) { + if (blen >= *buflen) { + error = ERANGE; + goto done; + } + buf[blen++] = 0; + } + } + + /* Done */ + *buflen = blen; +done: + FREE(foff, M_NETGRAPH); + return (error); +} + +/* + * Convert an array or structure from binary to ASCII + */ +static int +ng_unparse_composite(const struct ng_parse_type *type, const u_char *data, + int *off, char *cbuf, int cbuflen, const enum comptype ctype) +{ + const int num = ng_get_composite_len(type, data, data + *off, ctype); + int nextIndex = 0, didOne = 0; + int error, index; + + /* Opening brace/bracket */ + NG_PARSE_APPEND("%c", (ctype == CT_STRUCT) ? '{' : '['); + + /* Do each item */ + for (index = 0; index < num; index++) { + const struct ng_parse_type *const + etype = ng_get_composite_etype(type, index, ctype); + u_char temp[1024]; + + /* Skip any alignment pad bytes */ + *off += ng_parse_get_elem_pad(type, index, ctype, *off); + + /* See if element is equal to its default value; skip if so */ + if (*off < sizeof(temp)) { + int tempsize = sizeof(temp) - *off; + + bcopy(data, temp, *off); + if (ng_get_composite_elem_default(type, index, temp, + temp + *off, &tempsize, ctype) == 0 + && bcmp(temp + *off, data + *off, tempsize) == 0) { + *off += tempsize; + continue; + } + } + + /* Print name= */ + NG_PARSE_APPEND(" "); + if (ctype != CT_STRUCT) { + if (index != nextIndex) { + nextIndex = index; + NG_PARSE_APPEND("%d=", index); + } + nextIndex++; + } else { + const struct ng_parse_struct_info *si = type->info; + + NG_PARSE_APPEND("%s=", si->fields[index].name); + } + + /* Print value */ + if ((error = INVOKE(etype, unparse) + (etype, data, off, cbuf, cbuflen)) != 0) + return (error); + cbuflen -= strlen(cbuf); + cbuf += strlen(cbuf); + didOne = 1; + } + + /* Closing brace/bracket */ + NG_PARSE_APPEND("%s%c", + didOne ? " " : "", (ctype == CT_STRUCT) ? '}' : ']'); + return (0); +} + +/* + * Generate the default value for an element of an array or structure + * Returns EOPNOTSUPP if default value is unspecified. + */ +static int +ng_get_composite_elem_default(const struct ng_parse_type *type, + int index, const u_char *const start, u_char *buf, int *buflen, + const enum comptype ctype) +{ + const struct ng_parse_type *etype; + ng_getDefault_t *func; + + switch (ctype) { + case CT_STRUCT: + break; + case CT_ARRAY: + { + const struct ng_parse_array_info *const ai = type->info; + + if (ai->getDefault != NULL) { + return (*ai->getDefault)(type, + index, start, buf, buflen); + } + break; + } + case CT_FIXEDARRAY: + { + const struct ng_parse_fixedarray_info *const fi = type->info; + + if (*fi->getDefault != NULL) { + return (*fi->getDefault)(type, + index, start, buf, buflen); + } + break; + } + default: + panic("%s", __FUNCTION__); + } + + /* Default to element type default */ + etype = ng_get_composite_etype(type, index, ctype); + func = METHOD(etype, getDefault); + if (func == NULL) + return (EOPNOTSUPP); + return (*func)(etype, start, buf, buflen); +} + +/* + * Get the number of elements in a struct, variable or fixed array. + */ +static int +ng_get_composite_len(const struct ng_parse_type *type, + const u_char *const start, const u_char *buf, + const enum comptype ctype) +{ + switch (ctype) { + case CT_STRUCT: + { + const struct ng_parse_struct_info *const si = type->info; + int numFields = 0; + + for (numFields = 0; ; numFields++) { + const struct ng_parse_struct_field *const + fi = &si->fields[numFields]; + + if (fi->name == NULL) + break; + } + return (numFields); + } + case CT_ARRAY: + { + const struct ng_parse_array_info *const ai = type->info; + + return (*ai->getLength)(type, start, buf); + } + case CT_FIXEDARRAY: + { + const struct ng_parse_fixedarray_info *const fi = type->info; + + return fi->length; + } + default: + panic("%s", __FUNCTION__); + } + return (0); +} + +/* + * Return the type of the index'th element of a composite structure + */ +static const struct ng_parse_type * +ng_get_composite_etype(const struct ng_parse_type *type, + int index, const enum comptype ctype) +{ + const struct ng_parse_type *etype = NULL; + + switch (ctype) { + case CT_STRUCT: + { + const struct ng_parse_struct_info *const si = type->info; + + etype = si->fields[index].type; + break; + } + case CT_ARRAY: + { + const struct ng_parse_array_info *const ai = type->info; + + etype = ai->elementType; + break; + } + case CT_FIXEDARRAY: + { + const struct ng_parse_fixedarray_info *const fi = type->info; + + etype = fi->elementType; + break; + } + default: + panic("%s", __FUNCTION__); + } + return (etype); +} + +/* + * Get the number of bytes to skip to align for the next + * element in a composite structure. + */ +static int +ng_parse_get_elem_pad(const struct ng_parse_type *type, + int index, enum comptype ctype, int posn) +{ + const struct ng_parse_type *const + etype = ng_get_composite_etype(type, index, ctype); + int align; + + /* Get element's alignment, and possibly override */ + align = ALIGNMENT(etype); + if (ctype == CT_STRUCT) { + const struct ng_parse_struct_info *si = type->info; + + if (si->fields[index].alignment != 0) + align = si->fields[index].alignment; + } + + /* Return number of bytes to skip to align */ + return (align ? (align - (posn % align)) % align : 0); +} + +/************************************************************************ + PARSING HELPER ROUTINES + ************************************************************************/ + +/* + * Skip over a value + */ +static int +ng_parse_skip_value(const char *s, int off0, int *lenp) +{ + int len, nbracket, nbrace; + int off = off0; + + len = nbracket = nbrace = 0; + do { + switch (ng_parse_get_token(s, &off, &len)) { + case T_LBRACKET: + nbracket++; + break; + case T_LBRACE: + nbrace++; + break; + case T_RBRACKET: + if (nbracket-- == 0) + return (EINVAL); + break; + case T_RBRACE: + if (nbrace-- == 0) + return (EINVAL); + break; + case T_EOF: + return (EINVAL); + default: + break; + } + off += len; + } while (nbracket > 0 || nbrace > 0); + *lenp = off - off0; + return (0); +} + +/* + * Find the next token in the string, starting at offset *startp. + * Returns the token type, with *startp pointing to the first char + * and *lenp the length. + */ +enum ng_parse_token +ng_parse_get_token(const char *s, int *startp, int *lenp) +{ + char *t; + int i; + + while (isspace(s[*startp])) + (*startp)++; + switch (s[*startp]) { + case '\0': + *lenp = 0; + return T_EOF; + case '{': + *lenp = 1; + return T_LBRACE; + case '}': + *lenp = 1; + return T_RBRACE; + case '[': + *lenp = 1; + return T_LBRACKET; + case ']': + *lenp = 1; + return T_RBRACKET; + case '=': + *lenp = 1; + return T_EQUALS; + case '"': + if ((t = ng_get_string_token(s, startp, lenp)) == NULL) + return T_ERROR; + FREE(t, M_NETGRAPH); + return T_STRING; + default: + for (i = *startp + 1; s[i] != '\0' && !isspace(s[i]) + && s[i] != '{' && s[i] != '}' && s[i] != '[' + && s[i] != ']' && s[i] != '=' && s[i] != '"'; i++) + ; + *lenp = i - *startp; + return T_WORD; + } +} + +/* + * Get a string token, which must be enclosed in double quotes. + * The normal C backslash escapes are recognized. + */ +char * +ng_get_string_token(const char *s, int *startp, int *lenp) +{ + char *cbuf, *p; + int start, off; + + while (isspace(s[*startp])) + (*startp)++; + start = *startp; + if (s[*startp] != '"') + return (NULL); + MALLOC(cbuf, char *, strlen(s + start), M_NETGRAPH, M_NOWAIT); + if (cbuf == NULL) + return (NULL); + strcpy(cbuf, s + start + 1); + for (off = 1, p = cbuf; *p != '\0'; off++, p++) { + if (*p == '"') { + *p = '\0'; + *lenp = off + 1; + return (cbuf); + } else if (p[0] == '\\' && p[1] != '\0') { + int x, k; + char *v; + + strcpy(p, p + 1); + v = p; + switch (*p) { + case 't': + *v = '\t'; + off++; + continue; + case 'n': + *v = '\n'; + off++; + continue; + case 'r': + *v = '\r'; + off++; + continue; + case 'v': + *v = '\v'; + off++; + continue; + case 'f': + *v = '\f'; + off++; + continue; + case '"': + *v = '"'; + off++; + continue; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + for (x = k = 0; + k < 3 && *v >= '0' && *v <= '7'; v++) { + x = (x << 3) + (*v - '0'); + off++; + } + *--v = (char)x; + break; + case 'x': + for (v++, x = k = 0; + k < 2 && isxdigit(*v); v++) { + x = (x << 4) + (isdigit(*v) ? + (*v - '0') : + (tolower(*v) - 'a' + 10)); + off++; + } + *--v = (char)x; + break; + default: + continue; + } + strcpy(p, v); + } + } + return (NULL); /* no closing quote */ +} + +/* + * Encode a string so it can be safely put in double quotes. + * Caller must free the result. + */ +char * +ng_encode_string(const char *raw) +{ + char *cbuf; + int off = 0; + + MALLOC(cbuf, char *, strlen(raw) * 4 + 3, M_NETGRAPH, M_NOWAIT); + if (cbuf == NULL) + return (NULL); + cbuf[off++] = '"'; + for ( ; *raw != '\0'; raw++) { + switch (*raw) { + case '\t': + cbuf[off++] = '\\'; + cbuf[off++] = 't'; + break; + case '\f': + cbuf[off++] = '\\'; + cbuf[off++] = 'f'; + break; + case '\n': + cbuf[off++] = '\\'; + cbuf[off++] = 'n'; + break; + case '\r': + cbuf[off++] = '\\'; + cbuf[off++] = 'r'; + break; + case '\v': + cbuf[off++] = '\\'; + cbuf[off++] = 'v'; + break; + case '"': + case '\\': + cbuf[off++] = '\\'; + cbuf[off++] = *raw; + break; + default: + if (*raw < 0x20 || *raw > 0x7e) { + off += sprintf(cbuf + off, + "\\x%02x", (u_char)*raw); + break; + } + cbuf[off++] = *raw; + break; + } + } + cbuf[off++] = '"'; + cbuf[off] = '\0'; + return (cbuf); +} + +/************************************************************************ + VIRTUAL METHOD LOOKUP + ************************************************************************/ + +static ng_parse_t * +ng_get_parse_method(const struct ng_parse_type *t) +{ + while (t != NULL && t->parse == NULL) + t = t->supertype; + return (t ? t->parse : NULL); +} + +static ng_unparse_t * +ng_get_unparse_method(const struct ng_parse_type *t) +{ + while (t != NULL && t->unparse == NULL) + t = t->supertype; + return (t ? t->unparse : NULL); +} + +static ng_getDefault_t * +ng_get_getDefault_method(const struct ng_parse_type *t) +{ + while (t != NULL && t->getDefault == NULL) + t = t->supertype; + return (t ? t->getDefault : NULL); +} + +static ng_getAlign_t * +ng_get_getAlign_method(const struct ng_parse_type *t) +{ + while (t != NULL && t->getAlign == NULL) + t = t->supertype; + return (t ? t->getAlign : NULL); +} + diff --git a/sys/netgraph/ng_parse.h b/sys/netgraph/ng_parse.h new file mode 100644 index 0000000..1efbdbe --- /dev/null +++ b/sys/netgraph/ng_parse.h @@ -0,0 +1,388 @@ + +/* + * ng_parse.h + * + * Copyright (c) 1999 Whistle Communications, Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Whistle Communications; + * provided, however, that: + * 1. Any and all reproductions of the source or object code must include the + * copyright notice above and the following disclaimer of warranties; and + * 2. No rights are granted, in any manner or form, to use Whistle + * Communications, Inc. trademarks, including the mark "WHISTLE + * COMMUNICATIONS" on advertising, endorsements, or otherwise except as + * such appears in the above copyright notice or in the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY + * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS + * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. + * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Archie Cobbs <archie@whistle.com> + * + * $Whistle: ng_parse.h,v 1.2 1999/11/29 01:43:48 archie Exp $ + * $FreeBSD$ + */ + +#ifndef _NETGRAPH_PARSE_H_ +#define _NETGRAPH_PARSE_H_ + +/* + + This defines a library of routines for converting between various C + language types in binary form and ASCII strings. Types are user + definable. Several pre-defined types are supplied, for some + common C types: structures, variable and fixed length arrays, + integer types, variable and fixed length strings, IP addresses, + etc. + + Syntax + ------ + + Structures: + + '{' [ <name>=<value> ... ] '}' + + Omitted fields have their default values by implication. + The order in which the fields are specified does not matter. + + Arrays: + + '[' [ [index=]<value> ... ] ']' + + Element value may be specified with or without the "<index>=" prefix; + If omitted, the index after the previous element is used. + Omitted fields have their default values by implication. + + Strings: + + "foo bar blah\r\n" + + That is, strings are specified just like C strings. The usual + backslash escapes are accepted. + + Other simple types have their obvious ASCII forms. + + Example + ------- + + Structure Binary (big endian) + --------- ------------------- + + struct foo { + struct in_addr ip; 03 03 03 03 + int bar; 00 00 00 00 + u_char num; 02 00 + short ary[0]; 00 05 00 00 00 0a + }; + + ASCII form: "{ ip=3.3.3.3 num=3 ary=[ 5 2=10 ] }" + + Note that omitted fields or array elements get their default values + ("bar" and ary[2]), and that the alignment is handled automatically + (the extra 00 byte after "num"). + + To define a type, you can define it as a sub-type of a predefined + type, overriding some of the predefined type's methods and/or its + alignment, or define your own syntax, with the restriction that + the ASCII representation must not contain any whitespace or these + characters: { } [ ] = " + +*/ + +/************************************************************************ + METHODS REQUIRED BY A TYPE + ************************************************************************/ + +/* + * Three methods are required for a type. These may be given explicitly + * or, if NULL, inherited from the super-type. + */ + +struct ng_parse_type; + +/* + * Convert ASCII to binary according to the supplied type. + * + * The ASCII characters begin at offset *off in 'string'. The binary + * representation is put into 'buf', which has at least *buflen bytes. + * 'start' points to the first byte output by ng_parse() (ie, start <= buf). + * + * Upon return, *buflen contains the length of the new binary data, and + * *off is updated to point just past the end of the parsed range of + * characters, or, in the case of an error, to the offending character(s). + * + * Return values: + * 0 Success; *buflen holds the length of the data + * and *off points just past the last char parsed. + * EALREADY Field specified twice + * ENOENT Unknown field + * E2BIG Array or character string overflow + * ERANGE Output was longer than *buflen bytes + * EINVAL Parse failure or other invalid content + * ENOMEM Out of memory + * EOPNOTSUPP Mandatory array/structure element missing + */ +typedef int ng_parse_t(const struct ng_parse_type *type, const char *string, + int *off, const u_char *start, + u_char *buf, int *buflen); + +/* + * Convert binary to ASCII according to the supplied type. + * + * The results are put into 'buf', which is at least buflen bytes long. + * *off points to the current byte in 'data' and should be updated + * before return to point just past the last byte unparsed. + * + * Returns: + * 0 Success + * ERANGE Output was longer than buflen bytes + */ +typedef int ng_unparse_t(const struct ng_parse_type *type, + const u_char *data, int *off, char *buf, int buflen); + +/* + * Compute the default value according to the supplied type. + * + * Store the result in 'buf', which is at least *buflen bytes long. + * Upon return *buflen contains the length of the output. + * + * Returns: + * 0 Success + * ERANGE Output was longer than *buflen bytes + * EOPNOTSUPP Default value is not specified for this type + */ +typedef int ng_getDefault_t(const struct ng_parse_type *type, + const u_char *start, u_char *buf, int *buflen); + +/* + * Return the alignment requirement of this type. Zero is same as one. + */ +typedef int ng_getAlign_t(const struct ng_parse_type *type); + +/************************************************************************ + TYPE DEFINITION + ************************************************************************/ + +/* + * This structure describes a type, which may be a sub-type of another + * type by pointing to it with 'supertype' and omitting one or more methods. + */ +struct ng_parse_type { + const struct ng_parse_type *supertype; /* super-type, if any */ + const void *info; /* type-specific info */ + void *private; /* client private info */ + ng_parse_t *parse; /* parse method */ + ng_unparse_t *unparse; /* unparse method */ + ng_getDefault_t *getDefault; /* get default value method */ + ng_getAlign_t *getAlign; /* get alignment */ +}; + +/************************************************************************ + PRE-DEFINED TYPES + ************************************************************************/ + +/* + * Structures + * + * Default value: Determined on a per-field basis + * Additional info: struct ng_parse_struct_info * + */ +extern const struct ng_parse_type ng_parse_struct_type; + +/* Each field has a name, type, and optional alignment override. If the + override is non-zero, the alignment is determined from the field type. + Note: add an extra struct ng_parse_struct_field with name == NULL + to indicate the end of the list. */ +struct ng_parse_struct_info { + struct ng_parse_struct_field { + const char *name; /* field name */ + const struct ng_parse_type + *type; /* field type */ + int alignment; /* override alignment */ + } fields[0]; +}; + +/* + * Fixed length arrays + * + * Default value: See below + * Additional info: struct ng_parse_fixedarray_info * + */ +extern const struct ng_parse_type ng_parse_fixedarray_type; + +typedef int ng_parse_array_getLength_t(const struct ng_parse_type *type, + const u_char *start, const u_char *buf); +typedef int ng_parse_array_getDefault_t(const struct ng_parse_type *type, + int index, const u_char *start, + u_char *buf, int *buflen); + +/* The 'getDefault' method may be NULL, in which case the default value + is computed from the element type. If not, it should fill in the + default value at *buf (having size *buflen) and update *buflen to the + length of the filled-in value before return. */ +struct ng_parse_fixedarray_info { + const struct ng_parse_type *elementType; + int length; + ng_parse_array_getDefault_t *getDefault; +}; + +/* + * Variable length arrays + * + * Default value: Same as with fixed length arrays + * Additional info: struct ng_parse_array_info * + */ +extern const struct ng_parse_type ng_parse_array_type; + +struct ng_parse_array_info { + const struct ng_parse_type *elementType; + ng_parse_array_getLength_t *getLength; + ng_parse_array_getDefault_t *getDefault; +}; + +/* + * Arbitrary length strings + * + * Default value: Empty string + * Additional info: None required + */ +extern const struct ng_parse_type ng_parse_string_type; + +/* + * Bounded length strings. These are strings that have a fixed-size + * buffer, and always include a terminating NUL character. + * + * Default value: Empty string + * Additional info: struct ng_parse_fixedsstring_info * + */ +extern const struct ng_parse_type ng_parse_fixedstring_type; + +struct ng_parse_fixedsstring_info { + int bufSize; /* size of buffer (including NUL) */ +}; + +/* + * Some commonly used bounded length string types + */ +extern const struct ng_parse_type ng_parse_nodebuf_type; /* NG_NODELEN + 1 */ +extern const struct ng_parse_type ng_parse_hookbuf_type; /* NG_HOOKLEN + 1 */ +extern const struct ng_parse_type ng_parse_pathbuf_type; /* NG_PATHLEN + 1 */ +extern const struct ng_parse_type ng_parse_typebuf_type; /* NG_TYPELEN + 1 */ +extern const struct ng_parse_type ng_parse_cmdbuf_type; /* NG_CMDSTRLEN + 1 */ + +/* + * Integer types + * + * Default value: 0 + * Additional info: None required + */ +extern const struct ng_parse_type ng_parse_int8_type; +extern const struct ng_parse_type ng_parse_int16_type; +extern const struct ng_parse_type ng_parse_int32_type; +extern const struct ng_parse_type ng_parse_int64_type; + +/* + * IP address type + * + * Default value: 0.0.0.0 + * Additional info: None required + */ +extern const struct ng_parse_type ng_parse_ipaddr_type; + +/* + * Variable length byte array. The bytes are displayed in hex. + * ASCII form may be either an array of bytes or a string constant, + * in which case the array is zero-filled after the string bytes. + * + * Default value: All bytes are zero + * Additional info: ng_parse_array_getLength_t * + */ +extern const struct ng_parse_type ng_parse_bytearray_type; + +/* + * Netgraph control message type + * + * Default value: All fields zero + * Additional info: None required + */ +extern const struct ng_parse_type ng_parse_ng_mesg_type; + +/************************************************************************ + CONVERSTION AND PARSING ROUTINES + ************************************************************************/ + +/* Tokens for parsing structs and arrays */ +enum ng_parse_token { + T_LBRACE, /* '{' */ + T_RBRACE, /* '}' */ + T_LBRACKET, /* '[' */ + T_RBRACKET, /* ']' */ + T_EQUALS, /* '=' */ + T_STRING, /* string in double quotes */ + T_ERROR, /* error parsing string in double quotes */ + T_WORD, /* anything else containing no whitespace */ + T_EOF, /* end of string reached */ +}; + +/* + * See typedef ng_parse_t for definition + */ +extern int ng_parse(const struct ng_parse_type *type, const char *string, + int *off, u_char *buf, int *buflen); + +/* + * See typedef ng_unparse_t for definition (*off assumed to be zero). + */ +extern int ng_unparse(const struct ng_parse_type *type, + const u_char *data, char *buf, int buflen); + +/* + * See typedef ng_getDefault_t for definition + */ +extern int ng_parse_getDefault(const struct ng_parse_type *type, + u_char *buf, int *buflen); + +/* + * Parse a token: '*startp' is the offset to start looking. Upon + * successful return, '*startp' equals the beginning of the token + * and '*lenp' the length. If error, '*startp' points at the + * offending character(s). + */ +extern enum ng_parse_token ng_parse_get_token(const char *s, + int *startp, int *lenp); + +/* + * Like above, but specifically for getting a string token and returning + * the string value. The string token must be enclosed in double quotes + * and the normal C backslash escapes are recognized. The caller must + * eventually free() the returned result. Returns NULL if token is + * not a string token, or parse or other error. + */ +extern char *ng_get_string_token(const char *s, int *startp, int *lenp); + +/* + * Convert a raw string into a doubly-quoted string including any + * necessary backslash escapes. Caller must free the result. + * Returns NULL if ENOMEM. + */ +extern char *ng_encode_string(const char *s); + +#endif /* _NETGRAPH_PARSE_H_ */ + diff --git a/sys/netgraph/ng_ppp.c b/sys/netgraph/ng_ppp.c index 2097de1..7cfb4b6 100644 --- a/sys/netgraph/ng_ppp.c +++ b/sys/netgraph/ng_ppp.c @@ -57,6 +57,7 @@ #include <netgraph/ng_message.h> #include <netgraph/netgraph.h> +#include <netgraph/ng_parse.h> #include <netgraph/ng_ppp.h> #include <netgraph/ng_vjc.h> @@ -205,6 +206,78 @@ static int ng_ppp_config_valid(node_p node, static void ng_ppp_update(node_p node, int newConf); static void ng_ppp_free_frags(node_p node); +/* Parse type for struct ng_ppp_link_config */ +static const struct ng_parse_struct_info + ng_ppp_link_type_info = NG_PPP_LINK_TYPE_INFO; +static const struct ng_parse_type ng_ppp_link_type = { + &ng_parse_struct_type, + &ng_ppp_link_type_info, +}; + +/* Parse type for struct ng_ppp_node_config */ +struct ng_parse_fixedarray_info ng_ppp_array_info = { + &ng_ppp_link_type, + NG_PPP_MAX_LINKS +}; +static const struct ng_parse_type ng_ppp_link_array_type = { + &ng_parse_fixedarray_type, + &ng_ppp_array_info, +}; +static const struct ng_parse_struct_info ng_ppp_config_type_info + = NG_PPP_CONFIG_TYPE_INFO(&ng_ppp_link_array_type); +static const struct ng_parse_type ng_ppp_config_type = { + &ng_parse_struct_type, + &ng_ppp_config_type_info +}; + +/* Parse type for struct ng_ppp_link_stat */ +static const struct ng_parse_struct_info + ng_ppp_stats_type_info = NG_PPP_STATS_TYPE_INFO; +static const struct ng_parse_type ng_ppp_stats_type = { + &ng_parse_struct_type, + &ng_ppp_stats_type_info +}; + +/* List of commands and how to convert arguments to/from ASCII */ +static const struct ng_cmdlist ng_ppp_cmds[] = { + { + NGM_PPP_COOKIE, + NGM_PPP_SET_CONFIG, + "setconfig", + &ng_ppp_config_type, + NULL + }, + { + NGM_PPP_COOKIE, + NGM_PPP_GET_CONFIG, + "getconfig", + NULL, + &ng_ppp_config_type + }, + { + NGM_PPP_COOKIE, + NGM_PPP_GET_LINK_STATS, + "getstats", + &ng_parse_int16_type, + &ng_ppp_stats_type + }, + { + NGM_PPP_COOKIE, + NGM_PPP_CLR_LINK_STATS, + "clrstats", + &ng_parse_int16_type, + NULL + }, + { + NGM_PPP_COOKIE, + NGM_PPP_GETCLR_LINK_STATS, + "getclrstats", + &ng_parse_int16_type, + &ng_ppp_stats_type + }, + { 0 } +}; + /* Node type descriptor */ static struct ng_type ng_ppp_typestruct = { NG_VERSION, @@ -218,7 +291,8 @@ static struct ng_type ng_ppp_typestruct = { NULL, ng_ppp_rcvdata, ng_ppp_rcvdata, - ng_ppp_disconnect + ng_ppp_disconnect, + ng_ppp_cmds }; NETGRAPH_INIT(ppp, &ng_ppp_typestruct); @@ -334,7 +408,7 @@ ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg, case NGM_PPP_SET_CONFIG: { struct ng_ppp_node_config *const newConf = - (struct ng_ppp_node_config *) msg->data; + (struct ng_ppp_node_config *) msg->data; /* Check for invalid or illegal config */ if (msg->header.arglen != sizeof(*newConf)) diff --git a/sys/netgraph/ng_ppp.h b/sys/netgraph/ng_ppp.h index 7962746..c32b5a31 100644 --- a/sys/netgraph/ng_ppp.h +++ b/sys/netgraph/ng_ppp.h @@ -92,6 +92,19 @@ struct ng_ppp_link_config { u_int32_t bandwidth; /* link bandwidth (in bytes/second) */ }; +/* Keep this in sync with the above structure definition */ +#define NG_PPP_LINK_TYPE_INFO { \ + { \ + { "enable", &ng_parse_int8_type }, \ + { "protocomp", &ng_parse_int8_type }, \ + { "acfcomp", &ng_parse_int8_type }, \ + { "mru", &ng_parse_int16_type }, \ + { "latency", &ng_parse_int32_type }, \ + { "bandwidth", &ng_parse_int32_type }, \ + { NULL }, \ + } \ +} + /* Node config structure */ struct ng_ppp_node_config { u_int16_t mrru; /* multilink peer MRRU */ @@ -112,6 +125,28 @@ struct ng_ppp_node_config { links[NG_PPP_MAX_LINKS]; }; +/* Keep this in sync with the above structure definition */ +#define NG_PPP_CONFIG_TYPE_INFO(arytype) { \ + { \ + { "mrru", &ng_parse_int16_type }, \ + { "multilink", &ng_parse_int8_type }, \ + { "recvShortSeq", &ng_parse_int8_type }, \ + { "xmitShortSeq", &ng_parse_int8_type }, \ + { "roundRobin", &ng_parse_int8_type }, \ + { "ip", &ng_parse_int8_type }, \ + { "appletalk", &ng_parse_int8_type }, \ + { "ipx", &ng_parse_int8_type }, \ + { "comp", &ng_parse_int8_type }, \ + { "decomp", &ng_parse_int8_type }, \ + { "encryption", &ng_parse_int8_type }, \ + { "decryption", &ng_parse_int8_type }, \ + { "vjcomp", &ng_parse_int8_type }, \ + { "vjdecomp", &ng_parse_int8_type }, \ + { "links", (arytype) }, \ + { NULL }, \ + } \ +} + /* Statistics struct for a link (or the bundle if NG_PPP_BUNDLE_LINKNUM) */ struct ng_ppp_link_stat { u_int32_t xmitFrames; /* xmit frames on link */ @@ -122,4 +157,17 @@ struct ng_ppp_link_stat { u_int32_t dupFragments; /* MP frames with duplicate seq # */ }; +/* Keep this in sync with the above structure definition */ +#define NG_PPP_STATS_TYPE_INFO { \ + { \ + { "xmitFrames", &ng_parse_int32_type }, \ + { "xmitOctets", &ng_parse_int32_type }, \ + { "recvFrames", &ng_parse_int32_type }, \ + { "recvOctets", &ng_parse_int32_type }, \ + { "badProtos", &ng_parse_int32_type }, \ + { "dupFragments", &ng_parse_int32_type }, \ + { NULL }, \ + } \ +} + #endif /* _NETGRAPH_PPP_H_ */ diff --git a/sys/netgraph/ng_pppoe.c b/sys/netgraph/ng_pppoe.c index 6499e8f..836b295 100644 --- a/sys/netgraph/ng_pppoe.c +++ b/sys/netgraph/ng_pppoe.c @@ -88,7 +88,8 @@ static struct ng_type typestruct = { ng_pppoe_connect, ng_pppoe_rcvdata, ng_pppoe_rcvdata, - ng_pppoe_disconnect + ng_pppoe_disconnect, + NULL }; NETGRAPH_INIT(pppoe, &typestruct); diff --git a/sys/netgraph/ng_rfc1490.c b/sys/netgraph/ng_rfc1490.c index 6cd20a5..13c3d2d 100644 --- a/sys/netgraph/ng_rfc1490.c +++ b/sys/netgraph/ng_rfc1490.c @@ -109,7 +109,8 @@ static struct ng_type typestruct = { NULL, ng_rfc1490_rcvdata, ng_rfc1490_rcvdata, - ng_rfc1490_disconnect + ng_rfc1490_disconnect, + NULL }; NETGRAPH_INIT(rfc1490, &typestruct); diff --git a/sys/netgraph/ng_rfc1490.h b/sys/netgraph/ng_rfc1490.h index 98cdf63..494a60f 100644 --- a/sys/netgraph/ng_rfc1490.h +++ b/sys/netgraph/ng_rfc1490.h @@ -45,7 +45,7 @@ /* Node type name */ #define NG_RFC1490_NODE_TYPE "rfc1490" -#define NGM_RFC1490_NODE_COOKIE 861060632 +#define NGM_RFC1490_COOKIE 861060632 /* Hook names */ #define NG_RFC1490_HOOK_DOWNSTREAM "downstream" diff --git a/sys/netgraph/ng_sample.c b/sys/netgraph/ng_sample.c index 5c68ad9..688cd81 100644 --- a/sys/netgraph/ng_sample.c +++ b/sys/netgraph/ng_sample.c @@ -79,7 +79,8 @@ static struct ng_type typestruct = { ng_xxx_connect, ng_xxx_rcvdata, ng_xxx_rcvdataq, - ng_xxx_disconnect + ng_xxx_disconnect, + NULL }; NETGRAPH_INIT(xxx, &typestruct); diff --git a/sys/netgraph/ng_socket.c b/sys/netgraph/ng_socket.c index 321037a..d783687 100644 --- a/sys/netgraph/ng_socket.c +++ b/sys/netgraph/ng_socket.c @@ -133,7 +133,8 @@ static struct ng_type typestruct = { NULL, ngs_rcvdata, ngs_rcvdata, - ngs_disconnect + ngs_disconnect, + NULL }; NETGRAPH_INIT(socket, &typestruct); diff --git a/sys/netgraph/ng_tee.c b/sys/netgraph/ng_tee.c index 7ae417c..2c5b9f3 100644 --- a/sys/netgraph/ng_tee.c +++ b/sys/netgraph/ng_tee.c @@ -96,7 +96,8 @@ static struct ng_type typestruct = { NULL, ngt_rcvdata, ngt_rcvdata, - ngt_disconnect + ngt_disconnect, + NULL }; NETGRAPH_INIT(tee, &typestruct); diff --git a/sys/netgraph/ng_tty.c b/sys/netgraph/ng_tty.c index 2d3a1c4..9b5568f 100644 --- a/sys/netgraph/ng_tty.c +++ b/sys/netgraph/ng_tty.c @@ -89,15 +89,15 @@ /* 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) */ + 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; @@ -106,7 +106,7 @@ typedef struct ngt_sc *sc_p; #define FLG_DEBUG 0x0002 /* Debugging */ -#ifdef DIAGNOSTICS +#ifdef INVARIANTS #define QUEUECHECK(sc) \ do { \ struct mbuf **mp; \ @@ -173,6 +173,7 @@ static struct ng_type typestruct = { ngt_rcvdata, ngt_rcvdata, ngt_disconnect, + NULL }; NETGRAPH_INIT(tty, &typestruct); diff --git a/sys/netgraph/ng_vjc.c b/sys/netgraph/ng_vjc.c index db96f1c..b572600 100644 --- a/sys/netgraph/ng_vjc.c +++ b/sys/netgraph/ng_vjc.c @@ -112,7 +112,8 @@ static struct ng_type typestruct = { NULL, ng_vjc_rcvdata, ng_vjc_rcvdata, - ng_vjc_disconnect + ng_vjc_disconnect, + NULL }; NETGRAPH_INIT(vjc, &typestruct); |