socsvn commit: r287735 - in soc2015/roam: . ayiya_resp ng_ayiya

roam at FreeBSD.org roam at FreeBSD.org
Mon Jun 29 19:03:16 UTC 2015


Author: roam
Date: Mon Jun 29 19:03:13 2015
New Revision: 287735
URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=287735

Log:
  Add a userland responder to non-forward packets.
  
  ng_ayiya:
  - send all packets that do not contain forwarded IPv6 data
    down the "control" hook if it's connected.
  - break out the signing part of ayiya_build() into ayiya_sign()
  - move the m_adj() call to remove the AYIYA header from
    ayiya_verify() to the consumer, the rcvdata function, since we
    do need the full AYIYA packet to forward down the control hook
  - add constants for the AYIYA opcodes and use them
  
  ayiya_resp:
  - write a proof-of-concept C program that uses libnetgraph to
    connect to the "control" hook, wait for remote packets,
    interpret them, and send packets of its own
  
  ObQuote:	"Do you, do you, do you, do you wanna dance?"

Added:
  soc2015/roam/Makefile
  soc2015/roam/ayiya_resp/
  soc2015/roam/ayiya_resp/Makefile
  soc2015/roam/ayiya_resp/main.c
Modified:
  soc2015/roam/ng_ayiya/ng_ayiya.4
  soc2015/roam/ng_ayiya/ng_ayiya.c
  soc2015/roam/ng_ayiya/ng_ayiya.h

Added: soc2015/roam/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ soc2015/roam/Makefile	Mon Jun 29 19:03:13 2015	(r287735)
@@ -0,0 +1,42 @@
+# Copyright (c) 2015  Peter Pentchev
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON 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 ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+SUBDIR=		ayiya_resp ng_ayiya
+
+.include <bsd.subdir.mk>
+
+down:
+		cd ${.CURDIR}/ng_ayiya && ${MAKE} down
+
+up:
+		cd ${.CURDIR}/ng_ayiya && ${MAKE} up
+
+tic:
+		cd ${.CURDIR}/ng_ayiya && ${MAKE} tic
+
+clitest:	tic
+		cd ${.CURDIR}/ayiya_resp && ${MAKE} clitest
+
+clitest-quiet:	tic
+		cd ${.CURDIR}/ayiya_resp && ${MAKE} clitest-quiet

Added: soc2015/roam/ayiya_resp/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ soc2015/roam/ayiya_resp/Makefile	Mon Jun 29 19:03:13 2015	(r287735)
@@ -0,0 +1,42 @@
+# Copyright (c) 2015  Peter Pentchev
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON 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 ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+PROG=	ayiya_resp
+SRCS=	main.c
+NO_MAN=	yes
+WARNS?=	9
+DPADD=	${LIBNETGRAPH}
+LDADD=	-lnetgraph
+
+CFLAGS+=	-I${.CURDIR}/..
+
+.include <bsd.prog.mk>
+
+clitest:
+		sudo ${.OBJDIR}/${PROG} -v config
+		sudo ${.OBJDIR}/${PROG} -v loop
+
+clitest-quiet:
+		sudo ${.OBJDIR}/${PROG} -v config
+		sudo ${.OBJDIR}/${PROG} -q loop

