NetBSD-Users archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: Error trying to create gre tunnel



On Sat, Aug 12, 2017 at 12:05:00PM +0700, Robert Elz wrote:
>     Date:        Fri, 11 Aug 2017 22:55:41 -0400
>     From:        "D'Arcy Cain" <darcy%NetBSD.org@localhost>
>     Message-ID:  <711b9619-36ef-fedf-cfcf-39a9b969d92c%NetBSD.org@localhost>
> 
>   | I thought about that but my Linksys WRT router doesn't appear to have 
>   | the ability to forward anything but TCP and UDP.
> 
> Since you have control over both ends, you could switch to GRE over UDP
> and suffer the small MTU hit.

Just in case it helps....

About the same time that I added GRE over UDP to the kernel, I wrote the
attached tunnel daemon.  It acts as server or client depending on the
parameters you provide.

The routers on our (defunct) community wireless network ran this daemon
in client mode.  The server ran on a Soekris net4521 at my office.
Many of the routers were behind NAT firewalls, so the NAT-piercing UDP
tunnels helped us maintain administrative control of them.

There is no authentication, but a determined developer should be able to
add some.  I guess that you could add privacy with IPsec.

Dave
 
-- 
David Young
dyoung%pobox.com@localhost    Urbana, IL    (217) 721-9981
# $Id: Makefile 4324 2006-10-09 07:27:15Z dyoung $

NOMAN=
.include <bsd.own.mk>

LIBLOG != cd ${.CURDIR}/../log && ${PRINTOBJDIR}
LIBMISC != cd ${.CURDIR}/../misc && ${PRINTOBJDIR}
LIBSSRV != cd ${.CURDIR}/../ssrv && ${PRINTOBJDIR}

CPPFLAGS=-I${.CURDIR}/.. -Wall -pedantic -std=c99 -fPIC
BINOWN=root
BINGRP=wheel
BINMODE=0755
BINDIR=/usr/sbin
LDADD+=-L${LIBSSRV} -lssrv
LDADD+=-L${LIBLOG} -llog
LDADD+=-L${LIBMISC} -lmisc
LDADD+=-levent -lutil

DPADD+=	${LIBLOG}/liblog.a
DPADD+=	${LIBSSRV}/libssrv.a ${LIBMISC}/libmisc.a

PROG=utd
INCS=	utd.h
SRCS=	main.c utd.c utd_sm.c

LINKS+=	${BINDIR}/${PROG} ${BINDIR}/${PROG}6

CPPFLAGS+=-DUTD_DEBUG
CPPFLAGS+=-DDIAGNOSTIC
CFLAGS+=-g -O1
#-DDEBUG

.include "../mk/cuwin.mk"
.include <bsd.prog.mk>
utd: UDP Tunnel Daemon

WORK IN PROGRESS

A basic daemon for setting up UDP+GRE-encapsulated IP tunnels.

usage: utd [-d] [-e] local-outer,port local-inner[/masklen]
       [remote-outer,port remote-inner]

-d      tells utd not to daemonize.  utd will write debug messages
        to the standard error.

-e      tells utd to add an EUI64 to the local inner address, if
        it is an IPv6 address with mask length equal to 64 and
        zeroes in the host part of the address.

-m N    tells utd that the gre(4) unit numbers it uses should
        roll over at N.  That is, utd should choose unit number 0
        after unit number N - 1.

utd acts as a server when it is run with two arguments.  utd acts
as a client when it is run with four arguments.

A utd server binds a UDP socket to local-outer,port and listens
for messages from utd clients.  A utd client also binds local-outer,port,
but it connects the socket to remote-outer,port and sends a message
that initiates the client-server handshake that creates a tunnel.

The handshake is like this,

        1 client sends probe request

        2 server receives request; sends probe reply to the probe's
          source

        3 client receives reply, creates tunnel, sends acknowledgement,
          starts monitoring tunnel's input-packet count

        4 server receives acknowledgement, creates tunnel, starts
          monitoring tunnel's input-packet count

If the input-packet count stops ascending for a minute, then utd
will destroy the tunnel and restart the handshake.

A utd client creates one GRE interface, sets its local address to
local-inner, and sets the address of the remote tunnel endpoint to
remote-inner.

A utd server creates a tunnel interface for each client.  It sets
the local address of each interface to local-inner.  It sets the
address of the remote tunnel endpoint on each interface to the
address specified by the client in the handshake.
/* $Id: main.c 5026 2008-05-09 17:50:11Z dyoung $ */
/*
 * Copyright (c) 2005, 2006, 2007 David Young.  All rights reserved.
 *
 * This file contains code contributed by David Young.
 *
 * 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.
 * 3. David Young's name may not be used to endorse or promote
 *    products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``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 DAVID
 * YOUNG 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.
 *
 * This material is based upon work partially supported by NSF
 * under Contract No. NSF CNS-0626584.
 */
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <limits.h>
#include <unistd.h>

#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/time.h>

#include <net/if.h>
#include <net/if_gre.h>

#include <event.h>
#include <netdb.h>

#include "misc/misc.h"
#include "log/log.h"
#include "ssrv/sockaddr.h"
#include "utd.h"

#ifndef lint
__RCSID("$Id: main.c 5026 2008-05-09 17:50:11Z dyoung $");
#endif

#ifdef UTD_DEBUG
#define	UTD_OPTION_FLAGS "dem:"
#else
#define	UTD_OPTION_FLAGS "em:"
#endif /* UTD_DEBUG */

void
usage(void)
{
	errx(EXIT_FAILURE, "usage: %s "
#ifdef UTD_DEBUG
	    "[-d] "
#endif /* UTD_DEBUG */
	    "[-e] "
	    "local-outer,port "
	    "local-inner[/masklen] [remote-outer,port remote-inner]",
	    getprogname());
}

static void
parse_outer_inner(int flags, char **argv,
    struct sockaddr **outer, struct sockaddr **inner, int *masklenp)
{
	long masklen, max_masklen;
	int rc;
	struct addrinfo hints, *res;
	char *end, *len, *port;

	MEMZERO(&hints);
	hints.ai_flags = flags;
	hints.ai_family = PF_INET;
	hints.ai_socktype = SOCK_DGRAM;
	hints.ai_protocol = IPPROTO_UDP;

	/* outer */
	port = argv[0];
	if (strsep(&port, ",") == NULL)
		usage();
	if ((rc = getaddrinfo(argv[0], port, &hints, &res)) != 0)
		errx(EXIT_FAILURE, "%s: getaddrinfo: %s", __func__,
		     gai_strerror(rc));
	if ((*outer = sockaddr_dup(res->ai_addr)) == NULL)
		err(EXIT_FAILURE, "%s: sockaddr_dup", __func__);
	freeaddrinfo(res);

	hints.ai_family = PF_UNSPEC;

