aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/bpf_trace_filter/bpf_trace_filter.rst65
-rw-r--r--src/plugins/cdp/cdp_periodic.c2
-rw-r--r--src/plugins/cnat/cnat_node.h187
-rw-r--r--src/plugins/crypto_sw_scheduler/crypto_sw_scheduler.h1
-rw-r--r--src/plugins/crypto_sw_scheduler/main.c109
-rw-r--r--src/plugins/hs_apps/http_cli.c36
-rw-r--r--src/plugins/hs_apps/http_client.c379
-rw-r--r--src/plugins/hs_apps/proxy.c7
-rw-r--r--src/plugins/hs_apps/proxy.h2
-rw-r--r--src/plugins/http/http.c204
-rw-r--r--src/plugins/http/http.h282
-rw-r--r--src/plugins/http/http_plugin.rst79
-rw-r--r--src/plugins/http/test/http_test.c23
-rw-r--r--src/plugins/map/map_api.c19
-rw-r--r--src/plugins/memif/device.c64
-rw-r--r--src/plugins/memif/node.c3
-rw-r--r--src/plugins/memif/private.h5
-rw-r--r--src/plugins/nat/nat44-ed/nat44_ed_cli.c22
-rw-r--r--src/plugins/osi/node.c19
-rw-r--r--src/plugins/ppp/CMakeLists.txt25
-rw-r--r--src/plugins/ppp/error.def42
-rw-r--r--src/plugins/ppp/node.c381
-rw-r--r--src/plugins/ppp/packet.h199
-rw-r--r--src/plugins/ppp/pg.c114
-rw-r--r--src/plugins/ppp/plugin.c26
-rw-r--r--src/plugins/ppp/ppp.c270
-rw-r--r--src/plugins/ppp/ppp.h120
-rw-r--r--src/plugins/pppoe/pppoe.c2
-rw-r--r--src/plugins/pppoe/pppoe_cp_node.c2
-rw-r--r--src/plugins/pppoe/pppoe_decap.c2
-rw-r--r--src/plugins/srtp/srtp_plugin.rst6
-rw-r--r--src/plugins/tlsopenssl/tls_async.c598
-rw-r--r--src/plugins/tlsopenssl/tls_openssl.c211
-rw-r--r--src/plugins/tlsopenssl/tls_openssl.h23
34 files changed, 3046 insertions, 483 deletions
diff --git a/src/plugins/bpf_trace_filter/bpf_trace_filter.rst b/src/plugins/bpf_trace_filter/bpf_trace_filter.rst
index 63deddbc5ab..0cd9902fda7 100644
--- a/src/plugins/bpf_trace_filter/bpf_trace_filter.rst
+++ b/src/plugins/bpf_trace_filter/bpf_trace_filter.rst
@@ -1,4 +1,67 @@
BPF Trace Filter Function
============================
This plugin provides a trace filter function that relies on a BPF interpreter to select which packets
-must be traced. \ No newline at end of file
+must be traced. This filter function can be applied to vpp traces and pcap captures.
+
+Note that if a classifier-based filter has been specified, then it will be used
+in conjunction with the BPF filter.
+
+Setting BPF filter:
+---------------------
+
+Add filter for ICMP packets
+::
+
+ vpp# set bpf trace filter {{ip proto icmp}}
+
+Show BPF bytecode:
+::
+
+ vpp# show bpf trace filter
+ (000) ldh [12]
+ (001) jeq #0x800 jt 2 jf 5
+ (002) ldb [23]
+ (003) jeq #0x1 jt 4 jf 5
+ (004) ret #65535
+ (005) ret #0
+
+Applying BPF filter on trace:
+-----------------------------
+
+Enable BPF filter function for trace:
+::
+
+ vpp# set trace filter function bpf_trace_filter
+ vpp# show trace filter function
+ (*) name:bpf_trace_filter description: bpf based trace filter priority: 10
+ name:vnet_is_packet_traced description: classifier based filter priority: 50
+
+Add trace with filter:
+::
+
+ vpp# trace add <input-graph-node> 100 filter
+ vpp# show trace
+
+Enabling BPF filter on pcap capture:
+-------------------------------------
+
+Enable BPF filter function for pcap capture:
+::
+
+ vpp# set pcap filter function bpf_trace_filter
+ vpp# show pcap filter function
+ (*) name:bpf_trace_filter description: bpf based trace filter priority: 10
+ name:vnet_is_packet_traced description: classifier based filter priority: 50
+
+Enable pcap capture with filter:
+::
+
+ vpp# pcap trace rx tx max 1000 intfc <interface> filter
+ vpp# pcap trace off
+
+Additional information:
+-------------------------------------
+
+BPF syntax reference : https://www.tcpdump.org/manpages/pcap-filter.7.html
+
+FAQ on limitations when filtering on VLAN/Geneve/MPLS packets: https://www.tcpdump.org/faq.html#q13
diff --git a/src/plugins/cdp/cdp_periodic.c b/src/plugins/cdp/cdp_periodic.c
index 03a2de0d9ab..3e700ae46f8 100644
--- a/src/plugins/cdp/cdp_periodic.c
+++ b/src/plugins/cdp/cdp_periodic.c
@@ -16,7 +16,7 @@
#include <vppinfra/hash.h>
#include <vppinfra/pcap.h>
#include <vnet/srp/srp.h>
-#include <vnet/ppp/ppp.h>
+#include <plugins/ppp/ppp.h>
#include <vnet/hdlc/hdlc.h>
#include <vnet/srp/packet.h>
diff --git a/src/plugins/cnat/cnat_node.h b/src/plugins/cnat/cnat_node.h
index d81f6745bc4..549eeae4416 100644
--- a/src/plugins/cnat/cnat_node.h
+++ b/src/plugins/cnat/cnat_node.h
@@ -334,6 +334,10 @@ cnat_translation_icmp4_error (ip4_header_t *outer_ip4, icmp46_header_t *icmp,
cnat_ip4_translate_l4 (ip4, udp, &inner_l4_sum, new_addr, new_port,
0 /* flags */);
tcp->checksum = ip_csum_fold (inner_l4_sum);
+
+ /* TCP checksum changed */
+ sum = ip_csum_update (sum, inner_l4_old_sum, inner_l4_sum, ip4_header_t,
+ checksum);
}
else if (ip4->protocol == IP_PROTOCOL_UDP)
{
@@ -341,14 +345,40 @@ cnat_translation_icmp4_error (ip4_header_t *outer_ip4, icmp46_header_t *icmp,
cnat_ip4_translate_l4 (ip4, udp, &inner_l4_sum, new_addr, new_port,
0 /* flags */);
udp->checksum = ip_csum_fold (inner_l4_sum);
+
+ /* UDP checksum changed */
+ sum = ip_csum_update (sum, inner_l4_old_sum, inner_l4_sum, ip4_header_t,
+ checksum);
+ }
+ else if (ip4->protocol == IP_PROTOCOL_ICMP)
+ {
+ icmp46_header_t *icmp = (icmp46_header_t *) udp;
+ if (icmp_type_is_echo (icmp->type))
+ {
+ u16 old_port;
+ cnat_echo_header_t *echo = (cnat_echo_header_t *) (icmp + 1);
+ inner_l4_old_sum = inner_l4_sum = icmp->checksum;
+
+ old_port = echo->identifier;
+ echo->identifier = new_port[VLIB_RX];
+
+ inner_l4_sum = ip_csum_update (
+ inner_l4_sum, old_port, new_port[VLIB_RX], udp_header_t, src_port);
+
+ icmp->checksum = ip_csum_fold (inner_l4_sum);
+
+ sum = ip_csum_update (sum, old_port, new_port[VLIB_RX], udp_header_t,
+ src_port);
+ /* checksum changed */
+ sum = ip_csum_update (sum, inner_l4_old_sum, inner_l4_sum,
+ ip4_header_t, checksum);
+ }
+ old_port[VLIB_TX] = 0;
+ old_port[VLIB_RX] = 0;
}
else
return;
- /* UDP/TCP checksum changed */
- sum = ip_csum_update (sum, inner_l4_old_sum, inner_l4_sum,
- ip4_header_t, checksum);
-
/* UDP/TCP Ports changed */
if (old_port[VLIB_TX] && new_port[VLIB_TX])
sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX],
@@ -569,6 +599,17 @@ cnat_translation_icmp6_error (ip6_header_t * outer_ip6,
cnat_ip6_translate_l4 (ip6, udp, &inner_l4_sum, new_addr, new_port,
0 /* oflags */);
tcp->checksum = ip_csum_fold (inner_l4_sum);
+
+ /* TCP checksum changed */
+ sum = ip_csum_update (sum, inner_l4_old_sum, inner_l4_sum, ip4_header_t,
+ checksum);
+
+ /* TCP Ports changed */
+ sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX],
+ tcp_header_t, dst_port);
+
+ sum = ip_csum_update (sum, old_port[VLIB_RX], new_port[VLIB_RX],
+ tcp_header_t, src_port);
}
else if (ip6->protocol == IP_PROTOCOL_UDP)
{
@@ -576,20 +617,60 @@ cnat_translation_icmp6_error (ip6_header_t * outer_ip6,
cnat_ip6_translate_l4 (ip6, udp, &inner_l4_sum, new_addr, new_port,
0 /* oflags */);
udp->checksum = ip_csum_fold (inner_l4_sum);
- }
- else
- return;
- /* UDP/TCP checksum changed */
- sum = ip_csum_update (sum, inner_l4_old_sum, inner_l4_sum, ip4_header_t,
- checksum);
+ /* UDP checksum changed */
+ sum = ip_csum_update (sum, inner_l4_old_sum, inner_l4_sum, ip4_header_t,
+ checksum);
- /* UDP/TCP Ports changed */
- sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX],
- udp_header_t, dst_port);
+ /* UDP Ports changed */
+ sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX],
+ udp_header_t, dst_port);
- sum = ip_csum_update (sum, old_port[VLIB_RX], new_port[VLIB_RX],
- udp_header_t, src_port);
+ sum = ip_csum_update (sum, old_port[VLIB_RX], new_port[VLIB_RX],
+ udp_header_t, src_port);
+ }
+ else if (ip6->protocol == IP_PROTOCOL_ICMP6)
+ {
+ /* Update ICMP6 checksum */
+ icmp46_header_t *inner_icmp = (icmp46_header_t *) udp;
+ inner_l4_old_sum = inner_l4_sum = inner_icmp->checksum;
+ if (icmp6_type_is_echo (inner_icmp->type))
+ {
+ cnat_echo_header_t *echo = (cnat_echo_header_t *) (inner_icmp + 1);
+ u16 old_port = echo->identifier;
+ echo->identifier = new_port[VLIB_RX];
+ inner_l4_sum = ip_csum_update (
+ inner_l4_sum, old_port, new_port[VLIB_RX], udp_header_t, src_port);
+
+ sum = ip_csum_update (sum, old_port, new_port[VLIB_RX], udp_header_t,
+ src_port);
+ }
+
+ inner_l4_sum =
+ ip_csum_add_even (inner_l4_sum, new_addr[VLIB_TX].as_u64[0]);
+ inner_l4_sum =
+ ip_csum_add_even (inner_l4_sum, new_addr[VLIB_TX].as_u64[1]);
+ inner_l4_sum =
+ ip_csum_sub_even (inner_l4_sum, ip6->dst_address.as_u64[0]);
+ inner_l4_sum =
+ ip_csum_sub_even (inner_l4_sum, ip6->dst_address.as_u64[1]);
+
+ inner_l4_sum =
+ ip_csum_add_even (inner_l4_sum, new_addr[VLIB_RX].as_u64[0]);
+ inner_l4_sum =
+ ip_csum_add_even (inner_l4_sum, new_addr[VLIB_RX].as_u64[1]);
+ inner_l4_sum =
+ ip_csum_sub_even (inner_l4_sum, ip6->src_address.as_u64[0]);
+ inner_l4_sum =
+ ip_csum_sub_even (inner_l4_sum, ip6->src_address.as_u64[1]);
+ inner_icmp->checksum = ip_csum_fold (inner_l4_sum);
+
+ /* Update ICMP6 checksum change */
+ sum = ip_csum_update (sum, inner_l4_old_sum, inner_l4_sum, ip4_header_t,
+ checksum);
+ }
+ else
+ return;
cnat_ip6_translate_l3 (ip6, new_addr);
/* IP src/dst addr changed */
@@ -677,15 +758,35 @@ cnat_session_make_key (vlib_buffer_t *b, ip_address_family_t af,
if (icmp_type_is_error_message (icmp->type))
{
ip4 = (ip4_header_t *) (icmp + 2); /* Use inner packet */
- udp = (udp_header_t *) (ip4 + 1);
- /* Swap dst & src for search as ICMP payload is reversed */
- ip46_address_set_ip4 (&session->key.cs_ip[VLIB_RX],
- &ip4->dst_address);
- ip46_address_set_ip4 (&session->key.cs_ip[VLIB_TX],
- &ip4->src_address);
- session->key.cs_proto = ip4->protocol;
- session->key.cs_port[VLIB_TX] = udp->src_port;
- session->key.cs_port[VLIB_RX] = udp->dst_port;
+ if (PREDICT_FALSE (ip4->protocol == IP_PROTOCOL_ICMP))
+ {
+ icmp = (icmp46_header_t *) (ip4 + 1);
+ if (icmp_type_is_echo (icmp->type))
+ {
+ cnat_echo_header_t *echo =
+ (cnat_echo_header_t *) (icmp + 1);
+ ip46_address_set_ip4 (&session->key.cs_ip[VLIB_RX],
+ &ip4->dst_address);
+ ip46_address_set_ip4 (&session->key.cs_ip[VLIB_TX],
+ &ip4->src_address);
+ session->key.cs_proto = ip4->protocol;
+ session->key.cs_port[VLIB_TX] = echo->identifier;
+ session->key.cs_port[VLIB_RX] = echo->identifier;
+ }
+ }
+ else
+ {
+ udp = (udp_header_t *) (ip4 + 1);
+ /* Swap dst & src for search as ICMP payload is reversed
+ */
+ ip46_address_set_ip4 (&session->key.cs_ip[VLIB_RX],
+ &ip4->dst_address);
+ ip46_address_set_ip4 (&session->key.cs_ip[VLIB_TX],
+ &ip4->src_address);
+ session->key.cs_proto = ip4->protocol;
+ session->key.cs_port[VLIB_TX] = udp->src_port;
+ session->key.cs_port[VLIB_RX] = udp->dst_port;
+ }
}
else if (icmp_type_is_echo (icmp->type))
{
@@ -738,15 +839,35 @@ cnat_session_make_key (vlib_buffer_t *b, ip_address_family_t af,
if (icmp6_type_is_error_message (icmp->type))
{
ip6 = (ip6_header_t *) (icmp + 2); /* Use inner packet */
- udp = (udp_header_t *) (ip6 + 1);
- /* Swap dst & src for search as ICMP payload is reversed */
- ip46_address_set_ip6 (&session->key.cs_ip[VLIB_RX],
- &ip6->dst_address);
- ip46_address_set_ip6 (&session->key.cs_ip[VLIB_TX],
- &ip6->src_address);
- session->key.cs_proto = ip6->protocol;
- session->key.cs_port[VLIB_TX] = udp->src_port;
- session->key.cs_port[VLIB_RX] = udp->dst_port;
+ if (PREDICT_FALSE (ip6->protocol == IP_PROTOCOL_ICMP6))
+ {
+ icmp = (icmp46_header_t *) (ip6 + 1);
+ if (icmp6_type_is_echo (icmp->type))
+ {
+ cnat_echo_header_t *echo =
+ (cnat_echo_header_t *) (icmp + 1);
+ ip46_address_set_ip6 (&session->key.cs_ip[VLIB_RX],
+ &ip6->dst_address);
+ ip46_address_set_ip6 (&session->key.cs_ip[VLIB_TX],
+ &ip6->src_address);
+ session->key.cs_proto = ip6->protocol;
+ session->key.cs_port[VLIB_TX] = echo->identifier;
+ session->key.cs_port[VLIB_RX] = echo->identifier;
+ }
+ }
+ else
+ {
+ udp = (udp_header_t *) (ip6 + 1);
+ /* Swap dst & src for search as ICMP payload is reversed
+ */
+ ip46_address_set_ip6 (&session->key.cs_ip[VLIB_RX],
+ &ip6->dst_address);
+ ip46_address_set_ip6 (&session->key.cs_ip[VLIB_TX],
+ &ip6->src_address);
+ session->key.cs_proto = ip6->protocol;
+ session->key.cs_port[VLIB_TX] = udp->src_port;
+ session->key.cs_port[VLIB_RX] = udp->dst_port;
+ }
}
else if (icmp6_type_is_echo (icmp->type))
{
diff --git a/src/plugins/crypto_sw_scheduler/crypto_sw_scheduler.h b/src/plugins/crypto_sw_scheduler/crypto_sw_scheduler.h
index e74dfdd2c2a..1f68aa30ac0 100644
--- a/src/plugins/crypto_sw_scheduler/crypto_sw_scheduler.h
+++ b/src/plugins/crypto_sw_scheduler/crypto_sw_scheduler.h
@@ -60,6 +60,7 @@ typedef struct
u32 crypto_engine_index;
crypto_sw_scheduler_per_thread_data_t *per_thread_data;
vnet_crypto_key_t *keys;
+ u32 crypto_sw_scheduler_queue_mask;
} crypto_sw_scheduler_main_t;
extern crypto_sw_scheduler_main_t crypto_sw_scheduler_main;
diff --git a/src/plugins/crypto_sw_scheduler/main.c b/src/plugins/crypto_sw_scheduler/main.c
index 73a158e86b2..8d0430ab2f6 100644
--- a/src/plugins/crypto_sw_scheduler/main.c
+++ b/src/plugins/crypto_sw_scheduler/main.c
@@ -85,7 +85,7 @@ crypto_sw_scheduler_frame_enqueue (vlib_main_t *vm,
&ptd->queue[CRYPTO_SW_SCHED_QUEUE_TYPE_DECRYPT];
u64 head = current_queue->head;
- if (current_queue->jobs[head & CRYPTO_SW_SCHEDULER_QUEUE_MASK])
+ if (current_queue->jobs[head & cm->crypto_sw_scheduler_queue_mask])
{
u32 n_elts = frame->n_elts, i;
for (i = 0; i < n_elts; i++)
@@ -93,7 +93,7 @@ crypto_sw_scheduler_frame_enqueue (vlib_main_t *vm,
return -1;
}
- current_queue->jobs[head & CRYPTO_SW_SCHEDULER_QUEUE_MASK] = frame;
+ current_queue->jobs[head & cm->crypto_sw_scheduler_queue_mask] = frame;
head += 1;
CLIB_MEMORY_STORE_BARRIER ();
current_queue->head = head;
@@ -487,13 +487,13 @@ run_next_queues:
* Prior to that, the largest possible value of head is
* (queue size - 2).
*/
- if ((tail > head) && (head >= CRYPTO_SW_SCHEDULER_QUEUE_MASK))
+ if ((tail > head) && (head >= cm->crypto_sw_scheduler_queue_mask))
goto skip_queue;
for (j = tail; j != head; j++)
{
- f = current_queue->jobs[j & CRYPTO_SW_SCHEDULER_QUEUE_MASK];
+ f = current_queue->jobs[j & cm->crypto_sw_scheduler_queue_mask];
if (!f)
continue;
@@ -553,7 +553,7 @@ run_next_queues:
ptd->last_return_queue = 1;
}
- tail = current_queue->tail & CRYPTO_SW_SCHEDULER_QUEUE_MASK;
+ tail = current_queue->tail & cm->crypto_sw_scheduler_queue_mask;
if (current_queue->jobs[tail] &&
current_queue->jobs[tail]->state >= VNET_CRYPTO_FRAME_STATE_SUCCESS)
@@ -686,36 +686,7 @@ clib_error_t *
crypto_sw_scheduler_init (vlib_main_t * vm)
{
crypto_sw_scheduler_main_t *cm = &crypto_sw_scheduler_main;
- vlib_thread_main_t *tm = vlib_get_thread_main ();
clib_error_t *error = 0;
- crypto_sw_scheduler_per_thread_data_t *ptd;
- u32 i;
-
- vec_validate_aligned (cm->per_thread_data, tm->n_vlib_mains - 1,
- CLIB_CACHE_LINE_BYTES);
-
- for (i = 0; i < tm->n_vlib_mains; i++)
- {
- ptd = cm->per_thread_data + i;
- ptd->self_crypto_enabled = i > 0 || vlib_num_workers () < 1;
-
- ptd->queue[CRYPTO_SW_SCHED_QUEUE_TYPE_DECRYPT].head = 0;
- ptd->queue[CRYPTO_SW_SCHED_QUEUE_TYPE_DECRYPT].tail = 0;
-
- vec_validate_aligned (
- ptd->queue[CRYPTO_SW_SCHED_QUEUE_TYPE_DECRYPT].jobs,
- CRYPTO_SW_SCHEDULER_QUEUE_SIZE - 1, CLIB_CACHE_LINE_BYTES);
-
- ptd->queue[CRYPTO_SW_SCHED_QUEUE_TYPE_ENCRYPT].head = 0;
- ptd->queue[CRYPTO_SW_SCHED_QUEUE_TYPE_ENCRYPT].tail = 0;
-
- ptd->last_serve_encrypt = 0;
- ptd->last_return_queue = 0;
-
- vec_validate_aligned (
- ptd->queue[CRYPTO_SW_SCHED_QUEUE_TYPE_ENCRYPT].jobs,
- CRYPTO_SW_SCHEDULER_QUEUE_SIZE - 1, CLIB_CACHE_LINE_BYTES);
- }
cm->crypto_engine_index =
vnet_crypto_register_engine (vm, "sw_scheduler", 100,
@@ -749,9 +720,6 @@ crypto_sw_scheduler_init (vlib_main_t * vm)
vnet_crypto_register_dequeue_handler (vm, cm->crypto_engine_index,
crypto_sw_scheduler_dequeue);
- if (error)
- vec_free (cm->per_thread_data);
-
return error;
}
@@ -764,6 +732,73 @@ VLIB_PLUGIN_REGISTER () = {
.description = "SW Scheduler Crypto Async Engine plugin",
};
+static clib_error_t *
+crypto_sw_scheduler_config (vlib_main_t *vm, unformat_input_t *input)
+{
+ crypto_sw_scheduler_main_t *cm = &crypto_sw_scheduler_main;
+ u32 crypto_sw_scheduler_queue_size = CRYPTO_SW_SCHEDULER_QUEUE_SIZE;
+ clib_error_t *error = 0;
+ vlib_thread_main_t *tm = vlib_get_thread_main ();
+ crypto_sw_scheduler_per_thread_data_t *ptd;
+ u32 i;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "crypto-sw-scheduler-queue-size %d",
+ &crypto_sw_scheduler_queue_size))
+ {
+ if (!is_pow2 (crypto_sw_scheduler_queue_size))
+ {
+ return clib_error_return (0, "input %d is not pow2",
+ format_unformat_error,
+ crypto_sw_scheduler_queue_size);
+ }
+ }
+ else
+ {
+ cm->crypto_sw_scheduler_queue_mask =
+ crypto_sw_scheduler_queue_size - 1;
+ return clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, input);
+ }
+ }
+
+ cm->crypto_sw_scheduler_queue_mask = crypto_sw_scheduler_queue_size - 1;
+
+ vec_validate_aligned (cm->per_thread_data, tm->n_vlib_mains - 1,
+ CLIB_CACHE_LINE_BYTES);
+
+ for (i = 0; i < tm->n_vlib_mains; i++)
+ {
+ ptd = cm->per_thread_data + i;
+ ptd->self_crypto_enabled = i > 0 || vlib_num_workers () < 1;
+
+ ptd->queue[CRYPTO_SW_SCHED_QUEUE_TYPE_DECRYPT].head = 0;
+ ptd->queue[CRYPTO_SW_SCHED_QUEUE_TYPE_DECRYPT].tail = 0;
+
+ vec_validate_aligned (
+ ptd->queue[CRYPTO_SW_SCHED_QUEUE_TYPE_DECRYPT].jobs,
+ CRYPTO_SW_SCHEDULER_QUEUE_SIZE - 1, CLIB_CACHE_LINE_BYTES);
+
+ ptd->queue[CRYPTO_SW_SCHED_QUEUE_TYPE_ENCRYPT].head = 0;
+ ptd->queue[CRYPTO_SW_SCHED_QUEUE_TYPE_ENCRYPT].tail = 0;
+
+ ptd->last_serve_encrypt = 0;
+ ptd->last_return_queue = 0;
+
+ vec_validate_aligned (
+ ptd->queue[CRYPTO_SW_SCHED_QUEUE_TYPE_ENCRYPT].jobs,
+ CRYPTO_SW_SCHEDULER_QUEUE_SIZE - 1, CLIB_CACHE_LINE_BYTES);
+ }
+
+ if (error)
+ vec_free (cm->per_thread_data);
+
+ return error;
+}
+
+VLIB_CONFIG_FUNCTION (crypto_sw_scheduler_config, "crypto_sw_scheduler");
+
/*
* fd.io coding-style-patch-verification: ON
*
diff --git a/src/plugins/hs_apps/http_cli.c b/src/plugins/hs_apps/http_cli.c
index dfa90f9eced..3ca86d24673 100644
--- a/src/plugins/hs_apps/http_cli.c
+++ b/src/plugins/hs_apps/http_cli.c
@@ -51,6 +51,7 @@ typedef struct
u8 *tx_buf;
u32 tx_offset;
u32 vpp_session_index;
+ http_header_table_t req_headers;
http_header_t *resp_headers;
} hcs_session_t;
@@ -177,7 +178,7 @@ start_send_data (hcs_session_t *hs, http_status_code_t status)
if (vec_len (hs->resp_headers))
{
headers_buf = http_serialize_headers (hs->resp_headers);
- vec_free (hs->resp_headers);
+ vec_reset_length (hs->resp_headers);
msg.data.headers_offset = 0;
msg.data.headers_len = vec_len (headers_buf);
}
@@ -370,7 +371,8 @@ hcs_ts_rx_callback (session_t *ts)
hs = hcs_session_get (ts->thread_index, ts->opaque);
hs->tx_buf = 0;
- hs->resp_headers = 0;
+ vec_reset_length (hs->resp_headers);
+ http_reset_header_table (&hs->req_headers);
/* Read the http message header */
rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg);
@@ -413,30 +415,22 @@ hcs_ts_rx_callback (session_t *ts)
if (msg.data.headers_len)
{
- u8 *headers = 0;
- http_header_table_t *ht;
- vec_validate (headers, msg.data.headers_len - 1);
+ http_init_header_table_buf (&hs->req_headers, msg);
rv = svm_fifo_peek (ts->rx_fifo, msg.data.headers_offset,
- msg.data.headers_len, headers);
+ msg.data.headers_len, hs->req_headers.buf);
ASSERT (rv == msg.data.headers_len);
- if (http_parse_headers (headers, &ht))
+ http_build_header_table (&hs->req_headers, msg);
+ const http_header_t *accept = http_get_header (
+ &hs->req_headers, http_header_name_token (HTTP_HEADER_ACCEPT));
+ if (accept)
{
- start_send_data (hs, HTTP_STATUS_BAD_REQUEST);
- vec_free (args.buf);
- vec_free (headers);
- goto done;
- }
- const char *accept_value =
- http_get_header (ht, http_header_name_str (HTTP_HEADER_ACCEPT));
- if (accept_value)
- {
- HCS_DBG ("client accept: %s", accept_value);
+ HCS_DBG ("client accept: %U", format_http_bytes, accept->value.base,
+ accept->value.len);
/* just for testing purpose, we don't care about precedence */
- if (strstr (accept_value, "text/plain"))
+ if (http_token_contains (accept->value.base, accept->value.len,
+ http_token_lit ("text/plain")))
args.plain_text = 1;
}
- http_free_header_table (ht);
- vec_free (headers);
}
args.hs_index = hs->session_index;
@@ -547,6 +541,8 @@ hcs_ts_cleanup_callback (session_t *s, session_cleanup_ntf_t ntf)
return;
vec_free (hs->tx_buf);
+ vec_free (hs->resp_headers);
+ http_free_header_table (&hs->req_headers);
hcs_session_free (hs);
}
diff --git a/src/plugins/hs_apps/http_client.c b/src/plugins/hs_apps/http_client.c
index 05a87ec7de8..cfa5e9c1001 100644
--- a/src/plugins/hs_apps/http_client.c
+++ b/src/plugins/hs_apps/http_client.c
@@ -23,33 +23,51 @@ typedef struct
typedef struct
{
+ u64 request_count;
+ f64 start, end;
+ f64 elapsed_time;
+} hc_stats_t;
+
+typedef struct
+{
hc_session_t *sessions;
u32 thread_index;
vlib_main_t *vlib_main;
+ u8 *headers_buf;
+ http_header_t *req_headers;
+ http_msg_t msg;
} hc_worker_t;
typedef struct
{
+ u8 *name;
+ u8 *value;
+} hc_http_header_t;
+
+typedef struct
+{
u32 app_index;
u32 cli_node_index;
u8 attached;
u8 *uri;
session_endpoint_cfg_t connect_sep;
u8 *target;
- u8 *headers_buf;
u8 *data;
u64 data_offset;
hc_worker_t *wrk;
u8 *resp_headers;
u8 *http_response;
u8 *response_status;
- http_header_ht_t *custom_header;
+ hc_http_header_t *custom_header;
u8 is_file;
u8 use_ptr;
u8 *filename;
bool verbose;
f64 timeout;
http_req_method_t req_method;
+ u64 repeat_count;
+ f64 duration;
+ bool repeat;
} hc_main_t;
typedef enum
@@ -57,9 +75,12 @@ typedef enum
HC_CONNECT_FAILED = 1,
HC_TRANSPORT_CLOSED,
HC_REPLY_RECEIVED,
+ HC_GENERIC_ERR,
+ HC_REPEAT_DONE,
} hc_cli_signal_t;
static hc_main_t hc_main;
+static hc_stats_t hc_stats;
static inline hc_worker_t *
hc_worker_get (u32 thread_index)
@@ -95,39 +116,94 @@ hc_session_alloc (hc_worker_t *wrk)
}
static int
-hc_session_connected_callback (u32 app_index, u32 hc_session_index,
- session_t *s, session_error_t err)
+hc_request (session_t *s, session_error_t err)
{
hc_main_t *hcm = &hc_main;
- hc_session_t *hc_session, *new_hc_session;
- hc_worker_t *wrk;
- http_msg_t msg;
u64 to_send;
u32 n_enq;
u8 n_segs;
int rv;
- http_header_ht_t *header;
- http_header_t *req_headers = 0;
- u32 new_hc_index;
+ hc_worker_t *wrk = hc_worker_get (s->thread_index);
+
+ if (hcm->use_ptr)
+ {
+ uword target = pointer_to_uword (hcm->target);
+ uword headers = pointer_to_uword (wrk->headers_buf);
+ uword body = pointer_to_uword (hcm->data);
+ svm_fifo_seg_t segs[4] = {
+ { (u8 *) &wrk->msg, sizeof (wrk->msg) },
+ { (u8 *) &target, sizeof (target) },
+ { (u8 *) &headers, sizeof (headers) },
+ { (u8 *) &body, sizeof (body) },
+ };
+
+ n_segs = (hcm->req_method == HTTP_REQ_GET) ? 3 : 4;
+ rv = svm_fifo_enqueue_segments (s->tx_fifo, segs, n_segs,
+ 0 /* allow partial */);
+ if (hcm->req_method == HTTP_REQ_POST)
+ ASSERT (rv == (sizeof (wrk->msg) + sizeof (target) + sizeof (headers) +
+ sizeof (body)));
+ else
+ ASSERT (rv ==
+ (sizeof (wrk->msg) + sizeof (target) + sizeof (headers)));
+ goto done;
+ }
+
+ rv = svm_fifo_enqueue (s->tx_fifo, sizeof (wrk->msg), (u8 *) &wrk->msg);
+ ASSERT (rv == sizeof (wrk->msg));
+
+ rv = svm_fifo_enqueue (s->tx_fifo, vec_len (hcm->target), hcm->target);
+ ASSERT (rv == vec_len (hcm->target));
+
+ rv = svm_fifo_enqueue (s->tx_fifo, vec_len (wrk->headers_buf),
+ wrk->headers_buf);
+ ASSERT (rv == wrk->msg.data.headers_len);
+
+ if (hcm->req_method == HTTP_REQ_POST)
+ {
+ to_send = vec_len (hcm->data);
+ n_enq = clib_min (svm_fifo_size (s->tx_fifo), to_send);
+
+ rv = svm_fifo_enqueue (s->tx_fifo, n_enq, hcm->data);
+ if (rv < to_send)
+ {
+ hcm->data_offset = (rv > 0) ? rv : 0;
+ svm_fifo_add_want_deq_ntf (s->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
+ }
+ }
+done:
+ if (svm_fifo_set_event (s->tx_fifo))
+ {
+ session_program_tx_io_evt (s->handle, SESSION_IO_EVT_TX);
+ }
+ return 0;
+}
+
+static int
+hc_session_connected_callback (u32 app_index, u32 hc_session_index,
+ session_t *s, session_error_t err)
+{
+ hc_main_t *hcm = &hc_main;
+ hc_worker_t *wrk;
+ u32 new_hc_index;
+ hc_http_header_t *header;
HTTP_DBG (1, "ho hc_index: %d", hc_session_index);
if (err)
{
clib_warning ("hc_session_index[%d] connected error: %U",
hc_session_index, format_session_error, err);
- vlib_process_signal_event_mt (hcm->wrk->vlib_main, hcm->cli_node_index,
- HC_CONNECT_FAILED, 0);
+ vlib_process_signal_event_mt (vlib_get_main_by_index (s->thread_index),
+ hcm->cli_node_index, HC_CONNECT_FAILED, 0);
return -1;
}
- hc_session = hc_session_get (hc_session_index, 0);
wrk = hc_worker_get (s->thread_index);
- new_hc_session = hc_session_alloc (wrk);
+ hc_session_t *hc_session, *new_hc_session = hc_session_alloc (wrk);
+ hc_session = hc_session_get (hc_session_index, 0);
new_hc_index = new_hc_session->session_index;
clib_memcpy_fast (new_hc_session, hc_session, sizeof (*hc_session));
- hc_session->vpp_session_index = s->session_index;
-
new_hc_session->session_index = new_hc_index;
new_hc_session->thread_index = s->thread_index;
new_hc_session->vpp_session_index = s->session_index;
@@ -138,104 +214,64 @@ hc_session_connected_callback (u32 app_index, u32 hc_session_index,
{
if (hcm->is_file)
http_add_header (
- &req_headers, http_header_name_token (HTTP_HEADER_CONTENT_TYPE),
+ &wrk->req_headers, http_header_name_token (HTTP_HEADER_CONTENT_TYPE),
http_content_type_token (HTTP_CONTENT_APP_OCTET_STREAM));
else
http_add_header (
- &req_headers, http_header_name_token (HTTP_HEADER_CONTENT_TYPE),
+ &wrk->req_headers, http_header_name_token (HTTP_HEADER_CONTENT_TYPE),
http_content_type_token (HTTP_CONTENT_APP_X_WWW_FORM_URLENCODED));
}
vec_foreach (header, hcm->custom_header)
- http_add_header (&req_headers, (const char *) header->name,
+ http_add_header (&wrk->req_headers, (const char *) header->name,
vec_len (header->name), (const char *) header->value,
vec_len (header->value));
- hcm->headers_buf = http_serialize_headers (req_headers);
- vec_free (req_headers);
+ wrk->headers_buf = http_serialize_headers (wrk->req_headers);
+ vec_free (wrk->req_headers);
- msg.method_type = hcm->req_method;
+ wrk->msg.method_type = hcm->req_method;
if (hcm->req_method == HTTP_REQ_POST)
- msg.data.body_len = vec_len (hcm->data);
+ wrk->msg.data.body_len = vec_len (hcm->data);
else
- msg.data.body_len = 0;
+ wrk->msg.data.body_len = 0;
- msg.type = HTTP_MSG_REQUEST;
+ wrk->msg.type = HTTP_MSG_REQUEST;
/* request target */
- msg.data.target_form = HTTP_TARGET_ORIGIN_FORM;
- msg.data.target_path_len = vec_len (hcm->target);
+ wrk->msg.data.target_form = HTTP_TARGET_ORIGIN_FORM;
+ wrk->msg.data.target_path_len = vec_len (hcm->target);
/* custom headers */
- msg.data.headers_len = vec_len (hcm->headers_buf);
+ wrk->msg.data.headers_len = vec_len (wrk->headers_buf);
/* total length */
- msg.data.len =
- msg.data.target_path_len + msg.data.headers_len + msg.data.body_len;
+ wrk->msg.data.len = wrk->msg.data.target_path_len +
+ wrk->msg.data.headers_len + wrk->msg.data.body_len;
if (hcm->use_ptr)
{
- uword target = pointer_to_uword (hcm->target);
- uword headers = pointer_to_uword (hcm->headers_buf);
- uword body = pointer_to_uword (hcm->data);
- msg.data.type = HTTP_MSG_DATA_PTR;
- svm_fifo_seg_t segs[4] = {
- { (u8 *) &msg, sizeof (msg) },
- { (u8 *) &target, sizeof (target) },
- { (u8 *) &headers, sizeof (headers) },
- { (u8 *) &body, sizeof (body) },
- };
-
- n_segs = (hcm->req_method == HTTP_REQ_GET) ? 3 : 4;
- rv = svm_fifo_enqueue_segments (s->tx_fifo, segs, n_segs,
- 0 /* allow partial */);
- if (hcm->req_method == HTTP_REQ_POST)
- ASSERT (rv == (sizeof (msg) + sizeof (target) + sizeof (headers) +
- sizeof (body)));
- else
- ASSERT (rv == (sizeof (msg) + sizeof (target) + sizeof (headers)));
- goto done;
+ wrk->msg.data.type = HTTP_MSG_DATA_PTR;
}
-
- msg.data.type = HTTP_MSG_DATA_INLINE;
- msg.data.target_path_offset = 0;
- msg.data.headers_offset = msg.data.target_path_len;
- msg.data.body_offset = msg.data.headers_offset + msg.data.headers_len;
-
- rv = svm_fifo_enqueue (s->tx_fifo, sizeof (msg), (u8 *) &msg);
- ASSERT (rv == sizeof (msg));
-
- rv = svm_fifo_enqueue (s->tx_fifo, vec_len (hcm->target), hcm->target);
- ASSERT (rv == vec_len (hcm->target));
-
- rv = svm_fifo_enqueue (s->tx_fifo, vec_len (hcm->headers_buf),
- hcm->headers_buf);
- ASSERT (rv == msg.data.headers_len);
-
- if (hcm->req_method == HTTP_REQ_POST)
+ else
{
- to_send = vec_len (hcm->data);
- n_enq = clib_min (svm_fifo_size (s->tx_fifo), to_send);
-
- rv = svm_fifo_enqueue (s->tx_fifo, n_enq, hcm->data);
- if (rv < to_send)
- {
- hcm->data_offset = (rv > 0) ? rv : 0;
- svm_fifo_add_want_deq_ntf (s->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
- }
+ wrk->msg.data.type = HTTP_MSG_DATA_INLINE;
+ wrk->msg.data.target_path_offset = 0;
+ wrk->msg.data.headers_offset = wrk->msg.data.target_path_len;
+ wrk->msg.data.body_offset =
+ wrk->msg.data.headers_offset + wrk->msg.data.headers_len;
}
-done:
- if (svm_fifo_set_event (s->tx_fifo))
- session_program_tx_io_evt (s->handle, SESSION_IO_EVT_TX);
+ if (hcm->repeat)
+ hc_stats.start = vlib_time_now (vlib_get_main_by_index (s->thread_index));
- return 0;
+ return hc_request (s, err);
}
static void
hc_session_disconnect_callback (session_t *s)
{
hc_main_t *hcm = &hc_main;
+ HTTP_DBG (1, "disconnecting");
vnet_disconnect_args_t _a = { 0 }, *a = &_a;
int rv;
-
a->handle = session_handle (s);
a->app_index = hcm->app_index;
if ((rv = vnet_disconnect_session (a)))
@@ -252,10 +288,10 @@ hc_session_transport_closed_callback (session_t *s)
}
static void
-hc_ho_cleanup_callback (session_t *ts)
+hc_ho_cleanup_callback (session_t *s)
{
- HTTP_DBG (1, "ho hc_index: %d:", ts->opaque);
- hc_ho_session_free (ts->opaque);
+ HTTP_DBG (1, "ho hc_index: %d:", s->opaque);
+ hc_ho_session_free (s->opaque);
}
static void
@@ -280,9 +316,12 @@ static int
hc_rx_callback (session_t *s)
{
hc_main_t *hcm = &hc_main;
+ hc_worker_t *wrk = hc_worker_get (s->thread_index);
hc_session_t *hc_session;
http_msg_t msg;
int rv;
+ session_error_t session_err = 0;
+ int send_err = 0;
hc_session = hc_session_get (s->opaque, s->thread_index);
@@ -300,28 +339,26 @@ hc_rx_callback (session_t *s)
if (msg.type != HTTP_MSG_REPLY)
{
clib_warning ("unexpected msg type %d", msg.type);
+ vlib_process_signal_event_mt (wrk->vlib_main, hcm->cli_node_index,
+ HC_GENERIC_ERR, 0);
return -1;
}
if (msg.data.headers_len)
{
- http_header_table_t *ht;
+ hcm->response_status =
+ format (0, "%U", format_http_status_code, msg.code);
+ svm_fifo_dequeue_drop (s->rx_fifo, msg.data.headers_offset);
+
vec_validate (hcm->resp_headers, msg.data.headers_len - 1);
- rv = svm_fifo_peek (s->rx_fifo, msg.data.headers_offset,
- msg.data.headers_len, hcm->resp_headers);
+ vec_set_len (hcm->resp_headers, msg.data.headers_len);
+ rv = svm_fifo_dequeue (s->rx_fifo, msg.data.headers_len,
+ hcm->resp_headers);
ASSERT (rv == msg.data.headers_len);
- HTTP_DBG (1, (char *) hcm->resp_headers);
-
- if (http_parse_headers (hcm->resp_headers, &ht))
- {
- clib_warning ("invalid headers received");
- return -1;
- }
- http_free_header_table (ht);
-
- hcm->response_status =
- format (0, "%U", format_http_status_code, msg.code);
+ HTTP_DBG (1, (char *) format (0, "%v", hcm->resp_headers));
+ msg.data.body_offset -=
+ msg.data.headers_len + msg.data.headers_offset;
}
if (msg.data.body_len == 0)
@@ -342,13 +379,18 @@ hc_rx_callback (session_t *s)
}
u32 max_deq = svm_fifo_max_dequeue (s->rx_fifo);
-
+ if (!max_deq)
+ {
+ goto done;
+ }
u32 n_deq = clib_min (hc_session->to_recv, max_deq);
u32 curr = vec_len (hcm->http_response);
rv = svm_fifo_dequeue (s->rx_fifo, n_deq, hcm->http_response + curr);
if (rv < 0)
{
clib_warning ("app dequeue(n=%d) failed; rv = %d", n_deq, rv);
+ vlib_process_signal_event_mt (wrk->vlib_main, hcm->cli_node_index,
+ HC_GENERIC_ERR, 0);
return -1;
}
@@ -360,11 +402,33 @@ hc_rx_callback (session_t *s)
done:
if (hc_session->to_recv == 0)
{
- hc_session_disconnect_callback (s);
- vlib_process_signal_event_mt (hcm->wrk->vlib_main, hcm->cli_node_index,
- HC_REPLY_RECEIVED, 0);
- }
+ if (hcm->repeat)
+ {
+ hc_stats.request_count++;
+ hc_stats.end = vlib_time_now (wrk->vlib_main);
+ hc_stats.elapsed_time = hc_stats.end - hc_stats.start;
+ if (hc_stats.elapsed_time >= hcm->duration &&
+ hc_stats.request_count >= hcm->repeat_count)
+ {
+ vlib_process_signal_event_mt (
+ wrk->vlib_main, hcm->cli_node_index, HC_REPEAT_DONE, 0);
+ hc_session_disconnect_callback (s);
+ }
+ else
+ {
+ send_err = hc_request (s, session_err);
+ if (send_err)
+ clib_warning ("failed to send request, error %d", send_err);
+ }
+ }
+ else
+ {
+ vlib_process_signal_event_mt (wrk->vlib_main, hcm->cli_node_index,
+ HC_REPLY_RECEIVED, 0);
+ hc_session_disconnect_callback (s);
+ }
+ }
return 0;
}
@@ -455,6 +519,7 @@ hc_connect ()
vnet_connect_args_t *a = 0;
hc_worker_t *wrk;
hc_session_t *hc_session;
+ transport_endpt_ext_cfg_t *ext_cfg;
vec_validate (a, 0);
clib_memset (a, 0, sizeof (a[0]));
@@ -462,6 +527,10 @@ hc_connect ()
clib_memcpy (&a->sep_ext, &hcm->connect_sep, sizeof (hcm->connect_sep));
a->app_index = hcm->app_index;
+ ext_cfg = session_endpoint_add_ext_cfg (
+ &a->sep_ext, TRANSPORT_ENDPT_EXT_CFG_HTTP, sizeof (ext_cfg->opaque));
+ ext_cfg->opaque = hcm->timeout;
+
/* allocate http session on main thread */
wrk = hc_worker_get (0);
hc_session = hc_session_alloc (wrk);
@@ -472,29 +541,19 @@ hc_connect ()
}
static clib_error_t *
-hc_run (vlib_main_t *vm)
+hc_get_event (vlib_main_t *vm)
{
hc_main_t *hcm = &hc_main;
- vlib_thread_main_t *vtm = vlib_get_thread_main ();
- u32 num_threads;
- hc_worker_t *wrk;
uword event_type, *event_data = 0;
- clib_error_t *err;
+ clib_error_t *err = NULL;
FILE *file_ptr;
+ u64 event_timeout = 10;
- num_threads = 1 /* main thread */ + vtm->n_threads;
- vec_validate (hcm->wrk, num_threads - 1);
- vec_foreach (wrk, hcm->wrk)
- wrk->thread_index = wrk - hcm->wrk;
-
- if ((err = hc_attach ()))
- return clib_error_return (0, "http client attach: %U", format_clib_error,
- err);
-
- hc_connect ();
-
- vlib_process_wait_for_event_or_clock (vm, hcm->timeout);
+ if (event_timeout == hcm->timeout || event_timeout == hcm->duration)
+ event_timeout += 5;
+ vlib_process_wait_for_event_or_clock (vm, event_timeout);
event_type = vlib_process_get_events (vm, &event_data);
+
switch (event_type)
{
case ~0:
@@ -506,11 +565,14 @@ hc_run (vlib_main_t *vm)
case HC_TRANSPORT_CLOSED:
err = clib_error_return (0, "error: transport closed");
break;
+ case HC_GENERIC_ERR:
+ err = clib_error_return (0, "error: unknown");
+ break;
case HC_REPLY_RECEIVED:
if (hcm->filename)
{
file_ptr =
- fopen ((char *) format (0, "/tmp/%v", hcm->filename), "w");
+ fopen ((char *) format (0, "/tmp/%v", hcm->filename), "a");
if (file_ptr == NULL)
{
vlib_cli_output (vm, "couldn't open file %v", hcm->filename);
@@ -524,10 +586,17 @@ hc_run (vlib_main_t *vm)
}
}
if (hcm->verbose)
- vlib_cli_output (vm, "< %v\n< %v", hcm->response_status,
+ vlib_cli_output (vm, "< %v< %v", hcm->response_status,
hcm->resp_headers);
- vlib_cli_output (vm, "<\n%v", hcm->http_response);
-
+ vlib_cli_output (vm, "\n%v\n", hcm->http_response);
+ break;
+ case HC_REPEAT_DONE:
+ vlib_cli_output (vm,
+ "< %d request(s) in %.6fs\n< avg latency "
+ "%.4fms\n< %.2f req/sec",
+ hc_stats.request_count, hc_stats.elapsed_time,
+ (hc_stats.elapsed_time / hc_stats.request_count) * 1000,
+ hc_stats.request_count / hc_stats.elapsed_time);
break;
default:
err = clib_error_return (0, "error: unexpected event %d", event_type);
@@ -538,6 +607,29 @@ hc_run (vlib_main_t *vm)
return err;
}
+static clib_error_t *
+hc_run (vlib_main_t *vm)
+{
+ hc_main_t *hcm = &hc_main;
+ vlib_thread_main_t *vtm = vlib_get_thread_main ();
+ u32 num_threads;
+ hc_worker_t *wrk;
+ clib_error_t *err;
+
+ num_threads = 1 /* main thread */ + vtm->n_threads;
+ vec_validate (hcm->wrk, num_threads - 1);
+ vec_foreach (wrk, hcm->wrk)
+ wrk->thread_index = wrk - hcm->wrk;
+
+ if ((err = hc_attach ()))
+ return clib_error_return (0, "http client attach: %U", format_clib_error,
+ err);
+
+ hc_connect ();
+
+ return hc_get_event (vm);
+}
+
static int
hc_detach ()
{
@@ -560,22 +652,24 @@ hc_detach ()
static void
hcc_worker_cleanup (hc_worker_t *wrk)
{
+ HTTP_DBG (1, "worker cleanup");
+ vec_free (wrk->headers_buf);
pool_free (wrk->sessions);
}
static void
hc_cleanup ()
{
+ HTTP_DBG (1, "cleanup");
hc_main_t *hcm = &hc_main;
hc_worker_t *wrk;
- http_header_ht_t *header;
+ hc_http_header_t *header;
vec_foreach (wrk, hcm->wrk)
hcc_worker_cleanup (wrk);
vec_free (hcm->uri);
vec_free (hcm->target);
- vec_free (hcm->headers_buf);
vec_free (hcm->data);
vec_free (hcm->resp_headers);
vec_free (hcm->http_response);
@@ -599,17 +693,19 @@ hc_command_fn (vlib_main_t *vm, unformat_input_t *input,
unformat_input_t _line_input, *line_input = &_line_input;
u8 *path = 0;
u8 *file_data;
- http_header_ht_t new_header;
+ hc_http_header_t new_header;
u8 *name;
u8 *value;
int rv;
hcm->timeout = 10;
+ hcm->repeat_count = 0;
+ hcm->duration = 0;
+ hcm->repeat = false;
+ hc_stats.request_count = 0;
if (hcm->attached)
return clib_error_return (0, "failed: already running!");
- hcm->use_ptr = 0;
-
/* Get a line of input. */
if (!unformat_user (input, unformat_line_input, line_input))
return clib_error_return (0, "expected required arguments");
@@ -652,6 +748,12 @@ hc_command_fn (vlib_main_t *vm, unformat_input_t *input,
hcm->verbose = true;
else if (unformat (line_input, "timeout %f", &hcm->timeout))
;
+ else if (unformat (line_input, "repeat %d", &hcm->repeat_count))
+ {
+ hcm->repeat = true;
+ }
+ else if (unformat (line_input, "duration %f", &hcm->duration))
+ hcm->repeat = true;
else
{
err = clib_error_return (0, "unknown input `%U'",
@@ -685,6 +787,12 @@ hc_command_fn (vlib_main_t *vm, unformat_input_t *input,
goto done;
}
}
+ if (hcm->duration && hcm->repeat_count)
+ {
+ err = clib_error_return (
+ 0, "combining duration and repeat is not supported");
+ goto done;
+ }
if ((rv = parse_uri ((char *) hcm->uri, &hcm->connect_sep)))
{
@@ -693,6 +801,9 @@ hc_command_fn (vlib_main_t *vm, unformat_input_t *input,
goto done;
}
+ if (hcm->repeat)
+ vlib_cli_output (vm, "Running, please wait...");
+
session_enable_disable_args_t args = { .is_en = 1,
.rt_engine_type =
RT_BACKEND_ENGINE_RULE_TABLE };
@@ -701,7 +812,6 @@ hc_command_fn (vlib_main_t *vm, unformat_input_t *input,
vlib_worker_thread_barrier_release (vm);
hcm->cli_node_index = vlib_get_current_process (vm)->node_runtime.node_index;
-
err = hc_run (vm);
if ((rv = hc_detach ()))
@@ -724,10 +834,11 @@ done:
VLIB_CLI_COMMAND (hc_command, static) = {
.path = "http client",
- .short_help = "[post] uri http://<ip-addr> target <origin-form> "
- "[data <form-urlencoded> | file <file-path>] [use-ptr] "
- "[save-to <filename>] [header <Key:Value>] [verbose] "
- "[timeout <seconds> (default = 10)]",
+ .short_help =
+ "[post] uri http://<ip-addr> target <origin-form> "
+ "[data <form-urlencoded> | file <file-path>] [use-ptr] "
+ "[save-to <filename>] [header <Key:Value>] [verbose] "
+ "[timeout <seconds> (default = 10)] [repeat <count> | duration <seconds>]",
.function = hc_command_fn,
.is_mp_safe = 1,
};
diff --git a/src/plugins/hs_apps/proxy.c b/src/plugins/hs_apps/proxy.c
index d7fe6fb54df..6d4a2cf399a 100644
--- a/src/plugins/hs_apps/proxy.c
+++ b/src/plugins/hs_apps/proxy.c
@@ -577,8 +577,7 @@ proxy_session_start_connect (proxy_session_side_ctx_t *sc, session_t *s)
* on its contents, the destination and parameters of the connect to the
* upstream are decided
*/
-
- clib_memcpy (&a->sep_ext, &pm->client_sep, sizeof (pm->client_sep));
+ clib_memcpy (&a->sep_ext, &pm->client_sep[tp], sizeof (*pm->client_sep));
}
a->api_context = ps_index;
@@ -1295,7 +1294,8 @@ proxy_server_create_command_fn (vlib_main_t * vm, unformat_input_t * input,
default_client_uri);
client_uri = format (0, "%s%c", default_client_uri, 0);
}
- if (parse_uri ((char *) client_uri, &pm->client_sep))
+ if (parse_uri ((char *) client_uri,
+ &pm->client_sep[pm->server_sep.transport_proto]))
{
error = clib_error_return (0, "Invalid client uri %v", client_uri);
goto done;
@@ -1345,6 +1345,7 @@ proxy_main_init (vlib_main_t * vm)
pm->active_open_client_index = ~0;
pm->server_app_index = APP_INVALID_INDEX;
pm->idle_timeout = 600; /* connect-proxy default idle timeout 10 minutes */
+ vec_validate (pm->client_sep, TRANSPORT_N_PROTOS - 1);
return 0;
}
diff --git a/src/plugins/hs_apps/proxy.h b/src/plugins/hs_apps/proxy.h
index 75567e4c1ba..b42d46468e1 100644
--- a/src/plugins/hs_apps/proxy.h
+++ b/src/plugins/hs_apps/proxy.h
@@ -100,7 +100,7 @@ typedef struct
u32 idle_timeout; /**< connect-proxy timeout for idle connections */
int rcv_buffer_size;
session_endpoint_cfg_t server_sep;
- session_endpoint_cfg_t client_sep;
+ session_endpoint_cfg_t *client_sep;
/*
* Flags
diff --git a/src/plugins/http/http.c b/src/plugins/http/http.c
index 7c0b55e26d2..c5cd894cb59 100644
--- a/src/plugins/http/http.c
+++ b/src/plugins/http/http.c
@@ -17,6 +17,7 @@
#include <vnet/session/session.h>
#include <http/http_timer.h>
#include <http/http_status_codes.h>
+#include <http/http_header_names.h>
static http_main_t http_main;
@@ -35,6 +36,12 @@ const http_buffer_type_t msg_to_buf_type[] = {
[HTTP_MSG_DATA_PTR] = HTTP_BUFFER_PTR,
};
+const char *http_upgrade_proto_str[] = { "",
+#define _(sym, str) str,
+ foreach_http_upgrade_proto
+#undef _
+};
+
static u8 *
format_http_req_state (u8 *s, va_list *va)
{
@@ -446,6 +453,9 @@ static const char *http_response_template = "HTTP/1.1 %s\r\n"
static const char *content_len_template = "Content-Length: %llu\r\n";
+static const char *connection_upgrade_template = "Connection: upgrade\r\n"
+ "Upgrade: %s\r\n";
+
/**
* http request boilerplate
*/
@@ -706,6 +716,7 @@ http_parse_request_line (http_req_t *req, http_status_code_t *ec)
{
HTTP_DBG (0, "CONNECT method");
req->method = HTTP_REQ_CONNECT;
+ req->upgrade_proto = HTTP_UPGRADE_PROTO_NA;
req->target_path_offset = method_offset + 8;
req->is_tunnel = 1;
}
@@ -892,7 +903,17 @@ http_parse_status_line (http_req_t *req)
static int
http_identify_headers (http_req_t *req, http_status_code_t *ec)
{
- int i;
+ int rv;
+ u8 *p, *end, *name_start, *value_start;
+ u32 name_len, value_len;
+ http_field_line_t *field_line;
+ uword header_index;
+
+ vec_reset_length (req->headers);
+ req->content_len_header_index = ~0;
+ req->connection_header_index = ~0;
+ req->upgrade_header_index = ~0;
+ req->headers_offset = req->rx_buf_offset;
/* check if we have any header */
if ((req->rx_buf[req->rx_buf_offset] == '\r') &&
@@ -905,16 +926,53 @@ http_identify_headers (http_req_t *req, http_status_code_t *ec)
return 0;
}
- /* find empty line indicating end of header section */
- i = v_find_index (req->rx_buf, req->rx_buf_offset, 0, "\r\n\r\n");
- if (i < 0)
+ end = req->rx_buf + vec_len (req->rx_buf);
+ p = req->rx_buf + req->rx_buf_offset;
+
+ while (1)
{
- clib_warning ("cannot find header section end");
- *ec = HTTP_STATUS_BAD_REQUEST;
- return -1;
- }
- req->headers_offset = req->rx_buf_offset;
- req->headers_len = i - req->rx_buf_offset + 2;
+ rv = _parse_field_name (&p, end, &name_start, &name_len);
+ if (rv != 0)
+ {
+ *ec = HTTP_STATUS_BAD_REQUEST;
+ return -1;
+ }
+ rv = _parse_field_value (&p, end, &value_start, &value_len);
+ if (rv != 0 || (end - p) < 2)
+ {
+ *ec = HTTP_STATUS_BAD_REQUEST;
+ return -1;
+ }
+
+ vec_add2 (req->headers, field_line, 1);
+ field_line->name_offset =
+ (name_start - req->rx_buf) - req->headers_offset;
+ field_line->name_len = name_len;
+ field_line->value_offset =
+ (value_start - req->rx_buf) - req->headers_offset;
+ field_line->value_len = value_len;
+ header_index = field_line - req->headers;
+
+ /* find headers that will be used later in preprocessing */
+ if (req->content_len_header_index == ~0 &&
+ http_token_is ((const char *) name_start, name_len,
+ http_header_name_token (HTTP_HEADER_CONTENT_LENGTH)))
+ req->content_len_header_index = header_index;
+ else if (req->connection_header_index == ~0 &&
+ http_token_is ((const char *) name_start, name_len,
+ http_header_name_token (HTTP_HEADER_CONNECTION)))
+ req->connection_header_index = header_index;
+ else if (req->upgrade_header_index == ~0 &&
+ http_token_is ((const char *) name_start, name_len,
+ http_header_name_token (HTTP_HEADER_UPGRADE)))
+ req->upgrade_header_index = header_index;
+
+ /* are we done? */
+ if (*p == '\r' && *(p + 1) == '\n')
+ break;
+ }
+
+ req->headers_len = p - (req->rx_buf + req->headers_offset);
req->control_data_len += (req->headers_len + 2);
HTTP_DBG (2, "headers length: %u", req->headers_len);
HTTP_DBG (2, "headers offset: %u", req->headers_offset);
@@ -925,9 +983,10 @@ http_identify_headers (http_req_t *req, http_status_code_t *ec)
static int
http_identify_message_body (http_req_t *req, http_status_code_t *ec)
{
- int i, value_len;
- u8 *end, *p, *value_start;
+ int i;
+ u8 *p;
u64 body_len = 0, digit;
+ http_field_line_t *field_line;
req->body_len = 0;
@@ -944,67 +1003,15 @@ http_identify_message_body (http_req_t *req, http_status_code_t *ec)
/* TODO check for chunked transfer coding */
- /* try to find Content-Length header */
- i = v_find_index (req->rx_buf, req->headers_offset, req->headers_len,
- "Content-Length:");
- if (i < 0)
+ if (req->content_len_header_index == ~0)
{
HTTP_DBG (2, "Content-Length header not present, no message-body");
return 0;
}
- req->rx_buf_offset = i + 15;
+ field_line = vec_elt_at_index (req->headers, req->content_len_header_index);
- i = v_find_index (req->rx_buf, req->rx_buf_offset, req->headers_len, "\r\n");
- if (i < 0)
- {
- clib_warning ("end of line missing");
- *ec = HTTP_STATUS_BAD_REQUEST;
- return -1;
- }
- value_len = i - req->rx_buf_offset;
- if (value_len < 1)
- {
- clib_warning ("invalid header, content length value missing");
- *ec = HTTP_STATUS_BAD_REQUEST;
- return -1;
- }
-
- end = req->rx_buf + req->rx_buf_offset + value_len;
- p = req->rx_buf + req->rx_buf_offset;
- /* skip leading whitespace */
- while (1)
- {
- if (p == end)
- {
- clib_warning ("value not found");
- *ec = HTTP_STATUS_BAD_REQUEST;
- return -1;
- }
- else if (*p != ' ' && *p != '\t')
- {
- break;
- }
- p++;
- value_len--;
- }
- value_start = p;
- /* skip trailing whitespace */
- p = value_start + value_len - 1;
- while (*p == ' ' || *p == '\t')
- {
- p--;
- value_len--;
- }
-
- if (value_len < 1)
- {
- clib_warning ("value not found");
- *ec = HTTP_STATUS_BAD_REQUEST;
- return -1;
- }
-
- p = value_start;
- for (i = 0; i < value_len; i++)
+ p = req->rx_buf + req->headers_offset + field_line->value_offset;
+ for (i = 0; i < field_line->value_len; i++)
{
/* check for digit */
if (!isdigit (*p))
@@ -1095,6 +1102,7 @@ http_req_state_wait_transport_reply (http_conn_t *hc,
msg.data.body_len = hc->req.body_len;
msg.data.type = HTTP_MSG_DATA_INLINE;
msg.data.len = len;
+ msg.data.headers_ctx = pointer_to_uword (hc->req.headers);
svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) },
{ hc->req.rx_buf, len } };
@@ -1130,6 +1138,49 @@ error:
return HTTP_SM_ERROR;
}
+#define http_field_line_value_token(_fl, _req) \
+ (const char *) ((_req)->rx_buf + (_req)->headers_offset + \
+ (_fl)->value_offset), \
+ (_fl)->value_len
+
+static void
+http_check_connection_upgrade (http_req_t *req)
+{
+ http_field_line_t *connection, *upgrade;
+ u8 skip;
+
+ skip = (req->method != HTTP_REQ_GET) + (req->connection_header_index == ~0) +
+ (req->upgrade_header_index == ~0);
+ if (skip)
+ return;
+
+ connection = vec_elt_at_index (req->headers, req->connection_header_index);
+ /* connection options are case-insensitive (RFC9110 7.6.1) */
+ if (http_token_is_case (http_field_line_value_token (connection, req),
+ http_token_lit ("upgrade")))
+ {
+ upgrade = vec_elt_at_index (req->headers, req->upgrade_header_index);
+
+ /* check upgrade protocol, we want to ignore something like upgrade to
+ * newer HTTP version, only tunnels are supported */
+ if (0)
+ ;
+#define _(sym, str) \
+ else if (http_token_is (http_field_line_value_token (upgrade, req), \
+ http_token_lit (str))) req->upgrade_proto = \
+ HTTP_UPGRADE_PROTO_##sym;
+ foreach_http_upgrade_proto
+#undef _
+ else return;
+
+ HTTP_DBG (1, "connection upgrade: %U", format_http_bytes,
+ req->rx_buf + req->headers_offset + upgrade->value_offset,
+ upgrade->value_len);
+ req->is_tunnel = 1;
+ req->method = HTTP_REQ_CONNECT;
+ }
+}
+
static http_sm_result_t
http_req_state_wait_transport_method (http_conn_t *hc,
transport_send_params_t *sp)
@@ -1164,6 +1215,8 @@ http_req_state_wait_transport_method (http_conn_t *hc,
if (rv)
goto error;
+ http_check_connection_upgrade (&hc->req);
+
rv = http_identify_message_body (&hc->req, &ec);
if (rv)
goto error;
@@ -1196,6 +1249,8 @@ http_req_state_wait_transport_method (http_conn_t *hc,
msg.data.headers_len = hc->req.headers_len;
msg.data.body_offset = hc->req.body_offset;
msg.data.body_len = hc->req.body_len;
+ msg.data.headers_ctx = pointer_to_uword (hc->req.headers);
+ msg.data.upgrade_proto = hc->req.upgrade_proto;
svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) },
{ hc->req.rx_buf, len } };
@@ -1286,15 +1341,21 @@ http_req_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp)
/* Server */
hc->app_name);
- /* RFC9110 9.3.6: A server MUST NOT send Content-Length header field in a
- * 2xx (Successful) response to CONNECT. */
- if (hc->req.is_tunnel && http_status_code_str[msg.code][0] == '2')
+ /* RFC9110 8.6: A server MUST NOT send Content-Length header field in a
+ * 2xx (Successful) response to CONNECT or with a status code of 101
+ * (Switching Protocols). */
+ if (hc->req.is_tunnel && (http_status_code_str[msg.code][0] == '2' ||
+ msg.code == HTTP_STATUS_SWITCHING_PROTOCOLS))
{
ASSERT (msg.data.body_len == 0);
next_state = HTTP_REQ_STATE_TUNNEL;
+ if (hc->req.upgrade_proto > HTTP_UPGRADE_PROTO_NA)
+ response = format (response, connection_upgrade_template,
+ http_upgrade_proto_str[hc->req.upgrade_proto]);
/* cleanup some stuff we don't need anymore in tunnel mode */
http_conn_timer_stop (hc);
vec_free (hc->req.rx_buf);
+ vec_free (hc->req.headers);
http_buffer_free (&hc->req.tx_buf);
}
else
@@ -1860,6 +1921,7 @@ http_ts_cleanup_callback (session_t *ts, session_cleanup_ntf_t ntf)
HTTP_DBG (1, "going to free hc [%u]%x", ts->thread_index, ts->opaque);
vec_free (hc->req.rx_buf);
+ vec_free (hc->req.headers);
http_buffer_free (&hc->req.tx_buf);
diff --git a/src/plugins/http/http.h b/src/plugins/http/http.h
index 844235cbdbb..7405d3d3bf7 100644
--- a/src/plugins/http/http.h
+++ b/src/plugins/http/http.h
@@ -352,12 +352,36 @@ typedef enum http_header_name_
#undef _
} http_header_name_t;
+#define HTTP_BOOLEAN_TRUE "?1"
+
+#define foreach_http_upgrade_proto \
+ _ (CONNECT_UDP, "connect-udp") \
+ _ (CONNECT_IP, "connect-ip") \
+ _ (WEBSOCKET, "websocket")
+
+typedef enum http_upgrade_proto_
+{
+ HTTP_UPGRADE_PROTO_NA =
+ 0, /* indicating standard CONNECT where protocol is omitted */
+#define _(sym, str) HTTP_UPGRADE_PROTO_##sym,
+ foreach_http_upgrade_proto
+#undef _
+} http_upgrade_proto_t;
+
typedef enum http_msg_data_type_
{
HTTP_MSG_DATA_INLINE,
HTTP_MSG_DATA_PTR
} http_msg_data_type_t;
+typedef struct http_field_line_
+{
+ u32 name_offset;
+ u32 name_len;
+ u32 value_offset;
+ u32 value_len;
+} http_field_line_t;
+
typedef struct http_msg_data_
{
http_msg_data_type_t type;
@@ -371,6 +395,8 @@ typedef struct http_msg_data_
u32 headers_len;
u32 body_offset;
u64 body_len;
+ uword headers_ctx;
+ http_upgrade_proto_t upgrade_proto;
u8 data[0];
} http_msg_data_t;
@@ -422,6 +448,13 @@ typedef struct http_req_
u32 body_offset;
u64 body_len;
+
+ http_field_line_t *headers;
+ uword content_len_header_index;
+ uword connection_header_index;
+ uword upgrade_header_index;
+
+ http_upgrade_proto_t upgrade_proto;
} http_req_t;
typedef struct http_tc_
@@ -477,6 +510,27 @@ typedef struct http_main_
u32 fifo_size;
} http_main_t;
+always_inline u8 *
+format_http_bytes (u8 *s, va_list *va)
+{
+ u8 *bytes = va_arg (*va, u8 *);
+ int n_bytes = va_arg (*va, int);
+ uword i;
+
+ if (n_bytes == 0)
+ return s;
+
+ for (i = 0; i < n_bytes; i++)
+ {
+ if (isprint (bytes[i]))
+ s = format (s, "%c", bytes[i]);
+ else
+ s = format (s, "\\x%02x", bytes[i]);
+ }
+
+ return s;
+}
+
always_inline int
_validate_target_syntax (u8 *target, u32 len, int is_query, int *is_encoded)
{
@@ -767,22 +821,94 @@ _parse_field_value (u8 **pos, u8 *end, u8 **field_value_start,
typedef struct
{
- u8 *name;
- u8 *value;
-} http_header_ht_t;
-
-typedef struct
-{
http_token_t name;
http_token_t value;
} http_header_t;
typedef struct
{
- http_header_ht_t *headers;
+ http_header_t *headers;
uword *value_by_name;
+ u8 *buf;
+ char **concatenated_values;
} http_header_table_t;
+#define HTTP_HEADER_TABLE_NULL \
+ { \
+ .headers = 0, .value_by_name = 0, .buf = 0, .concatenated_values = 0, \
+ }
+
+always_inline u8
+http_token_is (const char *actual, uword actual_len, const char *expected,
+ uword expected_len)
+{
+ ASSERT (actual != 0);
+ if (actual_len != expected_len)
+ return 0;
+ return memcmp (actual, expected, expected_len) == 0 ? 1 : 0;
+}
+
+always_inline u8
+http_token_is_case (const char *actual, uword actual_len, const char *expected,
+ uword expected_len)
+{
+ uword i;
+ ASSERT (actual != 0);
+ if (actual_len != expected_len)
+ return 0;
+ for (i = 0; i < expected_len; i++)
+ {
+ if (tolower (actual[i]) != expected[i])
+ return 0;
+ }
+ return 1;
+}
+
+always_inline u8
+http_token_contains (const char *haystack, uword haystack_len,
+ const char *needle, uword needle_len)
+{
+ uword end_index, i;
+ ASSERT (haystack != 0);
+ if (haystack_len < needle_len)
+ return 0;
+ end_index = haystack_len - needle_len;
+ for (i = 0; i <= end_index; i++)
+ {
+ if (!memcmp (haystack + i, needle, needle_len))
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Reset header table before reuse.
+ *
+ * @param ht Header table to reset.
+ */
+always_inline void
+http_reset_header_table (http_header_table_t *ht)
+{
+ int i;
+ for (i = 0; i < vec_len (ht->concatenated_values); i++)
+ vec_free (ht->concatenated_values[i]);
+ vec_reset_length (ht->concatenated_values);
+ vec_reset_length (ht->headers);
+ vec_reset_length (ht->buf);
+ hash_free (ht->value_by_name);
+}
+
+/**
+ * Initialize header table input buffer.
+ * @param ht Header table.
+ * @param msg HTTP transport message metadata.
+ */
+always_inline void
+http_init_header_table_buf (http_header_table_t *ht, http_msg_t msg)
+{
+ vec_validate (ht->buf, msg.data.headers_len - 1);
+}
+
/**
* Free header table's memory.
*
@@ -791,86 +917,87 @@ typedef struct
always_inline void
http_free_header_table (http_header_table_t *ht)
{
- http_header_ht_t *header;
- vec_foreach (header, ht->headers)
- {
- vec_free (header->name);
- vec_free (header->value);
- }
+ int i;
+ for (i = 0; i < vec_len (ht->concatenated_values); i++)
+ vec_free (ht->concatenated_values[i]);
+ vec_free (ht->concatenated_values);
vec_free (ht->headers);
+ vec_free (ht->buf);
hash_free (ht->value_by_name);
- clib_mem_free (ht);
+}
+
+static uword
+_http_ht_hash_key_sum (hash_t *h, uword key)
+{
+ http_token_t *name = uword_to_pointer (key, http_token_t *);
+ return hash_memory (name->base, name->len, 0);
+}
+
+static uword
+_http_ht_hash_key_equal (hash_t *h, uword key1, uword key2)
+{
+ http_token_t *name1 = uword_to_pointer (key1, http_token_t *);
+ http_token_t *name2 = uword_to_pointer (key2, http_token_t *);
+ return name1 && name2 &&
+ http_token_is (name1->base, name1->len, name2->base, name2->len);
}
/**
- * Parse headers in given vector.
+ * Build header table.
*
- * @param headers Vector to parse.
- * @param [out] header_table Parsed headers in case of success.
- *
- * @return @c 0 on success.
+ * @param header_table Header table with loaded buffer.
+ * @param msg HTTP transport message metadata.
*
- * The caller is responsible to free the returned @c header_table
- * using @c http_free_header_table .
+ * @note If reusing already allocated header table use
+ * @c http_reset_header_table first.
*/
-always_inline int
-http_parse_headers (u8 *headers, http_header_table_t **header_table)
+always_inline void
+http_build_header_table (http_header_table_t *ht, http_msg_t msg)
{
- u8 *pos, *end, *name_start, *value_start, *name;
- u32 name_len, value_len;
- int rv;
- http_header_ht_t *header;
- http_header_table_t *ht;
+ http_token_t name;
+ http_header_t *header;
+ http_field_line_t *field_lines, *field_line;
uword *p;
- end = headers + vec_len (headers);
- pos = headers;
+ ASSERT (ht);
+ field_lines = uword_to_pointer (msg.data.headers_ctx, http_field_line_t *);
+ ht->value_by_name =
+ hash_create2 (0, 0, sizeof (uword), _http_ht_hash_key_sum,
+ _http_ht_hash_key_equal, 0, 0);
- ht = clib_mem_alloc (sizeof (*ht));
- ht->value_by_name = hash_create_string (0, sizeof (uword));
- ht->headers = 0;
- do
+ vec_foreach (field_line, field_lines)
{
- rv = _parse_field_name (&pos, end, &name_start, &name_len);
- if (rv != 0)
- {
- http_free_header_table (ht);
- return rv;
- }
- rv = _parse_field_value (&pos, end, &value_start, &value_len);
- if (rv != 0)
- {
- http_free_header_table (ht);
- return rv;
- }
- name = vec_new (u8, name_len);
- clib_memcpy (name, name_start, name_len);
- vec_terminate_c_string (name);
+ name.base = (char *) (ht->buf + field_line->name_offset);
+ name.len = field_line->name_len;
/* check if header is repeated */
- p = hash_get_mem (ht->value_by_name, name);
+ p = hash_get_mem (ht->value_by_name, &name);
if (p)
{
- /* if yes combine values */
+ char *new_value = 0;
header = vec_elt_at_index (ht->headers, p[0]);
- vec_pop (header->value); /* drop null byte */
- header->value = format (header->value, ", %U%c", format_ascii_bytes,
- value_start, value_len, 0);
- vec_free (name);
+ u32 new_len = header->value.len + field_line->value_len + 2;
+ vec_validate (new_value, new_len - 1);
+ clib_memcpy (new_value, header->value.base, header->value.len);
+ new_value[header->value.len] = ',';
+ new_value[header->value.len + 1] = ' ';
+ clib_memcpy (new_value + header->value.len + 2,
+ ht->buf + field_line->value_offset,
+ field_line->value_len);
+ vec_add1 (ht->concatenated_values, new_value);
+ header->value.base = new_value;
+ header->value.len = new_len;
continue;
}
/* or create new record */
- vec_add2 (ht->headers, header, sizeof (*header));
- header->name = name;
- header->value = vec_new (u8, value_len);
- clib_memcpy (header->value, value_start, value_len);
- vec_terminate_c_string (header->value);
- hash_set_mem (ht->value_by_name, header->name, header - ht->headers);
+ vec_add2 (ht->headers, header, 1);
+ header->name.base = name.base;
+ header->name.len = name.len;
+ header->value.base = (char *) (ht->buf + field_line->value_offset);
+ header->value.len = field_line->value_len;
+ HTTP_DBG (1, "value: %U", format_http_bytes, header->value.base,
+ header->value.len);
+ hash_set_mem (ht->value_by_name, &header->name, header - ht->headers);
}
- while (pos != end);
-
- *header_table = ht;
-
- return 0;
}
/**
@@ -881,17 +1008,19 @@ http_parse_headers (u8 *headers, http_header_table_t **header_table)
*
* @return Header's value in case of success, @c 0 otherwise.
*/
-always_inline const char *
-http_get_header (http_header_table_t *header_table, const char *name)
+always_inline const http_header_t *
+http_get_header (http_header_table_t *header_table, const char *name,
+ uword name_len)
{
uword *p;
- http_header_ht_t *header;
+ http_header_t *header;
+ http_token_t name_token = { (char *) name, name_len };
- p = hash_get_mem (header_table->value_by_name, name);
+ p = hash_get_mem (header_table->value_by_name, &name_token);
if (p)
{
header = vec_elt_at_index (header_table->headers, p[0]);
- return (const char *) header->value;
+ return header;
}
return 0;
@@ -1340,6 +1469,12 @@ _http_parse_capsule (u8 *data, u64 len, u64 *type, u8 *value_offset,
return -1;
}
+ if (p == end)
+ {
+ clib_warning ("capsule length missing");
+ return -1;
+ }
+
capsule_value_len = _http_decode_varint (&p, end);
if (capsule_value_len == HTTP_INVALID_VARINT)
{
@@ -1389,6 +1524,11 @@ http_decap_udp_payload_datagram (u8 *data, u64 len, u8 *payload_offset,
}
p += value_offset;
+ if (p == end)
+ {
+ clib_warning ("context ID missing");
+ return -1;
+ }
/* context ID field should be zero (RFC9298 section 4) */
context_id = _http_decode_varint (&p, end);
diff --git a/src/plugins/http/http_plugin.rst b/src/plugins/http/http_plugin.rst
index f86c796bd83..bf414cf96ef 100644
--- a/src/plugins/http/http_plugin.rst
+++ b/src/plugins/http/http_plugin.rst
@@ -15,9 +15,11 @@ Usage
-----
The plugin exposes following inline functions: ``http_validate_abs_path_syntax``, ``http_validate_query_syntax``,
-``http_percent_decode``, ``http_path_remove_dot_segments``, ``http_parse_headers``, ``http_get_header``,
-``http_free_header_table``, ``http_add_header``, ``http_serialize_headers``, ``http_parse_authority_form_target``,
-``http_serialize_authority_form_target``, ``http_parse_absolute_form``, ``http_parse_masque_host_port``.
+``http_percent_decode``, ``http_path_remove_dot_segments``, ``http_build_header_table``, ``http_get_header``,
+``http_reset_header_table``, ``http_free_header_table``, ``http_add_header``,
+``http_serialize_headers``, ``http_parse_authority_form_target``, ``http_serialize_authority_form_target``,
+``http_parse_absolute_form``, ``http_parse_masque_host_port``, ``http_decap_udp_payload_datagram``,
+``http_encap_udp_payload_datagram``. ``http_token_is``, ``http_token_is_case``, ``http_token_contains``
It relies on the hoststack constructs and uses ``http_msg_data_t`` data structure for passing metadata to/from applications.
@@ -125,24 +127,60 @@ Following example shows how to parse headers:
#include <http/http_header_names.h>
if (msg.data.headers_len)
{
- u8 *headers = 0;
- http_header_table_t *ht;
- vec_validate (headers, msg.data.headers_len - 1);
+ http_header_table_t ht = HTTP_HEADER_TABLE_NULL;
+ http_init_header_table_buf (&ht, msg);
rv = svm_fifo_peek (ts->rx_fifo, msg.data.headers_offset,
- msg.data.headers_len, headers);
+ msg.data.headers_len, ht.buf);
ASSERT (rv == msg.data.headers_len);
- if (http_parse_headers (headers, &ht))
+ http_build_header_table (&ht, msg);
+ /* get Accept header */
+ const http_header_t *accept = http_get_header (&ht, http_header_name_token (HTTP_HEADER_ACCEPT));
+ if (accept_value)
{
- /* your error handling */
+ /* do something interesting */
}
+ http_free_header_table (&ht);
+ }
+
+Allocated header table memory can be reused, you just need to reset it using ``http_reset_header_table`` before reuse.
+We will add following member to our session context structure:
+
+.. code-block:: C
+
+ typedef struct
+ {
+ /* ... */
+ http_header_table_t ht;
+ } session_ctx_t;
+
+Don't forget to zero allocated session context.
+
+And in ``session_cleanup_callback`` we free header table memory:
+
+.. code-block:: C
+
+ http_free_header_table (&ctx->ht);
+
+Modified example above:
+
+.. code-block:: C
+
+ #include <http/http_header_names.h>
+ http_reset_header_table (&ctx->ht);
+ /* ... */
+ if (msg.data.headers_len)
+ {
+ http_init_header_table_buf (&ctx->ht, msg);
+ rv = svm_fifo_peek (ts->rx_fifo, msg.data.headers_offset,
+ msg.data.headers_len, ctx->ht.buf);
+ ASSERT (rv == msg.data.headers_len);
+ http_build_header_table (&ctx->ht, msg);
/* get Accept header */
- const char *accept_value = http_get_header (ht, http_header_name_str (HTTP_HEADER_ACCEPT));
+ const http_header_t *accept = http_get_header (&ctx->ht, http_header_name_token (HTTP_HEADER_ACCEPT));
if (accept_value)
{
/* do something interesting */
}
- http_free_header_table (ht);
- vec_free (headers);
}
Finally application reads body (if any), which might be received in multiple pieces (depends on size), so we might need some state machine in ``builtin_app_rx_callback``.
@@ -437,24 +475,19 @@ Following example shows how to parse headers:
#include <http/http_header_names.h>
if (msg.data.headers_len)
{
- u8 *headers = 0;
- http_header_table_t *ht;
- vec_validate (headers, msg.data.headers_len - 1);
+ http_header_table_t ht = HTTP_HEADER_TABLE_NULL;
+ http_init_header_table_buf (&ht, msg);
rv = svm_fifo_peek (ts->rx_fifo, msg.data.headers_offset,
- msg.data.headers_len, headers);
+ msg.data.headers_len, ht.buf);
ASSERT (rv == msg.data.headers_len);
- if (http_parse_headers (headers, &ht))
- {
- /* your error handling */
- }
+ http_build_header_table (&ht, msg);
/* get Content-Type header */
- const char *content_type = http_get_header (ht, http_header_name_str (HTTP_HEADER_CONTENT_TYPE));
+ const http_header_t *content_type = http_get_header (&ht, http_header_name_token (HTTP_HEADER_CONTENT_TYPE));
if (content_type)
{
/* do something interesting */
}
- http_free_header_table (ht);
- vec_free (headers);
+ http_free_header_table (&ht);
}
Finally application reads body, which might be received in multiple pieces (depends on size), so we might need some state machine in ``builtin_app_rx_callback``.
diff --git a/src/plugins/http/test/http_test.c b/src/plugins/http/test/http_test.c
index a96d5f9d9f7..40fd4463b61 100644
--- a/src/plugins/http/test/http_test.c
+++ b/src/plugins/http/test/http_test.c
@@ -333,11 +333,26 @@ http_test_udp_payload_datagram (vlib_main_t *vm)
HTTP_TEST ((payload_offset == 4), "payload_offset=%u should be 4",
payload_offset);
+ /* Type = 0x00, Len = incomplete */
u8 invalid_input[] = { 0x00, 0x7B };
rv = http_decap_udp_payload_datagram (invalid_input, sizeof (invalid_input),
&payload_offset, &payload_len);
- HTTP_TEST ((rv == -1), "'%U' should be invalid", format_hex_bytes,
- invalid_input, sizeof (invalid_input));
+ HTTP_TEST ((rv == -1), "'%U' should be invalid (length incomplete)",
+ format_hex_bytes, invalid_input, sizeof (invalid_input));
+
+ /* Type = 0x00, Len = missing */
+ u8 invalid_input2[] = { 0x00 };
+ rv = http_decap_udp_payload_datagram (
+ invalid_input2, sizeof (invalid_input2), &payload_offset, &payload_len);
+ HTTP_TEST ((rv == -1), "'%U' should be invalid (length missing)",
+ format_hex_bytes, invalid_input2, sizeof (invalid_input2));
+
+ /* Type = 0x00, Len = 15293, Context ID = missing */
+ u8 invalid_input3[] = { 0x00, 0x7B, 0xBD };
+ rv = http_decap_udp_payload_datagram (
+ invalid_input3, sizeof (invalid_input3), &payload_offset, &payload_len);
+ HTTP_TEST ((rv == -1), "'%U' should be invalid (context id missing)",
+ format_hex_bytes, invalid_input3, sizeof (invalid_input3));
/* Type = 0x00, Len = 494878333, Context ID = 0x00 */
u8 long_payload_input[] = { 0x00, 0x9D, 0x7F, 0x3E, 0x7D, 0x00, 0x12 };
@@ -408,7 +423,9 @@ test_http_command_fn (vlib_main_t *vm, unformat_input_t *input,
done:
if (res)
- return clib_error_return (0, "HTTP unit test failed");
+ return clib_error_return (0, "FAILED");
+
+ vlib_cli_output (vm, "SUCCESS");
return 0;
}
diff --git a/src/plugins/map/map_api.c b/src/plugins/map/map_api.c
index 1dbff4ca0d1..21a3ca5f173 100644
--- a/src/plugins/map/map_api.c
+++ b/src/plugins/map/map_api.c
@@ -92,10 +92,9 @@ send_domain_details (u32 map_domain_index, vl_api_registration_t * rp,
map_domain_t *d = pool_elt_at_index (mm->domains, map_domain_index);
/* Make sure every field is initiated (or don't skip the clib_memset()) */
- map_domain_extra_t *de =
- vec_elt_at_index (mm->domain_extras, map_domain_index);
- int tag_len = clib_min (ARRAY_LEN (rmp->tag), vec_len (de->tag) + 1);
-
+ map_domain_extra_t *de = 0;
+ if (mm->domain_extras)
+ de = vec_elt_at_index (mm->domain_extras, map_domain_index);
REPLY_MACRO_DETAILS4(VL_API_MAP_DOMAIN_DETAILS, rp, context,
({
rmp->domain_index = htonl (map_domain_index);
@@ -113,8 +112,16 @@ send_domain_details (u32 map_domain_index, vl_api_registration_t * rp,
rmp->psid_length = d->psid_length;
rmp->flags = d->flags;
rmp->mtu = htons (d->mtu);
- memcpy (rmp->tag, de->tag, tag_len - 1);
- rmp->tag[tag_len - 1] = '\0';
+ if (de)
+ {
+ int tag_len = clib_min (ARRAY_LEN (rmp->tag), vec_len (de->tag) + 1);
+ clib_memcpy (rmp->tag, de->tag, tag_len - 1);
+ rmp->tag[tag_len - 1] = '\0';
+ }
+ else
+ {
+ clib_memset (rmp->tag, 0, sizeof (rmp->tag));
+ }
}));
}
diff --git a/src/plugins/memif/device.c b/src/plugins/memif/device.c
index 017a001168b..5e070ebd32a 100644
--- a/src/plugins/memif/device.c
+++ b/src/plugins/memif/device.c
@@ -67,12 +67,37 @@ format_memif_device (u8 * s, va_list * args)
u32 dev_instance = va_arg (*args, u32);
int verbose = va_arg (*args, int);
u32 indent = format_get_indent (s);
+ memif_main_t *mm = &memif_main;
+ memif_queue_t *mq;
+ uword i;
s = format (s, "MEMIF interface");
if (verbose)
{
s = format (s, "\n%U instance %u", format_white_space, indent + 2,
dev_instance);
+
+ memif_if_t *mif = pool_elt_at_index (mm->interfaces, dev_instance);
+ vec_foreach_index (i, mif->tx_queues)
+ {
+ mq = vec_elt_at_index (mif->tx_queues, i);
+ s = format (s, "\n%U master-to-slave ring %u", format_white_space,
+ indent + 4, i);
+ s = format (s, "\n%U packets sent: %u", format_white_space,
+ indent + 6, mq->n_packets);
+ s = format (s, "\n%U no tx slot: %u", format_white_space, indent + 6,
+ mq->no_free_tx);
+ s = format (s, "\n%U max no tx slot: %u", format_white_space,
+ indent + 6, mq->max_no_free_tx);
+ }
+ vec_foreach_index (i, mif->rx_queues)
+ {
+ mq = vec_elt_at_index (mif->rx_queues, i);
+ s = format (s, "\n%U slave-to-master ring %u", format_white_space,
+ indent + 4, i);
+ s = format (s, "\n%U packets received: %u", format_white_space,
+ indent + 6, mq->n_packets);
+ }
}
return s;
}
@@ -111,12 +136,14 @@ memif_interface_tx_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
memif_region_index_t last_region = ~0;
void *last_region_shm = 0;
u16 head, tail;
+ u64 local_n_packets = 0;
ring = mq->ring;
ring_size = 1 << mq->log2_ring_size;
mask = ring_size - 1;
retry:
+ local_n_packets = 0;
if (type == MEMIF_RING_S2M)
{
@@ -226,8 +253,10 @@ retry:
buffers++;
n_left--;
+ local_n_packets++;
}
no_free_slots:
+ mq->n_packets += local_n_packets;
/* copy data */
n_copy_op = vec_len (ptd->copy_ops);
@@ -291,8 +320,10 @@ memif_interface_tx_zc_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
int n_retries = 5;
vlib_buffer_t *b0;
u16 head, tail;
+ u64 local_n_packets = 0;
retry:
+ local_n_packets = 0;
tail = __atomic_load_n (&ring->tail, __ATOMIC_ACQUIRE);
slot = head = ring->head;
@@ -358,8 +389,10 @@ retry:
/* next from */
buffers++;
n_left--;
+ local_n_packets++;
}
no_free_slots:
+ mq->n_packets += local_n_packets;
__atomic_store_n (&ring->head, slot, __ATOMIC_RELEASE);
@@ -419,6 +452,7 @@ memif_interface_tx_dma_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
memif_per_thread_data_t *ptd;
memif_main_t *mm = &memif_main;
u16 mif_id = mif - mm->interfaces;
+ u64 local_n_packets = 0;
ring = mq->ring;
ring_size = 1 << mq->log2_ring_size;
@@ -450,6 +484,7 @@ retry:
head = __atomic_load_n (&ring->head, __ATOMIC_ACQUIRE);
mq->last_tail += tail - mq->last_tail;
free_slots = head - mq->dma_tail;
+ local_n_packets = 0;
while (n_left && free_slots)
{
@@ -543,8 +578,10 @@ retry:
buffers++;
n_left--;
+ local_n_packets += 1;
}
no_free_slots:
+ mq->n_packets += local_n_packets;
/* copy data */
n_copy_op = vec_len (ptd->copy_ops);
@@ -676,8 +713,13 @@ VNET_DEVICE_CLASS_TX_FN (memif_device_class) (vlib_main_t * vm,
clib_spinlock_unlock (&mq->lockp);
if (n_left)
- vlib_error_count (vm, node->node_index, MEMIF_TX_ERROR_NO_FREE_SLOTS,
- n_left);
+ {
+ vlib_error_count (vm, node->node_index, MEMIF_TX_ERROR_NO_FREE_SLOTS,
+ n_left);
+ mq->no_free_tx += n_left;
+ if (n_left > mq->max_no_free_tx)
+ mq->max_no_free_tx = n_left;
+ }
if ((mq->ring->flags & MEMIF_RING_FLAG_MASK_INT) == 0 && mq->int_fd > -1)
{
@@ -721,7 +763,23 @@ memif_set_interface_next_node (vnet_main_t * vnm, u32 hw_if_index,
static void
memif_clear_hw_interface_counters (u32 instance)
{
- /* Nothing for now */
+ memif_main_t *mm = &memif_main;
+ memif_queue_t *mq;
+ uword i;
+
+ memif_if_t *mif = pool_elt_at_index (mm->interfaces, instance);
+ vec_foreach_index (i, mif->tx_queues)
+ {
+ mq = vec_elt_at_index (mif->tx_queues, i);
+ mq->n_packets = 0;
+ mq->no_free_tx = 0;
+ mq->max_no_free_tx = 0;
+ }
+ vec_foreach_index (i, mif->rx_queues)
+ {
+ mq = vec_elt_at_index (mif->rx_queues, i);
+ mq->n_packets = 0;
+ }
}
static clib_error_t *
diff --git a/src/plugins/memif/node.c b/src/plugins/memif/node.c
index 70933f4aa9d..d483f92b2fe 100644
--- a/src/plugins/memif/node.c
+++ b/src/plugins/memif/node.c
@@ -721,6 +721,7 @@ memif_device_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
vlib_increment_combined_counter (
vnm->interface_main.combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX,
thread_index, mif->sw_if_index, ptd->n_packets, ptd->n_rx_bytes);
+ mq->n_packets += ptd->n_packets;
/* refill ring with empty buffers */
refill:
@@ -981,6 +982,7 @@ memif_device_input_zc_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
+ VNET_INTERFACE_COUNTER_RX, thread_index,
mif->sw_if_index, n_rx_packets,
n_rx_bytes);
+ mq->n_packets += n_rx_packets;
/* refill ring with empty buffers */
refill:
@@ -1166,6 +1168,7 @@ CLIB_MARCH_FN (memif_dma_completion_cb, void, vlib_main_t *vm,
vlib_increment_combined_counter (
vnm->interface_main.combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX,
thread_index, mif->sw_if_index, ptd->n_packets, ptd->n_rx_bytes);
+ mq->n_packets += ptd->n_packets;
mq->dma_info_head++;
if (mq->dma_info_head == mq->dma_info_size)
diff --git a/src/plugins/memif/private.h b/src/plugins/memif/private.h
index f6335410ba8..43455d00522 100644
--- a/src/plugins/memif/private.h
+++ b/src/plugins/memif/private.h
@@ -150,6 +150,11 @@ typedef struct
u16 dma_info_size;
u8 dma_info_full;
+ /* packets received or sent */
+ u64 n_packets;
+ u64 no_free_tx;
+ u32 max_no_free_tx;
+
/* interrupts */
int int_fd;
uword int_clib_file_index;
diff --git a/src/plugins/nat/nat44-ed/nat44_ed_cli.c b/src/plugins/nat/nat44-ed/nat44_ed_cli.c
index 14313d05a35..fba128208d9 100644
--- a/src/plugins/nat/nat44-ed/nat44_ed_cli.c
+++ b/src/plugins/nat/nat44-ed/nat44_ed_cli.c
@@ -1842,6 +1842,28 @@ done:
return error;
}
+static clib_error_t *
+nat44_ed_clear_sessions_command_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ clib_error_t *error = 0;
+ nat44_ed_sessions_clear ();
+ return error;
+}
+
+/*?
+ * @cliexpar
+ * @cliexstart{clear nat44 ed sessions}
+ * To clear all NAT44 sessions
+ * vpp# clear nat44 ed sessions
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (nat44_ed_clear_sessions_command, static) = {
+ .path = "clear nat44 ed sessions",
+ .short_help = "clear nat44 ed sessions",
+ .function = nat44_ed_clear_sessions_command_fn,
+};
+
/*?
* @cliexpar
* @cliexstart{nat44}
diff --git a/src/plugins/osi/node.c b/src/plugins/osi/node.c
index a36b1525e0e..51a3dc6424d 100644
--- a/src/plugins/osi/node.c
+++ b/src/plugins/osi/node.c
@@ -40,9 +40,10 @@
#include <vlib/vlib.h>
#include <vnet/pg/pg.h>
#include <osi/osi.h>
-#include <vnet/ppp/ppp.h>
+#include <plugins/ppp/ppp.h>
#include <vnet/hdlc/hdlc.h>
#include <vnet/llc/llc.h>
+#include <vnet/plugin/plugin.h>
#define foreach_osi_input_next \
_ (PUNT, "error-punt") \
@@ -271,11 +272,24 @@ osi_setup_node (vlib_main_t *vm, u32 node_index)
pn->unformat_edit = unformat_pg_osi_header;
}
+typedef void (*ppp_register_input_protocol_fn) (vlib_main_t *vm,
+ ppp_protocol_t protocol,
+ u32 node_index);
+
static clib_error_t *
osi_input_init (vlib_main_t * vm)
{
clib_error_t *error = 0;
osi_main_t *lm = &osi_main;
+ ppp_register_input_protocol_fn ppp_register_input_protocol_fn_ptr;
+
+ ppp_register_input_protocol_fn_ptr =
+ vlib_get_plugin_symbol ("ppp_plugin.so", "ppp_register_input_protocol");
+ if (ppp_register_input_protocol_fn_ptr == 0)
+ {
+ error = clib_error_return (0, "ppp_plugin.so is not loaded");
+ return error;
+ }
if ((error = vlib_call_init_function (vm, osi_init)))
return error;
@@ -288,7 +302,8 @@ osi_input_init (vlib_main_t * vm)
lm->input_next_by_protocol[i] = OSI_INPUT_NEXT_DROP;
}
- ppp_register_input_protocol (vm, PPP_PROTOCOL_osi, osi_input_node.index);
+ ppp_register_input_protocol_fn_ptr (vm, PPP_PROTOCOL_osi,
+ osi_input_node.index);
hdlc_register_input_protocol (vm, HDLC_PROTOCOL_osi, osi_input_node.index);
llc_register_input_protocol (vm, LLC_PROTOCOL_osi_layer1,
osi_input_node.index);
diff --git a/src/plugins/ppp/CMakeLists.txt b/src/plugins/ppp/CMakeLists.txt
new file mode 100644
index 00000000000..771d4dcd732
--- /dev/null
+++ b/src/plugins/ppp/CMakeLists.txt
@@ -0,0 +1,25 @@
+# Copyright (c) 2024 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_vpp_plugin(ppp
+ SOURCES
+ node.c
+ pg.c
+ ppp.c
+ plugin.c
+
+ INSTALL_HEADERS
+ ppp.h
+ error.def
+ packet.h
+) \ No newline at end of file
diff --git a/src/plugins/ppp/error.def b/src/plugins/ppp/error.def
new file mode 100644
index 00000000000..ba645408582
--- /dev/null
+++ b/src/plugins/ppp/error.def
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * ppp_error.def: ppp errors
+ *
+ * Copyright (c) 2008 Eliot Dresselhaus
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+ppp_error (NONE, "no error")
+ppp_error (UNKNOWN_PROTOCOL, "unknown ppp protocol")
+ppp_error (UNKNOWN_ADDRESS_CONTROL, "address, control != 0xff03")
diff --git a/src/plugins/ppp/node.c b/src/plugins/ppp/node.c
new file mode 100644
index 00000000000..4c252e444c7
--- /dev/null
+++ b/src/plugins/ppp/node.c
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * ppp_node.c: ppp packet processing
+ *
+ * Copyright (c) 2010 Eliot Dresselhaus
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/pg/pg.h>
+#include <plugins/ppp/ppp.h>
+#include <vppinfra/sparse_vec.h>
+
+#define foreach_ppp_input_next \
+ _ (PUNT, "error-punt") \
+ _ (DROP, "error-drop")
+
+typedef enum
+{
+#define _(s,n) PPP_INPUT_NEXT_##s,
+ foreach_ppp_input_next
+#undef _
+ PPP_INPUT_N_NEXT,
+} ppp_input_next_t;
+
+typedef struct
+{
+ u8 packet_data[32];
+} ppp_input_trace_t;
+
+static u8 *
+format_ppp_input_trace (u8 * s, va_list * va)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
+ ppp_input_trace_t *t = va_arg (*va, ppp_input_trace_t *);
+
+ s = format (s, "%U", format_ppp_header, t->packet_data);
+
+ return s;
+}
+
+typedef struct
+{
+ /* Sparse vector mapping ppp protocol in network byte order
+ to next index. */
+ u16 *next_by_protocol;
+
+ u32 *sparse_index_by_next_index;
+} ppp_input_runtime_t;
+
+static uword
+ppp_input (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * from_frame)
+{
+ ppp_input_runtime_t *rt = (void *) node->runtime_data;
+ u32 n_left_from, next_index, i_next, *from, *to_next;
+
+ from = vlib_frame_vector_args (from_frame);
+ n_left_from = from_frame->n_vectors;
+
+ if (node->flags & VLIB_NODE_FLAG_TRACE)
+ vlib_trace_frame_buffers_only (vm, node,
+ from,
+ n_left_from,
+ sizeof (from[0]),
+ sizeof (ppp_input_trace_t));
+
+ next_index = node->cached_next_index;
+ i_next = vec_elt (rt->sparse_index_by_next_index, next_index);
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from >= 4 && n_left_to_next >= 2)
+ {
+ u32 bi0, bi1;
+ vlib_buffer_t *b0, *b1;
+ ppp_header_t *h0, *h1;
+ u32 i0, i1, protocol0, protocol1, enqueue_code;
+
+ /* Prefetch next iteration. */
+ {
+ vlib_buffer_t *p2, *p3;
+
+ p2 = vlib_get_buffer (vm, from[2]);
+ p3 = vlib_get_buffer (vm, from[3]);
+
+ vlib_prefetch_buffer_header (p2, LOAD);
+ vlib_prefetch_buffer_header (p3, LOAD);
+
+ CLIB_PREFETCH (p2->data, sizeof (h0[0]), LOAD);
+ CLIB_PREFETCH (p3->data, sizeof (h1[0]), LOAD);
+ }
+
+ bi0 = from[0];
+ bi1 = from[1];
+ to_next[0] = bi0;
+ to_next[1] = bi1;
+ from += 2;
+ to_next += 2;
+ n_left_to_next -= 2;
+ n_left_from -= 2;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ b1 = vlib_get_buffer (vm, bi1);
+
+ h0 = vlib_buffer_get_current (b0);
+ h1 = vlib_buffer_get_current (b1);
+
+ vlib_buffer_advance (b0, sizeof (ppp_header_t));
+ vlib_buffer_advance (b1, sizeof (ppp_header_t));
+
+ /* Index sparse array with network byte order. */
+ protocol0 = h0->protocol;
+ protocol1 = h1->protocol;
+ sparse_vec_index2 (rt->next_by_protocol, protocol0, protocol1, &i0,
+ &i1);
+
+ b0->error =
+ node->errors[i0 ==
+ SPARSE_VEC_INVALID_INDEX ? PPP_ERROR_UNKNOWN_PROTOCOL
+ : PPP_ERROR_NONE];
+ b1->error =
+ node->errors[i1 ==
+ SPARSE_VEC_INVALID_INDEX ? PPP_ERROR_UNKNOWN_PROTOCOL
+ : PPP_ERROR_NONE];
+
+ enqueue_code = (i0 != i_next) + 2 * (i1 != i_next);
+
+ if (PREDICT_FALSE (enqueue_code != 0))
+ {
+ switch (enqueue_code)
+ {
+ case 1:
+ /* A B A */
+ to_next[-2] = bi1;
+ to_next -= 1;
+ n_left_to_next += 1;
+ vlib_set_next_frame_buffer (vm, node,
+ vec_elt (rt->next_by_protocol,
+ i0), bi0);
+ break;
+
+ case 2:
+ /* A A B */
+ to_next -= 1;
+ n_left_to_next += 1;
+ vlib_set_next_frame_buffer (vm, node,
+ vec_elt (rt->next_by_protocol,
+ i1), bi1);
+ break;
+
+ case 3:
+ /* A B B or A B C */
+ to_next -= 2;
+ n_left_to_next += 2;
+ vlib_set_next_frame_buffer (vm, node,
+ vec_elt (rt->next_by_protocol,
+ i0), bi0);
+ vlib_set_next_frame_buffer (vm, node,
+ vec_elt (rt->next_by_protocol,
+ i1), bi1);
+ if (i0 == i1)
+ {
+ vlib_put_next_frame (vm, node, next_index,
+ n_left_to_next);
+ i_next = i1;
+ next_index = vec_elt (rt->next_by_protocol, i_next);
+ vlib_get_next_frame (vm, node, next_index, to_next,
+ n_left_to_next);
+ }
+ }
+ }
+ }
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ u32 bi0;
+ vlib_buffer_t *b0;
+ ppp_header_t *h0;
+ u32 i0, protocol0;
+
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+
+ h0 = vlib_buffer_get_current (b0);
+
+ vlib_buffer_advance (b0, sizeof (ppp_header_t));
+
+ protocol0 = h0->protocol;
+ i0 = sparse_vec_index (rt->next_by_protocol, protocol0);
+
+ b0->error =
+ node->errors[i0 ==
+ SPARSE_VEC_INVALID_INDEX ? PPP_ERROR_UNKNOWN_PROTOCOL
+ : PPP_ERROR_NONE];
+
+ /* Sent packet to wrong next? */
+ if (PREDICT_FALSE (i0 != i_next))
+ {
+ /* Return old frame; remove incorrectly enqueued packet. */
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next + 1);
+
+ /* Send to correct next. */
+ i_next = i0;
+ next_index = vec_elt (rt->next_by_protocol, i_next);
+ vlib_get_next_frame (vm, node, next_index,
+ to_next, n_left_to_next);
+ to_next[0] = bi0;
+ to_next += 1;
+ n_left_to_next -= 1;
+ }
+ }
+
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ return from_frame->n_vectors;
+}
+
+static char *ppp_error_strings[] = {
+#define ppp_error(n,s) s,
+#include "error.def"
+#undef ppp_error
+};
+
+VLIB_REGISTER_NODE (ppp_input_node) = {
+ .function = ppp_input,
+ .name = "ppp-input",
+ /* Takes a vector of packets. */
+ .vector_size = sizeof (u32),
+
+ .runtime_data_bytes = sizeof (ppp_input_runtime_t),
+
+ .n_errors = PPP_N_ERROR,
+ .error_strings = ppp_error_strings,
+
+ .n_next_nodes = PPP_INPUT_N_NEXT,
+ .next_nodes = {
+#define _(s,n) [PPP_INPUT_NEXT_##s] = n,
+ foreach_ppp_input_next
+#undef _
+ },
+
+ .format_buffer = format_ppp_header_with_length,
+ .format_trace = format_ppp_input_trace,
+ .unformat_buffer = unformat_ppp_header,
+};
+
+static clib_error_t *
+ppp_input_runtime_init (vlib_main_t * vm)
+{
+ ppp_input_runtime_t *rt;
+
+ rt = vlib_node_get_runtime_data (vm, ppp_input_node.index);
+
+ rt->next_by_protocol = sparse_vec_new
+ ( /* elt bytes */ sizeof (rt->next_by_protocol[0]),
+ /* bits in index */ BITS (((ppp_header_t *) 0)->protocol));
+
+ vec_validate (rt->sparse_index_by_next_index, PPP_INPUT_NEXT_DROP);
+ vec_validate (rt->sparse_index_by_next_index, PPP_INPUT_NEXT_PUNT);
+ rt->sparse_index_by_next_index[PPP_INPUT_NEXT_DROP]
+ = SPARSE_VEC_INVALID_INDEX;
+ rt->sparse_index_by_next_index[PPP_INPUT_NEXT_PUNT]
+ = SPARSE_VEC_INVALID_INDEX;
+
+ return 0;
+}
+
+static void
+ppp_setup_node (vlib_main_t *vm, u32 node_index)
+{
+ vlib_node_t *n = vlib_get_node (vm, node_index);
+ pg_node_t *pn = pg_get_node (node_index);
+
+ n->format_buffer = format_ppp_header_with_length;
+ n->unformat_buffer = unformat_ppp_header;
+ pn->unformat_edit = unformat_pg_ppp_header;
+}
+
+static clib_error_t *
+ppp_input_init (vlib_main_t * vm)
+{
+ {
+ clib_error_t *error = vlib_call_init_function (vm, ppp_init);
+ if (error)
+ clib_error_report (error);
+ }
+
+ ppp_setup_node (vm, ppp_input_node.index);
+ ppp_input_runtime_init (vm);
+
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (ppp_input_init);
+VLIB_WORKER_INIT_FUNCTION (ppp_input_runtime_init);
+
+__clib_export void
+ppp_register_input_protocol (vlib_main_t *vm, ppp_protocol_t protocol,
+ u32 node_index)
+{
+ ppp_main_t *em = &ppp_main;
+ ppp_protocol_info_t *pi;
+ ppp_input_runtime_t *rt;
+ u16 *n;
+ u32 i;
+
+ {
+ clib_error_t *error = vlib_call_init_function (vm, ppp_input_init);
+ if (error)
+ clib_error_report (error);
+ }
+
+ pi = ppp_get_protocol_info (em, protocol);
+ pi->node_index = node_index;
+ pi->next_index = vlib_node_add_next (vm, ppp_input_node.index, node_index);
+
+ /* Setup ppp protocol -> next index sparse vector mapping. */
+ rt = vlib_node_get_runtime_data (vm, ppp_input_node.index);
+ n =
+ sparse_vec_validate (rt->next_by_protocol,
+ clib_host_to_net_u16 (protocol));
+ n[0] = pi->next_index;
+
+ /* Rebuild next index -> sparse index inverse mapping when sparse vector
+ is updated. */
+ vec_validate (rt->sparse_index_by_next_index, pi->next_index);
+ for (i = 1; i < vec_len (rt->next_by_protocol); i++)
+ rt->sparse_index_by_next_index[rt->next_by_protocol[i]] = i;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ppp/packet.h b/src/plugins/ppp/packet.h
new file mode 100644
index 00000000000..cab9743de92
--- /dev/null
+++ b/src/plugins/ppp/packet.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef included_vnet_ppp_packet_h
+#define included_vnet_ppp_packet_h
+
+/*
+ * PPP packet format
+ *
+ * Copyright (c) 2009 Eliot Dresselhaus
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+See http://www.iana.org/assignments/ppp-numbers.
+
+The Point-to-Point Protocol (PPP) Data Link Layer [146,147,175]
+contains a 16 bit Protocol field to identify the the encapsulated
+protocol. The Protocol field is consistent with the ISO 3309 (HDLC)
+extension mechanism for Address fields. All Protocols MUST be
+assigned such that the least significant bit of the most significant
+octet equals "0", and the least significant bit of the least
+significant octet equals "1".
+*/
+
+#define foreach_ppp_protocol \
+_ (0x0001, padding) \
+_ (0x0003, rohc_small_cid) \
+_ (0x0005, rohc_large_cid) \
+_ (0x0021, ip4) \
+_ (0x0023, osi) \
+_ (0x0025, xerox_ns_idp) \
+_ (0x0027, decnet) \
+_ (0x0029, appletalk) \
+_ (0x002b, ipx) \
+_ (0x002d, vj_compressed_tcp) \
+_ (0x002f, vj_uncompressed_tcp) \
+_ (0x0031, bpdu) \
+_ (0x0033, streams) \
+_ (0x0035, vines) \
+_ (0x0039, appletalk_eddp) \
+_ (0x003b, appletalk_smart_buffered) \
+_ (0x003d, multilink) \
+_ (0x003f, netbios_framing) \
+_ (0x0041, cisco) \
+_ (0x0043, timeplex) \
+_ (0x0045, fujitsu_lblb) \
+_ (0x0047, dca_remote_lan) \
+_ (0x0049, sdtp) \
+_ (0x004b, sna_over_802_2) \
+_ (0x004d, sna) \
+_ (0x004f, ip6_header_compression) \
+_ (0x0051, knx) \
+_ (0x0053, encryption) \
+_ (0x0055, link_encryption) \
+_ (0x0057, ip6) \
+_ (0x0059, ppp_mux) \
+_ (0x005b, vendor_specific_a) \
+_ (0x0061, rtp_iphc_full_header) \
+_ (0x0063, rtp_iphc_compressed_tcp) \
+_ (0x0065, rtp_iphc_compressed_non_tcp) \
+_ (0x0067, rtp_iphc_compressed_udp_8) \
+_ (0x0069, rtp_iphc_compressed_rtp_8) \
+_ (0x006f, stampede) \
+_ (0x0073, mp_plus) \
+_ (0x007d, control) \
+_ (0x00c1, ntcits_ipi) \
+_ (0x00cf, ppp_nlpid) \
+_ (0x00fb, multilink_compression) \
+_ (0x00fd, compressed_datagram) \
+_ (0x0201, 802_1d_hello) \
+_ (0x0203, ibm_source_routing) \
+_ (0x0205, dec_lanbridge) \
+_ (0x0207, cdp) \
+_ (0x0209, netcs) \
+_ (0x020b, stp) \
+_ (0x020d, edp) \
+_ (0x0211, oscp_a) \
+_ (0x0213, oscp_b) \
+_ (0x0231, luxcom) \
+_ (0x0233, sigma) \
+_ (0x0235, apple_client_server) \
+_ (0x0281, mpls_unicast) \
+_ (0x0283, mpls_multicast) \
+_ (0x0285, ieee_p1284_4) \
+_ (0x0287, tetra) \
+_ (0x0289, multichannel_flow_treatment) \
+_ (0x2063, rtp_iphc_compressed_tcp_no_delta) \
+_ (0x2065, rtp_iphc_context_state) \
+_ (0x2067, rtp_iphc_compressed_udp_16) \
+_ (0x2069, rtp_iphc_compressed_rtp_16) \
+_ (0x4001, cray) \
+_ (0x4003, cdpd) \
+_ (0x4005, expand) \
+_ (0x4007, odsicp) \
+_ (0x4009, docsis_dll) \
+_ (0x400B, cetacean) \
+_ (0x4021, lzs) \
+_ (0x4023, reftek) \
+_ (0x4025, fibre_channel) \
+_ (0x4027, emit) \
+_ (0x405b, vendor_specific_b) \
+_ (0xc021, lcp) \
+_ (0xc023, pap) \
+_ (0xc025, link_quality_report) \
+_ (0xc027, shiva_password) \
+_ (0xc029, cbcp) \
+_ (0xc02b, bacp) \
+_ (0xc02d, bap) \
+_ (0xc05b, vendor_specific_password) \
+_ (0xc081, container_control) \
+_ (0xc223, chap) \
+_ (0xc225, rsa) \
+_ (0xc227, extensible_authentication) \
+_ (0xc229, mitsubishi_security_info) \
+_ (0xc26f, stampede_authorization) \
+_ (0xc281, proprietary_authentication_a) \
+_ (0xc283, proprietary_authentication_b) \
+_ (0xc481, proprietary_node_id_authentication)
+
+typedef enum
+{
+#define _(n,f) PPP_PROTOCOL_##f = n,
+ foreach_ppp_protocol
+#undef _
+} ppp_protocol_t;
+
+/* PPP Link Control Protocol (LCP) and Internet Protocol Control Protocol (IPCP) Codes
+
+The Point-to-Point Protocol (PPP) Link Control Protocol (LCP),
+the Compression Control Protocol (CCP), Internet Protocol Control
+Protocol (IPCP), and other control protocols, contain an 8 bit
+Code field which identifies the type of packet. */
+
+#define foreach_ppp_lcp_code \
+_ (0, vendor_specific) \
+_ (1, configure_request) \
+_ (2, configure_ack) \
+_ (3, configure_nak) \
+_ (4, configure_reject) \
+_ (5, terminate_request) \
+_ (6, terminate_ack) \
+_ (7, code_reject) \
+_ (8, protocol_reject) \
+_ (9, echo_request) \
+_ (10, echo_reply) \
+_ (11, discard_request) \
+_ (12, identification) \
+_ (13, time_remaining) \
+_ (14, reset_request) \
+_ (15, reset_reply)
+
+typedef struct
+{
+ /* Set to 0xff 0x03 */
+ u8 address, control;
+
+ /* Layer 3 protocol for this packet. */
+ u16 protocol;
+} ppp_header_t;
+
+#endif /* included_vnet_ppp_packet_h */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ppp/pg.c b/src/plugins/ppp/pg.c
new file mode 100644
index 00000000000..39f6c25badc
--- /dev/null
+++ b/src/plugins/ppp/pg.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * ppp_pg.c: packet generator ppp interface
+ *
+ * Copyright (c) 2008 Eliot Dresselhaus
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/pg/pg.h>
+#include <plugins/ppp/ppp.h>
+
+typedef struct
+{
+ pg_edit_t address;
+ pg_edit_t control;
+ pg_edit_t protocol;
+} pg_ppp_header_t;
+
+static inline void
+pg_ppp_header_init (pg_ppp_header_t * e)
+{
+ pg_edit_init (&e->address, ppp_header_t, address);
+ pg_edit_init (&e->control, ppp_header_t, control);
+ pg_edit_init (&e->protocol, ppp_header_t, protocol);
+}
+
+uword
+unformat_pg_ppp_header (unformat_input_t * input, va_list * args)
+{
+ pg_stream_t *s = va_arg (*args, pg_stream_t *);
+ pg_ppp_header_t *h;
+ u32 group_index, error;
+
+ h = pg_create_edit_group (s, sizeof (h[0]), sizeof (ppp_header_t),
+ &group_index);
+ pg_ppp_header_init (h);
+
+ pg_edit_set_fixed (&h->address, 0xff);
+ pg_edit_set_fixed (&h->control, 0x03);
+
+ error = 1;
+ if (!unformat (input, "%U",
+ unformat_pg_edit,
+ unformat_ppp_protocol_net_byte_order, &h->protocol))
+ goto done;
+
+ {
+ ppp_main_t *pm = &ppp_main;
+ ppp_protocol_info_t *pi = 0;
+ pg_node_t *pg_node = 0;
+
+ if (h->protocol.type == PG_EDIT_FIXED)
+ {
+ u16 t = *(u16 *) h->protocol.values[PG_EDIT_LO];
+ pi = ppp_get_protocol_info (pm, clib_net_to_host_u16 (t));
+ if (pi && pi->node_index != ~0)
+ pg_node = pg_get_node (pi->node_index);
+ }
+
+ if (pg_node && pg_node->unformat_edit
+ && unformat_user (input, pg_node->unformat_edit, s))
+ ;
+
+ else if (!unformat_user (input, unformat_pg_payload, s))
+ goto done;
+ }
+
+ error = 0;
+done:
+ if (error)
+ pg_free_edit_group (s);
+ return error == 0;
+}
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ppp/plugin.c b/src/plugins/ppp/plugin.c
new file mode 100644
index 00000000000..fac25b3e5c7
--- /dev/null
+++ b/src/plugins/ppp/plugin.c
@@ -0,0 +1,26 @@
+/*
+ * plugin.c: ppp
+ *
+ * Copyright (c) 2024 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h>
+
+// register a plugin
+VLIB_PLUGIN_REGISTER () = {
+ .version = VPP_BUILD_VER,
+ .description = "Point-to-Point Protocol (PPP) plugin",
+};
diff --git a/src/plugins/ppp/ppp.c b/src/plugins/ppp/ppp.c
new file mode 100644
index 00000000000..8b394dc4b72
--- /dev/null
+++ b/src/plugins/ppp/ppp.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2024 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * ppp.c: ppp support
+ *
+ * Copyright (c) 2010 Eliot Dresselhaus
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <vnet/vnet.h>
+#include <plugins/ppp/ppp.h>
+
+/* Global main structure. */
+ppp_main_t ppp_main;
+
+u8 *
+format_ppp_protocol (u8 * s, va_list * args)
+{
+ ppp_protocol_t p = va_arg (*args, u32);
+ ppp_main_t *pm = &ppp_main;
+ ppp_protocol_info_t *pi = ppp_get_protocol_info (pm, p);
+
+ if (pi)
+ s = format (s, "%s", pi->name);
+ else
+ s = format (s, "0x%04x", p);
+
+ return s;
+}
+
+u8 *
+format_ppp_header_with_length (u8 * s, va_list * args)
+{
+ ppp_main_t *pm = &ppp_main;
+ ppp_header_t *h = va_arg (*args, ppp_header_t *);
+ u32 max_header_bytes = va_arg (*args, u32);
+ ppp_protocol_t p = clib_net_to_host_u16 (h->protocol);
+ u32 indent, header_bytes;
+
+ header_bytes = sizeof (h[0]);
+ if (max_header_bytes != 0 && header_bytes > max_header_bytes)
+ return format (s, "ppp header truncated");
+
+ indent = format_get_indent (s);
+
+ s = format (s, "PPP %U", format_ppp_protocol, p);
+
+ if (h->address != 0xff)
+ s = format (s, ", address 0x%02x", h->address);
+ if (h->control != 0x03)
+ s = format (s, ", control 0x%02x", h->control);
+
+ if (max_header_bytes != 0 && header_bytes > max_header_bytes)
+ {
+ ppp_protocol_info_t *pi = ppp_get_protocol_info (pm, p);
+ vlib_node_t *node = vlib_get_node (pm->vlib_main, pi->node_index);
+ if (node->format_buffer)
+ s = format (s, "\n%U%U",
+ format_white_space, indent,
+ node->format_buffer, (void *) (h + 1),
+ max_header_bytes - header_bytes);
+ }
+
+ return s;
+}
+
+u8 *
+format_ppp_header (u8 * s, va_list * args)
+{
+ ppp_header_t *h = va_arg (*args, ppp_header_t *);
+ return format (s, "%U", format_ppp_header_with_length, h, 0);
+}
+
+/* Returns ppp protocol as an int in host byte order. */
+uword
+unformat_ppp_protocol_host_byte_order (unformat_input_t * input,
+ va_list * args)
+{
+ u16 *result = va_arg (*args, u16 *);
+ ppp_main_t *pm = &ppp_main;
+ int p, i;
+
+ /* Numeric type. */
+ if (unformat (input, "0x%x", &p) || unformat (input, "%d", &p))
+ {
+ if (p >= (1 << 16))
+ return 0;
+ *result = p;
+ return 1;
+ }
+
+ /* Named type. */
+ if (unformat_user (input, unformat_vlib_number_by_name,
+ pm->protocol_info_by_name, &i))
+ {
+ ppp_protocol_info_t *pi = vec_elt_at_index (pm->protocol_infos, i);
+ *result = pi->protocol;
+ return 1;
+ }
+
+ return 0;
+}
+
+uword
+unformat_ppp_protocol_net_byte_order (unformat_input_t * input,
+ va_list * args)
+{
+ u16 *result = va_arg (*args, u16 *);
+ if (!unformat_user (input, unformat_ppp_protocol_host_byte_order, result))
+ return 0;
+ *result = clib_host_to_net_u16 ((u16) * result);
+ return 1;
+}
+
+uword
+unformat_ppp_header (unformat_input_t * input, va_list * args)
+{
+ u8 **result = va_arg (*args, u8 **);
+ ppp_header_t _h, *h = &_h;
+ u16 p;
+
+ if (!unformat (input, "%U", unformat_ppp_protocol_host_byte_order, &p))
+ return 0;
+
+ h->address = 0xff;
+ h->control = 0x03;
+ h->protocol = clib_host_to_net_u16 (p);
+
+ /* Add header to result. */
+ {
+ void *p;
+ u32 n_bytes = sizeof (h[0]);
+
+ vec_add2 (*result, p, n_bytes);
+ clib_memcpy (p, h, n_bytes);
+ }
+
+ return 1;
+}
+
+static u8 *
+ppp_build_rewrite (vnet_main_t * vnm,
+ u32 sw_if_index,
+ vnet_link_t link_type, const void *dst_hw_address)
+{
+ ppp_header_t *h;
+ u8 *rewrite = NULL;
+ ppp_protocol_t protocol;
+
+ switch (link_type)
+ {
+#define _(a,b) case VNET_LINK_##a: protocol = PPP_PROTOCOL_##b; break
+ _(IP4, ip4);
+ _(IP6, ip6);
+ _(MPLS, mpls_unicast);
+#undef _
+ default:
+ return (NULL);
+ }
+
+ vec_validate (rewrite, sizeof (*h) - 1);
+ h = (ppp_header_t *) rewrite;
+ h->address = 0xff;
+ h->control = 0x03;
+ h->protocol = clib_host_to_net_u16 (protocol);
+
+ return (rewrite);
+}
+
+VNET_HW_INTERFACE_CLASS (ppp_hw_interface_class) = {
+ .name = "PPP",
+ .format_header = format_ppp_header_with_length,
+ .unformat_header = unformat_ppp_header,
+ .build_rewrite = ppp_build_rewrite,
+ .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
+};
+
+static void
+add_protocol (ppp_main_t * pm, ppp_protocol_t protocol, char *protocol_name)
+{
+ ppp_protocol_info_t *pi;
+ u32 i;
+
+ vec_add2 (pm->protocol_infos, pi, 1);
+ i = pi - pm->protocol_infos;
+
+ pi->name = protocol_name;
+ pi->protocol = protocol;
+ pi->next_index = pi->node_index = ~0;
+
+ hash_set (pm->protocol_info_by_protocol, protocol, i);
+ hash_set_mem (pm->protocol_info_by_name, pi->name, i);
+}
+
+static clib_error_t *
+ppp_init (vlib_main_t * vm)
+{
+ ppp_main_t *pm = &ppp_main;
+ clib_error_t *error;
+ vlib_node_t *ip4_input_node, *ip6_input_node;
+
+ clib_memset (pm, 0, sizeof (pm[0]));
+ pm->vlib_main = vm;
+
+ if ((error = vlib_call_init_function (vm, ip_main_init)))
+ return error;
+
+ if ((error = vlib_call_init_function (vm, ip4_init)))
+ return error;
+
+ if ((error = vlib_call_init_function (vm, ip6_init)))
+ return error;
+
+ pm->protocol_info_by_name = hash_create_string (0, sizeof (uword));
+ pm->protocol_info_by_protocol = hash_create (0, sizeof (uword));
+
+#define _(n,s) add_protocol (pm, PPP_PROTOCOL_##s, #s);
+ foreach_ppp_protocol;
+#undef _
+
+ ip4_input_node = vlib_get_node_by_name (vm, (u8 *) "ip4-input");
+ ASSERT (ip4_input_node);
+ ip6_input_node = vlib_get_node_by_name (vm, (u8 *) "ip6-input");
+ ASSERT (ip6_input_node);
+
+ ppp_register_input_protocol (vm, PPP_PROTOCOL_ip4, ip4_input_node->index);
+ ppp_register_input_protocol (vm, PPP_PROTOCOL_ip6, ip6_input_node->index);
+
+ return vlib_call_init_function (vm, ppp_input_init);
+}
+
+VLIB_INIT_FUNCTION (ppp_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ppp/ppp.h b/src/plugins/ppp/ppp.h
new file mode 100644
index 00000000000..b94f096476a
--- /dev/null
+++ b/src/plugins/ppp/ppp.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2024 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * ppp.h: types/functions for ppp.
+ *
+ * Copyright (c) 2008 Eliot Dresselhaus
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef included_ppp_h
+#define included_ppp_h
+
+#include <vnet/vnet.h>
+#include <ppp/packet.h>
+
+extern vnet_hw_interface_class_t ppp_hw_interface_class;
+
+typedef enum
+{
+#define ppp_error(n,s) PPP_ERROR_##n,
+#include <ppp/error.def>
+#undef ppp_error
+ PPP_N_ERROR,
+} ppp_error_t;
+
+typedef struct
+{
+ /* Name (a c string). */
+ char *name;
+
+ /* PPP protocol type in host byte order. */
+ ppp_protocol_t protocol;
+
+ /* Node which handles this type. */
+ u32 node_index;
+
+ /* Next index for this type. */
+ u32 next_index;
+} ppp_protocol_info_t;
+
+typedef struct
+{
+ vlib_main_t *vlib_main;
+
+ ppp_protocol_info_t *protocol_infos;
+
+ /* Hash tables mapping name/protocol to protocol info index. */
+ uword *protocol_info_by_name, *protocol_info_by_protocol;
+} ppp_main_t;
+
+always_inline ppp_protocol_info_t *
+ppp_get_protocol_info (ppp_main_t * em, ppp_protocol_t protocol)
+{
+ uword *p = hash_get (em->protocol_info_by_protocol, protocol);
+ return p ? vec_elt_at_index (em->protocol_infos, p[0]) : 0;
+}
+
+extern ppp_main_t ppp_main;
+
+/* Register given node index to take input for given ppp type. */
+void
+ppp_register_input_type (vlib_main_t * vm,
+ ppp_protocol_t protocol, u32 node_index);
+
+format_function_t format_ppp_protocol;
+format_function_t format_ppp_header;
+format_function_t format_ppp_header_with_length;
+
+/* Parse ppp protocol as 0xXXXX or protocol name.
+ In either host or network byte order. */
+unformat_function_t unformat_ppp_protocol_host_byte_order;
+unformat_function_t unformat_ppp_protocol_net_byte_order;
+
+/* Parse ppp header. */
+unformat_function_t unformat_ppp_header;
+unformat_function_t unformat_pg_ppp_header;
+
+__clib_export void ppp_register_input_protocol (vlib_main_t *vm,
+ ppp_protocol_t protocol,
+ u32 node_index);
+
+#endif /* included_ppp_h */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/pppoe/pppoe.c b/src/plugins/pppoe/pppoe.c
index 0d5f9c1aeac..497fbc09ae0 100644
--- a/src/plugins/pppoe/pppoe.c
+++ b/src/plugins/pppoe/pppoe.c
@@ -27,7 +27,7 @@
#include <vnet/dpo/interface_tx_dpo.h>
#include <vnet/plugin/plugin.h>
#include <vpp/app/version.h>
-#include <vnet/ppp/packet.h>
+#include <ppp/packet.h>
#include <pppoe/pppoe.h>
#include <vnet/adj/adj_midchain.h>
#include <vnet/adj/adj_mcast.h>
diff --git a/src/plugins/pppoe/pppoe_cp_node.c b/src/plugins/pppoe/pppoe_cp_node.c
index 1a44b5d3853..c96559679f0 100644
--- a/src/plugins/pppoe/pppoe_cp_node.c
+++ b/src/plugins/pppoe/pppoe_cp_node.c
@@ -16,7 +16,7 @@
*/
#include <vlib/vlib.h>
-#include <vnet/ppp/packet.h>
+#include <ppp/packet.h>
#include <pppoe/pppoe.h>
#define foreach_pppoe_cp_next \
diff --git a/src/plugins/pppoe/pppoe_decap.c b/src/plugins/pppoe/pppoe_decap.c
index 7c456a7a9cc..854364b1aca 100644
--- a/src/plugins/pppoe/pppoe_decap.c
+++ b/src/plugins/pppoe/pppoe_decap.c
@@ -16,7 +16,7 @@
*/
#include <vlib/vlib.h>
-#include <vnet/ppp/packet.h>
+#include <ppp/packet.h>
#include <pppoe/pppoe.h>
typedef struct {
diff --git a/src/plugins/srtp/srtp_plugin.rst b/src/plugins/srtp/srtp_plugin.rst
index 568ebb66f01..bbc44735631 100644
--- a/src/plugins/srtp/srtp_plugin.rst
+++ b/src/plugins/srtp/srtp_plugin.rst
@@ -48,9 +48,9 @@ Custom libsrtp2 build
::
- srtp_version := 2.3.0
- srtp_tarball := srtp_$(srtp_version).tar.gz
- srtp_tarball_md5sum := da38ee5d9c31be212a12964c22d7f795
+ srtp_version := 2.6.0
+ srtp_tarball := libsrtp_$(srtp_version).tar.gz
+ srtp_tarball_sha256sum := bf641aa654861be10570bfc137d1441283822418e9757dc71ebb69a6cf84ea6b
srtp_tarball_strip_dirs := 1
srtp_url := https://github.com/cisco/libsrtp/archive/v$(srtp_version).tar.gz
diff --git a/src/plugins/tlsopenssl/tls_async.c b/src/plugins/tlsopenssl/tls_async.c
index d85af686d21..c6d2b2fe9e1 100644
--- a/src/plugins/tlsopenssl/tls_async.c
+++ b/src/plugins/tlsopenssl/tls_async.c
@@ -17,12 +17,36 @@
#include <vlib/node_funcs.h>
#include <openssl/engine.h>
#include <tlsopenssl/tls_openssl.h>
+#include <dlfcn.h>
-#define SSL_ASYNC_INFLIGHT 1
-#define SSL_ASYNC_READY 2
-#define SSL_ASYNC_REENTER 3
#define MAX_VECTOR_ASYNC 256
+#define SSL_WANT_NAMES \
+ { \
+ [0] = "N/A", [SSL_NOTHING] = "SSL_NOTHING", \
+ [SSL_WRITING] = "SSL_WRITING", [SSL_READING] = "SSL_READING", \
+ [SSL_X509_LOOKUP] = "SSL_X509_LOOKUP", \
+ [SSL_ASYNC_PAUSED] = "SSL_ASYNC_PAUSED", \
+ [SSL_ASYNC_NO_JOBS] = "SSL_ASYNC_NO_JOBS", \
+ [SSL_CLIENT_HELLO_CB] = "SSL_CLIENT_HELLO_CB", \
+ }
+
+static const char *ssl_want[] = SSL_WANT_NAMES;
+
+#define foreach_ssl_evt_status_type_ \
+ _ (INVALID_STATUS, "Async event invalid status") \
+ _ (INFLIGHT, "Async event inflight") \
+ _ (READY, "Async event ready") \
+ _ (REENTER, "Async event reenter") \
+ _ (MAX_STATUS, "Async event max status")
+
+typedef enum ssl_evt_status_type_
+{
+#define _(sym, str) SSL_ASYNC_##sym,
+ foreach_ssl_evt_status_type_
+#undef _
+} ssl_evt_status_type_t;
+
typedef struct openssl_tls_callback_arg_
{
int thread_index;
@@ -33,7 +57,8 @@ typedef struct openssl_event_
{
u32 ctx_index;
int session_index;
- u8 status;
+ ssl_evt_status_type_t status;
+ ssl_async_evt_type_t type;
openssl_resume_handler *handler;
openssl_tls_callback_arg_t cb_args;
@@ -46,12 +71,15 @@ typedef struct openssl_async_queue_
{
int evt_run_head;
int evt_run_tail;
+ int depth;
+ int max_depth;
} openssl_async_queue_t;
typedef struct openssl_async_
{
openssl_evt_t ***evt_pool;
openssl_async_queue_t *queue;
+ openssl_async_queue_t *queue_in_init;
void (*polling) (void);
u8 start_polling;
ENGINE *engine;
@@ -74,8 +102,8 @@ struct engine_polling
void qat_init_thread (void *arg);
struct engine_polling engine_list[] = {
- {"qat", qat_polling, qat_pre_init, qat_init_thread},
- {"dasync", dasync_polling, NULL, NULL}
+ { "qat", qat_polling, qat_pre_init, qat_init_thread },
+ { "dasync", dasync_polling, NULL, NULL }
};
openssl_async_t openssl_async_main;
@@ -98,6 +126,7 @@ evt_pool_init (vlib_main_t * vm)
vec_validate (om->evt_pool, num_threads - 1);
vec_validate (om->queue, num_threads - 1);
+ vec_validate (om->queue_in_init, num_threads - 1);
om->start_polling = 0;
om->engine = 0;
@@ -106,6 +135,13 @@ evt_pool_init (vlib_main_t * vm)
{
om->queue[i].evt_run_head = -1;
om->queue[i].evt_run_tail = -1;
+ om->queue[i].depth = 0;
+ om->queue[i].max_depth = 0;
+
+ om->queue_in_init[i].evt_run_head = -1;
+ om->queue_in_init[i].evt_run_tail = -1;
+ om->queue_in_init[i].depth = 0;
+ om->queue_in_init[i].max_depth = 0;
}
om->polling = NULL;
@@ -243,16 +279,23 @@ int
tls_async_openssl_callback (SSL * s, void *cb_arg)
{
openssl_evt_t *event, *event_tail;
+ openssl_async_queue_t *queue;
openssl_async_t *om = &openssl_async_main;
openssl_tls_callback_arg_t *args = (openssl_tls_callback_arg_t *) cb_arg;
int thread_index = args->thread_index;
int event_index = args->event_index;
- int *evt_run_tail = &om->queue[thread_index].evt_run_tail;
- int *evt_run_head = &om->queue[thread_index].evt_run_head;
TLS_DBG (2, "Set event %d to run\n", event_index);
event = openssl_evt_get_w_thread (event_index, thread_index);
+ if (event->type == SSL_ASYNC_EVT_INIT)
+ queue = om->queue_in_init;
+ else
+ queue = om->queue;
+
+ int *evt_run_tail = &queue[thread_index].evt_run_tail;
+ int *evt_run_head = &queue[thread_index].evt_run_head;
+
/* Happend when a recursive case, especially in SW simulation */
if (PREDICT_FALSE (event->status == SSL_ASYNC_READY))
{
@@ -276,10 +319,498 @@ tls_async_openssl_callback (SSL * s, void *cb_arg)
return 1;
}
+/*
+ * Continue an async SSL_write() call.
+ * This function is _only_ called when continuing an SSL_write() call
+ * that returned WANT_ASYNC.
+ * Since it continues the handling of an existing, paused SSL job
+ * (ASYNC_JOB*), the 'buf' and 'num' params to SSL_write() have
+ * already been set in the initial call, and are meaningless here.
+ * Therefore setting buf=null,num=0, to emphasize the point.
+ * On successful write, TLS context total_async_write bytes are updated.
+ */
+static int
+openssl_async_write_from_fifo_into_ssl (svm_fifo_t *f, SSL *ssl,
+ openssl_ctx_t *oc)
+{
+ int wrote = 0;
+
+ wrote = SSL_write (ssl, NULL, 0);
+ ossl_check_err_is_fatal (ssl, wrote);
+
+ oc->total_async_write -= wrote;
+ svm_fifo_dequeue_drop (f, wrote);
+
+ return wrote;
+}
+
+/*
+ * Perform SSL_write from TX FIFO head.
+ * On successful write, TLS context total_async_write bytes are updated.
+ */
+static_always_inline int
+openssl_write_from_fifo_head_into_ssl (svm_fifo_t *f, SSL *ssl,
+ openssl_ctx_t *oc, u32 max_len)
+{
+ int wrote = 0, rv, i = 0, len;
+ u32 n_segs = 2;
+ svm_fifo_seg_t fs[n_segs];
+
+ max_len = clib_min (oc->total_async_write, max_len);
+
+ len = svm_fifo_segments (f, 0, fs, &n_segs, max_len);
+ if (len <= 0)
+ return 0;
+
+ while (wrote < len && i < n_segs)
+ {
+ rv = SSL_write (ssl, fs[i].data, fs[i].len);
+ wrote += (rv > 0) ? rv : 0;
+ if (rv < (int) fs[i].len)
+ break;
+ i++;
+ }
+
+ if (wrote)
+ {
+ oc->total_async_write -= wrote;
+ svm_fifo_dequeue_drop (f, wrote);
+ }
+
+ return wrote;
+}
+
+static int
+openssl_async_read_from_ssl_into_fifo (svm_fifo_t *f, SSL *ssl)
+{
+ int read;
+
+ read = SSL_read (ssl, NULL, 0);
+ if (read <= 0)
+ return read;
+
+ svm_fifo_enqueue_nocopy (f, read);
+
+ return read;
+}
+
+/*
+ * Pop the current event from queue and update tail if needed
+ */
+static void
+tls_async_dequeue_update (openssl_evt_t *event, int *evt_run_head,
+ int *evt_run_tail, int *queue_depth)
+{
+ /* remove the event from queue head */
+ *evt_run_head = event->next;
+ event->status = SSL_ASYNC_INVALID_STATUS;
+ event->next = -1;
+
+ (*queue_depth)--;
+
+ if (*evt_run_head < 0)
+ {
+ *evt_run_tail = -1;
+ if (*queue_depth)
+ clib_warning ("queue empty but depth:%d\n", *queue_depth);
+ }
+}
+
+static int
+tls_async_dequeue_event (int thread_index)
+{
+ openssl_evt_t *event;
+ openssl_async_t *om = &openssl_async_main;
+ openssl_async_queue_t *queue = om->queue;
+ int *evt_run_tail = &queue[thread_index].evt_run_tail;
+ int *evt_run_head = &queue[thread_index].evt_run_head;
+ int dequeue_cnt = clib_min (queue[thread_index].depth, MAX_VECTOR_ASYNC);
+ const u32 max_len = 128 << 10;
+
+ /* dequeue all pending events, events enqueued during this routine call,
+ * will be handled next time tls_async_dequeue_event is invoked */
+ while (*evt_run_head >= 0 && dequeue_cnt--)
+ {
+ session_t *app_session, *tls_session;
+ openssl_ctx_t *oc;
+ tls_ctx_t *ctx;
+ SSL *ssl;
+
+ event = openssl_evt_get_w_thread (*evt_run_head, thread_index);
+ ctx = openssl_ctx_get_w_thread (event->ctx_index, thread_index);
+ oc = (openssl_ctx_t *) ctx;
+ ssl = oc->ssl;
+
+ if (event->type == SSL_ASYNC_EVT_RD)
+ {
+ /* read event */
+ svm_fifo_t *app_rx_fifo, *tls_rx_fifo;
+ int read;
+
+ app_session = session_get_from_handle (ctx->app_session_handle);
+ app_rx_fifo = app_session->rx_fifo;
+
+ tls_session = session_get_from_handle (ctx->tls_session_handle);
+ tls_rx_fifo = tls_session->rx_fifo;
+
+ /* continue the paused job */
+ read = openssl_async_read_from_ssl_into_fifo (app_rx_fifo, ssl);
+ if (read < 0)
+ {
+ if (SSL_want_async (ssl))
+ goto handle_later;
+
+ tls_async_dequeue_update (event, evt_run_head, evt_run_tail,
+ &queue[thread_index].depth);
+ goto ev_rd_done;
+ }
+
+ /* read finished or in error, remove the event from queue */
+ tls_async_dequeue_update (event, evt_run_head, evt_run_tail,
+ &queue[thread_index].depth);
+
+ /* Unrecoverable protocol error. Reset connection */
+ if (PREDICT_FALSE ((read < 0) &&
+ (SSL_get_error (ssl, read) == SSL_ERROR_SSL)))
+ {
+ tls_notify_app_io_error (ctx);
+ goto ev_rd_done;
+ }
+
+ /*
+ * Managed to read some data. If handshake just completed, session
+ * may still be in accepting state.
+ */
+ if (app_session->session_state >= SESSION_STATE_READY)
+ tls_notify_app_enqueue (ctx, app_session);
+
+ /* managed to read, try to read more */
+ while (read > 0)
+ {
+ read =
+ openssl_read_from_ssl_into_fifo (app_rx_fifo, ctx, max_len);
+ if (read < 0)
+ {
+ if (SSL_want_async (ssl))
+ {
+ vpp_tls_async_enqueue_event (oc, SSL_ASYNC_EVT_RD, NULL,
+ 0);
+ goto ev_rd_queued;
+ }
+ }
+
+ /* Unrecoverable protocol error. Reset connection */
+ if (PREDICT_FALSE ((read < 0) &&
+ (SSL_get_error (ssl, read) == SSL_ERROR_SSL)))
+ {
+ tls_notify_app_io_error (ctx);
+ goto ev_rd_done;
+ }
+
+ /* If handshake just completed, session may still be in accepting
+ * state */
+ if (read >= 0 &&
+ app_session->session_state >= SESSION_STATE_READY)
+ tls_notify_app_enqueue (ctx, app_session);
+ }
+
+ ev_rd_done:
+ /* read done */
+ ctx->flags &= ~TLS_CONN_F_ASYNC_RD;
+
+ if ((SSL_pending (ssl) > 0) ||
+ svm_fifo_max_dequeue_cons (tls_rx_fifo))
+ {
+ tls_add_vpp_q_builtin_rx_evt (tls_session);
+ }
+
+ ev_rd_queued:
+ continue;
+ }
+ else if (event->type == SSL_ASYNC_EVT_WR)
+ {
+ /* write event */
+ int wrote, wrote_sum = 0;
+ u32 space, enq_buf;
+ svm_fifo_t *app_tx_fifo, *tls_tx_fifo;
+ transport_send_params_t *sp =
+ (transport_send_params_t *) event->handler;
+
+ app_session = session_get_from_handle (ctx->app_session_handle);
+ app_tx_fifo = app_session->tx_fifo;
+
+ /* continue the paused job */
+ wrote =
+ openssl_async_write_from_fifo_into_ssl (app_tx_fifo, ssl, oc);
+ if (wrote < 0)
+ {
+ if (SSL_want_async (ssl))
+ /* paused job not ready, wait */
+ goto handle_later;
+ clib_warning ("[wrote:%d want:%s ctx:%d]\n", wrote,
+ ssl_want[SSL_want (ssl)], oc->openssl_ctx_index);
+ }
+ wrote_sum += wrote;
+
+ /* paused job done, remove event, update queue */
+ tls_async_dequeue_update (event, evt_run_head, evt_run_tail,
+ &queue[thread_index].depth);
+
+ /* Unrecoverable protocol error. Reset connection */
+ if (PREDICT_FALSE (wrote < 0))
+ {
+ tls_notify_app_io_error (ctx);
+ clib_warning (
+ "Unrecoverable protocol error. Reset connection\n");
+ goto ev_in_queue;
+ }
+
+ tls_session = session_get_from_handle (ctx->tls_session_handle);
+ tls_tx_fifo = tls_session->tx_fifo;
+
+ /* prepare for remaining write(s) */
+ space = svm_fifo_max_enqueue_prod (tls_tx_fifo);
+ /* Leave a bit of extra space for tls ctrl data, if any needed */
+ space = clib_max ((int) space - TLSO_CTRL_BYTES, 0);
+
+ /* continue remaining openssl_ctx_write request */
+ while (oc->total_async_write)
+ {
+ int rv;
+ u32 deq_max = svm_fifo_max_dequeue_cons (app_tx_fifo);
+
+ deq_max = clib_min (deq_max, space);
+ deq_max = clib_min (deq_max, sp->max_burst_size);
+ if (!deq_max)
+ goto check_tls_fifo;
+
+ /* Make sure tcp's tx fifo can actually buffer all bytes to
+ * be dequeued. If under memory pressure, tls's fifo segment
+ * might not be able to allocate the chunks needed. This also
+ * avoids errors from the underlying custom bio to the ssl
+ * infra which at times can get stuck. */
+ if (svm_fifo_provision_chunks (tls_tx_fifo, 0, 0,
+ deq_max + TLSO_CTRL_BYTES))
+ goto check_tls_fifo;
+
+ rv = openssl_write_from_fifo_head_into_ssl (app_tx_fifo, ssl, oc,
+ deq_max);
+
+ /* Unrecoverable protocol error. Reset connection */
+ if (PREDICT_FALSE (rv < 0))
+ {
+ tls_notify_app_io_error (ctx);
+ clib_warning (
+ "Unrecoverable protocol error. Reset connection\n");
+ goto ev_in_queue;
+ }
+
+ if (!rv)
+ {
+ if (SSL_want_async (ssl))
+ {
+ /* new paused job, add queue event and wait */
+ vpp_tls_async_enqueue_event (oc, SSL_ASYNC_EVT_WR, sp,
+ 0);
+ goto ev_in_queue;
+ }
+ clib_warning ("[rv:%d want:%s ctx:%d]\n", rv,
+ ssl_want[SSL_want (ssl)],
+ oc->openssl_ctx_index);
+ break;
+ }
+ wrote_sum += rv;
+ }
+
+ if (svm_fifo_needs_deq_ntf (app_tx_fifo, wrote_sum))
+ session_dequeue_notify (app_session);
+
+ check_tls_fifo:
+ /* we got here, async write is done or not possible */
+ oc->total_async_write = 0;
+
+ if (PREDICT_FALSE (BIO_ctrl_pending (oc->rbio) <= 0))
+ tls_notify_app_io_error (ctx);
+
+ /* Deschedule and wait for deq notification if fifo is almost full */
+ enq_buf =
+ clib_min (svm_fifo_size (tls_tx_fifo) / 2, TLSO_MIN_ENQ_SPACE);
+ if (space < wrote_sum + enq_buf)
+ {
+ svm_fifo_add_want_deq_ntf (tls_tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
+ transport_connection_deschedule (&ctx->connection);
+ sp->flags |= TRANSPORT_SND_F_DESCHED;
+ }
+ else
+ {
+ /* Request tx reschedule of the app session */
+ app_session->flags |= SESSION_F_CUSTOM_TX;
+ transport_connection_reschedule (&ctx->connection);
+ }
+
+ ev_in_queue:
+ /* job removed, openssl_ctx_write will resume */
+ continue;
+ }
+ else
+ {
+ /* wrong event type */
+ clib_warning ("goto remove_event [event->type:%d]\n", event->type);
+ tls_async_dequeue_update (event, evt_run_head, evt_run_tail,
+ &queue[thread_index].depth);
+ }
+ }
+
+handle_later:
+ return 1;
+}
+
+static int
+tls_async_dequeue_event_in_init (int thread_index)
+{
+ openssl_evt_t *event;
+ openssl_async_t *om = &openssl_async_main;
+ openssl_async_queue_t *queue = om->queue_in_init;
+ int *evt_run_tail = &queue[thread_index].evt_run_tail;
+ int *evt_run_head = &queue[thread_index].evt_run_head;
+
+ /* dequeue events if exists */
+ while (*evt_run_head >= 0)
+ {
+ openssl_ctx_t *oc;
+ tls_ctx_t *ctx;
+ int rv, err;
+
+ event = openssl_evt_get_w_thread (*evt_run_head, thread_index);
+ ctx = openssl_ctx_get_w_thread (event->ctx_index, thread_index);
+ oc = (openssl_ctx_t *) ctx;
+
+ if (event->type != SSL_ASYNC_EVT_INIT)
+ {
+ /* wrong event type */
+ clib_warning ("goto remove_event [event->type:%d]\n", event->type);
+ goto remove_event;
+ }
+
+ if (!SSL_in_init (oc->ssl))
+ {
+ clib_warning ("[!SSL_in_init() != ev->type:%d] th:%d ev:%d\n",
+ event->type, event->cb_args.thread_index,
+ event->cb_args.event_index);
+ goto remove_event;
+ }
+
+ rv = SSL_do_handshake (oc->ssl);
+ err = SSL_get_error (oc->ssl, rv);
+
+ /* Do not remove session from tail */
+ if (err == SSL_ERROR_WANT_ASYNC)
+ goto handle_later;
+
+ if (err == SSL_ERROR_SSL)
+ {
+ char buf[512];
+ ERR_error_string (ERR_get_error (), buf);
+ clib_warning ("Err: %s\n", buf);
+ openssl_handle_handshake_failure (ctx);
+ goto remove_event;
+ }
+
+ if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ)
+ goto handle_later;
+
+ /* client not supported */
+ if (!SSL_is_server (oc->ssl))
+ {
+ clib_warning ("goto remove_event [!SSL_is_server]\n");
+ goto remove_event;
+ }
+
+ if (tls_notify_app_accept (ctx))
+ {
+ ctx->c_s_index = SESSION_INVALID_INDEX;
+ tls_disconnect_transport (ctx);
+ }
+
+ TLS_DBG (1, "Handshake for %u complete. TLS cipher is %s",
+ oc->openssl_ctx_index, SSL_get_cipher (oc->ssl));
+
+ remove_event:
+ *evt_run_head = event->next;
+ queue[thread_index].depth--;
+
+ if (*evt_run_head < 0)
+ {
+ /* queue empty, bail out */
+ *evt_run_tail = -1;
+ if (queue[thread_index].depth)
+ clib_warning ("queue empty but depth:%d\n",
+ queue[thread_index].depth);
+ break;
+ }
+ }
+
+handle_later:
+ return 1;
+}
+
int
-vpp_tls_async_init_event (tls_ctx_t * ctx,
- openssl_resume_handler * handler,
- session_t * session)
+vpp_tls_async_enqueue_event (openssl_ctx_t *ctx, int evt_type,
+ transport_send_params_t *sp, int size)
+{
+ openssl_evt_t *event;
+ openssl_async_t *om = &openssl_async_main;
+ openssl_async_queue_t *queue;
+ int thread_index;
+ int event_index;
+ int *evt_run_tail;
+ int *evt_run_head;
+
+ event = openssl_evt_get (ctx->evt_index[evt_type]);
+
+ thread_index = event->thread_idx;
+ event_index = event->event_idx;
+
+ /* set queue to be used */
+ if (SSL_in_init (ctx->ssl))
+ queue = om->queue_in_init;
+ else
+ queue = om->queue;
+
+ evt_run_tail = &queue[thread_index].evt_run_tail;
+ evt_run_head = &queue[thread_index].evt_run_head;
+
+ event->type = evt_type;
+ event->handler = (openssl_resume_handler *) sp;
+ event->next = -1;
+
+ /* first we enqueue the request */
+ if (*evt_run_tail >= 0)
+ {
+ openssl_evt_t *event_tail;
+
+ /* queue not empty, append to tail event */
+ event_tail = openssl_evt_get_w_thread (*evt_run_tail, thread_index);
+ event_tail->next = event_index;
+ }
+
+ /* set tail to use new event index */
+ *evt_run_tail = event_index;
+
+ if (*evt_run_head < 0)
+ /* queue is empty, update head */
+ *evt_run_head = event_index;
+
+ queue[thread_index].depth++;
+ if (queue[thread_index].depth > queue[thread_index].max_depth)
+ queue[thread_index].max_depth = queue[thread_index].depth;
+
+ return 1;
+}
+
+static int
+vpp_tls_async_init_event (tls_ctx_t *ctx, openssl_resume_handler *handler,
+ session_t *session, ssl_async_evt_type_t evt_type)
{
u32 eidx;
openssl_evt_t *event;
@@ -293,8 +824,10 @@ vpp_tls_async_init_event (tls_ctx_t * ctx,
event->thread_idx = thread_id;
event->handler = handler;
event->session_index = session->session_index;
- event->status = 0;
- ctx->evt_index = eidx;
+ event->type = evt_type;
+ event->status = SSL_ASYNC_INVALID_STATUS;
+ oc->evt_index[evt_type] = eidx;
+ event->next = -1;
#ifdef HAVE_OPENSSL_ASYNC
SSL_set_async_callback_arg (oc->ssl, &event->cb_args);
#endif
@@ -303,25 +836,45 @@ vpp_tls_async_init_event (tls_ctx_t * ctx,
}
int
-vpp_openssl_is_inflight (tls_ctx_t * ctx)
+vpp_tls_async_init_events (tls_ctx_t *ctx, openssl_resume_handler *handler,
+ session_t *session)
+{
+ vpp_tls_async_init_event (ctx, handler, session, SSL_ASYNC_EVT_INIT);
+ vpp_tls_async_init_event (ctx, handler, session, SSL_ASYNC_EVT_RD);
+ vpp_tls_async_init_event (ctx, handler, session, SSL_ASYNC_EVT_WR);
+
+ return 1;
+}
+
+int
+vpp_openssl_is_inflight (tls_ctx_t *ctx)
{
u32 eidx;
+ openssl_ctx_t *oc = (openssl_ctx_t *) ctx;
openssl_evt_t *event;
- eidx = ctx->evt_index;
- event = openssl_evt_get (eidx);
+ int i;
+
+ for (i = SSL_ASYNC_EVT_INIT; i < SSL_ASYNC_EVT_MAX; i++)
+ {
+ eidx = oc->evt_index[i];
+ event = openssl_evt_get (eidx);
+
+ if (event->status == SSL_ASYNC_INFLIGHT)
+ return 1;
+ }
- if (event->status == SSL_ASYNC_INFLIGHT)
- return 1;
return 0;
}
int
-vpp_tls_async_update_event (tls_ctx_t * ctx, int eagain)
+vpp_tls_async_update_event (tls_ctx_t *ctx, int eagain,
+ ssl_async_evt_type_t type)
{
u32 eidx;
+ openssl_ctx_t *oc = (openssl_ctx_t *) ctx;
openssl_evt_t *event;
- eidx = ctx->evt_index;
+ eidx = oc->evt_index[type];
event = openssl_evt_get (eidx);
event->status = SSL_ASYNC_INFLIGHT;
if (eagain)
@@ -469,7 +1022,7 @@ tls_resume_from_crypto (int thread_index)
continue;
}
- event->status = 0;
+ event->status = SSL_ASYNC_INVALID_STATUS;
*evt_run_head = event->next;
if (event->next < 0)
@@ -502,7 +1055,8 @@ tls_async_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
if (pool_elts (om->evt_pool[thread_index]) > 0)
{
openssl_async_polling ();
- tls_resume_from_crypto (thread_index);
+ tls_async_dequeue_event_in_init (thread_index);
+ tls_async_dequeue_event (thread_index);
}
return 0;
diff --git a/src/plugins/tlsopenssl/tls_openssl.c b/src/plugins/tlsopenssl/tls_openssl.c
index c8e685f20c5..d7adbed3269 100644
--- a/src/plugins/tlsopenssl/tls_openssl.c
+++ b/src/plugins/tlsopenssl/tls_openssl.c
@@ -74,9 +74,16 @@ openssl_ctx_free (tls_ctx_t * ctx)
SSL_free (oc->ssl);
vec_free (ctx->srv_hostname);
SSL_CTX_free (oc->client_ssl_ctx);
-#ifdef HAVE_OPENSSL_ASYNC
- openssl_evt_free (ctx->evt_index, ctx->c_thread_index);
-#endif
+
+ if (openssl_main.async)
+ {
+ openssl_evt_free (oc->evt_index[SSL_ASYNC_EVT_INIT],
+ ctx->c_thread_index);
+ openssl_evt_free (oc->evt_index[SSL_ASYNC_EVT_RD],
+ ctx->c_thread_index);
+ openssl_evt_free (oc->evt_index[SSL_ASYNC_EVT_WR],
+ ctx->c_thread_index);
+ }
}
pool_put_index (openssl_main.ctx_pool[ctx->c_thread_index],
@@ -159,17 +166,43 @@ openssl_lctx_get (u32 lctx_index)
return pool_elt_at_index (openssl_main.lctx_pool, lctx_index);
}
-#define ossl_check_err_is_fatal(_ssl, _rv) \
- if (PREDICT_FALSE (_rv < 0 && SSL_get_error (_ssl, _rv) == SSL_ERROR_SSL)) \
- return -1;
-
static int
-openssl_read_from_ssl_into_fifo (svm_fifo_t *f, SSL *ssl, u32 max_len)
+openssl_handle_want_async (tls_ctx_t *ctx, int evt_type,
+ transport_send_params_t *sp, int size)
+{
+ int ret;
+ openssl_ctx_t *oc = (openssl_ctx_t *) ctx;
+
+ if (evt_type >= SSL_ASYNC_EVT_MAX || evt_type == 0)
+ {
+ clib_warning ("return 0 [illegal evt_type value:%d]\n", evt_type);
+ return 0;
+ }
+
+ if (evt_type == SSL_ASYNC_EVT_WR)
+ {
+ /* de-schedule transport connection */
+ transport_connection_deschedule (&ctx->connection);
+ sp->flags |= TRANSPORT_SND_F_DESCHED;
+ oc->total_async_write = size;
+ }
+ ret = vpp_tls_async_enqueue_event (oc, evt_type, sp, size);
+
+ return ret;
+}
+
+int
+openssl_read_from_ssl_into_fifo (svm_fifo_t *f, tls_ctx_t *ctx, u32 max_len)
{
int read, rv, n_fs, i;
+ openssl_ctx_t *oc = (openssl_ctx_t *) ctx;
const int n_segs = 2;
svm_fifo_seg_t fs[n_segs];
u32 max_enq;
+ SSL *ssl = oc->ssl;
+
+ if (ctx->flags & TLS_CONN_F_ASYNC_RD)
+ return 0;
max_enq = svm_fifo_max_enqueue_prod (f);
if (!max_enq)
@@ -184,6 +217,12 @@ openssl_read_from_ssl_into_fifo (svm_fifo_t *f, SSL *ssl, u32 max_len)
read = SSL_read (ssl, fs[0].data, fs[0].len);
if (read <= 0)
{
+ if (openssl_main.async && SSL_want_async (oc->ssl))
+ {
+ ctx->flags |= TLS_CONN_F_ASYNC_RD;
+ openssl_handle_want_async (ctx, SSL_ASYNC_EVT_RD, NULL, 0);
+ return 0;
+ }
ossl_check_err_is_fatal (ssl, read);
return 0;
}
@@ -208,11 +247,14 @@ openssl_read_from_ssl_into_fifo (svm_fifo_t *f, SSL *ssl, u32 max_len)
}
static int
-openssl_write_from_fifo_into_ssl (svm_fifo_t *f, SSL *ssl, u32 max_len)
+openssl_write_from_fifo_into_ssl (svm_fifo_t *f, tls_ctx_t *ctx,
+ transport_send_params_t *sp, u32 max_len)
{
int wrote = 0, rv, i = 0, len;
u32 n_segs = 2;
svm_fifo_seg_t fs[n_segs];
+ openssl_ctx_t *oc = (openssl_ctx_t *) ctx;
+ SSL *ssl = oc->ssl;
len = svm_fifo_segments (f, 0, fs, &n_segs, max_len);
if (len <= 0)
@@ -230,6 +272,11 @@ openssl_write_from_fifo_into_ssl (svm_fifo_t *f, SSL *ssl, u32 max_len)
i++;
}
+ if (openssl_main.async && SSL_want_async (ssl))
+ {
+ openssl_handle_want_async (ctx, SSL_ASYNC_EVT_WR, sp, max_len);
+ return 0;
+ }
if (wrote)
svm_fifo_dequeue_drop (f, wrote);
@@ -247,11 +294,15 @@ openssl_check_async_status (tls_ctx_t * ctx, openssl_resume_handler * handler,
SSL_get_async_status (oc->ssl, &estatus);
if (estatus == ASYNC_STATUS_EAGAIN)
{
- vpp_tls_async_update_event (ctx, 1);
+ vpp_tls_async_update_event (ctx, 1, SSL_ASYNC_EVT_INIT);
+ vpp_tls_async_update_event (ctx, 1, SSL_ASYNC_EVT_RD);
+ vpp_tls_async_update_event (ctx, 1, SSL_ASYNC_EVT_WR);
}
else
{
- vpp_tls_async_update_event (ctx, 0);
+ vpp_tls_async_update_event (ctx, 0, SSL_ASYNC_EVT_INIT);
+ vpp_tls_async_update_event (ctx, 0, SSL_ASYNC_EVT_RD);
+ vpp_tls_async_update_event (ctx, 0, SSL_ASYNC_EVT_WR);
}
return 1;
@@ -260,8 +311,8 @@ openssl_check_async_status (tls_ctx_t * ctx, openssl_resume_handler * handler,
#endif
-static void
-openssl_handle_handshake_failure (tls_ctx_t * ctx)
+void
+openssl_handle_handshake_failure (tls_ctx_t *ctx)
{
/* Failed to renegotiate handshake */
if (ctx->flags & TLS_CONN_F_HS_DONE)
@@ -304,19 +355,17 @@ openssl_ctx_handshake_rx (tls_ctx_t * ctx, session_t * tls_session)
rv = SSL_do_handshake (oc->ssl);
err = SSL_get_error (oc->ssl, rv);
-#ifdef HAVE_OPENSSL_ASYNC
- if (err == SSL_ERROR_WANT_ASYNC)
+ if (openssl_main.async && err == SSL_ERROR_WANT_ASYNC)
{
- openssl_check_async_status (ctx, openssl_ctx_handshake_rx,
- tls_session);
+ openssl_handle_want_async (ctx, SSL_ASYNC_EVT_INIT, NULL, 0);
+ return -1;
}
-#endif
+
if (err == SSL_ERROR_SSL)
{
char buf[512];
ERR_error_string (ERR_get_error (), buf);
clib_warning ("Err: %s", buf);
-
openssl_handle_handshake_failure (ctx);
return -1;
}
@@ -385,8 +434,8 @@ openssl_ctx_handshake_rx (tls_ctx_t * ctx, session_t * tls_session)
return rv;
}
-static void
-openssl_confirm_app_close (tls_ctx_t * ctx)
+void
+openssl_confirm_app_close (tls_ctx_t *ctx)
{
openssl_ctx_t *oc = (openssl_ctx_t *) ctx;
SSL_shutdown (oc->ssl);
@@ -425,7 +474,7 @@ openssl_ctx_write_tls (tls_ctx_t *ctx, session_t *app_session,
if (svm_fifo_provision_chunks (ts->tx_fifo, 0, 0, deq_max + TLSO_CTRL_BYTES))
goto check_tls_fifo;
- wrote = openssl_write_from_fifo_into_ssl (f, oc->ssl, deq_max);
+ wrote = openssl_write_from_fifo_into_ssl (f, ctx, sp, deq_max);
/* Unrecoverable protocol error. Reset connection */
if (PREDICT_FALSE (wrote < 0))
@@ -556,7 +605,7 @@ openssl_ctx_read_tls (tls_ctx_t *ctx, session_t *tls_session)
app_session = session_get_from_handle (ctx->app_session_handle);
f = app_session->rx_fifo;
- read = openssl_read_from_ssl_into_fifo (f, oc->ssl, max_len);
+ read = openssl_read_from_ssl_into_fifo (f, ctx, max_len);
/* Unrecoverable protocol error. Reset connection */
if (PREDICT_FALSE (read < 0))
@@ -753,10 +802,9 @@ openssl_ctx_init_client (tls_ctx_t * ctx)
SSL_CTX_set_ecdh_auto (oc->client_ssl_ctx, 1);
SSL_CTX_set_mode (oc->client_ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
-#ifdef HAVE_OPENSSL_ASYNC
if (om->async)
SSL_CTX_set_mode (oc->client_ssl_ctx, SSL_MODE_ASYNC);
-#endif
+
rv =
SSL_CTX_set_cipher_list (oc->client_ssl_ctx, (const char *) om->ciphers);
if (rv != 1)
@@ -801,7 +849,17 @@ openssl_ctx_init_client (tls_ctx_t * ctx)
{
TLS_DBG (1, "Couldn't set client certificate-key pair");
}
-
+ /* Set TLS Record size */
+ if (om->record_size)
+ {
+ rv = SSL_CTX_set_max_send_fragment (oc->client_ssl_ctx, om->record_size);
+ if (rv != 1)
+ {
+ TLS_DBG (1, "Couldn't set TLS record-size");
+ return -1;
+ }
+ TLS_DBG (1, "Using TLS record-size of %d", om->record_size);
+ }
/*
* 2. Do the first steps in the handshake.
*/
@@ -810,7 +868,7 @@ openssl_ctx_init_client (tls_ctx_t * ctx)
#ifdef HAVE_OPENSSL_ASYNC
session_t *tls_session = session_get_from_handle (ctx->tls_session_handle);
- vpp_tls_async_init_event (ctx, openssl_ctx_handshake_rx, tls_session);
+ vpp_tls_async_init_events (ctx, openssl_ctx_handshake_rx, tls_session);
#endif
while (1)
{
@@ -828,7 +886,7 @@ openssl_ctx_init_client (tls_ctx_t * ctx)
break;
}
- TLS_DBG (2, "tls state for [%u]%u is su", ctx->c_thread_index,
+ TLS_DBG (2, "tls state for [%u]%u is %s", ctx->c_thread_index,
oc->openssl_ctx_index, SSL_state_string_long (oc->ssl));
return 0;
}
@@ -895,6 +953,39 @@ openssl_start_listen (tls_ctx_t * lctx)
return -1;
}
+ /* Set TLS Record size */
+ if (om->record_size)
+ {
+ rv = SSL_CTX_set_max_send_fragment (ssl_ctx, om->record_size);
+ if (rv != 1)
+ {
+ TLS_DBG (1, "Couldn't set TLS record-size");
+ return -1;
+ }
+ }
+
+ /* Set TLS Record Split size */
+ if (om->record_split_size)
+ {
+ rv = SSL_CTX_set_split_send_fragment (ssl_ctx, om->record_split_size);
+ if (rv != 1)
+ {
+ TLS_DBG (1, "Couldn't set TLS record-split-size");
+ return -1;
+ }
+ }
+
+ /* Set TLS Max Pipeline count */
+ if (om->max_pipelines)
+ {
+ rv = SSL_CTX_set_max_pipelines (ssl_ctx, om->max_pipelines);
+ if (rv != 1)
+ {
+ TLS_DBG (1, "Couldn't set TLS max-pipelines");
+ return -1;
+ }
+ }
+
/*
* Set the key and cert
*/
@@ -1012,22 +1103,23 @@ openssl_ctx_init_server (tls_ctx_t * ctx)
TLS_DBG (1, "Initiating handshake for [%u]%u", ctx->c_thread_index,
oc->openssl_ctx_index);
-#ifdef HAVE_OPENSSL_ASYNC
- session_t *tls_session = session_get_from_handle (ctx->tls_session_handle);
- vpp_tls_async_init_event (ctx, openssl_ctx_handshake_rx, tls_session);
-#endif
+ if (openssl_main.async)
+ {
+ session_t *tls_session =
+ session_get_from_handle (ctx->tls_session_handle);
+ vpp_tls_async_init_events (ctx, openssl_ctx_handshake_rx, tls_session);
+ }
+
while (1)
{
rv = SSL_do_handshake (oc->ssl);
err = SSL_get_error (oc->ssl, rv);
-#ifdef HAVE_OPENSSL_ASYNC
- if (err == SSL_ERROR_WANT_ASYNC)
+ if (openssl_main.async && err == SSL_ERROR_WANT_ASYNC)
{
- openssl_check_async_status (ctx, openssl_ctx_handshake_rx,
- tls_session);
+ openssl_handle_want_async (ctx, SSL_ASYNC_EVT_INIT, NULL, 0);
+
break;
}
-#endif
if (err != SSL_ERROR_WANT_WRITE)
break;
}
@@ -1040,10 +1132,8 @@ openssl_ctx_init_server (tls_ctx_t * ctx)
static int
openssl_transport_close (tls_ctx_t * ctx)
{
-#ifdef HAVE_OPENSSL_ASYNC
- if (vpp_openssl_is_inflight (ctx))
+ if (openssl_main.async && vpp_openssl_is_inflight (ctx))
return 0;
-#endif
if (!(ctx->flags & TLS_CONN_F_HS_DONE))
{
@@ -1224,7 +1314,6 @@ VLIB_INIT_FUNCTION (tls_openssl_init) =
.runs_after = VLIB_INITS("tls_init"),
};
-#ifdef HAVE_OPENSSL_ASYNC
static clib_error_t *
tls_openssl_set_command_fn (vlib_main_t * vm, unformat_input_t * input,
vlib_cli_command_t * cmd)
@@ -1302,7 +1391,45 @@ VLIB_CLI_COMMAND (tls_openssl_set_command, static) =
.short_help = "tls openssl set [engine <engine name>] [alg [algorithm] [async]",
.function = tls_openssl_set_command_fn,
};
-#endif
+
+static clib_error_t *
+tls_openssl_set_tls_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ openssl_main_t *om = &openssl_main;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "record-size %U", unformat_memory_size,
+ &om->record_size))
+ {
+ clib_warning ("Using TLS record-size of %d", om->record_size);
+ }
+ else if (unformat (input, "record-split-size %U", unformat_memory_size,
+ &om->record_split_size))
+ {
+ clib_warning ("Using TLS record-split-size of %d",
+ om->record_split_size);
+ }
+ else if (unformat (input, "max-pipelines %U", unformat_memory_size,
+ &om->max_pipelines))
+ {
+ clib_warning ("Using TLS max-pipelines of %d", om->max_pipelines);
+ }
+ else
+ return clib_error_return (0, "failed: unknown input `%U'",
+ format_unformat_error, input);
+ }
+
+ return 0;
+}
+
+VLIB_CLI_COMMAND (tls_openssl_set_tls, static) = {
+ .path = "tls openssl set-tls",
+ .short_help = "tls openssl set-tls [record-size <size>] [record-split-size "
+ "<size>] [max-pipelines <size>]",
+ .function = tls_openssl_set_tls_fn,
+};
VLIB_PLUGIN_REGISTER () = {
.version = VPP_BUILD_VER,
diff --git a/src/plugins/tlsopenssl/tls_openssl.h b/src/plugins/tlsopenssl/tls_openssl.h
index 1600cd77aba..8f6c6652a52 100644
--- a/src/plugins/tlsopenssl/tls_openssl.h
+++ b/src/plugins/tlsopenssl/tls_openssl.h
@@ -29,12 +29,18 @@
#define DTLSO_MAX_DGRAM 2000
+#define ossl_check_err_is_fatal(_ssl, _rv) \
+ if (PREDICT_FALSE (_rv < 0 && SSL_get_error (_ssl, _rv) == SSL_ERROR_SSL)) \
+ return -1;
+
typedef struct tls_ctx_openssl_
{
tls_ctx_t ctx; /**< First */
u32 openssl_ctx_index;
SSL_CTX *client_ssl_ctx;
SSL *ssl;
+ u32 evt_index[SSL_ASYNC_EVT_MAX];
+ u32 total_async_write;
BIO *rbio;
BIO *wbio;
} openssl_ctx_t;
@@ -63,15 +69,20 @@ typedef struct openssl_main_
u8 *ciphers;
int engine_init;
int async;
+ u32 record_size;
+ u32 record_split_size;
+ u32 max_pipelines;
} openssl_main_t;
typedef int openssl_resume_handler (tls_ctx_t * ctx, session_t * tls_session);
tls_ctx_t *openssl_ctx_get_w_thread (u32 ctx_index, u8 thread_index);
-int vpp_tls_async_init_event (tls_ctx_t * ctx,
- openssl_resume_handler * handler,
- session_t * session);
-int vpp_tls_async_update_event (tls_ctx_t * ctx, int eagain);
+int vpp_tls_async_init_events (tls_ctx_t *ctx, openssl_resume_handler *handler,
+ session_t *session);
+int vpp_tls_async_update_event (tls_ctx_t *ctx, int eagain,
+ ssl_async_evt_type_t type);
+int vpp_tls_async_enqueue_event (openssl_ctx_t *ctx, int evt_type,
+ transport_send_params_t *sp, int size);
int tls_async_openssl_callback (SSL * s, void *evt);
int openssl_evt_free (int event_idx, u8 thread_index);
void openssl_polling_start (ENGINE * engine);
@@ -80,6 +91,10 @@ void openssl_async_node_enable_disable (u8 is_en);
clib_error_t *tls_openssl_api_init (vlib_main_t * vm);
int tls_openssl_set_ciphers (char *ciphers);
int vpp_openssl_is_inflight (tls_ctx_t * ctx);
+int openssl_read_from_ssl_into_fifo (svm_fifo_t *f, tls_ctx_t *ctx,
+ u32 max_len);
+void openssl_handle_handshake_failure (tls_ctx_t *ctx);
+void openssl_confirm_app_close (tls_ctx_t *ctx);
#endif /* SRC_PLUGINS_TLSOPENSSL_TLS_OPENSSL_H_ */