allocatable
allocator
allowlist
+alpn
Altivec
amine
analyse
--- /dev/null
+package main
+
+import (
+ . "fd.io/hs-test/infra"
+)
+
+func init() {
+ RegisterVethTests(TlsAlpMatchTest, TlsAlpnOverlapMatchTest, TlsAlpnServerPriorityMatchTest, TlsAlpnMismatchTest, TlsAlpnEmptyServerListTest, TlsAlpnEmptyClientListTest)
+}
+
+func TlsAlpMatchTest(s *VethsSuite) {
+ s.Log(s.Containers.ServerVpp.VppInstance.Vppctl("test alpn server alpn-proto1 2 uri tls://0.0.0.0:123"))
+
+ uri := "tls://" + s.Interfaces.Server.Ip4AddressString() + ":123"
+ o := s.Containers.ClientVpp.VppInstance.Vppctl("test alpn client alpn-proto1 2 uri " + uri)
+ s.Log(o)
+ s.AssertNotContains(o, "connect failed")
+ s.AssertNotContains(o, "timeout")
+ // selected based on 1:1 match
+ s.AssertContains(o, "ALPN selected: h2")
+}
+
+func TlsAlpnOverlapMatchTest(s *VethsSuite) {
+ s.Log(s.Containers.ServerVpp.VppInstance.Vppctl("test alpn server alpn-proto1 2 alpn-proto2 1 uri tls://0.0.0.0:123"))
+
+ uri := "tls://" + s.Interfaces.Server.Ip4AddressString() + ":123"
+ o := s.Containers.ClientVpp.VppInstance.Vppctl("test alpn client alpn-proto1 3 alpn-proto2 2 uri " + uri)
+ s.Log(o)
+ s.AssertNotContains(o, "connect failed")
+ s.AssertNotContains(o, "timeout")
+ // selected based on overlap
+ s.AssertContains(o, "ALPN selected: h2")
+}
+
+func TlsAlpnServerPriorityMatchTest(s *VethsSuite) {
+ s.Log(s.Containers.ServerVpp.VppInstance.Vppctl("test alpn server alpn-proto1 2 alpn-proto2 1 uri tls://0.0.0.0:123"))
+
+ uri := "tls://" + s.Interfaces.Server.Ip4AddressString() + ":123"
+ o := s.Containers.ClientVpp.VppInstance.Vppctl("test alpn client alpn-proto1 1 alpn-proto2 2 uri " + uri)
+ s.Log(o)
+ s.AssertNotContains(o, "connect failed")
+ s.AssertNotContains(o, "timeout")
+ // selected based on server priority
+ s.AssertContains(o, "ALPN selected: h2")
+}
+
+func TlsAlpnMismatchTest(s *VethsSuite) {
+ s.Log(s.Containers.ServerVpp.VppInstance.Vppctl("test alpn server alpn-proto1 2 alpn-proto2 1 uri tls://0.0.0.0:123"))
+
+ uri := "tls://" + s.Interfaces.Server.Ip4AddressString() + ":123"
+ o := s.Containers.ClientVpp.VppInstance.Vppctl("test alpn client alpn-proto1 3 alpn-proto2 4 uri " + uri)
+ s.Log(o)
+ s.AssertNotContains(o, "timeout")
+ s.AssertNotContains(o, "ALPN selected")
+ // connection refused on mismatch
+ s.AssertContains(o, "connect error failed tls handshake")
+}
+
+func TlsAlpnEmptyServerListTest(s *VethsSuite) {
+ s.Log(s.Containers.ServerVpp.VppInstance.Vppctl("test alpn server uri tls://0.0.0.0:123"))
+
+ uri := "tls://" + s.Interfaces.Server.Ip4AddressString() + ":123"
+ o := s.Containers.ClientVpp.VppInstance.Vppctl("test alpn client alpn-proto1 1 alpn-proto2 2 uri " + uri)
+ s.Log(o)
+ s.AssertNotContains(o, "connect failed")
+ s.AssertNotContains(o, "timeout")
+ // no alpn negotiation
+ s.AssertContains(o, "ALPN selected: none")
+}
+
+func TlsAlpnEmptyClientListTest(s *VethsSuite) {
+ s.Log(s.Containers.ServerVpp.VppInstance.Vppctl("test alpn server alpn-proto1 2 alpn-proto2 1 uri tls://0.0.0.0:123"))
+
+ uri := "tls://" + s.Interfaces.Server.Ip4AddressString() + ":123"
+ o := s.Containers.ClientVpp.VppInstance.Vppctl("test alpn client uri " + uri)
+ s.Log(o)
+ s.AssertNotContains(o, "connect failed")
+ s.AssertNotContains(o, "timeout")
+ // no alpn negotiation
+ s.AssertContains(o, "ALPN selected: none")
+}
##############################################################################
add_vpp_plugin(hs_apps
SOURCES
+ alpn_client.c
+ alpn_server.c
echo_client.c
echo_server.c
hs_apps.c
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vnet/session/application_interface.h>
+#include <vnet/session/application.h>
+#include <vnet/session/session.h>
+#include <vnet/tls/tls_types.h>
+
+typedef struct
+{
+ u32 app_index;
+ u8 *uri;
+ u32 tls_engine;
+ u32 ckpair_index;
+ u32 cli_node_index;
+ session_endpoint_cfg_t connect_sep;
+ tls_alpn_proto_t alpn_proto_selected;
+ u8 alpn_protos[4];
+ vlib_main_t *vlib_main;
+} alpn_client_main_t;
+
+typedef enum
+{
+ AC_CLI_TEST_DONE = 1,
+ AC_CLI_CONNECT_FAILED,
+} ac_cli_signal_t;
+
+alpn_client_main_t alpn_client_main;
+
+static int
+ac_ts_rx_callback (session_t *ts)
+{
+ clib_warning ("called...");
+ return -1;
+}
+
+static int
+ac_ts_tx_callback (session_t *ts)
+{
+ clib_warning ("called...");
+ return -1;
+}
+
+static int
+ac_ts_accept_callback (session_t *ts)
+{
+ clib_warning ("called...");
+ return -1;
+}
+
+static int
+ac_ts_connected_callback (u32 app_index, u32 api_context, session_t *s,
+ session_error_t err)
+{
+ alpn_client_main_t *cm = &alpn_client_main;
+ vnet_disconnect_args_t _a = { 0 }, *a = &_a;
+
+ if (err)
+ {
+ vlib_process_signal_event_mt (cm->vlib_main, cm->cli_node_index,
+ AC_CLI_CONNECT_FAILED, err);
+ return -1;
+ }
+
+ cm->alpn_proto_selected = tls_get_alpn_selected (s->connection_index);
+
+ a->handle = session_handle (s);
+ a->app_index = cm->app_index;
+ vnet_disconnect_session (a);
+
+ vlib_process_signal_event_mt (cm->vlib_main, cm->cli_node_index,
+ AC_CLI_TEST_DONE, 0);
+
+ return 0;
+}
+
+static void
+ac_ts_disconnect_callback (session_t *s)
+{
+ alpn_client_main_t *cm = &alpn_client_main;
+ vnet_disconnect_args_t _a = { 0 }, *a = &_a;
+
+ a->handle = session_handle (s);
+ a->app_index = cm->app_index;
+ vnet_disconnect_session (a);
+}
+
+static void
+ac_ts_reset_callback (session_t *s)
+{
+ alpn_client_main_t *cm = &alpn_client_main;
+ vnet_disconnect_args_t _a = { 0 }, *a = &_a;
+
+ a->handle = session_handle (s);
+ a->app_index = cm->app_index;
+ vnet_disconnect_session (a);
+}
+
+static void
+ac_ts_cleanup_callback (session_t *s, session_cleanup_ntf_t ntf)
+{
+ return;
+}
+
+static int
+ac_add_segment_callback (u32 client_index, u64 segment_handle)
+{
+ return 0;
+}
+
+static int
+ac_del_segment_callback (u32 client_index, u64 segment_handle)
+{
+ return 0;
+}
+
+static session_cb_vft_t ac_session_cb_vft = {
+ .session_accept_callback = ac_ts_accept_callback,
+ .session_disconnect_callback = ac_ts_disconnect_callback,
+ .session_connected_callback = ac_ts_connected_callback,
+ .add_segment_callback = ac_add_segment_callback,
+ .del_segment_callback = ac_del_segment_callback,
+ .builtin_app_rx_callback = ac_ts_rx_callback,
+ .builtin_app_tx_callback = ac_ts_tx_callback,
+ .session_reset_callback = ac_ts_reset_callback,
+ .session_cleanup_callback = ac_ts_cleanup_callback,
+};
+
+static int
+ac_attach ()
+{
+ alpn_client_main_t *cm = &alpn_client_main;
+ vnet_app_attach_args_t _a, *a = &_a;
+ u64 options[18];
+ vnet_app_add_cert_key_pair_args_t _ck_pair, *ck_pair = &_ck_pair;
+
+ clib_memset (a, 0, sizeof (*a));
+ clib_memset (options, 0, sizeof (options));
+
+ a->api_client_index = ~0;
+ a->name = format (0, "test_alpn_client");
+ a->session_cb_vft = &ac_session_cb_vft;
+ a->options = options;
+ a->options[APP_OPTIONS_SEGMENT_SIZE] = 128 << 20;
+ a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = 128 << 20;
+ a->options[APP_OPTIONS_RX_FIFO_SIZE] = 8 << 10;
+ a->options[APP_OPTIONS_TX_FIFO_SIZE] = 8 << 10;
+ a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
+ a->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = 0;
+ a->options[APP_OPTIONS_TLS_ENGINE] = cm->tls_engine;
+
+ if (vnet_application_attach (a))
+ return -1;
+
+ cm->app_index = a->app_index;
+ vec_free (a->name);
+
+ clib_memset (ck_pair, 0, sizeof (*ck_pair));
+ ck_pair->cert = (u8 *) test_srv_crt_rsa;
+ ck_pair->key = (u8 *) test_srv_key_rsa;
+ ck_pair->cert_len = test_srv_crt_rsa_len;
+ ck_pair->key_len = test_srv_key_rsa_len;
+ vnet_app_add_cert_key_pair (ck_pair);
+ cm->ckpair_index = ck_pair->index;
+
+ return 0;
+}
+
+static int
+ac_connect_rpc (void *rpc_args)
+{
+ vnet_connect_args_t *a = rpc_args;
+ int rv;
+
+ rv = vnet_connect (a);
+ if (rv)
+ clib_warning (0, "connect returned: %U", format_session_error, rv);
+
+ session_endpoint_free_ext_cfgs (&a->sep_ext);
+ vec_free (a);
+
+ return rv;
+}
+
+static void
+ac_program_connect (vnet_connect_args_t *a)
+{
+ session_send_rpc_evt_to_thread_force (transport_cl_thread (), ac_connect_rpc,
+ a);
+}
+
+static void
+ac_connect ()
+{
+ alpn_client_main_t *cm = &alpn_client_main;
+ vnet_connect_args_t *a = 0;
+ transport_endpt_ext_cfg_t *ext_cfg;
+
+ vec_validate (a, 0);
+ clib_memset (a, 0, sizeof (a[0]));
+
+ clib_memcpy (&a->sep_ext, &cm->connect_sep, sizeof (cm->connect_sep));
+ a->app_index = cm->app_index;
+
+ ext_cfg =
+ session_endpoint_add_ext_cfg (&a->sep_ext, TRANSPORT_ENDPT_EXT_CFG_CRYPTO,
+ sizeof (transport_endpt_crypto_cfg_t));
+ ext_cfg->crypto.ckpair_index = cm->ckpair_index;
+ clib_memcpy (ext_cfg->crypto.alpn_protos, cm->alpn_protos, 4);
+
+ ac_program_connect (a);
+}
+
+static clib_error_t *
+ac_run (vlib_main_t *vm)
+{
+ alpn_client_main_t *cm = &alpn_client_main;
+ uword event_type, *event_data = 0;
+ clib_error_t *error = 0;
+
+ if (ac_attach ())
+ return clib_error_return (0, "attach failed");
+
+ ac_connect ();
+
+ vlib_process_wait_for_event_or_clock (vm, 10);
+ event_type = vlib_process_get_events (vm, &event_data);
+ switch (event_type)
+ {
+ case ~0:
+ error = clib_error_return (0, "timeout");
+ break;
+ case AC_CLI_TEST_DONE:
+ vlib_cli_output (vm, "ALPN selected: %U", format_tls_alpn_proto,
+ cm->alpn_proto_selected);
+ break;
+ case AC_CLI_CONNECT_FAILED:
+ error = clib_error_return (0, "connect error %U", format_session_error,
+ event_data[0]);
+ break;
+ default:
+ error = clib_error_return (0, "unexpected event %d", event_type);
+ break;
+ }
+
+ vec_free (event_data);
+ return error;
+}
+
+static int
+ac_detach ()
+{
+ alpn_client_main_t *cm = &alpn_client_main;
+ vnet_app_detach_args_t _da, *da = &_da;
+ int rv;
+
+ if (cm->app_index == APP_INVALID_INDEX)
+ return 0;
+
+ da->app_index = cm->app_index;
+ da->api_client_index = ~0;
+ rv = vnet_application_detach (da);
+ cm->app_index = APP_INVALID_INDEX;
+
+ return rv;
+}
+
+static clib_error_t *
+alpn_client_run_command_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ alpn_client_main_t *cm = &alpn_client_main;
+ unformat_input_t _line_input, *line_input = &_line_input;
+ clib_error_t *error = 0;
+
+ cm->tls_engine = CRYPTO_ENGINE_OPENSSL;
+
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return clib_error_return (0, "expected URI");
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "uri %_%v%_", &cm->uri))
+ ;
+ else if (unformat (line_input, "tls-engine %d", &cm->tls_engine))
+ ;
+ else if (unformat (line_input, "alpn-proto1 %d", &cm->alpn_protos[0]))
+ ;
+ else if (unformat (line_input, "alpn-proto2 %d", &cm->alpn_protos[1]))
+ ;
+ else if (unformat (line_input, "alpn-proto3 %d", &cm->alpn_protos[2]))
+ ;
+ else if (unformat (line_input, "alpn-proto4 %d", &cm->alpn_protos[3]))
+ ;
+ else
+ {
+ error = clib_error_return (0, "failed: unknown input `%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
+ }
+
+ cm->cli_node_index = vlib_get_current_process (vm)->node_runtime.node_index;
+
+ if (cm->uri == 0)
+ {
+ error = clib_error_return (0, "uri not defined");
+ goto done;
+ }
+
+ if (parse_uri ((char *) cm->uri, &cm->connect_sep))
+ {
+ error = clib_error_return (0, "invalid uri");
+ goto done;
+ }
+
+ session_enable_disable_args_t args = { .is_en = 1,
+ .rt_engine_type =
+ RT_BACKEND_ENGINE_RULE_TABLE };
+ vlib_worker_thread_barrier_sync (vm);
+ vnet_session_enable_disable (vm, &args);
+ vlib_worker_thread_barrier_release (vm);
+
+ error = ac_run (vm);
+
+ if (ac_detach ())
+ {
+ if (!error)
+ error = clib_error_return (0, "detach failed");
+ else
+ clib_warning ("detach failed");
+ }
+
+done:
+ vec_free (cm->uri);
+ unformat_free (line_input);
+ return error;
+}
+
+VLIB_CLI_COMMAND (alpn_client_run_command, static) = {
+ .path = "test alpn client",
+ .short_help = "test alpn client [uri <tls://ip/port>] [tls-engine %d]",
+ .function = alpn_client_run_command_fn,
+};
+
+clib_error_t *
+alpn_client_main_init (vlib_main_t *vm)
+{
+ alpn_client_main_t *cm = &alpn_client_main;
+ cm->vlib_main = vm;
+ cm->app_index = APP_INVALID_INDEX;
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (alpn_client_main_init);
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vnet/session/application_interface.h>
+#include <vnet/session/session.h>
+#include <vnet/tls/tls_types.h>
+
+typedef struct
+{
+ u32 app_index;
+ u32 listener_handle;
+ u8 *uri;
+ u32 tls_engine;
+ u32 ckpair_index;
+ u8 alpn_protos[4];
+ vlib_main_t *vlib_main;
+} alpn_server_main_t;
+
+alpn_server_main_t alpn_server_main;
+
+static int
+as_ts_rx_callback (session_t *ts)
+{
+ clib_warning ("called...");
+ return -1;
+}
+
+static int
+as_ts_tx_callback (session_t *ts)
+{
+ clib_warning ("called...");
+ return -1;
+}
+
+static int
+as_ts_accept_callback (session_t *ts)
+{
+ tls_alpn_proto_t alpn_proto;
+
+ ts->session_state = SESSION_STATE_READY;
+
+ alpn_proto = tls_get_alpn_selected (ts->connection_index);
+ clib_warning ("ALPN selected: %U", format_tls_alpn_proto, alpn_proto);
+
+ return 0;
+}
+
+static int
+as_ts_connected_callback (u32 app_index, u32 api_context, session_t *s,
+ session_error_t err)
+{
+ clib_warning ("called...");
+ return -1;
+}
+
+static void
+as_ts_disconnect_callback (session_t *s)
+{
+ alpn_server_main_t *sm = &alpn_server_main;
+ vnet_disconnect_args_t _a = { 0 }, *a = &_a;
+
+ a->handle = session_handle (s);
+ a->app_index = sm->app_index;
+ vnet_disconnect_session (a);
+}
+
+static void
+as_ts_reset_callback (session_t *s)
+{
+ alpn_server_main_t *sm = &alpn_server_main;
+ vnet_disconnect_args_t _a = { 0 }, *a = &_a;
+
+ a->handle = session_handle (s);
+ a->app_index = sm->app_index;
+ vnet_disconnect_session (a);
+}
+
+static void
+as_ts_cleanup_callback (session_t *s, session_cleanup_ntf_t ntf)
+{
+ return;
+}
+
+static int
+as_add_segment_callback (u32 client_index, u64 segment_handle)
+{
+ return 0;
+}
+
+static int
+as_del_segment_callback (u32 client_index, u64 segment_handle)
+{
+ return 0;
+}
+
+static session_cb_vft_t as_session_cb_vft = {
+ .session_accept_callback = as_ts_accept_callback,
+ .session_disconnect_callback = as_ts_disconnect_callback,
+ .session_connected_callback = as_ts_connected_callback,
+ .add_segment_callback = as_add_segment_callback,
+ .del_segment_callback = as_del_segment_callback,
+ .builtin_app_rx_callback = as_ts_rx_callback,
+ .builtin_app_tx_callback = as_ts_tx_callback,
+ .session_reset_callback = as_ts_reset_callback,
+ .session_cleanup_callback = as_ts_cleanup_callback,
+};
+
+static int
+as_attach ()
+{
+ alpn_server_main_t *sm = &alpn_server_main;
+ vnet_app_add_cert_key_pair_args_t _ck_pair, *ck_pair = &_ck_pair;
+ u64 options[APP_OPTIONS_N_OPTIONS];
+ vnet_app_attach_args_t _a, *a = &_a;
+
+ clib_memset (a, 0, sizeof (*a));
+ clib_memset (options, 0, sizeof (options));
+
+ a->api_client_index = ~0;
+ a->name = format (0, "test_alpn_server");
+ a->session_cb_vft = &as_session_cb_vft;
+ a->options = options;
+ a->options[APP_OPTIONS_SEGMENT_SIZE] = 128 << 20;
+ a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = 128 << 20;
+ a->options[APP_OPTIONS_RX_FIFO_SIZE] = 8 << 10;
+ a->options[APP_OPTIONS_TX_FIFO_SIZE] = 8 << 10;
+ a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
+ a->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = 0;
+ a->options[APP_OPTIONS_TLS_ENGINE] = sm->tls_engine;
+
+ if (vnet_application_attach (a))
+ {
+ vec_free (a->name);
+ return -1;
+ }
+
+ vec_free (a->name);
+ sm->app_index = a->app_index;
+
+ clib_memset (ck_pair, 0, sizeof (*ck_pair));
+ ck_pair->cert = (u8 *) test_srv_crt_rsa;
+ ck_pair->key = (u8 *) test_srv_key_rsa;
+ ck_pair->cert_len = test_srv_crt_rsa_len;
+ ck_pair->key_len = test_srv_key_rsa_len;
+ vnet_app_add_cert_key_pair (ck_pair);
+ sm->ckpair_index = ck_pair->index;
+
+ return 0;
+}
+
+static int
+as_listen ()
+{
+ alpn_server_main_t *sm = &alpn_server_main;
+ vnet_listen_args_t _a, *a = &_a;
+ transport_endpt_ext_cfg_t *ext_cfg;
+ char *uri;
+ int rv;
+
+ clib_memset (a, 0, sizeof (*a));
+ a->app_index = sm->app_index;
+
+ uri = (char *) sm->uri;
+ ASSERT (uri);
+
+ if (parse_uri (uri, &a->sep_ext))
+ return -1;
+
+ ext_cfg =
+ session_endpoint_add_ext_cfg (&a->sep_ext, TRANSPORT_ENDPT_EXT_CFG_CRYPTO,
+ sizeof (transport_endpt_crypto_cfg_t));
+ ext_cfg->crypto.ckpair_index = sm->ckpair_index;
+ clib_memcpy (ext_cfg->crypto.alpn_protos, sm->alpn_protos, 4);
+
+ rv = vnet_listen (a);
+ if (rv == 0)
+ {
+ sm->listener_handle = a->handle;
+ }
+
+ session_endpoint_free_ext_cfgs (&a->sep_ext);
+
+ return rv;
+}
+
+static clib_error_t *
+alpn_server_create_command_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ alpn_server_main_t *sm = &alpn_server_main;
+ unformat_input_t _line_input, *line_input = &_line_input;
+ clib_error_t *error = 0;
+
+ sm->tls_engine = CRYPTO_ENGINE_OPENSSL;
+
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return clib_error_return (0, "expected URI");
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "uri %_%v%_", &sm->uri))
+ ;
+ else if (unformat (line_input, "tls-engine %d", &sm->tls_engine))
+ ;
+ else if (unformat (line_input, "alpn-proto1 %d", &sm->alpn_protos[0]))
+ ;
+ else if (unformat (line_input, "alpn-proto2 %d", &sm->alpn_protos[1]))
+ ;
+ else if (unformat (line_input, "alpn-proto3 %d", &sm->alpn_protos[2]))
+ ;
+ else if (unformat (line_input, "alpn-proto4 %d", &sm->alpn_protos[3]))
+ ;
+ else
+ {
+ error = clib_error_return (0, "failed: unknown input `%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
+ }
+
+ session_enable_disable_args_t args = { .is_en = 1,
+ .rt_engine_type =
+ RT_BACKEND_ENGINE_RULE_TABLE };
+ vnet_session_enable_disable (vm, &args);
+
+ if (as_attach ())
+ {
+ error = clib_error_return (0, "attach failed");
+ goto done;
+ }
+ if (as_listen ())
+ {
+ error = clib_error_return (0, "lsiten failed");
+ goto done;
+ }
+
+ vlib_cli_output (vm, "server started");
+
+done:
+ vec_free (sm->uri);
+ unformat_free (line_input);
+
+ return error;
+}
+
+VLIB_CLI_COMMAND (alpn_server_create_command, static) = {
+ .path = "test alpn server",
+ .short_help = "test alpn server uri <tls://ip/port> [tls-engine %d]",
+ .function = alpn_server_create_command_fn,
+};
+
+clib_error_t *
+alpn_server_main_init (vlib_main_t *vm)
+{
+ alpn_server_main_t *sm = &alpn_server_main;
+ sm->vlib_main = vm;
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (alpn_server_main_init);
return wrote;
}
+u8 *
+format_openssl_alpn_proto (u8 *s, va_list *va)
+{
+ const unsigned char *proto = va_arg (*va, const unsigned char *);
+ unsigned int proto_len = va_arg (*va, unsigned int);
+ return format (s, "%U", format_ascii_bytes, proto, proto_len);
+}
+
void
openssl_handle_handshake_failure (tls_ctx_t *ctx)
{
/*
* Handshake complete
*/
+ if (ctx->alpn_list)
+ {
+ const unsigned char *proto = 0;
+ unsigned int proto_len;
+ SSL_get0_alpn_selected (oc->ssl, &proto, &proto_len);
+ if (proto_len)
+ {
+ TLS_DBG (1, "Selected ALPN protocol: %U", format_openssl_alpn_proto,
+ proto, proto_len);
+ tls_alpn_proto_id_t id = { .len = (u8) proto_len,
+ .base = (u8 *) proto };
+ ctx->alpn_selected = tls_alpn_proto_by_str (&id);
+ }
+ else
+ TLS_DBG (1, "No ALPN negotiated");
+ }
if (!SSL_is_server (oc->ssl))
{
/*
SSL_CTX_set_options (oc->client_ssl_ctx, flags);
SSL_CTX_set1_cert_store (oc->client_ssl_ctx, om->cert_store);
+ if (ctx->alpn_list)
+ {
+ rv = SSL_CTX_set_alpn_protos (oc->client_ssl_ctx,
+ (const unsigned char *) ctx->alpn_list,
+ (unsigned int) vec_len (ctx->alpn_list));
+ if (rv != 0)
+ {
+ TLS_DBG (1, "Couldn't set alpn protos");
+ return -1;
+ }
+ }
+
oc->ssl = SSL_new (oc->client_ssl_ctx);
if (oc->ssl == NULL)
{
return 0;
}
+static int
+openssl_alpn_select_cb (SSL *ssl, const unsigned char **out,
+ unsigned char *outlen, const unsigned char *in,
+ unsigned int inlen, void *arg)
+{
+ u8 *proto_list = arg;
+ if (SSL_select_next_proto (
+ (unsigned char **) out, outlen, (const unsigned char *) proto_list,
+ vec_len (proto_list), in, inlen) != OPENSSL_NPN_NEGOTIATED)
+ {
+ TLS_DBG (1, "server support no alpn proto advertised by client");
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ return SSL_TLSEXT_ERR_OK;
+}
+
static int
openssl_start_listen (tls_ctx_t * lctx)
{
BIO_free (cert_bio);
+ if (lctx->alpn_list)
+ SSL_CTX_set_alpn_select_cb (ssl_ctx, openssl_alpn_select_cb,
+ (void *) lctx->alpn_list);
+
olc_index = openssl_listen_ctx_alloc ();
olc = openssl_lctx_get (olc_index);
olc->ssl_ctx = ssl_ctx;
tls/tls_inlines.h
tls/tls_record.h
tls/tls_test.h
+ tls/tls_types.h
)
typedef struct transport_endpt_crypto_cfg_
{
u32 ckpair_index;
+ u8 alpn_protos[4]; /**< ordered by preference for server */
u8 crypto_engine;
u8 hostname[256]; /**< full domain len is 255 as per rfc 3986 */
} transport_endpt_crypto_cfg_t;
void tls_disconnect (u32 ctx_handle, clib_thread_index_t thread_index);
+static const tls_alpn_proto_id_t tls_alpn_proto_ids[] = {
+#define _(sym, str) { (u8) (sizeof (str) - 1), (u8 *) str },
+ foreach_tls_alpn_protos
+#undef _
+};
+
void
tls_disconnect_transport (tls_ctx_t * ctx)
{
return 0;
}
+tls_alpn_proto_t
+tls_alpn_proto_by_str (tls_alpn_proto_id_t *alpn_id)
+{
+ tls_main_t *tm = &tls_main;
+ uword *p;
+
+ p = hash_get_mem (tm->alpn_proto_by_str, alpn_id);
+ if (p)
+ return p[0];
+
+ return TLS_ALPN_PROTO_NONE;
+}
+
+tls_alpn_proto_t
+tls_get_alpn_selected (u32 ctx_handle)
+{
+ tls_ctx_t *ctx;
+ ctx = tls_ctx_get (ctx_handle);
+ return ctx->alpn_selected;
+}
+
+u8 *
+format_tls_alpn_proto (u8 *s, va_list *args)
+{
+ tls_alpn_proto_t alpn_proto = va_arg (*args, int);
+ u8 *t = 0;
+
+ switch (alpn_proto)
+ {
+#define _(sym, str) \
+ case TLS_ALPN_PROTO_##sym: \
+ t = (u8 *) str; \
+ break;
+ foreach_tls_alpn_protos
+#undef _
+ default : return format (s, "BUG: unknown");
+ }
+ return format (s, "%s", t);
+}
+
u32
tls_listener_ctx_alloc (void)
{
tls_ctx_t *ctx;
u32 ctx_index;
transport_endpt_ext_cfg_t *ext_cfg;
- int rv;
+ int rv, i;
+ u8 *p;
+ const tls_alpn_proto_id_t *alpn_proto;
sep = (session_endpoint_cfg_t *) tep;
ext_cfg = session_endpoint_get_ext_cfg (sep, TRANSPORT_ENDPT_EXT_CFG_CRYPTO);
ctx->srv_hostname = format (0, "%s", ccfg->hostname);
vec_terminate_c_string (ctx->srv_hostname);
}
+ for (i = 0; i < sizeof (ccfg->alpn_protos) && ccfg->alpn_protos[i]; i++)
+ {
+ alpn_proto = &tls_alpn_proto_ids[ccfg->alpn_protos[i]];
+ vec_add2 (ctx->alpn_list, p, alpn_proto->len + 1);
+ *p++ = alpn_proto->len;
+ clib_memcpy_fast (p, alpn_proto->base, alpn_proto->len);
+ }
ctx->tls_ctx_engine = engine_type;
tls_ctx_t *lctx;
u32 lctx_index;
transport_endpt_ext_cfg_t *ext_cfg;
- int rv;
+ int rv, i;
+ u8 *p;
+ const tls_alpn_proto_id_t *alpn_proto;
sep = (session_endpoint_cfg_t *) tep;
ext_cfg = session_endpoint_get_ext_cfg (sep, TRANSPORT_ENDPT_EXT_CFG_CRYPTO);
lctx->ckpair_index = ccfg->ckpair_index;
lctx->c_s_index = app_listener_index;
lctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
+ for (i = 0; i < sizeof (ccfg->alpn_protos) && ccfg->alpn_protos[i]; i++)
+ {
+ alpn_proto = &tls_alpn_proto_ids[ccfg->alpn_protos[i]];
+ vec_add2 (lctx->alpn_list, p, alpn_proto->len + 1);
+ *p++ = alpn_proto->len;
+ clib_memcpy_fast (p, alpn_proto->base, alpn_proto->len);
+ }
if (tls_vfts[engine_type].ctx_start_listen (lctx))
{
tls_vfts[type] = *vft;
}
+static uword
+tls_alpn_proto_hash_key_sum (hash_t *h, uword key)
+{
+ tls_alpn_proto_id_t *id = uword_to_pointer (key, tls_alpn_proto_id_t *);
+ return hash_memory (id->base, id->len, 0);
+}
+
+static uword
+tls_alpn_proto_hash_key_equal (hash_t *h, uword key1, uword key2)
+{
+ tls_alpn_proto_id_t *id1 = uword_to_pointer (key1, tls_alpn_proto_id_t *);
+ tls_alpn_proto_id_t *id2 = uword_to_pointer (key2, tls_alpn_proto_id_t *);
+ return id1 && id2 && tls_alpn_proto_id_eq (id1, id2);
+}
+
static clib_error_t *
tls_init (vlib_main_t * vm)
{
vlib_thread_main_t *vtm = vlib_get_thread_main ();
tls_main_t *tm = &tls_main;
u32 num_threads;
+ const tls_alpn_proto_id_t *alpn_proto;
num_threads = 1 /* main thread */ + vtm->n_threads;
FIB_PROTOCOL_IP4, ~0);
transport_register_protocol (TRANSPORT_PROTO_DTLS, &dtls_proto,
FIB_PROTOCOL_IP6, ~0);
- return 0;
+
+ tm->alpn_proto_by_str = hash_create2 (
+ 0, sizeof (tls_alpn_proto_id_t), sizeof (uword),
+ tls_alpn_proto_hash_key_sum, tls_alpn_proto_hash_key_equal, 0, 0);
+
+#define _(sym, str) \
+ alpn_proto = &tls_alpn_proto_ids[TLS_ALPN_PROTO_##sym]; \
+ hash_set_mem (tm->alpn_proto_by_str, alpn_proto, TLS_ALPN_PROTO_##sym);
+ foreach_tls_alpn_protos
+#undef _
+
+ return 0;
}
VLIB_INIT_FUNCTION (tls_init);
#include <vnet/session/application_interface.h>
#include <vnet/session/application.h>
#include <vnet/session/session.h>
+#include <vnet/tls/tls_types.h>
#include <vppinfra/lock.h>
#ifndef SRC_VNET_TLS_TLS_H_
u8 *srv_hostname;
u32 ckpair_index;
transport_proto_t tls_type;
+ u8 *alpn_list;
+ tls_alpn_proto_t alpn_selected;
} tls_ctx_t;
typedef struct tls_main_
u8 **rx_bufs;
u8 **tx_bufs;
+ uword *alpn_proto_by_str;
/*
* Config
*/
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ */
+
+#ifndef SRC_VNET_TLS_TLS_TYPES_H_
+#define SRC_VNET_TLS_TLS_TYPES_H_
+
+#include <vppinfra/types.h>
+
+#define foreach_tls_alpn_protos \
+ _ (NONE, "none") \
+ _ (HTTP_1_1, "http/1.1") \
+ _ (HTTP_2, "h2") \
+ _ (HTTP_3, "h3") \
+ _ (IMAP, "imap") \
+ _ (POP3, "pop3") \
+ _ (SMB2, "smb") \
+ _ (TURN, "stun.turn") \
+ _ (STUN, "stun.nat-discovery") \
+ _ (WEBRTC, "webrtc") \
+ _ (CWEBRTC, "c-webrtc") \
+ _ (FTP, "ftp") \
+ _ (MANAGE_SIEVE, "managesieve") \
+ _ (COAP_TLS, "coap") \
+ _ (COAP_DSTL, "co") \
+ _ (XMPP_CLIENT, "xmpp-client") \
+ _ (XMPP_SERVER, "xmpp-server") \
+ _ (ACME_TLS_1, "acme-tls/1") \
+ _ (MQTT, "mqtt") \
+ _ (DNS_OVER_TLS, "dot") \
+ _ (NTSKE_1, "ntske/1") \
+ _ (SUN_RPC, "sunrpc") \
+ _ (IRC, "irc") \
+ _ (NNTP, "nntp") \
+ _ (NNSP, "nnsp") \
+ _ (DOQ, "doq") \
+ _ (SIP_2, "sip/2") \
+ _ (TDS_8_0, "tds/8.0") \
+ _ (DICOM, "dicom") \
+ _ (POSTGRESQL, "postgresql") \
+ _ (RADIUS_1_0, "radius/1.0") \
+ _ (RADIUS_1_1, "radius/1.1")
+
+typedef enum tls_alpn_proto_
+{
+#define _(sym, str) TLS_ALPN_PROTO_##sym,
+ foreach_tls_alpn_protos
+#undef _
+} __clib_packed tls_alpn_proto_t;
+
+typedef struct tls_alpn_proto_id_
+{
+ u8 len;
+ u8 *base;
+} tls_alpn_proto_id_t;
+
+static inline u8
+tls_alpn_proto_id_eq (tls_alpn_proto_id_t *actual,
+ tls_alpn_proto_id_t *expected)
+{
+ if (actual->len != expected->len)
+ return 0;
+ return memcmp (actual->base, expected->base, expected->len) == 0 ? 1 : 0;
+}
+
+tls_alpn_proto_t tls_alpn_proto_by_str (tls_alpn_proto_id_t *alpn_id);
+
+tls_alpn_proto_t tls_get_alpn_selected (u32 ctx_handle);
+
+format_function_t format_tls_alpn_proto;
+
+#endif /* SRC_VNET_TLS_TLS_TYPES_H_ */