	/* inner */
	len = argv[1];
	strsep(&len, "/");
	if ((rc = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) {
		errx(EXIT_FAILURE, "%s: getaddrinfo: %s", __func__,
		    gai_strerror(rc));
	}
	if ((*inner = sockaddr_dup(res->ai_addr)) == NULL)
		err(EXIT_FAILURE, "%s: sockaddr_dup", __func__);

	max_masklen = NBBY * sockaddr_getlen(*inner);

	if (masklenp == NULL && len != NULL)
		errx(EXIT_FAILURE, "%s: no mask length expected", __func__);
	else if (masklenp == NULL)
		;
	else if (len == NULL)
		*masklenp = max_masklen;
	else if (((masklen = strtol(len, &end, 10)) == LONG_MIN ||
		 masklen == LONG_MAX) && errno == ERANGE)
		err(EXIT_FAILURE, "%s: strtol", __func__);
	else if (len == end)
		errx(EXIT_FAILURE, "%s: mask length missing", __func__);
	else if (masklen < 0 || masklen > max_masklen)
		errx(EXIT_FAILURE, "%s: mask length out of range", __func__);
	else
		*masklenp = masklen;
	freeaddrinfo(res);
}

static void
parse_opts(int argc, char **argv, struct utd_opts *o)
{
	char *end;
	int ch;
	long maxunit;

	MEMZERO(o);

	while ((ch = getopt(argc, argv, UTD_OPTION_FLAGS)) != -1) {
		switch (ch) {
#ifdef UTD_DEBUG
		case 'd':
			utd_debug++;
			break;
#endif /* UTD_DEBUG */
		case 'e':
			o->o_eui64 = 1;
			break;
		case 'm':
			if (((maxunit = strtol(optarg, &end, 10)) == LONG_MIN ||
			    maxunit == LONG_MAX) && errno == ERANGE)
				err(EXIT_FAILURE, "%s: strtol", __func__);
			else if (optarg == end) {
				errx(EXIT_FAILURE, "%s: max unit missing",
				    __func__);
			}
			o->o_maxunit = (unsigned int)maxunit;
			break;
		default:
			warnx("unknown option -%c", ch);
			usage();
			break;
		}
	}

	argc -= optind;
	argv += optind;

	switch (argc) {
	case 2:
		o->o_mode = UTD_M_SERVER;
		break;
	case 4:
		o->o_mode = UTD_M_CLIENT;
		break;
	default:
		usage();
		break;
	}

	switch (argc) {
	case 4:
		parse_outer_inner(0, &argv[2], &o->o_remote_outer,
		    &o->o_dst_inner, NULL);
		/*FALLTHROUGH*/
	case 2:
		parse_outer_inner(AI_PASSIVE, &argv[0], &o->o_local_outer,
		    &o->o_inner,
		    (argc == 2) ? &o->o_masklen : NULL);
		break;
	default:
		usage();
		break;
	}
	if (argc == 4 &&
	    (o->o_remote_outer->sa_family != o->o_local_outer->sa_family ||
	     o->o_dst_inner->sa_family != o->o_inner->sa_family)) {
		warnx("local and remote address families must match");
		usage();
	}
}

int
main(int argc, char **argv)
{
	struct utd_client *c;
	struct utd_server *s;
	struct utd_opts o;
	enum loglib_mode mode = LOGLIB_M_SYSLOG;
	int facility = LOG_DAEMON;

	setprogname(argv[0]);

	parse_opts(argc, argv, &o);

#ifdef UTD_DEBUG
	if (utd_debug) {
		mode = LOGLIB_M_STDERR;
		facility = 0;
	} else if (daemon(0, 0) == -1)
		err(EXIT_FAILURE, "%s: daemon failed", __func__);
#else
	daemon(0, 0);
#endif /* UTD_DEBUG */

	event_init();

	if (loglib_start(mode, facility) == -1)
		errx(EXIT_FAILURE, "%s: loglib_start failed", __func__);

	loglib_set_state("all", LOGLIB_SINK_S_ON, 1);

	if (pidfile(getprogname()) == -1)
		loglib_warn("%s: pidfile", __func__);

	switch (o.o_mode) {
	case UTD_M_SERVER:
		if ((s = utd_server_create(&o)) == NULL)
			loglib_err(EXIT_FAILURE, "%s: utd_server_create",
			    __func__);
		break;
	case UTD_M_CLIENT:
		if ((s = utd_server_create(&o)) == NULL)
			loglib_err(EXIT_FAILURE, "%s: utd_server_create",
			    __func__);
		if ((c = utd_local_client_create(s, &o)) == NULL) {
			loglib_err(EXIT_FAILURE, "%s: utd_local_client_create",
			    __func__);
		}
		utd_client_retry(c);
		break;
	default:
		loglib_errx(EXIT_FAILURE, "%s: unknown mode %d", __func__,
		    o.o_mode);
		break;
	}
	if (utd_server_schedule(s) == -1)
		loglib_err(EXIT_FAILURE, "%s: utd_server_schedule", __func__);

	if (event_dispatch() != 0)
		loglib_err(EXIT_FAILURE, "%s: event_dispatch", __func__);

	return 0;
}
/* $Id: utd.c 5026 2008-05-09 17:50:11Z dyoung $ */
/*
 * Copyright (c) 2005, 2006, 2007 David Young.  All rights reserved.
 *
 * This file contains code contributed by David Young.
 *
 * 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.
 * 3. David Young's name may not be used to endorse or promote
 *    products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``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 DAVID
 * YOUNG 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.
 *
 * This material is based upon work partially supported by NSF
 * under Contract No. NSF CNS-0626584.
 */
#include <err.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <unistd.h>

#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>

#include <net/if.h>
#include <net/if_gre.h>

#include <event.h>
#include <ifaddrs.h>
#include <netdb.h>

#include "misc/misc.h"
#include "ssrv/sockaddr.h"
#include "ssrv/ssrv.h"
#include "utd.h"

#ifndef lint
__RCSID("$Id: utd.c 5026 2008-05-09 17:50:11Z dyoung $");
#endif

static void utd_client_cb(int, short, void *);

#ifdef UTD_DEBUG
int utd_debug = 0;
#endif

int
utd_server_schedule(struct utd_server *s)
{
	static const struct timeval sleep_ival = {.tv_sec = 5, .tv_usec = 0};
	static const struct timeval zero = {.tv_sec = 0, .tv_usec = 0};
	struct timeval ival, now;

	if (gettimeofday(&now, NULL) == -1) {
		loglib_warn("%s: gettimeofday", __func__);
		return -1;
	}

	if (timercmp(&s->s_due, &zero, ==))
		s->s_due = now;
	while (timercmp(&s->s_due, &now, <=))
		timeradd(&s->s_due, &sleep_ival, &s->s_due);
	timersub(&s->s_due, &now, &ival);

	signal_add(&s->s_sigchld_ev, NULL);
	signal_add(&s->s_sighup_ev, NULL);
	signal_add(&s->s_sigint_ev, NULL);
	signal_add(&s->s_sigquit_ev, NULL);
	signal_add(&s->s_sigterm_ev, NULL);
	return event_add(&s->s_ev, &ival);
}

static struct utd_client *
utd_client_lookup(struct utd_server *s, struct utd_params *p,
    struct sockaddr *from)
{
	struct utd_client *c;
	in_port_t *fromport, *outerport;

	if ((fromport = sockaddr_portaddr(from)) == NULL)
		return NULL;

	TAILQ_FOREACH(c, &s->s_clients, c_next) {
		if (c->c_state == utd_garbage_state())
			continue;
		if ((outerport = sockaddr_portaddr(c->c_outer)) == NULL)
			continue;
		if (*fromport != *outerport)
			continue;
		if (sockaddr_cmp(c->c_outer, from) == 0)
			break;
	}
	return c;
}

static int
utd_client_send(struct utd_client *c, struct timeval *timo,
    enum utd_seqno seqno)
{
	struct timeval now;
	socklen_t slen;
	const void *p;
	struct utd_param_payload pp;

	pp.pp_magic = htonl(UTD_MAGIC);
	pp.pp_seqno = seqno;

        /* Note well: I purposefully reverse the local and the
         * destination addresses here.  It has to happen once,
         * somewhere, since the local address is the destination
         * on the other host, and vice versa.
	 */
	if ((p = sockaddr_const_addr(c->c_dst_inner, &slen)) == NULL)
		return -1;
	memcpy(&pp.pp_inner[0], p, MIN(slen, sizeof(pp.pp_inner)));

	if ((p = sockaddr_const_addr(c->c_inner, &slen)) == NULL)
		return -1;
	memcpy(&pp.pp_dst_inner[0], p, MIN(slen, sizeof(pp.pp_dst_inner)));

	pp.pp_masklen = c->c_masklen;

	switch (c->c_dst_inner->sa_family) {
	case AF_INET:
		pp.pp_family = UTD_FAMILY_IPV4;
		break;
	case AF_INET6:
		pp.pp_family = UTD_FAMILY_IPV6;
		break;
	default:
		errno = EAFNOSUPPORT;
		return -1;
	}
	if (gettimeofday(&now, NULL) == -1) {
		loglib_warn("%s: gettimeofday", __func__);
		return -1;
	}
	timeradd(&now, timo, &c->c_due);
	event_add(&c->c_ev, timo);
	if (send(c->c_sock, &pp, sizeof(pp), 0) == -1) {
		loglib_warn("%s: send", __func__);
		return -1;
	}
	return 0;
}

int
utd_client_send_answer(struct utd_client *c)
{
	static struct timeval answer_ival = {.tv_sec = 5, .tv_usec = 0};
	int rc;

	UTD_DPRINTF("%s: enter", __func__);
	rc = utd_client_send(c, &answer_ival, UTD_SEQNO_SERVER_PROBERESP);
	UTD_DPRINTF("%s: exit", __func__);
	return rc;
}

int
utd_client_send_ack(struct utd_client *c)
{
	static struct timeval ack_ival = {.tv_sec = 5, .tv_usec = 0};
	int rc;

	UTD_DPRINTF("%s: enter", __func__);
	rc = utd_client_send(c, &ack_ival, UTD_SEQNO_CLIENT_ACK);
	UTD_DPRINTF("%s: exit", __func__);
	return rc;
}

int
utd_tunnel_destroy(struct utd_client *c)
{
	struct ifreq ifr;

	UTD_DPRINTF("%s: enter", __func__);

	MEMZERO(&ifr);

	if (simple_strlcpy(ifr.ifr_name, c->c_name, sizeof(ifr.ifr_name)) == -1)
		return -1;
	if (c->c_sock == -1)
		;
	else if (ioctl(c->c_sock, SIOCIFDESTROY, &ifr) == -1 &&
	         errno != ENXIO) {
		loglib_warn("%s: ioctl(SIOCIFDESTROY)", __func__);
		return -1;
	} else
		UTD_DPRINTF("%s: deleted %s", __func__, ifr.ifr_name);
	c->c_unit = -1;
	memset(c->c_name, 0, sizeof(c->c_name));
	if (c->c_sock != -1) {
		event_del(&c->c_ev);
		event_set(&c->c_ev, c->c_sock, EV_READ, utd_client_cb,
		    (void *)c);
	}
	return 0;
}

static int
utd_tunnel_create(struct utd_client *c)
{
	unsigned int maxunit = MAX(1, c->c_server->s_maxunit);
	unsigned int unit, unit0 = c->c_server->s_unit0;
	size_t power;
	struct ifreq ifr;

	if (maxunit == 1) {
		for (power = 0;
		     power < sizeof(ifr.ifr_name) - sizeof("gre");
		     power++)
			maxunit = MAX(maxunit, maxunit * 10);
	}

	MEMZERO(&ifr);

	event_del(&c->c_ev);
	evtimer_set(&c->c_ev, utd_client_cb, (void *)c);

	unit = unit0;
	do {
		UTD_DPRINTF("%s: trying to create gre%u", __func__, unit);
		if (simple_snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "gre%u",
		                    unit) == -1)
			continue;
		if (simple_strlcpy(c->c_name, ifr.ifr_name,
		                   sizeof(c->c_name)) == -1)
			continue;
		if (ioctl(c->c_sock, SIOCIFCREATE, &ifr) != -1) {
			c->c_server->s_unit0 = unit;
			UTD_DPRINTF("%s: created gre%u", __func__, unit);
			return c->c_unit = unit;
		}
		if (errno != EEXIST) {
			loglib_warn("%s: ioctl(SIOCIFCREATE)", __func__);
			return -1;
		}
	} while ((unit = (unit + 1) % maxunit) != unit0);
	event_set(&c->c_ev, c->c_sock, EV_READ, utd_client_cb, (void *)c);
	errno = EAGAIN;
	return -1;
}