Added: soc2015/roam/ayiya_resp/main.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ soc2015/roam/ayiya_resp/main.c	Mon Jun 29 19:03:13 2015	(r287735)
@@ -0,0 +1,433 @@
+/*-
+ * Copyright (c) 2015  Peter Pentchev
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON 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 ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <netgraph.h>
+#include <ng_ayiya/ng_ayiya.h>
+
+#include <netinet/in.h>
+
+#define A_SEL_TIMEOUT	0x0000
+#define A_SEL_RD_DATA	0x0001
+#define A_SEL_WR_DATA	0x0002
+#define A_SEL_EXC	0x0004
+
+#define MOTD		\
+	"1435504634\n" \
+	"Complaint In The System\n" \
+	"Welcome to the system, here's the situation;\n" \
+	"It's a bit confusing, welcome to the maze!\n"
+
+static int		quiet;
+static int		verbose;
+
+static void		usage(int _ferr);
+static void		version(void);
+static void		debug(const char *fmt, ...) __printflike(1, 2);
+
+static int		cmd_config(const char *cmd, char * const args[], unsigned argc);
+static int		cmd_loop(const char *cmd, char * const args[], unsigned argc);
+
+static void		ayiya_connect(int *, int *);
+static void		ayiya_get_config(int, bool);
+static unsigned		ayiya_select(int, bool, bool);
+static void		send_packet(int, ayiya_opcode, const char *, size_t);
+static void		send_empty_packet(int, ayiya_opcode);
+
+static struct {
+	const char *name;
+	int (*func)(const char *cmd, char * const args[], unsigned argc);
+} commands[] = {
+	{ "config", cmd_config },
+	{ "loop", cmd_loop },
+};
+#define COMMANDS	(sizeof(commands) / sizeof(commands[0]))
+
+#define AYIYA_ND	"sc_ayiya"
+
+static union {
+	struct ng_mesg msg;
+	char buf[32768];
+} ng_msgbuf;
+static char ng_msgpath[NG_PATHSIZ];
+
+int
+main(int argc, char * const argv[])
+{
+	int ch, hflag, Vflag;
+
+	hflag = Vflag = 0;
+	while (ch = getopt(argc, argv, "hqVv"), ch != -1)
+		switch (ch) {
+			case 'h':
+				hflag = 1;
+				break;
+
+			case 'q':
+				quiet = 1;
+				break;
+
+			case 'V':
+				Vflag = 1;
+				break;
+
+			case 'v':
+				verbose++;
+				break;
+
+			default:
+				usage(1);
+				/* NOTREACHED */
+		}
+	if (Vflag)
+		version();
+	if (hflag)
+		usage(0);
+	if (Vflag || hflag)
+		return (0);
+
+	argc -= optind;
+	argv += optind;
+	if (argc < 1) {
+		warnx("No command specified");
+		usage(1);
+	}
+	const size_t len = strlen(argv[0]);
+	unsigned idx = COMMANDS;
+	for (unsigned i = 0; i < COMMANDS; i++) {
+		if (strcmp(argv[0], commands[i].name) == 0) {
+			idx = i;
+			break;
+		} else if (strncmp(argv[0], commands[i].name, len) == 0) {
+			if (idx != COMMANDS) {
+				warnx("Ambiguous command '%s'", argv[0]);
+				usage(1);
+			}
+			idx = i;
+		}
+	}
+	if (idx == COMMANDS) {
+		warnx("Unrecognized command '%s'", argv[0]);
+		usage(1);
+	}
+	return (commands[idx].func)(argv[0], argv + 1, argc - 1);
+}
+
+void
+usage(const int _ferr)
+{
+	const char * const s =
+	    "Usage:\tayiya_resp [-v] config\n"
+	    "\tayiya_resp [-qv] loop\n"
+	    "\tayiya_resp -V | -h\n"
+	    "\n"
+	    "\t-h\tdisplay program usage information and exit\n"
+	    "\t-n\tspecify the number of lines to checksum for each file\n"
+	    "\t-q\tquiet mode; only respond, do not originate AYIYA packets\n"
+	    "\t-V\tdisplay program version information and exit\n"
+	    "\t-v\tverbose operation; display diagnostic output\n";
+
+	fprintf(_ferr? stderr: stdout, "%s", s);
+	if (_ferr)
+		exit(1);
+}
+
+void
+version(void)
+{
+	puts("ayiya_resp 0.1.0.dev178");
+}
+
+void
+debug(const char * const fmt, ...)
+{
+	va_list v;
+
+	va_start(v, fmt);
+	if (verbose)
+		vfprintf(stderr, fmt, v);
+	va_end(v);
+}
+
+int
+cmd_config(const char * const cmd __unused, char * const args[] __unused,
+		const unsigned argc)
+{
+	if (argc != 0)
+		errx(1, "The 'config' command expects no arguments");
+
+	int cs, ds;
+	ayiya_connect(&cs, &ds);
+	ayiya_get_config(cs, true);
+	return (0);
+}
+
+int
+cmd_loop(const char * const cmd __unused, char * const args[] __unused,
+		const unsigned argc)
+{
+	if (argc != 0)
+		errx(1, "The 'loop' command expects no arguments");
+
+	int cs, ds;
+	ayiya_connect(&cs, &ds);
+	ayiya_get_config(cs, false);
+
+	srandomdev();
+	time_t next_heartbeat = time(NULL);
+	time_t next_motd = next_heartbeat + 7 + (random() % 7);
+	for (;;) {
+		const time_t now = time(NULL);
+		debug("Loop: now %ld heartbeat %ld motd %ld\n",
+		    now, next_heartbeat, next_motd);
+		const bool do_heartbeat = !quiet && now >= next_heartbeat;
+		const bool do_motd = !quiet && now >= next_motd;
+		debug("- select, heartbeat %s, motd %s\n",
+		    do_heartbeat? "true": "false",
+		    do_motd? "true": "false");
+		const unsigned sel = ayiya_select(ds, true,
+		    do_heartbeat || do_motd);
+		if (sel == A_SEL_TIMEOUT) {
+			debug("- we got nothin'\n");
+			continue;
+		}
+		const time_t rcvat = time(NULL);
+
+		if (sel & A_SEL_WR_DATA) {
+			if (do_heartbeat) {
+				debug("Sending a heartbeat\n");
+				send_empty_packet(ds, AYIYA_OP_HEARTBEAT);
+				next_heartbeat = now + 10;
+			}
+
+			if (do_motd) {
+				debug("Sending a MOTD\n");
+				send_packet(ds, AYIYA_OP_MOTD,
+				    MOTD, sizeof(MOTD) - 1);
+				next_motd = now + 7 + (random() % 7);
+			}
+		}
+
+		if (sel & A_SEL_RD_DATA) {
+			const int len = NgRecvData(ds, ng_msgbuf.buf,
+			    sizeof(ng_msgbuf.buf), ng_msgpath);
+			if (len < 0)
+				err(1, "Receiving data");
+			if (len < 1) {
+				warnx("Our Netgraph socket was closed");
+				return (0);
+			}
+			debug("Got %d bytes of data:\n", len);
+			for (int d = 0; d < len; d++)
+				debug("%02hhX%c", ng_msgbuf.buf[d],
+				    d % 16 == 15? '\n': ' ');
+			if (len % 16 != 0)
+				debug("\n");
+
+			const struct ng_ayiya_header * const hdr =
+			    (const struct ng_ayiya_header *)ng_msgbuf.buf;
+			const size_t hlen = ayiya_offset_data(hdr);
+			const char * const data = &ng_msgbuf.buf[hlen];
+			const size_t dlen = len - hlen;
+			switch (hdr->opcode) {
+				case AYIYA_OP_HEARTBEAT:
+					printf("%jd Heartbeat received\n",
+					    (intmax_t)rcvat);
+					break;
+
+				case AYIYA_OP_MOTD:
+					printf("%jd Message of the day:\n",
+					    (intmax_t)rcvat);
+					for (size_t d = 0; d < dlen; d++) {
+						const char ch = data[d];
+						if (ch >= 32 || ch == '\n' || ch == '\t')
+							putchar(ch);
+						else
+							printf("%%%02hhX", ch);
+					}
+					putchar('\n');
+					break;
+
+				default:
+					debug("FIXME: handle opcode %d\n", hdr->opcode);
+					break;
+			}
+		}
+	}
+	/* NOTREACHED */
+}
+
+void
+ayiya_get_config(const int cs, const bool twice)
+{
+	if (NgSendMsg(cs, "c", NGM_GENERIC_COOKIE, NGM_TEXT_CONFIG, NULL, 0) == -1)
+		err(1, "Could not query the configuration of the %s AYIYA node",
+		    AYIYA_ND);
+	debug("Waiting for the ng_ayiya 'config' reply\n");
+
+	unsigned sel = ayiya_select(cs, true, false);
+	if (sel == A_SEL_TIMEOUT)
+		errx(1, "Did not receive a reply from the %s AYIYA node in two seconds", AYIYA_ND);
+	else if (sel & A_SEL_EXC)
+		errx(1, "Something bad happened on the control fd %d", cs);
+	else if (!(sel & A_SEL_RD_DATA))
+		errx(1, "Something very weird happened, no data to read on the control fd %d", cs);
+
+	struct ng_mesg * const msg = &ng_msgbuf.msg;
+	if (NgRecvMsg(cs, msg, sizeof(ng_msgbuf.buf), ng_msgpath) == -1)
+		err(1, "Could not get the configuration of the %s AYIYA node",
+		    AYIYA_ND);
+	debug("Got a 'config' reply from '%s': version %u cmd %X arglen %u\n",
+	    ng_msgpath, msg->header.version, msg->header.cmd, msg->header.arglen);
+	char * endptr = &msg->data[msg->header.arglen];
+	if (endptr >= &ng_msgbuf.buf[sizeof(ng_msgbuf.buf)])
+		errx(1, "The 'config' response did not fit in 32K...");
+	*endptr = '\0';
+	debug("The data itself:\n%s\n", msg->data);
+
+	if (!twice)
+		return;
+	debug("OK, let's wait a second time\n");
+	sel = ayiya_select(cs, true, false);
+	if (sel != A_SEL_TIMEOUT)
+		errx(1, "Something really weird happened, ayiya_select() "
+		    "returned %d for the control fd", sel);
+	debug("No data as expected\n");
+}
+
+void
+ayiya_connect(int * const cs, int * const ds)
+{
+	assert(cs != NULL && ds != NULL);
+
+	if (NgMkSockNode("ayiya_resp_ctl", cs, ds) == -1)
+		err(1, "Could not create the control socket node");
+
+	const struct ngm_connect c = {
+		.path = AYIYA_ND ":",
+		.ourhook = "c",
+		.peerhook = "control",
+	};
+	if (NgSendMsg(*cs, ".", NGM_GENERIC_COOKIE, NGM_CONNECT,
+	    &c, sizeof(c)) == -1)
+		err(1, "Could not connect to the %s AYIYA node", AYIYA_ND);
+
+	int flags = fcntl(*cs, F_GETFL);
+	debug("- cs flags: %X (non-blocking: %s, append-only: %s, "
+	    "direct I/O: %s, async: %s)\n", flags,
+	    flags & O_NONBLOCK? "true": "false",
+	    flags & O_APPEND? "true": "false",
+	    flags & O_DIRECT? "true": "false",
+	    flags & O_ASYNC? "true": "false");
+	if (!(flags & O_NONBLOCK) &&
+	    fcntl(*cs, F_SETFL, flags | O_NONBLOCK) == -1)
+		err(1, "Could not set the control socket to non-blocking mode");
+	flags = fcntl(*ds, F_GETFL);
+	debug("- ds flags: %X (non-blocking: %s, append-only: %s, "
+	    "direct I/O: %s, async: %s)\n", flags,
+	    flags & O_NONBLOCK? "true": "false",
+	    flags & O_APPEND? "true": "false",
+	    flags & O_DIRECT? "true": "false",
+	    flags & O_ASYNC? "true": "false");
+	if (!(flags & O_NONBLOCK) &&
+	    fcntl(*ds, F_SETFL, flags | O_NONBLOCK) == -1)
+		err(1, "Could not set the ds socket to non-blocking mode");
+}
+
+unsigned
+ayiya_select(const int fd, const bool do_read, const bool do_write)
+{
+	fd_set rd, wr, exc;
+	FD_ZERO(&rd);
+	FD_ZERO(&wr);
+	FD_ZERO(&exc);
+	if (do_read)
+		FD_SET(fd, &rd);
+	if (do_write)
+		FD_SET(fd, &wr);
+	FD_SET(fd, &exc);
+
+	struct timeval to = { .tv_sec = 2, .tv_usec = 0 };
+	const int res = select(fd + 1, &rd, &wr, &exc, &to);
+	if (res == 0)
+		return (A_SEL_TIMEOUT);
+	else if (res != 1)
+		errx(1, "Something really weird happened, "
+		    "select() on fd %d returned %d", fd, res);
+
+	unsigned val = 0;
+	if (FD_ISSET(fd, &rd))
+		val |= A_SEL_RD_DATA;
+	if (FD_ISSET(fd, &wr))
+		val |= A_SEL_WR_DATA;
+	if (FD_ISSET(fd, &exc))
+		val |= A_SEL_EXC;
+	return (val);
+}
+
+void
+send_packet(const int ds, const ayiya_opcode op, const char * const data,
+	    const size_t len)
+{
+	struct ng_ayiya_packet * const pkt =
+	    (struct ng_ayiya_packet *)&ng_msgbuf.buf;
+	if (sizeof(*pkt) + len > sizeof(ng_msgbuf.buf))
+		errx(1, "Internal error: send_packet() length %zu too big, "
+		    "max %zu", len, sizeof(ng_msgbuf.buf) - sizeof(*pkt));
+	pkt->hdr.idlen = 4;
+	pkt->hdr.idtype = 1; // Integer
+	pkt->hdr.siglen = sizeof(pkt->signature) / 4;
+	pkt->hdr.hshmeth = 2; // SHA1
+	pkt->hdr.autmeth = 1; // shared secret
+	pkt->hdr.opcode = op;
+	pkt->hdr.nextheader = IPPROTO_NONE;
+	pkt->hdr.epochtime = time(NULL);
+
+	if (len > 0)
+		bcopy(data, pkt + 1, len);
+
+	if (NgSendData(ds, "c", ng_msgbuf.buf, sizeof(*pkt) + len) == -1)
+		err(1, "Could not send data on the data socket");
+}
+
+void
+send_empty_packet(const int ds, const ayiya_opcode op)
+{
+	send_packet(ds, op, NULL, 0);
+}

