socsvn commit: r289926 - in soc2015/roam: . ayiya_listen ayiya_resp ng_ayiya
roam at FreeBSD.org
roam at FreeBSD.org
Wed Aug 19 15:56:19 UTC 2015
Author: roam
Date: Wed Aug 19 15:56:16 2015
New Revision: 289926
URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=289926
Log:
Add ayiya_listen for server-side ng_ayiya use.
Add a daemon that listens for UDP packets coming in for port 5072,
figures out which of the pre-configured tunnels the packet is for,
then sets up the ng_ayiya/ng_ksocket/ng_iface set and starts
an instance of ayiya_resp to inject the packet and then handle any
incoming AYIYA queries.
ayiya_resp:
- add NO_WCAST_ALIGN to the Makefile; I do believe that most of
the casts should be safe, even though the compiler does not
realize that
- add the -i inputfd command-line option to specify a fd for
packets that have arrived before the ng_ayiya node was set up
- inject these packets using ng_ayiya's new "inject from AYIYA"
control message
ng_ayiya:
- add the "inject from AYIYA" control message with a data packet
that must be processed as if it was received from the "ayiya"
hook
Added:
soc2015/roam/ayiya_listen/
soc2015/roam/ayiya_listen/Makefile
- copied, changed from r289925, soc2015/roam/ayiya_resp/Makefile
soc2015/roam/ayiya_listen/main.c
Modified:
soc2015/roam/Makefile
soc2015/roam/ayiya_resp/Makefile
soc2015/roam/ayiya_resp/main.c
soc2015/roam/ng_ayiya/ng_ayiya.c
soc2015/roam/ng_ayiya/ng_ayiya.h
Modified: soc2015/roam/Makefile
==============================================================================
--- soc2015/roam/Makefile Wed Aug 19 15:56:09 2015 (r289925)
+++ soc2015/roam/Makefile Wed Aug 19 15:56:16 2015 (r289926)
@@ -22,12 +22,12 @@
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
-SUBDIR= ayiya_resp ng_ayiya
+SUBDIR= ayiya_listen ayiya_resp ng_ayiya
.include <bsd.subdir.mk>
NAME= ng_ayiya
-VERSION= 0.1.0.dev210
+VERSION= 0.1.0.dev225
DISTNAME= ${NAME}-${VERSION}
DISTDIR= ${DISTNAME}
Copied and modified: soc2015/roam/ayiya_listen/Makefile (from r289925, soc2015/roam/ayiya_resp/Makefile)
==============================================================================
--- soc2015/roam/ayiya_resp/Makefile Wed Aug 19 15:56:09 2015 (r289925, copy source)
+++ soc2015/roam/ayiya_listen/Makefile Wed Aug 19 15:56:16 2015 (r289926)
@@ -22,12 +22,13 @@
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
-PROG= ayiya_resp
+PROG= ayiya_listen
SRCS= main.c
NO_MAN= yes
WARNS?= 9
-DPADD= ${LIBNETGRAPH}
-LDADD= -lnetgraph
+NO_WCAST_ALIGN= 1
+DPADD= ${LIBMD} ${LIBNETGRAPH}
+LDADD= -lmd -lnetgraph
CFLAGS+= -I${.CURDIR}/..
Added: soc2015/roam/ayiya_listen/main.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ soc2015/roam/ayiya_listen/main.c Wed Aug 19 15:56:16 2015 (r289926)
@@ -0,0 +1,880 @@
+/*-
+ * 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.
+ */
+
+#define _WITH_GETLINE
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <regex.h>
+#include <sha.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <netgraph.h>
+#include <netgraph/ng_iface.h>
+#include <netgraph/ng_ksocket.h>
+#include <ng_ayiya/ng_ayiya.h>
+
+#define MAX_TUNNELS 4
+#define MAX_RESPAWNS 3
+#define INET_ADDRPORTSTRLEN (INET_ADDRSTRLEN + 1 + 5)
+
+struct tunnel {
+ char id[16 + 1];
+ struct in6_addr local_addr, remote_addr;
+ char password[20];
+ unsigned prefix_len;
+ unsigned mtu;
+
+ char remote_outer[INET_ADDRPORTSTRLEN];
+ int comm[2];
+ pid_t pid;
+ ng_ID_t node_id;
+ unsigned respawned;
+};
+
+static struct tunnel tunnels[MAX_TUNNELS];
+static size_t tunnels_count;
+
+static struct sockaddr_in local;
+static int ng_cs, ng_ds;
+static const char *resppath;
+
+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 void parse_tunnels(const char *);
+static int setup_listener(const char *);
+static void accept_connection(int);
+static void handle_tunnel(struct tunnel *, const char *,
+ const struct sockaddr_in *, const char *, size_t);
+static void stop_tunnel(struct tunnel *, int);
+static void start_tunnel(struct tunnel *,
+ const struct sockaddr_in *, const char *);
+static void check_wait_result(pid_t res, int stat, pid_t expected,
+ const char *progname);
+static void spawn_child(const struct tunnel *) __dead2;
+static int inet_addrport(const struct sockaddr_in *,
+ char *, size_t);
+static void get_ayiya_node(char *, size_t, const struct tunnel *,
+ const char *);
+static void sigchld_handler(int);
+
+static void report_regerror(int, const regex_t * restrict);
+
+int
+main(int argc, char * const argv[])
+{
+ int ch, hflag, Vflag;
+ const char *addr, *tunnelsfile;
+
+ hflag = Vflag = 0;
+ addr = tunnelsfile = NULL;
+ while (ch = getopt(argc, argv, "a:hqr:t:Vv"), ch != -1)
+ switch (ch) {
+ case 'a':
+ if (addr != NULL)
+ errx(1, "Duplicate address specified");
+ addr = optarg;
+ break;
+
+ case 'h':
+ hflag = 1;
+ break;
+
+ case 'q':
+ quiet = 1;
+ break;
+
+ case 'r':
+ resppath = optarg;
+ break;
+
+ case 't':
+ tunnelsfile = optarg;
+ 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);
+
+ if (addr == NULL) {
+ warnx("No address (-a) specified");
+ usage(1);
+ } else if (tunnelsfile == NULL) {
+ warnx("No tunnels file (-t) specified");
+ usage(1);
+ } else if (resppath == NULL) {
+ warnx("No ayiya_resp path (-r) specified");
+ usage(1);
+ }
+
+ argc -= optind;
+ argv += optind;
+ if (argc > 0) {
+ warnx("No positional arguments expected");
+ usage(1);
+ }
+
+ parse_tunnels(tunnelsfile);
+
+ signal(SIGPIPE, SIG_IGN);
+ const struct sigaction sa = {
+ .sa_handler = sigchld_handler,
+ .sa_flags = SA_NOCLDSTOP | SA_RESETHAND,
+ };
+ sigaction(SIGCHLD, &sa, NULL);
+
+ const int fd = setup_listener(addr);
+ while (1)
+ accept_connection(fd);
+ /* NOTREACHED */
+}
+
+void
+usage(const int _ferr)
+{
+ const char * const s =
+ "Usage:\tayiya_listen [-v] -a address -r resppath -t tunnelsfile config\n"
+ "\tayiya_listen [-qv] loop\n"
+ "\tayiya_listen -V | -h\n"
+ "\n"
+ "\t-a\tspecify the IPv4 address to listen on\n"
+ "\t-h\tdisplay program usage information and exit\n"
+ "\t-n\tspecify the name of the AYIYA node to accept the connection\n"
+ "\t-q\tquiet mode; only respond, do not originate AYIYA packets\n"
+ "\t-r\tspecify the path to the ayiya_resp executable\n"
+ "\t-t\tspecify the path to the tunnel information file\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.dev225");
+}
+
+void
+debug(const char * const fmt, ...)
+{
+ va_list v;
+
+ va_start(v, fmt);
+ if (verbose)
+ vfprintf(stderr, fmt, v);
+ va_end(v);
+}
+
+int
+setup_listener(const char * const addr)
+{
+ assert(addr != NULL);
+
+ if (NgMkSockNode("ayiya_listen", &ng_cs, &ng_ds) == -1)
+ err(1, "Could not create the Netgraph socket node");
+
+ struct addrinfo hints;
+ bzero(&hints, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV | AI_PASSIVE;
+ struct addrinfo *res;
+ const int ares = getaddrinfo(addr, "5072", &hints, &res);
+ if (ares != 0)
+ errx(1, "Could not parse the '%s' address",
+ gai_strerror(ares));
+ if (res == NULL)
+ errx(1, "Invalid address '%s' specified", addr);
+
+ for (; res != NULL; res = res->ai_next) {
+ const int fd = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol);
+ if (fd == -1) {
+ warn("Could not create a %d/%d/%d socket",
+ res->ai_family, res->ai_socktype,
+ res->ai_protocol);
+ continue;
+ }
+ const int optval = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT,
+ &optval, sizeof(optval)) == -1) {
+ warn("Could not set the 'reuse port' socket option");
+ close(fd);
+ continue;
+ }
+ if (bind(fd, res->ai_addr, res->ai_addrlen) == -1) {
+ warn("Could not bind to %s", addr);
+ close(fd);
+ continue;
+ }
+
+ debug("Looks like we've set up the socket\n");
+ bcopy(res->ai_addr, &local, sizeof(local));
+ return fd;
+ }
+ errx(1, "Could not set up a listener on %s", addr);
+}
+
+void
+accept_connection(const int fd)
+{
+ static char buf[32768];
+ struct sockaddr_in sin;
+ socklen_t sin_len;
+retry:
+ sin_len = sizeof(sin);
+ ssize_t len = recvfrom(fd, buf, sizeof(buf), 0,
+ (struct sockaddr *)&sin, &sin_len);
+ if (len == -1) {
+ if (errno == EINTR) {
+ debug("Hm, was a SIGCHLD received?\n");
+ while (1) {
+ int stat;
+ const pid_t pid = waitpid(-1, &stat, WNOHANG);
+ if (pid == -1) {
+ if (errno != ECHILD)
+ err(1, "waitpid() failed");
+ /**
+ * recvfrom() was not interrupted by
+ * a SIGCHLD? Ah well, never mind :)
+ */
+ break;
+ } else if (pid == 0) {
+ break;
+ }
+ for (size_t idx = 0; idx < tunnels_count; idx++) {
+ struct tunnel * const t =
+ tunnels + idx;
+ if (t->pid == pid) {
+ check_wait_result(pid, stat,
+ t->pid, "AYIYA responder");
+ stop_tunnel(t, 1);
+ break;
+ }
+ }
+ }
+ goto retry;
+ }
+ err(1, "Could not receive an incoming message");
+ } else if (len < 1) {
+ errx(1, "Short read on an incoming message");
+ }
+
+ char sender[INET_ADDRPORTSTRLEN];
+ if (!inet_addrport(&sin, sender, sizeof(sender)))
+ return;
+ debug("Received a message from %s\n", sender);
+
+ const struct ng_ayiya_header * const ay =
+ (const struct ng_ayiya_header *)buf;
+ if (ayiya_length_id(ay) != 16) {
+ warnx("Invalid AYIYA identity header in the message");
+ return;
+ }
+ const struct in6_addr * const addr =
+ (const struct in6_addr *)(buf + ayiya_offset_id(ay));
+ char id[INET6_ADDRSTRLEN];
+ if (inet_ntop(AF_INET6, addr, id, sizeof(id)) == NULL) {
+ warn("Could not convert the AYIYA identity to an IPv6 address string");
+ return;
+ }
+ debug("- AYIYA sender identity: %s\n", id);
+
+ struct tunnel *t;
+ for (t = tunnels; t < tunnels + tunnels_count; t++)
+ if (bcmp(addr, &t->remote_addr, sizeof(addr)) == 0) {
+ handle_tunnel(t, sender, &sin, buf, len);
+ return;
+ }
+ debug("- unknown sender, ignoring the packet\n");
+}
+
+void
+handle_tunnel(struct tunnel * const t, const char * const sender,
+ const struct sockaddr_in * const sin,
+ const char * const buf, const size_t len)
+{
+ /* Do we need to kill a tunnel first? */
+ if (strcmp(t->remote_outer, sender) != 0) {
+ stop_tunnel(t, 0);
+ snprintf(t->remote_outer, sizeof(t->remote_outer), "%s", sender);
+ }
+
+ if (t->pid == 0) {
+ /* Silently ignore the packet if too many respanws. */
+ if (t->respawned > MAX_RESPAWNS)
+ return;
+
+ debug("Starting a new tunnel instance for %s\n", t->id);
+ start_tunnel(t, sin, sender);
+ }
+
+ debug("Sending a packet of size %zu to process %jd for %s\n",
+ len, (intmax_t)t->pid, t->id);
+ const uint16_t slen = len;
+ size_t written = 0;
+ while (written < sizeof(slen) + len) {
+ ssize_t n;
+ if (written < sizeof(slen))
+ n = write(t->comm[1], ((const char *)&slen) + written,
+ sizeof(slen) - written);
+ else
+ n = write(t->comm[1], buf + written - sizeof(slen),
+ len - written + sizeof(slen));
+ if (n < 1) {
+ /* Oof, kill and respawn the child process. */
+ debug("Could not send the packet to child process %jd, respawning the child\n",
+ (intmax_t)t->pid);
+ bzero(&t->remote_outer, sizeof(t->remote_outer));
+ t->respawned++;
+ handle_tunnel(t, sender, sin, buf, len);
+ return;
+ }
+
+ written += n;
+ }
+ debug("Packet sent successfully\n");
+}
+
+void
+stop_tunnel(struct tunnel * const t, const int waited)
+{
+ if (t->pid != 0) {
+ close(t->comm[1]);
+ if (!waited) {
+ kill(t->pid, SIGTERM);
+ int stat;
+ const pid_t wpid = waitpid(t->pid, &stat, 0);
+ check_wait_result(wpid, stat, t->pid, "AYIYA responder");
+ }
+ t->pid = 0;
+ }
+
+ /**
+ * Always try to shut the Netgraph nodes down, no matter if
+ * we created them a while ago or they were there before
+ * we started.
+ */
+ char path[NG_PATHSIZ];
+ get_ayiya_node(path, sizeof(path), t, ":inet6");
+ debug("Shutting down the ng_iface node %s\n", path);
+ if (NgSendMsg(ng_cs, path, NGM_GENERIC_COOKIE,
+ NGM_SHUTDOWN, NULL, 0) == -1 && errno != ENOENT)
+ err(1, "Could not shut down the ng_iface node %s",
+ path);
+
+ get_ayiya_node(path, sizeof(path), t, ":");
+ debug("Shutting down the ng_ayiya node %s\n", path);
+ if (NgSendMsg(ng_cs, path, NGM_GENERIC_COOKIE,
+ NGM_SHUTDOWN, NULL, 0) == -1 && errno != ENOENT)
+ err(1, "Could not shut down the AYIYA node %s",
+ path);
+ t->node_id = 0;
+
+ t->respawned = 0;
+}
+
+void
+start_tunnel(struct tunnel * const t, const struct sockaddr_in * const sin,
+ const char * const sender)
+{
+ debug("Creating a new node for %s\n", t->id);
+ assert(t->node_id == 0);
+ struct ngm_mkpeer pa = {
+ .type = "ayiya",
+ .ourhook = "a",
+ .peerhook = "control",
+ };
+ if (NgSendMsg(ng_cs, ".", NGM_GENERIC_COOKIE, NGM_MKPEER,
+ &pa, sizeof(pa)) == -1)
+ err(1, "Could not create an AYIYA node");
+ char node_name[NG_NODESIZ];
+ get_ayiya_node(node_name, sizeof(node_name), t, "");
+ if (NgNameNode(ng_cs, "a", node_name, t->id) == -1) {
+ warn("Could not set the AYIYA node's name");
+ goto ng_error;
+ }
+
+ static union {
+ struct ng_mesg msg;
+ char buf[2000];
+ } ng_buf;
+ char ng_path[NG_PATHSIZ];
+ if (NgSendMsg(ng_cs, "a", NGM_GENERIC_COOKIE, NGM_NODEINFO,
+ NULL, 0) == -1) {
+ warn("Could not request the AYIYA node's ID");
+ goto ng_error;
+ }
+ if (NgRecvMsg(ng_cs, &ng_buf.msg, sizeof(ng_buf.buf), ng_path) == -1) {
+ warn("Could not receive the AYIYA node's ID");
+ goto ng_error;
+ }
+ const struct nodeinfo * const info =
+ (const struct nodeinfo *)ng_buf.msg.data;
+ debug("Created the ng_ayiya node %s [%jx], it seems\n",
+ node_name, (intmax_t)info->id);
+ t->node_id = info->id;
+
+ struct ngm_mkpeer pk = {
+ .type = "ksocket",
+ .ourhook = "ayiya",
+ .peerhook = "inet/dgram/udp",
+ };
+ if (NgSendMsg(ng_cs, "a", NGM_GENERIC_COOKIE, NGM_MKPEER,
+ &pk, sizeof(pk)) == -1) {
+ warn("Could not create a ksocket node");
+ goto ng_error;
+ }
+ get_ayiya_node(node_name, sizeof(node_name), t, "_conn");
+ if (NgNameNode(ng_cs, "a.ayiya", node_name, t->id) == -1) {
+ warn("Could not set the ksocket node's name");
+ goto ng_error;
+ }
+ debug("Created the %s ksocket node\n", node_name);
+
+ union {
+ struct ng_ksocket_sockopt ks;
+ char buf[sizeof(struct ng_ksocket_sockopt) + sizeof(uint32_t)];
+ } opt;
+ opt.ks.level = SOL_SOCKET,
+ opt.ks.name = SO_REUSEPORT,
+ *(uint32_t *)&opt.ks.value[0] = 1;
+ if (NgSendMsg(ng_cs, "a.ayiya", NGM_KSOCKET_COOKIE, NGM_KSOCKET_SETOPT,
+ &opt, sizeof(opt)) == -1) {
+ warn("Could not set the SO_REUSEPORT option on the ksocket node");
+ goto ng_error;
+ }
+
+ if (NgSendMsg(ng_cs, "a.ayiya", NGM_KSOCKET_COOKIE, NGM_KSOCKET_BIND,
+ &local, sizeof(local)) == -1) {
+ warn("Could not bind the ksocket node to %s:%d",
+ inet_ntoa(local.sin_addr), htons(local.sin_port));
+ goto ng_error;
+ }
+ if (NgSendMsg(ng_cs, "a.ayiya", NGM_KSOCKET_COOKIE, NGM_KSOCKET_CONNECT,
+ sin, sizeof(*sin)) == -1) {
+ warn("Could not connect the ksocket node to %s:%d",
+ inet_ntoa(sin->sin_addr), htons(sin->sin_port));
+ goto ng_error;
+ }
+
+ struct ngm_mkpeer pi = {
+ .type = "iface",
+ .ourhook = "inet6",
+ .peerhook = "inet6",
+ };
+ if (NgSendMsg(ng_cs, "a", NGM_GENERIC_COOKIE, NGM_MKPEER,
+ &pi, sizeof(pi)) == -1) {
+ warn("Could not create the iface node");
+ goto ng_error;
+ }
+ if (NgSendMsg(ng_cs, "a.inet6", NGM_IFACE_COOKIE, NGM_IFACE_GET_IFNAME,
+ NULL, 0) == -1) {
+ warn("Could not request the iface node's name");
+ goto ng_error;
+ }
+ if (NgRecvMsg(ng_cs, &ng_buf.msg, sizeof(ng_buf.buf), ng_path) == -1) {
+ warn("Could not receive the iface node's name");
+ goto ng_error;
+ }
+ const char * const ifname = ng_buf.msg.data;
+ debug("Created the %s ng_iface node\n", ifname);
+
+ char local6[INET6_ADDRSTRLEN], remote6[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &t->local_addr, local6, sizeof(local6));
+ inet_ntop(AF_INET6, &t->remote_addr, remote6, sizeof(remote6));
+ static char cmdbuf[1024];
+ snprintf(cmdbuf, sizeof(cmdbuf),
+ "ifconfig %s inet6 %s %s prefixlen 128 alias mtu %u",
+ ifname, local6, remote6, t->mtu);
+ debug("Running %s\n", cmdbuf);
+ const int res = system(cmdbuf);
+ if (res == -1) {
+ warn("Could not run ifconfig for the %s tunnel", t->id);
+ goto ng_error;
+ } else if (!WIFEXITED(res) || WEXITSTATUS(res) != 0) {
+ warnx("Could not configure the %s tunnel using '%s'",
+ t->id, cmdbuf);
+ goto ng_error;
+ }
+
+ debug("Sending the 'configure' message to the ng_ayiya node\n");
+ if (NgSendMsg(ng_cs, "a", NGM_AYIYA_COOKIE, NGM_AYIYA_SECRETHASH,
+ t->password, sizeof(t->password)) == -1) {
+ warn("Could not set the AYIYA node's secret hash");
+ goto ng_error;
+ }
+ if (NgSendMsg(ng_cs, "a", NGM_AYIYA_COOKIE, NGM_AYIYA_CONFIGURE,
+ NULL, 0) == -1) {
+ warn("Could not request the AYIYA node to start up");
+ goto ng_error;
+ }
+ if (NgRecvMsg(ng_cs, &ng_buf.msg, sizeof(ng_buf.buf), ng_path) == -1) {
+ warn("Could not get the AYIYA node's configure response");
+ goto ng_error;
+ }
+ const uint32_t presp = *(const uint32_t *)ng_buf.msg.data;
+ if (presp != 0) {
+ warnc(presp, "The AYIYA node failed to start up");
+ goto ng_error;
+ }
+ debug("Well, well, it looks like we have a running ng_ayiya node!\n");
+
+ struct ngm_rmhook rm = {
+ .ourhook = "a",
+ };
+ if (NgSendMsg(ng_cs, ".", NGM_GENERIC_COOKIE, NGM_RMHOOK,
+ &rm, sizeof(rm)) == -1) {
+ warn("Could not remove the hook to the AYIYA node");
+ goto ng_error;
+ }
+
+ debug("Spawning a new process for %s\n", t->id);
+ if (pipe(t->comm) == -1)
+ err(1, "Could not create a communications pipe");
+
+ const pid_t pid = fork();
+ if (pid == -1) {
+ err(1, "Could not fork for a %s connection from %s",
+ t->id, sender);
+ } else if (pid == 0) {
+ close(t->comm[1]);
+ spawn_child(t);
+ /* NOTREACHED */
+ } else {
+ close(t->comm[0]);
+ t->pid = pid;
+ }
+ return;
+
+ng_error:
+ NgSendMsg(ng_cs, "a.inet6", NGM_GENERIC_COOKIE, NGM_SHUTDOWN, NULL, 0);
+ NgSendMsg(ng_cs, "a", NGM_GENERIC_COOKIE, NGM_SHUTDOWN, NULL, 0);
+ t->node_id = 0;
+ exit(1);
+}
+
+void
+check_wait_result(const pid_t pid, const int stat, const pid_t expected,
+ const char * const progname)
+{
+ if (pid != expected) {
+ errx(1, "Waiting for %s: expected pid %ld, got %ld", progname, (long)expected, (long)pid);
+ } else if (WIFEXITED(stat)) {
+ if (WEXITSTATUS(stat) != 0)
+ errx(1, "Child %s (pid %ld) exited with code %d", progname, (long)pid, WEXITSTATUS(stat));
+ } else if (WIFSIGNALED(stat)) {
+ if (WTERMSIG(stat) != SIGTERM)
+ errx(1, "Child %s (pid %ld) was killed by signal %d", progname, (long)pid, WTERMSIG(stat));
+ } else if (WIFSTOPPED(stat)) {
+ errx(1, "Child %s (pid %ld) was stopped by signal %d", progname, (long)pid, WSTOPSIG(stat));
+ } else {
+ errx(1, "Child %s (pid %ld) neither exited nor was killed or stopped; what in the world does wait(2) status %d mean?!", progname, (long)pid, stat);
+ }
+}
+
+void
+spawn_child(const struct tunnel * const t)
+{
+ debug("Starting ayiya_resp for tunnel %s, source %s\n",
+ t->id, t->remote_outer);
+ assert(resppath != NULL);
+
+ char node_name[NG_NODESIZ];
+ get_ayiya_node(node_name, sizeof(node_name), t, "");
+ char input[10];
+ snprintf(input, sizeof(input), "%d", t->comm[0]);
+ execl(resppath, "ayiya_resp", (verbose? "-vn": "-n"), node_name,
+ "-i", input, "loop", NULL);
+ err(1, "Could not execute the AYIYA responder %s for %s",
+ resppath, t->id);
+}
+
+int
+inet_addrport(const struct sockaddr_in * const sin, char * const buf,
+ const size_t size)
+{
+ char addr[INET_ADDRSTRLEN];
+ char port[6];
+ const int ares = getnameinfo((const struct sockaddr *)sin,
+ sizeof(*sin), addr, sizeof(addr), port, sizeof(port),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (ares != 0) {
+ warnx("Could not convert the message source to an IPv4 address:port string: %s",
+ gai_strerror(ares));
+ return 0;
+ }
+
+ const ssize_t len = snprintf(buf, size, "%s:%s", addr, port);
+ if (len < 0) {
+ warn("Message source snprintf() failed");
+ return 0;
+ } else if ((size_t)len >= size) {
+ warnx("Could not convert the message source to an IPv4 "
+ "address:port string, tried to store %zd bytes in "
+ "a %zu byte buffer", len, size);
+ return 0;
+ }
+ return 1;
+}
+
+void
+parse_tunnels(const char * const fname)
+{
+ FILE * const fp = fopen(fname, "r");
+ if (fp == NULL)
+ err(1, "Could not open the tunnels file %s", fname);
+
+ regex_t re_tunnel, re_line;
+ int rerr = regcomp(&re_tunnel, "^T[0-9]+$", REG_EXTENDED);
+ if (rerr != 0)
+ report_regerror(rerr, &re_tunnel);
+ rerr = regcomp(&re_line, "^[[:space:]]+([^:]+):[[:space:]]*(.*)$",
+ REG_EXTENDED);
+ if (rerr != 0)
+ report_regerror(rerr, &re_line);
+
+ char *line = NULL;
+ size_t cap = 0;
+ int in = 0;
+ struct tunnel *t = tunnels;
+ debug("Parsing the tunnels file %s\n", fname);
+ unsigned flags;
+ while (1) {
+ ssize_t len = getline(&line, &cap, fp);
+ if (len == -1) {
+ if (ferror(fp))
+ err(1, "Could not read the tunnels file %s",
+ fname);
+ else
+ break;
+ }
+ while (len > 0 && strchr(" \t\r\n", line[len - 1]) != NULL)
+ line[--len] = '\0';
+ if (len == 0)
+ continue;
+
+ regmatch_t match[3];
+ rerr = regexec(&re_tunnel, line, 1, match, 0);
+ if (rerr == 0) {
+ assert(match[0].rm_so == 0);
+ assert(match[0].rm_eo == len);
+
+ if (in) {
+ if (flags != (1 << 5) - 1)
+ errx(1,
+ "Error in the tunnels file %s: "
+ "incomplete specification for "
+ "tunnel %s", fname, t->id);
+
+ debug("- tunnel %s complete, moving on\n",
+ t->id);
+ t++;
+ tunnels_count++;
+ }
+
+ if ((size_t)len + 1 >= sizeof(t->id))
+ errx(1,
+ "Error in the tunnels file %s: "
+ "tunnel name '%s' too long, must be "
+ "at most %zu characters long",
+ fname, line, sizeof(t->id) - 1);
+ snprintf(t->id, sizeof(t->id), "%s", line);
+ /* Let's hope that the rest is pre-zeroed */
+ debug("- parsing tunnel %s\n", line);
+
+ flags = 0;
+ in = 1;
+ continue;
+ } else if (rerr != REG_NOMATCH) {
+ report_regerror(rerr, &re_tunnel);
+ }
+
+ rerr = regexec(&re_line, line, 3, match, 0);
+ if (rerr == 0) {
+ if (!in)
+ errx(1, "Error in the tunnels file %s: "
+ "data before the tunnel name", fname);
+ assert(match[1].rm_so != -1);
+ const char * const var = line + match[1].rm_so;
+ line[match[1].rm_eo] = '\0';
+ assert(match[2].rm_so != -1);
+ const char * const value = line + match[2].rm_so;
+ line[match[2].rm_eo] = '\0';
+
+ if (strcmp(var, "IPv6 Endpoint") == 0) {
+ debug(" - local address %s\n", value);
+ const int res = inet_pton(AF_INET6, value,
+ &t->local_addr);
+ if (res == 0)
+ errx(1,
+ "Error in the tunnels file %s: "
+ "invalid IPv6 endpoint '%s' for "
+ "tunnel %s", fname, value, t->id);
+ else if (res == -1)
+ errx(1, "Could not parse the IPv6 "
+ "endpoint '%s' for tunnel %s",
+ value, t->id);
+ flags |= 1 << 0;
+ } else if (strcmp(var, "IPv6 POP") == 0) {
+ debug(" - remote address %s\n", value);
+ const int res = inet_pton(AF_INET6, value,
+ &t->remote_addr);
+ if (res == 0)
+ errx(1,
+ "Error in the tunnels file %s: "
+ "invalid IPv6 client '%s' for "
+ "tunnel %s", fname, value, t->id);
+ else if (res == -1)
+ errx(1, "Could not parse the IPv6 "
+ "client '%s' for tunnel %s",
+ value, t->id);
+ flags |= 1 << 1;
+ } else if (strcmp(var, "IPv6 PrefixLength") == 0) {
+ debug(" - prefix length %s\n", value);
+ char *end;
+ const unsigned long v =
+ strtoul(value, &end, 10);
+ if (v > 127 || *end != '\0')
+ errx(1,
+ "Error in the tunnels file %s: "
+ "invalid prefix length '%s' for "
+ "tunnel %s", fname, value, t->id);
+ t->prefix_len = v;
+ flags |= 1 << 2;
+ } else if (strcmp(var, "Password") == 0) {
+ debug(" - tunnel password %s\n", value);
+ SHA_CTX ctx;
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, value,
+ match[2].rm_eo - match[2].rm_so);
+ SHA1_Final(t->password, &ctx);
+ flags |= 1 << 3;
+ } else if (strcmp(var, "Tunnel MTU") == 0) {
+ debug(" - MTU %s\n", value);
+ char *end;
+ const unsigned long v =
+ strtoul(value, &end, 10);
+ if (v > 100000 || *end != '\0')
+ errx(1,
+ "Error in the tunnels file %s: "
+ "invalid MTU '%s' for "
+ "tunnel %s", fname, value, t->id);
+ t->mtu = v;
+ flags |= 1 << 4;
+ }
+ } else if (rerr != REG_NOMATCH) {
+ report_regerror(rerr, &re_tunnel);
+ } else {
+ errx(1, "Error in the tunnels file %s: "
+ "unrecognized line '%s'", fname, line);
+ }
+ }
+
+ if (flags != (1 << 5) - 1)
+ errx(1, "Error in the tunnels file %s: "
+ "incomplete specification for tunnel %s", fname, t->id);
+ tunnels_count++;
+ debug("- tunnel %s complete\n", t->id);
+}
+
+void
+report_regerror(const int code, const regex_t * restrict const preg)
+{
+ char *buf = NULL;
+ size_t sz = 0;
+
+ while (1) {
+ const size_t newsz = regerror(code, preg, buf, sz);
+ if (newsz <= sz)
+ break;
+
+ char * const nbuf = realloc(buf, newsz);
+ if (nbuf == NULL)
+ err(1, "No memory for an regerror buffer");
+ buf = nbuf;
+ sz = newsz;
+ }
+ errx(1, "Internal error compiling a regular expression: %s", buf);
+}
+
+void
+get_ayiya_node(char * const buf, const size_t sz,
+ const struct tunnel * const t,const char * const suffix)
+{
+ snprintf(buf, sz, "ayiya_%s%s", t->id, suffix);
+}
+
+void
+sigchld_handler(int sig)
+{
+ /**
+ * The only purpose of this signal handler is to make sure
+ * that accept_connection()'s recvfrom() is interrupted.
+ */
+ (void)sig;
+}
Modified: soc2015/roam/ayiya_resp/Makefile
==============================================================================
--- soc2015/roam/ayiya_resp/Makefile Wed Aug 19 15:56:09 2015 (r289925)
+++ soc2015/roam/ayiya_resp/Makefile Wed Aug 19 15:56:16 2015 (r289926)
@@ -26,6 +26,7 @@
SRCS= main.c
NO_MAN= yes
WARNS?= 9
+NO_WCAST_ALIGN= 1
DPADD= ${LIBNETGRAPH}
LDADD= -lnetgraph
Modified: soc2015/roam/ayiya_resp/main.c
==============================================================================
--- soc2015/roam/ayiya_resp/main.c Wed Aug 19 15:56:09 2015 (r289925)
+++ soc2015/roam/ayiya_resp/main.c Wed Aug 19 15:56:16 2015 (r289926)
@@ -25,10 +25,13 @@
*/
#include <sys/types.h>
+#include <sys/queue.h>
#include <assert.h>
#include <err.h>
+#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
@@ -47,6 +50,7 @@
#define A_SEL_RD_DATA 0x0001
#define A_SEL_WR_DATA 0x0002
#define A_SEL_EXC 0x0004
+#define A_SEL_RD_INPUT 0x0008
#define MOTD \
"1435504634\n" \
@@ -54,6 +58,15 @@
"Welcome to the system, here's the situation;\n" \
"It's a bit confusing, welcome to the maze!\n"
+struct ayqueue {
+ size_t sz;
+ STAILQ_ENTRY(ayqueue) next;
+ char buf[];
+};
+
+static int inputfd = -1;
+static STAILQ_HEAD(ayqhead, ayqueue) injectq;
+
static int quiet;
static int verbose;
@@ -69,6 +82,10 @@
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 void inject_packets(int);
+
+static size_t check_ayiya_packet(const char *, size_t);
+static void inject_ayiya_packet(const char *, size_t);
static struct {
const char *name;
@@ -79,7 +96,7 @@
};
#define COMMANDS (sizeof(commands) / sizeof(commands[0]))
-#define AYIYA_ND "sc_ayiya"
+static const char *node_name = "sc_ayiya";
static union {
struct ng_mesg msg;
@@ -93,12 +110,32 @@
int ch, hflag, Vflag;
hflag = Vflag = 0;
- while (ch = getopt(argc, argv, "hqVv"), ch != -1)
+ while (ch = getopt(argc, argv, "hi:n:qVv"), ch != -1)
switch (ch) {
case 'h':
hflag = 1;
break;
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-soc-all
mailing list