Beispiel #1
0
int
c_do_connect(struct connection *c, enum c_event ev)
{
	if (c->fd == -1) {
		log_warnx("connect(%s), lost socket",
		    log_sockaddr(&c->config.TargetAddr));
		session_fsm(c->session, SESS_EV_CONN_FAIL, c, 0);
		return CONN_FREE;
	}
	if (c->config.LocalAddr.ss_len != 0) {
		if (bind(c->fd, (struct sockaddr *)&c->config.LocalAddr,
		    c->config.LocalAddr.ss_len) == -1) {
			log_warn("bind(%s)",
			    log_sockaddr(&c->config.LocalAddr));
			session_fsm(c->session, SESS_EV_CONN_FAIL, c, 0);
			return CONN_FREE;
		}
	}
	if (connect(c->fd, (struct sockaddr *)&c->config.TargetAddr,
	    c->config.TargetAddr.ss_len) == -1) {
		if (errno == EINPROGRESS) {
			event_add(&c->wev, NULL);
			event_add(&c->ev, NULL);
			return CONN_XPT_WAIT;
		} else {
			log_warn("connect(%s)",
			    log_sockaddr(&c->config.TargetAddr));
			session_fsm(c->session, SESS_EV_CONN_FAIL, c, 0);
			return CONN_FREE;
		}
	}
	event_add(&c->ev, NULL);
	/* move forward */
	return c_do_login(c, CONN_EV_CONNECTED);
}
Beispiel #2
0
void
client_log_error(struct ntp_peer *peer, const char *operation, int error)
{
    const char *address;

    address = log_sockaddr((struct sockaddr *)&peer->addr->ss);
    if (peer->lasterror == error) {
        log_debug("%s %s: %s", operation, address, strerror(error));
        return;
    }
    peer->lasterror = error;
    log_warn("%s %s", operation, address);
}
Beispiel #3
0
int
ntp_getmsg(struct sockaddr *sa, char *p, ssize_t len, struct ntp_msg *msg)
{
	if (len != NTP_MSGSIZE_NOAUTH && len != NTP_MSGSIZE) {
		log_debug("malformed packet received from %s",
		    log_sockaddr(sa));
		return (-1);
	}

	memcpy(msg, p, sizeof(*msg));

	return (0);
}
Beispiel #4
0
void
log_conn_attempt(const struct peer *peer, struct sockaddr *sa)
{
	char		*p;
	const char	*b;

	if (peer == NULL) {	/* connection from non-peer, drop */
		b = log_sockaddr(sa);
		logit(LOG_INFO, "connection from non-peer %s refused", b);
	} else {
		p = log_fmt_peer(&peer->conf);
		logit(LOG_INFO, "Connection attempt from %s while session is "
		    "in state %s", p, statenames[peer->state]);
		free(p);
	}
}
Beispiel #5
0
void
print_mainconf(struct bgpd_config *conf)
{
	struct in_addr		 ina;
	struct listen_addr	*la;

	printf("AS %s", log_as(conf->as));
	if (conf->as > USHRT_MAX && conf->short_as != AS_TRANS)
		printf(" %u", conf->short_as);
	ina.s_addr = conf->bgpid;
	printf("\nrouter-id %s\n", inet_ntoa(ina));

	printf("socket \"%s\"\n", conf->csock);
	if (conf->rcsock)
		printf("socket \"%s\" restricted\n", conf->rcsock);
	if (conf->holdtime)
		printf("holdtime %u\n", conf->holdtime);
	if (conf->min_holdtime)
		printf("holdtime min %u\n", conf->min_holdtime);
	if (conf->connectretry)
		printf("connect-retry %u\n", conf->connectretry);

	if (conf->flags & BGPD_FLAG_NO_EVALUATE)
		printf("route-collector yes\n");

	if (conf->flags & BGPD_FLAG_DECISION_ROUTEAGE)
		printf("rde route-age evaluate\n");

	if (conf->flags & BGPD_FLAG_DECISION_MED_ALWAYS)
		printf("rde med compare always\n");

	if (conf->log & BGPD_LOG_UPDATES)
		printf("log updates\n");

	TAILQ_FOREACH(la, conf->listen_addrs, entry)
		printf("listen on %s\n",
		    log_sockaddr((struct sockaddr *)&la->sa));

	if (conf->flags & BGPD_FLAG_NEXTHOP_BGP)
		printf("nexthop qualify via bgp\n");
	if (conf->flags & BGPD_FLAG_NEXTHOP_DEFAULT)
		printf("nexthop qualify via default\n");
	printf("fib-priority %hhu", conf->fib_priority);
}
Beispiel #6
0
void
log_conn_attempt(const struct peer *peer, struct sockaddr *sa)
{
	char		*p;
	const char	*b;

	if (peer == NULL) {	/* connection from non-peer, drop */
		b = log_sockaddr(sa);
		logit(LOG_INFO, "connection from non-peer %s refused", b);
	} else {
		/* only log if there is a chance that the session may come up */
		if (peer->conf.down && peer->state == STATE_IDLE)
			return;
		p = log_fmt_peer(&peer->conf);
		logit(LOG_INFO, "Connection attempt from %s while session is "
		    "in state %s", p, statenames[peer->state]);
		free(p);
	}
}
Beispiel #7
0
void
conn_write_dispatch(int fd, short event, void *arg)
{
	struct connection *c = arg;
	ssize_t n;
	int error;
	socklen_t len;

	if (!(event & EV_WRITE)) {
		log_debug("spurious write call");
		return;
	}

	switch (c->state) {
	case CONN_XPT_WAIT:
		len = sizeof(error);
		if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR,
		    &error, &len) == -1 || (errno = error)) {
			log_warn("connect to %s failed",
			    log_sockaddr(&c->config.TargetAddr));
			conn_fsm(c, CONN_EV_FAIL);
			return;
		}
		conn_fsm(c, CONN_EV_CONNECTED);
		break;
	default:
		if ((n = pdu_write(c)) == -1) {
			log_warn("pdu_write");
			conn_fsm(c, CONN_EV_FAIL);
			return;
		}
		if (n == 0) {    /* connection closed */
			conn_fsm(c, CONN_EV_CLOSED);
			return;
		}

		/* check if there is more to send */
		if (pdu_pending(c))
			event_add(&c->wev, NULL);
	}
}
Beispiel #8
0
int
client_dispatch(struct ntp_peer *p, u_int8_t settime)
{
    struct ntp_msg		 msg;
    struct msghdr		 somsg;
    struct iovec		 iov[1];
    struct timeval		 tv;
    char			 buf[NTP_MSGSIZE];
    union {
        struct cmsghdr	hdr;
        char		buf[CMSG_SPACE(sizeof(tv))];
    } cmsgbuf;
    struct cmsghdr		*cmsg;
    ssize_t			 size;
    double			 T1, T2, T3, T4;
    time_t			 interval;

    bzero(&somsg, sizeof(somsg));
    iov[0].iov_base = buf;
    iov[0].iov_len = sizeof(buf);
    somsg.msg_iov = iov;
    somsg.msg_iovlen = 1;
    somsg.msg_control = cmsgbuf.buf;
    somsg.msg_controllen = sizeof(cmsgbuf.buf);

    T4 = getoffset();
    if ((size = recvmsg(p->query->fd, &somsg, 0)) == -1) {
        if (errno == EHOSTUNREACH || errno == EHOSTDOWN ||
                errno == ENETUNREACH || errno == ENETDOWN ||
                errno == ECONNREFUSED || errno == EADDRNOTAVAIL ||
                errno == ENOPROTOOPT || errno == ENOENT) {
            client_log_error(p, "recvmsg", errno);
            set_next(p, error_interval());
            return (0);
        } else
            fatal("recvfrom");
    }

    if (somsg.msg_flags & MSG_TRUNC) {
        client_log_error(p, "recvmsg packet", EMSGSIZE);
        set_next(p, error_interval());
        return (0);
    }

    if (somsg.msg_flags & MSG_CTRUNC) {
        client_log_error(p, "recvmsg control data", E2BIG);
        set_next(p, error_interval());
        return (0);
    }

#ifdef HAVE_RTABLE
    if (p->rtable != -1 &&
            setsockopt(p->query->fd, SOL_SOCKET, SO_RTABLE, &p->rtable,
                       sizeof(p->rtable)) == -1)
        fatal("client_dispatch setsockopt SO_RTABLE");
#endif

    for (cmsg = CMSG_FIRSTHDR(&somsg); cmsg != NULL;
            cmsg = CMSG_NXTHDR(&somsg, cmsg)) {
        if (cmsg->cmsg_level == SOL_SOCKET &&
                cmsg->cmsg_type == SCM_TIMESTAMP) {
            memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));
            T4 += tv.tv_sec + JAN_1970 + 1.0e-6 * tv.tv_usec;
            break;
        }
    }

    if (T4 < JAN_1970) {
        client_log_error(p, "recvmsg control format", EBADF);
        set_next(p, error_interval());
        return (0);
    }

    ntp_getmsg((struct sockaddr *)&p->addr->ss, buf, size, &msg);

    if (msg.orgtime.int_partl != p->query->msg.xmttime.int_partl ||
            msg.orgtime.fractionl != p->query->msg.xmttime.fractionl)
        return (0);

    if ((msg.status & LI_ALARM) == LI_ALARM || msg.stratum == 0 ||
            msg.stratum > NTP_MAXSTRATUM) {
        char s[16];

        if ((msg.status & LI_ALARM) == LI_ALARM) {
            strlcpy(s, "alarm", sizeof(s));
        } else if (msg.stratum == 0) {
            /* Kiss-o'-Death (KoD) packet */
            strlcpy(s, "KoD", sizeof(s));
        } else if (msg.stratum > NTP_MAXSTRATUM) {
            snprintf(s, sizeof(s), "stratum %d", msg.stratum);
        }
        interval = error_interval();
        set_next(p, interval);
        log_info("reply from %s: not synced (%s), next query %llds",
                 log_sockaddr((struct sockaddr *)&p->addr->ss), s,
                 (long long)interval);
        return (0);
    }

    /*
     * From RFC 2030 (with a correction to the delay math):
     *
     *     Timestamp Name          ID   When Generated
     *     ------------------------------------------------------------
     *     Originate Timestamp     T1   time request sent by client
     *     Receive Timestamp       T2   time request received by server
     *     Transmit Timestamp      T3   time reply sent by server
     *     Destination Timestamp   T4   time reply received by client
     *
     *  The roundtrip delay d and local clock offset t are defined as
     *
     *    d = (T4 - T1) - (T3 - T2)     t = ((T2 - T1) + (T3 - T4)) / 2.
     */

    T1 = p->query->xmttime;
    T2 = lfp_to_d(msg.rectime);
    T3 = lfp_to_d(msg.xmttime);

    /*
     * XXX workaround: time_t / tv_sec must never wrap.
     * around 2020 we will need a solution (64bit time_t / tv_sec).
     * consider every answer with a timestamp beyond january 2030 bogus.
     */
    if (T2 > JAN_2030 || T3 > JAN_2030) {
        set_next(p, error_interval());
        return (0);
    }

    p->reply[p->shift].offset = ((T2 - T1) + (T3 - T4)) / 2;
    p->reply[p->shift].delay = (T4 - T1) - (T3 - T2);
    p->reply[p->shift].status.stratum = msg.stratum;
    if (p->reply[p->shift].delay < 0) {
        interval = error_interval();
        set_next(p, interval);
        log_info("reply from %s: negative delay %fs, "
                 "next query %llds",
                 log_sockaddr((struct sockaddr *)&p->addr->ss),
                 p->reply[p->shift].delay, (long long)interval);
        return (0);
    }
    p->reply[p->shift].error = (T2 - T1) - (T3 - T4);
    p->reply[p->shift].rcvd = getmonotime();
    p->reply[p->shift].good = 1;

    p->reply[p->shift].status.leap = (msg.status & LIMASK);
    p->reply[p->shift].status.precision = msg.precision;
    p->reply[p->shift].status.rootdelay = sfp_to_d(msg.rootdelay);
    p->reply[p->shift].status.rootdispersion = sfp_to_d(msg.dispersion);
    p->reply[p->shift].status.refid = msg.refid;
    p->reply[p->shift].status.reftime = lfp_to_d(msg.reftime);
    p->reply[p->shift].status.poll = msg.ppoll;

    if (p->addr->ss.ss_family == AF_INET) {
        p->reply[p->shift].status.send_refid =
            ((struct sockaddr_in *)&p->addr->ss)->sin_addr.s_addr;
    } else if (p->addr->ss.ss_family == AF_INET6) {
        MD5_CTX		context;
        u_int8_t	digest[MD5_DIGEST_LENGTH];

        MD5_Init(&context);
        MD5_Update(&context, ((struct sockaddr_in6 *)&p->addr->ss)->
                   sin6_addr.s6_addr, sizeof(struct in6_addr));
        MD5_Final(digest, &context);
        memcpy((char *)&p->reply[p->shift].status.send_refid, digest,
               sizeof(u_int32_t));
    } else
        p->reply[p->shift].status.send_refid = msg.xmttime.fractionl;

    if (p->trustlevel < TRUSTLEVEL_PATHETIC)
        interval = scale_interval(INTERVAL_QUERY_PATHETIC);
    else if (p->trustlevel < TRUSTLEVEL_AGGRESSIVE)
        interval = scale_interval(INTERVAL_QUERY_AGGRESSIVE);
    else
        interval = scale_interval(INTERVAL_QUERY_NORMAL);

    set_next(p, interval);
    p->state = STATE_REPLY_RECEIVED;

    /* every received reply which we do not discard increases trust */
    if (p->trustlevel < TRUSTLEVEL_MAX) {
        if (p->trustlevel < TRUSTLEVEL_BADPEER &&
                p->trustlevel + 1 >= TRUSTLEVEL_BADPEER)
            log_info("peer %s now valid",
                     log_sockaddr((struct sockaddr *)&p->addr->ss));
        p->trustlevel++;
    }

    log_debug("reply from %s: offset %f delay %f, "
              "next query %llds %s",
              log_sockaddr((struct sockaddr *)&p->addr->ss),
              p->reply[p->shift].offset, p->reply[p->shift].delay,
              (long long)interval, print_rtable(p->rtable));

    client_update(p);
    if (settime)
        priv_settime(p->reply[p->shift].offset);

    if (++p->shift >= OFFSET_ARRAY_SIZE)
        p->shift = 0;

    return (0);
}
Beispiel #9
0
void
prepare_listeners(struct bgpd_config *conf)
{
	struct listen_addr	*la, *next;
	int			 opt = 1;

	if (TAILQ_EMPTY(conf->listen_addrs)) {
		if ((la = calloc(1, sizeof(struct listen_addr))) == NULL)
			fatal("setup_listeners calloc");
		la->fd = -1;
		la->flags = DEFAULT_LISTENER;
		la->reconf = RECONF_REINIT;
		la->sa.ss_len = sizeof(struct sockaddr_in);
		((struct sockaddr_in *)&la->sa)->sin_family = AF_INET;
		((struct sockaddr_in *)&la->sa)->sin_addr.s_addr =
		    htonl(INADDR_ANY);
		((struct sockaddr_in *)&la->sa)->sin_port = htons(BGP_PORT);
		TAILQ_INSERT_TAIL(conf->listen_addrs, la, entry);

		if ((la = calloc(1, sizeof(struct listen_addr))) == NULL)
			fatal("setup_listeners calloc");
		la->fd = -1;
		la->flags = DEFAULT_LISTENER;
		la->reconf = RECONF_REINIT;
		la->sa.ss_len = sizeof(struct sockaddr_in6);
		((struct sockaddr_in6 *)&la->sa)->sin6_family = AF_INET6;
		((struct sockaddr_in6 *)&la->sa)->sin6_port = htons(BGP_PORT);
		TAILQ_INSERT_TAIL(conf->listen_addrs, la, entry);
	}

	for (la = TAILQ_FIRST(conf->listen_addrs); la != NULL; la = next) {
		next = TAILQ_NEXT(la, entry);
		if (la->reconf != RECONF_REINIT)
			continue;

		if ((la->fd = socket(la->sa.ss_family, SOCK_STREAM,
		    IPPROTO_TCP)) == -1) {
			if (la->flags & DEFAULT_LISTENER && (errno ==
			    EAFNOSUPPORT || errno == EPROTONOSUPPORT)) {
				TAILQ_REMOVE(conf->listen_addrs, la, entry);
				free(la);
				continue;
			} else
				fatal("socket");
		}

		opt = 1;
		if (setsockopt(la->fd, SOL_SOCKET, SO_REUSEADDR,
		    &opt, sizeof(opt)) == -1)
			fatal("setsockopt SO_REUSEADDR");

		if (bind(la->fd, (struct sockaddr *)&la->sa, la->sa.ss_len) ==
		    -1) {
			switch (la->sa.ss_family) {
			case AF_INET:
				log_warn("cannot bind to %s:%u",
				    log_sockaddr((struct sockaddr *)&la->sa),
				    ntohs(((struct sockaddr_in *)
				    &la->sa)->sin_port));
				break;
			case AF_INET6:
				log_warn("cannot bind to [%s]:%u",
				    log_sockaddr((struct sockaddr *)&la->sa),
				    ntohs(((struct sockaddr_in6 *)
				    &la->sa)->sin6_port));
				break;
			default:
				log_warn("cannot bind to %s",
				    log_sockaddr((struct sockaddr *)&la->sa));
				break;
			}
			close(la->fd);
			TAILQ_REMOVE(conf->listen_addrs, la, entry);
			free(la);
			continue;
		}
	}
}
Beispiel #10
0
int
main(int argc, char *argv[])
{
	int			 c;
	int			 debug = 0;
	int			 noaction = 0;
	const char		*conffile = CONF_FILE;
	u_int			 flags = 0;
	struct passwd		*pw;
	struct igdpcpd		*env;
	struct in6_addr		 ssdp_link_nodes = IN6ADDR_LINKLOCAL_SSDP_INIT;
	struct in6_addr		 all_nodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
	struct ifaddrs		*ifap, *ifa, *ifal;
	struct listen_addr	*la;
	int			 reuse = 1;
	unsigned char		 loop4 = 0;
	unsigned int		 loop6 = 0;
	unsigned char		 ttl4 = UPNP_MULTICAST_TTL;
	int			 ttl6 = UPNP_MULTICAST_TTL;
	struct ip_mreq		 mreq4;
	struct ipv6_mreq	 mreq6;
	struct event		*ev_sighup;
	struct event		*ev_sigint;
	struct event		*ev_sigterm;
	struct timeval		 tv = { 0, 0 };
	socklen_t		 slen;

	log_init(1);

	while ((c = getopt(argc, argv, "df:nv")) != -1) {
		switch (c) {
		case 'd':
			debug = 1;
			break;
		case 'f':
			conffile = optarg;
			break;
		case 'n':
			noaction++;
			break;
		case 'v':
			flags |= IGDPCPD_F_VERBOSE;
			break;
		default:
			usage();
			/* NOTREACHED */
		}
	}

	argc -= optind;
	argv += optind;
	if (argc > 0)
		usage();

	if ((env = parse_config(conffile, flags)) == NULL)
		exit(1);

	if (noaction) {
		fprintf(stderr, "configuration ok\n");
		exit(0);
	}

	if (geteuid())
		errx(1, "need root privileges");

	if ((pw = getpwnam(IGDPCPD_USER)) == NULL)
		errx(1, "unknown user %s", IGDPCPD_USER);

	log_init(debug);

	if (!debug) {
		if (daemon(1, 0) == -1)
			err(1, "failed to daemonize");
	}

	gettimeofday(&env->sc_boottime, NULL);

	memset(&ssdp4, 0, sizeof(ssdp4));
	ssdp4.sin_family = AF_INET;
	ssdp4.sin_len = sizeof(ssdp4);
	ssdp4.sin_addr.s_addr = htonl(INADDR_SSDP_GROUP);
	ssdp4.sin_port = htons(SSDP_PORT);

	memset(&ssdp6, 0, sizeof(ssdp6));
	ssdp6.sin6_family = AF_INET6;
	ssdp6.sin6_len = sizeof(ssdp6);
	ssdp6.sin6_addr = ssdp_link_nodes;
	ssdp6.sin6_port = htons(SSDP_PORT);

	memset(&pcp4, 0, sizeof(pcp4));
	pcp4.sin_family = AF_INET;
	pcp4.sin_len = sizeof(pcp4);
	pcp4.sin_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
	pcp4.sin_port = htons(PCP_CLIENT_PORT);

	memset(&pcp6, 0, sizeof(pcp6));
	pcp6.sin6_family = AF_INET6;
	pcp6.sin6_len = sizeof(pcp6);
	pcp6.sin6_addr = all_nodes;
	pcp6.sin6_port = htons(PCP_CLIENT_PORT);

	if (uname(&name) == -1)
		fatal("uname");

	if (getifaddrs(&ifap) == -1)
		fatal("getifaddrs");

	for (la = TAILQ_FIRST(&env->listen_addrs); la; ) {
		switch (la->sa.ss_family) {
		case AF_INET:
			if (((struct sockaddr_in *)&la->sa)->sin_port == 0)
				((struct sockaddr_in *)&la->sa)->sin_port =
				    htons(SSDP_PORT);
			break;
		case AF_INET6:
			if (((struct sockaddr_in6 *)&la->sa)->sin6_port == 0)
				((struct sockaddr_in6 *)&la->sa)->sin6_port =
				    htons(SSDP_PORT);
			break;
		default:
			fatalx("king bula sez: af borked");
		}

		log_info("listening on %s:%u",
		    log_sockaddr((struct sockaddr *)&la->sa),
		    SSDP_PORT);

		if ((la->fd = socket(la->sa.ss_family, SOCK_DGRAM, 0)) == -1)
			fatal("socket");

		if (fcntl(la->fd, F_SETFL, O_NONBLOCK) == -1)
			fatal("fcntl");

		if (setsockopt(la->fd, SOL_SOCKET, SO_REUSEADDR, &reuse,
		    sizeof(reuse)) == -1)
			fatal("setsockopt");

		if (setsockopt(la->fd, SOL_SOCKET, SO_REUSEPORT, &reuse,
		    sizeof(reuse)) == -1)
			fatal("setsockopt");

		switch (la->sa.ss_family) {
		case AF_INET:
			if (setsockopt(la->fd, IPPROTO_IP, IP_MULTICAST_LOOP,
			    &loop4, sizeof(loop4)) == -1)
				fatal("setsockopt");

			if (setsockopt(la->fd, IPPROTO_IP, IP_MULTICAST_TTL,
			    &ttl4, sizeof(ttl4)) == -1)
				fatal("setsockopt");

			if (setsockopt(la->fd, IPPROTO_IP, IP_MULTICAST_IF,
			    &(((struct sockaddr_in *)&la->sa)->sin_addr),
			    sizeof(struct in_addr)) == -1)
				fatal("setsockopt");

			if (setsockopt(la->fd, IPPROTO_IP, IP_RECVDSTADDR,
			    &reuse, sizeof(reuse)) == -1)
				fatal("setsockopt");

			if (setsockopt(la->fd, IPPROTO_IP, IP_RECVIF, &reuse,
			    sizeof(reuse)) == -1)
				fatal("setsockopt");

			/* Assume AF_LINK always comes first */
			for (ifa = ifal = ifap; ifa; ifa = ifa->ifa_next) {
				if (ifa->ifa_addr == NULL)
					continue;
				switch (ifa->ifa_addr->sa_family) {
				case AF_LINK:
					ifal = ifa;
					break;
				case AF_INET:
					if (memcmp(&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr,
					    &((struct sockaddr_in *)&la->sa)->sin_addr,
					    sizeof(struct in_addr)) == 0)
						la->index = ((struct sockaddr_dl *)ifal->ifa_addr)->sdl_index;
					break;
				}
			}
			break;
		case AF_INET6:
			if (setsockopt(la->fd, IPPROTO_IPV6,
			    IPV6_MULTICAST_LOOP, &loop6, sizeof(loop6)) == -1)
				fatal("setsockopt");

			if (setsockopt(la->fd, IPPROTO_IPV6,
			    IPV6_MULTICAST_HOPS, &ttl6, sizeof(ttl6)) == -1)
				fatal("setsockopt");

			if (setsockopt(la->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
			    &((struct sockaddr_in6 *)&la->sa)->sin6_scope_id,
			    sizeof(((struct sockaddr_in6 *)&la->sa)->sin6_scope_id)) == -1)
				fatal("setsockopt");

			if (setsockopt(la->fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
			    &reuse, sizeof(reuse)) == -1)
				fatal("setsockopt");

			la->index = ((struct sockaddr_in6 *)&la->sa)->sin6_scope_id;
			break;
		default:
			/* NOTREACHED */
			break;
		}

		if (bind(la->fd, (struct sockaddr *)&la->sa,
		    SA_LEN((struct sockaddr *)&la->sa)) == -1) {
			struct listen_addr	*nla;

			log_warn("bind on %d failed, skipping",
			    log_sockaddr((struct sockaddr *)&la->sa));
			close(la->fd);
			nla = TAILQ_NEXT(la, entry);
			TAILQ_REMOVE(&env->listen_addrs, la, entry);
			free(la);
			la = nla;
			continue;
		}

		switch (la->sa.ss_family) {
		case AF_INET:
			/* Create IPv4 multicast socket if needed */
			if (env->sc_mc4_fd == 0) {
				if ((env->sc_mc4_fd = socket(AF_INET,
				    SOCK_DGRAM, 0)) == -1)
					fatal("socket");

				if (fcntl(env->sc_mc4_fd, F_SETFL,
				    O_NONBLOCK) == -1)
					fatal("fcntl");

				if (setsockopt(env->sc_mc4_fd, SOL_SOCKET,
				    SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
					fatal("setsockopt");

				if (setsockopt(env->sc_mc4_fd, SOL_SOCKET,
				    SO_REUSEPORT, &reuse, sizeof(reuse)) == -1)
					fatal("setsockopt");

				if (bind(env->sc_mc4_fd,
				    (struct sockaddr *)&ssdp4,
				    sizeof(ssdp4)) == -1)
					fatal("bind");

				if (setsockopt(env->sc_mc4_fd, IPPROTO_IP,
				    IP_MULTICAST_LOOP, &loop4,
				    sizeof(loop4)) == -1)
					fatal("setsockopt");

				if (setsockopt(env->sc_mc4_fd, IPPROTO_IP,
				    IP_RECVDSTADDR, &reuse,
				    sizeof(reuse)) == -1)
					fatal("setsockopt");

				if (setsockopt(env->sc_mc4_fd, IPPROTO_IP,
				    IP_RECVIF, &reuse, sizeof(reuse)) == -1)
					fatal("setsockopt");

				log_info("listening on %s:%u",
				    log_sockaddr((struct sockaddr *)&ssdp4),
				    SSDP_PORT);
			}

			memset(&mreq4, 0, sizeof(mreq4));
			mreq4.imr_multiaddr = ssdp4.sin_addr;
			mreq4.imr_interface = ((struct sockaddr_in *)&la->sa)->sin_addr;

			if (setsockopt(env->sc_mc4_fd, IPPROTO_IP,
			    IP_ADD_MEMBERSHIP, &mreq4, sizeof(mreq4)) == -1)
				fatal("setsockopt");
			break;
		case AF_INET6:
			/* Create IPv6 multicast socket if needed */
			if (env->sc_mc6_fd == 0) {
				if ((env->sc_mc6_fd = socket(AF_INET6,
				    SOCK_DGRAM, 0)) == -1)
					fatal("socket");

				if (fcntl(env->sc_mc6_fd, F_SETFL,
				    O_NONBLOCK) == -1)
					fatal("fcntl");

				if (setsockopt(env->sc_mc6_fd, SOL_SOCKET,
				    SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
					fatal("setsockopt");

				if (setsockopt(env->sc_mc6_fd, SOL_SOCKET,
				    SO_REUSEPORT, &reuse, sizeof(reuse)) == -1)
					fatal("setsockopt");

				if (bind(env->sc_mc6_fd,
				    (struct sockaddr *)&ssdp6,
				    sizeof(ssdp6)) == -1)
					fatal("bind");

				if (setsockopt(env->sc_mc6_fd, IPPROTO_IPV6,
				    IPV6_MULTICAST_LOOP, &loop6,
				    sizeof(loop6)) == -1)
					fatal("setsockopt");

				if (setsockopt(env->sc_mc6_fd, IPPROTO_IPV6,
				    IPV6_RECVPKTINFO, &reuse,
				    sizeof(reuse)) == -1)
					fatal("setsockopt");

				log_info("listening on %s:%u",
				    log_sockaddr((struct sockaddr *)&ssdp6),
				    SSDP_PORT);
			}

			memset(&mreq6, 0, sizeof(mreq6));
			mreq6.ipv6mr_multiaddr = ssdp6.sin6_addr;
			mreq6.ipv6mr_interface = ((struct sockaddr_in6 *)&la->sa)->sin6_scope_id;

			if (setsockopt(env->sc_mc6_fd, IPPROTO_IPV6,
			    IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6)) == -1)
				fatal("setsockopt");
			break;
		default:
			/* NOTREACHED */
			break;
		}

		/* HTTP */
		memcpy(&la->http_sa, &la->sa, sizeof(la->sa));

		switch (la->http_sa.ss_family) {
		case AF_INET:
			((struct sockaddr_in *)&la->http_sa)->sin_port =
			    htons(env->sc_port);
			break;
		case AF_INET6:
			((struct sockaddr_in6 *)&la->http_sa)->sin6_port =
			    htons(env->sc_port);
			break;
		default:
			/* NOTREACHED */
			break;
		}

		if ((la->http_fd = socket(la->http_sa.ss_family, SOCK_STREAM,
		    0)) == -1)
			fatal("socket");

		if (fcntl(la->http_fd, F_SETFL, O_NONBLOCK) == -1)
			fatal("fcntl");

		if (setsockopt(la->http_fd, SOL_SOCKET, SO_REUSEADDR, &reuse,
		    sizeof(reuse)) == -1)
			fatal("setsockopt");

		if (bind(la->http_fd, (struct sockaddr *)&la->http_sa,
		    SA_LEN((struct sockaddr *)&la->http_sa)) == -1)
			fatal("bind");

		/* If the HTTP port is not explicitly configured, read back
		 * what the kernel gives us so we can use it in SSDP messages
		 */
		if (env->sc_port == 0) {
			slen = sizeof(la->http_fd);

			if (getsockname(la->http_fd,
			    (struct sockaddr *)&la->http_sa, &slen) == -1)
				fatal("getsockname");

#if 0
			/* Store the port so subsequent HTTP listeners will
			 * attempt to get the same port?
			 */
			switch (la->http_sa.ss_family) {
			case AF_INET:
				env->sc_port = ntohs(((struct sockaddr_in *)&la->http_sa)->sin_port);
				break;
			case AF_INET6:
				env->sc_port = ntohs(((struct sockaddr_in6 *)&la->http_sa)->sin6_port);
				break;
			default:
				/* NOTREACHED */
				break;
			}
#endif
		}

		if (listen(la->http_fd, 16) == -1)
			fatal("listen");

		la = TAILQ_NEXT(la, entry);
	}

	freeifaddrs(ifap);

	log_info("startup");

	if (chroot(pw->pw_dir) == -1)
		fatal("chroot");
	if (chdir("/") == -1)
		fatal("chdir(\"/\")");

	if (setgroups(1, &pw->pw_gid) ||
	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
		fatal("cannot drop privileges");

	if ((env->sc_base = event_base_new()) == NULL)
		fatalx("event_base_new");

	signal(SIGPIPE, SIG_IGN);
	ev_sighup = evsignal_new(env->sc_base, SIGHUP, handle_signal, env);
	ev_sigint = evsignal_new(env->sc_base, SIGINT, handle_signal, env);
	ev_sigterm = evsignal_new(env->sc_base, SIGTERM, handle_signal, env);
	evsignal_add(ev_sighup, NULL);
	evsignal_add(ev_sigint, NULL);
	evsignal_add(ev_sigterm, NULL);

	if (env->sc_mc4_fd) {
		env->sc_mc4_ev = event_new(env->sc_base, env->sc_mc4_fd,
		    EV_READ|EV_PERSIST, ssdp_recvmsg, env);
		event_add(env->sc_mc4_ev, NULL);
	}

	if (env->sc_mc6_fd) {
		env->sc_mc6_ev = event_new(env->sc_base, env->sc_mc6_fd,
		    EV_READ|EV_PERSIST, ssdp_recvmsg, env);
		event_add(env->sc_mc6_ev, NULL);
	}

	env->sc_httpd = evhttp_new(env->sc_base);

	for (la = TAILQ_FIRST(&env->listen_addrs); la; ) {
		la->ev = event_new(env->sc_base, la->fd, EV_READ|EV_PERSIST,
		    ssdp_recvmsg, env);
		event_add(la->ev, NULL);

		evhttp_accept_socket(env->sc_httpd, la->http_fd);

		la = TAILQ_NEXT(la, entry);
	}

	env->sc_root = upnp_root_device(env->sc_version,
	    UPNP_DEVICE_INTERNET_GATEWAY_DEVICE, env->sc_httpd);

	/* FIXME DEBUG */
	evhttp_set_gencb(env->sc_httpd, upnp_debug, env);

	env->sc_announce_ev = evtimer_new(env->sc_base, ssdp_announce, env);
	evtimer_add(env->sc_announce_ev, &tv);

	event_base_dispatch(env->sc_base);

	return (0);
}