Modified: soc2015/roam/ng_ayiya/ng_ayiya.4
==============================================================================
--- soc2015/roam/ng_ayiya/ng_ayiya.4	Mon Jun 29 18:56:53 2015	(r287734)
+++ soc2015/roam/ng_ayiya/ng_ayiya.4	Mon Jun 29 19:03:13 2015	(r287735)
@@ -130,6 +130,23 @@
 .Dv NGM_AYIYA_CONFIGURE
 control message; when the message has been processed, the tunnel
 should be up and running.
+.It *
+Start an
+.Tn AYIYA
+responder program that will connect to the
+.Nm ayiya
+node's
+.Va control
+hook, listen for any incoming heartbeat, echo, "message of the day",
+or query packets, and process them as necessary.
+A sample
+.Tn AYIYA
+responder using the
+.Nm ayiya
+Netgraph node is available in the
+.Nm
+source directory as
+.Xr ayiya_resp 8 .
 .El
 .Sh HOOKS
 The

Modified: soc2015/roam/ng_ayiya/ng_ayiya.c
==============================================================================
--- soc2015/roam/ng_ayiya/ng_ayiya.c	Mon Jun 29 18:56:53 2015	(r287734)
+++ soc2015/roam/ng_ayiya/ng_ayiya.c	Mon Jun 29 19:03:13 2015	(r287735)
@@ -567,20 +567,31 @@
 	return (AYIYA_HOOK_LAST);
 }
 
