Date: Wed, 19 Aug 2015 15:56:16 GMT From: roam@FreeBSD.org To: svn-soc-all@FreeBSD.org Subject: socsvn commit: r289926 - in soc2015/roam: . ayiya_listen ayiya_resp ng_ayiya Message-ID: <201508191556.t7JFuGsr076963@socsvn.freebsd.org>
next in thread | raw e-mail | index | archive | help
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 ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201508191556.t7JFuGsr076963>