Commit 1a046cd2 authored by kelsey's avatar kelsey
Browse files

Primarily modifications related to containered alerts and discovery

Additionally, maintenance work on connection handling and code structure
parent d20fa8ce
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ lib_LTLIBRARIES = \
libtlmsp_cfg_la_SOURCES = \
	libtlmsp-cfg/api.c \
	libtlmsp-cfg/format.c \
	libtlmsp-cfg/openssl.c \
	libtlmsp-cfg/parse.c \
	libtlmsp-cfg/print.c
libtlmsp_cfg_la_LIBADD = \
@@ -95,6 +96,7 @@ tlmsp_server_LDADD = \

pkginclude_HEADERS = \
	libtlmsp-cfg/libtlmsp-cfg.h \
	libtlmsp-cfg/libtlmsp-cfg-openssl.h \
	libtlmsp-util/libtlmsp-util.h


+110 −57
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <inttypes.h>
#include <libgen.h>
#include <libtlmsp-cfg.h>
#include <libtlmsp-cfg-openssl.h>
#include <libtlmsp-util.h>
#include <stdbool.h>
#include <stdio.h>
@@ -61,7 +62,8 @@ static void free_client_cb(void *app_data);
static bool new_connection(struct client_state *client,
	                   const TLMSP_ReconnectState *reconnect_state);
static void show_connection_info(void *app_data);
static int validate_discovery_results(SSL *ssl, void *arg);
static int validate_discovery_results(SSL *ssl, void *arg,
                                      TLMSP_Middleboxes *middleboxes);
static void free_connection_cb(void *app_data);
static void connection_died(struct demo_connection *conn);
static void conn_cb(EV_P_ ev_io *w, int revents);
@@ -240,7 +242,7 @@ new_connection(struct client_state *client,
{
	struct connection_state *conn_state;
	struct demo_connection *conn;
	struct sockaddr *addr;
	struct sockaddr *addr = NULL;
	socklen_t addr_len;
	uint8_t *first_hop_addr = NULL;
	char *first_hop_addr_str;
@@ -351,9 +353,11 @@ new_connection(struct client_state *client,
		demo_print_errno("Connect failed");
		goto error;
	}
	free(addr);
	addr = NULL;

	if (!demo_connection_init_io(conn, client->ssl_ctx, sock, client->loop,
		(demo_connection_failed_cb_t)connection_died, conn_cb, EV_WRITE))
		connection_died, NULL, conn_cb, EV_WRITE))
		goto error;

	SSL_set_connect_state(conn->ssl);
@@ -366,7 +370,6 @@ new_connection(struct client_state *client,
			demo_print_error_ssl_errq("Failed to set reconnect state");
			goto error;
		}
		TLMSP_reconnect_state_free(reconnect_state);
	}

	demo_connection_start_io(conn);
@@ -374,6 +377,7 @@ new_connection(struct client_state *client,
	return (true);

error:
	free(addr);
	OPENSSL_free(first_hop_addr);
	demo_connection_free(conn);
	return (false);
@@ -394,23 +398,19 @@ free_connection_cb(void *app_data)
	struct connection_state *conn_state = app_data;

	TLMSP_reconnect_state_free(conn_state->reconnect_state);
	free(conn_state->read_buffer);
	free(conn_state);
}

static int
validate_discovery_results(SSL *ssl, void *arg)
validate_discovery_results(SSL *ssl, void *arg, TLMSP_Middleboxes *middleboxes)
{
	struct demo_connection *conn = arg;
	const struct tlmsp_cfg *cfg = conn->app->cfg;
	TLMSP_Middleboxes *middleboxes;
	int result;

	middleboxes = TLMSP_get_middleboxes_instance(conn->ssl);
	if (middleboxes == NULL)
		return (0);
	result = tlmsp_cfg_validate_middlebox_list_client_openssl(cfg,
		middleboxes);
	TLMSP_middleboxes_free(middleboxes);
	return (result);
}

