summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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