summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorarchie <archie@FreeBSD.org>1999-11-30 02:45:32 +0000
committerarchie <archie@FreeBSD.org>1999-11-30 02:45:32 +0000
commit81fceb37a95304786f88f6611289a27c7262d394 (patch)
tree321a35b746bb34b931d0691c03f9888db83ed68b
parent9716636318d4160418baceabe7ba05ce065692fc (diff)
downloadFreeBSD-src-81fceb37a95304786f88f6611289a27c7262d394.zip
FreeBSD-src-81fceb37a95304786f88f6611289a27c7262d394.tar.gz
Add two new generic control messages, NGM_ASCII2BINARY and
NGM_BINARY2ASCII, which convert control messages to ASCII and back. This allows control messages to be sent and received in ASCII form using ngctl(8), which makes ngctl a lot more useful. This also allows all the type-specific debugging code in libnetgraph to go away -- instead, we just ask the node itself to do the ASCII translation for us. Currently, all generic control messages are supported, as well as messages associated with the following node types: async, cisco, ksocket, and ppp. See /usr/share/examples/netgraph/ngctl for an example of using this. Also give ngctl(8) the ability to print out incoming data and control messages at any time. Eventually nghook(8) may be subsumed. Several other misc. bug fixes. Reviewed by: julian
-rw-r--r--lib/libnetgraph/Makefile3
-rw-r--r--lib/libnetgraph/debug.c377
-rw-r--r--lib/libnetgraph/internal.h4
-rw-r--r--lib/libnetgraph/msg.c109
-rw-r--r--lib/libnetgraph/netgraph.368
-rw-r--r--lib/libnetgraph/netgraph.h2
-rw-r--r--share/examples/netgraph/ngctl173
-rw-r--r--share/man/man4/netgraph.4109
-rw-r--r--share/man/man4/ng_cisco.41
-rw-r--r--share/man/man4/ng_pppoe.41
-rw-r--r--sys/conf/files1
-rw-r--r--sys/modules/netgraph/cisco/ng_cisco.41
-rw-r--r--sys/modules/netgraph/cisco/ng_cisco.81
-rw-r--r--sys/modules/netgraph/netgraph/Makefile2
-rw-r--r--sys/modules/netgraph/netgraph/netgraph.4109
-rw-r--r--sys/modules/netgraph/pppoe/ng_pppoe.41
-rw-r--r--sys/modules/netgraph/pppoe/ng_pppoe.81
-rw-r--r--sys/net/if_ethersubr.c3
-rw-r--r--sys/netgraph/netgraph.h26
-rw-r--r--sys/netgraph/ng_UI.c3
-rw-r--r--sys/netgraph/ng_UI.h2
-rw-r--r--sys/netgraph/ng_async.c55
-rw-r--r--sys/netgraph/ng_async.h28
-rw-r--r--sys/netgraph/ng_base.c319
-rw-r--r--sys/netgraph/ng_cisco.c62
-rw-r--r--sys/netgraph/ng_cisco.h39
-rw-r--r--sys/netgraph/ng_echo.c3
-rw-r--r--sys/netgraph/ng_ether.h10
-rw-r--r--sys/netgraph/ng_frame_relay.c5
-rw-r--r--sys/netgraph/ng_hole.c3
-rw-r--r--sys/netgraph/ng_iface.c14
-rw-r--r--sys/netgraph/ng_ksocket.c557
-rw-r--r--sys/netgraph/ng_ksocket.h22
-rw-r--r--sys/netgraph/ng_lmi.c3
-rw-r--r--sys/netgraph/ng_message.h159
-rw-r--r--sys/netgraph/ng_parse.c1604
-rw-r--r--sys/netgraph/ng_parse.h388
-rw-r--r--sys/netgraph/ng_ppp.c78
-rw-r--r--sys/netgraph/ng_ppp.h48
-rw-r--r--sys/netgraph/ng_pppoe.c3
-rw-r--r--sys/netgraph/ng_rfc1490.c3
-rw-r--r--sys/netgraph/ng_rfc1490.h2
-rw-r--r--sys/netgraph/ng_sample.c3
-rw-r--r--sys/netgraph/ng_socket.c3
-rw-r--r--sys/netgraph/ng_tee.c3
-rw-r--r--sys/netgraph/ng_tty.c21
-rw-r--r--sys/netgraph/ng_vjc.c3
-rw-r--r--usr.sbin/ngctl/Makefile2
-rw-r--r--usr.sbin/ngctl/connect.c3
-rw-r--r--usr.sbin/ngctl/list.c3
-rw-r--r--usr.sbin/ngctl/main.c219
-rw-r--r--usr.sbin/ngctl/msg.c88
-rw-r--r--usr.sbin/ngctl/ngctl.822
-rw-r--r--usr.sbin/ngctl/ngctl.h12
-rw-r--r--usr.sbin/ngctl/rmhook.c3
-rw-r--r--usr.sbin/ngctl/show.c3
-rw-r--r--usr.sbin/ngctl/shutdown.c3
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
OpenPOWER on IntegriCloud