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>