+static int	ayiya_sign(struct mbuf **mb, priv_p priv);
+
 static int
-ayiya_build(struct mbuf **mb, const u_char opcode, const u_char nextheader,
+ayiya_build(struct mbuf ** const mb, const u_char opcode, const u_char nextheader,
 		const priv_p priv)
 {
 	struct mbuf *m = *mb;
 
-	if (m == NULL)
+	if (m == NULL) {
 		m = ayiya_m_getm(sizeof(struct ng_ayiya_packet), M_NOWAIT);
-	else
+		*mb = m;
+	} else {
 		M_PREPEND(m, sizeof(struct ng_ayiya_packet), M_NOWAIT);
-	if (m->m_next)
-		m = m_defrag(m, M_NOWAIT);
-	if (m == NULL)
-		return (ENOMEM);
+		*mb = m;
+	}
+	if (m->m_next) {
+		struct mbuf * const m2 = m_defrag(m, M_NOWAIT);
+		if (m2 == NULL) {
+			m_freem(m);
+			*mb = NULL;
+			return (ENOMEM);
+		}
+		m = m2;
+		*mb = m;
+	}
 	struct ng_ayiya_header * const hdr =
 	    (struct ng_ayiya_header *)m->m_data;
 	struct ng_ayiya_packet * const pkt =
@@ -595,6 +606,27 @@
 	hdr->nextheader = nextheader;
 	hdr->epochtime = htonl(time_second);
 
+	return (ayiya_sign(mb, priv));
+}
+
+static int
+ayiya_sign(struct mbuf ** const mb, const priv_p priv)
+{
+	struct mbuf *m = *mb;
+
+	if (m->m_next) {
+		struct mbuf * const m2 = m_defrag(m, M_NOWAIT);
+		if (m2 == NULL) {
+			m_freem(m);
+			*mb = NULL;
+			return (ENOMEM);
+		}
+		m = m2;
+		*mb = m;
+	}
+
+	struct ng_ayiya_packet * const pkt =
+	    (struct ng_ayiya_packet *)m->m_data;
 	bcopy(priv->identity, pkt->identity, sizeof(pkt->identity));
 
 	/* Start with the secret hash... */
@@ -610,22 +642,25 @@
 	bcopy(hash, pkt->signature, sizeof(pkt->signature));
 
 	/* And I think we're done. */
-	*mb = m;
 	return (0);
 }
 
 static int
 ayiya_verify(struct mbuf **mb, u_char * const opcode, u_char * const nextheader,
-		const priv_p priv)
+		int32_t * const ofs, const priv_p priv)
 {
 	struct mbuf *m = *mb;
 
 	if (m->m_next) {
-		m = m_defrag(m, M_NOWAIT);
+		struct mbuf * const m2 = m_defrag(m, M_NOWAIT);
+		if (m2 == NULL) {
+			m_freem(m);
+			*mb = NULL;
+			return (ENOMEM);
+		}
+		m = m2;
 		*mb = m;
 	}
-	if (m == NULL)
-		return (ENOMEM);
 	const int32_t len = m->m_len;
 	struct ng_ayiya_header * const hdr =
 	    (struct ng_ayiya_header *)m->m_data;
@@ -704,8 +739,7 @@
 		return (EOPNOTSUPP);
 	}
 