int
utd_client_tunnel(struct utd_client *c, const struct utd_params *p)
{
	int sock;
	struct ifreq ifr;
	struct if_laddrreq iflr;

	MEMZERO(&ifr);
	MEMZERO(&iflr);

	if (utd_tunnel_create(c) == -1)
		return -1;

	if (simple_strlcpy(ifr.ifr_name, c->c_name,
	                   sizeof(ifr.ifr_name)) == -1 ||
	    simple_strlcpy(iflr.iflr_name, c->c_name,
	                   sizeof(iflr.iflr_name)) == -1)
		goto post_tun_err;

	if (sockaddr_cpy((struct sockaddr *)&iflr.addr, sizeof(iflr.addr),
	                 p->p_inner) == NULL)
		goto post_tun_err;

	if (p->p_inner->sa_family == AF_INET6)
		iflr.prefixlen = 128;
	else
		iflr.prefixlen = p->p_masklen;
	UTD_DPRINTF("%s: iflr.prefixlen = %d", __func__, iflr.prefixlen);

	if (sockaddr_cpy((struct sockaddr *)&iflr.dstaddr, sizeof(iflr.dstaddr),
	                 c->c_dst_inner) == NULL)
		goto post_tun_err;

	ifr.ifr_flags = IPPROTO_UDP;
	if (ioctl(c->c_sock, GRESPROTO, &ifr) == -1) {
		loglib_warn("%s: ioctl(GRESPROTO)", __func__);
		goto post_tun_err;
	}

	ifr.ifr_value = c->c_sock;
	if (ioctl(c->c_sock, GRESSOCK, &ifr) == -1) {
		loglib_warn("%s: ioctl(GRESSOCK)", __func__);
		goto post_tun_err;
	}
        /* Need to create a temporary socket here, just in case
         * the address family of c->c_sock is different from the
         * address family of iflr.addr: you cannot add an IPv6
         * address using an IPv4 socket!
	 */
	sock = socket(iflr.addr.ss_family, SOCK_DGRAM, IPPROTO_UDP);
	if (sock == -1) {
		loglib_warn("%s: socket", __func__);
		goto post_tun_err;
	}
	if (ioctl(sock, SIOCALIFADDR, &iflr) == -1) {
		loglib_warn("%s: ioctl(SIOCALIFADDR) %s --> %s prefixlen %u",
		    __func__,
		    sockaddrtoa("%a%%%S,%p",
		        (const struct sockaddr *)&iflr.addr),
		    sockaddrtoa("%a%%%S,%p",
		        (const struct sockaddr *)&iflr.dstaddr),
		    iflr.prefixlen);
		goto post_sock_err;
	}
	(void)close(sock);
	sock = -1;
	UTD_DPRINTF("%s: ioctl(SIOCALIFADDR) %s --> %s prefixlen %u",
	    __func__,
	    sockaddrtoa("%a%%%S", (const struct sockaddr *)&iflr.addr),
	    sockaddrtoa("%a%%%S", (const struct sockaddr *)&iflr.dstaddr),
	    iflr.prefixlen);

	/* XXX Superfluous?  SIOCALIFADDR probably sets IFF_UP. */
	if (ioctl(c->c_sock, SIOCGIFFLAGS, &ifr) == -1) {
		loglib_warn("%s: ioctl(SIOCGIFFLAGS)", __func__);
		goto post_tun_err;
	}
	ifr.ifr_flags |= IFF_UP;
	if (utd_debug > 1)
		ifr.ifr_flags |= IFF_DEBUG;
	if (ioctl(c->c_sock, SIOCSIFFLAGS, &ifr) == -1) {
		loglib_warn("%s: ioctl(SIOCSIFFLAGS)", __func__);
		goto post_tun_err;
	}

	return 0;
post_sock_err:
	(void)close(sock);
post_tun_err:
	utd_tunnel_destroy(c);
	return -1;
}

int
utd_client_send_probe(struct utd_client *c)
{
	static struct timeval probe_ival = {.tv_sec = 5, .tv_usec = 0};
	int rc;

	UTD_DPRINTF("%s: enter", __func__);
	rc = utd_client_send(c, &probe_ival, UTD_SEQNO_CLIENT_PROBEREQ);
	UTD_DPRINTF("%s: exit", __func__);
	return rc;
}

static int
utd_client_schedule(struct utd_client *c, const struct timeval *sleep)
{
	static const struct timeval zero = {.tv_sec = 0, .tv_usec = 0};
	struct timeval ival, now;

	if (gettimeofday(&now, NULL) == -1) {
		loglib_warn("%s: gettimeofday", __func__);
		return -1;
	}

	if (timercmp(&c->c_due, &zero, ==))
		c->c_due = now;
	while (timercmp(&c->c_due, &now, <=))
		timeradd(&c->c_due, sleep, &c->c_due);
	timersub(&c->c_due, &now, &ival);

	return event_add(&c->c_ev, &ival);
}

int
utd_client_ping(struct utd_client *c)
{
	int rc;
	pid_t pid;
	char dst_inner[sizeof("UTD_DST_INNER=0000:1111:2222:3333:4444:5555:"
	                      "6666:7777")],
	     ifname[sizeof("UTD_IFNAME=") + IFNAMSIZ],
	     inner[sizeof("UTD_INNER=0000:1111:2222:3333:4444:5555:6666:7777")],
	     masklen[sizeof("UTD_MASKLEN=128")];
	char *argv[] = {NULL, NULL};
	char *envp[] = {dst_inner, ifname, inner, masklen, NULL};

	rc = sockaddr_snprintf(dst_inner, sizeof(dst_inner), "UTD_DST_INNER=%a",
	    c->c_dst_inner);
	if (rc == -1 || rc >= sizeof(dst_inner))
		return -1;

	rc = simple_snprintf(ifname, sizeof(ifname), "UTD_IFNAME=%s",
	    c->c_name);
	if (rc == -1)
		return -1;

	rc = sockaddr_snprintf(inner, sizeof(inner), "UTD_INNER=%a",
	    c->c_inner);
	if (rc == -1 || rc >= sizeof(inner))
		return -1;

	rc = simple_snprintf(masklen, sizeof(masklen), "UTD_MASKLEN=%" PRIu8,
	    c->c_masklen);
	if (rc == -1)
		return -1;

	if (c->c_pid != -1) {
		loglib_warnx("%s: child %d already running\n", __func__,
		    c->c_pid);
		return -1;
	}

	switch (pid = fork()) {
	case -1:
		c->c_pid = pid;
		return -1;
	case 0:
		if (chdir("/") == -1)
			loglib_err(EXIT_FAILURE, "%s: chdir", __func__);
		(void)closefrom(0);
		argv[0] = c->c_server->s_script;
		if (execve(c->c_server->s_script, argv, envp) == -1)
			loglib_err(EXIT_FAILURE, "%s: execve", __func__);
		return 0;
	default:
		c->c_pid = pid;
		loglib_warnx("%s: started script %s, pid %d", __func__,
		    c->c_server->s_script, pid);
		return 0;
	}
}

int
utd_client_monitor(struct utd_client *c)
{
	struct ifdatareq ifdr;
	struct if_data *ifi;
	u_quad_t ipackets;

	if (simple_strlcpy(ifdr.ifdr_name, c->c_name,
	                   sizeof(ifdr.ifdr_name)) == -1)
		return -1;
	if (ioctl(c->c_sock, SIOCGIFDATA, &ifdr) == -1) {
		loglib_warn("%s: SIOCGIFDATA", __func__);
		return -1;
	}
	ifi = &ifdr.ifdr_data;
	ipackets = ifi->ifi_ipackets -
	    MIN(ifi->ifi_ipackets, ifi->ifi_noproto + ifi->ifi_ierrors);
	if (c->c_ipackets != ipackets) {
		/* Note that wraparound (unlikely!) or SIOCZIFDATA
		 * will cause c_ipackets < ifi_ipackets.
		 */
		c->c_ipackets = ipackets;
		return 0;
	}
	return EAGAIN;
}

