socsvn commit: r286979 - soc2015/roam/ng_ayiya
roam at FreeBSD.org
roam at FreeBSD.org
Thu Jun 11 22:34:06 UTC 2015
Author: roam
Date: Thu Jun 11 22:34:04 2015
New Revision: 286979
URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=286979
Log:
A first, not completely working, attempt at AYIYA.
Add the "configure" control message to query the ng_iface node for
its IPv6 address (we shall use it as our identity).
Add the rcvdata method and use the new ayiya_build() function to
wrap and send inet6->ayiya packets and the new ayiya_verify()
function to unwrap and send ayiya->inet6 packets.
ObQuote: "Why won't you talk to me? You never talk to me"
Modified:
soc2015/roam/ng_ayiya/Makefile
soc2015/roam/ng_ayiya/ng_ayiya.c
soc2015/roam/ng_ayiya/ng_ayiya.h
soc2015/roam/ng_ayiya/scaffold.pl
Modified: soc2015/roam/ng_ayiya/Makefile
==============================================================================
--- soc2015/roam/ng_ayiya/Makefile Thu Jun 11 22:14:09 2015 (r286978)
+++ soc2015/roam/ng_ayiya/Makefile Thu Jun 11 22:34:04 2015 (r286979)
@@ -52,4 +52,4 @@
tic: tic-tunnels.txt up
${SCAFFOLD} inet6 ${TIC_TUNNEL}
- ${SCAFFOLD} ayiya
+ ${SCAFFOLD} ayiya ${TIC_TUNNEL}
Modified: soc2015/roam/ng_ayiya/ng_ayiya.c
==============================================================================
--- soc2015/roam/ng_ayiya/ng_ayiya.c Thu Jun 11 22:14:09 2015 (r286978)
+++ soc2015/roam/ng_ayiya/ng_ayiya.c Thu Jun 11 22:34:04 2015 (r286979)
@@ -31,9 +31,15 @@
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/sbuf.h>
+#include <sys/socket.h>
+#include <crypto/sha1.h>
+#include <net/if.h>
+#include <net/if_var.h>
#include <netgraph/ng_message.h>
+#include <netgraph/ng_iface.h>
#include <netgraph/ng_parse.h>
#include <netgraph/netgraph.h>
+#include <netinet/in.h>
#include "ng_ayiya.h"
@@ -48,6 +54,7 @@
static ng_newhook_t ng_ayiya_newhook;
static ng_disconnect_t ng_ayiya_disconnect;
static ng_shutdown_t ng_ayiya_shutdown;
+static ng_rcvdata_t ng_ayiya_rcvdata;
static ng_parse_array_getLength_t ng_ayiya_secrethash_getLength;
@@ -64,6 +71,13 @@
&ng_ayiya_secrethash_type,
NULL,
},
+ {
+ NGM_AYIYA_COOKIE,
+ NGM_AYIYA_CONFIGURE,
+ "configure",
+ NULL,
+ &ng_parse_uint32_type,
+ },
{ 0 }
};
@@ -77,6 +91,7 @@
.disconnect = ng_ayiya_disconnect,
.shutdown = ng_ayiya_shutdown,
.cmdlist = ng_ayiya_cmds,
+ .rcvdata = ng_ayiya_rcvdata,
};
NETGRAPH_INIT(ayiya, &typestruct);
@@ -99,9 +114,12 @@
};
struct ng_ayiya_private {
- node_p node;
- u_char secrethash[16];
+ u_char identity[16];
+ u_char secrethash[20];
hook_p hooks[AYIYA_HOOK_LAST];
+ node_p node;
+ item_p configuring;
+ bool configured;
};
typedef struct ng_ayiya_private *priv_p;
@@ -127,16 +145,16 @@
"{\n"
"\t\"id\":\t\"%x\",\n"
"\t\"name\":\t\"%s\",\n"
- "\t\"has_secret\":\t%s,\n"
+ "\t\"configured\":\t%s,\n"
"\t\"hooks\": {\n",
NG_NODE_ID(node), NG_NODE_NAME(node),
- priv->secrethash? "true": "false");
+ priv->configured? "true": "false");
else
sbuf_printf(sb,
"Node [%x] %s\n"
- "Secret hash %sset\n",
+ "Configured: %s\n",
NG_NODE_ID(node), NG_NODE_NAME(node),
- priv->secrethash? "": "not ");
+ priv->configured? "yes": "no");
for (int idx = 0; idx < AYIYA_HOOK_LAST; idx++) {
const char * const hname = hookdefs[idx].name;
@@ -180,6 +198,28 @@
sbuf_printf(sb, "\t}\n}");
}
+static void
+configuring_respond(const node_p node, const uint32_t error)
+{
+ const priv_p priv = NG_NODE_PRIVATE(node);
+ item_p item = priv->configuring;
+ struct ng_mesg *msg;
+ struct ng_mesg *resp;
+
+ if (item == NULL)
+ return;
+
+ NGI_GET_MSG(item, msg);
+ NG_MKRESPONSE(resp, msg, sizeof(error), M_NOWAIT);
+ *(uint32_t *)(resp->data) = error;
+ int err;
+ NG_RESPOND_MSG(err, node, item, resp);
+ NG_FREE_MSG(msg);
+
+ priv->configuring = NULL;
+ priv->configured = (error == 0);
+}
+
static int
ng_ayiya_rcvmsg(const node_p node, item_p item, const hook_p lasthook)
{
@@ -188,8 +228,11 @@
struct ng_mesg *msg;
NGI_GET_MSG(item, msg);
+ const bool is_resp = msg->header.flags & NGF_RESP;
switch (msg->header.typecookie) {
case NGM_GENERIC_COOKIE:
+ if (is_resp)
+ ERROUT(EINVAL);
switch (msg->header.cmd) {
case NGM_TEXT_CONFIG:
case NGM_TEXT_STATUS:
@@ -217,6 +260,8 @@
break;
case NGM_AYIYA_COOKIE:
+ if (is_resp)
+ ERROUT(EINVAL);
switch (msg->header.cmd) {
case NGM_AYIYA_SECRETHASH:
{
@@ -230,12 +275,72 @@
}
break;
+ case NGM_AYIYA_CONFIGURE:
+ {
+ const priv_p priv = NG_NODE_PRIVATE(node);
+ if (msg->header.arglen != 0 || priv->configured ||
+ !priv->hooks[AYIYA_HOOK_INET6])
+ ERROUT(EINVAL);
+ if (priv->configuring)
+ ERROUT(EINPROGRESS);
+
+ /* Configuration is mostly querying the IPv6 address */
+ struct ng_mesg *q;
+ NG_MKMESSAGE(q, NGM_IFACE_COOKIE,
+ NGM_IFACE_GET_IFINDEX, 0, M_NOWAIT);
+ if (q == NULL)
+ ERROUT(ENOMEM);
+ NG_SEND_MSG_HOOK(error, node, q,
+ priv->hooks[AYIYA_HOOK_INET6], NG_NODE_ID(node));
+ /* Put that message back where it was! */
+ NGI_MSG(item) = msg;
+ priv->configuring = item;
+ /**
+ * Do not pass "Go", do not respond to the message,
+ * do not free the item.
+ */
+ return (0);
+ }
+
default:
error = EINVAL;
break;
}
break;
+ case NGM_IFACE_COOKIE:
+ {
+ if (!is_resp || msg->header.cmd != NGM_IFACE_GET_IFINDEX ||
+ msg->header.arglen != 4)
+ ERROUT(EINVAL);
+
+ const uint32_t ifidx = *(const uint32_t * const)msg->data;
+ struct ifnet * const ifp = ifnet_byindex_ref(ifidx);
+ if (ifp == NULL)
+ ERROUT(EINVAL);
+
+ bool found = false;
+ const struct ifaddr *ifa;
+ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+ if (ifa->ifa_addr == NULL ||
+ ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ const struct sockaddr_in6 * const a =
+ (const struct sockaddr_in6 *)ifa->ifa_addr;
+ if (a->sin6_addr.s6_addr16[0] == IPV6_ADDR_INT16_ULL)
+ continue;
+
+ const priv_p priv = NG_NODE_PRIVATE(node);
+ bcopy(a->sin6_addr.s6_addr, priv->identity, sizeof(priv->identity));
+ found = 1;
+ break;
+ }
+ if_rele(ifp);
+
+ configuring_respond(node, found? 0: ENOENT);
+ break;
+ }
+
default:
error = EINVAL;
break;
@@ -269,7 +374,8 @@
if (priv->hooks[idx] != NULL)
return (EISCONN);
- /* TODO: some more checks here */
+ if (idx == AYIYA_HOOK_INET6)
+ priv->configured = false;
priv->hooks[idx] = hook;
return (0);
@@ -278,7 +384,8 @@
static int
ng_ayiya_disconnect(const hook_p hook)
{
- priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ const node_p node = NG_HOOK_NODE(hook);
+ const priv_p priv = NG_NODE_PRIVATE(node);
int idx;
for (idx = 0; idx < AYIYA_HOOK_LAST; idx++)
if (priv->hooks[idx] == hook)
@@ -286,6 +393,8 @@
if (idx == AYIYA_HOOK_LAST)
panic("%s", __func__);
priv->hooks[idx] = NULL;
+ if (priv->configuring && idx == AYIYA_HOOK_INET6)
+ configuring_respond(node, ECONNABORTED);
return (0);
}
@@ -294,6 +403,8 @@
{
const priv_p priv = NG_NODE_PRIVATE(node);
+ if (priv->configuring)
+ configuring_respond(0, ECONNABORTED);
free(priv, M_NETGRAPH_AYIYA);
NG_NODE_SET_PRIVATE(node, NULL);
NG_NODE_UNREF(node);
@@ -306,3 +417,222 @@
{
return (sizeof(((const priv_p)NULL)->secrethash));
}
+
+static unsigned
+which_hook(const hook_p hook, const priv_p priv)
+{
+ for (unsigned i = 0; i < AYIYA_HOOK_LAST; i++)
+ if (hook == priv->hooks[i])
+ return (i);
+ return (AYIYA_HOOK_LAST);
+}
+
+static int
+ayiya_build(struct mbuf **mb, const u_char opcode, const u_char nextheader,
+ const priv_p priv)
+{
+ struct mbuf *m = *mb;
+
+ 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);
+ struct ng_ayiya_header * const hdr =
+ (struct ng_ayiya_header *)m->m_data;
+ struct ng_ayiya_packet * const pkt =
+ (struct ng_ayiya_packet *)m->m_data;
+
+ hdr->idlen = 4;
+ hdr->idtype = 1; // Integer
+ hdr->siglen = sizeof(pkt->signature) / 4;
+ hdr->hshmeth = 2; // SHA1
+ hdr->autmeth = 1; // shared secret
+ hdr->opcode = opcode;
+ hdr->nextheader = nextheader;
+ hdr->epochtime = htonl(time_second);
+
+ bcopy(priv->identity, pkt->identity, sizeof(pkt->identity));
+
+ /* Start with the secret hash... */
+ bcopy(priv->secrethash, pkt->signature, sizeof(pkt->signature));
+
+ /* ...now hash it */
+ SHA1_CTX c;
+ u_char hash[20];
+
+ SHA1Init(&c);
+ SHA1Update(&c, m->m_data, m->m_len);
+ SHA1Final(hash, &c);
+ 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)
+{
+ struct mbuf *m = *mb;
+
+ if (m->m_next) {
+ m = m_defrag(m, M_NOWAIT);
+ *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;
+ const int32_t ofs_id = sizeof(*hdr);
+ if (len < ofs_id)
+ return (EINVAL);
+ if (hdr->idlen > 4)
+ return (EINVAL);
+ const int32_t ofs_sig = ofs_id + (2 << hdr->idlen);
+ if (len < ofs_sig)
+ return (EINVAL);
+ const unsigned siglen = 4 * hdr->siglen;
+ u_char * const sig = ((u_char *)hdr) + ofs_sig;
+ const int32_t ofs_data = ofs_sig + siglen;
+ if (len < ofs_data)
+ return (EINVAL);
+
+ /* Okay, it seems that we have enough of a packet to process. */
+ switch (hdr->autmeth) {
+ case 0:
+ /* Hmm... */
+ break;
+
+ case 1:
+ switch (hdr->hshmeth) {
+ case 0:
+ return (EINVAL);
+
+ case 1:
+ {
+ /*
+ u_char csum[16];
+ if (sizeof(csum) != siglen)
+ return (EINVAL);
+ bcopy(sig, csum, siglen);
+ bcopy(priv->secrethash, sig, siglen);
+ MD5_CTX c;
+ MD5Init(&c);
+ MD5Update(&c, m->m_data, m->m_len);
+ u_char hash[16];
+ MD5Final(hash, &c);
+ if (bcmp(csum, hash, siglen) != 0)
+ return (EINVAL);
+ */
+ m_freem(m);
+ *mb = NULL;
+ return (0);
+ }
+
+ case 2:
+ {
+ u_char csum[20];
+ if (sizeof(csum) != siglen)
+ return (EINVAL);
+ bcopy(sig, csum, siglen);
+ bcopy(priv->secrethash, sig, siglen);
+ SHA1_CTX c;
+ SHA1Init(&c);
+ SHA1Update(&c, m->m_data, m->m_len);
+ u_char hash[20];
+ SHA1Final(hash, &c);
+ if (bcmp(csum, hash, siglen) != 0)
+ return (EINVAL);
+ break;
+ }
+
+ default:
+ return (EOPNOTSUPP);
+ }
+ break;
+
+ case 2:
+ return (EOPNOTSUPP);
+
+ default:
+ return (EOPNOTSUPP);
+ }
+
+ m_adj(m, ofs_data);
+ *mb = m;
+ *opcode = hdr->opcode;
+ *nextheader = hdr->nextheader;
+ return (0);
+}
+
+static int
+ng_ayiya_rcvdata(const hook_p hook, const item_p item)
+{
+ const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ struct mbuf *m;
+
+ NGI_GET_M(item, m);
+ NG_FREE_ITEM(item);
+
+ unsigned hidx = which_hook(hook, priv);
+ switch (hidx) {
+ case AYIYA_HOOK_INET6:
+ {
+ if (!priv->configured || priv->hooks[AYIYA_HOOK_AYIYA] == NULL)
+ {
+ /* FIXME: enqueue the packet? */
+ m_freem(m);
+ return (0);
+ }
+
+ /* Prepare a packet for forwarding */
+ int error = ayiya_build(&m, 1, IPPROTO_IPV6, priv);
+ if (error != 0)
+ return (error);
+
+ NG_SEND_DATA_ONLY(error, priv->hooks[AYIYA_HOOK_INET6], m);
+ return (error);
+ }
+
+ case AYIYA_HOOK_AYIYA:
+ {
+ u_char opcode;
+ u_char nextheader;
+ int error = ayiya_verify(&m, &opcode, &nextheader, priv);
+ if (error != 0)
+ {
+ m_freem(m);
+ return (0);
+ }
+
+ switch (opcode)
+ {
+ case 1:
+ /* Forward */
+ if (nextheader != IPPROTO_IPV6)
+ {
+ 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;
+
+ default:
+ break;
+ }
+
+ m_freem(m);
+ return (0);
+ }
+
+ default:
+ m_freem(m);
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
Modified: soc2015/roam/ng_ayiya/ng_ayiya.h
==============================================================================
--- soc2015/roam/ng_ayiya/ng_ayiya.h Thu Jun 11 22:14:09 2015 (r286978)
+++ soc2015/roam/ng_ayiya/ng_ayiya.h Thu Jun 11 22:34:04 2015 (r286979)
@@ -33,6 +33,36 @@
enum {
NGM_AYIYA_SECRETHASH = 1,
+ NGM_AYIYA_CONFIGURE,
};
+struct ng_ayiya_header {
+#if _BYTE_ORDER == _BIG_ENDIAN
+ unsigned idlen:4,
+ idtype:4,
+ siglen:4,
+ hshmeth:4,
+ autmeth:4,
+ opcode:4,
+ nextheader:8;
+#endif
+#if _BYTE_ORDER == _LITTLE_ENDIAN
+ unsigned idtype:4,
+ idlen:4,
+ hshmeth:4,
+ siglen:4,
+ opcode:4,
+ autmeth:4,
+ nextheader:8;
+#endif
+ uint32_t epochtime;
+} __packed;
+
+/* A packet that we will send: IPv6 address as identity, SHA1 signature */
+struct ng_ayiya_packet {
+ struct ng_ayiya_header hdr;
+ u_char identity[16];
+ u_char signature[20];
+} __packed;
+
#endif
Modified: soc2015/roam/ng_ayiya/scaffold.pl
==============================================================================
--- soc2015/roam/ng_ayiya/scaffold.pl Thu Jun 11 22:14:09 2015 (r286978)
+++ soc2015/roam/ng_ayiya/scaffold.pl Thu Jun 11 22:34:04 2015 (r286979)
@@ -28,6 +28,7 @@
use strict;
use warnings;
+use Digest::SHA1 qw/sha1_hex/;
use Getopt::Std;
use JSON::PP;
use Net::SixXS::Data::Tunnel;
@@ -55,6 +56,7 @@
sub cmd_inet6($ @);
my %cmds = (
+ ayiya => \&cmd_ayiya,
build => \&cmd_setup,
erect => \&cmd_setup,
help => \&cmd_help,
@@ -91,7 +93,8 @@
{
my ($err) = @_;
my $s = <<EOUSAGE
-Usage: scaffold help
+Usage: scaffold ayiya tunnelname
+ scaffold help
scaffold inet6 tunnelname
scaffold [-v] setup
scaffold [-v] shutdown [all]
@@ -237,8 +240,8 @@
if ((run_command 'kldstat') !~ /ng_ayiya\.ko/) {
debug "Trying to build the ng_ayiya.ko module";
- debug run_command 'make', 'depend';
- debug run_command 'make';
+ debug run_command 'env', 'CFLAGS=-O0', 'DEBUG_FLAGS=-g', 'make', 'depend';
+ debug run_command 'env', 'CFLAGS=-O0', 'DEBUG_FLAGS=-g', 'make';
debug "Trying to load the ng_ayiya.ko module";
run_command 'make', 'load';
} else {
@@ -302,7 +305,7 @@
};
my $err = $@;
if (length ($err // '') || !defined($d) || ref $d ne 'HASH' ||
- grep !exists $d->{$_}, qw/id name has_secret hooks/) {
+ grep !exists $d->{$_}, qw/id name configured hooks/) {
warn "Node [$node->{id}] '$node->{name}' returned ".
"an invalid JSON configuration\n";
next;
@@ -417,6 +420,9 @@
run_command 'ifconfig', $iface, 'inet6', $t->ipv6_local;
# FIXME: Add a default route here, too.
+
+ debug "Trying to get the node to configure itself";
+ ngctl 'msg', "$c->{name}:", 'configure';
}
sub get_tic_tunnel($)
@@ -451,3 +457,47 @@
}
return Net::SixXS::Data::Tunnel->from_hash(\%cfg);
}
+
+sub cmd_ayiya($ @)
+{
+ my ($cmd, @args) = @_;
+
+ if (@args != 1) {
+ warn "The ayiya command expects a tunnel name parameter\n";
+ usage 1;
+ }
+ my $tunnel = shift @args;
+
+ my $t = get_tic_tunnel $tunnel;
+ my $ayiya = get_ayiya;
+
+ if (!$ayiya->{ours}) {
+ die "Our ng_ayiya node is not configured\n";
+ }
+ # Tear down the socket if it's configured
+ my $c = $ayiya->{ours}->{config};
+ my $sa = $c->{hooks}->{ayiya};
+ if (defined $sa) {
+ debug "Shutting down the current socket";
+ ngctl 'shutdown', "[$sa->{id}]:";
+ }
+
+ # Initialize the shared secret
+ my $p = sha1_hex($t->password);
+ $p =~ s/(..)/0x$1, /g;
+ $p = "[ $p ]";
+ ngctl 'msg', "$c->{name}:", 'secrethash', $p;
+ # OK, let's create one
+ my $hkname = "ayiya/$tunnel";
+ my $hkpeer = 'inet/dgram/udp';
+ my $pname = 'sc_conn';
+ ngctl 'mkpeer', "$c->{name}:", 'ksocket', $hkname, $hkpeer;
+ ngctl 'name', "$c->{name}:$hkname", $pname;
+ $ayiya = get_ayiya;
+ $c = $ayiya->{ours}->{config};
+ if (!defined $c || $c->{hooks}->{ayiya}->{name} ne $pname) {
+ die "Could not query the newly-created ng_ksocket node\n";
+ }
+ ngctl 'msg', "$pname:", 'connect', 'inet/'.$t->ipv4_pop.':5072';
+ ...;
+}
More information about the svn-soc-all
mailing list