-	m_adj(m, ofs_data);
-	*mb = m;
+	*ofs = ofs_data;
 	*opcode = hdr->opcode;
 	*nextheader = hdr->nextheader;
 	return (0);
@@ -746,7 +780,8 @@
 			}
 
 			/* Prepare a packet for forwarding */
-			int error = ayiya_build(&m, 1, IPPROTO_IPV6, priv);
+			int error = ayiya_build(&m, AYIYA_OP_FORWARD,
+			    IPPROTO_IPV6, priv);
 			if (error != 0)
 				return (error);
 			
@@ -758,7 +793,8 @@
 			{
 			u_char opcode;
 			u_char nextheader;
-			int error = ayiya_verify(&m, &opcode, &nextheader, priv);
+			int32_t ofs;
+			int error = ayiya_verify(&m, &opcode, &nextheader, &ofs, priv);
 			if (error != 0)
 			{
 				m_freem(m);
@@ -767,24 +803,63 @@
 
 			switch (opcode)
 			{
-			case 1:
+			case AYIYA_OP_FORWARD:
+			case AYIYA_OP_ECHO_AND_FORWARD:
+				{
+				const int echo = opcode == AYIYA_OP_ECHO_AND_FORWARD;
+				struct mbuf *mf;
+				if (!echo) {
+					m_adj(m, ofs);
+					mf = m;
+				} else {
+					mf = ayiya_m_getm(m->m_len - ofs, M_NOWAIT);
+					bcopy(m->m_data + ofs, mf->m_data, mf->m_len);
+				}
+
 				/* Forward */
 				if (nextheader != IPPROTO_IPV6)
 				{
+					if (echo)
+						m_freem(mf);
 					m_freem(m);
 					return (0);
 				}
 				int error;
-				NG_SEND_DATA_ONLY(error, priv->hooks[AYIYA_HOOK_INET6], m);
-				/* Ignore the error, nothing to do, really. */
-				break;
+				NG_SEND_DATA_ONLY(error, priv->hooks[AYIYA_HOOK_INET6], mf);
+				if (!echo)
+					return (0);
+				}
 
 			default:
-				break;
+				{
+				if (priv->hooks[AYIYA_HOOK_CONTROL] == NULL) {
+					m_freem(m);
+					return (0);
+				}
+				int error;
+				NG_SEND_DATA_ONLY(error, priv->hooks[AYIYA_HOOK_CONTROL], m);
+				return (0);
+				}
+			}
+			/* NOTREACHED */
 			}
 
-			m_freem(m);
-			return (0);
+		case AYIYA_HOOK_CONTROL:
+			{
+			if (!priv->configured) {
+				m_freem(m);
+				return (ENOTCONN);
+			}
+
+			int error = ayiya_sign(&m, priv);
+			if (error != 0)
+			{
+				m_freem(m);
+				return (0);
+			}
+
+			NG_SEND_DATA_ONLY(error, priv->hooks[AYIYA_HOOK_AYIYA], m);
+			return (error);
 			}
 
 		default:

Modified: soc2015/roam/ng_ayiya/ng_ayiya.h
==============================================================================
--- soc2015/roam/ng_ayiya/ng_ayiya.h	Mon Jun 29 18:56:53 2015	(r287734)
+++ soc2015/roam/ng_ayiya/ng_ayiya.h	Mon Jun 29 19:03:13 2015	(r287735)
@@ -40,6 +40,17 @@
 	NGM_AYIYA_GET_MOTD,
 };
 
