summaryrefslogtreecommitdiffstats
path: root/src/vnet
diff options
context:
space:
mode:
Diffstat (limited to 'src/vnet')
-rw-r--r--src/vnet/session/application.c21
-rw-r--r--src/vnet/session/application_interface.h14
-rw-r--r--src/vnet/session/session.c43
-rw-r--r--src/vnet/session/session.h2
-rw-r--r--src/vnet/session/session_node.c25
-rw-r--r--src/vnet/session/session_types.h3
-rw-r--r--src/vnet/session/transport.c7
-rw-r--r--src/vnet/session/transport.h3
-rw-r--r--src/vnet/tcp/tcp.c26
9 files changed, 139 insertions, 5 deletions
diff --git a/src/vnet/session/application.c b/src/vnet/session/application.c
index 8abec7797ec..83106ef172e 100644
--- a/src/vnet/session/application.c
+++ b/src/vnet/session/application.c
@@ -1385,6 +1385,27 @@ vnet_unlisten (vnet_unlisten_args_t * a)
}
int
+vnet_shutdown_session (vnet_shutdown_args_t *a)
+{
+ app_worker_t *app_wrk;
+ session_t *s;
+
+ s = session_get_from_handle_if_valid (a->handle);
+ if (!s)
+ return SESSION_E_NOSESSION;
+
+ app_wrk = app_worker_get (s->app_wrk_index);
+ if (app_wrk->app_index != a->app_index)
+ return SESSION_E_OWNER;
+
+ /* We're peeking into another's thread pool. Make sure */
+ ASSERT (s->session_index == session_index_from_handle (a->handle));
+
+ session_half_close (s);
+ return 0;
+}
+
+int
vnet_disconnect_session (vnet_disconnect_args_t * a)
{
app_worker_t *app_wrk;
diff --git a/src/vnet/session/application_interface.h b/src/vnet/session/application_interface.h
index 961561547d7..d3bfb3b03dd 100644
--- a/src/vnet/session/application_interface.h
+++ b/src/vnet/session/application_interface.h
@@ -141,6 +141,12 @@ typedef struct _vnet_connect_args
u32 api_context;
} vnet_connect_args_t;
+typedef struct _vnet_shutdown_args_t
+{
+ session_handle_t handle;
+ u32 app_index;
+} vnet_shutdown_args_t;
+
typedef struct _vnet_disconnect_args_t
{
session_handle_t handle;
@@ -266,6 +272,7 @@ int vnet_application_detach (vnet_app_detach_args_t * a);
int vnet_listen (vnet_listen_args_t * a);
int vnet_connect (vnet_connect_args_t * a);
int vnet_unlisten (vnet_unlisten_args_t * a);
+int vnet_shutdown_session (vnet_shutdown_args_t *a);
int vnet_disconnect_session (vnet_disconnect_args_t * a);
int vnet_app_add_cert_key_pair (vnet_app_add_cert_key_pair_args_t * a);
@@ -426,6 +433,13 @@ typedef struct session_connected_msg_
transport_endpoint_t lcl;
} __clib_packed session_connected_msg_t;
+typedef struct session_shutdown_msg_
+{
+ u32 client_index;
+ u32 context;
+ session_handle_t handle;
+} __clib_packed session_shutdown_msg_t;
+
typedef struct session_disconnect_msg_
{
u32 client_index;
diff --git a/src/vnet/session/session.c b/src/vnet/session/session.c
index 1fac5ed2bb9..eba9f64ca22 100644
--- a/src/vnet/session/session.c
+++ b/src/vnet/session/session.c
@@ -97,9 +97,10 @@ session_send_io_evt_to_thread_custom (void *data, u32 thread_index,
int
session_send_ctrl_evt_to_thread (session_t * s, session_evt_type_t evt_type)
{
- /* only events supported are disconnect and reset */
- ASSERT (evt_type == SESSION_CTRL_EVT_CLOSE
- || evt_type == SESSION_CTRL_EVT_RESET);
+ /* only events supported are disconnect, shutdown and reset */
+ ASSERT (evt_type == SESSION_CTRL_EVT_CLOSE ||
+ evt_type == SESSION_CTRL_EVT_HALF_CLOSE ||
+ evt_type == SESSION_CTRL_EVT_RESET);
return session_send_evt_to_thread (s, 0, s->thread_index, evt_type);
}
@@ -1087,7 +1088,9 @@ session_transport_closed_notify (transport_connection_t * tc)
return;
/* Transport thinks that app requested close but it actually didn't.
- * Can happen for tcp if fin and rst are received in close succession. */
+ * Can happen for tcp:
+ * 1)if fin and rst are received in close succession.
+ * 2)if app shutdown the connection. */
if (s->session_state == SESSION_STATE_READY)
{
session_transport_closing_notify (tc);
@@ -1399,6 +1402,20 @@ session_stop_listen (session_t * s)
}
/**
+ * Initialize session half-closing procedure.
+ *
+ * Note that half-closing will not change the state of the session.
+ */
+void
+session_half_close (session_t *s)
+{
+ if (!s)
+ return;
+
+ session_program_transport_ctrl_evt (s, SESSION_CTRL_EVT_HALF_CLOSE);
+}
+
+/**
* Initialize session closing procedure.
*
* Request is always sent to session node to ensure that all outstanding
@@ -1439,6 +1456,24 @@ session_reset (session_t * s)
}
/**
+ * Notify transport the session can be half-disconnected.
+ *
+ * Must be called from the session's thread.
+ */
+void
+session_transport_half_close (session_t *s)
+{
+ /* Only READY session can be half-closed */
+ if (s->session_state != SESSION_STATE_READY)
+ {
+ return;
+ }
+
+ transport_half_close (session_get_transport_proto (s), s->connection_index,
+ s->thread_index);
+}
+
+/**
* Notify transport the session can be disconnected. This should eventually
* result in a delete notification that allows us to cleanup session state.
* Called for both active/passive disconnects.
diff --git a/src/vnet/session/session.h b/src/vnet/session/session.h
index 1a59d7df403..17a870dfcf0 100644
--- a/src/vnet/session/session.h
+++ b/src/vnet/session/session.h
@@ -455,8 +455,10 @@ session_clone_safe (u32 session_index, u32 thread_index)
int session_open (u32 app_index, session_endpoint_t * tep, u32 opaque);
int session_listen (session_t * s, session_endpoint_cfg_t * sep);
int session_stop_listen (session_t * s);
+void session_half_close (session_t *s);
void session_close (session_t * s);
void session_reset (session_t * s);
+void session_transport_half_close (session_t *s);
void session_transport_close (session_t * s);
void session_transport_reset (session_t * s);
void session_transport_cleanup (session_t * s);
diff --git a/src/vnet/session/session_node.c b/src/vnet/session/session_node.c
index d30df33a5e7..b68ff53dd7a 100644
--- a/src/vnet/session/session_node.c
+++ b/src/vnet/session/session_node.c
@@ -195,6 +195,22 @@ session_mq_connect_uri_handler (void *data)
}
static void
+session_mq_shutdown_handler (void *data)
+{
+ session_shutdown_msg_t *mp = (session_shutdown_msg_t *) data;
+ vnet_shutdown_args_t _a, *a = &_a;
+ application_t *app;
+
+ app = application_lookup (mp->client_index);
+ if (!app)
+ return;
+
+ a->app_index = app->app_index;
+ a->handle = mp->handle;
+ vnet_shutdown_session (a);
+}
+
+static void
session_mq_disconnect_handler (void *data)
{
session_disconnect_msg_t *mp = (session_disconnect_msg_t *) data;
@@ -1287,6 +1303,12 @@ session_event_dispatch_ctrl (session_worker_t * wrk, session_evt_elt_t * elt)
fp = e->rpc_args.fp;
(*fp) (e->rpc_args.arg);
break;
+ case SESSION_CTRL_EVT_HALF_CLOSE:
+ s = session_get_from_handle_if_valid (e->session_handle);
+ if (PREDICT_FALSE (!s))
+ break;
+ session_transport_half_close (s);
+ break;
case SESSION_CTRL_EVT_CLOSE:
s = session_get_from_handle_if_valid (e->session_handle);
if (PREDICT_FALSE (!s))
@@ -1314,6 +1336,9 @@ session_event_dispatch_ctrl (session_worker_t * wrk, session_evt_elt_t * elt)
case SESSION_CTRL_EVT_CONNECT_URI:
session_mq_connect_uri_handler (session_evt_ctrl_data (wrk, elt));
break;
+ case SESSION_CTRL_EVT_SHUTDOWN:
+ session_mq_shutdown_handler (session_evt_ctrl_data (wrk, elt));
+ break;
case SESSION_CTRL_EVT_DISCONNECT:
session_mq_disconnect_handler (session_evt_ctrl_data (wrk, elt));
break;
diff --git a/src/vnet/session/session_types.h b/src/vnet/session/session_types.h
index 2c3db5f18b5..821aac9394d 100644
--- a/src/vnet/session/session_types.h
+++ b/src/vnet/session/session_types.h
@@ -331,6 +331,7 @@ typedef enum
SESSION_IO_EVT_BUILTIN_RX,
SESSION_IO_EVT_BUILTIN_TX,
SESSION_CTRL_EVT_RPC,
+ SESSION_CTRL_EVT_HALF_CLOSE,
SESSION_CTRL_EVT_CLOSE,
SESSION_CTRL_EVT_RESET,
SESSION_CTRL_EVT_BOUND,
@@ -344,6 +345,7 @@ typedef enum
SESSION_CTRL_EVT_REQ_WORKER_UPDATE,
SESSION_CTRL_EVT_WORKER_UPDATE,
SESSION_CTRL_EVT_WORKER_UPDATE_REPLY,
+ SESSION_CTRL_EVT_SHUTDOWN,
SESSION_CTRL_EVT_DISCONNECT,
SESSION_CTRL_EVT_CONNECT,
SESSION_CTRL_EVT_CONNECT_URI,
@@ -371,6 +373,7 @@ typedef enum
_ (CONNECT, connect) \
_ (CONNECT_URI, connect_uri) \
_ (CONNECTED, connected) \
+ _ (SHUTDOWN, shutdown) \
_ (DISCONNECT, disconnect) \
_ (DISCONNECTED, disconnected) \
_ (DISCONNECTED_REPLY, disconnected_reply) \
diff --git a/src/vnet/session/transport.c b/src/vnet/session/transport.c
index 18ae3ca5188..2c88a4c4931 100644
--- a/src/vnet/session/transport.c
+++ b/src/vnet/session/transport.c
@@ -318,6 +318,13 @@ transport_connect (transport_proto_t tp, transport_endpoint_cfg_t * tep)
}
void
+transport_half_close (transport_proto_t tp, u32 conn_index, u8 thread_index)
+{
+ if (tp_vfts[tp].half_close)
+ tp_vfts[tp].half_close (conn_index, thread_index);
+}
+
+void
transport_close (transport_proto_t tp, u32 conn_index, u8 thread_index)
{
tp_vfts[tp].close (conn_index, thread_index);
diff --git a/src/vnet/session/transport.h b/src/vnet/session/transport.h
index 67583d23be0..447552c539e 100644
--- a/src/vnet/session/transport.h
+++ b/src/vnet/session/transport.h
@@ -74,6 +74,7 @@ typedef struct _transport_proto_vft
u32 (*start_listen) (u32 session_index, transport_endpoint_t * lcl);
u32 (*stop_listen) (u32 conn_index);
int (*connect) (transport_endpoint_cfg_t * rmt);
+ void (*half_close) (u32 conn_index, u32 thread_index);
void (*close) (u32 conn_index, u32 thread_index);
void (*reset) (u32 conn_index, u32 thread_index);
void (*cleanup) (u32 conn_index, u32 thread_index);
@@ -134,6 +135,8 @@ do { \
} while (0)
int transport_connect (transport_proto_t tp, transport_endpoint_cfg_t * tep);
+void transport_half_close (transport_proto_t tp, u32 conn_index,
+ u8 thread_index);
void transport_close (transport_proto_t tp, u32 conn_index, u8 thread_index);
void transport_reset (transport_proto_t tp, u32 conn_index, u8 thread_index);
u32 transport_start_listen (transport_proto_t tp, u32 session_index,
diff --git a/src/vnet/tcp/tcp.c b/src/vnet/tcp/tcp.c
index 90b832cd73d..16bf9451709 100644
--- a/src/vnet/tcp/tcp.c
+++ b/src/vnet/tcp/tcp.c
@@ -354,7 +354,6 @@ tcp_program_cleanup (tcp_worker_ctx_t * wrk, tcp_connection_t * tc)
* 2) TIME_WAIT (active close) whereby after 2MSL the 2MSL timer triggers
* and cleanup is called.
*
- * N.B. Half-close connections are not supported
*/
void
tcp_connection_close (tcp_connection_t * tc)
@@ -426,6 +425,30 @@ tcp_connection_close (tcp_connection_t * tc)
}
static void
+tcp_session_half_close (u32 conn_index, u32 thread_index)
+{
+ tcp_worker_ctx_t *wrk;
+ tcp_connection_t *tc;
+
+ tc = tcp_connection_get (conn_index, thread_index);
+ wrk = tcp_get_worker (tc->c_thread_index);
+
+ /* If the connection is not in ESTABLISHED state, ignore it */
+ if (tc->state != TCP_STATE_ESTABLISHED)
+ return;
+ if (!transport_max_tx_dequeue (&tc->connection))
+ tcp_send_fin (tc);
+ else
+ tc->flags |= TCP_CONN_FINPNDG;
+ tcp_connection_set_state (tc, TCP_STATE_FIN_WAIT_1);
+ /* Set a timer in case the peer stops responding. Otherwise the
+ * connection will be stuck here forever. */
+ ASSERT (tc->timers[TCP_TIMER_WAITCLOSE] == TCP_TIMER_HANDLE_INVALID);
+ tcp_timer_set (&wrk->timer_wheel, tc, TCP_TIMER_WAITCLOSE,
+ tcp_cfg.finwait1_time);
+}
+
+static void
tcp_session_close (u32 conn_index, u32 thread_index)
{
tcp_connection_t *tc;
@@ -1316,6 +1339,7 @@ const static transport_proto_vft_t tcp_proto = {
.get_half_open = tcp_half_open_session_get_transport,
.attribute = tcp_session_attribute,
.connect = tcp_session_open,
+ .half_close = tcp_session_half_close,
.close = tcp_session_close,
.cleanup = tcp_session_cleanup,
.cleanup_ho = tcp_session_cleanup_ho,