static void
utd_client_cb(int fd, short ev, void *arg)
{
	struct timeval now;
	const struct utd_client_state *ostate;
	struct utd_param_payload pp;
	struct utd_client *c = (struct utd_client *)arg;
	union {
		struct sockaddr_storage ss;
		struct sockaddr sa;
	} u;
	socklen_t slen;
	ssize_t nread;

	UTD_DPRINTF("%s: enter %x", __func__, ev);

	/* Always read.  I used to read only if ev == EV_READ, but I
	 * think that we will not get an EV_READ event if there are
	 * received packets queued on a socket belonging to our tunnel
	 * pseudo-interface, and the pseudo-interface has been deleted
	 * out from under us.  At least, netstat -ln indicates that the
	 * receive queue is longish (1983 bytes), but we receive no
	 * EV_READ events.
	 */
	for (;;) {
		/* XXX factor with utd_server_cb */
		slen = sizeof(u.ss);
		nread = recvfrom(c->c_sock, &pp, sizeof(pp), 0, &u.sa, &slen);
		if (nread == -1 && errno == EAGAIN)
			break;
		else if (nread == -1 && errno == EINTR)
			continue;
		else if (nread == -1) {
			loglib_warn("%s: recvfrom(%d, ...)", __func__,
			    c->c_sock);
			utd_client_error(c, errno);
			break;
		} else if (nread < sizeof(pp)) {
			loglib_warnx("%s: payload too short", __func__);
			continue;
		}
		utd_client_input(c, &pp);
	}

	if (ev != EV_TIMEOUT)
		;
	else if (gettimeofday(&now, NULL) == -1)
		loglib_warn("%s: gettimeofday", __func__);
	else if (timercmp(&now, &c->c_due, >=)) {
		ostate = c->c_state;
		if (c->c_state->s_maxtry != 0 &&
		    c->c_try >= c->c_state->s_maxtry) {
			utd_client_timeout(c);
			c->c_try = 0;
		} else {
			utd_client_retry(c);
			if (c->c_state != ostate)
				c->c_try = 0;
			else if (c->c_state->s_maxtry != 0)
				++c->c_try;
		}
	}

	(void)utd_client_schedule(c, &c->c_state->s_delay);
	UTD_DPRINTF("%s: exit", __func__);
}

static int
utd_socket(const struct sockaddr *local_outer)
{
	int on, rc, sock;

	sock = socket(local_outer->sa_family, SOCK_DGRAM, IPPROTO_UDP);
	if (sock == -1)
		return -1;

	on = 1;
        if (ioctl(sock, FIONBIO, &on) == -1) {
		loglib_warn("%s: ioctl(, FIONBIO, )", __func__);
		goto post_sock_err;
	}

	on = 1;
	rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
	if (rc == -1)
		goto post_sock_err;

	on = 1;
	rc = setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
	if (rc == -1)
		goto post_sock_err;

	rc = bind(sock, local_outer, sockaddr_getlen(local_outer));
	if (rc == -1)
		goto post_sock_err;
	return sock;
post_sock_err:
	close(sock);
	return -1;
}

struct utd_client *
utd_common_client_create()
{
	struct utd_client *c;

	if ((c = zmalloc(sizeof(*c))) == NULL) {
		loglib_warn("%s: zmalloc", __func__);
		return NULL;
	}
	c->c_pid = -1;
	return c;
}