+typedef enum {
+	AYIYA_OP_HEARTBEAT = 0,
+	AYIYA_OP_FORWARD = 1,
+	AYIYA_OP_ECHO = 2,
+	AYIYA_OP_ECHO_AND_FORWARD = 3,
+	AYIYA_OP_ECHO_RESP = 4,
+	AYIYA_OP_MOTD = 5,
+	AYIYA_OP_QUERY = 6,
+	AYIYA_OP_QUERY_RESP = 7,
+} ayiya_opcode;
+
 struct ng_ayiya_header {
 #if _BYTE_ORDER == _BIG_ENDIAN
 	unsigned	idlen:4,
@@ -69,4 +80,24 @@
 	u_char			signature[20];
 } __packed;
 
+static inline size_t ayiya_offset_id(const struct ng_ayiya_header * const hdr) {
+	return (sizeof(*hdr));
+}
+
+static inline size_t ayiya_length_id(const struct ng_ayiya_header * const hdr) {
+	return (1 << hdr->idlen);
+}
+
+static inline size_t ayiya_offset_sig(const struct ng_ayiya_header * const hdr) {
+	return (ayiya_offset_id(hdr) + ayiya_length_id(hdr));
+}
+
+static inline size_t ayiya_length_sig(const struct ng_ayiya_header * const hdr) {
+	return (4 * hdr->siglen);
+}
+
+static inline size_t ayiya_offset_data(const struct ng_ayiya_header * const hdr) {
+	return (ayiya_offset_sig(hdr) + ayiya_length_sig(hdr));
+}
+
 #endif


More information about the svn-soc-all mailing list