@@ -440,11 +440,67 @@ static void
conn_cb(EV_P_ ev_io *w, int revents)
{
	struct demo_connection *conn = w->data;
	struct connection_state *conn_state = conn->app_data;
	int result;
	int ssl_error;
	bool pending_writes;

	demo_connection_pause_io(conn);
	demo_connection_events_arrived(conn, revents);

	switch (conn->phase) {
	case DEMO_CONNECTION_PHASE_HANDSHAKE:
		result = SSL_connect(conn->ssl);
		switch (result) {
		case 0:
			demo_conn_print_error(conn, "Handshake terminated by protocol");
			connection_died(conn);
			return;
			break;
		case 1:
			demo_conn_log(1, conn, "Handshake complete");
			demo_connection_wait_for(conn, EV_READ);
			if (!demo_connection_handshake_complete(conn)) {
				connection_died(conn);
				return;
			}
			break;
		default:
			ssl_error = SSL_get_error(conn->ssl, result);
			switch (ssl_error) {
			case SSL_ERROR_WANT_RECONNECT:
				demo_conn_log(5, conn, "SSL_ERROR_WANT_RECONNECT");
				/*
				 * Re-establish outbound connection and
				 * re-enter handshake.
				 */
				conn_state->reconnect_state =
				    TLMSP_get_reconnect_state(conn->ssl);
				if (conn_state->reconnect_state == NULL)
					demo_conn_print_error_ssl_errq(conn,
					    "Reconnect state retrieval failed");
				connection_died(conn);
				return;
				break;
			case SSL_ERROR_WANT_READ:
				demo_conn_log(5, conn, "SSL_ERROR_WANT_READ");
				demo_connection_wait_for(conn, EV_READ);
				break;
			case SSL_ERROR_WANT_WRITE:
				demo_conn_log(5, conn, "SSL_ERROR_WANT_WRITE");
				demo_connection_wait_for(conn, EV_WRITE);
				break;
			default:
				demo_conn_print_error_ssl(conn, ssl_error,
				    "Handshake terminated due to fatal error");
				connection_died(conn);
				return;
				break;
			}
			break;
		}
		break;
	case DEMO_CONNECTION_PHASE_APPLICATION:
		/*
		 * The general approach is that we prioritize writes over
		 * reads - we work on pending write data until it is all
@@ -488,6 +544,14 @@ conn_cb(EV_P_ ev_io *w, int revents)
		 */
		if (demo_connection_wait_events(conn) == 0)
			demo_connection_wait_for(conn, EV_READ);
		break;
	default:
		demo_conn_print_error(conn,
		    "Unexpected connection phase %d", conn->phase);
		connection_died(conn);
		return;
		break;
	}

	demo_connection_resume_io(conn);
}
@@ -546,7 +610,7 @@ read_containers(struct demo_connection *conn)
			} else if (!TLMSP_container_readable(container)) {
				demo_conn_print_error(conn,
				    "Opaque container unexpectedly received in "
				    "context %d using containe API", context_id);
				    "context %d using container API", context_id);
				TLMSP_container_free(ssl, container);
				return (false);
			} else {
@@ -577,12 +641,6 @@ read_containers(struct demo_connection *conn)
			demo_conn_log(5, conn, "SSL_ERROR_WANT_WRITE");
			demo_connection_wait_for(conn, EV_WRITE);
			break;
		case SSL_ERROR_WANT_RECONNECT:
			demo_conn_log(5, conn, "SSL_ERROR_WANT_RECONNECT");
			conn_state->reconnect_state =
			    TLMSP_get_reconnect_state(ssl);
			result = false;
			break;
		default:
			demo_conn_print_error_ssl(conn, ssl_error,
			    "Connection terminated due to fatal read error");
@@ -665,11 +723,6 @@ write_containers(struct demo_connection *conn)
			demo_conn_log(5, conn, "SSL_ERROR_WANT_WRITE");
			demo_connection_wait_for(conn, EV_WRITE);
			break;
		case SSL_ERROR_WANT_RECONNECT:
			demo_conn_log(5, conn, "SSL_ERROR_WANT_RECONNECT");
			conn_state->reconnect_state = TLMSP_get_reconnect_state(ssl);
			result = false;
			break;
		default:
			demo_conn_print_error_ssl(conn, ssl_error,
			    "Connection terminated due to fatal write error");
+56 −1
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ struct match_groups {
};

struct demo_activity_match_state {
	bool contexts[TLMSP_CONTEXT_ID_MAX + 1];
	bool contexts[TLMSP_UTIL_CONTEXT_ID_LUT_SIZE];
	struct tlmsp_cfg_activity *activity;
	struct container_queue_range match_range;
	size_t match_offset;
@@ -95,6 +95,9 @@ static bool demo_activity_queue_payload(struct demo_connection *conn,
                                        struct tlmsp_cfg_payload *payload,
                                        struct match_groups *match_groups,
                                        bool present);
static bool demo_activity_queue_alert(struct demo_connection *conn,
                                      struct tlmsp_cfg_alert *alert,
                                      bool present);
static void demo_activity_time_triggered_cb(EV_P_ ev_timer *w, int revents);
static bool demo_activity_get_payload_data(struct demo_connection *conn,
                                           struct tlmsp_cfg_payload *payload,
@@ -351,6 +354,42 @@ demo_activity_queue_payload(struct demo_connection *conn,
	return (result);
}

static bool
demo_activity_queue_alert(struct demo_connection *conn,
    struct tlmsp_cfg_alert *alert, bool present)
{
	struct container_queue *q = &conn->write_queue;
	SSL *ssl = q->conn->ssl;
	TLMSP_Container *container;
	tlmsp_context_id_t context_id;

	context_id = alert->context ? alert->context->id : 0;
	if (present)
		demo_conn_present(conn, "Sending %s alert %d in context %u",
		    alert->level == TLMSP_CFG_ACTION_ALERT_LEVEL_WARNING ?
		    "warning" : "fatal", alert->description, context_id);

	if (!TLMSP_container_create_alert(ssl, &container, context_id,
		(alert->level << 8) | alert->description)) {
		demo_conn_print_error_ssl_errq(conn,
		    "Failed to create container for alert");
		return (false);
	}

	if (!container_queue_add(q, container)) {
		demo_conn_print_error(conn,
		    "Failed to add alert container to queue");
		TLMSP_container_free(ssl, container);
		return (false);
	}

	demo_conn_log(2, conn, "Sending %s alert %d in context %u",
	    alert->level == TLMSP_CFG_ACTION_ALERT_LEVEL_WARNING ?
	    "warning" : "fatal", alert->description, context_id);

	return (true);
}

static void
demo_activity_time_triggered_cb(EV_P_ ev_timer *w, int revents)
{
@@ -1357,12 +1396,28 @@ demo_activity_apply_actions(struct demo_connection *activity_conn,
			if (!demo_activity_queue_payload(outbound_conn,
				&action->send, match_groups, activity->present))
				return (false);
		} else if (action->alert.level != TLMSP_CFG_ACTION_ALERT_LEVEL_NONE) {
			if (!demo_activity_queue_alert(send_conn,
				&action->alert, activity->present))
				return (false);
		} else if (action->renegotiate) {
			if (!SSL_renegotiate_abbreviated(activity_conn->ssl)) {
				demo_conn_print_error_ssl_errq(activity_conn,
				    "Failed to schedule a renegotiation");
				return (false);
			}
		} else if (action->shutdown) {
			/*
			 * This will put the SSL into a state where our next
			 * attempt to read a container will return the
			 * appropriate WANT_WRITE, and we will transition into
			 * flushing the write part of the BIO.  Therefore, we
			 * just ignore any error here.
			 *
			 * We could narrow it to just ignore the expected
			 * errors.
			 */
			(void)SSL_shutdown(activity_conn->ssl);
		}
	}

+27 −19
Original line number Diff line number Diff line
@@ -71,8 +71,11 @@ void
demo_connection_shutdown(struct demo_connection *conn)
{

	if (conn->is_connected)
	if (conn->is_connected && !conn->is_shut_down) {
		demo_conn_log(1, conn, "Shutting down writes");
		shutdown(conn->socket, SHUT_WR);
		conn->is_shut_down = true;
	}
}

void
@@ -86,7 +89,8 @@ demo_connection_set_phase(struct demo_connection *conn,
bool
demo_connection_init_io(struct demo_connection *conn, SSL_CTX *ssl_ctx, int sock,
    struct ev_loop *loop, demo_connection_failed_cb_t fail_cb,
    demo_connection_cb_t cb, int initial_events)
    demo_connection_connected_cb_t connected_cb, demo_connection_cb_t cb,
    int initial_events)
{
	
	conn->ssl = SSL_new(ssl_ctx);
@@ -97,7 +101,7 @@ demo_connection_init_io(struct demo_connection *conn, SSL_CTX *ssl_ctx, int sock
	conn->socket = sock;
	conn->loop = loop;
	conn->fail_cb = fail_cb;
	conn->cb_data = conn;
	conn->connected_cb = connected_cb;

	ev_io_init(&conn->connected_watcher, demo_conn_connected_cb,
	    conn->socket, EV_WRITE);
@@ -116,7 +120,7 @@ demo_connection_init_io(struct demo_connection *conn, SSL_CTX *ssl_ctx, int sock
	conn->queues_initialized = true;

	ev_io_init(&conn->watcher, cb, conn->socket, conn->wait_events);
	conn->watcher.data = conn->cb_data;
	conn->watcher.data = conn;

	return (true);
}
@@ -126,6 +130,7 @@ demo_connection_start_io(struct demo_connection *conn)
{
	struct ev_loop *loop = conn->loop;

	demo_connection_set_phase(conn, DEMO_CONNECTION_PHASE_HANDSHAKE);
	ev_io_start(EV_A_ &conn->connected_watcher);

	return (true);
@@ -137,20 +142,21 @@ demo_conn_connected_cb(EV_P_ ev_io *w, int revents)
	struct demo_connection *conn = w->data;
	socklen_t len;

	if (conn->connected_cb)
		conn->connected_cb(conn);
	
	len = sizeof(conn->local_name);
	if (getsockname(conn->socket, (struct sockaddr *)&conn->local_name,
		&len) == -1) {
		demo_conn_print_errno(conn, "Could not get local address");
		conn->io_error = true;
		conn->fail_cb(conn->cb_data);
		conn->fail_cb(conn);
		return;
	}
	len = sizeof(conn->remote_name);
	if (getpeername(conn->socket, (struct sockaddr *)&conn->remote_name,
		&len) == -1) {
		demo_conn_print_errno(conn, "Could not get remote address");
		conn->io_error = true;
		conn->fail_cb(conn->cb_data);
		conn->fail_cb(conn);
		return;
	}
	demo_conn_log_sockaddr(1, conn, "Local  address is ",
@@ -158,13 +164,6 @@ demo_conn_connected_cb(EV_P_ ev_io *w, int revents)
	demo_conn_log_sockaddr(1, conn, "Remote address is ",
	    (struct sockaddr *)&conn->remote_name);

	/*
	 * XXX Once the client and server are structured to detect handshake
	 * complete, this will move to that point in client/server.
	 */
	if (!conn->splice)
		demo_connection_handshake_complete(conn);

	conn->is_connected = true;
	ev_io_stop(EV_A_ &conn->connected_watcher);
	ev_io_start(EV_A_ &conn->watcher);
@@ -173,6 +172,14 @@ demo_conn_connected_cb(EV_P_ ev_io *w, int revents)
bool
demo_connection_handshake_complete(struct demo_connection *conn)
{
	
	demo_connection_set_phase(conn, DEMO_CONNECTION_PHASE_APPLICATION);

	if (conn->initial_handshake_complete)
		return (true);

	conn->initial_handshake_complete = true;

	if (!demo_activity_conn_queue_initial(conn)) {
		demo_conn_print_error(conn,
		    "Failed to queue initial send data for write");
@@ -283,7 +290,7 @@ void
demo_connection_free(struct demo_connection *conn)
{

	demo_conn_log(1, conn, "Shutting down");
	demo_conn_log(1, conn, "Closing");

	if (!conn->app->uses_splices)
		demo_app_remove_connection(conn->app, conn);
@@ -301,6 +308,7 @@ demo_connection_free(struct demo_connection *conn)

	demo_activity_conn_tear_down_time_triggered(conn);

	free(conn->activity_states);
	free(conn);
}

+9 −5
Original line number Diff line number Diff line
@@ -20,11 +20,13 @@
struct demo_app;
struct demo_splice;
struct tlmsp_cfg_activity;
struct demo_connection;

typedef void (*demo_connection_connected_cb_t)(struct demo_connection *conn);
typedef void (*demo_connection_cb_t)(EV_P_ struct ev_io *w, int revents);
typedef void (*demo_connection_failed_cb_t)(void *cb_data);
typedef void (*demo_connection_free_cb_t)(void *);
typedef void (*demo_connection_show_info_cb_t)(void *);
typedef void (*demo_connection_failed_cb_t)(struct demo_connection *conn);
typedef void (*demo_connection_free_cb_t)(void *app_data);
typedef void (*demo_connection_show_info_cb_t)(void *app_data);

enum demo_connection_phase {
	DEMO_CONNECTION_PHASE_UNKNOWN,
@@ -37,13 +39,14 @@ struct demo_connection {
	struct demo_app *app;
	void *app_data;
	demo_connection_failed_cb_t fail_cb;
	void *cb_data;
	demo_connection_connected_cb_t connected_cb;
	struct demo_splice *splice; /* only used by middlebox */
	struct demo_connection *other_side; /* only used when there is a splice */
	bool to_client; /* only used when there is a splice */
	bool is_connected;
	bool initial_handshake_complete;
	bool is_shut_down;
	bool read_eof;  /* read returned SSL_ERROR_ZERO_RETURN */
	bool io_error;  /* other than eof */
	struct sockaddr_storage local_name;
	struct sockaddr_storage remote_name;
	uint64_t id;
@@ -92,6 +95,7 @@ bool demo_connection_handshake_complete(struct demo_connection *conn);
bool demo_connection_init_io(struct demo_connection *conn, SSL_CTX *ssl_ctx,
                             int sock, struct ev_loop *loop,
                             demo_connection_failed_cb_t fail_cb,
                             demo_connection_connected_cb_t connected_cb,
                             demo_connection_cb_t cb, int initial_events);
bool demo_connection_start_io(struct demo_connection *conn);
void demo_connection_pause_io(struct demo_connection *conn);
Loading