static struct sockaddr *
utd_add_eui64(const struct sockaddr *sa)
{
	struct sockaddr *rsa = NULL;
	struct ifaddrs *ifa, *ifap;
	const struct in6_addr *addr, *best = NULL;

	if (getifaddrs(&ifap) == -1)
		return NULL;

	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
		if (ifa->ifa_addr->sa_family != AF_INET6)
			continue;
		addr = &((const struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
		if (!IN6_IS_ADDR_LINKLOCAL(addr))
			continue;
		if (best == NULL ||
		    memcmp(&addr->s6_addr[8], &best->s6_addr[8], 8) > 0)
			best = addr;
	}
	if (best == NULL)
		goto err;
	if ((rsa = sockaddr_dup(sa)) == NULL)
		goto err;
	UTD_DPRINTF("%s: rsa 1 %s", __func__, sockaddrtoa("%a", rsa));
	memcpy(&((struct sockaddr_in6 *)rsa)->sin6_addr.s6_addr[8],
	    &best->s6_addr[8], 8);
	UTD_DPRINTF("%s: rsa 2 %s", __func__, sockaddrtoa("%a", rsa));
err:
	freeifaddrs(ifap);
	return rsa;
}

static int
utd_eui64_applicable(const struct sockaddr *sa, int masklen)
{
	struct sockaddr_storage tmp;

	switch (masklen) {
	case 0:
	case 64:
	case 128:
		break;
	default:
		return 0;
	}

	return sa->sa_family == AF_INET6 &&
	    sockaddr_cpy_apply_masklen((struct sockaddr *)&tmp, sizeof(tmp),
	        sa, 64) != NULL &&
	    sockaddr_cmp((const struct sockaddr *)&tmp, sa) == 0;
}

struct utd_client *
utd_local_client_create(struct utd_server *s, struct utd_opts *o)
{
	struct utd_client *c;

	if ((c = utd_common_client_create()) == NULL)
		return NULL;
	if ((c->c_outer = sockaddr_dup(o->o_remote_outer)) == NULL)
		goto post_c_err;
	c->c_masklen = s->s_masklen;
	if (o->o_eui64 &&
	    sockaddr_cmp(s->s_inner, sockaddr_any(s->s_inner)) != 0 &&
	    utd_eui64_applicable(s->s_inner, s->s_masklen)) {
		if ((c->c_inner = utd_add_eui64(s->s_inner)) == NULL)
			goto post_outer_err;
	} else if ((c->c_inner = sockaddr_dup(s->s_inner)) == NULL)
		goto post_outer_err;
	if ((c->c_dst_inner = sockaddr_dup(o->o_dst_inner)) == NULL)
		goto post_inner_err;
	if ((c->c_sock = utd_socket(o->o_local_outer)) == -1)
		goto post_dst_inner_err;
	if (connect(c->c_sock, c->c_outer, sockaddr_getlen(c->c_outer)) == -1)
		goto post_sock_err;
	c->c_server = s;
	TAILQ_INSERT_TAIL(&s->s_clients, c, c_next);
	c->c_state = utd_local_initstate();
	event_set(&c->c_ev, c->c_sock, EV_READ, utd_client_cb, (void *)c);
	return c;
post_sock_err:
	(void)close(c->c_sock);
	c->c_sock = -1;
post_dst_inner_err:
	sockaddr_free(c->c_dst_inner);
post_inner_err:
	sockaddr_free(c->c_inner);
post_outer_err:
	sockaddr_free(c->c_outer);
post_c_err:
	zfree(c);
	return NULL;
}

int
utd_server_auth_inner(struct utd_server *s, const struct sockaddr *sa)
{
	const struct utd_client *c;

	if (sockaddr_cmp(s->s_inner, sa) == 0) {
		errno = EEXIST;
		return 0;
	}

	if (!is_network_member(s->s_inner, s->s_masklen, sa)) {
		errno = EINVAL;
		loglib_warn("%s: sa %s", __func__,
		    sockaddrtoa("%a port %p scope %S", sa));
		return 0;
	}
	TAILQ_FOREACH(c, &s->s_clients, c_next) {
		if (c->c_state == utd_garbage_state())
			continue;
		if (sockaddr_cmp(sa, c->c_dst_inner) == 0) {
			errno = EAGAIN;
			return 0;
		}
	}
	return 1;
}

static void
increment_addr(uint8_t *next, socklen_t slen, int masklen0)
{
	uint8_t mask;
	int i, masklen;

	for (i = slen - 1; i >= masklen0 / NBBY; i--) {
		masklen = masklen0 - MIN(NBBY * i, masklen0);
		mask = ((uint8_t)1 << (NBBY - masklen)) - 1;
		UTD_DPRINTF("%s: i %d masklen %d mask 0x%02" PRIx8,
		    __func__, i, masklen, mask);

		if ((next[i] & mask) == mask)
			next[i] &= ~mask;
		else {
			next[i]++;
			break;
		}
	}
}

static int
same_prefix(const struct sockaddr *lsa, const struct sockaddr *rsa, int masklen)
{
	struct sockaddr_storage lss, rss;

	if (sockaddr_cpy_apply_masklen((struct sockaddr *)&lss, sizeof(lss),
	    lsa, masklen) == NULL)
		return 0;	/* XXX might be the same,
				 * but there was an error...
				 */
	if (sockaddr_cpy_apply_masklen((struct sockaddr *)&rss, sizeof(rss),
	    rsa, masklen) == NULL)
		return 0;	/* XXX might be the same,
				 * but there was an error...
				 */
	return sockaddr_cmp((struct sockaddr *)&lss,
	                    (struct sockaddr *)&rss) == 0;
}

struct sockaddr *
utd_server_next_inner(struct utd_server *s)
{
	socklen_t slen;
	struct utd_client *c;
	struct sockaddr *first_sa, *next_sa;
	uint8_t *next;

	first_sa = s->s_inner;
	TAILQ_FOREACH_REVERSE(c, &s->s_clients, utd_client_list, c_next) {
		if (same_prefix(c->c_dst_inner, s->s_inner, s->s_masklen)) {
			first_sa = c->c_dst_inner;
			break;
		}
	}

	UTD_DPRINTF("%s: first_sa %s", __func__, sockaddrtoa("%a", first_sa));

	next_sa = sockaddr_dup(first_sa);
	if ((next = sockaddr_addr(next_sa, &slen)) == NULL)
		goto err;

	for (;;) {
		increment_addr(next, slen, s->s_masklen);
		if (sockaddr_cmp(next_sa, first_sa) == 0)
			goto err;
		else if (utd_server_auth_inner(s, next_sa))
			break;
		else if (errno != EAGAIN && errno != EEXIST)
			goto err;
	}
	UTD_DPRINTF("%s: final next_sa %s", __func__, sockaddrtoa("%a",
	    next_sa));
	return next_sa;
err:
	sockaddr_free(next_sa);
	return NULL;
}

static struct utd_client *
utd_remote_client_create(struct utd_server *s, const struct utd_params *p,
    const struct sockaddr *from)
{
	struct utd_client *c;

	if (sockaddr_cmp(s->s_inner, p->p_inner) != 0) {
		loglib_warnx("%s: wrong server inner address, %s", __func__,
		      sockaddrtoa("%s", s->s_inner));
		errno = EINVAL;
		return NULL;
	}
	if ((c = utd_common_client_create()) == NULL)
		return NULL;
	c->c_masklen = s->s_masklen;
	if ((c->c_outer = sockaddr_dup(from)) == NULL)
		goto post_c_err;
	if ((c->c_inner = sockaddr_dup(s->s_inner)) == NULL)
		goto post_outer_err;
	if (sockaddr_cmp(p->p_dst_inner, sockaddr_any(p->p_dst_inner)) == 0) {
		if ((c->c_dst_inner = utd_server_next_inner(s)) == NULL)
			goto post_inner_err;
	} else if (!utd_server_auth_inner(s, p->p_dst_inner) ||
		   (c->c_dst_inner = sockaddr_dup(p->p_dst_inner)) == NULL)
		goto post_inner_err;
	if ((c->c_sock = utd_socket(s->s_outer)) == -1)
		goto post_dst_inner_err;
	if (connect(c->c_sock, c->c_outer, sockaddr_getlen(c->c_outer)) == -1)
		goto post_sock_err;
	c->c_server = s;
	TAILQ_INSERT_TAIL(&s->s_clients, c, c_next);
	c->c_state = utd_remote_initstate();
	event_set(&c->c_ev, c->c_sock, EV_READ, utd_client_cb, (void *)c);
	return c;
post_sock_err:
	(void)close(c->c_sock);
	c->c_sock = -1;
post_dst_inner_err:
	sockaddr_free(c->c_dst_inner);
post_inner_err:
	sockaddr_free(c->c_inner);
post_outer_err:
	sockaddr_free(c->c_outer);
post_c_err:
	zfree(c);
	return NULL;
}

void
utd_params_cleanup(struct utd_params *p)
{
	if (p->p_dst_inner != NULL)
		sockaddr_free(p->p_dst_inner);
	if (p->p_inner != NULL)
		sockaddr_free(p->p_inner);
	p->p_dst_inner = p->p_inner = NULL;
}

int
utd_params_extract(const struct utd_param_payload *pp, struct utd_params *p)
{
	sa_family_t af;
	union anyaddr any;

	MEMZERO(p);

	if (pp->pp_magic != htonl(UTD_MAGIC)) {
		loglib_warnx("%s: bad magic", __func__);
		errno = EINVAL;
		return -1;
	}

	switch (pp->pp_family) {
	case UTD_FAMILY_IPV4:
		af = AF_INET;
		break;
	case UTD_FAMILY_IPV6:
		af = AF_INET6;
		break;
	default:
		loglib_warnx("%s: bad address family", __func__);
		errno = EINVAL;
		return -1;
	}
	memcpy(&any.octets[0], &pp->pp_dst_inner[0],
	    MIN(sizeof(any), sizeof(pp->pp_dst_inner)));
	p->p_dst_inner = sockaddr_create(af, &any, 0);
	if (p->p_dst_inner == NULL)
		goto err;

	p->p_masklen = pp->pp_masklen;

	memcpy(&any.octets[0], &pp->pp_inner[0],
	    MIN(sizeof(any), sizeof(pp->pp_inner)));
	p->p_inner = sockaddr_create(af, &any, 0);
	if (p->p_inner == NULL)
		goto err;
	return 0;
err:
	utd_params_cleanup(p);
	return -1;
}

static void
utd_client_destroy(struct utd_client *c)
{
	event_del(&c->c_ev);
	if (c->c_inner != NULL)
		sockaddr_free(c->c_inner);
	if (c->c_dst_inner != NULL)
		sockaddr_free(c->c_dst_inner);
	if (c->c_outer != NULL)
		sockaddr_free(c->c_outer);
	zfree(c);
}

static void
utd_server_collect_garbage(struct utd_server *s)
{
	struct utd_client *c, *next_c;

	UTD_DPRINTF("%s: enter", __func__);
	for (c = TAILQ_FIRST(&s->s_clients); c != NULL; c = next_c) {
		next_c = TAILQ_NEXT(c, c_next);
		if (c->c_state == utd_garbage_state()) {
			TAILQ_REMOVE(&s->s_clients, c, c_next);
			utd_client_destroy(c);
		}
	}
}

static void
utd_server_shutdown(struct utd_server *s)
{
	struct utd_client *c;

	UTD_DPRINTF("%s: enter", __func__);

	TAILQ_FOREACH(c, &s->s_clients, c_next)
		utd_client_shutdown(c);
	event_del(&s->s_ev);
	event_del(&s->s_sigchld_ev);
	event_del(&s->s_sighup_ev);
	event_del(&s->s_sigint_ev);
	event_del(&s->s_sigquit_ev);
	event_del(&s->s_sigterm_ev);
}

static void
utd_child_cb(int fd, short ev, void *arg)
{
	int rc, status;
	struct utd_client *c;
	struct utd_server *s;

	s = (struct utd_server *)arg;

	TAILQ_FOREACH(c, &s->s_clients, c_next) {
		if (c->c_pid == -1)
			continue;
		switch ((rc = waitpid(c->c_pid, &status, WNOHANG|WUNTRACED))) {
		case 0:
			break;
		default:
			if (WIFSTOPPED(status)) {
				loglib_warnx("%s: pid %d stopped by signal %d, "
				    "restarting",
				    __func__, c->c_pid, WSTOPSIG(status));
				kill(c->c_pid, SIGCONT);
				continue;
			} else if (WIFEXITED(status)) {
				loglib_warnx("%s: pid %d terminated, status %d",
				    __func__, c->c_pid, WEXITSTATUS(status));
			} else if (WIFSIGNALED(status)) {
				loglib_warnx("%s: pid %d terminated, "
				    "signal %d%s",
				    __func__, c->c_pid, WTERMSIG(status),
				    WCOREDUMP(status) ? ", dumped core" : "");
			} else {
				loglib_warnx("%s: pid %d status unknown",
				    __func__, c->c_pid);
				continue;
			}
			/*FALLTHROUGH*/
		case -1:
			if (rc == -1)
				loglib_warn("%s: waitpid", __func__);
			utd_client_join(c);
			c->c_pid = -1;
			break;
		}
	}
}

static void
utd_server_cb(int fd, short ev, void *arg)
{
	struct timeval now;
	struct utd_client *c;
	struct utd_param_payload pp;
	struct utd_params p;
	struct utd_server *s;
	union {
		struct sockaddr_storage ss;
		struct sockaddr sa;
	} u;
	socklen_t slen;
	ssize_t nread;

	UTD_DPRINTF("%s: enter", __func__);

	s = (struct utd_server *)arg;

	if (gettimeofday(&now, NULL) == -1)
		loglib_warn("%s: gettimeofday", __func__);
	else if (timercmp(&now, &s->s_due, >=))
		utd_server_collect_garbage(s);

	if (ev == EV_SIGNAL) {
		utd_server_shutdown(s);
		return;
	}

	while (ev == EV_READ) {
		slen = sizeof(u.ss);
		nread = recvfrom(s->s_sock, &pp, sizeof(pp), 0, &u.sa, &slen);
		UTD_DPRINTF("%s: read %zd", __func__, nread);
		if (nread == -1 && errno == EAGAIN)
			break;
		else if (nread == -1 && errno == EINTR)
			continue;
		else if (nread == -1) {
			loglib_warn("%s: recvfrom(%d, ...)", __func__,
			    s->s_sock);
			break;
		} else if (nread < sizeof(pp)) {
			loglib_warnx("%s: payload too short", __func__);
			continue;
		}
		if (pp.pp_seqno != UTD_SEQNO_CLIENT_PROBEREQ) {
			loglib_warnx("%s: seqno %d", __func__, pp.pp_seqno);
			continue;
		}
		if (utd_params_extract(&pp, &p) == -1) {
			loglib_warn("%s: utd_params_extract", __func__);
			continue;
		}
		if ((c = utd_client_lookup(s, &p, &u.sa)) != NULL) {
			UTD_DPRINTF("%s: received message from known client on "
			    "default socket", __func__);
		} else if ((c = utd_remote_client_create(s, &p, &u.sa)) == NULL)
			loglib_warn("%s: utd_remote_client_create", __func__);
		utd_params_cleanup(&p);
		if (c != NULL)
			utd_client_input(c, &pp);
	}
	if (utd_server_schedule(s) == -1)
		loglib_err(EXIT_FAILURE, "%s: utd_server_schedule", __func__);
}

static void
utd_null_cb(int fd, short ev, void *arg)
{
	UTD_DPRINTF("%s: enter", __func__);
}

struct utd_server *
utd_server_create(struct utd_opts *o)
{
	struct utd_server *s;

	if ((s = zmalloc(sizeof(*s))) == NULL)
		return NULL;

	s->s_maxunit = o->o_maxunit;

	s->s_script =
	    strdup((o->o_script == NULL) ? UTD_SCRIPT_DEFAULT : o->o_script);

	if (s->s_script == NULL)
		goto post_alloc_err;

	TAILQ_INIT(&s->s_clients);
	s->s_masklen = o->o_masklen;

	if ((s->s_inner = sockaddr_dup(o->o_inner)) == NULL)
		goto post_script_err;

	if ((s->s_outer = sockaddr_dup(o->o_local_outer)) == NULL)
		goto post_inner_err;

	if ((s->s_sock = utd_socket(s->s_outer)) == -1)
		goto post_outer_err;

	event_set(&s->s_ev, s->s_sock, EV_READ, utd_server_cb, (void *)s);
	signal_set(&s->s_sigchld_ev, SIGCHLD, utd_child_cb, (void *)s);
	signal_set(&s->s_sighup_ev, SIGHUP, utd_null_cb, (void *)s);
	signal_set(&s->s_sigint_ev, SIGINT, utd_server_cb, (void *)s);
	signal_set(&s->s_sigquit_ev, SIGQUIT, utd_server_cb, (void *)s);
	signal_set(&s->s_sigterm_ev, SIGTERM, utd_server_cb, (void *)s);
	return s;
post_outer_err:
	sockaddr_free(s->s_outer);
post_inner_err:
	sockaddr_free(s->s_inner);
post_script_err:
	free(s);
post_alloc_err:
	zfree(s);
	return NULL;
}
/* $Id: utd.h 5026 2008-05-09 17:50:11Z dyoung $ */
/*
 * Copyright (c) 2006, 2007 David Young.  All rights reserved.
 *
 * This file contains code contributed by David Young.
 *
 * 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.
 * 3. David Young's name may not be used to endorse or promote
 *    products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``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 DAVID
 * YOUNG 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.
 *
 * This material is based upon work partially supported by NSF
 * under Contract No. NSF CNS-0626584.
 */
#include <sys/queue.h>
#include <sys/time.h>
#include <inttypes.h>
#include <sys/socket.h>
#include <event.h>

#ifdef UTD_DEBUG
extern int utd_debug;
#define	UTD_DPRINTF(__fmt, ...)					\
	do {							\
		if (utd_debug)					\
			loglib_warnx(__fmt, __VA_ARGS__);	\
	} while (/*CONSTCOND*/0)
#else
#define	UTD_DPRINTF(__fmt, ...)	do { } while (/*CONSTCOND*/0)
#endif

#define	UTD_SCRIPT_DEFAULT	"/etc/utd-script"

struct utd_params {
	struct sockaddr	*p_dst_inner;
	struct sockaddr	*p_inner;
	uint8_t		p_masklen;
};

struct utd_param_payload {
	uint32_t	pp_magic;
	uint8_t		pp_family;
	uint8_t		pp_seqno;
	uint8_t		pp_masklen;
	uint8_t		pp_dst_inner[16];
	uint8_t		pp_inner[16];
#define	UTD_MAGIC	0x5f757464	/* "_utd" */
#define	UTD_FAMILY_IPV4	0
#define	UTD_FAMILY_IPV6	1
} __attribute__((__packed__));

struct utd_server {
	char				*s_script;
	TAILQ_HEAD(utd_client_list, utd_client)	s_clients;
	int				s_masklen;
	struct sockaddr			*s_inner;
	struct sockaddr			*s_outer;
	int				s_sock;
	unsigned int			s_unit0;
	unsigned int			s_maxunit;
	struct event			s_ev;
	struct event			s_sigchld_ev;
	struct event			s_sighup_ev;
	struct event			s_sigint_ev;
	struct event			s_sigquit_ev;
	struct event			s_sigterm_ev;
	struct timeval			s_due;
};

typedef void (*utd_client_cb_t)(int, short, void *);

#define	UTD_CLIENT_STATE_CHILD_DECL(__name)				\
	const struct utd_client_state *__name(				\
	    const struct utd_client_state *, struct utd_client *)
#define	UTD_CLIENT_STATE_INPUT_DECL(__name)				\
	const struct utd_client_state *__name(				\
	    const struct utd_client_state *, struct utd_client *c,	\
	    const struct utd_param_payload *)
#define	UTD_CLIENT_STATE_RETRY_DECL(__name)				\
	const struct utd_client_state *__name(				\
	    const struct utd_client_state *, struct utd_client *)
#define	UTD_CLIENT_STATE_SHUTDOWN_DECL(__name)				\
	const struct utd_client_state *__name(				\
	    const struct utd_client_state *, struct utd_client *)
#define	UTD_CLIENT_STATE_TIMEOUT_DECL(__name)				\
	const struct utd_client_state *__name(				\
	    const struct utd_client_state *, struct utd_client *)
#define	UTD_CLIENT_STATE_ERROR_DECL(__name)				\
	const struct utd_client_state *__name(				\
	    const struct utd_client_state *, struct utd_client *, int)

typedef UTD_CLIENT_STATE_CHILD_DECL((*utd_client_state_join_t));
typedef UTD_CLIENT_STATE_INPUT_DECL((*utd_client_state_input_t));
typedef UTD_CLIENT_STATE_RETRY_DECL((*utd_client_state_retry_t));
typedef UTD_CLIENT_STATE_SHUTDOWN_DECL((*utd_client_state_shutdown_t));
typedef UTD_CLIENT_STATE_TIMEOUT_DECL((*utd_client_state_timeout_t));
typedef UTD_CLIENT_STATE_ERROR_DECL((*utd_client_state_error_t));

struct utd_client_state {
	const char			*s_name;
	utd_client_state_join_t		s_join;
	utd_client_state_input_t	s_input;
	utd_client_state_retry_t	s_retry;
	utd_client_state_shutdown_t	s_shutdown;
	utd_client_state_timeout_t	s_timeout;
	utd_client_state_error_t	s_error;
	struct timeval			s_delay;
	int				s_maxtry;
};

struct utd_client {
	u_quad_t			c_ipackets;
	struct utd_server		*c_server;
	TAILQ_ENTRY(utd_client)		c_next;
	struct sockaddr			*c_inner;
	uint8_t				c_masklen;
	struct sockaddr			*c_dst_inner;
	struct sockaddr			*c_outer;
	int				c_sock;
	struct event			c_ev;
	const struct utd_client_state	*c_state;
	struct timeval			c_due;
	unsigned int			c_unit;
	char				c_name[IFNAMSIZ];
	int				c_try;
	pid_t				c_pid;
};

enum utd_seqno {
	  UTD_SEQNO_CLIENT_PROBEREQ = 0
	, UTD_SEQNO_SERVER_PROBERESP = 1
	, UTD_SEQNO_CLIENT_ACK = 2
	, UTD_SEQNO_SERVER_ACK = 3
};

enum utd_mode {
	  UTD_M_CLIENT = 0
	, UTD_M_SERVER
};

struct utd_opts {
	unsigned int	o_eui64:1;
	const char	*o_script;
	enum utd_mode	o_mode;
	struct sockaddr	*o_inner;
	int		o_masklen;
	struct sockaddr	*o_local_outer;
	struct sockaddr	*o_dst_inner;
	struct sockaddr	*o_remote_outer;
	unsigned int	o_maxunit;
};

struct utd_server *utd_server_create(struct utd_opts *);
int utd_server_schedule(struct utd_server *);
struct utd_client *utd_local_client_create(struct utd_server *,
    struct utd_opts *);
const struct utd_client_state *utd_garbage_state(void);
const struct utd_client_state *utd_local_initstate(void);
const struct utd_client_state *utd_remote_initstate(void);
int utd_client_monitor(struct utd_client *);
int utd_client_ping(struct utd_client *);
int utd_client_send_ack(struct utd_client *);
int utd_client_send_answer(struct utd_client *);
int utd_client_send_probe(struct utd_client *);
int utd_client_tunnel(struct utd_client *, const struct utd_params *);
int utd_params_extract(const struct utd_param_payload *, struct utd_params *);
int utd_tunnel_destroy(struct utd_client *);
void utd_params_cleanup(struct utd_params *);

static inline void
utd_state_transition(struct utd_client *c, const struct utd_client_state *s,
    const char *reason)
{
	loglib_warnx("%s: %s (%s) %s -> %s (%s)", c->c_name,
	    sockaddrtoa("%a %p", c->c_outer), sockaddrtoa("%a", c->c_dst_inner),
	    c->c_state->s_name, s->s_name, reason);
	c->c_state = s;
}

static inline void
utd_client_error(struct utd_client *c, int error)
{
	utd_state_transition(c, (*c->c_state->s_error)(c->c_state, c, error),
	    "error");
}

static inline void
utd_client_timeout(struct utd_client *c)
{
	utd_state_transition(c, (*c->c_state->s_timeout)(c->c_state, c),
	    "timeout");
}

static inline void
utd_client_input(struct utd_client *c, const struct utd_param_payload *pp)
{
	utd_state_transition(c, (*c->c_state->s_input)(c->c_state, c, pp),
	    "input");
}

static inline void
utd_client_shutdown(struct utd_client *c)
{
	utd_state_transition(c, (*c->c_state->s_shutdown)(c->c_state, c),
	    "shutdown");
}

static inline void
utd_client_join(struct utd_client *c)
{
	utd_state_transition(c, (*c->c_state->s_join)(c->c_state, c), "join");
}

static inline void
utd_client_retry(struct utd_client *c)
{
	utd_state_transition(c, (*c->c_state->s_retry)(c->c_state, c), "retry");
}
/* $Id: utd_sm.c 5026 2008-05-09 17:50:11Z dyoung $ */
/*
 * Copyright (c) 2005, 2006, 2007 David Young.  All rights reserved.
 *
 * This file contains code contributed by David Young.
 *
 * 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.
 * 3. David Young's name may not be used to endorse or promote
 *    products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``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 DAVID
 * YOUNG 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.
 *
 * This material is based upon work partially supported by NSF
 * under Contract No. NSF CNS-0626584.
 */
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <unistd.h>

#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/time.h>

#include <net/if.h>
#include <net/if_gre.h>

#include <event.h>
#include <netdb.h>

#include "misc/misc.h"
#include "ssrv/sockaddr.h"
#include "ssrv/ssrv.h"
#include "utd.h"

#ifndef lint
__RCSID("$Id: utd_sm.c 5026 2008-05-09 17:50:11Z dyoung $");
#endif

static UTD_CLIENT_STATE_CHILD_DECL(joining_join);
static UTD_CLIENT_STATE_CHILD_DECL(null_join);

static UTD_CLIENT_STATE_INPUT_DECL(local_probing_input);
static UTD_CLIENT_STATE_INPUT_DECL(null_input);
static UTD_CLIENT_STATE_INPUT_DECL(remote_ackwait_input);
static UTD_CLIENT_STATE_INPUT_DECL(remote_answering_input);

static UTD_CLIENT_STATE_RETRY_DECL(local_active_retry);
static UTD_CLIENT_STATE_RETRY_DECL(local_inactive_retry);
static UTD_CLIENT_STATE_RETRY_DECL(local_probing_retry);
static UTD_CLIENT_STATE_RETRY_DECL(null_retry);
static UTD_CLIENT_STATE_RETRY_DECL(remote_active_retry);
static UTD_CLIENT_STATE_RETRY_DECL(remote_answering_retry);
static UTD_CLIENT_STATE_RETRY_DECL(remote_inactive_retry);

static UTD_CLIENT_STATE_TIMEOUT_DECL(full_shutdown);
static UTD_CLIENT_STATE_TIMEOUT_DECL(partial_shutdown);
static UTD_CLIENT_STATE_TIMEOUT_DECL(null_shutdown);

static UTD_CLIENT_STATE_TIMEOUT_DECL(local_inactive_timeout);
static UTD_CLIENT_STATE_TIMEOUT_DECL(null_timeout);
static UTD_CLIENT_STATE_TIMEOUT_DECL(remote_inactive_timeout);

static UTD_CLIENT_STATE_ERROR_DECL(full_error);
static UTD_CLIENT_STATE_ERROR_DECL(null_error);
static UTD_CLIENT_STATE_ERROR_DECL(partial_error);

static const struct utd_client_state remote_answering = {
	  .s_join	= null_join
	, .s_name	= "remote_answering"
	, .s_input	= remote_answering_input
	, .s_retry	= remote_answering_retry
	, .s_shutdown	= partial_shutdown
	, .s_timeout	= partial_shutdown
	, .s_error	= partial_error
	, .s_delay	= {.tv_sec = 5, .tv_usec = 0}
	, .s_maxtry	= 3
};

static const struct utd_client_state remote_ackwait = {
	  .s_join	= null_join
	, .s_name	= "remote_ackwait"
	, .s_input	= remote_ackwait_input
	, .s_retry	= remote_answering_retry
	, .s_shutdown	= partial_shutdown
	, .s_timeout	= partial_shutdown
	, .s_error	= partial_error
	, .s_delay	= {.tv_sec = 5, .tv_usec = 0}
	, .s_maxtry	= 3
};

static const struct utd_client_state joining = {
	  .s_join	= joining_join
	, .s_name	= "joining"
	, .s_input	= null_input
	, .s_retry	= null_retry
	, .s_shutdown	= null_shutdown
	, .s_timeout	= null_timeout
	, .s_error	= null_error
	, .s_delay	= {.tv_sec = 60, .tv_usec = 0}
	, .s_maxtry	= 0
};

static const struct utd_client_state garbage = {
	  .s_join	= null_join
	, .s_name	= "garbage"
	, .s_input	= null_input
	, .s_retry	= null_retry
	, .s_shutdown	= null_shutdown
	, .s_timeout	= null_timeout
	, .s_error	= null_error
	, .s_delay	= {.tv_sec = 60, .tv_usec = 0}
	, .s_maxtry	= 0
};

static const struct utd_client_state remote_active = {
	  .s_join	= null_join
	, .s_name	= "remote_active"
	, .s_input	= null_input
	, .s_retry	= remote_active_retry
	, .s_shutdown	= full_shutdown
	, .s_timeout	= null_timeout
	, .s_error	= full_error
	, .s_delay	= {.tv_sec = 15, .tv_usec = 0}
	, .s_maxtry	= 0
};

static const struct utd_client_state remote_inactive = {
	  .s_join	= null_join
	, .s_name	= "remote_inactive"
	, .s_input	= null_input
	, .s_retry	= remote_inactive_retry
	, .s_shutdown	= full_shutdown
	, .s_timeout	= remote_inactive_timeout
	, .s_error	= full_error
	, .s_delay	= {.tv_sec = 15, .tv_usec = 0}
	, .s_maxtry	= 3
};

static const struct utd_client_state local_probing = {
	  .s_join	= null_join
	, .s_name	= "local_probing"
	, .s_input	= local_probing_input
	, .s_retry	= local_probing_retry
	, .s_shutdown	= partial_shutdown
	, .s_timeout	= null_timeout
	, .s_error	= null_error
	, .s_delay	= {.tv_sec = 5, .tv_usec = 0}
	, .s_maxtry	= 0
};

static const struct utd_client_state local_active = {
	  .s_join	= null_join
	, .s_name	= "local_active"
	, .s_input	= null_input
	, .s_retry	= local_active_retry
	, .s_shutdown	= full_shutdown
	, .s_timeout	= null_timeout
	, .s_error	= null_error
	, .s_delay	= {.tv_sec = 15, .tv_usec = 0}
	, .s_maxtry	= 0
};

/* XXX Check interface statistics, re-send client ACK a few times
 * if no tunnel traffic flows from server to client, then switch
 * to probing state.
 */
static const struct utd_client_state local_inactive = {
	  .s_join	= null_join
	, .s_name	= "local_inactive"
	, .s_input	= null_input
	, .s_retry	= local_inactive_retry
	, .s_shutdown	= full_shutdown
	, .s_timeout	= local_inactive_timeout
	, .s_error	= null_error
	, .s_delay	= {.tv_sec = 15, .tv_usec = 0}
	, .s_maxtry	= 3
};

const struct utd_client_state *
full_shutdown(const struct utd_client_state *s, struct utd_client *c)
{
	if (utd_tunnel_destroy(c) == -1)
		warn("%s: utd_tunnel_destroy", __func__);
	return partial_shutdown(s, c);
}

const struct utd_client_state *
full_error(const struct utd_client_state *s, struct utd_client *c, int error)
{
	return full_shutdown(s, c);
}

const struct utd_client_state *
partial_error(const struct utd_client_state *s, struct utd_client *c, int error)
{
	return partial_shutdown(s, c);
}

const struct utd_client_state *
partial_shutdown(const struct utd_client_state *s, struct utd_client *c)
{
	if (c->c_sock != -1) {
		close(c->c_sock);
		c->c_sock = -1;
	}
	return null_shutdown(s, c);
}

static const struct utd_client_state *
null_shutdown(const struct utd_client_state *s, struct utd_client *c)
{
	UTD_DPRINTF("%s: enter", __func__);
	event_del(&c->c_ev);
	return (c->c_pid == -1) ? &garbage : &joining;
}

static const struct utd_client_state *
remote_answering_input(const struct utd_client_state *s, struct utd_client *c,
    const struct utd_param_payload *pp)
{
	UTD_DPRINTF("%s: enter", __func__);
	if (pp->pp_seqno != UTD_SEQNO_CLIENT_PROBEREQ) {
		warnx("%s: seqno %d", __func__, pp->pp_seqno);
		return s;
	}
	utd_client_send_answer(c);
	return &remote_ackwait;
}

static const struct utd_client_state *
remote_ackwait_input(const struct utd_client_state *s, struct utd_client *c,
    const struct utd_param_payload *pp)
{
	int rc;
	struct utd_params p;

	UTD_DPRINTF("%s: enter", __func__);
	switch (pp->pp_seqno) {
	case UTD_SEQNO_CLIENT_PROBEREQ:
		utd_client_send_answer(c);
		return s;
	case UTD_SEQNO_CLIENT_ACK:
		utd_params_extract(pp, &p);
		rc = utd_client_tunnel(c, &p);
		utd_params_cleanup(&p);
		if (rc == -1)
			return s;
		return &remote_inactive;
	default:
		warnx("%s: seqno %d", __func__, pp->pp_seqno);
		return s;
	}
}

static const struct utd_client_state *
remote_answering_retry(const struct utd_client_state *s, struct utd_client *c)
{
	UTD_DPRINTF("%s: enter", __func__);
	utd_client_send_answer(c);
	return s;
}

static const struct utd_client_state *
remote_active_retry(const struct utd_client_state *s, struct utd_client *c)
{
	UTD_DPRINTF("%s: enter", __func__);
	if (utd_client_monitor(c) == 0)
		return s;
	(void)utd_client_ping(c);
	return &remote_inactive;
}

static const struct utd_client_state *
remote_inactive_retry(const struct utd_client_state *s, struct utd_client *c)
{
	UTD_DPRINTF("%s: enter", __func__);
	if (utd_client_monitor(c) == 0)
		return &remote_active;
	(void)utd_client_ping(c);
	return s;
}

static const struct utd_client_state *
remote_inactive_timeout(const struct utd_client_state *s, struct utd_client *c)
{
	UTD_DPRINTF("%s: enter", __func__);
	UTD_DPRINTF("%s: client timed out", __func__);
	if (utd_tunnel_destroy(c) == -1)
		warn("%s: utd_tunnel_destroy", __func__);
	return partial_shutdown(s, c);
}

static const struct utd_client_state *
null_input(const struct utd_client_state *s, struct utd_client *c,
    const struct utd_param_payload *pp)
{
	UTD_DPRINTF("%s: enter", __func__);
	return s;
}

static const struct utd_client_state *
null_retry(const struct utd_client_state *s, struct utd_client *c)
{
	UTD_DPRINTF("%s: enter", __func__);
	return s;
}

static const struct utd_client_state *
joining_join(const struct utd_client_state *s, struct utd_client *c)
{
	UTD_DPRINTF("%s: enter", __func__);
	return &garbage;
}

static const struct utd_client_state *
null_join(const struct utd_client_state *s, struct utd_client *c)
{
	UTD_DPRINTF("%s: enter", __func__);
	return s;
}

static const struct utd_client_state *
null_error(const struct utd_client_state *s, struct utd_client *c, int error)
{
	UTD_DPRINTF("%s: enter, %d", __func__, error);
	return s;
}

static const struct utd_client_state *
null_timeout(const struct utd_client_state *s, struct utd_client *c)
{
	UTD_DPRINTF("%s: enter", __func__);
	return s;
}

static const struct utd_client_state *
local_probing_retry(const struct utd_client_state *s, struct utd_client *c)
{
	UTD_DPRINTF("%s: enter", __func__);
	utd_client_send_probe(c);
	return s;
}

static const struct utd_client_state *
local_probing_input(const struct utd_client_state *s, struct utd_client *c,
    const struct utd_param_payload *pp)
{
	int rc;
	struct utd_params p;

	UTD_DPRINTF("%s: enter", __func__);
	if (pp->pp_seqno != UTD_SEQNO_SERVER_PROBERESP) {
		warnx("%s: seqno %d", __func__, pp->pp_seqno);
		return s;
	}
	utd_params_extract(pp, &p);
	c->c_masklen = p.p_masklen;
	rc = utd_client_tunnel(c, &p);
	utd_params_cleanup(&p);
	if (rc == -1)
		return s;
	utd_client_send_ack(c);
	return &local_active;
}

static const struct utd_client_state *
local_active_retry(const struct utd_client_state *s, struct utd_client *c)
{
	UTD_DPRINTF("%s: enter", __func__);
	if (utd_client_monitor(c) == 0)
		return s;
	utd_client_send_ack(c);
	return &local_inactive;
}

static const struct utd_client_state *
local_inactive_retry(const struct utd_client_state *s, struct utd_client *c)
{
	UTD_DPRINTF("%s: enter", __func__);
	if (utd_client_monitor(c) == 0)
		return &local_active;
	utd_client_send_ack(c);
	return s;
}

static const struct utd_client_state *
local_inactive_timeout(const struct utd_client_state *s, struct utd_client *c)
{
	UTD_DPRINTF("%s: enter", __func__);
	utd_tunnel_destroy(c);
	utd_client_send_probe(c);
	return &local_probing;
}

const struct utd_client_state *
utd_remote_initstate(void)
{
	return &remote_answering;
}

const struct utd_client_state *
utd_local_initstate(void)
{
	return &local_probing;
}

const struct utd_client_state *
utd_garbage_state(void)
{
	return &garbage;
}
#!/bin/sh
#
# $Id: utd 4201 2006-09-14 07:40:20Z dyoung $
#
# $NetBSD: ntpd,v 1.12 2004/03/31 18:01:07 fredb Exp $
#

# PROVIDE: utd
# REQUIRE: route_conf

. /etc/rc.subr

name="utd"
rcvar=$name
command="/usr/sbin/${name}"
pidfile="/var/run/${name}.pid"

load_rc_config $name
run_rc_command "$1"
#!/bin/sh
#
# $Id: utd6 4266 2006-09-26 05:41:07Z dyoung $
#
# $NetBSD: ntpd,v 1.12 2004/03/31 18:01:07 fredb Exp $
#

# PROVIDE: utd6
# REQUIRE: route_conf

. /etc/rc.subr

name="utd6"
rcvar=$name
command="/usr/sbin/${name}"
pidfile="/var/run/${name}.pid"

load_rc_config $name
run_rc_command "$1"
v6_conc_outer=${conc_outer:-64.198.255.12,2524}
v6_conc_inner=${conc_inner:-2002:40c6:ff0c:0001::1}
conc_outer=${conc_outer:-64.198.255.12,2525}
conc_inner=${conc_inner:-192.168.49.1}
local_outer=${local_outer:-0.0.0.0,0}
local_inner=${local_inner:-0.0.0.0}
v6_local_inner=${v6_local_inner:-2002:40c6:ff0c:1::}
#utd_flags="-d"
#utd6_flags="-d"

utd_flags="${utd_flags:-} -m 2"  
utd6_flags="${utd6_flags:-} -m 2 -e"

for iface in $(ifconfig -l); do
	case $(/sbin/get_config_val $iface ieee80211_role) in
	backhaul)
		local_outer=$(/sbin/get_config_val $iface ipv4_address),0
		break
		;;
	*)
		continue
		;;
	esac
done

utd_flags="${utd_flags:-} ${local_outer} ${local_inner}"
utd_flags="${utd_flags:-} ${conc_outer} ${conc_inner}"
utd=YES

utd6_flags="${utd6_flags:-} ${local_outer} ${v6_local_inner}"
utd6_flags="${utd6_flags:-} ${v6_conc_outer} ${v6_conc_inner}"
utd6=YES
#!/bin/sh
# $Id: utd-script 4810 2007-09-19 04:11:53Z dyoung $

if [ ! -z "${UTD_DST_INNER:-}" ]; then
	for p in '/sbin/ping -w 1' /sbin/ping6 ; do
		$p -i .2 -c 4 -n ${UTD_DST_INNER} && break
	done
fi > /dev/null 2>&1
exit 0


Home | Main Index | Thread Index | Old Index