Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 11 Jun 2015 22:34:04 GMT
From:      roam@FreeBSD.org
To:        svn-soc-all@FreeBSD.org
Subject:   socsvn commit: r286979 - soc2015/roam/ng_ayiya
Message-ID:  <201506112234.t5BMY4at012125@socsvn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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';
+	...;
+}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201506112234.t5BMY4at012125>