Beispiel #1
0
static void
accept_cb(struct ev_loop *loop, ev_uinet *w, int revents)
{
	struct passive_context *passive = w->data;
	struct uinet_socket *newso = NULL;
	struct uinet_socket *newpeerso = NULL;
	struct connection_context *conn = NULL;
	struct connection_context *peerconn = NULL;
	int error;
	int batch_limit = 32;
	int processed = 0;

	while ((processed < batch_limit) &&
	       (UINET_EWOULDBLOCK != (error = uinet_soaccept(w->so, NULL, &newso)))) {
		processed++;

		if (0 == error) {
			newpeerso = NULL;
			conn = NULL;
			peerconn = NULL;

			if (passive->verbose)
				printf("accept succeeded\n");

			conn = create_conn(passive, newso, 1);
			if (NULL == conn)
				goto fail;

			newpeerso = uinet_sogetpassivepeer(newso);
			peerconn = create_conn(passive, newpeerso, 0);
			if (NULL == peerconn)
				goto fail;

			conn->peer = peerconn;
			peerconn->peer = conn;
			
			ev_uinet_start(loop, &conn->watcher);

			if (!passive->extract)
				ev_uinet_start(loop, &peerconn->watcher);

			passive->interface->num_sockets += 2;

			continue;
		fail:
			if (conn) destroy_conn(conn);
			if (newso) uinet_soclose(newso);
			if (newpeerso) uinet_soclose(newpeerso);
		}
	}

	if (processed > passive->interface->max_accept_batch)
		passive->interface->max_accept_batch = processed;
}
static void
destroy_conn(struct passive_connection *conn)
{
	ev_uinet *w  = &conn->watcher;

	ev_uinet_stop(conn->server->cfg.loop, &conn->connected_watcher);
	ev_uinet_stop(conn->server->cfg.loop, w);
	ev_uinet_detach(w->ctx);
	uinet_soclose(ev_uinet_so(w->ctx));
	conn->server->num_sockets--;
	free(conn);
}
static void
nproxy_conn_destroy(struct ev_loop *loop, struct nproxy_connection *conn)
{
	struct ev_uinet_ctx *ctx;

	ctx = conn->copy_watcher.ctx;
	ev_uinet_stop(loop, &conn->copy_watcher);
	ev_uinet_stop(loop, &conn->connected_watcher);
	ev_uinet_stop(loop, &conn->writable_watcher);
	uinet_soclose(ev_uinet_so(ctx));
	ev_uinet_detach(ctx);
}
Beispiel #4
0
static void
echo_accept_cb(struct ev_loop *loop, ev_uinet *w, int revents)
{
	struct uinet_demo_echo *echo = w->data;
	struct uinet_socket *newso = NULL;
	struct ev_uinet_ctx *soctx = NULL;
	struct echo_connection *conn = NULL;
	int error;

	if (0 != (error = uinet_soaccept(w->so, NULL, &newso))) {
		printf("%s: Accept failed (%d)\n", echo->cfg.name, error);
	} else {
		if (echo->cfg.verbose)
			printf("%s: Accept succeeded\n", echo->cfg.name);
		
		soctx = ev_uinet_attach(newso);
		if (NULL == soctx) {
			printf("%s: Failed to alloc libev context for new connection\n",
			       echo->cfg.name);
			goto fail;
		}

		conn = malloc(sizeof(*conn));
		if (NULL == conn) {
			printf("%s: Failed to alloc new connection context\n",
			       echo->cfg.name);
			goto fail;
		}
		conn->echo = echo;
		conn->id = echo->next_id++;
		conn->verbose = echo->cfg.verbose;

		ev_init(&conn->connected_watcher, echo_connected_cb);
		ev_uinet_set(&conn->connected_watcher, soctx, EV_WRITE);
		conn->connected_watcher.data = conn;
		if (conn->verbose || (echo->cfg.copy_mode & UINET_IP_COPY_MODE_MAYBE))
			ev_uinet_start(loop, &conn->connected_watcher);

		ev_init(&conn->watcher, echo_cb);
		ev_uinet_set(&conn->watcher, soctx, EV_READ);
		conn->watcher.data = conn;
		ev_uinet_start(loop, &conn->watcher);
	}

	return;

fail:
	if (conn) free(conn);
	if (soctx) ev_uinet_detach(soctx);
	if (newso) uinet_soclose(newso);
}
Beispiel #5
0
static void
destroy_conn(struct connection_context *conn)
{
	ev_uinet *w  = &conn->watcher;

	ev_uinet_stop(conn->server->loop, w);
	ev_uinet_detach(w->ctx);
	uinet_soclose(w->so);
#ifdef ENABLE_EXTRACT
	if (conn->buffer)
		free(conn->buffer);
	if (conn->parser)
		free(conn->parser);
#endif
	conn->server->interface->num_sockets--;
	free(conn);
}
Beispiel #6
0
static void
accept_cb(struct ev_loop *loop, ev_uinet *w, int revents)
{
	struct echo_context *echo = w->data;
	struct uinet_socket *newso = NULL;
	struct connection_context *conn = NULL;
	struct ev_uinet_ctx *soctx = NULL;
	int error;

	if (0 != (error = uinet_soaccept(w->so, NULL, &newso))) {
		printf("accept failed (%d)\n", error);
	} else {
		printf("accept succeeded\n");
		
		soctx = ev_uinet_attach(newso);
		if (NULL == soctx) {
			printf("Failed to alloc libev context for new connection socket\n");
			goto fail;
		}

		conn = malloc(sizeof(*conn));
		if (NULL == conn) {
			printf("Failed to alloc connection context for new connection\n");
			goto fail;
		}

		ev_init(&conn->watcher, echo_cb);
		ev_uinet_set(&conn->watcher, soctx, EV_READ);
		conn->watcher.data = conn;
		ev_uinet_start(loop, &conn->watcher);
	}

	return;
fail:
	if (conn) free(conn);
	if (soctx) ev_uinet_detach(soctx);
	if (newso) uinet_soclose(newso);
}
Beispiel #7
0
static void
echo_cb(struct ev_loop *loop, ev_uinet *w, int revents)
{
	struct echo_connection *conn = w->data;
	struct uinet_demo_echo *echo = conn->echo;
	struct uinet_iovec iov;
	struct uinet_uio uio;
	int max_read;
	int max_write;
	int read_size;
	int error;
#define BUFFER_SIZE (64*1024)
	char buffer[BUFFER_SIZE];

	max_read = uinet_soreadable(w->so, 0);
	if (max_read <= 0) {
		/* the watcher should never be invoked if there is no error and there no bytes to be read */
		assert(max_read != 0);
		if (conn->verbose)
			printf("%s: connection %llu: can't read, closing\n",
			       echo->cfg.name, (unsigned long long)conn->id);
		goto err;
	} else {
		max_write = echo->sink ? max_read : uinet_sowritable(w->so, 0);
		if (-1 == max_write) {
			if (conn->verbose)
				printf("%s: connection %llu: can't write, closing\n",
				       echo->cfg.name, (unsigned long long)conn->id);
			goto err;
		} else {
			read_size = imin(imin(max_read, max_write), BUFFER_SIZE);

			uio.uio_iov = &iov;
			iov.iov_base = buffer;
			iov.iov_len = read_size;
			uio.uio_iovcnt = 1;
			uio.uio_offset = 0;
			uio.uio_resid = read_size;
	
			error = uinet_soreceive(w->so, NULL, &uio, NULL);
			if (0 != error) {
				printf("%s: connection %llu: read error (%d), closing\n",
				       echo->cfg.name, (unsigned long long)conn->id, error);
				goto err;
			}

			assert(uio.uio_resid == 0);

			if (!echo->sink) {
				uio.uio_iov = &iov;
				iov.iov_base = buffer;
				iov.iov_len = read_size;
				uio.uio_iovcnt = 1;
				uio.uio_offset = 0;
				uio.uio_resid = read_size;
				error = uinet_sosend(w->so, NULL, &uio, 0);
				if (0 != error) {
					printf("%s: connection %llu: write error (%d), closing\n",
					       echo->cfg.name, (unsigned long long)conn->id, error);
					goto err;
				}
			}

			if (max_write < max_read) {
				/* limited by write space, so change to a
				 * write watch on the socket, if we aren't
				 * already one.
				 */
				if (w->events & EV_READ) {
					ev_uinet_stop(loop, w);
					w->events = EV_WRITE;
					ev_uinet_start(loop, w);
				}
				/* else, continue as a write watch */
			} else if (!(w->events & EV_READ)) {
				/* write space wasn't a limitation this
				 * time, so switch back to waiting on
				 * EV_READ
				 */
				ev_uinet_stop(loop, w);
				w->events = EV_READ;
				ev_uinet_start(loop, w);
			}
			/* else, continue as a read watcher */
		}
	}

	return;

err:
	ev_uinet_stop(loop, w);
	ev_uinet_detach(w->ctx);
	uinet_soclose(w->so);
	free(conn);
}
Beispiel #8
0
static int
echo_start(struct uinet_demo_config *cfg, uinet_instance_t uinst, struct ev_loop *loop)
{
	struct uinet_demo_echo *echo = (struct uinet_demo_echo *)cfg;
	struct uinet_socket *listen_socket = NULL;
	struct ev_uinet_ctx *soctx = NULL;
	struct uinet_in_addr addr;
	int optlen, optval;
	int error;
	struct uinet_sockaddr_in sin;

	if (uinet_inet_pton(UINET_AF_INET, echo->listen_addr, &addr) <= 0) {
		printf("%s: Malformed address %s\n", echo->cfg.name, echo->listen_addr);
		error = UINET_EINVAL;
		goto fail;
	}

	error = uinet_socreate(echo->cfg.uinst, UINET_PF_INET, &listen_socket, UINET_SOCK_STREAM, 0);
	if (0 != error) {
		printf("%s: Listen socket creation failed (%d)\n", echo->cfg.name, error);
		goto fail;
	}

	soctx = ev_uinet_attach(listen_socket);
	if (NULL == soctx) {
		printf("%s: Failed to alloc libev socket context\n", echo->cfg.name);
		error = UINET_ENOMEM;
		goto fail;
	}

	if (echo->promisc) {
		if ((error = uinet_make_socket_promiscuous(listen_socket, NULL))) {
			printf("%s: Failed to make listen socket promiscuous (%d)\n",
			       echo->cfg.name, error);
			goto fail;
		}
	}

	if (cfg->copy_mode) {
		if ((error = uinet_sosetcopymode(listen_socket, cfg->copy_mode,
						 cfg->copy_limit, cfg->copy_uif))) {
			printf("%s: Failed to set copy mode (%d)\n",
			       echo->cfg.name, error);
			goto fail;
		}
	}
	
	/*
	 * Socket needs to be non-blocking to work with the event system
	 */
	uinet_sosetnonblocking(listen_socket, 1);

	/* Set NODELAY on the listen socket so it will be set on all
	 * accepted sockets via inheritance.
	 */
	optlen = sizeof(optval);
	optval = 1;
	if ((error = uinet_sosetsockopt(listen_socket, UINET_IPPROTO_TCP, UINET_TCP_NODELAY,
					&optval, optlen))) {
		printf("%s: Failed to set TCP_NODELAY (%d)\n", echo->cfg.name, error);
		goto fail;
	}

	/* Listen on all VLANs */
	if ((error = uinet_setl2info2(listen_socket, NULL, NULL,
				      UINET_INL2I_TAG_ANY, NULL))) {
		printf("%s: Listen socket L2 info set failed (%d)\n",
		       echo->cfg.name, error);
		goto fail;
	}

	echo->listen_socket = listen_socket;

	memset(&sin, 0, sizeof(struct uinet_sockaddr_in));
	sin.sin_len = sizeof(struct uinet_sockaddr_in);
	sin.sin_family = UINET_AF_INET;
	sin.sin_addr = addr;
	sin.sin_port = htons(echo->listen_port);
	error = uinet_sobind(listen_socket, (struct uinet_sockaddr *)&sin);
	if (0 != error) {
		printf("%s: Bind to %s:%u failed\n", echo->cfg.name,
		       echo->listen_addr, echo->listen_port);
		goto fail;
	}
	
	error = uinet_solisten(echo->listen_socket, -1);
	if (0 != error) {
		printf("%s: Listen on %s:%u failed\n", echo->cfg.name,
		       echo->listen_addr, echo->listen_port);
		goto fail;
	}

	if (echo->cfg.verbose)
		printf("%s: Listening on %s:%u\n", echo->cfg.name,
		       echo->listen_addr, echo->listen_port);

	/*
	 * Set up a read watcher to accept new connections
	 */
	ev_init(&echo->listen_watcher, echo_accept_cb);
	ev_uinet_set(&echo->listen_watcher, soctx, EV_READ);
	echo->listen_watcher.data = echo;
	ev_uinet_start(echo->cfg.loop, &echo->listen_watcher);

	return (0);

fail:
	if (soctx) ev_uinet_detach(soctx);
	if (listen_socket) uinet_soclose(listen_socket);

	return (error);
}
Beispiel #9
0
static struct passive_context *
create_passive(struct ev_loop *loop, struct server_config *cfg)
{
	struct passive_context *passive = NULL;
	struct uinet_socket *listener = NULL;
	struct ev_uinet_ctx *soctx = NULL;
	struct uinet_in_addr addr;
	int optlen, optval;
	int error;
	struct uinet_sockaddr_in sin;

	if (uinet_inet_pton(UINET_AF_INET, cfg->listen_addr, &addr) <= 0) {
		printf("Malformed address %s\n", cfg->listen_addr);
		goto fail;
	}

	error = uinet_socreate(UINET_PF_INET, &listener, UINET_SOCK_STREAM, 0);
	if (0 != error) {
		printf("Listen socket creation failed (%d)\n", error);
		goto fail;
	}

	soctx = ev_uinet_attach(listener);
	if (NULL == soctx) {
		printf("Failed to alloc libev socket context\n");
		goto fail;
	}
	
	if ((error = uinet_make_socket_passive(listener))) {
		printf("Failed to make listen socket passive (%d)\n", error);
		goto fail;
	}

	if (cfg->interface->promisc) {
		if ((error = uinet_make_socket_promiscuous(listener, cfg->interface->cdom))) {
			printf("Failed to make listen socket promiscuous (%d)\n", error);
			goto fail;
		}
	}

	/* 
	 * The following settings will be inherited by all sockets created
	 * by this listen socket.
	 */

	/*
	 * Need to be non-blocking to work with the event system.
	 */
	uinet_sosetnonblocking(listener, 1);

	/* Wait 5 seconds for connections to complete */
	optlen = sizeof(optval);
	optval = 5;
	if ((error = uinet_sosetsockopt(listener, UINET_IPPROTO_TCP, UINET_TCP_KEEPINIT, &optval, optlen)))
		goto fail;

	/* Begin counting down to close after 10 seconds of idle */
	optlen = sizeof(optval);
	optval = 10;
	if ((error = uinet_sosetsockopt(listener, UINET_IPPROTO_TCP, UINET_TCP_KEEPIDLE, &optval, optlen)))
		goto fail;

	/* Count down to close once per second */
	optlen = sizeof(optval);
	optval = 1;
	if ((error = uinet_sosetsockopt(listener, UINET_IPPROTO_TCP, UINET_TCP_KEEPINTVL, &optval, optlen)))
		goto fail;

	/* Close after idle for 3 counts */
	optlen = sizeof(optval);
	optval = 3;
	if ((error = uinet_sosetsockopt(listener, UINET_IPPROTO_TCP, UINET_TCP_KEEPCNT, &optval, optlen)))
		goto fail;

	/* Wait 100 milliseconds for missing TCP segments */
	optlen = sizeof(optval);
	optval = 100;
	if ((error = uinet_sosetsockopt(listener, UINET_IPPROTO_TCP, UINET_TCP_REASSDL, &optval, optlen)))
		goto fail;



	passive = calloc(1, sizeof(struct passive_context));
	if (NULL == passive) {
		goto fail;
	}

	passive->loop = loop;
	passive->listener = listener;
	passive->verbose = cfg->verbose;
	passive->interface = cfg->interface;
	passive->extract = cfg->extract;
	memcpy(passive->content_types, cfg->content_types, sizeof(passive->content_types));

	memset(&sin, 0, sizeof(struct uinet_sockaddr_in));
	sin.sin_len = sizeof(struct uinet_sockaddr_in);
	sin.sin_family = UINET_AF_INET;
	sin.sin_addr = addr;
	sin.sin_port = htons(cfg->listen_port);
	error = uinet_sobind(listener, (struct uinet_sockaddr *)&sin);
	if (0 != error) {
		printf("bind failed\n");
		goto fail;
	}
	
	error = uinet_solisten(passive->listener, -1);
	if (0 != error)
		goto fail;

	if (passive->verbose) {
		char buf[32];

		printf("Listening on %s:%u\n", uinet_inet_ntoa(addr, buf, sizeof(buf)), cfg->listen_port);
	}

	ev_init(&passive->listen_watcher, accept_cb);
	ev_uinet_set(&passive->listen_watcher, soctx, EV_READ);
	passive->listen_watcher.data = passive;
	ev_uinet_start(loop, &passive->listen_watcher);

	return (passive);

fail:
	if (soctx) ev_uinet_detach(soctx);
	if (listener) uinet_soclose(listener);
	if (passive) free(passive);

	return (NULL);
}
static int
passive_start(struct uinet_demo_config *cfg, uinet_instance_t uinst, struct ev_loop *loop)
{
	struct uinet_demo_passive *passive = (struct uinet_demo_passive *)cfg;
	struct uinet_socket *listen_socket = NULL;
	struct ev_uinet_ctx *soctx = NULL;
	struct uinet_in_addr addr;
	int optlen, optval;
	int error;
	struct uinet_sockaddr_in sin;

	if (uinet_inet_pton(UINET_AF_INET, passive->listen_addr, &addr) <= 0) {
		printf("%s: Malformed address %s\n", passive->cfg.name, passive->listen_addr);
		error = UINET_EINVAL;
		goto fail;
	}

	error = uinet_socreate(passive->cfg.uinst, UINET_PF_INET, &listen_socket, UINET_SOCK_STREAM, 0);
	if (0 != error) {
		printf("%s: Listen socket creation failed (%d)\n", passive->cfg.name, error);
		goto fail;
	}

	soctx = ev_uinet_attach(listen_socket);
	if (NULL == soctx) {
		printf("%s: Failed to alloc libev socket context\n", passive->cfg.name);
		error = UINET_ENOMEM;
		goto fail;
	}
	
	if ((error = uinet_make_socket_passive(listen_socket))) {
		printf("%s: Failed to make listen socket passive (%d)\n", passive->cfg.name, error);
		goto fail;
	}

	if (passive->promisc) {
		if ((error = uinet_make_socket_promiscuous(listen_socket, NULL))) {
			printf("%s: Failed to make listen socket promiscuous (%d)\n", passive->cfg.name, error);
			goto fail;
		}
	}

	/* 
	 * The following settings will be inherited by all sockets created
	 * by this listen socket.
	 */

	/*
	 * Need to be non-blocking to work with the event system.
	 */
	uinet_sosetnonblocking(listen_socket, 1);

	/* Wait 5 seconds for connections to complete */
	optlen = sizeof(optval);
	optval = 5;
	if ((error = uinet_sosetsockopt(listen_socket, UINET_IPPROTO_TCP, UINET_TCP_KEEPINIT, &optval, optlen))) {
		printf("%s: Failed to set TCP_KEEPINIT (%d)\n", passive->cfg.name, error);
		goto fail;
	}

	/* Begin counting down to close after 10 seconds of idle */
	optlen = sizeof(optval);
	optval = 10;
	if ((error = uinet_sosetsockopt(listen_socket, UINET_IPPROTO_TCP, UINET_TCP_KEEPIDLE, &optval, optlen))) {
		printf("%s: Failed to set TCP_KEEPIDLE (%d)\n", passive->cfg.name, error);
		goto fail;
	}

	/* Count down to close once per second */
	optlen = sizeof(optval);
	optval = 1;
	if ((error = uinet_sosetsockopt(listen_socket, UINET_IPPROTO_TCP, UINET_TCP_KEEPINTVL, &optval, optlen))) {
		printf("%s: Failed to set TCP_KEEPINTVL (%d)\n", passive->cfg.name, error);
		goto fail;
	}

	/* Close after idle for 3 counts */
	optlen = sizeof(optval);
	optval = 3;
	if ((error = uinet_sosetsockopt(listen_socket, UINET_IPPROTO_TCP, UINET_TCP_KEEPCNT, &optval, optlen))) {
		printf("%s: Failed to set TCP_KEEPCNT (%d)\n", passive->cfg.name, error);
		goto fail;
	}

	/* Wait 100 milliseconds for missing TCP segments */
	optlen = sizeof(optval);
	optval = 100;
	if ((error = uinet_sosetsockopt(listen_socket, UINET_IPPROTO_TCP, UINET_TCP_REASSDL, &optval, optlen))) {
		printf("%s: Failed to set TCP_REASSDL (%d)\n", passive->cfg.name, error);
		goto fail;
	}


	passive->listen_socket = listen_socket;

	memset(&sin, 0, sizeof(struct uinet_sockaddr_in));
	sin.sin_len = sizeof(struct uinet_sockaddr_in);
	sin.sin_family = UINET_AF_INET;
	sin.sin_addr = addr;
	sin.sin_port = htons(passive->listen_port);
	error = uinet_sobind(listen_socket, (struct uinet_sockaddr *)&sin);
	if (0 != error) {
		printf("%s: Bind to %s:%u failed\n", passive->cfg.name,
		       passive->listen_addr, passive->listen_port);
		goto fail;
	}
	
	error = uinet_solisten(passive->listen_socket, -1);
	if (0 != error) {
		printf("%s: Listen on %s:%u failed\n", passive->cfg.name,
		       passive->listen_addr, passive->listen_port);
		goto fail;
	}

	if (passive->cfg.verbose)
		printf("%s: Listening on %s:%u\n", passive->cfg.name,
		       passive->listen_addr, passive->listen_port);

	ev_init(&passive->listen_watcher, passive_accept_cb);
	ev_uinet_set(&passive->listen_watcher, soctx, EV_READ);
	passive->listen_watcher.data = passive;
	ev_uinet_start(loop, &passive->listen_watcher);

	return (0);

fail:
	if (soctx) ev_uinet_detach(soctx);
	if (listen_socket) uinet_soclose(listen_socket);

	return (error);
}
static void
passive_accept_cb(struct ev_loop *loop, ev_uinet *w, int revents)
{
	struct uinet_demo_passive *passive = w->data;
	struct uinet_socket *newso = NULL;
	struct uinet_socket *newpeerso = NULL;
	struct passive_connection *conn = NULL;
	struct passive_connection *peerconn = NULL;
	int error;
	unsigned int batch_limit = 32;
	unsigned int processed = 0;

	while ((processed < batch_limit) &&
	       (UINET_EWOULDBLOCK != (error = uinet_soaccept(w->so, NULL, &newso)))) {
		processed++;

		if (0 == error) {
			newpeerso = NULL;
			conn = NULL;
			peerconn = NULL;

			if (passive->cfg.verbose)
				printf("%s: Accept succeeded\n", passive->cfg.name);

			conn = create_conn(passive, newso, 1);
			if (NULL == conn) {
				printf("%s: Failed to alloc new connection context\n",
				       passive->cfg.name);
				goto fail;
			}

			newpeerso = uinet_sogetpassivepeer(newso);
			peerconn = create_conn(passive, newpeerso, 0);
			if (NULL == peerconn) {
				printf("%s: Failed to alloc new peer connection context\n",
				       passive->cfg.name);
				goto fail;
			}

			conn->peer = peerconn;
			peerconn->peer = conn;
			
			ev_uinet_start(loop, &conn->watcher);
			ev_uinet_start(loop, &peerconn->watcher);

			if (conn->verbose || (passive->cfg.copy_mode & UINET_IP_COPY_MODE_MAYBE))
				ev_uinet_start(loop, &conn->connected_watcher);

			if (peerconn->verbose || (passive->cfg.copy_mode & UINET_IP_COPY_MODE_MAYBE))
				ev_uinet_start(loop, &peerconn->connected_watcher);

			passive->num_sockets += 2;

			continue;
		fail:
			if (conn) destroy_conn(conn);
			if (newso) uinet_soclose(newso);
			if (newpeerso) uinet_soclose(newpeerso);
		}
	}

	if (processed > passive->max_accept_batch)
		passive->max_accept_batch = processed;
}
Beispiel #12
0
static struct echo_context *
create_echo(struct ev_loop *loop, struct server_config *cfg)
{
	struct echo_context *echo = NULL;
	struct uinet_socket *listener = NULL;
	struct ev_uinet_ctx *soctx = NULL;
	struct uinet_in_addr addr;
	int optlen, optval;
	int error;
	struct uinet_sockaddr_in sin;

	if (uinet_inet_pton(UINET_AF_INET, cfg->listen_addr, &addr) <= 0) {
		printf("Malformed address %s\n", cfg->listen_addr);
		goto fail;
	}

	error = uinet_socreate(UINET_PF_INET, &listener, UINET_SOCK_STREAM, 0);
	if (0 != error) {
		printf("Listen socket creation failed (%d)\n", error);
		goto fail;
	}

	soctx = ev_uinet_attach(listener);
	if (NULL == soctx) {
		printf("Failed to alloc libev socket context\n");
		goto fail;
	}

	if (cfg->interface->promisc) {
		if ((error = uinet_make_socket_promiscuous(listener, cfg->interface->cdom))) {
			printf("Failed to make listen socket promiscuous (%d)\n", error);
			goto fail;
		}
	}

	uinet_sosetnonblocking(listener, 1);

	/* Set NODELAY on the listen socket so it will be set on all
	 * accepted sockets via inheritance.
	 */
	optlen = sizeof(optval);
	optval = 1;
	if ((error = uinet_sosetsockopt(listener, UINET_IPPROTO_TCP, UINET_TCP_NODELAY, &optval, optlen)))
		goto fail;

	echo = malloc(sizeof(struct echo_context));
	if (NULL == echo) {
		goto fail;
	}


	/* Listen on all VLANs */
	if ((error = uinet_setl2info2(listener, NULL, NULL, UINET_INL2I_TAG_ANY, NULL))) {
		printf("Listen socket L2 info set failed (%d)\n", error);
		goto fail;
	}

	echo->loop = loop;
	echo->listener = listener;
	echo->verbose = cfg->verbose;
	echo->listen_cdom = cfg->interface->cdom;

	memset(&sin, 0, sizeof(struct uinet_sockaddr_in));
	sin.sin_len = sizeof(struct uinet_sockaddr_in);
	sin.sin_family = UINET_AF_INET;
	sin.sin_addr = addr;
	sin.sin_port = htons(cfg->listen_port);
	error = uinet_sobind(listener, (struct uinet_sockaddr *)&sin);
	if (0 != error) {
		printf("bind failed\n");
		goto fail;
	}
	
	error = uinet_solisten(echo->listener, -1);
	if (0 != error)
		goto fail;

	if (echo->verbose) {
		char buf[32];

		printf("Listening on %s:%u\n", uinet_inet_ntoa(addr, buf, sizeof(buf)), cfg->listen_port);
	}

	ev_init(&echo->listen_watcher, accept_cb);
	ev_uinet_set(&echo->listen_watcher, soctx, EV_READ);
	echo->listen_watcher.data = echo;
	ev_uinet_start(loop, &echo->listen_watcher);

	return (echo);

fail:
	if (soctx) ev_uinet_detach(soctx);
	if (listener) uinet_soclose(listener);
	if (echo) free(echo);

	return (NULL);
}
static void
nproxy_accept_cb(struct ev_loop *loop, ev_uinet *w, int revents)
{
	struct uinet_demo_nproxy *nproxy = w->data;
	struct uinet_socket *newso = NULL;
	struct ev_uinet_ctx *soctx = NULL;
	struct nproxy_splice *splice = NULL;
	int error;

	if (0 != (error = uinet_soaccept(w->so, NULL, &newso))) {
		printf("%s: Accept failed (%d)\n", nproxy->cfg.name, error);
	} else {
		if (nproxy->cfg.verbose)
			printf("%s: Accept succeeded\n", nproxy->cfg.name);
		
		soctx = ev_uinet_attach(newso);
		if (NULL == soctx) {
			printf("%s: Failed to alloc libev context for new splice\n",
			       nproxy->cfg.name);
			goto fail;
		}

		splice = malloc(sizeof(*splice));
		if (NULL == splice) {
			printf("%s: Failed to alloc new splice context\n",
			       nproxy->cfg.name);
			goto fail;
		}
		splice->nproxy = nproxy;
		splice->id = nproxy->next_id++;
		splice->verbose = nproxy->cfg.verbose;
		splice->inbound.name = "inbound";
		splice->inbound.splice = splice;
		splice->inbound.other_side = &splice->outbound;
		splice->outbound.name = "outbound";
		splice->outbound.splice = splice;
		splice->outbound.other_side = &splice->inbound;
		
		ev_init(&splice->inbound.connected_watcher, nproxy_inbound_connected_cb);
		ev_uinet_set(&splice->inbound.connected_watcher, soctx, EV_WRITE);
		splice->inbound.connected_watcher.data = splice;
		ev_uinet_start(loop, &splice->inbound.connected_watcher);

		ev_init(&splice->inbound.writable_watcher, nproxy_writable_cb);
		ev_uinet_set(&splice->inbound.writable_watcher, soctx, EV_WRITE);
		splice->inbound.writable_watcher.data = &splice->inbound;
		/* will be started as necessary by the outbound copy watcher */

		ev_init(&splice->inbound.copy_watcher, nproxy_copy_cb);
		ev_uinet_set(&splice->inbound.copy_watcher, soctx, EV_READ);
		splice->inbound.copy_watcher.data = &splice->inbound;
		/* will be started when the outbound connection is established */
	}

	return;

fail:
	if (splice) free(splice);
	if (soctx) ev_uinet_detach(soctx);
	if (newso) uinet_soclose(newso);
}
static void
nproxy_inbound_connected_cb(struct ev_loop *loop, ev_uinet *w, int revents)
{
	struct nproxy_splice *splice = w->data;
	struct uinet_demo_nproxy *nproxy = splice->nproxy;
	struct uinet_sockaddr_in *sin_local, *sin_foreign;
	struct uinet_socket *outbound_socket = NULL;
	struct ev_uinet_ctx *soctx = NULL;
	struct uinet_in_l2info l2i;
	char buf1[32], buf2[32];
	int optlen, optval;
	int error;
	
	uinet_sogetsockaddr(w->so, (struct uinet_sockaddr **)&sin_local);
	uinet_sogetpeeraddr(w->so, (struct uinet_sockaddr **)&sin_foreign);
	if (splice->verbose)
		printf("%s: splice %llu: inbound connection established (local=%s:%u foreign=%s:%u)\n",
		       nproxy->cfg.name, (unsigned long long)splice->id,
		       uinet_inet_ntoa(sin_local->sin_addr, buf1, sizeof(buf1)), ntohs(sin_local->sin_port),
		       uinet_inet_ntoa(sin_foreign->sin_addr, buf2, sizeof(buf2)), ntohs(sin_foreign->sin_port));

	if ((nproxy->cfg.copy_mode & UINET_IP_COPY_MODE_MAYBE) &&
	    ((uinet_sogetserialno(w->so) % nproxy->cfg.copy_every) == 0)){
		if ((error =
		     uinet_sosetcopymode(w->so, UINET_IP_COPY_MODE_RX|UINET_IP_COPY_MODE_ON,
					 nproxy->cfg.copy_limit, nproxy->cfg.copy_uif)))
			printf("%s: splice %llu: Failed to set copy mode (%d)\n",
			       nproxy->cfg.name, (unsigned long long)splice->id, error);	
	}

	/* don't need this watcher anymore */
	ev_uinet_stop(loop, w);
	
	/* Create the outbound connection */
	error = uinet_socreate(nproxy->cfg.uinst, UINET_PF_INET, &outbound_socket, UINET_SOCK_STREAM, 0);
	if (error != 0) {
		printf("%s: splice %llu: outbound socket creation failed (%d)\n",
		       nproxy->cfg.name, (unsigned long long)splice->id, error);
		goto fail;
	}

	if ((error = uinet_make_socket_promiscuous(outbound_socket, nproxy->outbound_if))) {
		printf("%s: splice %llu: failed to make outbound socket promiscuous (%d)\n",
		       nproxy->cfg.name, (unsigned long long)splice->id, error);
		goto fail;
	}
	
	/*
	 * Socket needs to be non-blocking to work with the event system
	 */
	uinet_sosetnonblocking(outbound_socket, 1);

	optlen = sizeof(optval);
	optval = 1;
	error = uinet_sosetsockopt(outbound_socket, UINET_IPPROTO_TCP, UINET_TCP_NODELAY,
				   &optval, optlen);
	if (error != 0) {
		printf("%s: splice %llu: failed to set TCP_NODELAY on outbound socket (%d)\n",
		       nproxy->cfg.name, (unsigned long long)splice->id, error);
		goto fail;
	}

	/* Bind to the foreign address of the inbound connection */
	error = uinet_sobind(outbound_socket, (struct uinet_sockaddr *)sin_foreign);
	if (error != 0) {
		printf("%s: splice %llu: outbound socket bind failed (%d)\n",
		       nproxy->cfg.name, (unsigned long long)splice->id, error);
		goto fail;
	}

	/*
	 * Use the same MAC addrs and VLAN tag stack as the inbound
	 * connection, which requires swapping the local and foreign MAC
	 * addrs.
	 */
	error = uinet_getl2info(w->so, &l2i);
	if (error != 0) {
		printf("%s: splice %llu: unable to get l2info from inbound socket (%d)\n",
		       nproxy->cfg.name, (unsigned long long)splice->id, error);
		goto fail;
	}

	error = uinet_setl2info2(outbound_socket,
				 l2i.inl2i_foreign_addr, l2i.inl2i_local_addr,
				 l2i.inl2i_flags, &l2i.inl2i_tagstack);
	if (error != 0) {
		printf("%s: splice %llu: unable to set l2info for outbound socket (%d)\n",
		       nproxy->cfg.name, (unsigned long long)splice->id, error);
		goto fail;
	}

	soctx = ev_uinet_attach(outbound_socket);
	if (NULL == soctx) {
		printf("%s: splice %llu: failed to alloc libev context for outbound socket\n",
		       nproxy->cfg.name, (unsigned long long)splice->id);
		goto fail;
	}

	/* The connection target is the local address of the inbound connection */
	error = uinet_soconnect(outbound_socket, (struct uinet_sockaddr *)sin_local);
	if ((error != 0) && (error != UINET_EINPROGRESS)) {
		printf("%s: splice %llu: outbound socket connect failed (%d)\n",
		       nproxy->cfg.name, (unsigned long long)splice->id, error);
		goto fail;
	}
	
	uinet_free_sockaddr((struct uinet_sockaddr *)sin_local);
	uinet_free_sockaddr((struct uinet_sockaddr *)sin_foreign);

	ev_init(&splice->outbound.connected_watcher, nproxy_outbound_connected_cb);
	ev_uinet_set(&splice->outbound.connected_watcher, soctx, EV_WRITE);
	splice->outbound.connected_watcher.data = splice;
	ev_uinet_start(loop, &splice->outbound.connected_watcher);

	ev_init(&splice->outbound.writable_watcher, nproxy_writable_cb);
	ev_uinet_set(&splice->outbound.writable_watcher, soctx, EV_WRITE);
	splice->outbound.writable_watcher.data = &splice->outbound;
	/* will be started as necessary by the inbound copy watcher */
	
	ev_init(&splice->outbound.copy_watcher, nproxy_copy_cb);
	ev_uinet_set(&splice->outbound.copy_watcher, soctx, EV_READ);
	splice->outbound.copy_watcher.data = &splice->outbound;
	/* will be started when the outbound connection is established */
	
	return;

 fail:
	uinet_free_sockaddr((struct uinet_sockaddr *)sin_local);
	uinet_free_sockaddr((struct uinet_sockaddr *)sin_foreign);
	if (soctx) ev_uinet_detach(soctx);
	if (outbound_socket) uinet_soclose(outbound_socket);
	free(splice);
}