diff options
Diffstat (limited to 'src/plugins')
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_ */ |