diff options
57 files changed, 4337 insertions, 456 deletions
diff --git a/lib/libnetgraph/Makefile b/lib/libnetgraph/Makefile index 569a1f7..342b2e5 100644 --- a/lib/libnetgraph/Makefile +++ b/lib/libnetgraph/Makefile @@ -9,8 +9,7 @@ SHLIB_MAJOR= 1 SRCS= sock.c msg.c debug.c -CFLAGS+= -g -Wall -O2 -Werror -CFLAGS+= -I/usr/src.freefall/sys +CFLAGS+= -g -Wall beforeinstall: ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/netgraph.h \ diff --git a/lib/libnetgraph/debug.c b/lib/libnetgraph/debug.c index bf57da6..cd7f7cb 100644 --- a/lib/libnetgraph/debug.c +++ b/lib/libnetgraph/debug.c @@ -41,24 +41,45 @@ */ #include <sys/types.h> + #include <stdarg.h> + +#include <netinet/in.h> +#include <net/ethernet.h> + #include <netgraph/ng_message.h> #include <netgraph/ng_socket.h> #include "netgraph.h" #include "internal.h" -#include <netgraph/ng_socket.h> -#include <netgraph/ng_message.h> -#include <netgraph/ng_iface.h> -#include <netgraph/ng_rfc1490.h> -#include <netgraph/ng_cisco.h> +#include <netgraph/ng_UI.h> #include <netgraph/ng_async.h> -#include <netgraph/ng_ppp.h> +#include <netgraph/ng_cisco.h> +#include <netgraph/ng_echo.h> +#include <netgraph/ng_ether.h> #include <netgraph/ng_frame_relay.h> +#include <netgraph/ng_hole.h> +#include <netgraph/ng_iface.h> +#include <netgraph/ng_ksocket.h> #include <netgraph/ng_lmi.h> +#include <netgraph/ng_ppp.h> +#include <netgraph/ng_pppoe.h> +#include <netgraph/ng_rfc1490.h> +#include <netgraph/ng_socket.h> +#include <netgraph/ng_tee.h> #include <netgraph/ng_tty.h> -#include <netgraph/ng_tty.h> +#include <netgraph/ng_vjc.h> +#ifdef WHISTLE +#include <machine/../isa/df_def.h> +#include <machine/../isa/if_wfra.h> +#include <machine/../isa/ipac.h> +#include <netgraph/ng_df.h> +#include <netgraph/ng_ipac.h> +#include <netgraph/ng_mppc.h> +#include <netgraph/ng_pptpgre.h> +#include <netgraph/ng_tn.h> +#endif /* Global debug level */ int _gNgDebugLevel = 0; @@ -69,8 +90,45 @@ void (*_NgLogx) (const char *fmt,...) = warnx; /* Internal functions */ static const char *NgCookie(int cookie); -static const char *NgCmd(int cookie, int cmd); -static void NgArgs(int cookie, int cmd, int resp, void *args, int arglen); + +/* Known typecookie list */ +struct ng_cookie { + int cookie; + const char *type; +}; + +#define COOKIE(c) { NGM_ ## c ## _COOKIE, #c } + +/* List of known cookies */ +static const struct ng_cookie cookies[] = { + COOKIE(UI), + COOKIE(ASYNC), + COOKIE(CISCO), + COOKIE(ECHO), + COOKIE(ETHER), + COOKIE(FRAMERELAY), + COOKIE(GENERIC), + COOKIE(HOLE), + COOKIE(IFACE), + COOKIE(KSOCKET), + COOKIE(LMI), + COOKIE(PPP), + COOKIE(PPPOE), + COOKIE(RFC1490), + COOKIE(SOCKET), + COOKIE(TEE), + COOKIE(TTY), + COOKIE(VJC), +#ifdef WHISTLE + COOKIE(DF), + COOKIE(IPAC), + COOKIE(MPPC), + COOKIE(PPTPGRE), + COOKIE(TN), + COOKIE(WFRA), +#endif + { 0, NULL } +}; /* * Set debug level, ie, verbosity, if "level" is non-negative. @@ -102,27 +160,82 @@ NgSetErrLog(void (*log) (const char *fmt,...), * Display a netgraph sockaddr */ void -_NgDebugSockaddr(struct sockaddr_ng *sg) +_NgDebugSockaddr(const struct sockaddr_ng *sg) { NGLOGX("SOCKADDR: { fam=%d len=%d addr=\"%s\" }", sg->sg_family, sg->sg_len, sg->sg_data); } +#define ARGS_BUFSIZE 1024 + /* * Display a negraph message */ void -_NgDebugMsg(struct ng_mesg * msg) +_NgDebugMsg(const struct ng_mesg *msg, const char *path) { + u_char buf[2 * sizeof(struct ng_mesg) + ARGS_BUFSIZE]; + struct ng_mesg *const req = (struct ng_mesg *)buf; + struct ng_mesg *const bin = (struct ng_mesg *)req->data; + int arglen, debugSave, csock = -1; + + /* Lower debugging to avoid infinite recursion */ + debugSave = _gNgDebugLevel; + _gNgDebugLevel -= 4; + + /* Display header stuff */ NGLOGX("NG_MESG :"); NGLOGX(" vers %d", msg->header.version); NGLOGX(" arglen %d", msg->header.arglen); NGLOGX(" flags %ld", msg->header.flags); - NGLOGX(" token %lu", (u_long) msg->header.token); - NGLOGX(" cookie %s", NgCookie(msg->header.typecookie)); - NGLOGX(" cmd %s", NgCmd(msg->header.typecookie, msg->header.cmd)); - NgArgs(msg->header.typecookie, msg->header.cmd, - (msg->header.flags & NGF_RESP), msg->data, msg->header.arglen); + NGLOGX(" token %lu", (u_long)msg->header.token); + NGLOGX(" cookie %s (%d)", + NgCookie(msg->header.typecookie), msg->header.typecookie); + + /* At lower debugging levels, skip ASCII translation */ + if (_gNgDebugLevel <= 2) + goto fail2; + + /* If path is not absolute, don't bother trying to use relative + address on a different socket for the ASCII translation */ + if (strchr(path, ':') == NULL) + goto fail2; + + /* Get a temporary socket */ + if (NgMkSockNode(NULL, &csock, NULL) < 0) + goto fail; + + /* Copy binary message into request message payload */ + arglen = msg->header.arglen; + if (arglen > ARGS_BUFSIZE) + arglen = ARGS_BUFSIZE; + memcpy(bin, msg, sizeof(*msg) + arglen); + bin->header.arglen = arglen; + + /* Ask the node to translate the binary message to ASCII for us */ + if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, + NGM_BINARY2ASCII, bin, sizeof(*bin) + bin->header.arglen) < 0) + goto fail; + if (NgRecvMsg(csock, req, sizeof(buf), NULL) < 0) + goto fail; + + /* Display command string and arguments */ + NGLOGX(" cmd %s (%d)", bin->header.cmdstr, bin->header.cmd); + NGLOGX(" args %s", bin->data); + goto done; + +fail: + /* Just display binary version */ + NGLOGX(" [error decoding message: %s]", strerror(errno)); +fail2: + NGLOGX(" cmd %d", msg->header.cmd); + NGLOGX(" args (%d bytes)", msg->header.arglen); + _NgDebugBytes(msg->data, msg->header.arglen); + +done: + if (csock != -1) + (void)close(csock); + _gNgDebugLevel = debugSave; } /* @@ -131,240 +244,20 @@ _NgDebugMsg(struct ng_mesg * msg) static const char * NgCookie(int cookie) { - static char buf[20]; - - switch (cookie) { - case NGM_GENERIC_COOKIE: - return "generic"; - case NGM_TTY_COOKIE: - return "tty"; - case NGM_ASYNC_COOKIE: - return "async"; - case NGM_IFACE_COOKIE: - return "iface"; - case NGM_FRAMERELAY_COOKIE: - return "frame_relay"; - case NGM_LMI_COOKIE: - return "lmi"; - case NGM_CISCO_COOKIE: - return "cisco"; - case NGM_PPP_COOKIE: - return "ppp"; - case NGM_RFC1490_NODE_COOKIE: - return "rfc1490"; - case NGM_SOCKET_COOKIE: - return "socket"; - } - snprintf(buf, sizeof(buf), "?? (%d)", cookie); - return buf; -} - -/* - * Return the name of the command - */ -static const char * -NgCmd(int cookie, int cmd) -{ - static char buf[20]; - - switch (cookie) { - case NGM_GENERIC_COOKIE: - switch (cmd) { - case NGM_SHUTDOWN: - return "shutdown"; - case NGM_MKPEER: - return "mkpeer"; - case NGM_CONNECT: - return "connect"; - case NGM_NAME: - return "name"; - case NGM_RMHOOK: - return "rmhook"; - case NGM_NODEINFO: - return "nodeinfo"; - case NGM_LISTHOOKS: - return "listhooks"; - case NGM_LISTNAMES: - return "listnames"; - case NGM_LISTNODES: - return "listnodes"; - case NGM_TEXT_STATUS: - return "text_status"; - } - break; - case NGM_TTY_COOKIE: - switch (cmd) { - case NGM_TTY_GET_HOTCHAR: - return "getHotChar"; - case NGM_TTY_SET_HOTCHAR: - return "setHotChar"; - } - break; - case NGM_ASYNC_COOKIE: - switch (cmd) { - case NGM_ASYNC_CMD_GET_STATS: - return "getStats"; - case NGM_ASYNC_CMD_CLR_STATS: - return "setStats"; - case NGM_ASYNC_CMD_SET_CONFIG: - return "setConfig"; - case NGM_ASYNC_CMD_GET_CONFIG: - return "getConfig"; - } - break; - case NGM_IFACE_COOKIE: - switch (cmd) { - case NGM_IFACE_GET_IFNAME: - return "getIfName"; - case NGM_IFACE_GET_IFADDRS: - return "getIfAddrs"; - } - break; - case NGM_LMI_COOKIE: - switch (cmd) { - case NGM_LMI_GET_STATUS: - return "get-status"; - } - break; - } - snprintf(buf, sizeof(buf), "?? (%d)", cmd); - return buf; -} - -/* - * Decode message arguments - */ -static void -NgArgs(int cookie, int cmd, int resp, void *args, int arglen) -{ - -switch (cookie) { -case NGM_GENERIC_COOKIE: - switch (cmd) { - case NGM_SHUTDOWN: - return; - case NGM_MKPEER: - { - struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) args; - - if (resp) - return; - NGLOGX(" type \"%s\"", mkp->type); - NGLOGX(" ourhook \"%s\"", mkp->ourhook); - NGLOGX(" peerhook \"%s\"", mkp->peerhook); - return; - } - case NGM_CONNECT: - { - struct ngm_connect *const ngc = (struct ngm_connect *) args; - - if (resp) - return; - NGLOGX(" path \"%s\"", ngc->path); - NGLOGX(" ourhook \"%s\"", ngc->ourhook); - NGLOGX(" peerhook \"%s\"", ngc->peerhook); - return; - } - case NGM_NAME: - { - struct ngm_name *const ngn = (struct ngm_name *) args; - - if (resp) - return; - NGLOGX(" name \"%s\"", ngn->name); - return; - } - case NGM_RMHOOK: - { - struct ngm_rmhook *const ngr = (struct ngm_rmhook *) args; - - if (resp) - return; - NGLOGX(" hook \"%s\"", ngr->ourhook); - return; - } - case NGM_NODEINFO: - return; - case NGM_LISTHOOKS: - return; - case NGM_LISTNAMES: - case NGM_LISTNODES: - return; - case NGM_TEXT_STATUS: - if (!resp) - return; - NGLOGX(" status \"%s\"", (char *) args); - return; - } - break; - -case NGM_TTY_COOKIE: - switch (cmd) { - case NGM_TTY_GET_HOTCHAR: - if (!resp) - return; - NGLOGX(" char 0x%02x", *((int *) args)); - return; - case NGM_TTY_SET_HOTCHAR: - NGLOGX(" char 0x%02x", *((int *) args)); - return; - } - break; - -case NGM_ASYNC_COOKIE: - switch (cmd) { - case NGM_ASYNC_CMD_GET_STATS: - { - struct ng_async_stat *const as = (struct ng_async_stat *) args; - - if (!resp) - return; - NGLOGX(" syncOctets = %lu", as->syncOctets); - NGLOGX(" syncFrames = %lu", as->syncFrames); - NGLOGX(" syncOverflows = %lu", as->syncOverflows); - NGLOGX(" asyncOctets = %lu", as->asyncOctets); - NGLOGX(" asyncFrames = %lu", as->asyncFrames); - NGLOGX(" asyncRunts = %lu", as->asyncRunts); - NGLOGX(" asyncOverflows = %lu", as->asyncOverflows); - NGLOGX(" asyncBadCheckSums = %lu", as->asyncBadCheckSums); - return; - } - case NGM_ASYNC_CMD_GET_CONFIG: - case NGM_ASYNC_CMD_SET_CONFIG: - { - struct ng_async_cfg *const ac = (struct ng_async_cfg *) args; - - if (!resp ^ (cmd != NGM_ASYNC_CMD_GET_CONFIG)) - return; - NGLOGX(" enabled %s", ac->enabled ? "YES" : "NO"); - NGLOGX(" Async MRU %u", ac->amru); - NGLOGX(" Sync MRU %u", ac->smru); - NGLOGX(" ACCM 0x%08x", ac->accm); - return; - } - case NGM_ASYNC_CMD_CLR_STATS: - return; - } - break; - -case NGM_IFACE_COOKIE: - switch (cmd) { - case NGM_IFACE_GET_IFNAME: - return; - case NGM_IFACE_GET_IFADDRS: - return; - } - break; + int k; + for (k = 0; cookies[k].cookie != 0; k++) { + if (cookies[k].cookie == cookie) + return cookies[k].type; } - _NgDebugBytes(args, arglen); + return "??"; } /* * Dump bytes in hex */ void -_NgDebugBytes(const u_char * ptr, int len) +_NgDebugBytes(const u_char *ptr, int len) { char buf[100]; int k, count; diff --git a/lib/libnetgraph/internal.h b/lib/libnetgraph/internal.h index 1022183..d521cfa 100644 --- a/lib/libnetgraph/internal.h +++ b/lib/libnetgraph/internal.h @@ -61,7 +61,7 @@ extern void (*_NgLogx)(const char *fmt, ...); #define NGLOG (*_NgLog) #define NGLOGX (*_NgLogx) -extern void _NgDebugSockaddr(struct sockaddr_ng *sg); -extern void _NgDebugMsg(struct ng_mesg *msg); +extern void _NgDebugSockaddr(const struct sockaddr_ng *sg); +extern void _NgDebugMsg(const struct ng_mesg *msg, const char *path); extern void _NgDebugBytes(const u_char *ptr, int size); diff --git a/lib/libnetgraph/msg.c b/lib/libnetgraph/msg.c index 7eda707..ece1787 100644 --- a/lib/libnetgraph/msg.c +++ b/lib/libnetgraph/msg.c @@ -82,6 +82,66 @@ NgSendMsg(int cs, const char *path, } /* + * Send a message given in ASCII format. We first ask the node to translate + * the command into binary, and then we send the binary. + */ +int +NgSendAsciiMsg(int cs, const char *path, const char *fmt, ...) +{ + const int bufSize = 1024; + char replybuf[2 * sizeof(struct ng_mesg) + bufSize]; + struct ng_mesg *const reply = (struct ng_mesg *)replybuf; + struct ng_mesg *const binary = (struct ng_mesg *)reply->data; + struct ng_mesg *ascii; + char *buf, *cmd, *args; + va_list fmtargs; + + /* Parse out command and arguments */ + va_start(fmtargs, fmt); + vasprintf(&buf, fmt, fmtargs); + va_end(fmtargs); + if (buf == NULL) + return (-1); + + /* Parse out command, arguments */ + for (cmd = buf; isspace(*cmd); cmd++) + ; + for (args = cmd; *args != '\0' && !isspace(*args); args++) + ; + if (*args != '\0') { + while (isspace(*args)) + *args++ = '\0'; + } + + /* Get a bigger buffer to hold inner message header plus arg string */ + if ((ascii = malloc(sizeof(struct ng_mesg) + + strlen(buf) + 1)) == NULL) { + free(buf); + return (-1); + } + memset(ascii, 0, sizeof(*ascii)); + + /* Build inner header (only need cmdstr, arglen, and data fields) */ + strncpy(ascii->header.cmdstr, cmd, sizeof(ascii->header.cmdstr) - 1); + strcpy(ascii->data, args); + ascii->header.arglen = strlen(ascii->data) + 1; + free(buf); + + /* Send node a request to convert ASCII to binary */ + if (NgSendMsg(cs, path, NGM_GENERIC_COOKIE, NGM_ASCII2BINARY, + (u_char *)ascii, sizeof(*ascii) + ascii->header.arglen) < 0) + return (-1); + + /* Get reply */ + if (NgRecvMsg(cs, reply, sizeof(replybuf), NULL) < 0) + return (-1); + + /* Now send binary version */ + return NgDeliverMsg(cs, + path, binary, binary->data, binary->header.arglen); +} + +/* * Send a message that is a reply to a previously received message. * Returns -1 and sets errno on error, otherwise returns zero. */ @@ -143,7 +203,7 @@ NgDeliverMsg(int cs, const char *path, NGLOGX("SENDING %s:", (msg->header.flags & NGF_RESP) ? "RESPONSE" : "MESSAGE"); _NgDebugSockaddr(sg); - _NgDebugMsg(msg); + _NgDebugMsg(msg, sg->sg_data); } /* Send it */ @@ -193,7 +253,7 @@ NgRecvMsg(int cs, struct ng_mesg *rep, size_t replen, char *path) NGLOGX("RECEIVED %s:", (rep->header.flags & NGF_RESP) ? "RESPONSE" : "MESSAGE"); _NgDebugSockaddr(sg); - _NgDebugMsg(rep); + _NgDebugMsg(rep, sg->sg_data); } /* Done */ @@ -204,3 +264,48 @@ errout: return (-1); } +/* + * Receive a control message and convert the arguments to ASCII + */ +int +NgRecvAsciiMsg(int cs, struct ng_mesg *reply, size_t replen, char *path) +{ + struct ng_mesg *msg, *ascii; + int bufSize, errnosv; + u_char *buf; + + /* Allocate buffer */ + bufSize = 2 * sizeof(*reply) + replen; + if ((buf = malloc(bufSize)) == NULL) + return (-1); + msg = (struct ng_mesg *)buf; + ascii = (struct ng_mesg *)msg->data; + + /* Get binary message */ + if (NgRecvMsg(cs, msg, bufSize, path) < 0) + goto fail; + memcpy(reply, msg, sizeof(*msg)); + + /* Ask originating node to convert the arguments to ASCII */ + if (NgSendMsg(cs, path, NGM_GENERIC_COOKIE, + NGM_BINARY2ASCII, msg, sizeof(*msg) + msg->header.arglen) < 0) + goto fail; + if (NgRecvMsg(cs, msg, bufSize, NULL) < 0) + goto fail; + + /* Copy result to client buffer */ + if (sizeof(*ascii) + ascii->header.arglen > replen) { + errno = ERANGE; +fail: + errnosv = errno; + free(buf); + errno = errnosv; + return (-1); + } + strncpy(reply->data, ascii->data, ascii->header.arglen); + + /* Done */ + free(buf); + return (0); +} + diff --git a/lib/libnetgraph/netgraph.3 b/lib/libnetgraph/netgraph.3 index dc9df04..6926f96 100644 --- a/lib/libnetgraph/netgraph.3 +++ b/lib/libnetgraph/netgraph.3 @@ -57,10 +57,14 @@ .Ft int .Fn NgSendMsg "int cs" "const char *path" "int cookie" "int cmd" "const void *arg" "size_t arglen" .Ft int +.Fn NgSendAsciiMsg "int cs" "const char *path" "const char *fmt" "..." +.Ft int .Fn NgSendMsgReply "int cs" "const char *path" "struct ng_mesg *msg" "const void *arg" "size_t arglen" .Ft int .Fn NgRecvMsg "int cs" "struct ng_mesg *rep" "size_t replen" "char *path" .Ft int +.Fn NgRecvAsciiMsg "int cs" "struct ng_mesg *rep" "size_t replen" "char *path" +.Ft int .Fn NgSendData "int ds" "const char *hook" "const u_char *buf" "size_t len" .Ft int .Fn NgRecvData "int ds" "u_char *buf" "size_t len" "char *hook" @@ -97,7 +101,8 @@ assigns a global name to the node addressed by .Fa path . .Pp .Fn NgSendMsg -sends a control message from the socket node associated with control socket +sends a binary control message from the socket node associated +with control socket .Fa cs to the node addressed by .Fa path . @@ -122,6 +127,20 @@ to send reply to a previously received control message. The original message header should be pointed to by .Fa msg . .Pp +.Fn NgSendAsciiMsg +performs the same function as +.Fn NgSendMsg , +but adds support for ASCII encoding of control messages. +.Fn NgSendAsciiMsg +formats its input a la +.Xr printf 3 +and then sends the resulting ASCII string to the node in a +.Dv NGM_ASCII2BINARY +control message. The node returns a binary version of the +message, which is then sent back to the node just as with +.Fn NgSendMsg . +Note that ASCII conversion may not be supported by all node types. +.Pp .Fn NgRecvMsg reads the next control message received by the node associated with control socket @@ -136,6 +155,18 @@ is non-NULL, it must point to a buffer of at least bytes, which will be filled in (and NUL terminated) with the path to the node from which the message was received. .Pp +.Fn NgRecvAsciiMsg +works exactly like +.Fn NgRecvMsg , +except that after the message is received, any binary arguments +are converted to ASCII by sending a +.Dv NGM_BINARY2ASCII +request back to the originating node. The result is the same as +.Fn NgRecvAsciiMsg , +with the exception that the reply arguments field will contain +a NUL-terminated ASCII version of the arguments (and the reply +header argument length field will be adjusted). +.Pp .Fn NgSendData writes a data packet out on the specified hook of the node corresponding to data socket @@ -171,6 +202,11 @@ The default logging functions are and .Xr vwarnx 3 . .Pp +At debug level 3, the library attempts to display control message arguments +in ASCII format; however, this results in additional messages being +sent which may interfere with debugging. At even higher levels, +even these additional messagages will be displayed, etc. +.Pp Note that .Xr select 2 can be used on the data and the control sockets to detect the presence of @@ -182,9 +218,9 @@ User mode programs must be linked with the .Dv -lnetgraph flag to link in this library. .Sh INITIALIZATION -Netgraph is not part of the standard FreeBSD kernel. To enable it, -either your kernel must be compiled with ``options NETGRAPH'' -in the kernel configuration file, or else the +To enable Netgraph in your kernel, either your kernel must be +compiled with ``options NETGRAPH'' in the kernel configuration +file, or else the .Xr netgraph 4 and .Xr ng_socket 8 @@ -196,6 +232,26 @@ All functions except and .Fn NgSetErrLog return -1 if there was an error and set errno accordingly. +.Pp +For +.Fn NgSendAsciiMsg +and +.Fn NgRecvAsciiMsg , +the following additional errors are possible: +.Bl -tag -width Er +.It Bq Er ENOSYS +The node type does not know how to encode or decode the control message. +.It Bq Er ERANGE +The encoded or decoded arguments were too long for the supplied buffer. +.It Bq Er ENOENT +An unknown structure field was seen in an ASCII control message. +.It Bq Er EALREADY +The same structure field was specified twice in an ASCII control message. +.It Bq Er EINVAL +ASCII control message parse error or illegal value. +.It Bq Er E2BIG +ASCII control message array or fixed width string buffer overflow. +.El .Sh SEE ALSO .Xr netgraph 4 , .Xr socket 2 , @@ -205,7 +261,7 @@ return -1 if there was an error and set errno accordingly. .Sh HISTORY The .Em netgraph -system was designed and first implemented at Whistle Communications, Inc. -in a version FreeBSD 2.2 customized for the Whistle InterJet. +system was designed and first implemented at Whistle Communications, Inc. in +a version FreeBSD 2.2 customized for the Whistle InterJet. .Sh AUTHOR .An Archie Cobbs <archie@whistle.com> diff --git a/lib/libnetgraph/netgraph.h b/lib/libnetgraph/netgraph.h index f60f04b..c5a87cb 100644 --- a/lib/libnetgraph/netgraph.h +++ b/lib/libnetgraph/netgraph.h @@ -50,9 +50,11 @@ __BEGIN_DECLS int NgMkSockNode(const char *, int *, int *); int NgNameNode(int, const char *, const char *, ...); int NgSendMsg(int, const char *, int, int, const void *, size_t); +int NgSendAsciiMsg(int, const char *, const char *, ...); int NgSendReplyMsg(int, const char *, const struct ng_mesg *, const void *, size_t); int NgRecvMsg(int, struct ng_mesg *, size_t, char *); +int NgRecvAsciiMsg(int, struct ng_mesg *, size_t, char *); int NgSendData(int, const char *, const u_char *, size_t); int NgRecvData(int, u_char *, size_t, char *); int NgSetDebug(int); diff --git a/share/examples/netgraph/ngctl b/share/examples/netgraph/ngctl new file mode 100644 index 0000000..6f9507b --- /dev/null +++ b/share/examples/netgraph/ngctl @@ -0,0 +1,173 @@ +# $FreeBSD$ + +# +# This is an example that shows how to send ASCII formatted control +# messages to a node using ngctl(8). +# +# What we will do here create a divert(4) tap. This simply dumps +# out all packets diverted by some ipfw(8) divert rule to the console. +# +# Lines that begin with ``$'' (shell prompt) or ``+'' (ngctl prompt) +# indicate user input +# + +# First, start up ngctl in interactive mode: + + $ ngctl + Available commands: + connect Connects hook <peerhook> of the node at <relpath> to <hook> + debug Get/set debugging verbosity level + help Show command summary or get more help on a specific command + list Show information about all nodes + mkpeer Create and connect a new node to the node at "path" + msg Send a netgraph control message to the node at "path" + name Assign name <name> to the node at <path> + read Read and execute commands from a file + rmhook Disconnect hook "hook" of the node at "path" + show Show information about the node at <path> + shutdown Shutdown the node at <path> + status Get human readable status information from the node at <path> + types Show information about all installed node types + quit Exit program + + + +# Now let's create a ng_ksocket(8) node, in the family PF_INET, +# of type SOCK_RAW, and protocol IPPROTO_DIVERT: + + + mkpeer ksocket foo inet/raw/divert + +# Note that ``foo'' is the hook name on the socket node, which can be +# anything. The ``inet/raw/divert'' is the hook name on the ksocket +# node, which tells it what kind of socket to create. + +# Lets give our ksocket node a global name. How about ``fred'': + + + name foo fred + +# Note that we used ngctl's ``name'' command to do this. However, +# the following manually constructed netgraph message would have +# acomplished the exact same thing: + + + msg foo name { name="fred" } + +# Here we are using the ASCII <-> binary control message conversion +# routines. ngctl does this for us automatically when we use the +# ``msg'' command. + +# Now lets bind the socket associated with the ksocket node to a port +# supplied by the system. We do this by sending the ksocket node a +# ``bind'' control message. Again, ngctl does the conversion of the +# control message from ASCII to binary behind the scenes. + + + msg fred: bind inet/192.168.1.1 + +# The ksocket accepts arbitrary sockaddr structures, but also has +# special support for the PF_LOCAL and PF_INET protocol families. +# That is why we can specify the struct sockaddr argument to the +# ``bind'' command as ``inet/192.168.1.1'' (since we didn't specify +# a port number, it's assumed to be zero). We could have also +# relied on the generic sockaddr syntax and instead said this: + + + msg fred: bind { family=2 len=16 data=[ 2=192 168 1 1 ] } + +# This is what you would have to do for protocol families other +# that PF_INET and PF_LOCAL, at least until special handling for +# new ones is added. + +# The reason for the ``2=192'' is to skip the two byte IP port number, +# which causes it to be set to zero, the default value for integral +# types when parsing. Now since we didn't ask for a specific port +# number, we need to do a ``getname'' to see what port number we got: + + + msg fred: getname + Rec'd response "getname" (5) from "fred:": + Args: inet/192.168.1.1:1029 + +# As soon as we sent the message, we got back a response. Here +# ngctl is telling us that it received a control message with the +# NGF_RESP (response) flag set, the reponse was to a prior ``getname'' +# control message, that the originator was the node addressable +# as ``fred:''. The message arguments field is then displayed to +# us in its ASCII form. In this case, what we get back is a struct +# sockaddr, and there we see that our port number is 1029. + +# So now let's add the ipfw divert rule for whatever packets we +# want to see. How about anything from 192.168.1.129. + + + ^Z + Suspended + $ ipfw add 100 divert 1029 ip from 192.168.1.129 to any + 00100 divert 1029 ip from 192.168.1.129 to any + $ fg + +# Now watch what happens when we try to ping from that machine: + + + + Rec'd data packet on hook "foo": + 0000: 45 00 00 3c 57 00 00 00 20 01 bf ee c0 a8 01 81 E..<W... ....... + 0010: c0 a8 01 01 08 00 49 5c 03 00 01 00 61 62 63 64 ......I\....abcd + 0020: 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 efghijklmnopqrst + 0030: 75 76 77 61 62 63 64 65 66 67 68 69 uvwabcdefghi + + + Rec'd data packet on hook "foo": + 0000: 45 00 00 3c 58 00 00 00 20 01 be ee c0 a8 01 81 E..<X... ....... + 0010: c0 a8 01 01 08 00 48 5c 03 00 02 00 61 62 63 64 ......H\....abcd + 0020: 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 efghijklmnopqrst + 0030: 75 76 77 61 62 63 64 65 66 67 68 69 uvwabcdefghi + + + Rec'd data packet on hook "foo": + 0000: 45 00 00 3c 59 00 00 00 20 01 bd ee c0 a8 01 81 E..<Y... ....... + 0010: c0 a8 01 01 08 00 47 5c 03 00 03 00 61 62 63 64 ......G\....abcd + 0020: 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 efghijklmnopqrst + 0030: 75 76 77 61 62 63 64 65 66 67 68 69 uvwabcdefghi + + + +# So we're seeing the output from the ksocket socket appear on the ``foo'' +# hook of ngctl's socket node. Since the packets are getting diverted, +# the 192.168.1.129 machine doesn't see any response from us. + +# Of course, any type of socket can be used, even TCP: + + + mkpeer ksocket bar inet/stream/tcp + + msg bar connect inet/192.168.1.33:13 + ngctl: send msg: Operation now in progress + + + Rec'd data packet on hook "foo": + 0000: 4d 6f 6e 20 4e 6f 76 20 32 39 20 31 37 3a 34 38 Mon Nov 29 17:48 + 0010: 3a 33 37 20 31 39 39 39 0d 0a :37 1999.. + + + +# Or, UNIX domain: + + + mkpeer ksocket bar local/stream/0 + + msg bar bind local/"/tmp/bar.socket" + + + +# Here's an example of a more complicated ASCII control message argument. +# If you look in /sys/netgraph/ng_message.h, you will see that a node +# responds to a NGM_LISTHOOKS with a struct hooklist, which contains +# an array of struct linkinfo: +# +# /* Structure used for NGM_LISTHOOKS */ +# struct linkinfo { +# char ourhook[NG_HOOKLEN + 1]; /* hook name */ +# char peerhook[NG_HOOKLEN + 1]; /* peer hook */ +# struct nodeinfo nodeinfo; +# }; +# +# struct hooklist { +# struct nodeinfo nodeinfo; /* node information */ +# struct linkinfo link[0]; /* info about each hook */ +# }; +# +# By sending a node the ``listhooks'' command using ngctl, we can see +# this structure in ASCII form (lines wrapped for readability): + + + msg bar bind local/"/tmp/bar.socket" + + msg bar listhooks + Rec'd response "listhooks" (7) from "bar": + Args: { nodeinfo={ type="ksocket" id=9 hooks=1 } + linkinfo=[ { ourhook="local/stream/0" peerhook="bar" + nodeinfo={ name="ngctl1327" type="socket" id=8 hooks=1 } } ] } + + diff --git a/share/man/man4/netgraph.4 b/share/man/man4/netgraph.4 index e85e22e..f0d0e85 100644 --- a/share/man/man4/netgraph.4 +++ b/share/man/man4/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/share/man/man4/ng_cisco.4 b/share/man/man4/ng_cisco.4 index 452252f..26370cd 100644 --- a/share/man/man4/ng_cisco.4 +++ b/share/man/man4/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/share/man/man4/ng_pppoe.4 b/share/man/man4/ng_pppoe.4 index c0b0209..69d0016 100644 --- a/share/man/man4/ng_pppoe.4 +++ b/share/man/man4/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/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); diff --git a/usr.sbin/ngctl/Makefile b/usr.sbin/ngctl/Makefile index 9f51407..468a17c 100644 --- a/usr.sbin/ngctl/Makefile +++ b/usr.sbin/ngctl/Makefile @@ -3,7 +3,7 @@ PROG= ngctl SRCS= main.c mkpeer.c connect.c name.c show.c list.c \ - debug.c shutdown.c rmhook.c status.c types.c + msg.c debug.c shutdown.c rmhook.c status.c types.c MAN8= ngctl.8 DPADD+= ${LIBNETGRAPH} LDADD+= -lnetgraph diff --git a/usr.sbin/ngctl/connect.c b/usr.sbin/ngctl/connect.c index 8b15ca0..470468a 100644 --- a/usr.sbin/ngctl/connect.c +++ b/usr.sbin/ngctl/connect.c @@ -49,7 +49,8 @@ const struct ngcmd connect_cmd = { " \"path\" and \"relpath\" using hooks \"hook\" and \"peerhook\"," " respectively. The \"relpath\", if not absolute, is specified" " relative to the node at \"path\"." - " If \"path\" is omitted then \".\" is assumed." + " If \"path\" is omitted then \".\" is assumed.", + { "join" } }; static int diff --git a/usr.sbin/ngctl/list.c b/usr.sbin/ngctl/list.c index d763061..aeb74fb 100644 --- a/usr.sbin/ngctl/list.c +++ b/usr.sbin/ngctl/list.c @@ -47,7 +47,8 @@ const struct ngcmd list_cmd = { "Show information about all nodes", "The list command shows information every node that currently" " exists in the netgraph system. The optional -n argument limits" - " this list to only those nodes with a global name assignment." + " this list to only those nodes with a global name assignment.", + { "ls" } }; static int diff --git a/usr.sbin/ngctl/main.c b/usr.sbin/ngctl/main.c index f6d68b9..74017e7 100644 --- a/usr.sbin/ngctl/main.c +++ b/usr.sbin/ngctl/main.c @@ -35,14 +35,16 @@ * OF SUCH DAMAGE. * * $FreeBSD$ + * $Whistle: main.c,v 1.12 1999/11/29 19:17:46 archie Exp $ */ #include "ngctl.h" -#define PROMPT "+ " -#define MAX_ARGS 512 -#define WHITESPACE " \t\r\n\v\f" -#define NG_SOCKET_KLD "ng_socket.ko" +#define PROMPT "+ " +#define MAX_ARGS 512 +#define WHITESPACE " \t\r\n\v\f" +#define NG_SOCKET_KLD "ng_socket.ko" +#define DUMP_BYTES_PER_LINE 16 /* Internal functions */ static int ReadFile(FILE *fp); @@ -50,6 +52,8 @@ static int DoParseCommand(char *line); static int DoCommand(int ac, char **av); static int DoInteractive(void); static const struct ngcmd *FindCommand(const char *string); +static int MatchCommand(const struct ngcmd *cmd, const char *s); +static void DumpAscii(const u_char *buf, int len); static void Usage(const char *msg); static int ReadCmd(int ac, char **av); static int HelpCmd(int ac, char **av); @@ -62,6 +66,7 @@ static const struct ngcmd *const cmds[] = { &help_cmd, &list_cmd, &mkpeer_cmd, + &msg_cmd, &name_cmd, &read_cmd, &rmhook_cmd, @@ -78,19 +83,22 @@ const struct ngcmd read_cmd = { ReadCmd, "read <filename>", "Read and execute commands from a file", - NULL + NULL, + { "source", "." } }; const struct ngcmd help_cmd = { HelpCmd, "help [command]", "Show command summary or get more help on a specific command", - NULL + NULL, + { "?" } }; const struct ngcmd quit_cmd = { QuitCmd, "quit", "Exit program", - NULL + NULL, + { "exit" } }; /* Our control and data sockets */ @@ -200,16 +208,107 @@ ReadFile(FILE *fp) static int DoInteractive(void) { - char buf[LINE_MAX]; + const int maxfd = MAX(csock, dsock) + 1; - /* Read commands from stdin */ (*help_cmd.func)(0, NULL); - do { - printf("%s", PROMPT); - if (fgets(buf, sizeof(buf), stdin) == NULL) + while (1) { + struct timeval tv; + fd_set rfds; + + /* See if any data or control messages are arriving */ + FD_ZERO(&rfds); + FD_SET(csock, &rfds); + FD_SET(dsock, &rfds); + memset(&tv, 0, sizeof(tv)); + if (select(maxfd, &rfds, NULL, NULL, &tv) <= 0) { + + /* Issue prompt and wait for anything to happen */ + printf("%s", PROMPT); + fflush(stdout); + FD_ZERO(&rfds); + FD_SET(0, &rfds); + FD_SET(csock, &rfds); + FD_SET(dsock, &rfds); + if (select(maxfd, &rfds, NULL, NULL, NULL) < 0) + err(EX_OSERR, "select"); + + /* If not user input, print a newline first */ + if (!FD_ISSET(0, &rfds)) + printf("\n"); + } + + /* Display any incoming control message */ + while (FD_ISSET(csock, &rfds)) { + u_char buf[2 * sizeof(struct ng_mesg) + 8192]; + struct ng_mesg *const m = (struct ng_mesg *)buf; + struct ng_mesg *const ascii = (struct ng_mesg *)m->data; + char path[NG_PATHLEN+1]; + + /* Get incoming message (in binary form) */ + if (NgRecvMsg(csock, m, sizeof(buf), path) < 0) { + warn("recv incoming message"); + break; + } + + /* Ask originating node to convert to ASCII */ + if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, + NGM_BINARY2ASCII, m, + sizeof(*m) + m->header.arglen) < 0 + || NgRecvMsg(csock, m, sizeof(buf), NULL) < 0) { + printf("Rec'd %s %d from \"%s\":\n", + (m->header.flags & NGF_RESP) != 0 ? + "response" : "command", + m->header.cmd, path); + if (m->header.arglen == 0) + printf("No arguments\n"); + else + DumpAscii(m->data, m->header.arglen); + break; + } + + /* Display message in ASCII form */ + printf("Rec'd %s \"%s\" (%d) from \"%s\":\n", + (ascii->header.flags & NGF_RESP) != 0 ? + "response" : "command", + ascii->header.cmdstr, ascii->header.cmd, path); + if (*ascii->data != '\0') + printf("Args:\t%s\n", ascii->data); + else + printf("No arguments\n"); + break; + } + + /* Display any incoming data packet */ + while (FD_ISSET(dsock, &rfds)) { + u_char buf[8192]; + char hook[NG_HOOKLEN + 1]; + int rl; + + /* Read packet from socket */ + if ((rl = NgRecvData(dsock, + buf, sizeof(buf), hook)) < 0) + err(EX_OSERR, "read(hook)"); + if (rl == 0) + errx(EX_OSERR, "EOF from hook \"%s\"?", hook); + + /* Write packet to stdout */ + printf("Rec'd data packet on hook \"%s\":\n", hook); + DumpAscii(buf, rl); break; - fflush(stdout); - } while (DoParseCommand(buf) != CMDRTN_QUIT); + } + + /* Get any user input */ + if (FD_ISSET(0, &rfds)) { + char buf[LINE_MAX]; + + if (fgets(buf, sizeof(buf), stdin) == NULL) { + printf("\n"); + break; + } + if (DoParseCommand(buf) == CMDRTN_QUIT) + break; + } + } return(CMDRTN_QUIT); } @@ -255,17 +354,10 @@ DoCommand(int ac, char **av) static const struct ngcmd * FindCommand(const char *string) { - const struct ngcmd *cmd; - int k, len, found; - - if (strcmp(string, "?") == 0) - string = "help"; - for (k = 0, found = -1; cmds[k]; k++) { - cmd = cmds[k]; - len = strcspn(cmd->cmd, WHITESPACE); - if (len > strlen(string)) - len = strlen(string); - if (!strncasecmp(string, cmd->cmd, len)) { + int k, found = -1; + + for (k = 0; cmds[k] != NULL; k++) { + if (MatchCommand(cmds[k], string)) { if (found != -1) { warnx("\"%s\": ambiguous command", string); return(NULL); @@ -281,6 +373,32 @@ FindCommand(const char *string) } /* + * See if string matches a prefix of "cmd" (or an alias) case insensitively + */ +static int +MatchCommand(const struct ngcmd *cmd, const char *s) +{ + int a; + + /* Try to match command, ignoring the usage stuff */ + if (strlen(s) <= strcspn(cmd->cmd, WHITESPACE)) { + if (strncasecmp(s, cmd->cmd, strlen(s)) == 0) + return (1); + } + + /* Try to match aliases */ + for (a = 0; a < MAX_CMD_ALIAS && cmd->aliases[a] != NULL; a++) { + if (strlen(cmd->aliases[a]) >= strlen(s)) { + if (strncasecmp(s, cmd->aliases[a], strlen(s)) == 0) + return (1); + } + } + + /* No match */ + return (0); +} + +/* * ReadCmd() */ static int @@ -333,6 +451,20 @@ HelpCmd(int ac, char **av) /* Show help on a specific command */ if ((cmd = FindCommand(av[1])) != NULL) { printf("Usage: %s\n", cmd->cmd); + if (cmd->aliases[0] != NULL) { + int a = 0; + + printf("Aliases: "); + while (1) { + printf("%s", cmd->aliases[a++]); + if (a == MAX_CMD_ALIAS + || cmd->aliases[a] == NULL) { + printf("\n"); + break; + } + printf(", "); + } + } printf("Summary: %s\n", cmd->desc); if (cmd->help != NULL) { const char *s; @@ -370,6 +502,43 @@ QuitCmd(int ac, char **av) } /* + * Dump data in hex and ASCII form + */ +static void +DumpAscii(const u_char *buf, int len) +{ + char ch, sbuf[100]; + int k, count; + + for (count = 0; count < len; count += DUMP_BYTES_PER_LINE) { + snprintf(sbuf, sizeof(sbuf), "%04x: ", count); + for (k = 0; k < DUMP_BYTES_PER_LINE; k++) { + if (count + k < len) { + snprintf(sbuf + strlen(sbuf), + sizeof(sbuf) - strlen(sbuf), + "%02x ", buf[count + k]); + } else { + snprintf(sbuf + strlen(sbuf), + sizeof(sbuf) - strlen(sbuf), " "); + } + } + snprintf(sbuf + strlen(sbuf), sizeof(sbuf) - strlen(sbuf), " "); + for (k = 0; k < DUMP_BYTES_PER_LINE; k++) { + if (count + k < len) { + ch = isprint(buf[count + k]) ? + buf[count + k] : '.'; + snprintf(sbuf + strlen(sbuf), + sizeof(sbuf) - strlen(sbuf), "%c", ch); + } else { + snprintf(sbuf + strlen(sbuf), + sizeof(sbuf) - strlen(sbuf), " "); + } + } + printf("%s\n", sbuf); + } +} + +/* * Usage() */ static void diff --git a/usr.sbin/ngctl/msg.c b/usr.sbin/ngctl/msg.c new file mode 100644 index 0000000..7a04076 --- /dev/null +++ b/usr.sbin/ngctl/msg.c @@ -0,0 +1,88 @@ + +/* + * msg.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. + * + * $Whistle: msg.c,v 1.2 1999/11/29 23:38:35 archie Exp $ + * $FreeBSD$ + */ + +#include "ngctl.h" + +#define BUF_SIZE 1024 + +static int MsgCmd(int ac, char **av); + +const struct ngcmd msg_cmd = { + MsgCmd, + "msg path command [args ... ]", + "Send a netgraph control message to the node at \"path\"", + "The msg command constructs a netgraph control message from the" + " command name and ASCII arguments (if any) and sends that message" + " to the node. It does this by first asking the node to convert" + " the ASCII message into binary format, and resending the result." + " The typecookie used for the message is assumed to be the typecookie" + " corresponding to the target node's type.", + { "cmd" } +}; + +static int +MsgCmd(int ac, char **av) +{ + char buf[BUF_SIZE]; + char *path, *cmdstr; + int i; + + /* Get arguments */ + if (ac < 3) + return(CMDRTN_USAGE); + path = av[1]; + cmdstr = av[2]; + + /* Put command and arguments back together as one string */ + for (*buf = '\0', i = 3; i < ac; i++) { + snprintf(buf + strlen(buf), + sizeof(buf) - strlen(buf), " %s", av[i]); + } + + /* Send it */ + if (NgSendAsciiMsg(csock, path, "%s%s", cmdstr, buf) < 0) { + warn("send msg"); + return(CMDRTN_ERROR); + } + + /* Done */ + return(CMDRTN_OK); +} + diff --git a/usr.sbin/ngctl/ngctl.8 b/usr.sbin/ngctl/ngctl.8 index 8537d76..44b5402 100644 --- a/usr.sbin/ngctl/ngctl.8 +++ b/usr.sbin/ngctl/ngctl.8 @@ -60,11 +60,15 @@ will enter interactive mode. Otherwise .Nm ngctl will execute the supplied command(s) and exit immediately. .Pp -If the -.Dv ng_socket.ko -module is not installed in the kernel, -.Nm ngctl -will attempt to install it. +Nodes can be created, removed, joined together, etc. +ASCII formatted control messages can be sent to any node if that node +supports binary/ASCII control message conversion. +.Pp +In interactive mode, +.Nm +will display any control messages and data packets received by the socket node. +In the case of control messages, the message arguments are displayed in ASCII +form if the originating node supports conversion. .Pp The options are as follows: .Bl -tag -width indent @@ -92,6 +96,7 @@ debug Get/set debugging verbosity level help Show command summary or get help on a command list Show information about all nodes mkpeer Create and connect a new node to an existing node +msg Send an ASCII formatted message to a node name Assign a name to a node read Read and execute commands from a file rmhook Disconnect a node's hook @@ -102,8 +107,9 @@ types Show all installed node types quit Exit program .Ed .Pp +Some commands have aliases, e.g., ``ls'' is the same as ``list''. The ``help'' command displays the available -commands, their usage, and a brief description. +commands, their usage and aliases, and a brief description. .Sh EXIT VALUE The .Nm @@ -117,5 +123,5 @@ Archie Cobbs <archie@whistle.com> .Sh HISTORY The .Em netgraph -system was designed and first implemented at Whistle Communications, Inc. -in a version FreeBSD 2.2 customized for the Whistle InterJet. +system was designed and first implemented at Whistle Communications, Inc. in +a version FreeBSD 2.2 customized for the Whistle InterJet. diff --git a/usr.sbin/ngctl/ngctl.h b/usr.sbin/ngctl/ngctl.h index 817c6e8..8c7155b 100644 --- a/usr.sbin/ngctl/ngctl.h +++ b/usr.sbin/ngctl/ngctl.h @@ -58,12 +58,15 @@ #include <netgraph/ng_socket.h> #include <netgraph/ng_message.h> +#define MAX_CMD_ALIAS 8 + /* Command descriptors */ struct ngcmd { - int (*func)(int ac, char **av); - const char *cmd; - const char *desc; - const char *help; + int (*func)(int ac, char **av); /* command function */ + const char *cmd; /* command usage */ + const char *desc; /* description */ + const char *help; /* help text */ + const char *aliases[MAX_CMD_ALIAS]; /* command aliases */ }; /* Command return values */ @@ -78,6 +81,7 @@ extern const struct ngcmd debug_cmd; extern const struct ngcmd help_cmd; extern const struct ngcmd list_cmd; extern const struct ngcmd mkpeer_cmd; +extern const struct ngcmd msg_cmd; extern const struct ngcmd name_cmd; extern const struct ngcmd read_cmd; extern const struct ngcmd rmhook_cmd; diff --git a/usr.sbin/ngctl/rmhook.c b/usr.sbin/ngctl/rmhook.c index 551c06f..4af398e 100644 --- a/usr.sbin/ngctl/rmhook.c +++ b/usr.sbin/ngctl/rmhook.c @@ -47,7 +47,8 @@ const struct ngcmd rmhook_cmd = { "Disconnect hook \"hook\" of the node at \"path\"", "The rmhook command forces the node at \"path\" to break the link" " formed by its hook \"hook\", if connected." - " If \"path\" is omitted then \".\" is assumed." + " If \"path\" is omitted then \".\" is assumed.", + { "disconnect" } }; static int diff --git a/usr.sbin/ngctl/show.c b/usr.sbin/ngctl/show.c index 7cfdd48..da51361 100644 --- a/usr.sbin/ngctl/show.c +++ b/usr.sbin/ngctl/show.c @@ -49,7 +49,8 @@ const struct ngcmd show_cmd = { ShowCmd, "show [-n] <path>", "Show information about the node at <path>", - "If the -n flag is given, hooks are not listed." + "If the -n flag is given, hooks are not listed.", + { "inquire", "info" } }; static int diff --git a/usr.sbin/ngctl/shutdown.c b/usr.sbin/ngctl/shutdown.c index 769f122..d391ba8 100644 --- a/usr.sbin/ngctl/shutdown.c +++ b/usr.sbin/ngctl/shutdown.c @@ -45,7 +45,8 @@ const struct ngcmd shutdown_cmd = { ShutdownCmd, "shutdown <path>", "Shutdown the node at <path>", - NULL + NULL, + { "kill", "rmnode" } }; static int |