diff options
Diffstat (limited to 'src/plugins')
155 files changed, 15901 insertions, 4174 deletions
diff --git a/src/plugins/acl/acl.c b/src/plugins/acl/acl.c index e52e82fcf28..fbd94761027 100644 --- a/src/plugins/acl/acl.c +++ b/src/plugins/acl/acl.c @@ -2845,6 +2845,17 @@ acl_set_aclplugin_interface_fn (vlib_main_t * vm, } \ } while (0) +#define vec_validate_macip_acl_rules(v, idx) \ + do \ + { \ + if (vec_len (v) < idx + 1) \ + { \ + vec_validate (v, idx); \ + v[idx].is_permit = 0x1; \ + } \ + } \ + while (0) + static clib_error_t * acl_set_aclplugin_acl_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) @@ -3062,6 +3073,160 @@ acl_show_aclplugin_macip_interface_fn (vlib_main_t * vm, return error; } +static clib_error_t * +acl_set_aclplugin_macip_acl_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + vl_api_macip_acl_rule_t *rules = 0; + int rule_idx = 0; + int rv = 0; + u32 acl_index = ~0; + u32 action = 0; + u8 src_mac[6]; + u8 *tag = 0; + u8 mac_mask_all_1[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + ip_prefix_t src_ip; + + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + vec_validate_macip_acl_rules (rules, rule_idx); + if (unformat (line_input, "permit")) + { + rules[rule_idx].is_permit = 1; + } + else if (unformat (line_input, "deny")) + { + rules[rule_idx].is_permit = 0; + } + else if (unformat (line_input, "action %d", &action)) + { + rules[rule_idx].is_permit = action; + } + else if (unformat (line_input, "ip %U", unformat_ip_prefix, &src_ip)) + { + ip_prefix_encode2 (&src_ip, &rules[rule_idx].src_prefix); + } + else if (unformat (line_input, "src")) + { + /* Everything in MACIP is "source" but allow this verbosity */ + } + else if (unformat (line_input, "mac %U", unformat_mac_address, &src_mac)) + { + memcpy (rules[rule_idx].src_mac, &src_mac, + sizeof (rules[rule_idx].src_mac)); + memcpy (rules[rule_idx].src_mac_mask, &mac_mask_all_1, + sizeof (rules[rule_idx].src_mac_mask)); + } + else if (unformat (line_input, "mask %U", unformat_mac_address, + &src_mac)) + { + memcpy (rules[rule_idx].src_mac_mask, &src_mac, + sizeof (rules[rule_idx].src_mac_mask)); + } + else if (unformat (line_input, "tag %s", &tag)) + ; + else if (unformat (line_input, ",")) + { + rule_idx++; + } + else + break; + } + + if (!tag) + vec_add (tag, "cli", 4); + + rv = macip_acl_add_list (vec_len (rules), rules, &acl_index, tag); + vec_free (rules); + vec_free (tag); + + unformat_free (line_input); + if (rv) + return clib_error_return (0, "Failed to set MACIP ACL rule"); + + vlib_cli_output (vm, "ACL index:%u", acl_index); + return 0; +} + +static clib_error_t * +acl_macip_delete_aclplugin_acl_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + int rv; + u32 macip_acl_index = ~0; + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "index %u", &macip_acl_index)) + { + /* operate on this acl index (which must exist) */ + } + else + break; + } + + if (macip_acl_index == ~0) + return (clib_error_return (0, "invalid acl index")); + + rv = macip_acl_del_list (macip_acl_index); + + unformat_free (line_input); + if (rv) + return (clib_error_return (0, "Failed to delete ACL index")); + + vlib_cli_output (vm, "Deleted ACL index:%u", macip_acl_index); + return 0; +} + +static clib_error_t * +acl_set_aclplugin_macip_interface_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + int rv = 0; + u32 sw_if_index = ~0; + u32 acl_index = ~0; + u32 is_add = 1; + unformat_input_t _line_input, *line_input = &_line_input; + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%U", unformat_vnet_sw_interface, + vnet_get_main (), &sw_if_index)) + ; + else if (unformat (line_input, "add")) + is_add = 1; + else if (unformat (line_input, "del")) + is_add = 0; + else if (unformat (line_input, "acl %u", &acl_index)) + ; + else + break; + } + + if (sw_if_index == ~0) + return (clib_error_return (0, "invalid interface")); + + if (acl_index == ~0) + return (clib_error_return (0, "invalid acl index")); + + rv = macip_acl_interface_add_del_acl (sw_if_index, is_add, acl_index); + + if (rv) + return (clib_error_return (0, "Failed to add acl rule to interface")); + + return 0; +} + static void acl_plugin_show_acl (acl_main_t * am, u32 acl_index) { @@ -3632,6 +3797,38 @@ VLIB_CLI_COMMAND (aclplugin_set_acl_command, static) = { }; /*? + * Create an MACIP Access Control List (ACL) + * A MACIP ACL is used to add L2-L3 ACL rules. + * A MACIP ACL can be added similar to ACL rules by using following command : + * + * @cliexcmd{set acl-plugin macip acl <permit|deny|action N> + * ip <PREFIX> mac <MAC> mask <int> [tag FOO] {use comma + * separated list for multiple rules}} + ?*/ +VLIB_CLI_COMMAND (aclplugin_macip_set_acl_command, static) = { + .path = "set acl-plugin macip acl ", + .short_help = "set acl-plugin macip acl <permit|deny|action N> " + "ip <PREFIX> mac <MAC> mask <int> [tag FOO] {use comma " + "separated list for multiple rules}", + .function = acl_set_aclplugin_macip_acl_fn, +}; + +/*? + * [un]Apply a MACIP ACL to an interface. + * The ACL being applied must already exist. + * + * @cliexpar + * <b><em> set acl-plugin macip interface <interface> <acl INDEX> [del] + </b></em> + * @cliexend + ?*/ +VLIB_CLI_COMMAND (aclplugin_macip_set_interface_command, static) = { + .path = "set acl-plugin macip interface", + .short_help = "set acl-plugin macip interface <interface> <acl INDEX> [del]", + .function = acl_set_aclplugin_macip_interface_fn, +}; + +/*? * Delete an Access Control List (ACL) * Removes an ACL at the specified index, which must exist but not in use by * any interface. @@ -3644,6 +3841,20 @@ VLIB_CLI_COMMAND (aclplugin_delete_acl_command, static) = { .function = acl_delete_aclplugin_acl_fn, }; +/*? + * Delete a MACIP Access Control List (ACL) + * Removes an MACIP ACL at the specified index, which must exist but not in + * use by + * any interface. + * + * @cliexcmd{delete acl-plugin macip acl index <idx>} + ?*/ +VLIB_CLI_COMMAND (aclplugin_macip_delete_acl_command, static) = { + .path = "delete acl-plugin macip acl", + .short_help = "delete acl-plugin macip acl index <idx>", + .function = acl_macip_delete_aclplugin_acl_fn, +}; + static clib_error_t * acl_plugin_config (vlib_main_t * vm, unformat_input_t * input) { diff --git a/src/plugins/af_xdp/af_xdp.api b/src/plugins/af_xdp/af_xdp.api index 4c2908e2037..20aa20b4d7d 100644 --- a/src/plugins/af_xdp/af_xdp.api +++ b/src/plugins/af_xdp/af_xdp.api @@ -33,96 +33,6 @@ enumflag af_xdp_flag : u8 /** \brief @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request - @param host_if - Linux netdev interface name - @param name - new af_xdp interface name (optional) - @param rxq_num - number of receive queues. 65535 can be used as special value to request all available queues (optional) - @param rxq_size - receive queue size (optional) - @param txq_size - transmit queue size (optional) - @param mode - operation mode (optional) - @param flags - flags (optional) - @param prog - eBPF program path (optional) -*/ - -define af_xdp_create -{ - u32 client_index; - u32 context; - - string host_if[64]; - string name[64]; - u16 rxq_num [default=1]; - u16 rxq_size [default=0]; - u16 txq_size [default=0]; - vl_api_af_xdp_mode_t mode [default=0]; - vl_api_af_xdp_flag_t flags [default=0]; - string prog[256]; - option vat_help = "<host-if linux-ifname> [name ifname] [rx-queue-size size] [tx-queue-size size] [num-rx-queues <num|all>] [prog pathname] [zero-copy|no-zero-copy] [no-syscall-lock]"; - option deprecated; -}; - -/** \brief - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param host_if - Linux netdev interface name - @param name - new af_xdp interface name (optional) - @param rxq_num - number of receive queues. 65535 can be used as special value to request all available queues (optional) - @param rxq_size - receive queue size (optional) - @param txq_size - transmit queue size (optional) - @param mode - operation mode (optional) - @param flags - flags (optional) - @param prog - eBPF program path (optional) - @param namespace - netns of nic (optional) -*/ - -define af_xdp_create_v2 -{ - u32 client_index; - u32 context; - - string host_if[64]; - string name[64]; - u16 rxq_num [default=1]; - u16 rxq_size [default=0]; - u16 txq_size [default=0]; - vl_api_af_xdp_mode_t mode [default=0]; - vl_api_af_xdp_flag_t flags [default=0]; - string prog[256]; - string namespace[64]; - option vat_help = "<host-if linux-ifname> [name ifname] [rx-queue-size size] [tx-queue-size size] [num-rx-queues <num|all>] [prog pathname] [netns ns] [zero-copy|no-zero-copy] [no-syscall-lock]"; - option deprecated; -}; - -/** \brief - @param context - sender context, to match reply w/ request - @param retval - return value for request - @param sw_if_index - software index for the new af_xdp interface -*/ - -define af_xdp_create_reply -{ - u32 context; - i32 retval; - vl_api_interface_index_t sw_if_index; - option deprecated; -}; - -/** \brief - @param context - sender context, to match reply w/ request - @param retval - return value for request - @param sw_if_index - software index for the new af_xdp interface -*/ - -define af_xdp_create_v2_reply -{ - u32 context; - i32 retval; - vl_api_interface_index_t sw_if_index; - option deprecated; -}; - -/** \brief - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request @param sw_if_index - interface index */ diff --git a/src/plugins/af_xdp/api.c b/src/plugins/af_xdp/api.c index 3e9a3fe2578..9ead9856ff5 100644 --- a/src/plugins/af_xdp/api.c +++ b/src/plugins/af_xdp/api.c @@ -57,65 +57,6 @@ af_xdp_api_flags (vl_api_af_xdp_flag_t flags) } static void -vl_api_af_xdp_create_t_handler (vl_api_af_xdp_create_t * mp) -{ - vlib_main_t *vm = vlib_get_main (); - af_xdp_main_t *rm = &af_xdp_main; - vl_api_af_xdp_create_reply_t *rmp; - af_xdp_create_if_args_t args; - int rv; - - clib_memset (&args, 0, sizeof (af_xdp_create_if_args_t)); - - args.linux_ifname = mp->host_if[0] ? (char *) mp->host_if : 0; - args.name = mp->name[0] ? (char *) mp->name : 0; - args.prog = mp->prog[0] ? (char *) mp->prog : 0; - args.mode = af_xdp_api_mode (mp->mode); - args.flags = af_xdp_api_flags (mp->flags); - args.rxq_size = ntohs (mp->rxq_size); - args.txq_size = ntohs (mp->txq_size); - args.rxq_num = ntohs (mp->rxq_num); - - af_xdp_create_if (vm, &args); - rv = args.rv; - - REPLY_MACRO2 (VL_API_AF_XDP_CREATE_REPLY, - ({ rmp->sw_if_index = ntohl (args.sw_if_index); })); -} - -static void -vl_api_af_xdp_create_v2_t_handler (vl_api_af_xdp_create_v2_t *mp) -{ - vlib_main_t *vm = vlib_get_main (); - af_xdp_main_t *rm = &af_xdp_main; - vl_api_af_xdp_create_v2_reply_t *rmp; - af_xdp_create_if_args_t args; - int rv; - - clib_memset (&args, 0, sizeof (af_xdp_create_if_args_t)); - - args.linux_ifname = mp->host_if[0] ? (char *) mp->host_if : 0; - args.name = mp->name[0] ? (char *) mp->name : 0; - args.prog = mp->prog[0] ? (char *) mp->prog : 0; - args.netns = mp->namespace[0] ? (char *) mp->namespace : 0; - args.mode = af_xdp_api_mode (mp->mode); - args.flags = af_xdp_api_flags (mp->flags); - args.rxq_size = ntohs (mp->rxq_size); - args.txq_size = ntohs (mp->txq_size); - args.rxq_num = ntohs (mp->rxq_num); - - af_xdp_create_if (vm, &args); - rv = args.rv; - - /* clang-format off */ - REPLY_MACRO2 (VL_API_AF_XDP_CREATE_V2_REPLY, - ({ - rmp->sw_if_index = ntohl (args.sw_if_index); - })); - /* clang-format on */ -} - -static void vl_api_af_xdp_create_v3_t_handler (vl_api_af_xdp_create_v3_t *mp) { vlib_main_t *vm = vlib_get_main (); diff --git a/src/plugins/af_xdp/test_api.c b/src/plugins/af_xdp/test_api.c index 581697e341d..5f622adcb04 100644 --- a/src/plugins/af_xdp/test_api.c +++ b/src/plugins/af_xdp/test_api.c @@ -58,75 +58,7 @@ api_af_xdp_mode (af_xdp_mode_t mode) return ~0; } -/* af_xdp create API */ -static int -api_af_xdp_create (vat_main_t * vam) -{ - vl_api_af_xdp_create_t *mp; - af_xdp_create_if_args_t args; - int ret; - - if (!unformat_user (vam->input, unformat_af_xdp_create_if_args, &args)) - { - clib_warning ("unknown input `%U'", format_unformat_error, vam->input); - return -99; - } - - M (AF_XDP_CREATE, mp); - - snprintf ((char *) mp->host_if, sizeof (mp->host_if), "%s", - args.linux_ifname ? : ""); - snprintf ((char *) mp->name, sizeof (mp->name), "%s", args.name ? : ""); - mp->rxq_num = clib_host_to_net_u16 (args.rxq_num); - mp->rxq_size = clib_host_to_net_u16 (args.rxq_size); - mp->txq_size = clib_host_to_net_u16 (args.txq_size); - mp->mode = api_af_xdp_mode (args.mode); - if (args.flags & AF_XDP_CREATE_FLAGS_NO_SYSCALL_LOCK) - mp->flags |= AF_XDP_API_FLAGS_NO_SYSCALL_LOCK; - snprintf ((char *) mp->prog, sizeof (mp->prog), "%s", args.prog ? : ""); - - S (mp); - W (ret); - - return ret; -} - -/* af_xdp create v2 API */ -static int -api_af_xdp_create_v2 (vat_main_t *vam) -{ - vl_api_af_xdp_create_v2_t *mp; - af_xdp_create_if_args_t args; - int ret; - - if (!unformat_user (vam->input, unformat_af_xdp_create_if_args, &args)) - { - clib_warning ("unknown input `%U'", format_unformat_error, vam->input); - return -99; - } - - M (AF_XDP_CREATE, mp); - - snprintf ((char *) mp->host_if, sizeof (mp->host_if), "%s", - args.linux_ifname ?: ""); - snprintf ((char *) mp->name, sizeof (mp->name), "%s", args.name ?: ""); - snprintf ((char *) mp->namespace, sizeof (mp->namespace), "%s", - args.netns ?: ""); - mp->rxq_num = clib_host_to_net_u16 (args.rxq_num); - mp->rxq_size = clib_host_to_net_u16 (args.rxq_size); - mp->txq_size = clib_host_to_net_u16 (args.txq_size); - mp->mode = api_af_xdp_mode (args.mode); - if (args.flags & AF_XDP_CREATE_FLAGS_NO_SYSCALL_LOCK) - mp->flags |= AF_XDP_API_FLAGS_NO_SYSCALL_LOCK; - snprintf ((char *) mp->prog, sizeof (mp->prog), "%s", args.prog ?: ""); - - S (mp); - W (ret); - - return ret; -} - -/* af_xdp create v2 API */ +/* af_xdp create v3 API */ static int api_af_xdp_create_v3 (vat_main_t *vam) { @@ -140,7 +72,7 @@ api_af_xdp_create_v3 (vat_main_t *vam) return -99; } - M (AF_XDP_CREATE, mp); + M (AF_XDP_CREATE_V3, mp); snprintf ((char *) mp->host_if, sizeof (mp->host_if), "%s", args.linux_ifname ?: ""); @@ -160,45 +92,9 @@ api_af_xdp_create_v3 (vat_main_t *vam) return ret; } -/* af_xdp-create reply handler */ -static void -vl_api_af_xdp_create_reply_t_handler (vl_api_af_xdp_create_reply_t * mp) -{ - vat_main_t *vam = af_xdp_test_main.vat_main; - i32 retval = ntohl (mp->retval); - - if (retval == 0) - { - fformat (vam->ofp, "created af_xdp with sw_if_index %d\n", - ntohl (mp->sw_if_index)); - } - - vam->retval = retval; - vam->result_ready = 1; - vam->regenerate_interface_table = 1; -} - -/* af_xdp-create v2 reply handler */ -static void -vl_api_af_xdp_create_v2_reply_t_handler (vl_api_af_xdp_create_v2_reply_t *mp) -{ - vat_main_t *vam = af_xdp_test_main.vat_main; - i32 retval = ntohl (mp->retval); - - if (retval == 0) - { - fformat (vam->ofp, "created af_xdp with sw_if_index %d\n", - ntohl (mp->sw_if_index)); - } - - vam->retval = retval; - vam->result_ready = 1; - vam->regenerate_interface_table = 1; -} - /* af_xdp-create v3 reply handler */ static void -vl_api_af_xdp_create_v3_reply_t_handler (vl_api_af_xdp_create_v2_reply_t *mp) +vl_api_af_xdp_create_v3_reply_t_handler (vl_api_af_xdp_create_v3_reply_t *mp) { vat_main_t *vam = af_xdp_test_main.vat_main; i32 retval = mp->retval; diff --git a/src/plugins/avf/avf.h b/src/plugins/avf/avf.h index f6f79cf0e09..774aac0151b 100644 --- a/src/plugins/avf/avf.h +++ b/src/plugins/avf/avf.h @@ -180,6 +180,7 @@ typedef struct u8 int_mode; u8 buffer_pool_index; u32 queue_index; + u64 total_packets; } avf_rxq_t; typedef struct @@ -198,6 +199,8 @@ typedef struct avf_tx_desc_t *tmp_descs; u32 *tmp_bufs; u32 queue_index; + u64 total_packets; + u64 no_free_tx_count; } avf_txq_t; typedef struct diff --git a/src/plugins/avf/device.c b/src/plugins/avf/device.c index 1618800c432..98169f0bcfe 100644 --- a/src/plugins/avf/device.c +++ b/src/plugins/avf/device.c @@ -288,6 +288,7 @@ avf_rxq_init (vlib_main_t * vm, avf_device_t * ad, u16 qid, u16 rxq_size) d->qword[0] = vlib_buffer_get_pa (vm, b); d++; } + rxq->total_packets = 0; return 0; } @@ -337,6 +338,9 @@ avf_txq_init (vlib_main_t * vm, avf_device_t * ad, u16 qid, u16 txq_size) vec_validate_aligned (txq->tmp_descs, txq->size, CLIB_CACHE_LINE_BYTES); vec_validate_aligned (txq->tmp_bufs, txq->size, CLIB_CACHE_LINE_BYTES); + txq->total_packets = 0; + txq->no_free_tx_count = 0; + return 0; } diff --git a/src/plugins/avf/format.c b/src/plugins/avf/format.c index 0a153a093d9..436f5b9fbf2 100644 --- a/src/plugins/avf/format.c +++ b/src/plugins/avf/format.c @@ -104,6 +104,7 @@ format_avf_device (u8 * s, va_list * args) u8 *a = 0; avf_rxq_t *rxq = vec_elt_at_index (ad->rxqs, 0); avf_txq_t *txq = vec_elt_at_index (ad->txqs, 0); + u32 idx = 0; s = format (s, "rx: queues %u, desc %u (min %u max %u)", ad->n_rx_queues, rxq->size, AVF_QUEUE_SZ_MIN, AVF_QUEUE_SZ_MAX); @@ -114,6 +115,22 @@ format_avf_device (u8 * s, va_list * args) format_avf_device_flags, ad); s = format (s, "\n%Ucapability flags: %U", format_white_space, indent, format_avf_vf_cap_flags, ad->cap_flags); + s = + format (s, "\n%U Rx Queue: Total Packets", format_white_space, indent + 4); + for (idx = 0; idx < ad->n_rx_queues; idx++) + { + rxq = vec_elt_at_index (ad->rxqs, idx); + s = format (s, "\n%U %8u : %llu", format_white_space, indent + 4, idx, + rxq->total_packets); + } + s = format (s, "\n%U Tx Queue: Total Packets\t Total Drops", + format_white_space, indent + 4); + for (idx = 0; idx < ad->n_tx_queues; idx++) + { + txq = vec_elt_at_index (ad->txqs, idx); + s = format (s, "\n%U %8u : %llu\t %llu", format_white_space, indent + 4, + idx, txq->total_packets, txq->no_free_tx_count); + } s = format (s, "\n%Unum-queue-pairs %d max-vectors %u max-mtu %u " "rss-key-size %u rss-lut-size %u", format_white_space, indent, diff --git a/src/plugins/avf/input.c b/src/plugins/avf/input.c index 06007db540d..890259c88ab 100644 --- a/src/plugins/avf/input.c +++ b/src/plugins/avf/input.c @@ -539,6 +539,8 @@ done: else avf_rxq_refill (vm, node, rxq, 0 /* use_va_dma */ ); + rxq->total_packets += n_rx_packets; + return n_rx_packets; } diff --git a/src/plugins/avf/output.c b/src/plugins/avf/output.c index daa86ae86b2..0952886aaee 100644 --- a/src/plugins/avf/output.c +++ b/src/plugins/avf/output.c @@ -510,6 +510,7 @@ retry: avf_tail_write (txq->qtx_tail, txq->next); txq->n_enqueued += n_desc; n_left -= n_enq; + txq->total_packets += n_enq; } if (n_left) @@ -522,6 +523,7 @@ retry: vlib_buffer_free (vm, buffers, n_left); vlib_error_count (vm, node->node_index, AVF_TX_ERROR_NO_FREE_SLOTS, n_left); + txq->no_free_tx_count += n_left; } if (tf->shared_queue) diff --git a/src/plugins/builtinurl/FEATURE.yaml b/src/plugins/builtinurl/FEATURE.yaml deleted file mode 100644 index ba8e3c7ea7b..00000000000 --- a/src/plugins/builtinurl/FEATURE.yaml +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: Builtin URL support for the static http or https server -maintainer: Dave Barach <dave@barachs.net> -features: - - Builtin URLs for the static http/https server -description: "The (builtinurl) plugin adds a set of URLs to the static http/https server. - Current URLs, all of which return data in .json fmt: - <root-url>/version.json - vpp version info - <root-url>/interface_list.json - list of interfaces - <root-url>/interface_stats - single interface via HTTP POST - <root-url>/interface_stats - all intfcs via HTTP GET." -state: development -properties: [API, CLI, MULTITHREAD] diff --git a/src/plugins/builtinurl/builtins.c b/src/plugins/builtinurl/builtins.c deleted file mode 100644 index 6a0975503a7..00000000000 --- a/src/plugins/builtinurl/builtins.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2019 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 <vnet/vnet.h> -#include <builtinurl/builtinurl.h> -#include <http_static/http_static.h> -#include <vpp/app/version.h> - -hss_url_handler_rc_t -handle_get_version (hss_url_handler_args_t *args) -{ - u8 *s = 0; - - /* Build some json bullshit */ - s = format (s, "{\"vpp_details\": {"); - s = format (s, " \"version\": \"%s\",", VPP_BUILD_VER); - s = format (s, " \"build_date\": \"%s\"}}\r\n", VPP_BUILD_DATE); - - args->data = s; - args->data_len = vec_len (s); - args->free_vec_data = 1; - return HSS_URL_HANDLER_OK; -} - -void -trim_path_from_request (u8 * s, char *path) -{ - u8 *cp; - int trim_length = strlen (path) + 1 /* remove '?' */ ; - - /* Get rid of the path and question-mark */ - vec_delete (s, trim_length, 0); - - /* Tail trim irrelevant browser info */ - cp = s; - while ((cp - s) < vec_len (s)) - { - if (*cp == ' ') - { - /* - * Makes request a vector which happens to look - * like a c-string. - */ - *cp = 0; - vec_set_len (s, cp - s); - break; - } - cp++; - } -} - -hss_url_handler_rc_t -handle_get_interface_stats (hss_url_handler_args_t *args) -{ - u8 *s = 0, *stats = 0; - uword *p; - u32 *sw_if_indices = 0; - vnet_hw_interface_t *hi; - vnet_sw_interface_t *si; - char *q = "\""; - int i; - int need_comma = 0; - u8 *format_vnet_sw_interface_cntrs (u8 * s, vnet_interface_main_t * im, - vnet_sw_interface_t * si, int json); - vnet_main_t *vnm = vnet_get_main (); - vnet_interface_main_t *im = &vnm->interface_main; - - /* Get stats for a single interface via http POST */ - if (args->req_type == HTTP_REQ_POST) - { - /* Find the sw_if_index */ - p = hash_get (im->hw_interface_by_name, args->req_data); - if (!p) - { - s = format (s, "{\"interface_stats\": {[\n"); - s = format (s, " \"name\": \"%s\",", args->req_data); - s = format (s, " \"error\": \"%s\"", "UnknownInterface"); - s = format (s, "]}\n"); - goto out; - } - - vec_add1 (sw_if_indices, p[0]); - } - else /* default, HTTP_BUILTIN_METHOD_GET */ - { - pool_foreach (hi, im->hw_interfaces) - { - vec_add1 (sw_if_indices, hi->sw_if_index); - } - } - - s = format (s, "{%sinterface_stats%s: [\n", q, q); - - for (i = 0; i < vec_len (sw_if_indices); i++) - { - si = vnet_get_sw_interface (vnm, sw_if_indices[i]); - if (need_comma) - s = format (s, ",\n"); - - need_comma = 1; - - s = format (s, "{%sname%s: %s%U%s, ", q, q, q, - format_vnet_sw_if_index_name, vnm, sw_if_indices[i], q); - - stats = format_vnet_sw_interface_cntrs (stats, &vnm->interface_main, si, - 1 /* want json */ ); - if (vec_len (stats)) - s = format (s, "%v}", stats); - else - s = format (s, "%snone%s: %strue%s}", q, q, q, q); - vec_reset_length (stats); - } - - s = format (s, "]}\n"); - -out: - args->data = s; - args->data_len = vec_len (s); - args->free_vec_data = 1; - vec_free (sw_if_indices); - vec_free (stats); - return HSS_URL_HANDLER_OK; -} - -hss_url_handler_rc_t -handle_get_interface_list (hss_url_handler_args_t *args) -{ - u8 *s = 0; - int i; - vnet_main_t *vnm = vnet_get_main (); - vnet_interface_main_t *im = &vnm->interface_main; - vnet_hw_interface_t *hi; - u32 *hw_if_indices = 0; - int need_comma = 0; - - /* Construct vector of active hw_if_indexes ... */ - pool_foreach (hi, im->hw_interfaces) - { - /* No point in mentioning "local0"... */ - if (hi - im->hw_interfaces) - vec_add1 (hw_if_indices, hi - im->hw_interfaces); - } - - /* Build answer */ - s = format (s, "{\"interface_list\": [\n"); - for (i = 0; i < vec_len (hw_if_indices); i++) - { - if (need_comma) - s = format (s, ",\n"); - hi = pool_elt_at_index (im->hw_interfaces, hw_if_indices[i]); - s = format (s, "\"%v\"", hi->name); - need_comma = 1; - } - s = format (s, "]}\n"); - vec_free (hw_if_indices); - - args->data = s; - args->data_len = vec_len (s); - args->free_vec_data = 1; - return HSS_URL_HANDLER_OK; -} - -void -builtinurl_handler_init (builtinurl_main_t * bm) -{ - - bm->register_handler (handle_get_version, "version.json", HTTP_REQ_GET); - bm->register_handler (handle_get_interface_list, "interface_list.json", - HTTP_REQ_GET); - bm->register_handler (handle_get_interface_stats, "interface_stats.json", - HTTP_REQ_GET); - bm->register_handler (handle_get_interface_stats, "interface_stats.json", - HTTP_REQ_POST); -} - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/builtinurl/builtinurl.api b/src/plugins/builtinurl/builtinurl.api deleted file mode 100644 index 80efa73c725..00000000000 --- a/src/plugins/builtinurl/builtinurl.api +++ /dev/null @@ -1,44 +0,0 @@ -/* - * builtinurl.api - binary API skeleton - * - * Copyright (c) <current-year> <your-organization> - * 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. - */ - -/** - * @file builtinurl.api - * @brief VPP control-plane API messages. - * - * This file defines VPP control-plane binary API messages which are generally - * called through a shared memory interface. - */ - -/* Version and type recitations */ - -option version = "1.0.0"; - -/** @brief API to enable / disable builtinurl on an interface - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param enable_disable - 1 to enable, 0 to disable the feature - @param sw_if_index - interface handle -*/ - -autoreply define builtinurl_enable { - option deprecated="incorporated in http_static plugin"; - /* Client identifier, set from api_main.my_client_index */ - u32 client_index; - - /* Arbitrary context, so client can match reply to request */ - u32 context; -}; diff --git a/src/plugins/builtinurl/builtinurl.c b/src/plugins/builtinurl/builtinurl.c deleted file mode 100644 index 749a2c93b8a..00000000000 --- a/src/plugins/builtinurl/builtinurl.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * builtinurl.c - skeleton vpp engine plug-in - * - * Copyright (c) 2019 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 <vnet/vnet.h> -#include <vnet/plugin/plugin.h> -#include <builtinurl/builtinurl.h> - -#include <vlibapi/api.h> -#include <vlibmemory/api.h> -#include <vpp/app/version.h> -#include <stdbool.h> - -/* define message IDs */ -#include <builtinurl/builtinurl.api_enum.h> -#include <builtinurl/builtinurl.api_types.h> - -#define REPLY_MSG_ID_BASE bmp->msg_id_base -#include <vlibapi/api_helper_macros.h> - -builtinurl_main_t builtinurl_main; - -/* Action function shared between message handler and debug CLI */ - -int -builtinurl_enable (builtinurl_main_t * bmp) -{ - void (*fp) (void *, char *, int); - - if (bmp->initialized) - return 0; - - /* Look up the builtin URL registration handler */ - fp = vlib_get_plugin_symbol - ("http_static_plugin.so", "http_static_server_register_builtin_handler"); - - /* Most likely, the http_static plugin isn't loaded. Done. */ - if (fp == 0) - return VNET_API_ERROR_NO_SUCH_TABLE; - - bmp->register_handler = fp; - builtinurl_handler_init (bmp); - bmp->initialized = 1; - - return 0; -} - -static clib_error_t * -builtinurl_enable_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - builtinurl_main_t *bmp = &builtinurl_main; - - int rv; - - rv = builtinurl_enable (bmp); - - switch (rv) - { - case 0: - break; - - case VNET_API_ERROR_NO_SUCH_TABLE: - return clib_error_return - (0, "http_static_server_register_builtin_handler undefined"); - break; - - default: - return clib_error_return (0, "builtinurl_enable returned %d", rv); - } - return 0; -} - -VLIB_CLI_COMMAND (builtinurl_enable_command, static) = -{ - .path = "builtinurl enable", - .short_help = "Turn on builtin http/https GET and POST urls", - .function = builtinurl_enable_command_fn, -}; - -/* API message handler */ -static void vl_api_builtinurl_enable_t_handler - (vl_api_builtinurl_enable_t * mp) -{ - vl_api_builtinurl_enable_reply_t *rmp; - builtinurl_main_t *bmp = &builtinurl_main; - int rv; - - rv = builtinurl_enable (bmp); - - REPLY_MACRO (VL_API_BUILTINURL_ENABLE_REPLY); -} - -#include <builtinurl/builtinurl.api.c> -static clib_error_t * -builtinurl_init (vlib_main_t * vm) -{ - builtinurl_main_t *bmp = &builtinurl_main; - - bmp->vlib_main = vm; - bmp->vnet_main = vnet_get_main (); - - /* Ask for a correctly-sized block of API message decode slots */ - bmp->msg_id_base = setup_message_id_table (); - - return 0; -} - -VLIB_INIT_FUNCTION (builtinurl_init); - -VLIB_PLUGIN_REGISTER () = -{ - .version = VPP_BUILD_VER, - .description = "vpp built-in URL support", -}; - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/builtinurl/builtinurl.h b/src/plugins/builtinurl/builtinurl.h deleted file mode 100644 index 91302c1eee5..00000000000 --- a/src/plugins/builtinurl/builtinurl.h +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * builtinurl.h - built-in URLs for the http static server - * - * Copyright (c) 2019 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_builtinurl_h__ -#define __included_builtinurl_h__ - -#include <vnet/vnet.h> -#include <vnet/ip/ip.h> -#include <vnet/ethernet/ethernet.h> - -#include <vppinfra/hash.h> -#include <vppinfra/error.h> - -typedef struct -{ - /* API message ID base */ - u16 msg_id_base; - - /* GET / POST handler registration function */ - void (*register_handler) (void *, char *, int); - - /* Been there, done that */ - int initialized; - - /* convenience */ - vlib_main_t *vlib_main; - vnet_main_t *vnet_main; - ethernet_main_t *ethernet_main; -} builtinurl_main_t; - -extern builtinurl_main_t builtinurl_main; - -void builtinurl_handler_init (builtinurl_main_t * bm); - -#endif /* __included_builtinurl_h__ */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/builtinurl/builtinurl_test.c b/src/plugins/builtinurl/builtinurl_test.c deleted file mode 100644 index 9edfb81c525..00000000000 --- a/src/plugins/builtinurl/builtinurl_test.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * builtinurl.c - skeleton vpp-api-test plug-in - * - * Copyright (c) 2019 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 <vat/vat.h> -#include <vlibapi/api.h> -#include <vlibmemory/api.h> -#include <vppinfra/error.h> -#include <stdbool.h> - -uword unformat_sw_if_index (unformat_input_t * input, va_list * args); - -/* Declare message IDs */ -#include <builtinurl/builtinurl.api_enum.h> -#include <builtinurl/builtinurl.api_types.h> - -typedef struct -{ - /* API message ID base */ - u16 msg_id_base; - vat_main_t *vat_main; -} builtinurl_test_main_t; - -builtinurl_test_main_t builtinurl_test_main; - -#define __plugin_msg_base builtinurl_test_main.msg_id_base -#include <vlibapi/vat_helper_macros.h> - -static int -api_builtinurl_enable (vat_main_t * vam) -{ - vl_api_builtinurl_enable_t *mp; - int ret; - - /* Construct the API message */ - M (BUILTINURL_ENABLE, mp); - - /* send it... */ - S (mp); - - /* Wait for a reply... */ - W (ret); - return ret; -} - -#include <builtinurl/builtinurl.api_test.c> - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/crypto_native/FEATURE.yaml b/src/plugins/crypto_native/FEATURE.yaml index 06f26d4a8cf..d54816d673f 100644 --- a/src/plugins/crypto_native/FEATURE.yaml +++ b/src/plugins/crypto_native/FEATURE.yaml @@ -4,6 +4,9 @@ maintainer: Damjan Marion <damarion@cisco.com> features: - CBC(128, 192, 256) - GCM(128, 192, 256) + - CTR(128, 192, 256) + - SHA(224, 256) + - HMAC-SHA(224, 256) description: "An implementation of a native crypto-engine" state: production diff --git a/src/plugins/crypto_native/aes_cbc.c b/src/plugins/crypto_native/aes_cbc.c index dd7ca3f1cf1..c981897783f 100644 --- a/src/plugins/crypto_native/aes_cbc.c +++ b/src/plugins/crypto_native/aes_cbc.c @@ -25,191 +25,40 @@ #pragma GCC optimize ("O3") #endif -#if defined(__VAES__) && defined(__AVX512F__) -#define u8xN u8x64 -#define u32xN u32x16 -#define u32xN_min_scalar u32x16_min_scalar -#define u32xN_is_all_zero u32x16_is_all_zero -#define u32xN_splat u32x16_splat -#elif defined(__VAES__) -#define u8xN u8x32 -#define u32xN u32x8 -#define u32xN_min_scalar u32x8_min_scalar -#define u32xN_is_all_zero u32x8_is_all_zero -#define u32xN_splat u32x8_splat -#else -#define u8xN u8x16 -#define u32xN u32x4 -#define u32xN_min_scalar u32x4_min_scalar -#define u32xN_is_all_zero u32x4_is_all_zero -#define u32xN_splat u32x4_splat -#endif +#define CRYPTO_NATIVE_AES_CBC_ENC_VEC_SIZE 256 static_always_inline u32 aes_ops_enc_aes_cbc (vlib_main_t * vm, vnet_crypto_op_t * ops[], u32 n_ops, aes_key_size_t ks) { crypto_native_main_t *cm = &crypto_native_main; - int rounds = AES_KEY_ROUNDS (ks); - u8 placeholder[8192]; - u32 i, j, count, n_left = n_ops; - u32xN placeholder_mask = { }; - u32xN len = { }; - vnet_crypto_key_index_t key_index[4 * N_AES_LANES]; - u8 *src[4 * N_AES_LANES] = {}; - u8 *dst[4 * N_AES_LANES] = {}; - u8xN r[4] = {}; - u8xN k[15][4] = {}; - - for (i = 0; i < 4 * N_AES_LANES; i++) - key_index[i] = ~0; - -more: - for (i = 0; i < 4 * N_AES_LANES; i++) - if (len[i] == 0) - { - if (n_left == 0) - { - /* no more work to enqueue, so we are enqueueing placeholder buffer */ - src[i] = dst[i] = placeholder; - len[i] = sizeof (placeholder); - placeholder_mask[i] = 0; - } - else - { - u8x16 t = aes_block_load (ops[0]->iv); - ((u8x16 *) r)[i] = t; - - src[i] = ops[0]->src; - dst[i] = ops[0]->dst; - len[i] = ops[0]->len; - placeholder_mask[i] = ~0; - if (key_index[i] != ops[0]->key_index) - { - aes_cbc_key_data_t *kd; - key_index[i] = ops[0]->key_index; - kd = (aes_cbc_key_data_t *) cm->key_data[key_index[i]]; - for (j = 0; j < rounds + 1; j++) - ((u8x16 *) k[j])[i] = kd->encrypt_key[j]; - } - ops[0]->status = VNET_CRYPTO_OP_STATUS_COMPLETED; - n_left--; - ops++; - } - } - - count = u32xN_min_scalar (len); - - ASSERT (count % 16 == 0); - - for (i = 0; i < count; i += 16) + u32 i, n_left = n_ops; + uword key_indices[CRYPTO_NATIVE_AES_CBC_ENC_VEC_SIZE] = {}; + u8 *plaintext[CRYPTO_NATIVE_AES_CBC_ENC_VEC_SIZE] = {}; + uword oplen[CRYPTO_NATIVE_AES_CBC_ENC_VEC_SIZE] = {}; + u8 *iv[CRYPTO_NATIVE_AES_CBC_ENC_VEC_SIZE] = {}; + u8 *ciphertext[CRYPTO_NATIVE_AES_CBC_ENC_VEC_SIZE] = {}; + + while (n_left) { -#if defined(__VAES__) && defined(__AVX512F__) - r[0] = u8x64_xor3 (r[0], aes_block_load_x4 (src, i), k[0][0]); - r[1] = u8x64_xor3 (r[1], aes_block_load_x4 (src + 4, i), k[0][1]); - r[2] = u8x64_xor3 (r[2], aes_block_load_x4 (src + 8, i), k[0][2]); - r[3] = u8x64_xor3 (r[3], aes_block_load_x4 (src + 12, i), k[0][3]); - - for (j = 1; j < rounds; j++) + i = 0; + while (n_left && i < CRYPTO_NATIVE_AES_CBC_ENC_VEC_SIZE) { - r[0] = aes_enc_round_x4 (r[0], k[j][0]); - r[1] = aes_enc_round_x4 (r[1], k[j][1]); - r[2] = aes_enc_round_x4 (r[2], k[j][2]); - r[3] = aes_enc_round_x4 (r[3], k[j][3]); + key_indices[i] = ops[0]->key_index; + plaintext[i] = ops[0]->src; + ciphertext[i] = ops[0]->dst; + oplen[i] = ops[0]->len; + iv[i] = ops[0]->iv; + ops[0]->status = VNET_CRYPTO_OP_STATUS_COMPLETED; + + ops++; + n_left--; + i++; } - r[0] = aes_enc_last_round_x4 (r[0], k[j][0]); - r[1] = aes_enc_last_round_x4 (r[1], k[j][1]); - r[2] = aes_enc_last_round_x4 (r[2], k[j][2]); - r[3] = aes_enc_last_round_x4 (r[3], k[j][3]); - - aes_block_store_x4 (dst, i, r[0]); - aes_block_store_x4 (dst + 4, i, r[1]); - aes_block_store_x4 (dst + 8, i, r[2]); - aes_block_store_x4 (dst + 12, i, r[3]); -#elif defined(__VAES__) - r[0] = u8x32_xor3 (r[0], aes_block_load_x2 (src, i), k[0][0]); - r[1] = u8x32_xor3 (r[1], aes_block_load_x2 (src + 2, i), k[0][1]); - r[2] = u8x32_xor3 (r[2], aes_block_load_x2 (src + 4, i), k[0][2]); - r[3] = u8x32_xor3 (r[3], aes_block_load_x2 (src + 6, i), k[0][3]); - - for (j = 1; j < rounds; j++) - { - r[0] = aes_enc_round_x2 (r[0], k[j][0]); - r[1] = aes_enc_round_x2 (r[1], k[j][1]); - r[2] = aes_enc_round_x2 (r[2], k[j][2]); - r[3] = aes_enc_round_x2 (r[3], k[j][3]); - } - r[0] = aes_enc_last_round_x2 (r[0], k[j][0]); - r[1] = aes_enc_last_round_x2 (r[1], k[j][1]); - r[2] = aes_enc_last_round_x2 (r[2], k[j][2]); - r[3] = aes_enc_last_round_x2 (r[3], k[j][3]); - - aes_block_store_x2 (dst, i, r[0]); - aes_block_store_x2 (dst + 2, i, r[1]); - aes_block_store_x2 (dst + 4, i, r[2]); - aes_block_store_x2 (dst + 6, i, r[3]); -#else -#if __x86_64__ - r[0] = u8x16_xor3 (r[0], aes_block_load (src[0] + i), k[0][0]); - r[1] = u8x16_xor3 (r[1], aes_block_load (src[1] + i), k[0][1]); - r[2] = u8x16_xor3 (r[2], aes_block_load (src[2] + i), k[0][2]); - r[3] = u8x16_xor3 (r[3], aes_block_load (src[3] + i), k[0][3]); - - for (j = 1; j < rounds; j++) - { - r[0] = aes_enc_round_x1 (r[0], k[j][0]); - r[1] = aes_enc_round_x1 (r[1], k[j][1]); - r[2] = aes_enc_round_x1 (r[2], k[j][2]); - r[3] = aes_enc_round_x1 (r[3], k[j][3]); - } - - r[0] = aes_enc_last_round_x1 (r[0], k[j][0]); - r[1] = aes_enc_last_round_x1 (r[1], k[j][1]); - r[2] = aes_enc_last_round_x1 (r[2], k[j][2]); - r[3] = aes_enc_last_round_x1 (r[3], k[j][3]); - - aes_block_store (dst[0] + i, r[0]); - aes_block_store (dst[1] + i, r[1]); - aes_block_store (dst[2] + i, r[2]); - aes_block_store (dst[3] + i, r[3]); -#else - r[0] ^= aes_block_load (src[0] + i); - r[1] ^= aes_block_load (src[1] + i); - r[2] ^= aes_block_load (src[2] + i); - r[3] ^= aes_block_load (src[3] + i); - for (j = 0; j < rounds - 1; j++) - { - r[0] = vaesmcq_u8 (vaeseq_u8 (r[0], k[j][0])); - r[1] = vaesmcq_u8 (vaeseq_u8 (r[1], k[j][1])); - r[2] = vaesmcq_u8 (vaeseq_u8 (r[2], k[j][2])); - r[3] = vaesmcq_u8 (vaeseq_u8 (r[3], k[j][3])); - } - r[0] = vaeseq_u8 (r[0], k[j][0]) ^ k[rounds][0]; - r[1] = vaeseq_u8 (r[1], k[j][1]) ^ k[rounds][1]; - r[2] = vaeseq_u8 (r[2], k[j][2]) ^ k[rounds][2]; - r[3] = vaeseq_u8 (r[3], k[j][3]) ^ k[rounds][3]; - aes_block_store (dst[0] + i, r[0]); - aes_block_store (dst[1] + i, r[1]); - aes_block_store (dst[2] + i, r[2]); - aes_block_store (dst[3] + i, r[3]); -#endif -#endif + clib_aes_cbc_encrypt_multi ((aes_cbc_key_data_t **) cm->key_data, + key_indices, plaintext, oplen, iv, ks, + ciphertext, i); } - - len -= u32xN_splat (count); - - for (i = 0; i < 4 * N_AES_LANES; i++) - { - src[i] += count; - dst[i] += count; - } - - if (n_left > 0) - goto more; - - if (!u32xN_is_all_zero (len & placeholder_mask)) - goto more; - return n_ops; } diff --git a/src/plugins/dev_armada/CMakeLists.txt b/src/plugins/dev_armada/CMakeLists.txt new file mode 100644 index 00000000000..e755e7bdd46 --- /dev/null +++ b/src/plugins/dev_armada/CMakeLists.txt @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright(c) 2022 Cisco Systems, Inc. + + +find_path(MUSDK_INCLUDE_DIR NAMES mv_std.h) +find_library(MUSDK_LIB NAMES libmusdk.a) + +if(NOT MUSDK_INCLUDE_DIR OR NOT MUSDK_LIB) + message(WARNING "Marvell MUSDK not found - dev_armada plugin disabled") + return() +endif() + +get_filename_component(MUSDK_LIB_DIR ${MUSDK_LIB} DIRECTORY) +set(MUSDK_LINK_FLAGS "-Wl,--whole-archive,${MUSDK_LIB_DIR}/libmusdk.a,--no-whole-archive") + +add_vpp_plugin(dev_armada + SOURCES + plugin.c + pp2/counters.c + pp2/init.c + pp2/format.c + pp2/port.c + pp2/queue.c + pp2/rx.c + pp2/tx.c + + LINK_FLAGS + ${MUSDK_LINK_FLAGS} +) +include_directories(${MUSDK_INCLUDE_DIR}) + diff --git a/src/plugins/dev_armada/README.rst b/src/plugins/dev_armada/README.rst new file mode 100644 index 00000000000..2c757d04a06 --- /dev/null +++ b/src/plugins/dev_armada/README.rst @@ -0,0 +1,61 @@ +Armada device plugin +===================== + +Overview +-------- + +This plugins provides native device support for Marvell PP2 network +device, found in Marvel Armada family of SOCs. +It uses Marvell Usermode SDK +(`MUSDK <https://github.com/MarvellEmbeddedProcessors/musdk-marvell>`__). + +Prerequisites +------------- + +Plugins depends on installed MUSDK and Marvell provided linux in Marvell SDK. +Following kernel modules from MUSDK must be loaded for plugin to work: +``musdk_cma.ko`` +``mv_pp_uio.ko`` + +Musdk 18.09.3 compilation steps +------------------------------- + +:: + + ./bootstrap + ./configure --prefix=/opt/vpp/external/aarch64/ CFLAGS="-Wno-error=unused-result -g -fPIC" --enable-shared=no + sed -i -e 's/marvell,mv-pp-uio/generic-uio/' modules/pp2/mv_pp_uio.c + sed -i -e 's/O_CREAT/O_CREAT, S_IRUSR | S_IWUSR/' src/lib/file_utils.c + make + sudo make install + +Usage +----- + +Interface Creation and Deletion +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Interfaces are using new vnet dev APIs, CLIs or startup.conf to create and +delete interfaces. + +Sample startup.conf: + +:: + + devices { + dev platform/f2000000.ethernet { + port 1 { name ppio1 } + } + +Device identifier in this example is 'platform/f2000000.ethernet' where +'platform' is bus name and 'f2000000.ethernet' is linux platform bus +identifier for specific PP2. + +Platform identifier can be found in sysfs: + +:: + + $ ls /sys/bus/platform/devices | grep ethernet + f2000000.ethernet + + diff --git a/src/plugins/dev_armada/musdk.h b/src/plugins/dev_armada/musdk.h new file mode 100644 index 00000000000..aad2f4a1cef --- /dev/null +++ b/src/plugins/dev_armada/musdk.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2023 Cisco Systems, Inc. + */ + +#ifndef _MUSDK_H_ +#define _MUSDK_H_ + +#define MVCONF_DBG_LEVEL 0 +#define MVCONF_PP2_BPOOL_COOKIE_SIZE 32 +#define MVCONF_PP2_BPOOL_DMA_ADDR_SIZE 64 +#define MVCONF_DMA_PHYS_ADDR_T_SIZE 64 +#define MVCONF_SYS_DMA_UIO +#define MVCONF_TYPES_PUBLIC +#define MVCONF_DMA_PHYS_ADDR_T_PUBLIC + +#include <mv_std.h> +#include <env/mv_sys_dma.h> +#include <drivers/mv_pp2.h> +#include <drivers/mv_pp2_bpool.h> +#include <drivers/mv_pp2_ppio.h> + +#endif /* _MUSDK_H_ */ diff --git a/src/plugins/dev_armada/plugin.c b/src/plugins/dev_armada/plugin.c new file mode 100644 index 00000000000..1dc465c9a25 --- /dev/null +++ b/src/plugins/dev_armada/plugin.c @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2023 Cisco Systems, Inc. + */ + +#include <vlib/vlib.h> +#include <vnet/plugin/plugin.h> +#include <vpp/app/version.h> + +VLIB_PLUGIN_REGISTER () = { + .version = VPP_BUILD_VER, + .description = "Marvell Armada Drivers", +}; diff --git a/src/plugins/dev_armada/pp2/counters.c b/src/plugins/dev_armada/pp2/counters.c new file mode 100644 index 00000000000..a041138bc79 --- /dev/null +++ b/src/plugins/dev_armada/pp2/counters.c @@ -0,0 +1,241 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2023 Cisco Systems, Inc. + */ + +#include <vnet/vnet.h> +#include <vnet/ethernet/ethernet.h> +#include <vnet/dev/dev.h> +#include <vnet/dev/counters.h> +#include <vnet/dev/bus/platform.h> +#include <vppinfra/ring.h> +#include <dev_armada/musdk.h> +#include <dev_armada/pp2/pp2.h> + +VLIB_REGISTER_LOG_CLASS (mvpp2_log, static) = { + .class_name = "armada", + .subclass_name = "pp2-counters", +}; + +typedef enum +{ + MVPP2_PORT_CTR_RX_BYTES, + MVPP2_PORT_CTR_RX_PACKETS, + MVPP2_PORT_CTR_RX_UCAST, + MVPP2_PORT_CTR_RX_ERRORS, + MVPP2_PORT_CTR_RX_FULLQ_DROPPED, + MVPP2_PORT_CTR_RX_BM_DROPPED, + MVPP2_PORT_CTR_RX_EARLY_DROPPED, + MVPP2_PORT_CTR_RX_FIFO_DROPPED, + MVPP2_PORT_CTR_RX_CLS_DROPPED, + + MVPP2_PORT_CTR_TX_BYTES, + MVPP2_PORT_CTR_TX_PACKETS, + MVPP2_PORT_CTR_TX_UCAST, + MVPP2_PORT_CTR_TX_ERRORS, +} mvpp2_port_counter_id_t; + +typedef enum +{ + MVPP2_RXQ_CTR_ENQ_DESC, + MVPP2_RXQ_CTR_DROP_FULLQ, + MVPP2_RXQ_CTR_DROP_EARLY, + MVPP2_RXQ_CTR_DROP_BM, +} mvpp2_rxq_counter_id_t; + +typedef enum +{ + MVPP2_TXQ_CTR_ENQ_DESC, + MVPP2_TXQ_CTR_ENQ_DEC_TO_DDR, + MVPP2_TXQ_CTR_ENQ_BUF_TO_DDR, + MVPP2_TXQ_CTR_DEQ_DESC, +} mvpp2_txq_counter_id_t; + +static vnet_dev_counter_t mvpp2_port_counters[] = { + VNET_DEV_CTR_RX_BYTES (MVPP2_PORT_CTR_RX_BYTES), + VNET_DEV_CTR_RX_PACKETS (MVPP2_PORT_CTR_RX_PACKETS), + VNET_DEV_CTR_RX_DROPS (MVPP2_PORT_CTR_RX_ERRORS), + VNET_DEV_CTR_VENDOR (MVPP2_PORT_CTR_RX_UCAST, RX, PACKETS, "unicast"), + VNET_DEV_CTR_VENDOR (MVPP2_PORT_CTR_RX_FULLQ_DROPPED, RX, PACKETS, + "fullq dropped"), + VNET_DEV_CTR_VENDOR (MVPP2_PORT_CTR_RX_BM_DROPPED, RX, PACKETS, + "bm dropped"), + VNET_DEV_CTR_VENDOR (MVPP2_PORT_CTR_RX_EARLY_DROPPED, RX, PACKETS, + "early dropped"), + VNET_DEV_CTR_VENDOR (MVPP2_PORT_CTR_RX_FIFO_DROPPED, RX, PACKETS, + "fifo dropped"), + VNET_DEV_CTR_VENDOR (MVPP2_PORT_CTR_RX_CLS_DROPPED, RX, PACKETS, + "cls dropped"), + + VNET_DEV_CTR_TX_BYTES (MVPP2_PORT_CTR_TX_BYTES), + VNET_DEV_CTR_TX_PACKETS (MVPP2_PORT_CTR_TX_PACKETS), + VNET_DEV_CTR_TX_DROPS (MVPP2_PORT_CTR_TX_ERRORS), + VNET_DEV_CTR_VENDOR (MVPP2_PORT_CTR_TX_UCAST, TX, PACKETS, "unicast"), +}; + +static vnet_dev_counter_t mvpp2_rxq_counters[] = { + VNET_DEV_CTR_VENDOR (MVPP2_RXQ_CTR_ENQ_DESC, RX, DESCRIPTORS, "enqueued"), + VNET_DEV_CTR_VENDOR (MVPP2_RXQ_CTR_DROP_FULLQ, RX, PACKETS, "drop fullQ"), + VNET_DEV_CTR_VENDOR (MVPP2_RXQ_CTR_DROP_EARLY, RX, PACKETS, "drop early"), + VNET_DEV_CTR_VENDOR (MVPP2_RXQ_CTR_DROP_BM, RX, PACKETS, "drop BM"), +}; + +static vnet_dev_counter_t mvpp2_txq_counters[] = { + VNET_DEV_CTR_VENDOR (MVPP2_TXQ_CTR_ENQ_DESC, TX, DESCRIPTORS, "enqueued"), + VNET_DEV_CTR_VENDOR (MVPP2_TXQ_CTR_DEQ_DESC, TX, PACKETS, "dequeued"), + VNET_DEV_CTR_VENDOR (MVPP2_TXQ_CTR_ENQ_BUF_TO_DDR, TX, BUFFERS, + "enq to DDR"), + VNET_DEV_CTR_VENDOR (MVPP2_TXQ_CTR_ENQ_DEC_TO_DDR, TX, DESCRIPTORS, + "enq to DDR"), +}; + +void +mvpp2_port_add_counters (vlib_main_t *vm, vnet_dev_port_t *port) +{ + vnet_dev_port_add_counters (vm, port, mvpp2_port_counters, + ARRAY_LEN (mvpp2_port_counters)); + + foreach_vnet_dev_port_rx_queue (q, port) + vnet_dev_rx_queue_add_counters (vm, q, mvpp2_rxq_counters, + ARRAY_LEN (mvpp2_rxq_counters)); + + foreach_vnet_dev_port_tx_queue (q, port) + vnet_dev_tx_queue_add_counters (vm, q, mvpp2_txq_counters, + ARRAY_LEN (mvpp2_txq_counters)); +} + +void +mvpp2_port_clear_counters (vlib_main_t *vm, vnet_dev_port_t *port) +{ + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + struct pp2_ppio_statistics stats; + pp2_ppio_get_statistics (mp->ppio, &stats, 1); +} + +void +mvpp2_rxq_clear_counters (vlib_main_t *vm, vnet_dev_rx_queue_t *q) +{ + mvpp2_port_t *mp = vnet_dev_get_port_data (q->port); + struct pp2_ppio_inq_statistics stats; + pp2_ppio_inq_get_statistics (mp->ppio, 0, q->queue_id, &stats, 1); +} + +void +mvpp2_txq_clear_counters (vlib_main_t *vm, vnet_dev_tx_queue_t *q) +{ + mvpp2_port_t *mp = vnet_dev_get_port_data (q->port); + struct pp2_ppio_inq_statistics stats; + pp2_ppio_inq_get_statistics (mp->ppio, 0, q->queue_id, &stats, 1); +} + +vnet_dev_rv_t +mvpp2_port_get_stats (vlib_main_t *vm, vnet_dev_port_t *port) +{ + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + struct pp2_ppio_statistics stats; + pp2_ppio_get_statistics (mp->ppio, &stats, 0); + + foreach_vnet_dev_counter (c, port->counter_main) + { + switch (c->user_data) + { + case MVPP2_PORT_CTR_RX_BYTES: + vnet_dev_counter_value_update (vm, c, stats.rx_bytes); + break; + case MVPP2_PORT_CTR_RX_PACKETS: + vnet_dev_counter_value_update (vm, c, stats.rx_packets); + break; + case MVPP2_PORT_CTR_RX_UCAST: + vnet_dev_counter_value_update (vm, c, stats.rx_unicast_packets); + break; + case MVPP2_PORT_CTR_RX_ERRORS: + vnet_dev_counter_value_update (vm, c, stats.rx_errors); + break; + case MVPP2_PORT_CTR_TX_BYTES: + vnet_dev_counter_value_update (vm, c, stats.tx_bytes); + break; + case MVPP2_PORT_CTR_TX_PACKETS: + vnet_dev_counter_value_update (vm, c, stats.tx_packets); + break; + case MVPP2_PORT_CTR_TX_UCAST: + vnet_dev_counter_value_update (vm, c, stats.tx_unicast_packets); + break; + case MVPP2_PORT_CTR_TX_ERRORS: + vnet_dev_counter_value_update (vm, c, stats.tx_errors); + break; + case MVPP2_PORT_CTR_RX_FULLQ_DROPPED: + vnet_dev_counter_value_update (vm, c, stats.rx_fullq_dropped); + break; + case MVPP2_PORT_CTR_RX_BM_DROPPED: + vnet_dev_counter_value_update (vm, c, stats.rx_bm_dropped); + break; + case MVPP2_PORT_CTR_RX_EARLY_DROPPED: + vnet_dev_counter_value_update (vm, c, stats.rx_early_dropped); + break; + case MVPP2_PORT_CTR_RX_FIFO_DROPPED: + vnet_dev_counter_value_update (vm, c, stats.rx_fifo_dropped); + break; + case MVPP2_PORT_CTR_RX_CLS_DROPPED: + vnet_dev_counter_value_update (vm, c, stats.rx_cls_dropped); + break; + + default: + ASSERT (0); + } + } + + foreach_vnet_dev_port_rx_queue (q, port) + { + struct pp2_ppio_inq_statistics stats; + pp2_ppio_inq_get_statistics (mp->ppio, 0, q->queue_id, &stats, 0); + + foreach_vnet_dev_counter (c, q->counter_main) + { + switch (c->user_data) + { + case MVPP2_RXQ_CTR_ENQ_DESC: + vnet_dev_counter_value_update (vm, c, stats.enq_desc); + break; + case MVPP2_RXQ_CTR_DROP_BM: + vnet_dev_counter_value_update (vm, c, stats.drop_bm); + break; + case MVPP2_RXQ_CTR_DROP_EARLY: + vnet_dev_counter_value_update (vm, c, stats.drop_early); + break; + case MVPP2_RXQ_CTR_DROP_FULLQ: + vnet_dev_counter_value_update (vm, c, stats.drop_fullq); + break; + default: + ASSERT (0); + } + } + } + + foreach_vnet_dev_port_tx_queue (q, port) + { + struct pp2_ppio_outq_statistics stats; + pp2_ppio_outq_get_statistics (mp->ppio, q->queue_id, &stats, 0); + + foreach_vnet_dev_counter (c, q->counter_main) + { + switch (c->user_data) + { + case MVPP2_TXQ_CTR_ENQ_DESC: + vnet_dev_counter_value_update (vm, c, stats.enq_desc); + break; + case MVPP2_TXQ_CTR_DEQ_DESC: + vnet_dev_counter_value_update (vm, c, stats.deq_desc); + break; + case MVPP2_TXQ_CTR_ENQ_BUF_TO_DDR: + vnet_dev_counter_value_update (vm, c, stats.enq_buf_to_ddr); + break; + case MVPP2_TXQ_CTR_ENQ_DEC_TO_DDR: + vnet_dev_counter_value_update (vm, c, stats.enq_dec_to_ddr); + break; + default: + ASSERT (0); + } + } + } + + return VNET_DEV_OK; +} diff --git a/src/plugins/dev_armada/pp2/format.c b/src/plugins/dev_armada/pp2/format.c new file mode 100644 index 00000000000..42c4114c512 --- /dev/null +++ b/src/plugins/dev_armada/pp2/format.c @@ -0,0 +1,198 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2023 Cisco Systems, Inc. + */ + +#include <vnet/vnet.h> +#include <vnet/dev/dev.h> +#include <vnet/dev/counters.h> +#include <vnet/dev/bus/platform.h> +#include <dev_armada/musdk.h> +#include <dev_armada/pp2/pp2.h> + +static inline u32 +mrvl_get_u32_bits (void *start, int offset, int first, int last) +{ + u32 value = *(u32 *) (((u8 *) start) + offset); + if ((last == 0) && (first == 31)) + return value; + value >>= last; + value &= (1 << (first - last + 1)) - 1; + return value; +} + +u8 * +format_pp2_ppio_link_info (u8 *s, va_list *args) +{ + struct pp2_ppio_link_info *li = va_arg (*args, struct pp2_ppio_link_info *); + + char *port_duplex[] = { + [MV_NET_LINK_DUPLEX_HALF] = "half", + [MV_NET_LINK_DUPLEX_FULL] = "full", + }; + + u32 port_speeds[] = { + [MV_NET_LINK_SPEED_10] = 10, [MV_NET_LINK_SPEED_100] = 100, + [MV_NET_LINK_SPEED_1000] = 1000, [MV_NET_LINK_SPEED_2500] = 2500, + [MV_NET_LINK_SPEED_10000] = 10000, + }; + + char *port_phy_modes[] = { + [MV_NET_PHY_MODE_NONE] = "NONE", + [MV_NET_PHY_MODE_MII] = "MII", + [MV_NET_PHY_MODE_GMII] = "GMII", + [MV_NET_PHY_MODE_SGMII] = "SGMII", + [MV_NET_PHY_MODE_TBI] = "TBI", + [MV_NET_PHY_MODE_REVMII] = "REVMII", + [MV_NET_PHY_MODE_RMII] = "RMII", + [MV_NET_PHY_MODE_RGMII] = "RGMII", + [MV_NET_PHY_MODE_RGMII_ID] = "RGMII_ID", + [MV_NET_PHY_MODE_RGMII_RXID] = "RGMII_RXID", + [MV_NET_PHY_MODE_RGMII_TXID] = "RGMII_TXID", + [MV_NET_PHY_MODE_RTBI] = "RTBI", + [MV_NET_PHY_MODE_SMII] = "SMII", + [MV_NET_PHY_MODE_XGMII] = "XGMII", + [MV_NET_PHY_MODE_MOCA] = "MOCA", + [MV_NET_PHY_MODE_QSGMII] = "QSGMII", + [MV_NET_PHY_MODE_XAUI] = "XAUI", + [MV_NET_PHY_MODE_RXAUI] = "RXAUI", + [MV_NET_PHY_MODE_KR] = "KR", + }; + + s = + format (s, "duplex %s speed %d up %d phy_mode %s", port_duplex[li->duplex], + port_speeds[li->speed], li->up, port_phy_modes[li->phy_mode]); + + return s; +} + +u8 * +format_mvpp2_port_status (u8 *s, va_list *args) +{ + vnet_dev_format_args_t __clib_unused *a = + va_arg (*args, vnet_dev_format_args_t *); + vnet_dev_port_t *port = va_arg (*args, vnet_dev_port_t *); + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + struct pp2_ppio_link_info li = {}; + + if (mp->ppio == 0 || pp2_ppio_get_link_info (mp->ppio, &li)) + return format (s, "link info not available"); + + return format (s, "%U", format_pp2_ppio_link_info, &li); +} + +u8 * +format_mvpp2_dev_info (u8 *s, va_list *args) +{ + vnet_dev_format_args_t __clib_unused *a = + va_arg (*args, vnet_dev_format_args_t *); + vnet_dev_t *dev = va_arg (*args, vnet_dev_t *); + mvpp2_device_t *md = vnet_dev_get_data (dev); + + format (s, "pp_id is %u", md->pp_id); + return s; +} + +#define foreach_pp2_rx_desc_field \ + _ (0x00, 6, 0, l3_offset) \ + _ (0x00, 12, 8, ip_hdlen) \ + _ (0x00, 14, 13, ec) \ + _ (0x00, 15, 15, es) \ + _ (0x00, 19, 16, pool_id) \ + _ (0x00, 21, 21, hwf_sync) \ + _ (0x00, 22, 22, l4_chk_ok) \ + _ (0x00, 23, 23, ip_frg) \ + _ (0x00, 24, 24, ipv4_hdr_err) \ + _ (0x00, 27, 25, l4_info) \ + _ (0x00, 30, 28, l3_info) \ + _ (0x00, 31, 31, buf_header) \ + _ (0x04, 5, 0, lookup_id) \ + _ (0x04, 8, 6, cpu_code) \ + _ (0x04, 9, 9, pppoe) \ + _ (0x04, 11, 10, l3_cast_info) \ + _ (0x04, 13, 12, l2_cast_info) \ + _ (0x04, 15, 14, vlan_info) \ + _ (0x04, 31, 16, byte_count) \ + _ (0x08, 11, 0, gem_port_id) \ + _ (0x08, 13, 12, color) \ + _ (0x08, 14, 14, gop_sop_u) \ + _ (0x08, 15, 15, key_hash_enable) \ + _ (0x08, 31, 16, l4chk) \ + _ (0x0c, 31, 0, timestamp) \ + _ (0x10, 31, 0, buf_phys_ptr_lo) \ + _ (0x14, 7, 0, buf_phys_ptr_hi) \ + _ (0x14, 31, 8, key_hash) \ + _ (0x18, 31, 0, buf_virt_ptr_lo) \ + _ (0x1c, 7, 0, buf_virt_ptr_hi) \ + _ (0x1c, 14, 8, buf_qset_no) \ + _ (0x1c, 15, 15, buf_type) \ + _ (0x1c, 21, 16, mod_dscp) \ + _ (0x1c, 24, 22, mod_pri) \ + _ (0x1c, 25, 25, mdscp) \ + _ (0x1c, 26, 26, mpri) \ + _ (0x1c, 27, 27, mgpid) \ + _ (0x1c, 31, 29, port_num) + +u8 * +format_mvpp2_rx_desc (u8 *s, va_list *args) + +{ + struct pp2_ppio_desc *d = va_arg (*args, struct pp2_ppio_desc *); + u32 indent = format_get_indent (s); + u32 r32; + +#define _(a, b, c, n) \ + r32 = mrvl_get_u32_bits (d, a, b, c); \ + if (r32 > 9) \ + s = format (s, "%s %u (0x%x)", #n, r32, r32); \ + else \ + s = format (s, "%s %u", #n, r32); \ + if (format_get_indent (s) > 72) \ + s = format (s, "\n%U", format_white_space, indent + 2); \ + else \ + s = format (s, " "); + + foreach_pp2_rx_desc_field; +#undef _ + return s; +} + +u8 * +format_mv_dsa_tag (u8 *s, va_list *args) +{ + mv_dsa_tag_t *tag = va_arg (*args, mv_dsa_tag_t *); + u32 cnt = 0; + +#define _(b, n) \ + if (#n[0] != '_') \ + s = format (s, "%s" #n " %u", cnt++ ? " " : "", tag->n); + foreach_mv_dsa_tag_field +#undef _ + return s; +} + +u8 * +format_mvpp2_rx_trace (u8 *s, va_list *args) +{ + vlib_main_t *vm = va_arg (*args, vlib_main_t *); + vlib_node_t *node = va_arg (*args, vlib_node_t *); + mvpp2_rx_trace_t *t = va_arg (*args, mvpp2_rx_trace_t *); + vnet_main_t *vnm = vnet_get_main (); + u32 indent = format_get_indent (s); + struct pp2_ppio_desc *d = &t->desc; + + if (t->sw_if_index != CLIB_U32_MAX) + s = format (s, "pp2: %U (%d) next-node %U", format_vnet_sw_if_index_name, + vnm, t->sw_if_index, t->sw_if_index, + format_vlib_next_node_name, vm, node->index, t->next_index); + else + s = format (s, "pp2: next-node %U", format_vlib_next_node_name, vm, + node->index, t->next_index); + + s = format (s, "\n%U%U", format_white_space, indent + 2, + format_mvpp2_rx_desc, d); + if (t->dsa_tag.as_u32) + s = format (s, "\n%Udsa tag: %U", format_white_space, indent + 2, + format_mv_dsa_tag, &t->dsa_tag); + + return s; +} diff --git a/src/plugins/dev_armada/pp2/init.c b/src/plugins/dev_armada/pp2/init.c new file mode 100644 index 00000000000..4333dbb352f --- /dev/null +++ b/src/plugins/dev_armada/pp2/init.c @@ -0,0 +1,421 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2023 Cisco Systems, Inc. + */ + +#include <vnet/vnet.h> +#include <vnet/dev/dev.h> +#include <vnet/dev/counters.h> +#include <vnet/dev/bus/platform.h> +#include <vppinfra/ring.h> +#include <dev_armada/musdk.h> +#include <dev_armada/pp2/pp2.h> +#include <vnet/ethernet/ethernet.h> +#include <vnet/plugin/plugin.h> +#include <vpp/app/version.h> + +#include <linux/if.h> +#include <sys/ioctl.h> + +#define MV_SYS_DMA_MEM_SZ (2 << 20) + +VLIB_REGISTER_LOG_CLASS (mvpp2_log, static) = { + .class_name = "armada", + .subclass_name = "init", +}; + +static int num_pp2_in_use = 0; +static int dma_mem_initialized = 0; +static int global_pp2_initialized = 0; + +#define _(f, n, s, d) \ + { .name = #n, .desc = d, .severity = VL_COUNTER_SEVERITY_##s }, + +vlib_error_desc_t mvpp2_rx_node_counters[] = { foreach_mvpp2_rx_node_counter }; +vlib_error_desc_t mvpp2_tx_node_counters[] = { foreach_mvpp2_tx_node_counter }; +#undef _ + +vnet_dev_node_t mvpp2_rx_node = { + .error_counters = mvpp2_rx_node_counters, + .n_error_counters = ARRAY_LEN (mvpp2_rx_node_counters), + .format_trace = format_mvpp2_rx_trace, +}; + +vnet_dev_node_t mvpp2_tx_node = { + .error_counters = mvpp2_tx_node_counters, + .n_error_counters = ARRAY_LEN (mvpp2_tx_node_counters), +}; + +static u8 * +mvpp2_probe (vlib_main_t *vm, vnet_dev_bus_index_t bus_index, void *dev_info) +{ + vnet_dev_bus_platform_device_info_t *di = dev_info; + + if (clib_dt_node_is_compatible (di->node, "marvell,armada-7k-pp22")) + return format (0, "Marvell Armada Packet Processor v2.2"); + return 0; +} +static void +mvpp2_global_deinit (vlib_main_t *vm, vnet_dev_t *dev) +{ + mvpp2_device_t *md = vnet_dev_get_data (dev); + log_debug (dev, ""); + if (--num_pp2_in_use == 0) + { + if (global_pp2_initialized) + { + for (u32 i = 0; i < ARRAY_LEN (md->thread); i++) + if (md->thread[i].bpool) + { + pp2_bpool_deinit (md->thread[i].bpool); + md->thread[i].bpool = 0; + } + for (u32 i = 0; i < ARRAY_LEN (md->hif); i++) + if (md->hif[i]) + { + pp2_hif_deinit (md->hif[i]); + md->hif[i] = 0; + } + + pp2_deinit (); + global_pp2_initialized = 0; + } + if (dma_mem_initialized) + { + mv_sys_dma_mem_destroy (); + log_debug (0, "mv_sys_dma_mem_destroy()"); + dma_mem_initialized = 0; + } + } +} + +static void +mvpp2_deinit (vlib_main_t *vm, vnet_dev_t *dev) +{ + log_debug (dev, ""); + mvpp2_global_deinit (vm, dev); +} + +static vnet_dev_rv_t +mvpp2_global_init (vlib_main_t *vm, vnet_dev_t *dev) +{ + mvpp2_device_t *md = vnet_dev_get_data (dev); + vnet_dev_rv_t rv = VNET_DEV_OK; + int mrv; + u16 free_hifs, free_bpools; + u16 n_threads = vlib_get_n_threads (); + + struct pp2_init_params init_params = { + .hif_reserved_map = 0xf, + .bm_pool_reserved_map = 0x7, + }; + + if (num_pp2_in_use++) + return rv; + + mrv = mv_sys_dma_mem_init (MV_SYS_DMA_MEM_SZ); + if (mrv < 0) + { + log_err (0, "mv_sys_dma_mem_init failed, err %d", mrv); + rv = VNET_DEV_ERR_INIT_FAILED; + goto done; + } + + dma_mem_initialized = 1; + log_debug (0, "mv_sys_dma_mem_init(%u) ok", MV_SYS_DMA_MEM_SZ); + + if ((mrv = pp2_init (&init_params))) + { + log_err (dev, "pp2_init failed, err %d", mrv); + rv = VNET_DEV_ERR_INIT_FAILED; + goto done; + } + + log_debug (dev, "pp2_init() ok"); + + free_hifs = pow2_mask (MVPP2_NUM_HIFS) ^ init_params.hif_reserved_map; + free_bpools = + pow2_mask (MVPP2_NUM_BPOOLS) ^ init_params.bm_pool_reserved_map; + + if (n_threads > count_set_bits (free_hifs)) + { + log_err (dev, "no enough HIFs (needed %u available %u)", n_threads, + count_set_bits (free_hifs)); + rv = VNET_DEV_ERR_INIT_FAILED; + goto done; + } + + for (u32 i = 0; i < n_threads; i++) + { + char match[16]; + u8 index; + struct pp2_hif_params hif_params = { + .match = match, + .out_size = 2048, + }; + struct pp2_bpool_params bpool_params = { + .match = match, + .buff_len = vlib_buffer_get_default_data_size (vm), + }; + + index = get_lowest_set_bit_index (free_hifs); + free_hifs ^= 1 << index; + snprintf (match, sizeof (match), "hif-%u", index); + + mrv = pp2_hif_init (&hif_params, md->hif + i); + if (mrv < 0) + { + log_err (dev, "pp2_hif_init failed for hif %u thread %u, err %d", + index, i, mrv); + rv = VNET_DEV_ERR_INIT_FAILED; + goto done; + } + log_debug (dev, "pp2_hif_init(hif %u, thread %u) ok", index, i); + + index = get_lowest_set_bit_index (free_bpools); + free_bpools ^= 1 << index; + snprintf (match, sizeof (match), "pool-%u:%u", md->pp_id, index); + + mrv = pp2_bpool_init (&bpool_params, &md->thread[i].bpool); + if (mrv < 0) + { + log_err (dev, "pp2_bpool_init failed for bpool %u thread %u, err %d", + index, i, mrv); + rv = VNET_DEV_ERR_INIT_FAILED; + goto done; + } + log_debug (dev, "pp2_bpool_init(bpool %u, thread %u) pool-%u:%u ok", + index, i, md->thread[i].bpool->pp2_id, + md->thread[i].bpool->id); + for (u32 j = 0; j < ARRAY_LEN (md->thread[0].bre); j++) + md->thread[i].bre[j].bpool = md->thread[i].bpool; + } + +done: + return rv; +} + +static vnet_dev_rv_t +mvpp2_init (vlib_main_t *vm, vnet_dev_t *dev) +{ + mvpp2_device_t *md = vnet_dev_get_data (dev); + vnet_dev_rv_t rv = VNET_DEV_OK; + vnet_dev_bus_platform_device_data_t *dd = vnet_dev_get_bus_data (dev); + clib_dt_node_t *sc; + clib_dt_node_t *sw = 0; + int pp_id = -1; + + if (!clib_dt_node_is_compatible (dd->node, "marvell,armada-7k-pp22")) + return VNET_DEV_ERR_NOT_SUPPORTED; + + sc = clib_dt_dereference_node (dd->node, "marvell,system-controller"); + + if (sc && vec_len (sc->path) > strlen ("/cpX/")) + { + if (strncmp ((char *) sc->path, "/cp0/", 4) == 0) + pp_id = 0; + else if (strncmp ((char *) sc->path, "/cp1/", 4) == 0) + pp_id = 1; + } + + if (pp_id < 0) + return VNET_DEV_ERR_UNKNOWN_DEVICE; + + foreach_clib_dt_tree_node (n, clib_dt_get_root_node (sc)) + if (clib_dt_node_is_compatible (n, "marvell,mv88e6190") || + clib_dt_node_is_compatible (n, "marvell,mv88e6393x")) + { + clib_dt_node_t *ports; + sw = n; + log_debug (dev, "found mv88e6190 compatible switch at %v", n->path); + ports = clib_dt_get_child_node (sw, "ports"); + foreach_clib_dt_child_node (pn, ports) + { + u32 reg = CLIB_U32_MAX; + char *label = "(no label)"; + clib_dt_property_t *p; + clib_dt_node_t *n; + + p = clib_dt_get_node_property_by_name (pn, "reg"); + if (p) + reg = clib_dt_property_get_u32 (p); + p = clib_dt_get_node_property_by_name (pn, "label"); + if (p) + label = clib_dt_property_get_string (p); + + log_debug (dev, "port %u label %s", reg, label); + + n = clib_dt_dereference_node (pn, "phy-handle"); + if (n) + log_debug (dev, " phy is %v", n->path); + + n = clib_dt_dereference_node (pn, "sfp"); + if (n) + log_debug (dev, " sfp is %v", n->path); + + n = clib_dt_dereference_node (pn, "ethernet"); + if (n) + log_debug (dev, " connected to %v", n->path); + + p = clib_dt_get_node_property_by_name (pn, "phy-mode"); + if (p) + log_debug (dev, " phy mode is %s", + clib_dt_property_get_string (p)); + } + } + + if ((mvpp2_global_init (vm, dev)) != VNET_DEV_OK) + return rv; + + md->pp_id = pp_id; + + foreach_clib_dt_child_node (cn, dd->node) + { + clib_dt_property_t *p; + char netdev_name[IFNAMSIZ]; + struct ifreq s = {}; + u8 ppio_id; + int fd, srv; + + p = clib_dt_get_node_property_by_name (cn, "port-id"); + + if (!clib_dt_property_is_u32 (p)) + continue; + + ppio_id = clib_dt_property_get_u32 (p); + log_debug (dev, "found port with ppio id %u", ppio_id); + + if (pp2_ppio_available (md->pp_id, ppio_id) == 0) + continue; + + if (pp2_netdev_get_ifname (md->pp_id, ppio_id, netdev_name) < 0) + { + log_warn (dev, "failed to get ifname, skipping port %u ", ppio_id); + continue; + } + + srv = -1; + if ((fd = socket (PF_INET, SOCK_DGRAM, IPPROTO_IP)) >= 0) + { + strcpy (s.ifr_name, netdev_name); + srv = ioctl (fd, SIOCGIFHWADDR, &s); + close (fd); + } + + if (srv < 0) + { + log_warn (dev, "unable to get hw address, skipping port %u", + ppio_id); + continue; + } + + log_debug (dev, "adding ppio %u (netdev name %s, hwaddr %U)", ppio_id, + netdev_name, format_ethernet_address, s.ifr_addr.sa_data); + + mvpp2_port_t mvpp2_port = { + .ppio_id = ppio_id, + }; + + if (sw) + { + clib_dt_node_t *ports = clib_dt_get_child_node (sw, "ports"); + if (ports) + foreach_clib_dt_child_node (sp, ports) + { + clib_dt_node_t *eth; + + eth = clib_dt_dereference_node (sp, "ethernet"); + + if (cn != eth) + continue; + + mvpp2_port.is_dsa = 1; + mvpp2_port.switch_node = sw; + mvpp2_port.switch_port_node = sp; + log_debug (dev, "port is connected to switch port %v", + sp->path); + break; + } + } + + vnet_dev_port_add_args_t port_add_args = { + .port = { + .attr = { + .type = VNET_DEV_PORT_TYPE_ETHERNET, + .max_rx_queues = PP2_PPIO_MAX_NUM_INQS, + .max_tx_queues = PP2_PPIO_MAX_NUM_OUTQS, + .max_supported_rx_frame_size = 9216, + .caps.secondary_interfaces = mvpp2_port.is_dsa != 0, + }, + .ops = { + .init = mvpp2_port_init, + .deinit = mvpp2_port_deinit, + .start = mvpp2_port_start, + .stop = mvpp2_port_stop, + .add_sec_if = mvpp2_port_add_sec_if, + .del_sec_if = mvpp2_port_del_sec_if, + .config_change = mvpp2_port_cfg_change, + .config_change_validate = mvpp2_port_cfg_change_validate, + .format_status = format_mvpp2_port_status, + .clear_counters = mvpp2_port_clear_counters, + }, + .data_size = sizeof (mvpp2_port_t), + .initial_data = &mvpp2_port, + .sec_if_args = VNET_DEV_ARGS ( + VNET_DEV_ARG_UINT32 (MVPP2_SEC_IF_ARG_DSA_SWITCH, "dsa_switch", "DSA source switch ID", .max= 31), + VNET_DEV_ARG_UINT32 (MVPP2_SEC_IF_ARG_DSA_PORT, "dsa_port", "DSA source switch port ID", .max = 31) + ), + }, + .rx_node = &mvpp2_rx_node, + .tx_node = &mvpp2_tx_node, + .rx_queue = { + .config = { + .data_size = sizeof (mvpp2_rxq_t), + .default_size = 512, + .multiplier = 32, + .min_size = 32, + .max_size = 4096, + .size_is_power_of_two = 1, + }, + .ops = { + .clear_counters = mvpp2_rxq_clear_counters, + }, + }, + .tx_queue = { + .config = { + .data_size = sizeof (mvpp2_txq_t), + .default_size = 512, + .multiplier = 32, + .min_size = 32, + .max_size = 4096, + .size_is_power_of_two = 1, + }, + .ops = { + .alloc = mvpp2_txq_alloc, + .free = mvpp2_txq_free, + .clear_counters = mvpp2_txq_clear_counters, + }, + }, + }; + + vnet_dev_set_hw_addr_eth_mac (&port_add_args.port.attr.hw_addr, + (u8 *) s.ifr_addr.sa_data); + + vnet_dev_port_add (vm, dev, ppio_id, &port_add_args); + } + + if (rv != VNET_DEV_OK) + mvpp2_deinit (vm, dev); + return rv; +} + +VNET_DEV_REGISTER_DRIVER (pp2) = { + .name = "mvpp2", + .bus = PLATFORM_BUS_NAME, + .device_data_sz = sizeof (mvpp2_device_t), + .ops = { + .init = mvpp2_init, + .deinit = mvpp2_deinit, + .probe = mvpp2_probe, + .format_info = format_mvpp2_dev_info, + }, +}; diff --git a/src/plugins/dev_armada/pp2/port.c b/src/plugins/dev_armada/pp2/port.c new file mode 100644 index 00000000000..63a212e80c2 --- /dev/null +++ b/src/plugins/dev_armada/pp2/port.c @@ -0,0 +1,363 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2023 Cisco Systems, Inc. + */ + +#include <vnet/vnet.h> +#include <vnet/ethernet/ethernet.h> +#include <vnet/dev/dev.h> +#include <vnet/dev/counters.h> +#include <vnet/dev/bus/platform.h> +#include <vppinfra/ring.h> +#include <dev_armada/musdk.h> +#include <dev_armada/pp2/pp2.h> + +VLIB_REGISTER_LOG_CLASS (mvpp2_log, static) = { + .class_name = "armada", + .subclass_name = "pp2-port", +}; + +vnet_dev_rv_t +mvpp2_port_init (vlib_main_t *vm, vnet_dev_port_t *port) +{ + vnet_dev_t *dev = port->dev; + mvpp2_device_t *md = vnet_dev_get_data (dev); + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + vnet_dev_rv_t rv = VNET_DEV_OK; + vnet_dev_rx_queue_t *rxq0 = vnet_dev_get_port_rx_queue_by_id (port, 0); + struct pp2_ppio_link_info li; + char match[16]; + int mrv; + + log_debug (port->dev, ""); + + snprintf (match, sizeof (match), "ppio-%d:%d", md->pp_id, port->port_id); + + struct pp2_ppio_params ppio_params = { + .match = match, + .type = PP2_PPIO_T_NIC, + .eth_start_hdr = mp->is_dsa ? PP2_PPIO_HDR_ETH_DSA : PP2_PPIO_HDR_ETH, + .inqs_params = { + .num_tcs = 1, + .tcs_params[0] = { + .pkt_offset = 0, + .num_in_qs = 1, + .inqs_params = &(struct pp2_ppio_inq_params) { .size = rxq0->size }, + .pools[0][0] = md->thread[rxq0->rx_thread_index].bpool, + }, + }, + }; + + foreach_vnet_dev_port_tx_queue (q, port) + { + struct pp2_ppio_outqs_params *oqs = &ppio_params.outqs_params; + oqs->outqs_params[q->queue_id].weight = 1; + oqs->outqs_params[q->queue_id].size = q->size; + oqs->num_outqs++; + } + + mrv = pp2_ppio_init (&ppio_params, &mp->ppio); + if (mrv) + { + rv = VNET_DEV_ERR_INIT_FAILED; + log_err (dev, "port %u ppio '%s' init failed, rv %d", port->port_id, + match, mrv); + goto done; + } + log_debug (dev, "port %u ppio '%s' init ok", port->port_id, match); + + mrv = pp2_ppio_get_link_info (mp->ppio, &li); + if (mrv) + { + rv = VNET_DEV_ERR_INIT_FAILED; + log_err (dev, "failed to get link info for port %u, rv %d", + port->port_id, mrv); + goto done; + } + + log_debug (dev, "port %u %U", port->port_id, format_pp2_ppio_link_info, &li); + + for (u32 i = 0; i < VLIB_FRAME_SIZE; i++) + mp->desc_ptrs[i] = mp->descs + i; + + mvpp2_port_add_counters (vm, port); + +done: + if (rv != VNET_DEV_OK) + mvpp2_port_stop (vm, port); + return rv; +} + +void +mvpp2_port_deinit (vlib_main_t *vm, vnet_dev_port_t *port) +{ + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + + log_debug (port->dev, ""); + + if (mp->ppio) + { + pp2_ppio_deinit (mp->ppio); + mp->ppio = 0; + } +} + +void +mvpp2_port_poll (vlib_main_t *vm, vnet_dev_port_t *port) +{ + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + vnet_dev_t *dev = port->dev; + vnet_dev_port_state_changes_t changes = {}; + struct pp2_ppio_link_info li; + int mrv; + + mrv = pp2_ppio_get_link_info (mp->ppio, &li); + + if (mrv) + { + log_debug (dev, "pp2_ppio_get_link_info: failed, rv %d", mrv); + return; + } + + if (mp->last_link_info.up != li.up) + { + changes.change.link_state = 1; + changes.link_state = li.up != 0; + log_debug (dev, "link state changed to %u", changes.link_state); + } + + if (mp->last_link_info.duplex != li.duplex) + { + changes.change.link_duplex = 1; + changes.full_duplex = li.duplex != 0; + log_debug (dev, "link full duplex changed to %u", changes.full_duplex); + } + + if (mp->last_link_info.speed != li.speed) + { + u32 speeds[] = { + [MV_NET_LINK_SPEED_AN] = 0, + [MV_NET_LINK_SPEED_10] = 10000, + [MV_NET_LINK_SPEED_100] = 100000, + [MV_NET_LINK_SPEED_1000] = 1000000, + [MV_NET_LINK_SPEED_2500] = 2500000, + [MV_NET_LINK_SPEED_10000] = 10000000, + }; + + if (li.speed < ARRAY_LEN (speeds)) + { + changes.change.link_speed = 1; + changes.link_speed = speeds[li.speed]; + log_debug (dev, "link speed changed to %u", changes.link_speed); + } + } + + if (changes.change.any) + { + mp->last_link_info = li; + vnet_dev_port_state_change (vm, port, changes); + } + + mvpp2_port_get_stats (vm, port); +} + +vnet_dev_rv_t +mvpp2_port_start (vlib_main_t *vm, vnet_dev_port_t *port) +{ + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + int mrv; + + log_debug (port->dev, ""); + + mrv = pp2_ppio_enable (mp->ppio); + if (mrv) + { + log_err (port->dev, "pp2_ppio_enable() failed, rv %d", mrv); + return VNET_DEV_ERR_NOT_READY; + } + + mp->is_enabled = 1; + + vnet_dev_poll_port_add (vm, port, 0.5, mvpp2_port_poll); + + return VNET_DEV_OK; +} + +void +mvpp2_port_stop (vlib_main_t *vm, vnet_dev_port_t *port) +{ + int rv; + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + + log_debug (port->dev, ""); + + if (mp->is_enabled) + { + vnet_dev_poll_port_remove (vm, port, mvpp2_port_poll); + + rv = pp2_ppio_disable (mp->ppio); + if (rv) + log_err (port->dev, "pp2_ppio_disable() failed, rv %d", rv); + + vnet_dev_port_state_change (vm, port, + (vnet_dev_port_state_changes_t){ + .change.link_state = 1, + .change.link_speed = 1, + .link_speed = 0, + .link_state = 0, + }); + mp->is_enabled = 0; + } +} + +vnet_dev_rv_t +mvpp2_port_add_sec_if (vlib_main_t *vm, vnet_dev_port_t *port, void *p) +{ + vnet_dev_port_interface_t *sif = p; + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + u32 port_id = CLIB_U32_MAX, switch_id = 0, index; + + if (mp->is_dsa == 0) + return VNET_DEV_ERR_NOT_SUPPORTED; + + foreach_vnet_dev_args (a, sif) + { + switch (a->id) + { + case MVPP2_SEC_IF_ARG_DSA_PORT: + if (a->val_set) + port_id = vnet_dev_arg_get_uint32 (a); + break; + case MVPP2_SEC_IF_ARG_DSA_SWITCH: + switch_id = vnet_dev_arg_get_uint32 (a); + break; + default: + break; + } + } + + if (port_id == CLIB_U32_MAX) + { + log_err (port->dev, "missing dsa_port argument"); + return VNET_DEV_ERR_INVALID_ARG; + } + + log_debug (port->dev, "switch %u port %u", switch_id, port_id); + + mv_dsa_tag_t tag = { + .tag_type = MV_DSA_TAG_TYPE_FROM_CPU, + .src_port_or_lag = port_id, + .src_dev = switch_id, + }; + + index = switch_id << 5 | port_id; + + sif->user_data = tag.as_u32; + uword_bitmap_set_bits_at_index (mp->valid_dsa_src_bitmap, index, 1); + mp->dsa_to_sec_if[index] = sif->index; + return VNET_DEV_OK; +} + +vnet_dev_rv_t +mvpp2_port_del_sec_if (vlib_main_t *vm, vnet_dev_port_t *port, void *p) +{ + vnet_dev_port_interface_t *sif = p; + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + mv_dsa_tag_t tag = { .as_u32 = sif->user_data }; + u32 index = tag.src_dev << 5 | tag.src_port_or_lag; + + log_debug (port->dev, "switch %u port %u", tag.src_dev, tag.src_port_or_lag); + + uword_bitmap_clear_bits_at_index (mp->valid_dsa_src_bitmap, index, 1); + return VNET_DEV_OK; +} + +vnet_dev_rv_t +mvpp2_port_cfg_change_validate (vlib_main_t *vm, vnet_dev_port_t *port, + vnet_dev_port_cfg_change_req_t *req) +{ + vnet_dev_rv_t rv = VNET_DEV_OK; + + switch (req->type) + { + case VNET_DEV_PORT_CFG_PROMISC_MODE: + case VNET_DEV_PORT_CFG_CHANGE_PRIMARY_HW_ADDR: + case VNET_DEV_PORT_CFG_ADD_SECONDARY_HW_ADDR: + case VNET_DEV_PORT_CFG_REMOVE_SECONDARY_HW_ADDR: + break; + + default: + rv = VNET_DEV_ERR_NOT_SUPPORTED; + }; + + return rv; +} + +vnet_dev_rv_t +mvpp2_port_cfg_change (vlib_main_t *vm, vnet_dev_port_t *port, + vnet_dev_port_cfg_change_req_t *req) +{ + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + vnet_dev_rv_t rv = VNET_DEV_OK; + eth_addr_t addr; + int mrv; + + switch (req->type) + { + + case VNET_DEV_PORT_CFG_PROMISC_MODE: + mrv = pp2_ppio_set_promisc (mp->ppio, req->promisc); + if (mrv) + { + log_err (port->dev, "pp2_ppio_set_promisc: failed, rv %d", mrv); + rv = VNET_DEV_ERR_INTERNAL; + } + else + log_debug (port->dev, "pp2_ppio_set_promisc: promisc %u", + req->promisc); + break; + + case VNET_DEV_PORT_CFG_CHANGE_PRIMARY_HW_ADDR: + clib_memcpy (&addr, req->addr.eth_mac, sizeof (addr)); + mrv = pp2_ppio_set_mac_addr (mp->ppio, addr); + if (mrv) + { + log_err (port->dev, "pp2_ppio_set_mac_addr: failed, rv %d", mrv); + rv = VNET_DEV_ERR_INTERNAL; + } + else + log_debug (port->dev, "pp2_ppio_set_mac_addr: %U added", + format_ethernet_address, &addr); + break; + + case VNET_DEV_PORT_CFG_ADD_SECONDARY_HW_ADDR: + clib_memcpy (&addr, req->addr.eth_mac, sizeof (addr)); + mrv = pp2_ppio_add_mac_addr (mp->ppio, addr); + if (mrv) + { + log_err (port->dev, "pp2_ppio_add_mac_addr: failed, rv %d", mrv); + rv = VNET_DEV_ERR_INTERNAL; + } + else + log_debug (port->dev, "pp2_ppio_add_mac_addr: %U added", + format_ethernet_address, &addr); + break; + + case VNET_DEV_PORT_CFG_REMOVE_SECONDARY_HW_ADDR: + clib_memcpy (&addr, req->addr.eth_mac, sizeof (addr)); + mrv = pp2_ppio_remove_mac_addr (mp->ppio, addr); + if (mrv) + { + log_err (port->dev, "pp2_ppio_remove_mac_addr: failed, rv %d", mrv); + rv = VNET_DEV_ERR_INTERNAL; + } + else + log_debug (port->dev, "pp2_ppio_remove_mac_addr: %U added", + format_ethernet_address, &addr); + break; + + default: + return VNET_DEV_ERR_NOT_SUPPORTED; + }; + + return rv; +} diff --git a/src/plugins/dev_armada/pp2/pp2.h b/src/plugins/dev_armada/pp2/pp2.h new file mode 100644 index 00000000000..160bfd20c5c --- /dev/null +++ b/src/plugins/dev_armada/pp2/pp2.h @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2023 Cisco Systems, Inc. + */ + +#ifndef _PP2_H_ +#define _PP2_H_ + +#include <vppinfra/clib.h> +#include <vppinfra/error_bootstrap.h> +#include <vppinfra/format.h> +#include <vppinfra/devicetree.h> +#include <vnet/vnet.h> +#include <vnet/dev/dev.h> + +#define MVCONF_DBG_LEVEL 0 +#define MVCONF_PP2_BPOOL_COOKIE_SIZE 32 +#define MVCONF_PP2_BPOOL_DMA_ADDR_SIZE 64 +#define MVCONF_DMA_PHYS_ADDR_T_SIZE 64 +#define MVCONF_SYS_DMA_UIO +#define MVCONF_TYPES_PUBLIC +#define MVCONF_DMA_PHYS_ADDR_T_PUBLIC + +#include "mv_std.h" +#include "env/mv_sys_dma.h" +#include "drivers/mv_pp2.h" +#include <drivers/mv_pp2_bpool.h> +#include <drivers/mv_pp2_ppio.h> + +#define MVPP2_NUM_HIFS 9 +#define MVPP2_NUM_BPOOLS 16 +#define MVPP2_MAX_THREADS 4 +#define MRVL_PP2_BUFF_BATCH_SZ 32 +#define MV_DSA_N_SRC 32 + +#define foreach_mv_dsa_tag_field \ + _ (12, vid) \ + _ (1, _zero13) \ + _ (3, pri) \ + _ (1, cfi_dei) \ + _ (1, _unused17) \ + _ (1, src_is_lag) \ + _ (5, src_port_or_lag) \ + _ (5, src_dev) \ + _ (1, src_tagged) \ + _ (2, tag_type) + +typedef enum +{ + MV_DSA_TAG_TYPE_TO_CPU = 0, + MV_DSA_TAG_TYPE_FROM_CPU = 1, + MV_DSA_TAG_TYPE_TO_SNIFFER = 2, + MV_DSA_TAG_TYPE_FORWARD = 3 +} mv_dsa_tag_type_t; + +typedef enum +{ + MVPP2_SEC_IF_ARG_DSA_SWITCH, + MVPP2_SEC_IF_ARG_DSA_PORT +} mvpp2_sec_if_args_t; + +typedef union +{ + struct + { +#define _(b, n) u32 (n) : (b); + foreach_mv_dsa_tag_field +#undef _ + }; + u32 as_u32; +} mv_dsa_tag_t; + +STATIC_ASSERT_SIZEOF (mv_dsa_tag_t, 4); + +static_always_inline mv_dsa_tag_t +mv_dsa_tag_read (void *p) +{ + return (mv_dsa_tag_t){ .as_u32 = clib_net_to_host_u32 (*(u32u *) p) }; +} + +static_always_inline void +mv_dsa_tag_write (void *p, mv_dsa_tag_t tag) +{ + ((mv_dsa_tag_t *) p)->as_u32 = clib_host_to_net_u32 (tag.as_u32); +} + +typedef struct +{ + u8 pp_id; + struct pp2_hif *hif[MVPP2_NUM_HIFS]; + struct + { + struct pp2_bpool *bpool; + struct buff_release_entry bre[MRVL_PP2_BUFF_BATCH_SZ]; + } thread[MVPP2_NUM_BPOOLS]; + +} mvpp2_device_t; + +typedef struct +{ + u8 is_enabled : 1; + u8 is_dsa : 1; + struct pp2_ppio *ppio; + u8 ppio_id; + struct pp2_ppio_link_info last_link_info; + clib_dt_node_t *switch_node; + clib_dt_node_t *switch_port_node; + + struct pp2_ppio_desc descs[VLIB_FRAME_SIZE]; + struct pp2_ppio_desc *desc_ptrs[VLIB_FRAME_SIZE]; + uword valid_dsa_src_bitmap[1024 / uword_bits]; + u16 dsa_to_sec_if[1024]; +} mvpp2_port_t; + +typedef struct +{ + u16 next; + u16 n_enq; + u32 *buffers; +} mvpp2_txq_t; + +typedef struct +{ +} mvpp2_rxq_t; + +typedef struct +{ + struct pp2_ppio_desc desc; + u32 sw_if_index; + u16 next_index; + mv_dsa_tag_t dsa_tag; +} mvpp2_rx_trace_t; + +/* counters.c */ +void mvpp2_port_add_counters (vlib_main_t *, vnet_dev_port_t *); +void mvpp2_port_clear_counters (vlib_main_t *, vnet_dev_port_t *); +void mvpp2_rxq_clear_counters (vlib_main_t *, vnet_dev_rx_queue_t *); +void mvpp2_txq_clear_counters (vlib_main_t *, vnet_dev_tx_queue_t *); +vnet_dev_rv_t mvpp2_port_get_stats (vlib_main_t *, vnet_dev_port_t *); + +/* format.c */ +format_function_t format_pp2_ppio_link_info; +format_function_t format_mvpp2_port_status; +format_function_t format_mvpp2_dev_info; +format_function_t format_mvpp2_rx_trace; +format_function_t format_mvpp2_rx_desc; +format_function_t format_mv_dsa_tag; + +/* port.c */ +vnet_dev_port_op_t mvpp2_port_init; +vnet_dev_port_op_no_rv_t mvpp2_port_deinit; +vnet_dev_port_op_t mvpp2_port_start; +vnet_dev_port_op_no_rv_t mvpp2_port_stop; +vnet_dev_port_op_with_ptr_t mvpp2_port_add_sec_if; +vnet_dev_port_op_with_ptr_t mvpp2_port_del_sec_if; +vnet_dev_rv_t mvpp2_port_cfg_change (vlib_main_t *, vnet_dev_port_t *, + vnet_dev_port_cfg_change_req_t *); +vnet_dev_rv_t +mvpp2_port_cfg_change_validate (vlib_main_t *, vnet_dev_port_t *, + vnet_dev_port_cfg_change_req_t *); + +/* queue.c */ +vnet_dev_tx_queue_op_t mvpp2_txq_alloc; +vnet_dev_tx_queue_op_no_rv_t mvpp2_txq_free; + +/* inline funcs */ + +#define log_debug(dev, f, ...) \ + vlib_log (VLIB_LOG_LEVEL_DEBUG, mvpp2_log.class, "%U" f, \ + format_vnet_dev_log, (dev), \ + clib_string_skip_prefix (__func__, "mvpp2_"), ##__VA_ARGS__) +#define log_info(dev, f, ...) \ + vlib_log (VLIB_LOG_LEVEL_INFO, mvpp2_log.class, "%U" f, \ + format_vnet_dev_log, (dev), 0, ##__VA_ARGS__) +#define log_notice(dev, f, ...) \ + vlib_log (VLIB_LOG_LEVEL_NOTICE, mvpp2_log.class, "%U" f, \ + format_vnet_dev_log, (dev), 0, ##__VA_ARGS__) +#define log_warn(dev, f, ...) \ + vlib_log (VLIB_LOG_LEVEL_WARNING, mvpp2_log.class, "%U" f, \ + format_vnet_dev_log, (dev), 0, ##__VA_ARGS__) +#define log_err(dev, f, ...) \ + vlib_log (VLIB_LOG_LEVEL_ERR, mvpp2_log.class, "%U" f, format_vnet_dev_log, \ + (dev), 0, ##__VA_ARGS__) + +#define foreach_mvpp2_tx_node_counter \ + _ (NO_FREE_SLOTS, no_free_slots, ERROR, "no free tx slots") \ + _ (PPIO_SEND, ppio_semd, ERROR, "pp2_ppio_send errors") \ + _ (PPIO_GET_NUM_OUTQ_DONE, ppio_get_num_outq_done, ERROR, \ + "pp2_ppio_get_num_outq_done errors") + +typedef enum +{ +#define _(f, n, s, d) MVPP2_TX_NODE_CTR_##f, + foreach_mvpp2_tx_node_counter +#undef _ +} mvpp2_tx_node_counter_t; + +#define foreach_mvpp2_rx_node_counter \ + _ (PPIO_RECV, ppio_recv, ERROR, "pp2_ppio_recv error") \ + _ (BPOOL_GET_NUM_BUFFS, bpool_get_num_bufs, ERROR, \ + "pp2_bpool_get_num_buffs error") \ + _ (BPOOL_PUT_BUFFS, bpool_put_buffs, ERROR, "pp2_bpool_put_buffs error") \ + _ (BUFFER_ALLOC, buffer_alloc, ERROR, "buffer alloc error") \ + _ (UNKNOWN_DSA_SRC, unknown_dsa_src, ERROR, "unknown DSA source") \ + _ (MAC_CE, mac_ce, ERROR, "MAC error (CRC error)") \ + _ (MAC_OR, mac_or, ERROR, "overrun error") \ + _ (MAC_RSVD, mac_rsvd, ERROR, "unknown MAC error") \ + _ (MAC_RE, mac_re, ERROR, "resource error") \ + _ (IP_HDR, ip_hdr, ERROR, "ip4 header error") + +typedef enum +{ +#define _(f, n, s, d) MVPP2_RX_NODE_CTR_##f, + foreach_mvpp2_rx_node_counter +#undef _ +} mvpp2_rx_node_counter_t; + +#endif /* _PP2_H_ */ diff --git a/src/plugins/dev_armada/pp2/queue.c b/src/plugins/dev_armada/pp2/queue.c new file mode 100644 index 00000000000..05015414816 --- /dev/null +++ b/src/plugins/dev_armada/pp2/queue.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2023 Cisco Systems, Inc. + */ + +#include <vnet/vnet.h> +#include <vnet/ethernet/ethernet.h> +#include <vnet/dev/dev.h> +#include <vnet/dev/counters.h> +#include <vnet/dev/bus/platform.h> +#include <vppinfra/ring.h> +#include <dev_armada/musdk.h> +#include <dev_armada/pp2/pp2.h> + +VLIB_REGISTER_LOG_CLASS (mvpp2_log, static) = { + .class_name = "armada", + .subclass_name = "pp2-queue", +}; + +vnet_dev_rv_t +mvpp2_txq_alloc (vlib_main_t *vm, vnet_dev_tx_queue_t *txq) +{ + vnet_dev_rv_t rv = VNET_DEV_OK; + mvpp2_txq_t *mtq = vnet_dev_get_tx_queue_data (txq); + log_debug (txq->port->dev, ""); + + ASSERT (mtq->buffers == 0); + if (mtq->buffers == 0) + { + u32 sz = sizeof (u32) * txq->size; + mtq->buffers = clib_mem_alloc_aligned (sz, CLIB_CACHE_LINE_BYTES); + clib_memset (mtq->buffers, 0, sz); + } + + return rv; +} + +void +mvpp2_txq_free (vlib_main_t *vm, vnet_dev_tx_queue_t *txq) +{ + mvpp2_txq_t *mtq = vnet_dev_get_tx_queue_data (txq); + + log_debug (txq->port->dev, ""); + if (mtq->buffers) + { + clib_mem_free (mtq->buffers); + mtq->buffers = 0; + } +} diff --git a/src/plugins/dev_armada/pp2/rx.c b/src/plugins/dev_armada/pp2/rx.c new file mode 100644 index 00000000000..5b0e8d35000 --- /dev/null +++ b/src/plugins/dev_armada/pp2/rx.c @@ -0,0 +1,269 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2024 Cisco Systems, Inc. + */ + +#include <vlib/vlib.h> +#include <vnet/dev/dev.h> +#include <vnet/ethernet/ethernet.h> +#include <vppinfra/vector/mask_compare.h> +#include <vppinfra/vector/compress.h> + +#include <dev_armada/pp2/pp2.h> + +static_always_inline vlib_buffer_t * +desc_to_vlib_buffer (vlib_main_t *vm, struct pp2_ppio_desc *d) +{ + return vlib_get_buffer (vm, pp2_ppio_inq_desc_get_cookie (d)); +} + +static_always_inline u64 +mrvl_pp2_rx_one_if (vlib_main_t *vm, vlib_node_runtime_t *node, + vnet_dev_rx_queue_t *rxq, + vnet_dev_rx_queue_if_rt_data_t *if_rt_data, + struct pp2_ppio_desc **desc_ptrs, u32 n_desc, + i32 current_data, i32 len_adj, mv_dsa_tag_t tag) +{ + vnet_main_t *vnm = vnet_get_main (); + u64 n_rx_bytes = 0; + vlib_buffer_t *b0, *b1; + u32 n_trace, n_left = n_desc; + u32 buffer_indices[VLIB_FRAME_SIZE], *bi = buffer_indices; + struct pp2_ppio_desc **dp = desc_ptrs; + u32 next_index = if_rt_data->next_index; + vlib_buffer_template_t bt = if_rt_data->buffer_template; + u32 sw_if_index = if_rt_data->sw_if_index; + + bt.current_data = current_data; + + for (; n_left >= 4; dp += 2, bi += 2, n_left -= 2) + { + clib_prefetch_store (desc_to_vlib_buffer (vm, dp[2])); + clib_prefetch_store (desc_to_vlib_buffer (vm, dp[3])); + b0 = desc_to_vlib_buffer (vm, dp[0]); + b1 = desc_to_vlib_buffer (vm, dp[1]); + bi[0] = pp2_ppio_inq_desc_get_cookie (dp[0]); + bi[1] = pp2_ppio_inq_desc_get_cookie (dp[1]); + b0->template = bt; + b1->template = bt; + + n_rx_bytes += b0->current_length = + pp2_ppio_inq_desc_get_pkt_len (dp[0]) + len_adj; + n_rx_bytes += b1->current_length = + pp2_ppio_inq_desc_get_pkt_len (dp[1]) + len_adj; + } + + for (; n_left; dp++, bi++, n_left--) + { + b0 = desc_to_vlib_buffer (vm, dp[0]); + bi[0] = pp2_ppio_inq_desc_get_cookie (dp[0]); + b0->template = bt; + + n_rx_bytes += b0->current_length = + pp2_ppio_inq_desc_get_pkt_len (dp[0]) + len_adj; + } + + /* trace */ + n_trace = vlib_get_trace_count (vm, node); + if (PREDICT_FALSE (n_trace > 0)) + { + for (u32 i = 0; i < n_desc && n_trace > 0; i++) + { + vlib_buffer_t *b = desc_to_vlib_buffer (vm, desc_ptrs[i]); + if (PREDICT_TRUE (vlib_trace_buffer (vm, node, next_index, b, + /* follow_chain */ 0))) + { + mvpp2_rx_trace_t *tr; + tr = vlib_add_trace (vm, node, b, sizeof (*tr)); + tr->desc = *desc_ptrs[i]; + tr->next_index = next_index; + tr->sw_if_index = sw_if_index; + tr->dsa_tag = tag; + n_trace--; + } + } + vlib_set_trace_count (vm, node, n_trace); + } + vlib_buffer_enqueue_to_single_next (vm, node, buffer_indices, next_index, + n_desc); + + vlib_increment_combined_counter ( + vnm->interface_main.combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, + vm->thread_index, sw_if_index, n_desc, n_rx_bytes); + + return n_rx_bytes; +} + +static_always_inline uword +mrvl_pp2_rx_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame, vnet_dev_rx_queue_t *rxq) +{ + vnet_dev_port_t *port = rxq->port; + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + mv_dsa_tag_t dsa_tags[VLIB_FRAME_SIZE]; + u16 n_desc = VLIB_FRAME_SIZE; + vlib_buffer_t *b; + u32 i; + + if (PREDICT_FALSE ( + pp2_ppio_recv (mp->ppio, 0, rxq->queue_id, mp->descs, &n_desc))) + { + vlib_error_count (vm, node->node_index, MVPP2_RX_NODE_CTR_PPIO_RECV, 1); + return 0; + } + + if (mp->is_dsa) + { + for (i = 0; i < n_desc; i++) + { + b = desc_to_vlib_buffer (vm, mp->descs + i); + u8 *start = b->data; + mv_dsa_tag_t tag = mv_dsa_tag_read (start + 14); + dsa_tags[i] = tag; + clib_memmove (start + 6, start + 2, 12); + } + + vlib_frame_bitmap_t avail_bmp = {}; + vlib_frame_bitmap_init (avail_bmp, n_desc); + u32 n_avail = n_desc; + + while (n_avail) + { + vlib_frame_bitmap_t selected_bmp = {}; + struct pp2_ppio_desc *sel_descs[VLIB_FRAME_SIZE]; + mv_dsa_tag_t tag; + u32 n_sel, index; + + tag = dsa_tags[vlib_frame_bitmap_find_first_set (avail_bmp)]; + index = tag.src_dev << 5 | tag.src_port_or_lag; + + clib_mask_compare_u32 (tag.as_u32, (u32 *) dsa_tags, selected_bmp, + n_desc); + n_sel = vlib_frame_bitmap_count_set_bits (selected_bmp); + n_avail -= n_sel; + + if (uword_bitmap_is_bit_set (mp->valid_dsa_src_bitmap, index)) + { + clib_compress_u64 ((uword *) sel_descs, (uword *) mp->desc_ptrs, + selected_bmp, n_desc); + mrvl_pp2_rx_one_if (vm, node, rxq, + vnet_dev_get_rx_queue_sec_if_rt_data ( + rxq, mp->dsa_to_sec_if[index]), + sel_descs, n_sel, 6, -4, tag); + } + else + { + u32 n_free = 0, buffer_indices[VLIB_FRAME_SIZE]; + + foreach_vlib_frame_bitmap_set_bit_index (i, selected_bmp) + buffer_indices[n_free++] = + pp2_ppio_inq_desc_get_cookie (mp->descs + i); + + u32 n_trace = vlib_get_trace_count (vm, node); + if (PREDICT_FALSE (n_trace > 0)) + { + foreach_vlib_frame_bitmap_set_bit_index (i, selected_bmp) + { + vlib_buffer_t *b = + desc_to_vlib_buffer (vm, mp->descs + i); + + if (PREDICT_TRUE (vlib_trace_buffer ( + vm, node, VNET_DEV_ETH_RX_PORT_NEXT_DROP, b, + /* follow_chain */ 0))) + { + mvpp2_rx_trace_t *tr; + tr = vlib_add_trace (vm, node, b, sizeof (*tr)); + tr->desc = mp->descs[i]; + tr->next_index = VNET_DEV_ETH_RX_PORT_NEXT_DROP; + tr->sw_if_index = CLIB_U32_MAX; + tr->dsa_tag = dsa_tags[i]; + n_trace--; + } + if (n_trace == 0) + break; + } + vlib_set_trace_count (vm, node, n_trace); + } + + vlib_buffer_free (vm, buffer_indices, n_free); + vlib_error_count (vm, node->node_index, + MVPP2_RX_NODE_CTR_UNKNOWN_DSA_SRC, 1); + } + } + } + else + { + mrvl_pp2_rx_one_if (vm, node, rxq, + vnet_dev_get_rx_queue_if_rt_data (rxq), + mp->desc_ptrs, n_desc, 2, 0, (mv_dsa_tag_t){}); + } + + return n_desc; +} + +static_always_inline void +mrvl_pp2_rx_refill (vlib_main_t *vm, vlib_node_runtime_t *node, + vnet_dev_rx_queue_t *rxq) +{ + vnet_dev_port_t *port = rxq->port; + vnet_dev_t *dev = port->dev; + mvpp2_device_t *md = vnet_dev_get_data (dev); + u32 thread_index = vm->thread_index; + struct pp2_hif *hif = md->hif[thread_index]; + struct pp2_bpool *bpool = md->thread[thread_index].bpool; + struct buff_release_entry *bre = md->thread[thread_index].bre; + u32 n_bufs, *bi; + + if (PREDICT_FALSE (pp2_bpool_get_num_buffs (bpool, &n_bufs))) + { + vlib_error_count (vm, node->node_index, + MVPP2_RX_NODE_CTR_BPOOL_GET_NUM_BUFFS, 1); + return; + } + + n_bufs = rxq->size - n_bufs; + while (n_bufs >= MRVL_PP2_BUFF_BATCH_SZ) + { + u16 n_alloc, i; + struct buff_release_entry *e = bre; + u32 buffer_indices[MRVL_PP2_BUFF_BATCH_SZ]; + + n_alloc = vlib_buffer_alloc (vm, buffer_indices, MRVL_PP2_BUFF_BATCH_SZ); + + if (PREDICT_FALSE (n_alloc == 0)) + { + vlib_error_count (vm, node->node_index, + MVPP2_RX_NODE_CTR_BUFFER_ALLOC, 1); + return; + } + + for (i = n_alloc, bi = buffer_indices; i--; e++, bi++) + { + + vlib_buffer_t *b = vlib_get_buffer (vm, bi[0]); + e->buff.addr = vlib_buffer_get_pa (vm, b) - 64; + e->buff.cookie = bi[0]; + } + + if (PREDICT_FALSE (pp2_bpool_put_buffs (hif, bre, &n_alloc))) + { + vlib_error_count (vm, node->node_index, + MVPP2_RX_NODE_CTR_BPOOL_PUT_BUFFS, 1); + vlib_buffer_free (vm, buffer_indices, n_alloc); + return; + } + + n_bufs -= n_alloc; + } +} + +VNET_DEV_NODE_FN (mvpp2_rx_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + u32 n_rx = 0; + foreach_vnet_dev_rx_queue_runtime (rxq, node) + { + n_rx += mrvl_pp2_rx_inline (vm, node, frame, rxq); + mrvl_pp2_rx_refill (vm, node, rxq); + } + return n_rx; +} diff --git a/src/plugins/dev_armada/pp2/tx.c b/src/plugins/dev_armada/pp2/tx.c new file mode 100644 index 00000000000..583eec71d60 --- /dev/null +++ b/src/plugins/dev_armada/pp2/tx.c @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2024 Cisco Systems, Inc. + */ + +#include <vlib/vlib.h> +#include <vnet/dev/dev.h> +#include <vnet/ethernet/ethernet.h> + +#include <dev_armada/pp2/pp2.h> + +VNET_DEV_NODE_FN (mvpp2_tx_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + vnet_dev_tx_node_runtime_t *rt = vnet_dev_get_tx_node_runtime (node); + vnet_dev_instance_t *ins = vnet_dev_get_dev_instance (rt->dev_instance); + vnet_dev_tx_queue_t *txq = rt->tx_queue; + vnet_dev_port_t *port = txq->port; + vnet_dev_t *dev = port->dev; + mvpp2_txq_t *mtq = vnet_dev_get_tx_queue_data (txq); + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + mvpp2_device_t *md = vnet_dev_get_data (dev); + u8 qid = txq->queue_id; + u32 *buffers = vlib_frame_vector_args (frame); + u32 n_vectors = frame->n_vectors, n_left; + u16 n_sent; + struct pp2_ppio *ppio = mp->ppio; + struct pp2_hif *hif = md->hif[vm->thread_index]; + struct pp2_ppio_desc descs[VLIB_FRAME_SIZE], *d = descs; + u16 sz = txq->size; + u16 mask = sz - 1; + i16 len_adj = 0; + + if (ins->is_primary_if == 0) + { + vnet_dev_port_interface_t *sif = + vnet_dev_port_get_sec_if_by_index (port, ins->sec_if_index); + + mv_dsa_tag_t tag = { .as_u32 = sif->user_data }; + + for (u32 i = 0; i < n_vectors; i++) + { + vlib_buffer_t *b = vlib_get_buffer (vm, buffers[i]); + u8 *start = vlib_buffer_get_current (b); + clib_memmove (start - 4, start, 12); + mv_dsa_tag_write (start + 8, tag); + } + len_adj = 4; + } + + if (mtq->n_enq) + { + u16 n_done = 0; + if (PREDICT_FALSE (pp2_ppio_get_num_outq_done (ppio, hif, qid, &n_done))) + vlib_error_count (vm, node->node_index, + MVPP2_TX_NODE_CTR_PPIO_GET_NUM_OUTQ_DONE, 1); + + if (n_done) + { + vlib_buffer_free_from_ring ( + vm, mtq->buffers, (mtq->next - mtq->n_enq) & mask, sz, n_done); + mtq->n_enq -= n_done; + } + } + + n_sent = clib_min (n_vectors, sz - mtq->n_enq); + + for (d = descs, n_left = n_sent; n_left; d++, buffers++, n_left--) + { + vlib_buffer_t *b0 = vlib_get_buffer (vm, buffers[0]); + u64 paddr = vlib_buffer_get_pa (vm, b0); + + pp2_ppio_outq_desc_reset (d); + pp2_ppio_outq_desc_set_phys_addr (d, paddr + b0->current_data - len_adj); + pp2_ppio_outq_desc_set_pkt_offset (d, 0); + pp2_ppio_outq_desc_set_pkt_len (d, b0->current_length + len_adj); + } + + buffers = vlib_frame_vector_args (frame); + + if (pp2_ppio_send (ppio, hif, qid, descs, &n_sent)) + { + n_sent = 0; + vlib_error_count (vm, node->node_index, MVPP2_TX_NODE_CTR_PPIO_SEND, 1); + } + else if (n_sent) + { + vlib_buffer_copy_indices_to_ring (mtq->buffers, buffers, + mtq->next & mask, sz, n_sent); + mtq->next += n_sent; + mtq->n_enq += n_sent; + } + + /* free unsent buffers */ + if (PREDICT_FALSE (n_sent != n_vectors)) + { + vlib_buffer_free (vm, buffers + n_sent, n_vectors - n_sent); + vlib_error_count (vm, node->node_index, MVPP2_TX_NODE_CTR_NO_FREE_SLOTS, + n_vectors - n_sent); + } + + return n_sent; +} diff --git a/src/plugins/dev_ena/rx_node.c b/src/plugins/dev_ena/rx_node.c index 41fc5b8c943..51c6dbce84c 100644 --- a/src/plugins/dev_ena/rx_node.c +++ b/src/plugins/dev_ena/rx_node.c @@ -251,7 +251,6 @@ ena_device_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node, vnet_dev_rx_queue_t *rxq) { ena_rxq_t *q = vnet_dev_get_rx_queue_data (rxq); - vnet_dev_port_t *port = rxq->port; vnet_main_t *vnm = vnet_get_main (); vlib_buffer_t *buffers[VLIB_FRAME_SIZE], **b; ena_rx_cdesc_status_t statuses[VLIB_FRAME_SIZE + 8]; @@ -260,13 +259,13 @@ ena_device_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node, u16 *csi; uword n_rx_packets = 0, n_rx_bytes = 0; vlib_frame_bitmap_t head_bmp = {}; - u32 sw_if_index = port->intf.sw_if_index; - u32 hw_if_index = port->intf.hw_if_index; + u32 sw_if_index = vnet_dev_get_rx_queue_if_sw_if_index (rxq); + u32 hw_if_index = vnet_dev_get_rx_queue_if_hw_if_index (rxq); u32 n_trace, n_deq, n_left; u32 cq_next = q->cq_next; - u32 next_index = rxq->next_index; + u32 next_index = vnet_dev_get_rx_queue_if_next_index (rxq); vlib_frame_t *next_frame; - vlib_buffer_template_t bt = rxq->buffer_template; + vlib_buffer_template_t bt = vnet_dev_get_rx_queue_if_buffer_template (rxq); u32 *bi; int maybe_chained; diff --git a/src/plugins/dev_iavf/port.c b/src/plugins/dev_iavf/port.c index 7e4200ab37c..a0530822688 100644 --- a/src/plugins/dev_iavf/port.c +++ b/src/plugins/dev_iavf/port.c @@ -91,7 +91,7 @@ iavf_port_init_rss (vlib_main_t *vm, vnet_dev_port_t *port) .key_len = keylen, }; - clib_memcpy (key->key, default_rss_key, sizeof (default_rss_key)); + clib_memcpy (key->key, default_rss_key, keylen); return iavf_vc_op_config_rss_key (vm, dev, key); } @@ -263,7 +263,7 @@ avf_msix_n_handler (vlib_main_t *vm, vnet_dev_t *dev, u16 line) iavf_reg_write (ad, IAVF_VFINT_DYN_CTLN (line), dyn_ctln_enabled.as_u32); vlib_node_set_interrupt_pending (vlib_get_main_by_index (line), - port->intf.rx_node_index); + vnet_dev_get_port_rx_node_index (port)); } vnet_dev_rv_t @@ -425,17 +425,20 @@ iavf_port_add_del_eth_addr (vlib_main_t *vm, vnet_dev_port_t *port, int is_primary) { iavf_port_t *ap = vnet_dev_get_port_data (port); - virtchnl_ether_addr_list_t al = { + u8 buffer[VIRTCHNL_MSG_SZ (virtchnl_ether_addr_list_t, list, 1)]; + virtchnl_ether_addr_list_t *al = (virtchnl_ether_addr_list_t *) buffer; + + *al = (virtchnl_ether_addr_list_t){ .vsi_id = ap->vsi_id, .num_elements = 1, .list[0].primary = is_primary ? 1 : 0, .list[0].extra = is_primary ? 0 : 1, }; - clib_memcpy (al.list[0].addr, addr, sizeof (al.list[0].addr)); + clib_memcpy (al->list[0].addr, addr, sizeof (al->list[0].addr)); - return is_add ? iavf_vc_op_add_eth_addr (vm, port->dev, &al) : - iavf_vc_op_del_eth_addr (vm, port->dev, &al); + return is_add ? iavf_vc_op_add_eth_addr (vm, port->dev, al) : + iavf_vc_op_del_eth_addr (vm, port->dev, al); } static vnet_dev_rv_t diff --git a/src/plugins/dev_iavf/rx_node.c b/src/plugins/dev_iavf/rx_node.c index ee6d7e8def0..bf650f9bfb9 100644 --- a/src/plugins/dev_iavf/rx_node.c +++ b/src/plugins/dev_iavf/rx_node.c @@ -249,14 +249,14 @@ iavf_device_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node, u32 thr_idx = vlib_get_thread_index (); iavf_rt_data_t *rtd = vnet_dev_get_rt_temp_space (vm); iavf_rxq_t *arq = vnet_dev_get_rx_queue_data (rxq); - vlib_buffer_template_t bt = rxq->buffer_template; + vlib_buffer_template_t bt = vnet_dev_get_rx_queue_if_buffer_template (rxq); u32 n_trace, n_rx_packets = 0, n_rx_bytes = 0; u16 n_tail_desc = 0; u64 or_qw1 = 0; u32 *bi, *to_next, n_left_to_next; - u32 next_index = rxq->next_index; - u32 sw_if_index = port->intf.sw_if_index; - u32 hw_if_index = port->intf.hw_if_index; + u32 next_index = vnet_dev_get_rx_queue_if_next_index (rxq); + u32 sw_if_index = vnet_dev_get_rx_queue_if_sw_if_index (rxq); + u32 hw_if_index = vnet_dev_get_rx_queue_if_hw_if_index (rxq); u16 next = arq->next; u16 size = rxq->size; u16 mask = size - 1; diff --git a/src/plugins/dev_iavf/virtchnl_funcs.h b/src/plugins/dev_iavf/virtchnl_funcs.h index e7f3901e0ee..0d4ab2835f4 100644 --- a/src/plugins/dev_iavf/virtchnl_funcs.h +++ b/src/plugins/dev_iavf/virtchnl_funcs.h @@ -9,6 +9,10 @@ #include <vnet/dev/dev.h> #include <dev_iavf/iavf.h> +/* The "+ 1" fakes a trailing element, but the driver requires that. + * Using this "wrong" macro is the easiest solution, as long as + * port.c uses buffer sized by the same macro as the functions here. + */ #define VIRTCHNL_MSG_SZ(s, e, n) STRUCT_OFFSET_OF (s, e[(n) + 1]) typedef struct diff --git a/src/plugins/dev_octeon/CMakeLists.txt b/src/plugins/dev_octeon/CMakeLists.txt index c6271ecdfba..6109de57a7d 100644 --- a/src/plugins/dev_octeon/CMakeLists.txt +++ b/src/plugins/dev_octeon/CMakeLists.txt @@ -36,6 +36,7 @@ add_vpp_plugin(dev_octeon tx_node.c flow.c counter.c + crypto.c MULTIARCH_SOURCES rx_node.c diff --git a/src/plugins/dev_octeon/crypto.c b/src/plugins/dev_octeon/crypto.c new file mode 100644 index 00000000000..7d3790f3ec9 --- /dev/null +++ b/src/plugins/dev_octeon/crypto.c @@ -0,0 +1,1782 @@ +/* + * Copyright (c) 2024 Marvell. + * SPDX-License-Identifier: Apache-2.0 + * https://spdx.org/licenses/Apache-2.0.html + */ + +#include <vnet/dev/dev.h> +#include <vnet/devices/devices.h> +#include <dev_octeon/octeon.h> +#include <dev_octeon/crypto.h> +#include <base/roc_api.h> +#include <common.h> + +oct_crypto_main_t oct_crypto_main; +oct_crypto_dev_t oct_crypto_dev; + +VLIB_REGISTER_LOG_CLASS (oct_log, static) = { + .class_name = "octeon", + .subclass_name = "crypto", +}; + +static_always_inline void +oct_map_keyindex_to_session (oct_crypto_sess_t *sess, u32 key_index, u8 type) +{ + oct_crypto_main_t *ocm = &oct_crypto_main; + oct_crypto_key_t *ckey; + + ckey = vec_elt_at_index (ocm->keys[type], key_index); + + ckey->sess = sess; + sess->key_index = key_index; +} + +static_always_inline oct_crypto_sess_t * +oct_crypto_session_alloc (vlib_main_t *vm, u8 type) +{ + extern oct_plt_init_param_t oct_plt_init_param; + oct_crypto_sess_t *addr = NULL; + oct_crypto_main_t *ocm; + oct_crypto_dev_t *ocd; + u32 size; + + ocm = &oct_crypto_main; + ocd = ocm->crypto_dev[type]; + + size = sizeof (oct_crypto_sess_t); + + addr = oct_plt_init_param.oct_plt_zmalloc (size, CLIB_CACHE_LINE_BYTES); + if (addr == NULL) + { + log_err (ocd->dev, "Failed to allocate crypto session memory"); + return NULL; + } + + return addr; +} + +static_always_inline i32 +oct_crypto_session_create (vlib_main_t *vm, vnet_crypto_key_index_t key_index, + int op_type) +{ + oct_crypto_main_t *ocm = &oct_crypto_main; + oct_crypto_sess_t *session; + vnet_crypto_key_t *key; + oct_crypto_key_t *ckey; + + key = vnet_crypto_get_key (key_index); + + if (key->type == VNET_CRYPTO_KEY_TYPE_LINK) + { + /* + * Read crypto or integ key session. And map link key index to same. + */ + if (key->index_crypto != UINT32_MAX) + { + ckey = vec_elt_at_index (ocm->keys[op_type], key->index_crypto); + session = ckey->sess; + } + else if (key->index_integ != UINT32_MAX) + { + ckey = vec_elt_at_index (ocm->keys[op_type], key->index_integ); + session = ckey->sess; + } + else + return -1; + } + else + { + session = oct_crypto_session_alloc (vm, op_type); + if (session == NULL) + return -1; + } + + oct_map_keyindex_to_session (session, key_index, op_type); + return 0; +} + +void +oct_crypto_key_del_handler (vlib_main_t *vm, vnet_crypto_key_index_t key_index) +{ + extern oct_plt_init_param_t oct_plt_init_param; + oct_crypto_main_t *ocm = &oct_crypto_main; + oct_crypto_key_t *ckey_linked; + oct_crypto_key_t *ckey; + + vec_validate (ocm->keys[VNET_CRYPTO_OP_TYPE_ENCRYPT], key_index); + + ckey = vec_elt_at_index (ocm->keys[VNET_CRYPTO_OP_TYPE_ENCRYPT], key_index); + if (ckey->sess) + { + /* + * If in case link algo is pointing to same sesison, reset the pointer. + */ + if (ckey->sess->key_index != key_index) + { + ckey_linked = vec_elt_at_index ( + ocm->keys[VNET_CRYPTO_OP_TYPE_ENCRYPT], ckey->sess->key_index); + ckey_linked->sess = NULL; + } + oct_plt_init_param.oct_plt_free (ckey->sess); + ckey->sess = NULL; + } + + ckey = vec_elt_at_index (ocm->keys[VNET_CRYPTO_OP_TYPE_DECRYPT], key_index); + if (ckey->sess) + { + /* + * If in case link algo is pointing to same sesison, reset the pointer. + */ + if (ckey->sess->key_index != key_index) + { + ckey_linked = vec_elt_at_index ( + ocm->keys[VNET_CRYPTO_OP_TYPE_DECRYPT], ckey->sess->key_index); + ckey_linked->sess = NULL; + } + + oct_plt_init_param.oct_plt_free (ckey->sess); + ckey->sess = NULL; + } +} + +void +oct_crypto_key_add_handler (vlib_main_t *vm, vnet_crypto_key_index_t key_index) +{ + oct_crypto_main_t *ocm = &oct_crypto_main; + oct_crypto_key_t *ckey; + oct_crypto_dev_t *ocd = &oct_crypto_dev; + + vec_validate (ocm->keys[VNET_CRYPTO_OP_TYPE_ENCRYPT], key_index); + ckey = vec_elt_at_index (ocm->keys[VNET_CRYPTO_OP_TYPE_ENCRYPT], key_index); + if (ckey->sess == NULL) + { + if (oct_crypto_session_create (vm, key_index, + VNET_CRYPTO_OP_TYPE_ENCRYPT)) + { + log_err (ocd->dev, "Unable to create crypto session"); + return; + } + } + + vec_validate (ocm->keys[VNET_CRYPTO_OP_TYPE_DECRYPT], key_index); + ckey = vec_elt_at_index (ocm->keys[VNET_CRYPTO_OP_TYPE_DECRYPT], key_index); + if (ckey->sess == NULL) + { + if (oct_crypto_session_create (vm, key_index, + VNET_CRYPTO_OP_TYPE_DECRYPT)) + { + log_err (ocd->dev, "Unable to create crypto session"); + return; + } + } +} + +void +oct_crypto_key_handler (vlib_main_t *vm, vnet_crypto_key_op_t kop, + vnet_crypto_key_index_t idx) +{ + oct_crypto_main_t *ocm = &oct_crypto_main; + + if (kop == VNET_CRYPTO_KEY_OP_DEL) + { + oct_crypto_key_del_handler (vm, idx); + return; + } + oct_crypto_key_add_handler (vm, idx); + + ocm->started = 1; +} + +static_always_inline void +oct_crypto_session_free (vlib_main_t *vm, oct_crypto_sess_t *sess) +{ + extern oct_plt_init_param_t oct_plt_init_param; + + oct_plt_init_param.oct_plt_free (sess); + return; +} + +#ifdef PLATFORM_OCTEON9 +static inline void +oct_cpt_inst_submit (struct cpt_inst_s *inst, uint64_t lmtline, + uint64_t io_addr) +{ + uint64_t lmt_status; + + do + { + /* Copy CPT command to LMTLINE */ + roc_lmt_mov64 ((void *) lmtline, inst); + + /* + * Make sure compiler does not reorder memcpy and ldeor. + * LMTST transactions are always flushed from the write + * buffer immediately, a DMB is not required to push out + * LMTSTs. + */ + asm volatile ("dmb oshst" : : : "memory"); + lmt_status = roc_lmt_submit_ldeor (io_addr); + } + while (lmt_status == 0); +} +#endif + +static_always_inline void +oct_crypto_burst_submit (oct_crypto_dev_t *crypto_dev, struct cpt_inst_s *inst, + u32 n_left) +{ + u64 lmt_base; + u64 io_addr; + u32 count; + +#ifdef PLATFORM_OCTEON9 + lmt_base = crypto_dev->lf.lmt_base; + io_addr = crypto_dev->lf.io_addr; + + for (count = 0; count < n_left; count++) + oct_cpt_inst_submit (inst + count, lmt_base, io_addr); +#else + u64 *lmt_line[OCT_MAX_LMT_SZ]; + u64 lmt_arg, core_lmt_id; + + lmt_base = crypto_dev->lmtline.lmt_base; + io_addr = crypto_dev->lmtline.io_addr; + + ROC_LMT_CPT_BASE_ID_GET (lmt_base, core_lmt_id); + + for (count = 0; count < 16; count++) + { + lmt_line[count] = OCT_CPT_LMT_GET_LINE_ADDR (lmt_base, count); + } + + while (n_left > OCT_MAX_LMT_SZ) + { + + /* + * Add a memory barrier so that LMTLINEs from the previous iteration + * can be reused for a subsequent transfer. + */ + asm volatile ("dmb oshst" ::: "memory"); + + lmt_arg = ROC_CN10K_CPT_LMT_ARG | (u64) core_lmt_id; + + for (count = 0; count < 16; count++) + { + roc_lmt_mov_seg ((void *) lmt_line[count], inst + count, + CPT_LMT_SIZE_COPY); + } + + /* Set number of LMTSTs, excluding the first */ + lmt_arg |= (OCT_MAX_LMT_SZ - 1) << 12; + + roc_lmt_submit_steorl (lmt_arg, io_addr); + + inst += OCT_MAX_LMT_SZ; + n_left -= OCT_MAX_LMT_SZ; + } + + if (n_left > 0) + { + /* + * Add a memory barrier so that LMTLINEs from the previous iteration + * can be reused for a subsequent transfer. + */ + asm volatile ("dmb oshst" ::: "memory"); + + lmt_arg = ROC_CN10K_CPT_LMT_ARG | (u64) core_lmt_id; + + for (count = 0; count < n_left; count++) + { + roc_lmt_mov_seg ((void *) lmt_line[count], inst + count, + CPT_LMT_SIZE_COPY); + } + + /* Set number of LMTSTs, excluding the first */ + lmt_arg |= (n_left - 1) << 12; + + roc_lmt_submit_steorl (lmt_arg, io_addr); + } +#endif +} + +static_always_inline uint32_t +oct_crypto_fill_sg_comp_from_iov (struct roc_sglist_comp *list, uint32_t i, + struct roc_se_iov_ptr *from, + uint32_t from_offset, uint32_t *psize, + struct roc_se_buf_ptr *extra_buf, + uint32_t extra_offset) +{ + uint32_t extra_len = extra_buf ? extra_buf->size : 0; + uint32_t size = *psize; + int32_t j; + + for (j = 0; j < from->buf_cnt; j++) + { + struct roc_sglist_comp *to = &list[i >> 2]; + uint32_t buf_sz = from->bufs[j].size; + void *vaddr = from->bufs[j].vaddr; + uint64_t e_vaddr; + uint32_t e_len; + + if (PREDICT_FALSE (from_offset)) + { + if (from_offset >= buf_sz) + { + from_offset -= buf_sz; + continue; + } + e_vaddr = (uint64_t) vaddr + from_offset; + e_len = clib_min ((buf_sz - from_offset), size); + from_offset = 0; + } + else + { + e_vaddr = (uint64_t) vaddr; + e_len = clib_min (buf_sz, size); + } + + to->u.s.len[i % 4] = clib_host_to_net_u16 (e_len); + to->ptr[i % 4] = clib_host_to_net_u64 (e_vaddr); + + if (extra_len && (e_len >= extra_offset)) + { + /* Break the data at given offset */ + uint32_t next_len = e_len - extra_offset; + uint64_t next_vaddr = e_vaddr + extra_offset; + + if (!extra_offset) + { + i--; + } + else + { + e_len = extra_offset; + size -= e_len; + to->u.s.len[i % 4] = clib_host_to_net_u16 (e_len); + } + + extra_len = clib_min (extra_len, size); + /* Insert extra data ptr */ + if (extra_len) + { + i++; + to = &list[i >> 2]; + to->u.s.len[i % 4] = clib_host_to_net_u16 (extra_len); + to->ptr[i % 4] = + clib_host_to_net_u64 ((uint64_t) extra_buf->vaddr); + size -= extra_len; + } + + next_len = clib_min (next_len, size); + /* insert the rest of the data */ + if (next_len) + { + i++; + to = &list[i >> 2]; + to->u.s.len[i % 4] = clib_host_to_net_u16 (next_len); + to->ptr[i % 4] = clib_host_to_net_u64 (next_vaddr); + size -= next_len; + } + extra_len = 0; + } + else + { + size -= e_len; + } + if (extra_offset) + extra_offset -= size; + i++; + + if (PREDICT_FALSE (!size)) + break; + } + + *psize = size; + return (uint32_t) i; +} + +static_always_inline u32 +oct_crypto_fill_sg2_comp_from_iov (struct roc_sg2list_comp *list, u32 i, + struct roc_se_iov_ptr *from, + u32 from_offset, u32 *psize, + struct roc_se_buf_ptr *extra_buf, + u32 extra_offset) +{ + u32 extra_len = extra_buf ? extra_buf->size : 0; + u32 size = *psize, buf_sz, e_len, next_len; + struct roc_sg2list_comp *to; + u64 e_vaddr, next_vaddr; + void *vaddr; + i32 j; + + for (j = 0; j < from->buf_cnt; j++) + { + to = &list[i / 3]; + buf_sz = from->bufs[j].size; + vaddr = from->bufs[j].vaddr; + + if (PREDICT_FALSE (from_offset)) + { + if (from_offset >= buf_sz) + { + from_offset -= buf_sz; + continue; + } + e_vaddr = (u64) vaddr + from_offset; + e_len = clib_min ((buf_sz - from_offset), size); + from_offset = 0; + } + else + { + e_vaddr = (u64) vaddr; + e_len = clib_min (buf_sz, size); + } + + to->u.s.len[i % 3] = (e_len); + to->ptr[i % 3] = (e_vaddr); + to->u.s.valid_segs = (i % 3) + 1; + + if (extra_len && (e_len >= extra_offset)) + { + /* Break the data at given offset */ + next_len = e_len - extra_offset; + next_vaddr = e_vaddr + extra_offset; + + if (!extra_offset) + i--; + else + { + e_len = extra_offset; + size -= e_len; + to->u.s.len[i % 3] = (e_len); + } + + extra_len = clib_min (extra_len, size); + /* Insert extra data ptr */ + if (extra_len) + { + i++; + to = &list[i / 3]; + to->u.s.len[i % 3] = (extra_len); + to->ptr[i % 3] = ((u64) extra_buf->vaddr); + to->u.s.valid_segs = (i % 3) + 1; + size -= extra_len; + } + + next_len = clib_min (next_len, size); + /* insert the rest of the data */ + if (next_len) + { + i++; + to = &list[i / 3]; + to->u.s.len[i % 3] = (next_len); + to->ptr[i % 3] = (next_vaddr); + to->u.s.valid_segs = (i % 3) + 1; + size -= next_len; + } + extra_len = 0; + } + else + size -= e_len; + + if (extra_offset) + extra_offset -= size; + + i++; + + if (PREDICT_FALSE (!size)) + break; + } + + *psize = size; + return (u32) i; +} + +static_always_inline uint32_t +oct_crypto_fill_sg_comp_from_buf (struct roc_sglist_comp *list, uint32_t i, + struct roc_se_buf_ptr *from) +{ + struct roc_sglist_comp *to = &list[i >> 2]; + + to->u.s.len[i % 4] = clib_host_to_net_u16 (from->size); + to->ptr[i % 4] = clib_host_to_net_u64 ((uint64_t) from->vaddr); + return ++i; +} + +static_always_inline uint32_t +oct_crypto_fill_sg_comp (struct roc_sglist_comp *list, uint32_t i, + uint64_t dma_addr, uint32_t size) +{ + struct roc_sglist_comp *to = &list[i >> 2]; + + to->u.s.len[i % 4] = clib_host_to_net_u16 (size); + to->ptr[i % 4] = clib_host_to_net_u64 (dma_addr); + return ++i; +} + +static_always_inline u32 +oct_crypto_fill_sg2_comp (struct roc_sg2list_comp *list, u32 index, + u64 dma_addr, u32 size) +{ + struct roc_sg2list_comp *to = &list[index / 3]; + + to->u.s.len[index % 3] = (size); + to->ptr[index % 3] = (dma_addr); + to->u.s.valid_segs = (index % 3) + 1; + return ++index; +} + +static_always_inline u32 +oct_crypto_fill_sg2_comp_from_buf (struct roc_sg2list_comp *list, u32 index, + struct roc_se_buf_ptr *from) +{ + struct roc_sg2list_comp *to = &list[index / 3]; + + to->u.s.len[index % 3] = (from->size); + to->ptr[index % 3] = ((u64) from->vaddr); + to->u.s.valid_segs = (index % 3) + 1; + return ++index; +} + +static_always_inline int __attribute__ ((unused)) +oct_crypto_sg_inst_prep (struct roc_se_fc_params *params, + struct cpt_inst_s *inst, uint64_t offset_ctrl, + const uint8_t *iv_s, int iv_len, uint8_t pack_iv, + uint8_t pdcp_alg_type, int32_t inputlen, + int32_t outputlen, uint32_t passthrough_len, + uint32_t req_flags, int pdcp_flag, int decrypt) +{ + struct roc_sglist_comp *gather_comp, *scatter_comp; + void *m_vaddr = params->meta_buf.vaddr; + struct roc_se_buf_ptr *aad_buf = NULL; + uint32_t mac_len = 0, aad_len = 0; + struct roc_se_ctx *se_ctx; + uint32_t i, g_size_bytes; + uint64_t *offset_vaddr; + uint32_t s_size_bytes; + uint8_t *in_buffer; + uint32_t size; + uint8_t *iv_d; + int ret = 0; + + se_ctx = params->ctx; + mac_len = se_ctx->mac_len; + + if (PREDICT_FALSE (req_flags & ROC_SE_VALID_AAD_BUF)) + { + /* We don't support both AAD and auth data separately */ + aad_len = params->aad_buf.size; + aad_buf = ¶ms->aad_buf; + } + + /* save space for iv */ + offset_vaddr = m_vaddr; + + m_vaddr = + (uint8_t *) m_vaddr + ROC_SE_OFF_CTRL_LEN + PLT_ALIGN_CEIL (iv_len, 8); + + inst->w4.s.opcode_major |= (uint64_t) ROC_DMA_MODE_SG; + + /* iv offset is 0 */ + *offset_vaddr = offset_ctrl; + + iv_d = ((uint8_t *) offset_vaddr + ROC_SE_OFF_CTRL_LEN); + + if (PREDICT_TRUE (iv_len)) + memcpy (iv_d, iv_s, iv_len); + + /* DPTR has SG list */ + + /* TODO Add error check if space will be sufficient */ + gather_comp = (struct roc_sglist_comp *) ((uint8_t *) m_vaddr + 8); + + /* + * Input Gather List + */ + i = 0; + + /* Offset control word followed by iv */ + + i = oct_crypto_fill_sg_comp (gather_comp, i, (uint64_t) offset_vaddr, + ROC_SE_OFF_CTRL_LEN + iv_len); + + /* Add input data */ + if (decrypt && (req_flags & ROC_SE_VALID_MAC_BUF)) + { + size = inputlen - iv_len - mac_len; + + if (PREDICT_TRUE (size)) + { + uint32_t aad_offset = aad_len ? passthrough_len : 0; + i = oct_crypto_fill_sg_comp_from_iov ( + gather_comp, i, params->src_iov, 0, &size, aad_buf, aad_offset); + if (PREDICT_FALSE (size)) + { + clib_warning ("Cryptodev: Insufficient buffer" + " space, size %d needed", + size); + return -1; + } + } + + if (mac_len) + i = + oct_crypto_fill_sg_comp_from_buf (gather_comp, i, ¶ms->mac_buf); + } + else + { + /* input data */ + size = inputlen - iv_len; + if (size) + { + uint32_t aad_offset = aad_len ? passthrough_len : 0; + i = oct_crypto_fill_sg_comp_from_iov ( + gather_comp, i, params->src_iov, 0, &size, aad_buf, aad_offset); + if (PREDICT_FALSE (size)) + { + clib_warning ("Cryptodev: Insufficient buffer space," + " size %d needed", + size); + return -1; + } + } + } + + in_buffer = m_vaddr; + ((uint16_t *) in_buffer)[0] = 0; + ((uint16_t *) in_buffer)[1] = 0; + ((uint16_t *) in_buffer)[2] = clib_host_to_net_u16 (i); + + g_size_bytes = ((i + 3) / 4) * sizeof (struct roc_sglist_comp); + /* + * Output Scatter List + */ + + i = 0; + scatter_comp = + (struct roc_sglist_comp *) ((uint8_t *) gather_comp + g_size_bytes); + + i = oct_crypto_fill_sg_comp ( + scatter_comp, i, (uint64_t) offset_vaddr + ROC_SE_OFF_CTRL_LEN, iv_len); + + /* Add output data */ + if ((!decrypt) && (req_flags & ROC_SE_VALID_MAC_BUF)) + { + size = outputlen - iv_len - mac_len; + if (size) + { + + uint32_t aad_offset = aad_len ? passthrough_len : 0; + + i = oct_crypto_fill_sg_comp_from_iov ( + scatter_comp, i, params->dst_iov, 0, &size, aad_buf, aad_offset); + if (PREDICT_FALSE (size)) + { + clib_warning ("Cryptodev: Insufficient buffer space," + " size %d needed", + size); + return -1; + } + } + + /* mac data */ + if (mac_len) + i = + oct_crypto_fill_sg_comp_from_buf (scatter_comp, i, ¶ms->mac_buf); + } + else + { + /* Output including mac */ + size = outputlen - iv_len; + + if (size) + { + uint32_t aad_offset = aad_len ? passthrough_len : 0; + + i = oct_crypto_fill_sg_comp_from_iov ( + scatter_comp, i, params->dst_iov, 0, &size, aad_buf, aad_offset); + + if (PREDICT_FALSE (size)) + { + clib_warning ("Cryptodev: Insufficient buffer space," + " size %d needed", + size); + return -1; + } + } + } + ((uint16_t *) in_buffer)[3] = clib_host_to_net_u16 (i); + s_size_bytes = ((i + 3) / 4) * sizeof (struct roc_sglist_comp); + + size = g_size_bytes + s_size_bytes + ROC_SG_LIST_HDR_SIZE; + + /* This is DPTR len in case of SG mode */ + inst->w4.s.dlen = size; + + if (PREDICT_FALSE (size > ROC_SG_MAX_DLEN_SIZE)) + { + clib_warning ( + "Cryptodev: Exceeds max supported components. Reduce segments"); + ret = -1; + } + + inst->dptr = (uint64_t) in_buffer; + return ret; +} + +static_always_inline int __attribute__ ((unused)) +oct_crypto_sg2_inst_prep (struct roc_se_fc_params *params, + struct cpt_inst_s *inst, u64 offset_ctrl, + const u8 *iv_s, int iv_len, u8 pack_iv, + u8 pdcp_alg_type, i32 inputlen, i32 outputlen, + u32 passthrough_len, u32 req_flags, int pdcp_flag, + int decrypt) +{ + u32 mac_len = 0, aad_len = 0, size, index, g_size_bytes; + struct roc_sg2list_comp *gather_comp, *scatter_comp; + void *m_vaddr = params->meta_buf.vaddr; + struct roc_se_buf_ptr *aad_buf = NULL; + union cpt_inst_w5 cpt_inst_w5; + union cpt_inst_w6 cpt_inst_w6; + u16 scatter_sz, gather_sz; + struct roc_se_ctx *se_ctx; + u64 *offset_vaddr; + int ret = 0; + u8 *iv_d; + + se_ctx = params->ctx; + mac_len = se_ctx->mac_len; + + if (PREDICT_FALSE (req_flags & ROC_SE_VALID_AAD_BUF)) + { + /* We don't support both AAD and auth data separately */ + aad_len = params->aad_buf.size; + aad_buf = ¶ms->aad_buf; + } + + /* save space for iv */ + offset_vaddr = m_vaddr; + + m_vaddr = (u8 *) m_vaddr + ROC_SE_OFF_CTRL_LEN + PLT_ALIGN_CEIL (iv_len, 8); + + inst->w4.s.opcode_major |= (u64) ROC_DMA_MODE_SG; + + /* This is DPTR len in case of SG mode */ + inst->w4.s.dlen = inputlen + ROC_SE_OFF_CTRL_LEN; + + /* iv offset is 0 */ + *offset_vaddr = offset_ctrl; + iv_d = ((u8 *) offset_vaddr + ROC_SE_OFF_CTRL_LEN); + + if (PREDICT_TRUE (iv_len)) + clib_memcpy (iv_d, iv_s, iv_len); + + /* DPTR has SG list */ + + gather_comp = (struct roc_sg2list_comp *) ((u8 *) m_vaddr); + + /* + * Input Gather List + */ + index = 0; + + /* Offset control word followed by iv */ + + index = oct_crypto_fill_sg2_comp (gather_comp, index, (u64) offset_vaddr, + ROC_SE_OFF_CTRL_LEN + iv_len); + + /* Add input data */ + if (decrypt && (req_flags & ROC_SE_VALID_MAC_BUF)) + { + size = inputlen - iv_len - mac_len; + if (size) + { + /* input data only */ + u32 aad_offset = aad_len ? passthrough_len : 0; + + index = oct_crypto_fill_sg2_comp_from_iov (gather_comp, index, + params->src_iov, 0, &size, + aad_buf, aad_offset); + + if (PREDICT_FALSE (size)) + { + clib_warning ("Cryptodev: Insufficient buffer" + " space, size %d needed", + size); + return -1; + } + } + + /* mac data */ + if (mac_len) + index = oct_crypto_fill_sg2_comp_from_buf (gather_comp, index, + ¶ms->mac_buf); + } + else + { + /* input data */ + size = inputlen - iv_len; + if (size) + { + u32 aad_offset = aad_len ? passthrough_len : 0; + + index = oct_crypto_fill_sg2_comp_from_iov (gather_comp, index, + params->src_iov, 0, &size, + aad_buf, aad_offset); + if (PREDICT_FALSE (size)) + { + clib_warning ("Cryptodev: Insufficient buffer space," + " size %d needed", + size); + return -1; + } + } + } + + gather_sz = (index + 2) / 3; + g_size_bytes = gather_sz * sizeof (struct roc_sg2list_comp); + + /* + * Output Scatter List + */ + + index = 0; + scatter_comp = + (struct roc_sg2list_comp *) ((u8 *) gather_comp + g_size_bytes); + + index = oct_crypto_fill_sg2_comp ( + scatter_comp, index, (u64) offset_vaddr + ROC_SE_OFF_CTRL_LEN, iv_len); + + /* Add output data */ + if ((!decrypt) && (req_flags & ROC_SE_VALID_MAC_BUF)) + { + size = outputlen - iv_len - mac_len; + if (size) + { + + u32 aad_offset = aad_len ? passthrough_len : 0; + + index = oct_crypto_fill_sg2_comp_from_iov (scatter_comp, index, + params->dst_iov, 0, &size, + aad_buf, aad_offset); + if (PREDICT_FALSE (size)) + { + clib_warning ("Cryptodev: Insufficient buffer space," + " size %d needed", + size); + return -1; + } + } + + /* mac data */ + if (mac_len) + index = oct_crypto_fill_sg2_comp_from_buf (scatter_comp, index, + ¶ms->mac_buf); + } + else + { + /* Output including mac */ + size = outputlen - iv_len; + if (size) + { + u32 aad_offset = aad_len ? passthrough_len : 0; + + index = oct_crypto_fill_sg2_comp_from_iov (scatter_comp, index, + params->dst_iov, 0, &size, + aad_buf, aad_offset); + + if (PREDICT_FALSE (size)) + { + clib_warning ("Cryptodev: Insufficient buffer space," + " size %d needed", + size); + return -1; + } + } + } + + scatter_sz = (index + 2) / 3; + + cpt_inst_w5.s.gather_sz = gather_sz; + cpt_inst_w6.s.scatter_sz = scatter_sz; + + cpt_inst_w5.s.dptr = (u64) gather_comp; + cpt_inst_w6.s.rptr = (u64) scatter_comp; + + inst->w5.u64 = cpt_inst_w5.u64; + inst->w6.u64 = cpt_inst_w6.u64; + + if (PREDICT_FALSE ((scatter_sz >> 4) || (gather_sz >> 4))) + { + clib_warning ( + "Cryptodev: Exceeds max supported components. Reduce segments"); + ret = -1; + } + + return ret; +} + +static_always_inline int +oct_crypto_cpt_hmac_prep (u32 flags, u64 d_offs, u64 d_lens, + struct roc_se_fc_params *fc_params, + struct cpt_inst_s *inst, u8 is_decrypt) +{ + u32 encr_data_len, auth_data_len, aad_len = 0; + i32 inputlen, outputlen, enc_dlen, auth_dlen; + u32 encr_offset, auth_offset, iv_offset = 0; + union cpt_inst_w4 cpt_inst_w4; + u32 cipher_type; + struct roc_se_ctx *se_ctx; + u32 passthrough_len = 0; + const u8 *src = NULL; + u64 offset_ctrl; + u8 iv_len = 16; + u8 op_minor; + u32 mac_len; + int ret; + + encr_offset = ROC_SE_ENCR_OFFSET (d_offs); + auth_offset = ROC_SE_AUTH_OFFSET (d_offs); + encr_data_len = ROC_SE_ENCR_DLEN (d_lens); + auth_data_len = ROC_SE_AUTH_DLEN (d_lens); + + if (PREDICT_FALSE (flags & ROC_SE_VALID_AAD_BUF)) + { + /* We don't support both AAD and auth data separately */ + auth_data_len = 0; + auth_offset = 0; + aad_len = fc_params->aad_buf.size; + } + + se_ctx = fc_params->ctx; + cipher_type = se_ctx->enc_cipher; + mac_len = se_ctx->mac_len; + cpt_inst_w4.u64 = se_ctx->template_w4.u64; + op_minor = cpt_inst_w4.s.opcode_minor; + + if (PREDICT_FALSE (flags & ROC_SE_VALID_AAD_BUF)) + { + /* + * When AAD is given, data above encr_offset is pass through + * Since AAD is given as separate pointer and not as offset, + * this is a special case as we need to fragment input data + * into passthrough + encr_data and then insert AAD in between. + */ + passthrough_len = encr_offset; + auth_offset = passthrough_len + iv_len; + encr_offset = passthrough_len + aad_len + iv_len; + auth_data_len = aad_len + encr_data_len; + } + else + { + encr_offset += iv_len; + auth_offset += iv_len; + } + + auth_dlen = auth_offset + auth_data_len; + enc_dlen = encr_data_len + encr_offset; + + cpt_inst_w4.s.opcode_major = ROC_SE_MAJOR_OP_FC; + + if (is_decrypt) + { + cpt_inst_w4.s.opcode_minor |= ROC_SE_FC_MINOR_OP_DECRYPT; + + if (auth_dlen > enc_dlen) + { + inputlen = auth_dlen + mac_len; + outputlen = auth_dlen; + } + else + { + inputlen = enc_dlen + mac_len; + outputlen = enc_dlen; + } + } + else + { + cpt_inst_w4.s.opcode_minor |= ROC_SE_FC_MINOR_OP_ENCRYPT; + + /* Round up to 16 bytes alignment */ + if (PREDICT_FALSE (encr_data_len & 0xf)) + { + if (PREDICT_TRUE (cipher_type == ROC_SE_AES_CBC) || + (cipher_type == ROC_SE_DES3_CBC)) + enc_dlen = PLT_ALIGN_CEIL (encr_data_len, 8) + encr_offset; + } + + /* + * auth_dlen is larger than enc_dlen in Authentication cases + * like AES GMAC Authentication + */ + if (PREDICT_FALSE (auth_dlen > enc_dlen)) + { + inputlen = auth_dlen; + outputlen = auth_dlen + mac_len; + } + else + { + inputlen = enc_dlen; + outputlen = enc_dlen + mac_len; + } + } + + if (op_minor & ROC_SE_FC_MINOR_OP_HMAC_FIRST) + outputlen = enc_dlen; + + cpt_inst_w4.s.param1 = encr_data_len; + cpt_inst_w4.s.param2 = auth_data_len; + + if (PREDICT_FALSE ((encr_offset >> 16) || (iv_offset >> 8) || + (auth_offset >> 8))) + { + clib_warning ("Cryptodev: Offset not supported"); + clib_warning ( + "Cryptodev: enc_offset: %d, iv_offset : %d, auth_offset: %d", + encr_offset, iv_offset, auth_offset); + return -1; + } + + offset_ctrl = clib_host_to_net_u64 ( + ((u64) encr_offset << 16) | ((u64) iv_offset << 8) | ((u64) auth_offset)); + + src = fc_params->iv_buf; + + inst->w4.u64 = cpt_inst_w4.u64; + +#ifdef PLATFORM_OCTEON9 + ret = oct_crypto_sg_inst_prep (fc_params, inst, offset_ctrl, src, iv_len, 0, + 0, inputlen, outputlen, passthrough_len, + flags, 0, is_decrypt); +#else + ret = oct_crypto_sg2_inst_prep (fc_params, inst, offset_ctrl, src, iv_len, 0, + 0, inputlen, outputlen, passthrough_len, + flags, 0, is_decrypt); +#endif + + if (PREDICT_FALSE (ret)) + return -1; + + return 0; +} + +static_always_inline int +oct_crypto_fill_fc_params (oct_crypto_sess_t *sess, struct cpt_inst_s *inst, + const bool is_aead, u8 aad_length, u8 *payload, + vnet_crypto_async_frame_elt_t *elts, void *mdata, + u32 cipher_data_length, u32 cipher_data_offset, + u32 auth_data_length, u32 auth_data_offset, + vlib_buffer_t *b, u16 adj_len) +{ + struct roc_se_fc_params fc_params = { 0 }; + struct roc_se_ctx *ctx = &sess->cpt_ctx; + u64 d_offs = 0, d_lens = 0; + vlib_buffer_t *buffer = b; + u32 flags = 0, index = 0; + u8 op_minor = 0, cpt_op; + char src[SRC_IOV_SIZE]; + u32 *iv_buf; + + cpt_op = sess->cpt_op; + + if (is_aead) + { + flags |= ROC_SE_VALID_IV_BUF; + iv_buf = (u32 *) elts->iv; + iv_buf[3] = clib_host_to_net_u32 (0x1); + fc_params.iv_buf = elts->iv; + + d_offs = cipher_data_offset; + d_offs = d_offs << 16; + + d_lens = cipher_data_length; + d_lens = d_lens << 32; + + fc_params.aad_buf.vaddr = elts->aad; + fc_params.aad_buf.size = aad_length; + flags |= ROC_SE_VALID_AAD_BUF; + + if (sess->cpt_ctx.mac_len) + { + flags |= ROC_SE_VALID_MAC_BUF; + fc_params.mac_buf.size = sess->cpt_ctx.mac_len; + fc_params.mac_buf.vaddr = elts->tag; + } + } + else + { + op_minor = ctx->template_w4.s.opcode_minor; + + flags |= ROC_SE_VALID_IV_BUF; + + fc_params.iv_buf = elts->iv; + + d_offs = cipher_data_offset; + d_offs = (d_offs << 16) | auth_data_offset; + + d_lens = cipher_data_length; + d_lens = (d_lens << 32) | auth_data_length; + + if (PREDICT_TRUE (sess->cpt_ctx.mac_len)) + { + if (!(op_minor & ROC_SE_FC_MINOR_OP_HMAC_FIRST)) + { + flags |= ROC_SE_VALID_MAC_BUF; + fc_params.mac_buf.size = sess->cpt_ctx.mac_len; + fc_params.mac_buf.vaddr = elts->digest; + } + } + } + + fc_params.ctx = &sess->cpt_ctx; + + fc_params.src_iov = (void *) src; + + fc_params.src_iov->bufs[index].vaddr = payload; + fc_params.src_iov->bufs[index].size = b->current_length - adj_len; + index++; + + while (buffer->flags & VLIB_BUFFER_NEXT_PRESENT) + { + buffer = vlib_get_buffer (vlib_get_main (), buffer->next_buffer); + fc_params.src_iov->bufs[index].vaddr = + buffer->data + buffer->current_data; + fc_params.src_iov->bufs[index].size = buffer->current_length; + index++; + } + + fc_params.src_iov->buf_cnt = index; + + fc_params.dst_iov = (void *) src; + + fc_params.meta_buf.vaddr = mdata; + fc_params.meta_buf.size = OCT_SCATTER_GATHER_BUFFER_SIZE; + + return oct_crypto_cpt_hmac_prep (flags, d_offs, d_lens, &fc_params, inst, + cpt_op); +} + +static_always_inline u64 +oct_cpt_inst_w7_get (oct_crypto_sess_t *sess, struct roc_cpt *roc_cpt) +{ + union cpt_inst_w7 inst_w7; + + inst_w7.u64 = 0; + inst_w7.s.cptr = (u64) &sess->cpt_ctx.se_ctx.fctx; + /* Set the engine group */ + inst_w7.s.egrp = roc_cpt->eng_grp[CPT_ENG_TYPE_IE]; + + return inst_w7.u64; +} + +static_always_inline i32 +oct_crypto_link_session_update (vlib_main_t *vm, oct_crypto_sess_t *sess, + u32 key_index, u8 type) +{ + vnet_crypto_key_t *crypto_key, *auth_key; + roc_se_cipher_type enc_type = 0; + roc_se_auth_type auth_type = 0; + vnet_crypto_key_t *key; + u32 digest_len = ~0; + i32 rv = 0; + + key = vnet_crypto_get_key (key_index); + + switch (key->async_alg) + { + case VNET_CRYPTO_ALG_AES_128_CBC_SHA1_TAG12: + case VNET_CRYPTO_ALG_AES_192_CBC_SHA1_TAG12: + case VNET_CRYPTO_ALG_AES_256_CBC_SHA1_TAG12: + enc_type = ROC_SE_AES_CBC; + auth_type = ROC_SE_SHA1_TYPE; + digest_len = 12; + break; + case VNET_CRYPTO_ALG_AES_128_CBC_SHA224_TAG14: + case VNET_CRYPTO_ALG_AES_192_CBC_SHA224_TAG14: + case VNET_CRYPTO_ALG_AES_256_CBC_SHA224_TAG14: + enc_type = ROC_SE_AES_CBC; + auth_type = ROC_SE_SHA2_SHA224; + digest_len = 14; + break; + case VNET_CRYPTO_ALG_AES_128_CBC_SHA256_TAG16: + case VNET_CRYPTO_ALG_AES_192_CBC_SHA256_TAG16: + case VNET_CRYPTO_ALG_AES_256_CBC_SHA256_TAG16: + enc_type = ROC_SE_AES_CBC; + auth_type = ROC_SE_SHA2_SHA256; + digest_len = 16; + break; + case VNET_CRYPTO_ALG_AES_128_CBC_SHA384_TAG24: + case VNET_CRYPTO_ALG_AES_192_CBC_SHA384_TAG24: + case VNET_CRYPTO_ALG_AES_256_CBC_SHA384_TAG24: + enc_type = ROC_SE_AES_CBC; + auth_type = ROC_SE_SHA2_SHA384; + digest_len = 24; + break; + case VNET_CRYPTO_ALG_AES_128_CBC_SHA512_TAG32: + case VNET_CRYPTO_ALG_AES_192_CBC_SHA512_TAG32: + case VNET_CRYPTO_ALG_AES_256_CBC_SHA512_TAG32: + enc_type = ROC_SE_AES_CBC; + auth_type = ROC_SE_SHA2_SHA512; + digest_len = 32; + break; + case VNET_CRYPTO_ALG_AES_128_CBC_MD5_TAG12: + case VNET_CRYPTO_ALG_AES_192_CBC_MD5_TAG12: + case VNET_CRYPTO_ALG_AES_256_CBC_MD5_TAG12: + enc_type = ROC_SE_AES_CBC; + auth_type = ROC_SE_MD5_TYPE; + digest_len = 12; + break; + case VNET_CRYPTO_ALG_AES_128_CTR_SHA1_TAG12: + case VNET_CRYPTO_ALG_AES_192_CTR_SHA1_TAG12: + case VNET_CRYPTO_ALG_AES_256_CTR_SHA1_TAG12: + enc_type = ROC_SE_AES_CTR; + auth_type = ROC_SE_SHA1_TYPE; + digest_len = 12; + break; + case VNET_CRYPTO_ALG_3DES_CBC_MD5_TAG12: + enc_type = ROC_SE_DES3_CBC; + auth_type = ROC_SE_MD5_TYPE; + digest_len = 12; + break; + case VNET_CRYPTO_ALG_3DES_CBC_SHA1_TAG12: + enc_type = ROC_SE_DES3_CBC; + auth_type = ROC_SE_SHA1_TYPE; + digest_len = 12; + break; + case VNET_CRYPTO_ALG_3DES_CBC_SHA224_TAG14: + enc_type = ROC_SE_DES3_CBC; + auth_type = ROC_SE_SHA2_SHA224; + digest_len = 14; + break; + case VNET_CRYPTO_ALG_3DES_CBC_SHA256_TAG16: + enc_type = ROC_SE_DES3_CBC; + auth_type = ROC_SE_SHA2_SHA256; + digest_len = 16; + break; + case VNET_CRYPTO_ALG_3DES_CBC_SHA384_TAG24: + enc_type = ROC_SE_DES3_CBC; + auth_type = ROC_SE_SHA2_SHA384; + digest_len = 24; + break; + case VNET_CRYPTO_ALG_3DES_CBC_SHA512_TAG32: + enc_type = ROC_SE_DES3_CBC; + auth_type = ROC_SE_SHA2_SHA512; + digest_len = 32; + break; + default: + clib_warning ( + "Cryptodev: Undefined link algo %u specified. Key index %u", + key->async_alg, key_index); + return -1; + } + + if (type == VNET_CRYPTO_OP_TYPE_ENCRYPT) + sess->cpt_ctx.ciph_then_auth = true; + else + sess->cpt_ctx.auth_then_ciph = true; + + sess->iv_length = 16; + sess->cpt_op = type; + + crypto_key = vnet_crypto_get_key (key->index_crypto); + rv = roc_se_ciph_key_set (&sess->cpt_ctx, enc_type, crypto_key->data, + vec_len (crypto_key->data)); + if (rv) + { + clib_warning ("Cryptodev: Error in setting cipher key for enc type %u", + enc_type); + return -1; + } + + auth_key = vnet_crypto_get_key (key->index_integ); + + rv = roc_se_auth_key_set (&sess->cpt_ctx, auth_type, auth_key->data, + vec_len (auth_key->data), digest_len); + if (rv) + { + clib_warning ("Cryptodev: Error in setting auth key for auth type %u", + auth_type); + return -1; + } + + return 0; +} + +static_always_inline i32 +oct_crypto_aead_session_update (vlib_main_t *vm, oct_crypto_sess_t *sess, + u32 key_index, u8 type) +{ + vnet_crypto_key_t *key = vnet_crypto_get_key (key_index); + roc_se_cipher_type enc_type = 0; + roc_se_auth_type auth_type = 0; + u32 digest_len = ~0; + i32 rv = 0; + + switch (key->async_alg) + { + case VNET_CRYPTO_ALG_AES_128_GCM: + case VNET_CRYPTO_ALG_AES_192_GCM: + case VNET_CRYPTO_ALG_AES_256_GCM: + enc_type = ROC_SE_AES_GCM; + sess->aes_gcm = 1; + sess->iv_offset = 0; + sess->iv_length = 16; + sess->cpt_ctx.mac_len = 16; + sess->cpt_op = type; + digest_len = 16; + break; + case VNET_CRYPTO_ALG_CHACHA20_POLY1305: + enc_type = ROC_SE_CHACHA20; + auth_type = ROC_SE_POLY1305; + break; + default: + clib_warning ( + "Cryptodev: Undefined cipher algo %u specified. Key index %u", + key->async_alg, key_index); + return -1; + } + + rv = roc_se_ciph_key_set (&sess->cpt_ctx, enc_type, key->data, + vec_len (key->data)); + if (rv) + { + clib_warning ("Cryptodev: Error in setting cipher key for enc type %u", + enc_type); + return -1; + } + + rv = roc_se_auth_key_set (&sess->cpt_ctx, auth_type, NULL, 0, digest_len); + if (rv) + { + clib_warning ("Cryptodev: Error in setting auth key for auth type %u", + auth_type); + return -1; + } + + if (enc_type == ROC_SE_CHACHA20) + sess->cpt_ctx.template_w4.s.opcode_minor |= BIT (5); + + return 0; +} + +static_always_inline i32 +oct_crypto_session_init (vlib_main_t *vm, oct_crypto_sess_t *session, + vnet_crypto_key_index_t key_index, int op_type) +{ + oct_crypto_main_t *ocm = &oct_crypto_main; + vnet_crypto_key_t *key; + oct_crypto_dev_t *ocd; + i32 rv = 0; + + ocd = ocm->crypto_dev[op_type]; + + key = vnet_crypto_get_key (key_index); + + if (key->type == VNET_CRYPTO_KEY_TYPE_LINK) + rv = oct_crypto_link_session_update (vm, session, key_index, op_type); + else + rv = oct_crypto_aead_session_update (vm, session, key_index, op_type); + + if (rv) + { + oct_crypto_session_free (vm, session); + return -1; + } + + session->crypto_dev = ocd; + + session->cpt_inst_w7 = + oct_cpt_inst_w7_get (session, session->crypto_dev->roc_cpt); + + session->initialised = 1; + + return 0; +} + +static_always_inline void +oct_crypto_update_frame_error_status (vnet_crypto_async_frame_t *f, u32 index, + vnet_crypto_op_status_t s) +{ + u32 i; + + for (i = index; i < f->n_elts; i++) + f->elts[i].status = s; + + if (index == 0) + f->state = VNET_CRYPTO_FRAME_STATE_NOT_PROCESSED; +} + +static_always_inline int +oct_crypto_enqueue_enc_dec (vlib_main_t *vm, vnet_crypto_async_frame_t *frame, + const u8 is_aead, u8 aad_len, const u8 type) +{ + u32 i, enq_tail, enc_auth_len, buffer_index, nb_infl_allowed; + struct cpt_inst_s inst[VNET_CRYPTO_FRAME_SIZE]; + u32 crypto_start_offset, integ_start_offset; + oct_crypto_main_t *ocm = &oct_crypto_main; + vnet_crypto_async_frame_elt_t *elts; + oct_crypto_dev_t *crypto_dev = NULL; + oct_crypto_inflight_req_t *infl_req; + oct_crypto_pending_queue_t *pend_q; + u64 dptr_start_ptr, curr_ptr; + oct_crypto_sess_t *sess; + u32 crypto_total_length; + oct_crypto_key_t *key; + vlib_buffer_t *buffer; + u16 adj_len; + int ret; + + /* GCM packets having 8 bytes of aad and 8 bytes of iv */ + u8 aad_iv = 8 + 8; + + pend_q = &ocm->pend_q[vlib_get_thread_index ()]; + + enq_tail = pend_q->enq_tail; + + nb_infl_allowed = pend_q->n_desc - pend_q->n_crypto_inflight; + if (PREDICT_FALSE (nb_infl_allowed == 0)) + { + oct_crypto_update_frame_error_status ( + frame, 0, VNET_CRYPTO_OP_STATUS_FAIL_ENGINE_ERR); + return -1; + } + + infl_req = &pend_q->req_queue[enq_tail]; + infl_req->frame = frame; + + for (i = 0; i < frame->n_elts; i++) + { + elts = &frame->elts[i]; + buffer_index = frame->buffer_indices[i]; + key = vec_elt_at_index (ocm->keys[type], elts->key_index); + + if (PREDICT_FALSE (!key->sess)) + { + oct_crypto_update_frame_error_status ( + frame, i, VNET_CRYPTO_OP_STATUS_FAIL_ENGINE_ERR); + return -1; + } + + sess = key->sess; + + if (PREDICT_FALSE (!sess->initialised)) + oct_crypto_session_init (vm, sess, elts->key_index, type); + + crypto_dev = sess->crypto_dev; + + clib_memset (inst + i, 0, sizeof (struct cpt_inst_s)); + + buffer = vlib_get_buffer (vm, buffer_index); + + if (is_aead) + { + dptr_start_ptr = + (u64) (buffer->data + (elts->crypto_start_offset - aad_iv)); + curr_ptr = (u64) (buffer->data + buffer->current_data); + adj_len = (u16) (dptr_start_ptr - curr_ptr); + + crypto_total_length = elts->crypto_total_length; + crypto_start_offset = aad_iv; + integ_start_offset = 0; + + ret = oct_crypto_fill_fc_params ( + sess, inst + i, is_aead, aad_len, (u8 *) dptr_start_ptr, elts, + (oct_crypto_scatter_gather_t *) (infl_req->sg_data) + i, + crypto_total_length /* cipher_len */, + crypto_start_offset /* cipher_offset */, 0 /* auth_len */, + integ_start_offset /* auth_off */, buffer, adj_len); + if (PREDICT_FALSE (ret < 0)) + { + oct_crypto_update_frame_error_status ( + frame, i, VNET_CRYPTO_OP_STATUS_FAIL_ENGINE_ERR); + return -1; + } + } + else + { + dptr_start_ptr = (u64) (buffer->data + elts->integ_start_offset); + + enc_auth_len = elts->crypto_total_length + elts->integ_length_adj; + + curr_ptr = (u64) (buffer->data + buffer->current_data); + adj_len = (u16) (dptr_start_ptr - curr_ptr); + + crypto_total_length = elts->crypto_total_length; + crypto_start_offset = + elts->crypto_start_offset - elts->integ_start_offset; + integ_start_offset = 0; + + ret = oct_crypto_fill_fc_params ( + sess, inst + i, is_aead, aad_len, (u8 *) dptr_start_ptr, elts, + (oct_crypto_scatter_gather_t *) (infl_req->sg_data) + i, + crypto_total_length /* cipher_len */, + crypto_start_offset /* cipher_offset */, + enc_auth_len /* auth_len */, integ_start_offset /* auth_off */, + buffer, adj_len); + if (PREDICT_FALSE (ret < 0)) + { + oct_crypto_update_frame_error_status ( + frame, i, VNET_CRYPTO_OP_STATUS_FAIL_ENGINE_ERR); + return -1; + } + } + + inst[i].w7.u64 = sess->cpt_inst_w7; + inst[i].res_addr = (u64) &infl_req->res[i]; + } + + oct_crypto_burst_submit (crypto_dev, inst, frame->n_elts); + + infl_req->elts = frame->n_elts; + OCT_MOD_INC (pend_q->enq_tail, pend_q->n_desc); + pend_q->n_crypto_inflight++; + + return 0; +} + +int +oct_crypto_enqueue_linked_alg_enc (vlib_main_t *vm, + vnet_crypto_async_frame_t *frame) +{ + return oct_crypto_enqueue_enc_dec ( + vm, frame, 0 /* is_aead */, 0 /* aad_len */, VNET_CRYPTO_OP_TYPE_ENCRYPT); +} + +int +oct_crypto_enqueue_linked_alg_dec (vlib_main_t *vm, + vnet_crypto_async_frame_t *frame) +{ + return oct_crypto_enqueue_enc_dec ( + vm, frame, 0 /* is_aead */, 0 /* aad_len */, VNET_CRYPTO_OP_TYPE_DECRYPT); +} + +int +oct_crypto_enqueue_aead_aad_enc (vlib_main_t *vm, + vnet_crypto_async_frame_t *frame, u8 aad_len) +{ + return oct_crypto_enqueue_enc_dec (vm, frame, 1 /* is_aead */, aad_len, + VNET_CRYPTO_OP_TYPE_ENCRYPT); +} + +static_always_inline int +oct_crypto_enqueue_aead_aad_dec (vlib_main_t *vm, + vnet_crypto_async_frame_t *frame, u8 aad_len) +{ + return oct_crypto_enqueue_enc_dec (vm, frame, 1 /* is_aead */, aad_len, + VNET_CRYPTO_OP_TYPE_DECRYPT); +} + +int +oct_crypto_enqueue_aead_aad_8_enc (vlib_main_t *vm, + vnet_crypto_async_frame_t *frame) +{ + return oct_crypto_enqueue_aead_aad_enc (vm, frame, 8); +} + +int +oct_crypto_enqueue_aead_aad_12_enc (vlib_main_t *vm, + vnet_crypto_async_frame_t *frame) +{ + return oct_crypto_enqueue_aead_aad_enc (vm, frame, 12); +} + +int +oct_crypto_enqueue_aead_aad_0_enc (vlib_main_t *vm, + vnet_crypto_async_frame_t *frame) +{ + return oct_crypto_enqueue_aead_aad_enc (vm, frame, 0); +} + +int +oct_crypto_enqueue_aead_aad_8_dec (vlib_main_t *vm, + vnet_crypto_async_frame_t *frame) +{ + return oct_crypto_enqueue_aead_aad_dec (vm, frame, 8); +} + +int +oct_crypto_enqueue_aead_aad_12_dec (vlib_main_t *vm, + vnet_crypto_async_frame_t *frame) +{ + return oct_crypto_enqueue_aead_aad_dec (vm, frame, 12); +} + +int +oct_crypto_enqueue_aead_aad_0_dec (vlib_main_t *vm, + vnet_crypto_async_frame_t *frame) +{ + return oct_crypto_enqueue_aead_aad_dec (vm, frame, 0); +} + +vnet_crypto_async_frame_t * +oct_crypto_frame_dequeue (vlib_main_t *vm, u32 *nb_elts_processed, + u32 *enqueue_thread_idx) +{ + oct_crypto_main_t *ocm = &oct_crypto_main; + u32 deq_head, status = VNET_CRYPTO_OP_STATUS_COMPLETED; + vnet_crypto_async_frame_elt_t *fe = NULL; + oct_crypto_inflight_req_t *infl_req; + oct_crypto_pending_queue_t *pend_q; + vnet_crypto_async_frame_t *frame; + volatile union cpt_res_s *res; + int i; + + pend_q = &ocm->pend_q[vlib_get_thread_index ()]; + + if (!pend_q->n_crypto_inflight) + return NULL; + + deq_head = pend_q->deq_head; + infl_req = &pend_q->req_queue[deq_head]; + frame = infl_req->frame; + + fe = frame->elts; + + for (i = infl_req->deq_elts; i < infl_req->elts; ++i) + { + res = &infl_req->res[i]; + + if (PREDICT_FALSE (res->cn10k.compcode == CPT_COMP_NOT_DONE)) + return NULL; + + if (PREDICT_FALSE (res->cn10k.uc_compcode)) + { + if (res->cn10k.uc_compcode == ROC_SE_ERR_GC_ICV_MISCOMPARE) + status = fe[i].status = VNET_CRYPTO_OP_STATUS_FAIL_BAD_HMAC; + else + status = fe[i].status = VNET_CRYPTO_OP_STATUS_FAIL_ENGINE_ERR; + } + + infl_req->deq_elts++; + } + + clib_memset ((void *) infl_req->res, 0, + sizeof (union cpt_res_s) * VNET_CRYPTO_FRAME_SIZE); + + OCT_MOD_INC (pend_q->deq_head, pend_q->n_desc); + pend_q->n_crypto_inflight--; + + frame->state = status == VNET_CRYPTO_OP_STATUS_COMPLETED ? + VNET_CRYPTO_FRAME_STATE_SUCCESS : + VNET_CRYPTO_FRAME_STATE_ELT_ERROR; + + *nb_elts_processed = frame->n_elts; + *enqueue_thread_idx = frame->enqueue_thread_index; + + infl_req->deq_elts = 0; + infl_req->elts = 0; + + return frame; +} + +int +oct_init_crypto_engine_handlers (vlib_main_t *vm, vnet_dev_t *dev) +{ + u32 engine_index; + + engine_index = vnet_crypto_register_engine (vm, "oct_cryptodev", 100, + "OCT Cryptodev Engine"); + +#define _(n, k, t, a) \ + vnet_crypto_register_enqueue_handler ( \ + vm, engine_index, VNET_CRYPTO_OP_##n##_TAG##t##_AAD##a##_ENC, \ + oct_crypto_enqueue_aead_aad_##a##_enc); \ + vnet_crypto_register_enqueue_handler ( \ + vm, engine_index, VNET_CRYPTO_OP_##n##_TAG##t##_AAD##a##_DEC, \ + oct_crypto_enqueue_aead_aad_##a##_dec); + foreach_oct_crypto_aead_async_alg +#undef _ + +#define _(c, h, k, d) \ + vnet_crypto_register_enqueue_handler ( \ + vm, engine_index, VNET_CRYPTO_OP_##c##_##h##_TAG##d##_ENC, \ + oct_crypto_enqueue_linked_alg_enc); \ + vnet_crypto_register_enqueue_handler ( \ + vm, engine_index, VNET_CRYPTO_OP_##c##_##h##_TAG##d##_DEC, \ + oct_crypto_enqueue_linked_alg_dec); + foreach_oct_crypto_link_async_alg; +#undef _ + + vnet_crypto_register_dequeue_handler (vm, engine_index, + oct_crypto_frame_dequeue); + + vnet_crypto_register_key_handler (vm, engine_index, oct_crypto_key_handler); + + return 0; +} + +int +oct_conf_sw_queue (vlib_main_t *vm, vnet_dev_t *dev) +{ + oct_crypto_main_t *ocm = &oct_crypto_main; + vlib_thread_main_t *tm = vlib_get_thread_main (); + extern oct_plt_init_param_t oct_plt_init_param; + oct_crypto_inflight_req_t *infl_req_queue; + u32 n_inflight_req; + int i, j = 0; + + ocm->pend_q = oct_plt_init_param.oct_plt_zmalloc ( + tm->n_vlib_mains * sizeof (oct_crypto_pending_queue_t), + CLIB_CACHE_LINE_BYTES); + if (ocm->pend_q == NULL) + { + log_err (dev, "Failed to allocate memory for crypto pending queue"); + return -1; + } + + /* + * Each pending queue will get number of cpt desc / number of cores. + * And that desc count is shared across inflight entries. + */ + n_inflight_req = + (OCT_CPT_LF_MAX_NB_DESC / tm->n_vlib_mains) / VNET_CRYPTO_FRAME_SIZE; + + for (i = 0; i < tm->n_vlib_mains; ++i) + { + ocm->pend_q[i].n_desc = n_inflight_req; + + ocm->pend_q[i].req_queue = oct_plt_init_param.oct_plt_zmalloc ( + ocm->pend_q[i].n_desc * sizeof (oct_crypto_inflight_req_t), + CLIB_CACHE_LINE_BYTES); + if (ocm->pend_q[i].req_queue == NULL) + { + log_err (dev, + "Failed to allocate memory for crypto inflight request"); + goto free; + } + + for (j = 0; j <= ocm->pend_q[i].n_desc; ++j) + { + infl_req_queue = &ocm->pend_q[i].req_queue[j]; + + infl_req_queue->sg_data = oct_plt_init_param.oct_plt_zmalloc ( + OCT_SCATTER_GATHER_BUFFER_SIZE * VNET_CRYPTO_FRAME_SIZE, + CLIB_CACHE_LINE_BYTES); + if (infl_req_queue->sg_data == NULL) + { + log_err (dev, "Failed to allocate crypto scatter gather memory"); + goto free; + } + } + } + return 0; +free: + for (; i >= 0; i--) + { + if (ocm->pend_q[i].req_queue == NULL) + continue; + for (; j >= 0; j--) + { + infl_req_queue = &ocm->pend_q[i].req_queue[j]; + + if (infl_req_queue->sg_data == NULL) + continue; + + oct_plt_init_param.oct_plt_free (infl_req_queue->sg_data); + } + oct_plt_init_param.oct_plt_free (ocm->pend_q[i].req_queue); + } + oct_plt_init_param.oct_plt_free (ocm->pend_q); + + return -1; +} diff --git a/src/plugins/dev_octeon/crypto.h b/src/plugins/dev_octeon/crypto.h new file mode 100644 index 00000000000..27e1f600c68 --- /dev/null +++ b/src/plugins/dev_octeon/crypto.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2024 Marvell. + * SPDX-License-Identifier: Apache-2.0 + * https://spdx.org/licenses/Apache-2.0.html + */ + +#ifndef _CRYPTO_H_ +#define _CRYPTO_H_ +#include <vnet/crypto/crypto.h> +#include <vnet/ip/ip.h> + +#define OCT_MAX_N_CPT_DEV 2 + +#define OCT_CPT_LF_MAX_NB_DESC 128000 + +/* CRYPTO_ID, KEY_LENGTH_IN_BYTES, TAG_LEN, AAD_LEN */ +#define foreach_oct_crypto_aead_async_alg \ + _ (AES_128_GCM, 16, 16, 8) \ + _ (AES_128_GCM, 16, 16, 12) \ + _ (AES_192_GCM, 24, 16, 8) \ + _ (AES_192_GCM, 24, 16, 12) \ + _ (AES_256_GCM, 32, 16, 8) \ + _ (AES_256_GCM, 32, 16, 12) \ + _ (CHACHA20_POLY1305, 32, 16, 8) \ + _ (CHACHA20_POLY1305, 32, 16, 12) \ + _ (CHACHA20_POLY1305, 32, 16, 0) + +/* CRYPTO_ID, INTEG_ID, KEY_LENGTH_IN_BYTES, DIGEST_LEN */ +#define foreach_oct_crypto_link_async_alg \ + _ (AES_128_CBC, SHA1, 16, 12) \ + _ (AES_192_CBC, SHA1, 24, 12) \ + _ (AES_256_CBC, SHA1, 32, 12) \ + _ (AES_128_CBC, SHA256, 16, 16) \ + _ (AES_192_CBC, SHA256, 24, 16) \ + _ (AES_256_CBC, SHA256, 32, 16) \ + _ (AES_128_CBC, SHA384, 16, 24) \ + _ (AES_192_CBC, SHA384, 24, 24) \ + _ (AES_256_CBC, SHA384, 32, 24) \ + _ (AES_128_CBC, SHA512, 16, 32) \ + _ (AES_192_CBC, SHA512, 24, 32) \ + _ (AES_256_CBC, SHA512, 32, 32) \ + _ (AES_128_CBC, MD5, 16, 12) \ + _ (AES_192_CBC, MD5, 24, 12) \ + _ (AES_256_CBC, MD5, 32, 12) \ + _ (3DES_CBC, MD5, 24, 12) \ + _ (3DES_CBC, SHA1, 24, 12) \ + _ (3DES_CBC, SHA256, 24, 16) \ + _ (3DES_CBC, SHA384, 24, 24) \ + _ (3DES_CBC, SHA512, 24, 32) \ + _ (AES_128_CTR, SHA1, 16, 12) \ + _ (AES_192_CTR, SHA1, 24, 12) \ + _ (AES_256_CTR, SHA1, 32, 12) + +#define OCT_MOD_INC(i, l) ((i) == (l - 1) ? (i) = 0 : (i)++) + +#define OCT_SCATTER_GATHER_BUFFER_SIZE 1024 + +#define CPT_LMT_SIZE_COPY (sizeof (struct cpt_inst_s) / 16) +#define OCT_MAX_LMT_SZ 16 + +#define SRC_IOV_SIZE \ + (sizeof (struct roc_se_iov_ptr) + \ + (sizeof (struct roc_se_buf_ptr) * ROC_MAX_SG_CNT)) + +#define OCT_CPT_LMT_GET_LINE_ADDR(lmt_addr, lmt_num) \ + (void *) ((u64) (lmt_addr) + ((u64) (lmt_num) << ROC_LMT_LINE_SIZE_LOG2)) + +typedef struct +{ + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + struct roc_cpt *roc_cpt; + struct roc_cpt_lmtline lmtline; + struct roc_cpt_lf lf; + vnet_dev_t *dev; +} oct_crypto_dev_t; + +typedef struct +{ + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + /** CPT opcode */ + u16 cpt_op : 4; + /** Flag for AES GCM */ + u16 aes_gcm : 1; + /** IV length in bytes */ + u8 iv_length; + /** Auth IV length in bytes */ + u8 auth_iv_length; + /** IV offset in bytes */ + u16 iv_offset; + /** Auth IV offset in bytes */ + u16 auth_iv_offset; + /** CPT inst word 7 */ + u64 cpt_inst_w7; + /* initialise as part of first packet */ + u8 initialised; + /* store link key index in case of linked algo */ + vnet_crypto_key_index_t key_index; + oct_crypto_dev_t *crypto_dev; + struct roc_se_ctx cpt_ctx; +} oct_crypto_sess_t; + +typedef struct +{ + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + oct_crypto_sess_t *sess; + oct_crypto_dev_t *crypto_dev; +} oct_crypto_key_t; + +typedef struct oct_crypto_scatter_gather +{ + u8 buf[OCT_SCATTER_GATHER_BUFFER_SIZE]; +} oct_crypto_scatter_gather_t; + +typedef struct +{ + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + /** Result data of all entries in the frame */ + volatile union cpt_res_s res[VNET_CRYPTO_FRAME_SIZE]; + /** Scatter gather data */ + void *sg_data; + /** Frame pointer */ + vnet_crypto_async_frame_t *frame; + /** Number of async elements in frame */ + u16 elts; + /** Next read entry in frame, when dequeue */ + u16 deq_elts; +} oct_crypto_inflight_req_t; + +typedef struct +{ + /** Array of pending request */ + oct_crypto_inflight_req_t *req_queue; + /** Number of inflight operations in queue */ + u32 n_crypto_inflight; + /** Tail of queue to be used for enqueue */ + u16 enq_tail; + /** Head of queue to be used for dequeue */ + u16 deq_head; + /** Number of descriptors */ + u16 n_desc; +} oct_crypto_pending_queue_t; + +typedef struct +{ + oct_crypto_dev_t *crypto_dev[OCT_MAX_N_CPT_DEV]; + oct_crypto_key_t *keys[VNET_CRYPTO_ASYNC_OP_N_TYPES]; + oct_crypto_pending_queue_t *pend_q; + int n_cpt; + u8 started; +} oct_crypto_main_t; + +extern oct_crypto_main_t oct_crypto_main; + +void oct_crypto_key_del_handler (vlib_main_t *vm, + vnet_crypto_key_index_t key_index); + +void oct_crypto_key_add_handler (vlib_main_t *vm, + vnet_crypto_key_index_t key_index); + +void oct_crypto_key_handler (vlib_main_t *vm, vnet_crypto_key_op_t kop, + vnet_crypto_key_index_t idx); + +int oct_crypto_enqueue_linked_alg_enc (vlib_main_t *vm, + vnet_crypto_async_frame_t *frame); +int oct_crypto_enqueue_linked_alg_dec (vlib_main_t *vm, + vnet_crypto_async_frame_t *frame); +int oct_crypto_enqueue_aead_aad_8_enc (vlib_main_t *vm, + vnet_crypto_async_frame_t *frame); +int oct_crypto_enqueue_aead_aad_12_enc (vlib_main_t *vm, + vnet_crypto_async_frame_t *frame); +int oct_crypto_enqueue_aead_aad_0_enc (vlib_main_t *vm, + vnet_crypto_async_frame_t *frame); +int oct_crypto_enqueue_aead_aad_8_dec (vlib_main_t *vm, + vnet_crypto_async_frame_t *frame); +int oct_crypto_enqueue_aead_aad_12_dec (vlib_main_t *vm, + vnet_crypto_async_frame_t *frame); +int oct_crypto_enqueue_aead_aad_0_dec (vlib_main_t *vm, + vnet_crypto_async_frame_t *frame); +vnet_crypto_async_frame_t *oct_crypto_frame_dequeue (vlib_main_t *vm, + u32 *nb_elts_processed, + u32 *enqueue_thread_idx); +int oct_init_crypto_engine_handlers (vlib_main_t *vm, vnet_dev_t *dev); +int oct_conf_sw_queue (vlib_main_t *vm, vnet_dev_t *dev); +#endif /* _CRYPTO_H_ */ diff --git a/src/plugins/dev_octeon/flow.c b/src/plugins/dev_octeon/flow.c index 35aabde76a7..e86425ec85d 100644 --- a/src/plugins/dev_octeon/flow.c +++ b/src/plugins/dev_octeon/flow.c @@ -131,6 +131,7 @@ oct_flow_validate_params (vlib_main_t *vm, vnet_dev_port_t *port, vnet_dev_port_cfg_type_t type, u32 flow_index, uword *priv_data) { + vnet_dev_port_interfaces_t *ifs = port->interfaces; vnet_flow_t *flow = vnet_get_flow (flow_index); u32 last_queue; u32 qid; @@ -151,11 +152,11 @@ oct_flow_validate_params (vlib_main_t *vm, vnet_dev_port_t *port, if (flow->actions & VNET_FLOW_ACTION_REDIRECT_TO_QUEUE) { qid = flow->redirect_queue; - if (qid > port->intf.num_rx_queues - 1 || qid < 0) + if (qid > ifs->num_rx_queues - 1 || qid < 0) { log_err (port->dev, "Given Q(%d) is invalid, supported range is %d-%d", qid, 0, - port->intf.num_rx_queues - 1); + ifs->num_rx_queues - 1); return VNET_DEV_ERR_NOT_SUPPORTED; } } @@ -163,12 +164,12 @@ oct_flow_validate_params (vlib_main_t *vm, vnet_dev_port_t *port, if (flow->actions & VNET_FLOW_ACTION_RSS) { last_queue = flow->queue_index + flow->queue_num; - if (last_queue > port->intf.num_rx_queues - 1) + if (last_queue > ifs->num_rx_queues - 1) { log_err (port->dev, "Given Q range(%d-%d) is invalid, supported range is %d-%d", flow->queue_index, flow->queue_index + flow->queue_num, 0, - port->intf.num_rx_queues - 1); + ifs->num_rx_queues - 1); return VNET_DEV_ERR_NOT_SUPPORTED; } } @@ -189,6 +190,14 @@ oct_flow_rule_create (vnet_dev_port_t *port, struct roc_npc_action *actions, npc = &oct_port->npc; + for (int i = 0; item_info[i].type != ROC_NPC_ITEM_TYPE_END; i++) + { + log_debug (port->dev, "Flow[%d] Item[%d] type %d spec 0x%U mask 0x%U", + flow->index, i, item_info[i].type, format_hex_bytes, + item_info[i].spec, item_info[i].size, format_hex_bytes, + item_info[i].mask, item_info[i].size); + } + npc_flow = roc_npc_flow_create (npc, &attr, item_info, actions, npc->pf_func, &rv); if (rv) @@ -530,6 +539,15 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow, struct roc_npc_item_info item_info[ROC_NPC_ITEM_TYPE_END] = {}; struct roc_npc_action actions[ROC_NPC_ITEM_TYPE_END] = {}; oct_port_t *oct_port = vnet_dev_get_port_data (port); + vnet_dev_port_interfaces_t *ifs = port->interfaces; + ethernet_header_t eth_spec = {}, eth_mask = {}; + sctp_header_t sctp_spec = {}, sctp_mask = {}; + gtpu_header_t gtpu_spec = {}, gtpu_mask = {}; + ip4_header_t ip4_spec = {}, ip4_mask = {}; + ip6_header_t ip6_spec = {}, ip6_mask = {}; + udp_header_t udp_spec = {}, udp_mask = {}; + tcp_header_t tcp_spec = {}, tcp_mask = {}; + esp_header_t esp_spec = {}, esp_mask = {}; u16 l4_src_port = 0, l4_dst_port = 0; u16 l4_src_mask = 0, l4_dst_mask = 0; struct roc_npc_action_rss rss_conf = {}; @@ -537,6 +555,7 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow, struct roc_npc_action_mark mark = {}; struct roc_npc *npc = &oct_port->npc; u8 *flow_spec = 0, *flow_mask = 0; + u8 *drv_spec = 0, *drv_mask = 0; vnet_dev_rv_t rv = VNET_DEV_OK; int layer = 0, index = 0; u16 *queues = NULL; @@ -546,7 +565,6 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow, if (FLOW_IS_GENERIC_TYPE (flow)) { - u8 drv_item_spec[1024] = { 0 }, drv_item_mask[1024] = { 0 }; unformat_input_t input; int rc; @@ -562,11 +580,13 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow, unformat_user (&input, unformat_hex_string, &flow_mask); unformat_free (&input); + vec_validate (drv_spec, 1024); + vec_validate (drv_mask, 1024); oct_flow_parse_state pst = { .nxt_proto = 0, .port = port, .items = item_info, - .oct_drv = { .spec = drv_item_spec, .mask = drv_item_mask }, + .oct_drv = { .spec = drv_spec, .mask = drv_mask }, .generic = { .spec = flow_spec, .mask = flow_mask, .len = vec_len (flow_spec) }, @@ -577,6 +597,8 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow, { vec_free (flow_spec); vec_free (flow_mask); + vec_free (drv_spec); + vec_free (drv_mask); return VNET_DEV_ERR_NOT_SUPPORTED; } @@ -585,9 +607,8 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow, if (FLOW_IS_ETHERNET_CLASS (flow)) { - ethernet_header_t eth_spec = { .type = clib_host_to_net_u16 ( - flow->ethernet.eth_hdr.type) }, - eth_mask = { .type = 0xFFFF }; + eth_spec.type = clib_host_to_net_u16 (flow->ethernet.eth_hdr.type); + eth_mask.type = 0xFFFF; item_info[layer].spec = (void *) ð_spec; item_info[layer].mask = (void *) ð_mask; @@ -600,10 +621,11 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow, { vnet_flow_ip4_t *ip4_hdr = &flow->ip4; proto = ip4_hdr->protocol.prot; - ip4_header_t ip4_spec = { .src_address = ip4_hdr->src_addr.addr, - .dst_address = ip4_hdr->dst_addr.addr }, - ip4_mask = { .src_address = ip4_hdr->src_addr.mask, - .dst_address = ip4_hdr->dst_addr.mask }; + + ip4_spec.src_address = ip4_hdr->src_addr.addr; + ip4_spec.dst_address = ip4_hdr->dst_addr.addr; + ip4_mask.src_address = ip4_hdr->src_addr.mask; + ip4_mask.dst_address = ip4_hdr->dst_addr.mask; item_info[layer].spec = (void *) &ip4_spec; item_info[layer].mask = (void *) &ip4_mask; @@ -625,10 +647,11 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow, { vnet_flow_ip6_t *ip6_hdr = &flow->ip6; proto = ip6_hdr->protocol.prot; - ip6_header_t ip6_spec = { .src_address = ip6_hdr->src_addr.addr, - .dst_address = ip6_hdr->dst_addr.addr }, - ip6_mask = { .src_address = ip6_hdr->src_addr.mask, - .dst_address = ip6_hdr->dst_addr.mask }; + + ip6_spec.src_address = ip6_hdr->src_addr.addr; + ip6_spec.dst_address = ip6_hdr->dst_addr.addr; + ip6_mask.src_address = ip6_hdr->src_addr.mask; + ip6_mask.dst_address = ip6_hdr->dst_addr.mask; item_info[layer].spec = (void *) &ip6_spec; item_info[layer].mask = (void *) &ip6_mask; @@ -653,16 +676,15 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow, switch (proto) { case IP_PROTOCOL_UDP: - item_info[layer].type = ROC_NPC_ITEM_TYPE_UDP; - - udp_header_t udp_spec = { .src_port = l4_src_port, - .dst_port = l4_dst_port }, - udp_mask = { .src_port = l4_src_mask, - .dst_port = l4_dst_mask }; + udp_spec.src_port = l4_src_port; + udp_spec.dst_port = l4_dst_port; + udp_mask.src_port = l4_src_mask; + udp_mask.dst_port = l4_dst_mask; item_info[layer].spec = (void *) &udp_spec; item_info[layer].mask = (void *) &udp_mask; item_info[layer].size = sizeof (udp_header_t); + item_info[layer].type = ROC_NPC_ITEM_TYPE_UDP; layer++; if (FLOW_IS_L4_TUNNEL_TYPE (flow)) @@ -670,14 +692,13 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow, switch (flow->type) { case VNET_FLOW_TYPE_IP4_GTPU: - item_info[layer].type = ROC_NPC_ITEM_TYPE_GTPU; - gtpu_header_t gtpu_spec = { .teid = clib_host_to_net_u32 ( - flow->ip4_gtpu.teid) }, - gtpu_mask = { .teid = 0XFFFFFFFF }; + gtpu_spec.teid = clib_host_to_net_u32 (flow->ip4_gtpu.teid); + gtpu_mask.teid = 0XFFFFFFFF; item_info[layer].spec = (void *) >pu_spec; item_info[layer].mask = (void *) >pu_mask; item_info[layer].size = sizeof (gtpu_header_t); + item_info[layer].type = ROC_NPC_ITEM_TYPE_GTPU; layer++; break; @@ -689,42 +710,39 @@ oct_flow_add (vlib_main_t *vm, vnet_dev_port_t *port, vnet_flow_t *flow, break; case IP_PROTOCOL_TCP: - item_info[layer].type = ROC_NPC_ITEM_TYPE_TCP; - - tcp_header_t tcp_spec = { .src_port = l4_src_port, - .dst_port = l4_dst_port }, - tcp_mask = { .src_port = l4_src_mask, - .dst_port = l4_dst_mask }; + tcp_spec.src_port = l4_src_port; + tcp_spec.dst_port = l4_dst_port; + tcp_mask.src_port = l4_src_mask; + tcp_mask.dst_port = l4_dst_mask; item_info[layer].spec = (void *) &tcp_spec; item_info[layer].mask = (void *) &tcp_mask; item_info[layer].size = sizeof (tcp_header_t); + item_info[layer].type = ROC_NPC_ITEM_TYPE_TCP; layer++; break; case IP_PROTOCOL_SCTP: - item_info[layer].type = ROC_NPC_ITEM_TYPE_SCTP; - - sctp_header_t sctp_spec = { .src_port = l4_src_port, - .dst_port = l4_dst_port }, - sctp_mask = { .src_port = l4_src_mask, - .dst_port = l4_dst_mask }; + sctp_spec.src_port = l4_src_port; + sctp_spec.dst_port = l4_dst_port; + sctp_mask.src_port = l4_src_mask; + sctp_mask.dst_port = l4_dst_mask; item_info[layer].spec = (void *) &sctp_spec; item_info[layer].mask = (void *) &sctp_mask; item_info[layer].size = sizeof (sctp_header_t); + item_info[layer].type = ROC_NPC_ITEM_TYPE_SCTP; layer++; break; case IP_PROTOCOL_IPSEC_ESP: - item_info[layer].type = ROC_NPC_ITEM_TYPE_ESP; - esp_header_t esp_spec = { .spi = clib_host_to_net_u32 ( - flow->ip4_ipsec_esp.spi) }, - esp_mask = { .spi = 0xFFFFFFFF }; + esp_spec.spi = clib_host_to_net_u32 (flow->ip4_ipsec_esp.spi); + esp_mask.spi = 0xFFFFFFFF; item_info[layer].spec = (void *) &esp_spec; item_info[layer].mask = (void *) &esp_mask; item_info[layer].size = sizeof (u32); + item_info[layer].type = ROC_NPC_ITEM_TYPE_ESP; layer++; break; @@ -759,7 +777,7 @@ parse_flow_actions: log_err (port->dev, "RSS action has no queues"); return VNET_DEV_ERR_NOT_SUPPORTED; } - queues = clib_mem_alloc (sizeof (u16) * port->intf.num_rx_queues); + queues = clib_mem_alloc (sizeof (u16) * ifs->num_rx_queues); for (index = 0; index < flow->queue_num; index++) queues[index] = flow->queue_index++; @@ -803,10 +821,10 @@ parse_flow_actions: if (queues) clib_mem_free (queues); - if (flow_spec) - vec_free (flow_spec); - if (flow_mask) - vec_free (flow_mask); + vec_free (flow_spec); + vec_free (flow_mask); + vec_free (drv_spec); + vec_free (drv_mask); return rv; } diff --git a/src/plugins/dev_octeon/init.c b/src/plugins/dev_octeon/init.c index fd65ce6d9e2..99cadddfc24 100644 --- a/src/plugins/dev_octeon/init.c +++ b/src/plugins/dev_octeon/init.c @@ -10,6 +10,7 @@ #include <vnet/plugin/plugin.h> #include <vpp/app/version.h> #include <dev_octeon/octeon.h> +#include <dev_octeon/crypto.h> #include <base/roc_api.h> #include <common.h> @@ -54,7 +55,9 @@ static struct _ (0xa064, RVU_VF, "Marvell Octeon Resource Virtualization Unit VF"), _ (0xa0f8, LBK_VF, "Marvell Octeon Loopback Unit VF"), _ (0xa0f7, SDP_VF, "Marvell Octeon System DPI Packet Interface Unit VF"), - _ (0xa0f3, CPT_VF, "Marvell Octeon Cryptographic Accelerator Unit VF"), + _ (0xa0f3, O10K_CPT_VF, + "Marvell Octeon-10 Cryptographic Accelerator Unit VF"), + _ (0xa0fe, O9K_CPT_VF, "Marvell Octeon-9 Cryptographic Accelerator Unit VF"), #undef _ }; @@ -110,6 +113,7 @@ oct_init_nix (vlib_main_t *vm, vnet_dev_t *dev) .reta_sz = ROC_NIX_RSS_RETA_SZ_256, .max_sqb_count = 512, .pci_dev = &cd->plt_pci_dev, + .hw_vlan_ins = true, }; if ((rrv = roc_nix_dev_init (cd->nix))) @@ -131,6 +135,9 @@ oct_init_nix (vlib_main_t *vm, vnet_dev_t *dev) .rx_offloads = { .ip4_cksum = 1, }, + .tx_offloads = { + .ip4_cksum = 1, + }, }, .ops = { .init = oct_port_init, @@ -187,17 +194,113 @@ oct_init_nix (vlib_main_t *vm, vnet_dev_t *dev) return vnet_dev_port_add (vm, dev, 0, &port_add_args); } +static int +oct_conf_cpt (vlib_main_t *vm, vnet_dev_t *dev, oct_crypto_dev_t *ocd, + int nb_lf) +{ + struct roc_cpt *roc_cpt = ocd->roc_cpt; + int rrv; + + if ((rrv = roc_cpt_eng_grp_add (roc_cpt, CPT_ENG_TYPE_SE)) < 0) + { + log_err (dev, "Could not add CPT SE engines"); + return cnx_return_roc_err (dev, rrv, "roc_cpt_eng_grp_add"); + } + if ((rrv = roc_cpt_eng_grp_add (roc_cpt, CPT_ENG_TYPE_IE)) < 0) + { + log_err (dev, "Could not add CPT IE engines"); + return cnx_return_roc_err (dev, rrv, "roc_cpt_eng_grp_add"); + } + if (roc_cpt->eng_grp[CPT_ENG_TYPE_IE] != ROC_CPT_DFLT_ENG_GRP_SE_IE) + { + log_err (dev, "Invalid CPT IE engine group configuration"); + return -1; + } + if (roc_cpt->eng_grp[CPT_ENG_TYPE_SE] != ROC_CPT_DFLT_ENG_GRP_SE) + { + log_err (dev, "Invalid CPT SE engine group configuration"); + return -1; + } + if ((rrv = roc_cpt_dev_configure (roc_cpt, nb_lf, false, 0)) < 0) + { + log_err (dev, "could not configure crypto device %U", + format_vlib_pci_addr, roc_cpt->pci_dev->addr); + return cnx_return_roc_err (dev, rrv, "roc_cpt_dev_configure"); + } + return 0; +} + +static vnet_dev_rv_t +oct_conf_cpt_queue (vlib_main_t *vm, vnet_dev_t *dev, oct_crypto_dev_t *ocd) +{ + struct roc_cpt *roc_cpt = ocd->roc_cpt; + struct roc_cpt_lmtline *cpt_lmtline; + struct roc_cpt_lf *cpt_lf; + int rrv; + + cpt_lf = &ocd->lf; + cpt_lmtline = &ocd->lmtline; + + cpt_lf->nb_desc = OCT_CPT_LF_MAX_NB_DESC; + cpt_lf->lf_id = 0; + if ((rrv = roc_cpt_lf_init (roc_cpt, cpt_lf)) < 0) + return cnx_return_roc_err (dev, rrv, "roc_cpt_lf_init"); + + roc_cpt_iq_enable (cpt_lf); + + if ((rrv = roc_cpt_lmtline_init (roc_cpt, cpt_lmtline, 0) < 0)) + return cnx_return_roc_err (dev, rrv, "roc_cpt_lmtline_init"); + + return 0; +} + static vnet_dev_rv_t oct_init_cpt (vlib_main_t *vm, vnet_dev_t *dev) { + oct_crypto_main_t *ocm = &oct_crypto_main; + extern oct_plt_init_param_t oct_plt_init_param; oct_device_t *cd = vnet_dev_get_data (dev); + oct_crypto_dev_t *ocd = NULL; int rrv; - struct roc_cpt cpt = { - .pci_dev = &cd->plt_pci_dev, - }; - if ((rrv = roc_cpt_dev_init (&cpt))) + if (ocm->n_cpt == OCT_MAX_N_CPT_DEV || ocm->started) + return VNET_DEV_ERR_NOT_SUPPORTED; + + ocd = oct_plt_init_param.oct_plt_zmalloc (sizeof (oct_crypto_dev_t), + CLIB_CACHE_LINE_BYTES); + + ocd->roc_cpt = oct_plt_init_param.oct_plt_zmalloc (sizeof (struct roc_cpt), + CLIB_CACHE_LINE_BYTES); + ocd->roc_cpt->pci_dev = &cd->plt_pci_dev; + + ocd->dev = dev; + + if ((rrv = roc_cpt_dev_init (ocd->roc_cpt))) return cnx_return_roc_err (dev, rrv, "roc_cpt_dev_init"); + + if ((rrv = oct_conf_cpt (vm, dev, ocd, 1))) + return rrv; + + if ((rrv = oct_conf_cpt_queue (vm, dev, ocd))) + return rrv; + + if (!ocm->n_cpt) + { + /* + * Initialize s/w queues, which are common across multiple + * crypto devices + */ + oct_conf_sw_queue (vm, dev); + + ocm->crypto_dev[0] = ocd; + } + + ocm->crypto_dev[1] = ocd; + + oct_init_crypto_engine_handlers (vm, dev); + + ocm->n_cpt++; + return VNET_DEV_OK; } @@ -252,7 +355,8 @@ oct_init (vlib_main_t *vm, vnet_dev_t *dev) case OCT_DEVICE_TYPE_SDP_VF: return oct_init_nix (vm, dev); - case OCT_DEVICE_TYPE_CPT_VF: + case OCT_DEVICE_TYPE_O10K_CPT_VF: + case OCT_DEVICE_TYPE_O9K_CPT_VF: return oct_init_cpt (vm, dev); default: diff --git a/src/plugins/dev_octeon/octeon.h b/src/plugins/dev_octeon/octeon.h index a87a5e3e1ed..ccf8f62880d 100644 --- a/src/plugins/dev_octeon/octeon.h +++ b/src/plugins/dev_octeon/octeon.h @@ -30,7 +30,8 @@ typedef enum OCT_DEVICE_TYPE_RVU_VF, OCT_DEVICE_TYPE_LBK_VF, OCT_DEVICE_TYPE_SDP_VF, - OCT_DEVICE_TYPE_CPT_VF, + OCT_DEVICE_TYPE_O10K_CPT_VF, + OCT_DEVICE_TYPE_O9K_CPT_VF, } __clib_packed oct_device_type_t; typedef struct @@ -41,7 +42,6 @@ typedef struct u8 full_duplex : 1; u32 speed; struct plt_pci_device plt_pci_dev; - struct roc_cpt cpt; struct roc_nix *nix; } oct_device_t; @@ -102,7 +102,6 @@ typedef struct u64 aura_handle; u64 io_addr; void *lmt_addr; - oct_npa_batch_alloc_cl128_t *ba_buffer; u8 ba_first_cl; u8 ba_num_cl; diff --git a/src/plugins/dev_octeon/port.c b/src/plugins/dev_octeon/port.c index 8ba9041f858..f8a7d6ba7db 100644 --- a/src/plugins/dev_octeon/port.c +++ b/src/plugins/dev_octeon/port.c @@ -53,28 +53,117 @@ oct_roc_err (vnet_dev_t *dev, int rv, char *fmt, ...) } vnet_dev_rv_t +oct_port_pause_flow_control_init (vlib_main_t *vm, vnet_dev_port_t *port) +{ + vnet_dev_t *dev = port->dev; + oct_device_t *cd = vnet_dev_get_data (dev); + struct roc_nix *nix = cd->nix; + struct roc_nix_fc_cfg fc_cfg; + struct roc_nix_sq *sq; + struct roc_nix_cq *cq; + struct roc_nix_rq *rq; + int rrv; + + /* pause flow control is not supported on SDP/LBK devices */ + if (roc_nix_is_sdp (nix) || roc_nix_is_lbk (nix)) + { + log_notice (dev, + "pause flow control is not supported on SDP/LBK devices"); + return VNET_DEV_OK; + } + + fc_cfg.type = ROC_NIX_FC_RXCHAN_CFG; + fc_cfg.rxchan_cfg.enable = true; + rrv = roc_nix_fc_config_set (nix, &fc_cfg); + if (rrv) + return oct_roc_err (dev, rrv, "roc_nix_fc_config_set failed"); + + memset (&fc_cfg, 0, sizeof (struct roc_nix_fc_cfg)); + fc_cfg.type = ROC_NIX_FC_RQ_CFG; + fc_cfg.rq_cfg.enable = true; + fc_cfg.rq_cfg.tc = 0; + + foreach_vnet_dev_port_rx_queue (rxq, port) + { + oct_rxq_t *crq = vnet_dev_get_rx_queue_data (rxq); + + rq = &crq->rq; + cq = &crq->cq; + + fc_cfg.rq_cfg.rq = rq->qid; + fc_cfg.rq_cfg.cq_drop = cq->drop_thresh; + + rrv = roc_nix_fc_config_set (nix, &fc_cfg); + if (rrv) + return oct_roc_err (dev, rrv, "roc_nix_fc_config_set failed"); + } + + memset (&fc_cfg, 0, sizeof (struct roc_nix_fc_cfg)); + fc_cfg.type = ROC_NIX_FC_TM_CFG; + fc_cfg.tm_cfg.tc = 0; + fc_cfg.tm_cfg.enable = true; + + foreach_vnet_dev_port_tx_queue (txq, port) + { + oct_txq_t *ctq = vnet_dev_get_tx_queue_data (txq); + + sq = &ctq->sq; + + fc_cfg.tm_cfg.sq = sq->qid; + rrv = roc_nix_fc_config_set (nix, &fc_cfg); + if (rrv) + return oct_roc_err (dev, rrv, "roc_nix_fc_config_set failed"); + } + + /* By default, enable pause flow control */ + rrv = roc_nix_fc_mode_set (nix, ROC_NIX_FC_FULL); + if (rrv) + return oct_roc_err (dev, rrv, "roc_nix_fc_mode_set failed"); + + return VNET_DEV_OK; +} + +vnet_dev_rv_t oct_port_init (vlib_main_t *vm, vnet_dev_port_t *port) { vnet_dev_t *dev = port->dev; oct_device_t *cd = vnet_dev_get_data (dev); oct_port_t *cp = vnet_dev_get_port_data (port); + vnet_dev_port_interfaces_t *ifs = port->interfaces; + u8 mac_addr[PLT_ETHER_ADDR_LEN]; struct roc_nix *nix = cd->nix; vnet_dev_rv_t rv; int rrv; log_debug (dev, "port init: port %u", port->port_id); - if ((rrv = roc_nix_lf_alloc (nix, port->intf.num_rx_queues, - port->intf.num_tx_queues, rxq_cfg))) + if ((rrv = roc_nix_lf_alloc (nix, ifs->num_rx_queues, ifs->num_tx_queues, + rxq_cfg))) { oct_port_deinit (vm, port); return oct_roc_err ( dev, rrv, "roc_nix_lf_alloc(nb_rxq = %u, nb_txq = %d, rxq_cfg=0x%lx) failed", - port->intf.num_rx_queues, port->intf.num_tx_queues, rxq_cfg); + ifs->num_rx_queues, ifs->num_tx_queues, rxq_cfg); } cp->lf_allocated = 1; + if (!roc_nix_is_vf_or_sdp (nix)) + { + if ((rrv = roc_nix_npc_mac_addr_get (nix, mac_addr))) + { + oct_port_deinit (vm, port); + return oct_roc_err (dev, rrv, "roc_nix_npc_mac_addr_get failed"); + } + + /* Sync MAC address to CGX/RPM table */ + if ((rrv = roc_nix_mac_addr_set (nix, mac_addr))) + { + oct_port_deinit (vm, port); + return oct_roc_err (dev, rrv, "roc_nix_mac_addr_set failed"); + } + } + if ((rrv = roc_nix_tm_init (nix))) { oct_port_deinit (vm, port); @@ -125,6 +214,19 @@ oct_port_init (vlib_main_t *vm, vnet_dev_port_t *port) oct_port_add_counters (vm, port); + if ((rrv = roc_nix_mac_mtu_set (nix, port->max_rx_frame_size))) + { + rv = oct_roc_err (dev, rrv, "roc_nix_mac_mtu_set() failed"); + return rv; + } + + /* Configure pause frame flow control*/ + if ((rv = oct_port_pause_flow_control_init (vm, port))) + { + oct_port_deinit (vm, port); + return rv; + } + return VNET_DEV_OK; } @@ -188,7 +290,7 @@ oct_port_poll (vlib_main_t *vm, vnet_dev_port_t *port) return; } - if (roc_nix_is_lbk (nix)) + if (roc_nix_is_lbk (nix) || roc_nix_is_sdp (nix)) { link_info.status = 1; link_info.full_duplex = 1; @@ -327,6 +429,7 @@ oct_port_start (vlib_main_t *vm, vnet_dev_port_t *port) { vnet_dev_t *dev = port->dev; oct_device_t *cd = vnet_dev_get_data (dev); + oct_port_t *cp = vnet_dev_get_port_data (port); struct roc_nix *nix = cd->nix; struct roc_nix_eeprom_info eeprom_info = {}; vnet_dev_rv_t rv; @@ -344,15 +447,15 @@ oct_port_start (vlib_main_t *vm, vnet_dev_port_t *port) ctq->n_enq = 0; } - if ((rrv = roc_nix_mac_mtu_set (nix, 9200))) + if ((rrv = roc_nix_npc_rx_ena_dis (nix, true))) { - rv = oct_roc_err (dev, rrv, "roc_nix_mac_mtu_set() failed"); + rv = oct_roc_err (dev, rrv, "roc_nix_npc_rx_ena_dis() failed"); goto done; } - if ((rrv = roc_nix_npc_rx_ena_dis (nix, true))) + if ((rrv = roc_npc_mcam_enable_all_entries (&cp->npc, true))) { - rv = oct_roc_err (dev, rrv, "roc_nix_npc_rx_ena_dis() failed"); + rv = oct_roc_err (dev, rrv, "roc_npc_mcam_enable_all_entries() failed"); goto done; } @@ -374,6 +477,7 @@ oct_port_stop (vlib_main_t *vm, vnet_dev_port_t *port) { vnet_dev_t *dev = port->dev; oct_device_t *cd = vnet_dev_get_data (dev); + oct_port_t *cp = vnet_dev_get_port_data (port); struct roc_nix *nix = cd->nix; int rrv; @@ -381,6 +485,14 @@ oct_port_stop (vlib_main_t *vm, vnet_dev_port_t *port) vnet_dev_poll_port_remove (vm, port, oct_port_poll); + /* Disable all the NPC entries */ + rrv = roc_npc_mcam_enable_all_entries (&cp->npc, false); + if (rrv) + { + oct_roc_err (dev, rrv, "roc_npc_mcam_enable_all_entries() failed"); + return; + } + rrv = roc_nix_npc_rx_ena_dis (nix, false); if (rrv) { @@ -393,6 +505,18 @@ oct_port_stop (vlib_main_t *vm, vnet_dev_port_t *port) foreach_vnet_dev_port_tx_queue (q, port) oct_txq_stop (vm, q); + + vnet_dev_port_state_change (vm, port, + (vnet_dev_port_state_changes_t){ + .change.link_state = 1, + .change.link_speed = 1, + .link_speed = 0, + .link_state = 0, + }); + + /* Update the device status */ + cd->status = 0; + cd->speed = 0; } vnet_dev_rv_t @@ -445,7 +569,6 @@ oct_port_add_del_eth_addr (vlib_main_t *vm, vnet_dev_port_t *port, oct_device_t *cd = vnet_dev_get_data (dev); struct roc_nix *nix = cd->nix; vnet_dev_rv_t rv = VNET_DEV_OK; - i32 rrv; if (is_primary) @@ -469,8 +592,30 @@ oct_port_add_del_eth_addr (vlib_main_t *vm, vnet_dev_port_t *port, rv = oct_roc_err (dev, rrv, "roc_nix_mac_addr_set() failed"); } } + + rrv = roc_nix_rss_default_setup (nix, default_rss_flowkey); + if (rrv) + rv = oct_roc_err (dev, rrv, "roc_nix_rss_default_setup() failed"); } } + + return rv; +} + +vnet_dev_rv_t +oct_op_config_max_rx_len (vlib_main_t *vm, vnet_dev_port_t *port, + u32 rx_frame_size) +{ + vnet_dev_t *dev = port->dev; + oct_device_t *cd = vnet_dev_get_data (dev); + struct roc_nix *nix = cd->nix; + vnet_dev_rv_t rv = VNET_DEV_OK; + i32 rrv; + + rrv = roc_nix_mac_max_rx_len_set (nix, rx_frame_size); + if (rrv) + rv = oct_roc_err (dev, rrv, "roc_nix_mac_max_rx_len_set() failed"); + return rv; } @@ -535,6 +680,7 @@ oct_port_cfg_change (vlib_main_t *vm, vnet_dev_port_t *port, break; case VNET_DEV_PORT_CFG_MAX_RX_FRAME_SIZE: + rv = oct_op_config_max_rx_len (vm, port, req->max_rx_frame_size); break; case VNET_DEV_PORT_CFG_ADD_RX_FLOW: diff --git a/src/plugins/dev_octeon/roc_helper.c b/src/plugins/dev_octeon/roc_helper.c index 16e0a871a9d..c1166b654cf 100644 --- a/src/plugins/dev_octeon/roc_helper.c +++ b/src/plugins/dev_octeon/roc_helper.c @@ -75,13 +75,12 @@ oct_drv_physmem_alloc (vlib_main_t *vm, u32 size, u32 align) if (align) { - /* Force cache line alloc in case alignment is less than cache line */ - align = align < CLIB_CACHE_LINE_BYTES ? CLIB_CACHE_LINE_BYTES : align; + /* Force ROC align alloc in case alignment is less than ROC align */ + align = align < ROC_ALIGN ? ROC_ALIGN : align; mem = vlib_physmem_alloc_aligned_on_numa (vm, size, align, 0); } else - mem = - vlib_physmem_alloc_aligned_on_numa (vm, size, CLIB_CACHE_LINE_BYTES, 0); + mem = vlib_physmem_alloc_aligned_on_numa (vm, size, ROC_ALIGN, 0); if (!mem) return NULL; diff --git a/src/plugins/dev_octeon/rx_node.c b/src/plugins/dev_octeon/rx_node.c index 1f8d5d93fa3..833227eeea8 100644 --- a/src/plugins/dev_octeon/rx_node.c +++ b/src/plugins/dev_octeon/rx_node.c @@ -103,8 +103,10 @@ oct_rx_batch (vlib_main_t *vm, oct_rx_node_ctx_t *ctx, vnet_dev_rx_queue_t *rxq, u32 n) { oct_rxq_t *crq = vnet_dev_get_rx_queue_data (rxq); - vlib_buffer_template_t bt = rxq->buffer_template; - u32 n_left; + vlib_buffer_template_t bt = vnet_dev_get_rx_queue_if_buffer_template (rxq); + u32 b0_err_flags = 0, b1_err_flags = 0; + u32 b2_err_flags = 0, b3_err_flags = 0; + u32 n_left, err_flags = 0; oct_nix_rx_cqe_desc_t *d = ctx->next_desc; vlib_buffer_t *b[4]; @@ -145,6 +147,13 @@ oct_rx_batch (vlib_main_t *vm, oct_rx_node_ctx_t *ctx, oct_rx_attach_tail (vm, ctx, b[2], d + 2); oct_rx_attach_tail (vm, ctx, b[3], d + 3); } + + b0_err_flags = (d[0].parse.w[0] >> 20) & 0xFFF; + b1_err_flags = (d[1].parse.w[0] >> 20) & 0xFFF; + b2_err_flags = (d[2].parse.w[0] >> 20) & 0xFFF; + b3_err_flags = (d[3].parse.w[0] >> 20) & 0xFFF; + + err_flags |= b0_err_flags | b1_err_flags | b2_err_flags | b3_err_flags; } for (; n_left; d += 1, n_left -= 1, ctx->to_next += 1) @@ -157,11 +166,16 @@ oct_rx_batch (vlib_main_t *vm, oct_rx_node_ctx_t *ctx, ctx->n_segs += 1; if (d[0].sg0.segs > 1) oct_rx_attach_tail (vm, ctx, b[0], d + 0); + + err_flags |= ((d[0].parse.w[0] >> 20) & 0xFFF); } plt_write64 ((crq->cq.wdata | n), crq->cq.door); ctx->n_rx_pkts += n; ctx->n_left_to_next -= n; + if (err_flags) + ctx->parse_w0_or = (err_flags << 20); + return n; } @@ -333,9 +347,9 @@ oct_rx_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node, oct_nix_rx_cqe_desc_t *descs = crq->cq.desc_base; oct_nix_lf_cq_op_status_t status; oct_rx_node_ctx_t _ctx = { - .next_index = rxq->next_index, - .sw_if_index = port->intf.sw_if_index, - .hw_if_index = port->intf.hw_if_index, + .next_index = vnet_dev_get_rx_queue_if_next_index(rxq), + .sw_if_index = vnet_dev_get_rx_queue_if_sw_if_index (rxq), + .hw_if_index = vnet_dev_get_rx_queue_if_hw_if_index (rxq), }, *ctx = &_ctx; /* get head and tail from NIX_LF_CQ_OP_STATUS */ diff --git a/src/plugins/dev_octeon/tx_node.c b/src/plugins/dev_octeon/tx_node.c index 0907493814d..f42f18d989b 100644 --- a/src/plugins/dev_octeon/tx_node.c +++ b/src/plugins/dev_octeon/tx_node.c @@ -255,19 +255,18 @@ oct_tx_enq1 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vlib_buffer_t *b, if (oflags & VNET_BUFFER_OFFLOAD_F_IP_CKSUM) { d.hdr_w1.ol3type = NIX_SENDL3TYPE_IP4_CKSUM; - d.hdr_w1.ol3ptr = vnet_buffer (b)->l3_hdr_offset; - d.hdr_w1.ol4ptr = - vnet_buffer (b)->l3_hdr_offset + sizeof (ip4_header_t); + d.hdr_w1.ol3ptr = vnet_buffer (b)->l3_hdr_offset - b->current_data; + d.hdr_w1.ol4ptr = d.hdr_w1.ol3ptr + sizeof (ip4_header_t); } if (oflags & VNET_BUFFER_OFFLOAD_F_UDP_CKSUM) { d.hdr_w1.ol4type = NIX_SENDL4TYPE_UDP_CKSUM; - d.hdr_w1.ol4ptr = vnet_buffer (b)->l4_hdr_offset; + d.hdr_w1.ol4ptr = vnet_buffer (b)->l4_hdr_offset - b->current_data; } else if (oflags & VNET_BUFFER_OFFLOAD_F_TCP_CKSUM) { d.hdr_w1.ol4type = NIX_SENDL4TYPE_TCP_CKSUM; - d.hdr_w1.ol4ptr = vnet_buffer (b)->l4_hdr_offset; + d.hdr_w1.ol4ptr = vnet_buffer (b)->l4_hdr_offset - b->current_data; } } @@ -301,7 +300,8 @@ oct_tx_enq16 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vnet_dev_tx_queue_t *txq, { u8 dwords_per_line[16], *dpl = dwords_per_line; u64 __attribute__ ((unused)) lmt_arg, ioaddr, n_lines; - u32 n_left, or_flags_16 = 0, n = 0; + u32 __attribute__ ((unused)) or_flags_16 = 0; + u32 n_left, n = 0; const u32 not_simple_flags = VLIB_BUFFER_NEXT_PRESENT | VNET_BUFFER_F_OFFLOAD; lmt_line_t *l = ctx->lmt_lines; diff --git a/src/plugins/dhcp/client.c b/src/plugins/dhcp/client.c index 8fa67c616b2..d81d2935577 100644 --- a/src/plugins/dhcp/client.c +++ b/src/plugins/dhcp/client.c @@ -1153,7 +1153,9 @@ dhcp_client_set_command_fn (vlib_main_t * vm, a->is_add = is_add; a->sw_if_index = sw_if_index; a->hostname = hostname; - a->client_identifier = format (0, "vpp 1.1%c", 0); + a->client_identifier = + format (0, "%U", format_ethernet_address, + vnet_sw_interface_get_hw_address (vnet_get_main (), sw_if_index)); a->set_broadcast_flag = set_broadcast_flag; /* diff --git a/src/plugins/dhcp/dhcp4_proxy_node.c b/src/plugins/dhcp/dhcp4_proxy_node.c index 2b49d49bb7f..740ae8043e0 100644 --- a/src/plugins/dhcp/dhcp4_proxy_node.c +++ b/src/plugins/dhcp/dhcp4_proxy_node.c @@ -321,7 +321,8 @@ dhcp_proxy_to_server_input (vlib_main_t * vm, o->length += id_len + 5; } - len = o->length + 3; + /* 2 bytes for option header 82+len */ + len = o->length + 2; b0->current_length += len; /* Fix IP header length and checksum */ old_l0 = ip0->length; diff --git a/src/plugins/dpdk/device/dpdk.h b/src/plugins/dpdk/device/dpdk.h index 692bbbc09d9..2440439989f 100644 --- a/src/plugins/dpdk/device/dpdk.h +++ b/src/plugins/dpdk/device/dpdk.h @@ -131,7 +131,6 @@ typedef struct u32 interface_number_from_port_id : 1; u32 use_intel_phdr_cksum : 1; u32 int_unmaskable : 1; - vlib_simple_counter_main_t *xstats_counters; } dpdk_driver_t; dpdk_driver_t *dpdk_driver_find (const char *name, const char **desc); @@ -211,6 +210,8 @@ typedef struct struct rte_eth_stats last_stats; struct rte_eth_xstat *xstats; f64 time_last_stats_update; + vlib_simple_counter_main_t xstats_counters; + u32 *xstats_symlinks; /* mac address */ u8 *default_mac_address; @@ -241,6 +242,7 @@ typedef struct _ (num_rx_desc) \ _ (num_tx_desc) \ _ (max_lro_pkt_size) \ + _ (disable_rxq_int) \ _ (rss_fn) typedef enum diff --git a/src/plugins/dpdk/device/dpdk_priv.h b/src/plugins/dpdk/device/dpdk_priv.h index e5b5a35df80..794953da55e 100644 --- a/src/plugins/dpdk/device/dpdk_priv.h +++ b/src/plugins/dpdk/device/dpdk_priv.h @@ -54,39 +54,28 @@ dpdk_get_xstats (dpdk_device_t *xd, u32 thread_index) { int ret; int i; - int len; if (!(xd->flags & DPDK_DEVICE_FLAG_ADMIN_UP)) return; - if (xd->driver == 0) - return; - len = rte_eth_xstats_get (xd->port_id, NULL, 0); - if (len < 0) - return; - - vec_validate (xd->xstats, len - 1); - ret = rte_eth_xstats_get (xd->port_id, xd->xstats, len); - if (ret < 0 || ret > len) + ret = rte_eth_xstats_get (xd->port_id, xd->xstats, vec_len (xd->xstats)); + if (ret < 0) { - /* Failed, expand vector and try again on next time around the track. */ - vec_validate (xd->xstats, ret - 1); - vec_set_len (xd->xstats, 0); dpdk_log_warn ("rte_eth_xstats_get(%d) failed: %d", xd->port_id, ret); return; } - if (len == vec_len (xd->driver->xstats_counters)) + else if (ret != vec_len (xd->xstats)) { - vec_foreach_index (i, xd->xstats) - { - vlib_set_simple_counter (&xd->driver->xstats_counters[i], - thread_index, xd->sw_if_index, - xd->xstats[i].value); - } + dpdk_log_warn ( + "rte_eth_xstats_get(%d) returned %d/%d stats. Resetting counters.", + xd->port_id, ret, vec_len (xd->xstats)); + dpdk_counters_xstats_init (xd); + return; } - else + + vec_foreach_index (i, xd->xstats) { - dpdk_log_warn ("rte_eth_xstats_get vector size mismatch (%d/%d", len, - vec_len (xd->driver->xstats_counters)); + vlib_set_simple_counter (&xd->xstats_counters, thread_index, i, + xd->xstats[i].value); } } diff --git a/src/plugins/dpdk/device/init.c b/src/plugins/dpdk/device/init.c index 453d9cff7ef..ec9e6045de7 100644 --- a/src/plugins/dpdk/device/init.c +++ b/src/plugins/dpdk/device/init.c @@ -227,71 +227,71 @@ dpdk_find_startup_config (struct rte_eth_dev_info *di) } /* - * Initialise or refresh the xstats counters for a device + * Initialise the xstats counters for a device */ void dpdk_counters_xstats_init (dpdk_device_t *xd) { int len, ret, i; struct rte_eth_xstat_name *xstats_names = 0; - char *name; - dpdk_driver_t *dr = xd->driver; - /* Only support xstats for supported drivers */ - if (!dr) - return; + if (vec_len (xd->xstats_symlinks) > 0) + { + /* xstats already initialized. Reset counters */ + vec_foreach_index (i, xd->xstats_symlinks) + { + vlib_stats_remove_entry (xd->xstats_symlinks[i]); + } + } + else + { + xd->xstats_counters.stat_segment_name = + (char *) format (0, "/if/xstats/%d%c", xd->sw_if_index, 0); + xd->xstats_counters.counters = 0; + } len = rte_eth_xstats_get_names (xd->port_id, 0, 0); if (len < 0) { - dpdk_log_err ("[%u] rte_eth_xstats_get_names failed: %d", xd->port_id, - len); - return; - } - /* Counters for this driver is already initialised */ - if (vec_len (dr->xstats_counters) == len) - { - vec_foreach_index (i, dr->xstats_counters) - { - vlib_validate_simple_counter (&dr->xstats_counters[i], - xd->sw_if_index); - vlib_zero_simple_counter (&dr->xstats_counters[i], xd->sw_if_index); - } + dpdk_log_err ("[%u] rte_eth_xstats_get_names failed: %d. DPDK xstats " + "not configured.", + xd->port_id, len); return; } - /* Same driver, different interface, different length of counter array. */ - ASSERT (vec_len (dr->xstats_counters) == 0); + vlib_validate_simple_counter (&xd->xstats_counters, len); + vlib_zero_simple_counter (&xd->xstats_counters, len); vec_validate (xstats_names, len - 1); + vec_validate (xd->xstats, len - 1); + vec_validate (xd->xstats_symlinks, len - 1); ret = rte_eth_xstats_get_names (xd->port_id, xstats_names, len); if (ret >= 0 && ret <= len) { - vec_validate (dr->xstats_counters, len - 1); vec_foreach_index (i, xstats_names) { - name = (char *) format (0, "/if/%s/%s%c", dr->drivers->name, - xstats_names[i].name, 0); - /* There is a bug in the ENA driver where the xstats names are not * unique. */ - if (vlib_stats_find_entry_index (name) != STAT_SEGMENT_INDEX_INVALID) + xd->xstats_symlinks[i] = vlib_stats_add_symlink ( + xd->xstats_counters.stats_entry_index, i, "/interfaces/%U/%s%c", + format_vnet_sw_if_index_name, vnet_get_main (), xd->sw_if_index, + xstats_names[i].name, 0); + if (xd->xstats_symlinks[i] == STAT_SEGMENT_INDEX_INVALID) { - vec_free (name); - name = (char *) format (0, "/if/%s/%s_%d%c", dr->drivers->name, - xstats_names[i].name, i, 0); + xd->xstats_symlinks[i] = vlib_stats_add_symlink ( + xd->xstats_counters.stats_entry_index, i, + "/interfaces/%U/%s_%d%c", format_vnet_sw_if_index_name, + vnet_get_main (), xd->sw_if_index, xstats_names[i].name, i, 0); } - - dr->xstats_counters[i].name = name; - dr->xstats_counters[i].stat_segment_name = name; - dr->xstats_counters[i].counters = 0; - vlib_validate_simple_counter (&dr->xstats_counters[i], - xd->sw_if_index); - vlib_zero_simple_counter (&dr->xstats_counters[i], xd->sw_if_index); - vec_free (name); } } + else + { + dpdk_log_err ("[%u] rte_eth_xstats_get_names failed: %d. DPDK xstats " + "not configured.", + xd->port_id, ret); + } vec_free (xstats_names); } @@ -503,6 +503,14 @@ dpdk_lib_init (dpdk_main_t * dm) else if (dr && dr->n_tx_desc) xd->conf.n_tx_desc = dr->n_tx_desc; + if (xd->conf.n_tx_desc > di.tx_desc_lim.nb_max) + { + dpdk_log_warn ("[%u] Configured number of TX descriptors (%u) is " + "bigger than maximum supported (%u)", + port_id, xd->conf.n_tx_desc, di.tx_desc_lim.nb_max); + xd->conf.n_tx_desc = di.tx_desc_lim.nb_max; + } + dpdk_log_debug ( "[%u] n_rx_queues: %u n_tx_queues: %u n_rx_desc: %u n_tx_desc: %u", port_id, xd->conf.n_rx_queues, xd->conf.n_tx_queues, @@ -588,6 +596,9 @@ dpdk_lib_init (dpdk_main_t * dm) if (devconf->max_lro_pkt_size) xd->conf.max_lro_pkt_size = devconf->max_lro_pkt_size; + if (devconf->disable_rxq_int) + xd->conf.enable_rxq_int = 0; + dpdk_device_setup (xd); /* rss queues should be configured after dpdk_device_setup() */ @@ -729,7 +740,8 @@ dpdk_bind_devices_to_uio (dpdk_config_main_t * conf) ; /* Cisco VIC */ else if (d->vendor_id == 0x1137 && - (d->device_id == 0x0043 || d->device_id == 0x0071)) + (d->device_id == 0x0043 || d->device_id == 0x0071 || + d->device_id == 0x02b7)) ; /* Chelsio T4/T5 */ else if (d->vendor_id == 0x1425 && (d->device_id & 0xe000) == 0x4000) @@ -1006,6 +1018,10 @@ dpdk_device_config (dpdk_config_main_t *conf, void *addr, if (error) break; } + else if (unformat (input, "no-rx-interrupts")) + { + devconf->disable_rxq_int = 1; + } else if (unformat (input, "tso on")) { devconf->tso = DPDK_DEVICE_TSO_ON; @@ -1122,6 +1138,7 @@ dpdk_config (vlib_main_t * vm, unformat_input_t * input) #ifdef __linux vlib_thread_main_t *tm = vlib_get_thread_main (); uword default_hugepage_sz, x; + u8 file_prefix = 0; #endif /* __linux__ */ u8 *s, *tmp = 0; int ret, i; @@ -1129,7 +1146,6 @@ dpdk_config (vlib_main_t * vm, unformat_input_t * input) int eal_no_hugetlb = 0; u8 no_pci = 0; u8 no_vmbus = 0; - u8 file_prefix = 0; u8 *socket_mem = 0; u32 vendor, device, domain, bus, func; void *fmt_func; @@ -1289,6 +1305,7 @@ dpdk_config (vlib_main_t * vm, unformat_input_t * input) } foreach_eal_double_hyphen_predicate_arg #undef _ +#ifdef __linux__ #define _(a) \ else if (unformat(input, #a " %s", &s)) \ { \ @@ -1304,6 +1321,7 @@ dpdk_config (vlib_main_t * vm, unformat_input_t * input) } foreach_eal_double_hyphen_arg #undef _ +#endif /* __linux__ */ #define _(a,b) \ else if (unformat(input, #a " %s", &s)) \ { \ diff --git a/src/plugins/dpdk/main.c b/src/plugins/dpdk/main.c index 9781d0ed7f0..437cfbd230e 100644 --- a/src/plugins/dpdk/main.c +++ b/src/plugins/dpdk/main.c @@ -50,7 +50,7 @@ rte_delay_us_override (unsigned us) { /* Only suspend for the admin_down_process */ vlib_process_t *proc = vlib_get_current_process (vm); - if (!(proc->flags & VLIB_PROCESS_IS_RUNNING) || + if (proc->state != VLIB_PROCESS_STATE_RUNNING || (proc->node_runtime.node_index != admin_up_down_process_node.index)) return 0; diff --git a/src/plugins/flowprobe/flowprobe.c b/src/plugins/flowprobe/flowprobe.c index 58a7cfe22f1..ee0a8eb8a31 100644 --- a/src/plugins/flowprobe/flowprobe.c +++ b/src/plugins/flowprobe/flowprobe.c @@ -48,7 +48,7 @@ uword flowprobe_walker_process (vlib_main_t * vm, vlib_node_runtime_t * rt, VNET_FEATURE_INIT (flowprobe_input_ip4_unicast, static) = { .arc_name = "ip4-unicast", .node_name = "flowprobe-input-ip4", - .runs_before = VNET_FEATURES ("ip4-lookup"), + .runs_before = VNET_FEATURES ("ip4-lookup", "ip4-inacl"), }; VNET_FEATURE_INIT (flowprobe_input_ip4_multicast, static) = { .arc_name = "ip4-multicast", @@ -58,7 +58,7 @@ VNET_FEATURE_INIT (flowprobe_input_ip4_multicast, static) = { VNET_FEATURE_INIT (flowprobe_input_ip6_unicast, static) = { .arc_name = "ip6-unicast", .node_name = "flowprobe-input-ip6", - .runs_before = VNET_FEATURES ("ip6-lookup"), + .runs_before = VNET_FEATURES ("ip6-lookup", "ip6-inacl"), }; VNET_FEATURE_INIT (flowprobe_input_ip6_multicast, static) = { .arc_name = "ip6-multicast", diff --git a/src/plugins/hs_apps/CMakeLists.txt b/src/plugins/hs_apps/CMakeLists.txt index 179c9c7a4c4..eae100949d4 100644 --- a/src/plugins/hs_apps/CMakeLists.txt +++ b/src/plugins/hs_apps/CMakeLists.txt @@ -21,8 +21,10 @@ add_vpp_plugin(hs_apps hs_apps.c http_cli.c http_client_cli.c + http_client.c http_tps.c proxy.c + test_builtins.c ) ############################################################################## diff --git a/src/plugins/hs_apps/echo_client.c b/src/plugins/hs_apps/echo_client.c index d1443e75e80..d5edffbd02e 100644 --- a/src/plugins/hs_apps/echo_client.c +++ b/src/plugins/hs_apps/echo_client.c @@ -429,8 +429,11 @@ ec_init (vlib_main_t *vm) ecm->app_is_init = 1; + session_enable_disable_args_t args = { .is_en = 1, + .rt_engine_type = + RT_BACKEND_ENGINE_RULE_TABLE }; vlib_worker_thread_barrier_sync (vm); - vnet_session_enable_disable (vm, 1 /* turn on session and transports */); + vnet_session_enable_disable (vm, &args); /* Turn on the builtin client input nodes */ foreach_vlib_main () @@ -943,15 +946,16 @@ ec_connect_rpc (void *args) a->api_context = ci; if (needs_crypto) { - session_endpoint_alloc_ext_cfg (&a->sep_ext, - TRANSPORT_ENDPT_EXT_CFG_CRYPTO); - a->sep_ext.ext_cfg->crypto.ckpair_index = ecm->ckpair_index; + transport_endpt_ext_cfg_t *ext_cfg = session_endpoint_add_ext_cfg ( + &a->sep_ext, TRANSPORT_ENDPT_EXT_CFG_CRYPTO, + sizeof (transport_endpt_crypto_cfg_t)); + ext_cfg->crypto.ckpair_index = ecm->ckpair_index; } rv = vnet_connect (a); if (needs_crypto) - clib_mem_free (a->sep_ext.ext_cfg); + session_endpoint_free_ext_cfgs (&a->sep_ext); if (rv) { diff --git a/src/plugins/hs_apps/echo_server.c b/src/plugins/hs_apps/echo_server.c index 0243252434a..b981e775b57 100644 --- a/src/plugins/hs_apps/echo_server.c +++ b/src/plugins/hs_apps/echo_server.c @@ -591,6 +591,7 @@ echo_server_listen () i32 rv; echo_server_main_t *esm = &echo_server_main; vnet_listen_args_t _args = {}, *args = &_args; + int needs_crypto; if ((rv = parse_uri (esm->server_uri, &args->sep_ext))) { @@ -598,11 +599,14 @@ echo_server_listen () } args->app_index = esm->app_index; args->sep_ext.port = hs_make_data_port (args->sep_ext.port); - if (echo_client_transport_needs_crypto (args->sep_ext.transport_proto)) + needs_crypto = + echo_client_transport_needs_crypto (args->sep_ext.transport_proto); + if (needs_crypto) { - session_endpoint_alloc_ext_cfg (&args->sep_ext, - TRANSPORT_ENDPT_EXT_CFG_CRYPTO); - args->sep_ext.ext_cfg->crypto.ckpair_index = esm->ckpair_index; + transport_endpt_ext_cfg_t *ext_cfg = session_endpoint_add_ext_cfg ( + &args->sep_ext, TRANSPORT_ENDPT_EXT_CFG_CRYPTO, + sizeof (transport_endpt_crypto_cfg_t)); + ext_cfg->crypto.ckpair_index = esm->ckpair_index; } if (args->sep_ext.transport_proto == TRANSPORT_PROTO_UDP) @@ -612,8 +616,8 @@ echo_server_listen () rv = vnet_listen (args); esm->listener_handle = args->handle; - if (args->sep_ext.ext_cfg) - clib_mem_free (args->sep_ext.ext_cfg); + if (needs_crypto) + session_endpoint_free_ext_cfgs (&args->sep_ext); return rv; } @@ -736,7 +740,10 @@ echo_server_create_command_fn (vlib_main_t * vm, unformat_input_t * input, goto cleanup; } - vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */ ); + session_enable_disable_args_t args = { .is_en = 1, + .rt_engine_type = + RT_BACKEND_ENGINE_RULE_TABLE }; + vnet_session_enable_disable (vm, &args); if (!server_uri_set) { diff --git a/src/plugins/hs_apps/http_cli.c b/src/plugins/hs_apps/http_cli.c index 4970da72012..dfa90f9eced 100644 --- a/src/plugins/hs_apps/http_cli.c +++ b/src/plugins/hs_apps/http_cli.c @@ -17,6 +17,8 @@ #include <vnet/session/application_interface.h> #include <vnet/session/session.h> #include <http/http.h> +#include <http/http_header_names.h> +#include <http/http_content_types.h> #define HCS_DEBUG 0 @@ -28,6 +30,12 @@ typedef struct { + u32 handle; + u8 *uri; +} hcs_uri_map_t; + +typedef struct +{ u32 hs_index; u32 thread_index; u64 node_index; @@ -43,6 +51,7 @@ typedef struct u8 *tx_buf; u32 tx_offset; u32 vpp_session_index; + http_header_t *resp_headers; } hcs_session_t; typedef struct @@ -59,6 +68,16 @@ typedef struct u32 fifo_size; u8 *uri; vlib_main_t *vlib_main; + + /* hash table to store uri -> uri map pool index */ + uword *index_by_uri; + + /* pool of uri maps */ + hcs_uri_map_t *uri_map_pool; + + /* for appns */ + u8 *appns_id; + u64 appns_secret; } hcs_main_t; static hcs_main_t hcs_main; @@ -148,31 +167,52 @@ hcs_cli_output (uword arg, u8 *buffer, uword buffer_bytes) } static void -start_send_data (hcs_session_t *hs, http_status_code_t status, - http_content_type_t type) +start_send_data (hcs_session_t *hs, http_status_code_t status) { http_msg_t msg; session_t *ts; + u8 *headers_buf = 0; int rv; + if (vec_len (hs->resp_headers)) + { + headers_buf = http_serialize_headers (hs->resp_headers); + vec_free (hs->resp_headers); + msg.data.headers_offset = 0; + msg.data.headers_len = vec_len (headers_buf); + } + else + { + msg.data.headers_offset = 0; + msg.data.headers_len = 0; + } + msg.type = HTTP_MSG_REPLY; msg.code = status; - msg.content_type = type; msg.data.type = HTTP_MSG_DATA_INLINE; - msg.data.len = vec_len (hs->tx_buf); + msg.data.body_len = vec_len (hs->tx_buf); + msg.data.body_offset = msg.data.headers_len; + msg.data.len = msg.data.body_len + msg.data.headers_len; ts = session_get (hs->vpp_session_index, hs->thread_index); rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg); ASSERT (rv == sizeof (msg)); - if (!msg.data.len) + if (msg.data.headers_len) + { + rv = svm_fifo_enqueue (ts->tx_fifo, vec_len (headers_buf), headers_buf); + ASSERT (rv == msg.data.headers_len); + vec_free (headers_buf); + } + + if (!msg.data.body_len) goto done; rv = svm_fifo_enqueue (ts->tx_fifo, vec_len (hs->tx_buf), hs->tx_buf); if (rv != vec_len (hs->tx_buf)) { - hs->tx_offset = rv; + hs->tx_offset = (rv > 0) ? rv : 0; svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF); } else @@ -183,7 +223,7 @@ start_send_data (hcs_session_t *hs, http_status_code_t status, done: if (svm_fifo_set_event (ts->tx_fifo)) - session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX); + session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX); } static void @@ -203,7 +243,12 @@ send_data_to_http (void *rpc_args) hs->tx_buf = args->buf; if (args->plain_text) type = HTTP_CONTENT_TEXT_PLAIN; - start_send_data (hs, HTTP_STATUS_OK, type); + + http_add_header (&hs->resp_headers, + http_header_name_token (HTTP_HEADER_CONTENT_TYPE), + http_content_type_token (type)); + + start_send_data (hs, HTTP_STATUS_OK); cleanup: @@ -325,6 +370,7 @@ hcs_ts_rx_callback (session_t *ts) hs = hcs_session_get (ts->thread_index, ts->opaque); hs->tx_buf = 0; + hs->resp_headers = 0; /* Read the http message header */ rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg); @@ -332,16 +378,17 @@ hcs_ts_rx_callback (session_t *ts) if (msg.type != HTTP_MSG_REQUEST || msg.method_type != HTTP_REQ_GET) { - start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED, - HTTP_CONTENT_TEXT_HTML); + http_add_header (&hs->resp_headers, + http_header_name_token (HTTP_HEADER_ALLOW), + http_token_lit ("GET")); + start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED); goto done; } if (msg.data.target_path_len == 0 || msg.data.target_form != HTTP_TARGET_ORIGIN_FORM) { - hs->tx_buf = 0; - start_send_data (hs, HTTP_STATUS_BAD_REQUEST, HTTP_CONTENT_TEXT_HTML); + start_send_data (hs, HTTP_STATUS_BAD_REQUEST); goto done; } @@ -353,13 +400,13 @@ hcs_ts_rx_callback (session_t *ts) HCS_DBG ("%v", args.buf); if (http_validate_abs_path_syntax (args.buf, &is_encoded)) { - start_send_data (hs, HTTP_STATUS_BAD_REQUEST, HTTP_CONTENT_TEXT_HTML); + start_send_data (hs, HTTP_STATUS_BAD_REQUEST); vec_free (args.buf); goto done; } if (is_encoded) { - u8 *decoded = http_percent_decode (args.buf); + u8 *decoded = http_percent_decode (args.buf, vec_len (args.buf)); vec_free (args.buf); args.buf = decoded; } @@ -374,13 +421,13 @@ hcs_ts_rx_callback (session_t *ts) ASSERT (rv == msg.data.headers_len); if (http_parse_headers (headers, &ht)) { - start_send_data (hs, HTTP_STATUS_BAD_REQUEST, - HTTP_CONTENT_TEXT_HTML); + 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_ACCEPT); + 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); @@ -438,7 +485,7 @@ hcs_ts_tx_callback (session_t *ts) } if (svm_fifo_set_event (ts->tx_fifo)) - session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX); + session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX); return 0; } @@ -554,6 +601,11 @@ hcs_attach () hcm->fifo_size ? hcm->fifo_size : 32 << 10; a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN; a->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = hcm->prealloc_fifos; + if (hcm->appns_id) + { + a->namespace_id = hcm->appns_id; + a->options[APP_OPTIONS_NAMESPACE_SECRET] = hcm->appns_secret; + } if (vnet_application_attach (a)) { @@ -588,15 +640,15 @@ hcs_listen () session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL; hcs_main_t *hcm = &hcs_main; vnet_listen_args_t _a, *a = &_a; - char *uri = "tcp://0.0.0.0/80"; u8 need_crypto; int rv; + char *uri; clib_memset (a, 0, sizeof (*a)); a->app_index = hcm->app_index; - if (hcm->uri) - uri = (char *) hcm->uri; + uri = (char *) hcm->uri; + ASSERT (uri); if (parse_uri (uri, &sep)) return -1; @@ -608,15 +660,24 @@ hcs_listen () if (need_crypto) { - session_endpoint_alloc_ext_cfg (&a->sep_ext, - TRANSPORT_ENDPT_EXT_CFG_CRYPTO); - a->sep_ext.ext_cfg->crypto.ckpair_index = hcm->ckpair_index; + transport_endpt_ext_cfg_t *ext_cfg = session_endpoint_add_ext_cfg ( + &a->sep_ext, TRANSPORT_ENDPT_EXT_CFG_CRYPTO, + sizeof (transport_endpt_crypto_cfg_t)); + ext_cfg->crypto.ckpair_index = hcm->ckpair_index; } rv = vnet_listen (a); + if (rv == 0) + { + hcs_uri_map_t *map; + pool_get_zero (hcm->uri_map_pool, map); + map->uri = vec_dup (uri); + map->handle = a->handle; + hash_set_mem (hcm->index_by_uri, map->uri, map - hcm->uri_map_pool); + } if (need_crypto) - clib_mem_free (a->sep_ext.ext_cfg); + session_endpoint_free_ext_cfgs (&a->sep_ext); return rv; } @@ -633,6 +694,43 @@ hcs_detach () } static int +hcs_unlisten () +{ + hcs_main_t *hcm = &hcs_main; + vnet_unlisten_args_t _a, *a = &_a; + char *uri; + int rv = 0; + uword *value; + + clib_memset (a, 0, sizeof (*a)); + a->app_index = hcm->app_index; + + uri = (char *) hcm->uri; + ASSERT (uri); + + value = hash_get_mem (hcm->index_by_uri, uri); + if (value) + { + hcs_uri_map_t *map = pool_elt_at_index (hcm->uri_map_pool, *value); + + a->handle = map->handle; + rv = vnet_unlisten (a); + if (rv == 0) + { + hash_unset_mem (hcm->index_by_uri, uri); + vec_free (map->uri); + pool_put (hcm->uri_map_pool, map); + if (pool_elts (hcm->uri_map_pool) == 0) + hcs_detach (); + } + } + else + return -1; + + return rv; +} + +static int hcs_create (vlib_main_t *vm) { vlib_thread_main_t *vtm = vlib_get_thread_main (); @@ -665,6 +763,8 @@ hcs_create_command_fn (vlib_main_t *vm, unformat_input_t *input, hcs_main_t *hcm = &hcs_main; u64 seg_size; int rv; + u32 listener_add = ~0; + clib_error_t *error = 0; hcm->prealloc_fifos = 0; hcm->private_segment_size = 0; @@ -683,13 +783,32 @@ hcs_create_command_fn (vlib_main_t *vm, unformat_input_t *input, hcm->private_segment_size = seg_size; else if (unformat (line_input, "fifo-size %d", &hcm->fifo_size)) hcm->fifo_size <<= 10; - else if (unformat (line_input, "uri %s", &hcm->uri)) + else if (unformat (line_input, "uri %_%v%_", &hcm->uri)) + ; + else if (unformat (line_input, "appns %_%v%_", &hcm->appns_id)) ; + else if (unformat (line_input, "secret %lu", &hcm->appns_secret)) + ; + else if (unformat (line_input, "listener")) + { + if (unformat (line_input, "add")) + listener_add = 1; + else if (unformat (line_input, "del")) + listener_add = 0; + else + { + unformat_free (line_input); + error = clib_error_return (0, "unknown input `%U'", + format_unformat_error, line_input); + goto done; + } + } else { unformat_free (line_input); - return clib_error_return (0, "unknown input `%U'", - format_unformat_error, line_input); + error = clib_error_return (0, "unknown input `%U'", + format_unformat_error, line_input); + goto done; } } @@ -697,10 +816,43 @@ hcs_create_command_fn (vlib_main_t *vm, unformat_input_t *input, start_server: + if (hcm->uri == 0) + hcm->uri = format (0, "tcp://0.0.0.0/80"); + if (hcm->app_index != (u32) ~0) - return clib_error_return (0, "test http server is already running"); + { + if (hcm->appns_id && (listener_add != ~0)) + { + error = clib_error_return ( + 0, "appns must not be specified for listener add/del"); + goto done; + } + if (listener_add == 1) + { + if (hcs_listen ()) + error = + clib_error_return (0, "failed to start listening %v", hcm->uri); + goto done; + } + else if (listener_add == 0) + { + rv = hcs_unlisten (); + if (rv != 0) + error = clib_error_return ( + 0, "failed to stop listening %v, rv = %d", hcm->uri, rv); + goto done; + } + else + { + error = clib_error_return (0, "test http server is already running"); + goto done; + } + } - vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */ ); + session_enable_disable_args_t args = { .is_en = 1, + .rt_engine_type = + RT_BACKEND_ENGINE_RULE_TABLE }; + vnet_session_enable_disable (vm, &args); rv = hcs_create (vm); switch (rv) @@ -708,16 +860,23 @@ start_server: case 0: break; default: - return clib_error_return (0, "server_create returned %d", rv); + { + error = clib_error_return (0, "server_create returned %d", rv); + goto done; + } } - return 0; +done: + vec_free (hcm->appns_id); + vec_free (hcm->uri); + return error; } VLIB_CLI_COMMAND (hcs_create_command, static) = { .path = "http cli server", .short_help = "http cli server [uri <uri>] [fifo-size <nbytes>] " - "[private-segment-size <nMG>] [prealloc-fifos <n>]", + "[private-segment-size <nMG>] [prealloc-fifos <n>] " + "[listener <add|del>] [appns <app-ns> secret <appns-secret>]", .function = hcs_create_command_fn, }; @@ -728,6 +887,7 @@ hcs_main_init (vlib_main_t *vm) hcs->app_index = ~0; hcs->vlib_main = vm; + hcs->index_by_uri = hash_create_vec (0, sizeof (u8), sizeof (uword)); return 0; } diff --git a/src/plugins/hs_apps/http_client.c b/src/plugins/hs_apps/http_client.c new file mode 100644 index 00000000000..05a87ec7de8 --- /dev/null +++ b/src/plugins/hs_apps/http_client.c @@ -0,0 +1,743 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2024 Cisco Systems, Inc. + */ + +#include <vnet/session/application.h> +#include <vnet/session/application_interface.h> +#include <vnet/session/session.h> +#include <http/http.h> +#include <http/http_header_names.h> +#include <http/http_content_types.h> +#include <http/http_status_codes.h> +#include <vppinfra/unix.h> + +typedef struct +{ + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + u32 session_index; + u32 thread_index; + u32 vpp_session_index; + u64 to_recv; + u8 is_closed; +} hc_session_t; + +typedef struct +{ + hc_session_t *sessions; + u32 thread_index; + vlib_main_t *vlib_main; +} hc_worker_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; + u8 is_file; + u8 use_ptr; + u8 *filename; + bool verbose; + f64 timeout; + http_req_method_t req_method; +} hc_main_t; + +typedef enum +{ + HC_CONNECT_FAILED = 1, + HC_TRANSPORT_CLOSED, + HC_REPLY_RECEIVED, +} hc_cli_signal_t; + +static hc_main_t hc_main; + +static inline hc_worker_t * +hc_worker_get (u32 thread_index) +{ + return &hc_main.wrk[thread_index]; +} + +static inline hc_session_t * +hc_session_get (u32 session_index, u32 thread_index) +{ + hc_worker_t *wrk = hc_worker_get (thread_index); + wrk->vlib_main = vlib_get_main_by_index (thread_index); + return pool_elt_at_index (wrk->sessions, session_index); +} + +static void +hc_ho_session_free (u32 hs_index) +{ + hc_worker_t *wrk = hc_worker_get (0); + pool_put_index (wrk->sessions, hs_index); +} + +static hc_session_t * +hc_session_alloc (hc_worker_t *wrk) +{ + hc_session_t *s; + + pool_get_zero (wrk->sessions, s); + s->session_index = s - wrk->sessions; + s->thread_index = wrk->thread_index; + + return s; +} + +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_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; + + 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); + 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); + 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; + HTTP_DBG (1, "new hc_index: %d", new_hc_session->session_index); + s->opaque = new_hc_index; + + if (hcm->req_method == HTTP_REQ_POST) + { + if (hcm->is_file) + http_add_header ( + &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), + 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, + vec_len (header->name), (const char *) header->value, + vec_len (header->value)); + + hcm->headers_buf = http_serialize_headers (req_headers); + vec_free (req_headers); + + msg.method_type = hcm->req_method; + if (hcm->req_method == HTTP_REQ_POST) + msg.data.body_len = vec_len (hcm->data); + else + msg.data.body_len = 0; + + msg.type = HTTP_MSG_REQUEST; + /* request target */ + msg.data.target_form = HTTP_TARGET_ORIGIN_FORM; + msg.data.target_path_len = vec_len (hcm->target); + /* custom headers */ + msg.data.headers_len = vec_len (hcm->headers_buf); + /* total length */ + msg.data.len = + msg.data.target_path_len + msg.data.headers_len + 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; + } + + 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) + { + 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 void +hc_session_disconnect_callback (session_t *s) +{ + hc_main_t *hcm = &hc_main; + 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))) + clib_warning ("warning: disconnect returned: %U", format_session_error, + rv); +} + +static void +hc_session_transport_closed_callback (session_t *s) +{ + hc_main_t *hcm = &hc_main; + vlib_process_signal_event_mt (hcm->wrk->vlib_main, hcm->cli_node_index, + HC_TRANSPORT_CLOSED, 0); +} + +static void +hc_ho_cleanup_callback (session_t *ts) +{ + HTTP_DBG (1, "ho hc_index: %d:", ts->opaque); + hc_ho_session_free (ts->opaque); +} + +static void +hc_session_reset_callback (session_t *s) +{ + hc_main_t *hcm = &hc_main; + hc_session_t *hc_session; + vnet_disconnect_args_t _a = { 0 }, *a = &_a; + int rv; + + hc_session = hc_session_get (s->opaque, s->thread_index); + hc_session->is_closed = 1; + + a->handle = session_handle (s); + a->app_index = hcm->app_index; + if ((rv = vnet_disconnect_session (a))) + clib_warning ("warning: disconnect returned: %U", format_session_error, + rv); +} + +static int +hc_rx_callback (session_t *s) +{ + hc_main_t *hcm = &hc_main; + hc_session_t *hc_session; + http_msg_t msg; + int rv; + + hc_session = hc_session_get (s->opaque, s->thread_index); + + if (hc_session->is_closed) + { + clib_warning ("hc_session_index[%d] is closed", s->opaque); + return -1; + } + + if (hc_session->to_recv == 0) + { + rv = svm_fifo_dequeue (s->rx_fifo, sizeof (msg), (u8 *) &msg); + ASSERT (rv == sizeof (msg)); + + if (msg.type != HTTP_MSG_REPLY) + { + clib_warning ("unexpected msg type %d", msg.type); + return -1; + } + + if (msg.data.headers_len) + { + http_header_table_t *ht; + 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); + + 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); + } + + if (msg.data.body_len == 0) + { + svm_fifo_dequeue_drop_all (s->rx_fifo); + goto done; + } + + /* drop everything up to body */ + svm_fifo_dequeue_drop (s->rx_fifo, msg.data.body_offset); + hc_session->to_recv = msg.data.body_len; + if (msg.code != HTTP_STATUS_OK && hc_session->to_recv == 0) + { + goto done; + } + vec_validate (hcm->http_response, msg.data.body_len - 1); + vec_reset_length (hcm->http_response); + } + + u32 max_deq = svm_fifo_max_dequeue (s->rx_fifo); + + 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); + return -1; + } + + ASSERT (rv == n_deq); + vec_set_len (hcm->http_response, curr + n_deq); + ASSERT (hc_session->to_recv >= rv); + hc_session->to_recv -= rv; + +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); + } + + return 0; +} + +static int +hc_tx_callback (session_t *s) +{ + hc_main_t *hcm = &hc_main; + u64 to_send; + int rv; + + to_send = vec_len (hcm->data) - hcm->data_offset; + rv = svm_fifo_enqueue (s->tx_fifo, to_send, hcm->data + hcm->data_offset); + + if (rv <= 0) + { + svm_fifo_add_want_deq_ntf (s->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF); + return 0; + } + + if (rv < to_send) + { + hcm->data_offset += rv; + svm_fifo_add_want_deq_ntf (s->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF); + } + + if (svm_fifo_set_event (s->tx_fifo)) + session_program_tx_io_evt (s->handle, SESSION_IO_EVT_TX); + + return 0; +} + +static session_cb_vft_t hc_session_cb_vft = { + .session_connected_callback = hc_session_connected_callback, + .session_disconnect_callback = hc_session_disconnect_callback, + .session_transport_closed_callback = hc_session_transport_closed_callback, + .session_reset_callback = hc_session_reset_callback, + .builtin_app_rx_callback = hc_rx_callback, + .builtin_app_tx_callback = hc_tx_callback, + .half_open_cleanup_callback = hc_ho_cleanup_callback, +}; + +static clib_error_t * +hc_attach () +{ + hc_main_t *hcm = &hc_main; + vnet_app_attach_args_t _a, *a = &_a; + u64 options[18]; + int rv; + + clib_memset (a, 0, sizeof (*a)); + clib_memset (options, 0, sizeof (options)); + + a->api_client_index = APP_INVALID_INDEX; + a->name = format (0, "http_client"); + a->session_cb_vft = &hc_session_cb_vft; + a->options = options; + a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN; + + if ((rv = vnet_application_attach (a))) + return clib_error_return (0, "attach returned: %U", format_session_error, + rv); + + hcm->app_index = a->app_index; + vec_free (a->name); + hcm->attached = 1; + + return 0; +} + +static int +hc_connect_rpc (void *rpc_args) +{ + vnet_connect_args_t *a = rpc_args; + int rv; + + rv = vnet_connect (a); + if (rv > 0) + clib_warning (0, "connect returned: %U", format_session_error, rv); + + vec_free (a); + return rv; +} + +static void +hc_connect () +{ + hc_main_t *hcm = &hc_main; + vnet_connect_args_t *a = 0; + hc_worker_t *wrk; + hc_session_t *hc_session; + + vec_validate (a, 0); + clib_memset (a, 0, sizeof (a[0])); + + clib_memcpy (&a->sep_ext, &hcm->connect_sep, sizeof (hcm->connect_sep)); + a->app_index = hcm->app_index; + + /* allocate http session on main thread */ + wrk = hc_worker_get (0); + hc_session = hc_session_alloc (wrk); + a->api_context = hc_session->session_index; + + session_send_rpc_evt_to_thread_force (transport_cl_thread (), hc_connect_rpc, + a); +} + +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; + uword event_type, *event_data = 0; + clib_error_t *err; + FILE *file_ptr; + + 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); + event_type = vlib_process_get_events (vm, &event_data); + switch (event_type) + { + case ~0: + err = clib_error_return (0, "error: timeout"); + break; + case HC_CONNECT_FAILED: + err = clib_error_return (0, "error: failed to connect"); + break; + case HC_TRANSPORT_CLOSED: + err = clib_error_return (0, "error: transport closed"); + break; + case HC_REPLY_RECEIVED: + if (hcm->filename) + { + file_ptr = + fopen ((char *) format (0, "/tmp/%v", hcm->filename), "w"); + if (file_ptr == NULL) + { + vlib_cli_output (vm, "couldn't open file %v", hcm->filename); + } + else + { + fprintf (file_ptr, "< %s\n< %s\n< %s", hcm->response_status, + hcm->resp_headers, hcm->http_response); + fclose (file_ptr); + vlib_cli_output (vm, "file saved (/tmp/%v)", hcm->filename); + } + } + if (hcm->verbose) + vlib_cli_output (vm, "< %v\n< %v", hcm->response_status, + hcm->resp_headers); + vlib_cli_output (vm, "<\n%v", hcm->http_response); + + break; + default: + err = clib_error_return (0, "error: unexpected event %d", event_type); + break; + } + + vec_free (event_data); + return err; +} + +static int +hc_detach () +{ + hc_main_t *hcm = &hc_main; + vnet_app_detach_args_t _da, *da = &_da; + int rv; + + if (!hcm->attached) + return 0; + + da->app_index = hcm->app_index; + da->api_client_index = APP_INVALID_INDEX; + rv = vnet_application_detach (da); + hcm->attached = 0; + hcm->app_index = APP_INVALID_INDEX; + + return rv; +} + +static void +hcc_worker_cleanup (hc_worker_t *wrk) +{ + pool_free (wrk->sessions); +} + +static void +hc_cleanup () +{ + hc_main_t *hcm = &hc_main; + hc_worker_t *wrk; + http_header_ht_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); + vec_free (hcm->response_status); + vec_free (hcm->wrk); + vec_free (hcm->filename); + vec_foreach (header, hcm->custom_header) + { + vec_free (header->name); + vec_free (header->value); + } + vec_free (hcm->custom_header); +} + +static clib_error_t * +hc_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + hc_main_t *hcm = &hc_main; + clib_error_t *err = 0; + unformat_input_t _line_input, *line_input = &_line_input; + u8 *path = 0; + u8 *file_data; + http_header_ht_t new_header; + u8 *name; + u8 *value; + int rv; + hcm->timeout = 10; + + 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"); + + hcm->req_method = + (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) && + unformat (line_input, "post") ? + HTTP_REQ_POST : + HTTP_REQ_GET; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "uri %s", &hcm->uri)) + ; + else if (unformat (line_input, "data %v", &hcm->data)) + hcm->is_file = 0; + else if (unformat (line_input, "target %s", &hcm->target)) + ; + else if (unformat (line_input, "file %s", &path)) + hcm->is_file = 1; + else if (unformat (line_input, "use-ptr")) + hcm->use_ptr = 1; + else if (unformat (line_input, "save-to %s", &hcm->filename)) + { + if (strstr ((char *) hcm->filename, "..") || + strchr ((char *) hcm->filename, '/')) + { + err = clib_error_return ( + 0, "illegal characters in filename '%v'", hcm->filename); + goto done; + } + } + else if (unformat (line_input, "header %v:%v", &name, &value)) + { + new_header.name = name; + new_header.value = value; + vec_add1 (hcm->custom_header, new_header); + } + else if (unformat (line_input, "verbose")) + hcm->verbose = true; + else if (unformat (line_input, "timeout %f", &hcm->timeout)) + ; + else + { + err = clib_error_return (0, "unknown input `%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (!hcm->uri) + { + err = clib_error_return (0, "URI not defined"); + goto done; + } + if (!hcm->target) + { + err = clib_error_return (0, "target not defined"); + goto done; + } + if (!hcm->data && hcm->req_method == HTTP_REQ_POST) + { + if (path) + { + err = clib_file_contents ((char *) path, &file_data); + if (err) + goto done; + hcm->data = file_data; + } + else + { + err = clib_error_return (0, "data not defined"); + goto done; + } + } + + if ((rv = parse_uri ((char *) hcm->uri, &hcm->connect_sep))) + { + err = + clib_error_return (0, "URI parse error: %U", format_session_error, rv); + goto done; + } + + session_enable_disable_args_t args = { .is_en = 1, + .rt_engine_type = + RT_BACKEND_ENGINE_RULE_TABLE }; + vlib_worker_thread_barrier_sync (vm); + vnet_session_enable_disable (vm, &args); + vlib_worker_thread_barrier_release (vm); + + hcm->cli_node_index = vlib_get_current_process (vm)->node_runtime.node_index; + + err = hc_run (vm); + + if ((rv = hc_detach ())) + { + /* don't override last error */ + if (!err) + err = clib_error_return (0, "detach returned: %U", + format_session_error, rv); + else + clib_warning ("warning: detach returned: %U", format_session_error, + rv); + } + +done: + vec_free (path); + hc_cleanup (); + unformat_free (line_input); + return err; +} + +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)]", + .function = hc_command_fn, + .is_mp_safe = 1, +}; + +static clib_error_t * +hc_main_init () +{ + hc_main_t *hcm = &hc_main; + hcm->app_index = APP_INVALID_INDEX; + return 0; +} + +VLIB_INIT_FUNCTION (hc_main_init); diff --git a/src/plugins/hs_apps/http_client_cli.c b/src/plugins/hs_apps/http_client_cli.c index a99169bafea..861af7f03e2 100644 --- a/src/plugins/hs_apps/http_client_cli.c +++ b/src/plugins/hs_apps/http_client_cli.c @@ -16,6 +16,9 @@ #include <vnet/session/application_interface.h> #include <vnet/session/session.h> #include <http/http.h> +#include <http/http_header_names.h> +#include <http/http_content_types.h> +#include <http/http_status_codes.h> #define HCC_DEBUG 0 @@ -32,14 +35,14 @@ typedef struct u32 thread_index; u32 rx_offset; u32 vpp_session_index; - u32 to_recv; + u64 to_recv; u8 is_closed; + http_header_t *req_headers; } hcc_session_t; typedef struct { hcc_session_t *sessions; - u8 *rx_buf; u32 thread_index; } hcc_worker_t; @@ -96,10 +99,10 @@ hcc_session_get (u32 hs_index, u32 thread_index) } static void -hcc_session_free (u32 thread_index, hcc_session_t *hs) +hcc_ho_session_free (u32 hs_index) { - hcc_worker_t *wrk = hcc_worker_get (thread_index); - pool_put (wrk->sessions, hs); + hcc_worker_t *wrk = hcc_worker_get (0); + pool_put_index (wrk->sessions, hs_index); } static int @@ -128,9 +131,11 @@ hcc_ts_connected_callback (u32 app_index, u32 hc_index, session_t *as, hcc_session_t *hs, *new_hs; hcc_worker_t *wrk; http_msg_t msg; + u8 *headers_buf; + u32 new_hs_index; int rv; - HCC_DBG ("hc_index: %d", hc_index); + HCC_DBG ("ho hc_index: %d", hc_index); if (err) { @@ -141,32 +146,53 @@ hcc_ts_connected_callback (u32 app_index, u32 hc_index, session_t *as, return -1; } - /* TODO delete half open session once the support is added in http layer */ hs = hcc_session_get (hc_index, 0); wrk = hcc_worker_get (as->thread_index); new_hs = hcc_session_alloc (wrk); + new_hs_index = new_hs->session_index; clib_memcpy_fast (new_hs, hs, sizeof (*hs)); - - hs->vpp_session_index = as->session_index; + new_hs->session_index = new_hs_index; + new_hs->thread_index = as->thread_index; + new_hs->vpp_session_index = as->session_index; + HCC_DBG ("new hc_index: %d", new_hs->session_index); + as->opaque = new_hs_index; + + http_add_header (&new_hs->req_headers, + http_header_name_token (HTTP_HEADER_ACCEPT), + http_content_type_token (HTTP_CONTENT_TEXT_HTML)); + headers_buf = http_serialize_headers (new_hs->req_headers); + vec_free (new_hs->req_headers); msg.type = HTTP_MSG_REQUEST; msg.method_type = HTTP_REQ_GET; - msg.content_type = HTTP_CONTENT_TEXT_HTML; + /* request target */ + msg.data.target_form = HTTP_TARGET_ORIGIN_FORM; + msg.data.target_path_offset = 0; + msg.data.target_path_len = vec_len (hcm->http_query); + /* custom headers */ + msg.data.headers_offset = msg.data.target_path_len; + msg.data.headers_len = vec_len (headers_buf); + /* request body */ + msg.data.body_len = 0; + /* data type and total length */ msg.data.type = HTTP_MSG_DATA_INLINE; - msg.data.len = vec_len (hcm->http_query); + msg.data.len = + msg.data.target_path_len + msg.data.headers_len + msg.data.body_len; - svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) }, - { hcm->http_query, vec_len (hcm->http_query) } }; + svm_fifo_seg_t segs[3] = { { (u8 *) &msg, sizeof (msg) }, + { hcm->http_query, vec_len (hcm->http_query) }, + { headers_buf, vec_len (headers_buf) } }; - rv = svm_fifo_enqueue_segments (as->tx_fifo, segs, 2, 0 /* allow partial */); - if (rv < 0 || rv != sizeof (msg) + vec_len (hcm->http_query)) + rv = svm_fifo_enqueue_segments (as->tx_fifo, segs, 3, 0 /* allow partial */); + vec_free (headers_buf); + if (rv < 0 || rv != sizeof (msg) + msg.data.len) { clib_warning ("failed app enqueue"); return -1; } if (svm_fifo_set_event (as->tx_fifo)) - session_send_io_evt_to_thread (as->tx_fifo, SESSION_IO_EVT_TX); + session_program_tx_io_evt (as->handle, SESSION_IO_EVT_TX); return 0; } @@ -221,23 +247,32 @@ hcc_ts_rx_callback (session_t *ts) if (hs->to_recv == 0) { + /* read the http message header */ rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg); ASSERT (rv == sizeof (msg)); - if (msg.type != HTTP_MSG_REPLY || msg.code != HTTP_STATUS_OK) + if (msg.type != HTTP_MSG_REPLY) { clib_warning ("unexpected msg type %d", msg.type); return 0; } - vec_validate (hcm->http_response, msg.data.len - 1); + /* drop everything up to body */ + svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.body_offset); + hs->to_recv = msg.data.body_len; + if (msg.code != HTTP_STATUS_OK && hs->to_recv == 0) + { + hcm->http_response = format (0, "request failed, response code: %U", + format_http_status_code, msg.code); + goto done; + } + vec_validate (hcm->http_response, msg.data.body_len - 1); vec_reset_length (hcm->http_response); - hs->to_recv = msg.data.len; } u32 max_deq = svm_fifo_max_dequeue (ts->rx_fifo); u32 n_deq = clib_min (hs->to_recv, max_deq); - u32 curr = vec_len (hcm->http_response); + u64 curr = vec_len (hcm->http_response); rv = svm_fifo_dequeue (ts->rx_fifo, n_deq, hcm->http_response + curr); if (rv < 0) { @@ -251,10 +286,12 @@ hcc_ts_rx_callback (session_t *ts) vec_set_len (hcm->http_response, curr + n_deq); ASSERT (hs->to_recv >= rv); hs->to_recv -= rv; - HCC_DBG ("app rcvd %d, remains %d", rv, hs->to_recv); + HCC_DBG ("app rcvd %d, remains %llu", rv, hs->to_recv); +done: if (hs->to_recv == 0) { + HCC_DBG ("all data received, going to disconnect"); hcc_session_disconnect (ts); vlib_process_signal_event_mt (hcm->vlib_main, hcm->cli_node_index, HCC_REPLY_RECEIVED, 0); @@ -264,18 +301,6 @@ hcc_ts_rx_callback (session_t *ts) } static void -hcc_ts_cleanup_callback (session_t *s, session_cleanup_ntf_t ntf) -{ - hcc_session_t *hs; - - hs = hcc_session_get (s->thread_index, s->opaque); - if (!hs) - return; - - hcc_session_free (s->thread_index, hs); -} - -static void hcc_ts_transport_closed (session_t *s) { hcc_main_t *hcm = &hcc_main; @@ -286,6 +311,13 @@ hcc_ts_transport_closed (session_t *s) HCC_TRANSPORT_CLOSED, 0); } +static void +hcc_ho_cleanup_callback (session_t *ts) +{ + HCC_DBG ("ho hc_index: %d:", ts->opaque); + hcc_ho_session_free (ts->opaque); +} + static session_cb_vft_t hcc_session_cb_vft = { .session_accept_callback = hcc_ts_accept_callback, .session_disconnect_callback = hcc_ts_disconnect_callback, @@ -293,8 +325,8 @@ static session_cb_vft_t hcc_session_cb_vft = { .builtin_app_rx_callback = hcc_ts_rx_callback, .builtin_app_tx_callback = hcc_ts_tx_callback, .session_reset_callback = hcc_ts_reset_callback, - .session_cleanup_callback = hcc_ts_cleanup_callback, .session_transport_closed_callback = hcc_ts_transport_closed, + .half_open_cleanup_callback = hcc_ho_cleanup_callback, }; static clib_error_t * @@ -349,6 +381,7 @@ hcc_connect_rpc (void *rpc_args) if (rv) clib_warning (0, "connect returned: %U", format_session_error, rv); + session_endpoint_free_ext_cfgs (&a->sep_ext); vec_free (a); return rv; } @@ -367,6 +400,7 @@ hcc_connect () hcc_main_t *hcm = &hcc_main; hcc_worker_t *wrk; hcc_session_t *hs; + transport_endpt_ext_cfg_t *ext_cfg; vec_validate (a, 0); clib_memset (a, 0, sizeof (a[0])); @@ -374,6 +408,11 @@ hcc_connect () clib_memcpy (&a->sep_ext, &hcm->connect_sep, sizeof (hcm->connect_sep)); a->app_index = hcm->app_index; + /* set http (response) timeout to 10 seconds */ + ext_cfg = session_endpoint_add_ext_cfg ( + &a->sep_ext, TRANSPORT_ENDPT_EXT_CFG_HTTP, sizeof (ext_cfg->opaque)); + ext_cfg->opaque = 10; + /* allocate http session on main thread */ wrk = hcc_worker_get (0); hs = hcc_session_alloc (wrk); @@ -394,7 +433,7 @@ hcc_run (vlib_main_t *vm, int print_output) hcc_worker_t *wrk; num_threads = 1 /* main thread */ + vtm->n_threads; - vec_validate (hcm->wrk, num_threads); + vec_validate (hcm->wrk, num_threads - 1); vec_foreach (wrk, hcm->wrk) { wrk->thread_index = wrk - hcm->wrk; @@ -423,7 +462,6 @@ hcc_run (vlib_main_t *vm, int print_output) case HCC_REPLY_RECEIVED: if (print_output) vlib_cli_output (vm, "%v", hcm->http_response); - vec_free (hcm->http_response); break; case HCC_TRANSPORT_CLOSED: err = clib_error_return (0, "error, transport closed"); @@ -460,6 +498,28 @@ hcc_detach () return rv; } +static void +hcc_worker_cleanup (hcc_worker_t *wrk) +{ + pool_free (wrk->sessions); +} + +static void +hcc_cleanup () +{ + hcc_main_t *hcm = &hcc_main; + hcc_worker_t *wrk; + + vec_foreach (wrk, hcm->wrk) + hcc_worker_cleanup (wrk); + + vec_free (hcm->uri); + vec_free (hcm->http_query); + vec_free (hcm->http_response); + vec_free (hcm->appns_id); + vec_free (hcm->wrk); +} + static clib_error_t * hcc_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd) @@ -509,7 +569,6 @@ hcc_command_fn (vlib_main_t *vm, unformat_input_t *input, } } - vec_free (hcm->appns_id); hcm->appns_id = appns_id; hcm->cli_node_index = vlib_get_current_process (vm)->node_runtime.node_index; @@ -525,8 +584,11 @@ hcc_command_fn (vlib_main_t *vm, unformat_input_t *input, goto done; } + session_enable_disable_args_t args = { .is_en = 1, + .rt_engine_type = + RT_BACKEND_ENGINE_RULE_TABLE }; vlib_worker_thread_barrier_sync (vm); - vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */); + vnet_session_enable_disable (vm, &args); vlib_worker_thread_barrier_release (vm); err = hcc_run (vm, print_output); @@ -540,8 +602,7 @@ hcc_command_fn (vlib_main_t *vm, unformat_input_t *input, } done: - vec_free (hcm->uri); - vec_free (hcm->http_query); + hcc_cleanup (); unformat_free (line_input); return err; } diff --git a/src/plugins/hs_apps/http_tps.c b/src/plugins/hs_apps/http_tps.c index 3a086501f86..a40a31caf63 100644 --- a/src/plugins/hs_apps/http_tps.c +++ b/src/plugins/hs_apps/http_tps.c @@ -17,6 +17,10 @@ #include <vnet/session/application_interface.h> #include <vnet/session/session.h> #include <http/http.h> +#include <http/http_header_names.h> +#include <http/http_content_types.h> + +#define HTS_RX_BUF_SIZE (64 << 10) typedef struct { @@ -26,6 +30,8 @@ typedef struct u64 data_len; u64 data_offset; u32 vpp_session_index; + u64 left_recv; + u64 total_recv; union { /** threshold after which connection is closed */ @@ -34,6 +40,8 @@ typedef struct u32 close_rate; }; u8 *uri; + u8 *rx_buf; + http_header_t *resp_headers; } hts_session_t; typedef struct hts_listen_cfg_ @@ -102,6 +110,8 @@ hts_session_free (hts_session_t *hs) if (htm->debug_level > 0) clib_warning ("Freeing session %u", hs->session_index); + vec_free (hs->rx_buf); + if (CLIB_DEBUG) clib_memset (hs, 0xfa, sizeof (*hs)); @@ -151,7 +161,7 @@ hts_session_tx_zc (hts_session_t *hs, session_t *ts) svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF); if (svm_fifo_set_event (ts->tx_fifo)) - session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX); + session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX); } static void @@ -198,7 +208,7 @@ hts_session_tx_no_zc (hts_session_t *hs, session_t *ts) svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF); if (svm_fifo_set_event (ts->tx_fifo)) - session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX); + session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX); } static inline void @@ -223,22 +233,46 @@ hts_start_send_data (hts_session_t *hs, http_status_code_t status) { http_msg_t msg; session_t *ts; + u8 *headers_buf = 0; + u32 n_segs = 1; + svm_fifo_seg_t seg[2]; int rv; + if (vec_len (hs->resp_headers)) + { + headers_buf = http_serialize_headers (hs->resp_headers); + vec_free (hs->resp_headers); + msg.data.headers_offset = 0; + msg.data.headers_len = vec_len (headers_buf); + seg[1].data = headers_buf; + seg[1].len = msg.data.headers_len; + n_segs = 2; + } + else + { + msg.data.headers_offset = 0; + msg.data.headers_len = 0; + } + msg.type = HTTP_MSG_REPLY; msg.code = status; - msg.content_type = HTTP_CONTENT_APP_OCTET_STREAM; msg.data.type = HTTP_MSG_DATA_INLINE; - msg.data.len = hs->data_len; + msg.data.body_len = hs->data_len; + msg.data.body_offset = msg.data.headers_len; + msg.data.len = msg.data.body_len + msg.data.headers_len; + seg[0].data = (u8 *) &msg; + seg[0].len = sizeof (msg); ts = session_get (hs->vpp_session_index, hs->thread_index); - rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg); - ASSERT (rv == sizeof (msg)); + rv = svm_fifo_enqueue_segments (ts->tx_fifo, seg, n_segs, + 0 /* allow partial */); + vec_free (headers_buf); + ASSERT (rv == (sizeof (msg) + msg.data.headers_len)); - if (!msg.data.len) + if (!msg.data.body_len) { if (svm_fifo_set_event (ts->tx_fifo)) - session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX); + session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX); return; } @@ -286,6 +320,10 @@ try_test_file (hts_session_t *hs, u8 *target) } } + http_add_header (&hs->resp_headers, + http_header_name_token (HTTP_HEADER_CONTENT_TYPE), + http_content_type_token (HTTP_CONTENT_APP_OCTET_STREAM)); + hts_start_send_data (hs, HTTP_STATUS_OK); done: @@ -294,6 +332,40 @@ done: return rc; } +static inline void +hts_session_rx_body (hts_session_t *hs, session_t *ts) +{ + hts_main_t *htm = &hts_main; + u32 n_deq; + int rv; + + n_deq = svm_fifo_max_dequeue (ts->rx_fifo); + if (!htm->no_zc) + { + svm_fifo_dequeue_drop_all (ts->rx_fifo); + } + else + { + n_deq = clib_min (n_deq, HTS_RX_BUF_SIZE); + rv = svm_fifo_dequeue (ts->rx_fifo, n_deq, hs->rx_buf); + ASSERT (rv == n_deq); + } + hs->left_recv -= n_deq; + + if (hs->close_threshold > 0) + { + if ((f64) (hs->total_recv - hs->left_recv) / hs->total_recv > + hs->close_threshold) + hts_disconnect_transport (hs); + } + + if (hs->left_recv == 0) + { + hts_start_send_data (hs, HTTP_STATUS_OK); + vec_free (hs->rx_buf); + } +} + static int hts_ts_rx_callback (session_t *ts) { @@ -305,38 +377,77 @@ hts_ts_rx_callback (session_t *ts) hs = hts_session_get (ts->thread_index, ts->opaque); - /* Read the http message header */ - rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg); - ASSERT (rv == sizeof (msg)); - - if (msg.type != HTTP_MSG_REQUEST || msg.method_type != HTTP_REQ_GET) + if (hs->left_recv == 0) { - hts_start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED); - goto done; - } + hs->data_len = 0; + hs->resp_headers = 0; + hs->rx_buf = 0; - if (msg.data.target_path_len == 0 || - msg.data.target_form != HTTP_TARGET_ORIGIN_FORM) - { - hts_start_send_data (hs, HTTP_STATUS_BAD_REQUEST); - goto done; - } + /* Read the http message header */ + rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg); + ASSERT (rv == sizeof (msg)); - vec_validate (target, msg.data.target_path_len - 1); - rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_path_offset, - msg.data.target_path_len, target); - ASSERT (rv == msg.data.target_path_len); + if (msg.type != HTTP_MSG_REQUEST) + { + hts_start_send_data (hs, HTTP_STATUS_INTERNAL_ERROR); + goto done; + } + if (msg.method_type != HTTP_REQ_GET && msg.method_type != HTTP_REQ_POST) + { + http_add_header (&hs->resp_headers, + http_header_name_token (HTTP_HEADER_ALLOW), + http_token_lit ("GET, POST")); + hts_start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED); + goto done; + } - if (htm->debug_level) - clib_warning ("Request target: %v", target); + if (msg.data.target_path_len == 0 || + msg.data.target_form != HTTP_TARGET_ORIGIN_FORM) + { + hts_start_send_data (hs, HTTP_STATUS_BAD_REQUEST); + goto done; + } - if (try_test_file (hs, target)) - hts_start_send_data (hs, HTTP_STATUS_NOT_FOUND); + vec_validate (target, msg.data.target_path_len - 1); + rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_path_offset, + msg.data.target_path_len, target); + ASSERT (rv == msg.data.target_path_len); - vec_free (target); + if (htm->debug_level) + clib_warning ("%s request target: %v", + msg.method_type == HTTP_REQ_GET ? "GET" : "POST", + target); + + if (msg.method_type == HTTP_REQ_GET) + { + if (try_test_file (hs, target)) + hts_start_send_data (hs, HTTP_STATUS_NOT_FOUND); + vec_free (target); + } + else + { + vec_free (target); + if (!msg.data.body_len) + { + hts_start_send_data (hs, HTTP_STATUS_BAD_REQUEST); + goto done; + } + /* drop everything up to body */ + svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.body_offset); + hs->left_recv = msg.data.body_len; + hs->total_recv = msg.data.body_len; + if (htm->no_zc) + vec_validate (hs->rx_buf, HTS_RX_BUF_SIZE - 1); + hts_session_rx_body (hs, ts); + return 0; + } + + done: + svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.len); + } + else + hts_session_rx_body (hs, ts); -done: - svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.len); return 0; } @@ -363,6 +474,7 @@ hts_ts_accept_callback (session_t *ts) hs = hts_session_alloc (ts->thread_index); hs->vpp_session_index = ts->session_index; + hs->left_recv = 0; ts->opaque = hs->session_index; ts->session_state = SESSION_STATE_READY; @@ -529,15 +641,16 @@ hts_start_listen (hts_main_t *htm, session_endpoint_cfg_t *sep, u8 *uri, if (need_crypto) { - session_endpoint_alloc_ext_cfg (&a->sep_ext, - TRANSPORT_ENDPT_EXT_CFG_CRYPTO); - a->sep_ext.ext_cfg->crypto.ckpair_index = htm->ckpair_index; + transport_endpt_ext_cfg_t *ext_cfg = session_endpoint_add_ext_cfg ( + &a->sep_ext, TRANSPORT_ENDPT_EXT_CFG_CRYPTO, + sizeof (transport_endpt_crypto_cfg_t)); + ext_cfg->crypto.ckpair_index = htm->ckpair_index; } rv = vnet_listen (a); if (need_crypto) - clib_mem_free (a->sep_ext.ext_cfg); + session_endpoint_free_ext_cfgs (&a->sep_ext); if (rv) return rv; @@ -726,7 +839,10 @@ start_server: if (htm->app_index == (u32) ~0) { - vnet_session_enable_disable (vm, 1 /* is_enable */); + session_enable_disable_args_t args = { .is_en = 1, + .rt_engine_type = + RT_BACKEND_ENGINE_RULE_TABLE }; + vnet_session_enable_disable (vm, &args); if (hts_create (vm)) { diff --git a/src/plugins/hs_apps/proxy.c b/src/plugins/hs_apps/proxy.c index e8fedf921a5..d7fe6fb54df 100644 --- a/src/plugins/hs_apps/proxy.c +++ b/src/plugins/hs_apps/proxy.c @@ -19,50 +19,145 @@ #include <vnet/session/application_interface.h> #include <hs_apps/proxy.h> #include <vnet/tcp/tcp.h> +#include <http/http.h> +#include <http/http_header_names.h> proxy_main_t proxy_main; #define TCP_MSS 1460 -typedef struct +static proxy_session_side_ctx_t * +proxy_session_side_ctx_alloc (proxy_worker_t *wrk) { - session_endpoint_cfg_t sep; - u32 app_index; - u32 api_context; -} proxy_connect_args_t; + proxy_session_side_ctx_t *ctx; + + pool_get_zero (wrk->ctx_pool, ctx); + ctx->sc_index = ctx - wrk->ctx_pool; + ctx->ps_index = ~0; + + return ctx; +} static void -proxy_cb_fn (void *data, u32 data_len) +proxy_session_side_ctx_free (proxy_worker_t *wrk, + proxy_session_side_ctx_t *ctx) { - proxy_connect_args_t *pa = (proxy_connect_args_t *) data; - vnet_connect_args_t a; + pool_put (wrk->ctx_pool, ctx); +} - clib_memset (&a, 0, sizeof (a)); - a.api_context = pa->api_context; - a.app_index = pa->app_index; - clib_memcpy (&a.sep_ext, &pa->sep, sizeof (pa->sep)); - vnet_connect (&a); - if (a.sep_ext.ext_cfg) - clib_mem_free (a.sep_ext.ext_cfg); +static proxy_session_side_ctx_t * +proxy_session_side_ctx_get (proxy_worker_t *wrk, u32 ctx_index) +{ + return pool_elt_at_index (wrk->ctx_pool, ctx_index); } static void -proxy_call_main_thread (vnet_connect_args_t * a) +proxy_send_http_resp (session_t *s, http_status_code_t sc, + http_header_t *resp_headers) { - if (vlib_get_thread_index () == 0) + http_msg_t msg; + int rv; + u8 *headers_buf = 0; + + if (vec_len (resp_headers)) { - vnet_connect (a); - if (a->sep_ext.ext_cfg) - clib_mem_free (a->sep_ext.ext_cfg); + headers_buf = http_serialize_headers (resp_headers); + msg.data.len = msg.data.headers_len = vec_len (headers_buf); } else + msg.data.len = msg.data.headers_len = 0; + + msg.type = HTTP_MSG_REPLY; + msg.code = sc; + msg.data.type = HTTP_MSG_DATA_INLINE; + msg.data.headers_offset = 0; + msg.data.body_len = 0; + msg.data.body_offset = 0; + rv = svm_fifo_enqueue (s->tx_fifo, sizeof (msg), (u8 *) &msg); + ASSERT (rv == sizeof (msg)); + if (msg.data.headers_len) + { + rv = svm_fifo_enqueue (s->tx_fifo, vec_len (headers_buf), headers_buf); + ASSERT (rv == vec_len (headers_buf)); + vec_free (headers_buf); + } + + if (svm_fifo_set_event (s->tx_fifo)) + session_program_tx_io_evt (s->handle, SESSION_IO_EVT_TX); +} + +static void +proxy_do_connect (vnet_connect_args_t *a) +{ + ASSERT (session_vlib_thread_is_cl_thread ()); + vnet_connect (a); + session_endpoint_free_ext_cfgs (&a->sep_ext); +} + +static void +proxy_handle_connects_rpc (void *args) +{ + u32 thread_index = pointer_to_uword (args), n_connects = 0, n_pending; + proxy_worker_t *wrk; + u32 max_connects; + + wrk = proxy_worker_get (thread_index); + + clib_spinlock_lock (&wrk->pending_connects_lock); + + n_pending = clib_fifo_elts (wrk->pending_connects); + max_connects = clib_min (32, n_pending); + vec_validate (wrk->burst_connects, max_connects); + + while (n_connects < max_connects) + clib_fifo_sub1 (wrk->pending_connects, wrk->burst_connects[n_connects++]); + + clib_spinlock_unlock (&wrk->pending_connects_lock); + + /* Do connects without locking pending_connects */ + n_connects = 0; + while (n_connects < max_connects) { - proxy_connect_args_t args; - args.api_context = a->api_context; - args.app_index = a->app_index; - clib_memcpy (&args.sep, &a->sep_ext, sizeof (a->sep_ext)); - vl_api_rpc_call_main_thread (proxy_cb_fn, (u8 *) & args, sizeof (args)); + proxy_do_connect (&wrk->burst_connects[n_connects]); + n_connects += 1; } + + /* More work to do, program rpc */ + if (max_connects < n_pending) + session_send_rpc_evt_to_thread_force ( + transport_cl_thread (), proxy_handle_connects_rpc, + uword_to_pointer ((uword) thread_index, void *)); +} + +static void +proxy_program_connect (vnet_connect_args_t *a) +{ + u32 connects_thread = transport_cl_thread (), thread_index, n_pending; + proxy_worker_t *wrk; + + thread_index = vlib_get_thread_index (); + + /* If already on first worker, handle request */ + if (thread_index == connects_thread) + { + proxy_do_connect (a); + return; + } + + /* If not on first worker, queue request */ + wrk = proxy_worker_get (thread_index); + + clib_spinlock_lock (&wrk->pending_connects_lock); + + clib_fifo_add1 (wrk->pending_connects, *a); + n_pending = clib_fifo_elts (wrk->pending_connects); + + clib_spinlock_unlock (&wrk->pending_connects_lock); + + if (n_pending == 1) + session_send_rpc_evt_to_thread_force ( + connects_thread, proxy_handle_connects_rpc, + uword_to_pointer ((uword) thread_index, void *)); } static proxy_session_t * @@ -85,16 +180,6 @@ proxy_session_get (u32 ps_index) return pool_elt_at_index (pm->sessions, ps_index); } -static inline proxy_session_t * -proxy_session_get_if_valid (u32 ps_index) -{ - proxy_main_t *pm = &proxy_main; - - if (pool_is_free_index (pm->sessions, ps_index)) - return 0; - return pool_elt_at_index (pm->sessions, ps_index); -} - static void proxy_session_free (proxy_session_t *ps) { @@ -115,7 +200,7 @@ proxy_session_postponed_free_rpc (void *arg) clib_spinlock_lock_if_init (&pm->sessions_lock); ps = proxy_session_get (ps_index); - segment_manager_dealloc_fifos (ps->server_rx_fifo, ps->server_tx_fifo); + segment_manager_dealloc_fifos (ps->po.rx_fifo, ps->po.tx_fifo); proxy_session_free (ps); clib_spinlock_unlock_if_init (&pm->sessions_lock); @@ -126,54 +211,79 @@ proxy_session_postponed_free_rpc (void *arg) static void proxy_session_postponed_free (proxy_session_t *ps) { - session_send_rpc_evt_to_thread (ps->po_thread_index, + /* Passive open session handle has been invalidated so we don't have thread + * index at this point */ + session_send_rpc_evt_to_thread (ps->po.rx_fifo->master_thread_index, proxy_session_postponed_free_rpc, uword_to_pointer (ps->ps_index, void *)); } static void +proxy_session_close_po (proxy_session_t *ps) +{ + vnet_disconnect_args_t _a = {}, *a = &_a; + proxy_main_t *pm = &proxy_main; + + ASSERT (!vlib_num_workers () || + CLIB_SPINLOCK_IS_LOCKED (&pm->sessions_lock)); + + a->handle = ps->po.session_handle; + a->app_index = pm->server_app_index; + vnet_disconnect_session (a); + + ps->po_disconnected = 1; +} + +static void +proxy_session_close_ao (proxy_session_t *ps) +{ + vnet_disconnect_args_t _a = {}, *a = &_a; + proxy_main_t *pm = &proxy_main; + + ASSERT (!vlib_num_workers () || + CLIB_SPINLOCK_IS_LOCKED (&pm->sessions_lock)); + + a->handle = ps->ao.session_handle; + a->app_index = pm->active_open_app_index; + vnet_disconnect_session (a); + + ps->ao_disconnected = 1; +} + +static void proxy_try_close_session (session_t * s, int is_active_open) { proxy_main_t *pm = &proxy_main; - proxy_session_t *ps = 0; - vnet_disconnect_args_t _a, *a = &_a; + proxy_session_side_ctx_t *sc; + proxy_session_t *ps; + proxy_worker_t *wrk; + + wrk = proxy_worker_get (s->thread_index); + sc = proxy_session_side_ctx_get (wrk, s->opaque); clib_spinlock_lock_if_init (&pm->sessions_lock); - ps = proxy_session_get (s->opaque); + ps = proxy_session_get (sc->ps_index); if (is_active_open) { - a->handle = ps->vpp_active_open_handle; - a->app_index = pm->active_open_app_index; - vnet_disconnect_session (a); - ps->ao_disconnected = 1; + proxy_session_close_ao (ps); if (!ps->po_disconnected) { - ASSERT (ps->vpp_server_handle != SESSION_INVALID_HANDLE); - a->handle = ps->vpp_server_handle; - a->app_index = pm->server_app_index; - vnet_disconnect_session (a); - ps->po_disconnected = 1; + ASSERT (ps->po.session_handle != SESSION_INVALID_HANDLE); + proxy_session_close_po (ps); } } else { - a->handle = ps->vpp_server_handle; - a->app_index = pm->server_app_index; - vnet_disconnect_session (a); - ps->po_disconnected = 1; + proxy_session_close_po (ps); if (!ps->ao_disconnected && !ps->active_open_establishing) { /* Proxy session closed before active open */ - if (ps->vpp_active_open_handle != SESSION_INVALID_HANDLE) - { - a->handle = ps->vpp_active_open_handle; - a->app_index = pm->active_open_app_index; - vnet_disconnect_session (a); - } + if (ps->ao.session_handle != SESSION_INVALID_HANDLE) + proxy_session_close_ao (ps); ps->ao_disconnected = 1; } } @@ -181,29 +291,63 @@ proxy_try_close_session (session_t * s, int is_active_open) } static void +proxy_try_side_ctx_cleanup (session_t *s) +{ + proxy_main_t *pm = &proxy_main; + proxy_session_t *ps; + proxy_session_side_ctx_t *sc; + proxy_worker_t *wrk; + + wrk = proxy_worker_get (s->thread_index); + sc = proxy_session_side_ctx_get (wrk, s->opaque); + if (sc->state == PROXY_SC_S_CREATED) + return; + + clib_spinlock_lock_if_init (&pm->sessions_lock); + + ps = proxy_session_get (sc->ps_index); + + if (!ps->po_disconnected) + proxy_session_close_po (ps); + + if (!ps->ao_disconnected) + proxy_session_close_ao (ps); + + clib_spinlock_unlock_if_init (&pm->sessions_lock); +} + +static void proxy_try_delete_session (session_t * s, u8 is_active_open) { proxy_main_t *pm = &proxy_main; proxy_session_t *ps = 0; + proxy_session_side_ctx_t *sc; + proxy_worker_t *wrk; + u32 ps_index; + + wrk = proxy_worker_get (s->thread_index); + sc = proxy_session_side_ctx_get (wrk, s->opaque); + ps_index = sc->ps_index; + + proxy_session_side_ctx_free (wrk, sc); clib_spinlock_lock_if_init (&pm->sessions_lock); - ps = proxy_session_get (s->opaque); + ps = proxy_session_get (ps_index); if (is_active_open) { - ps->vpp_active_open_handle = SESSION_INVALID_HANDLE; + ps->ao.session_handle = SESSION_INVALID_HANDLE; /* Revert master thread index change on connect notification */ - ps->server_rx_fifo->master_thread_index = ps->po_thread_index; + ps->po.rx_fifo->master_thread_index = + ps->po.tx_fifo->master_thread_index; /* Passive open already cleaned up */ - if (ps->vpp_server_handle == SESSION_INVALID_HANDLE) + if (ps->po.session_handle == SESSION_INVALID_HANDLE) { - ASSERT (s->rx_fifo->refcnt == 1); - /* The two sides of the proxy on different threads */ - if (ps->po_thread_index != s->thread_index) + if (ps->po.tx_fifo->master_thread_index != s->thread_index) { /* This is not the right thread to delete the fifos */ s->rx_fifo = 0; @@ -211,14 +355,17 @@ proxy_try_delete_session (session_t * s, u8 is_active_open) proxy_session_postponed_free (ps); } else - proxy_session_free (ps); + { + ASSERT (s->rx_fifo->refcnt == 1); + proxy_session_free (ps); + } } } else { - ps->vpp_server_handle = SESSION_INVALID_HANDLE; + ps->po.session_handle = SESSION_INVALID_HANDLE; - if (ps->vpp_active_open_handle == SESSION_INVALID_HANDLE) + if (ps->ao.session_handle == SESSION_INVALID_HANDLE) { if (!ps->active_open_establishing) proxy_session_free (ps); @@ -275,16 +422,26 @@ static int proxy_accept_callback (session_t * s) { proxy_main_t *pm = &proxy_main; + proxy_session_side_ctx_t *sc; proxy_session_t *ps; + proxy_worker_t *wrk; + transport_proto_t tp = session_get_transport_proto (s); + + wrk = proxy_worker_get (s->thread_index); + sc = proxy_session_side_ctx_alloc (wrk); + s->opaque = sc->sc_index; clib_spinlock_lock_if_init (&pm->sessions_lock); ps = proxy_session_alloc (); - ps->vpp_server_handle = session_handle (s); - ps->vpp_active_open_handle = SESSION_INVALID_HANDLE; - ps->po_thread_index = s->thread_index; - s->opaque = ps->ps_index; + ps->po.session_handle = session_handle (s); + ps->po.rx_fifo = s->rx_fifo; + ps->po.tx_fifo = s->tx_fifo; + + ps->ao.session_handle = SESSION_INVALID_HANDLE; + sc->ps_index = ps->ps_index; + sc->is_http = tp == TRANSPORT_PROTO_HTTP ? 1 : 0; clib_spinlock_unlock_if_init (&pm->sessions_lock); @@ -325,92 +482,167 @@ proxy_transport_needs_crypto (transport_proto_t proto) return proto == TRANSPORT_PROTO_TLS; } -static int -proxy_rx_callback (session_t * s) +static void +proxy_session_start_connect (proxy_session_side_ctx_t *sc, session_t *s) { + int actual_transfer __attribute__ ((unused)); + vnet_connect_args_t _a = {}, *a = &_a; proxy_main_t *pm = &proxy_main; - u32 thread_index = vlib_get_thread_index (); - svm_fifo_t *ao_tx_fifo; + u32 max_dequeue, ps_index; proxy_session_t *ps; - - ASSERT (s->thread_index == thread_index); + transport_proto_t tp = session_get_transport_proto (s); clib_spinlock_lock_if_init (&pm->sessions_lock); - ps = proxy_session_get (s->opaque); + ps = proxy_session_get (sc->ps_index); - if (PREDICT_TRUE (ps->vpp_active_open_handle != SESSION_INVALID_HANDLE)) + /* maybe we were already here */ + if (ps->active_open_establishing) { clib_spinlock_unlock_if_init (&pm->sessions_lock); + return; + } - ao_tx_fifo = s->rx_fifo; + ps->active_open_establishing = 1; + ps_index = ps->ps_index; - /* - * Send event for active open tx fifo - */ - if (svm_fifo_set_event (ao_tx_fifo)) + clib_spinlock_unlock_if_init (&pm->sessions_lock); + + if (tp == TRANSPORT_PROTO_HTTP) + { + http_msg_t msg; + u8 *target_buf = 0; + http_uri_t target_uri; + http_header_t *resp_headers = 0; + session_endpoint_cfg_t target_sep = SESSION_ENDPOINT_CFG_NULL; + int rv; + + rv = svm_fifo_dequeue (s->rx_fifo, sizeof (msg), (u8 *) &msg); + ASSERT (rv == sizeof (msg)); + + if (msg.type != HTTP_MSG_REQUEST) + { + proxy_send_http_resp (s, HTTP_STATUS_INTERNAL_ERROR, 0); + return; + } + if (msg.method_type != HTTP_REQ_CONNECT) + { + http_add_header (&resp_headers, + http_header_name_token (HTTP_HEADER_ALLOW), + http_token_lit ("CONNECT")); + proxy_send_http_resp (s, HTTP_STATUS_METHOD_NOT_ALLOWED, + resp_headers); + vec_free (resp_headers); + return; + } + + if (msg.data.target_form != HTTP_TARGET_AUTHORITY_FORM || + msg.data.target_path_len == 0) { - u32 ao_thread_index = ao_tx_fifo->master_thread_index; - u32 ao_session_index = ao_tx_fifo->shr->master_session_index; - if (session_send_io_evt_to_thread_custom (&ao_session_index, - ao_thread_index, - SESSION_IO_EVT_TX)) - clib_warning ("failed to enqueue tx evt"); + proxy_send_http_resp (s, HTTP_STATUS_BAD_REQUEST, 0); + return; } - if (svm_fifo_max_enqueue (ao_tx_fifo) <= TCP_MSS) - svm_fifo_add_want_deq_ntf (ao_tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF); + /* read target uri */ + target_buf = vec_new (u8, msg.data.target_path_len); + rv = svm_fifo_peek (s->rx_fifo, msg.data.target_path_offset, + msg.data.target_path_len, target_buf); + ASSERT (rv == msg.data.target_path_len); + svm_fifo_dequeue_drop (s->rx_fifo, msg.data.len); + rv = http_parse_authority_form_target (target_buf, &target_uri); + vec_free (target_buf); + if (rv) + { + proxy_send_http_resp (s, HTTP_STATUS_BAD_REQUEST, 0); + return; + } + target_sep.is_ip4 = target_uri.is_ip4; + target_sep.ip = target_uri.ip; + target_sep.port = target_uri.port; + target_sep.transport_proto = TRANSPORT_PROTO_TCP; + clib_memcpy (&a->sep_ext, &target_sep, sizeof (target_sep)); } else { - vnet_connect_args_t _a, *a = &_a; - svm_fifo_t *tx_fifo, *rx_fifo; - u32 max_dequeue, ps_index; - int actual_transfer __attribute__ ((unused)); + max_dequeue = svm_fifo_max_dequeue_cons (s->rx_fifo); + if (PREDICT_FALSE (max_dequeue == 0)) + return; - rx_fifo = s->rx_fifo; - tx_fifo = s->tx_fifo; + max_dequeue = clib_min (pm->rcv_buffer_size, max_dequeue); + actual_transfer = + svm_fifo_peek (s->rx_fifo, 0 /* relative_offset */, max_dequeue, + pm->rx_buf[s->thread_index]); - ASSERT (rx_fifo->master_thread_index == thread_index); - ASSERT (tx_fifo->master_thread_index == thread_index); + /* Expectation is that here actual data just received is parsed and based + * on its contents, the destination and parameters of the connect to the + * upstream are decided + */ - max_dequeue = svm_fifo_max_dequeue_cons (s->rx_fifo); + clib_memcpy (&a->sep_ext, &pm->client_sep, sizeof (pm->client_sep)); + } - if (PREDICT_FALSE (max_dequeue == 0)) - { - clib_spinlock_unlock_if_init (&pm->sessions_lock); - return 0; - } + a->api_context = ps_index; + a->app_index = pm->active_open_app_index; - max_dequeue = clib_min (pm->rcv_buffer_size, max_dequeue); - actual_transfer = svm_fifo_peek (rx_fifo, 0 /* relative_offset */ , - max_dequeue, pm->rx_buf[thread_index]); + if (proxy_transport_needs_crypto (a->sep.transport_proto)) + { + transport_endpt_ext_cfg_t *ext_cfg = session_endpoint_add_ext_cfg ( + &a->sep_ext, TRANSPORT_ENDPT_EXT_CFG_CRYPTO, + sizeof (transport_endpt_crypto_cfg_t)); + ext_cfg->crypto.ckpair_index = pm->ckpair_index; + } - /* $$$ your message in this space: parse url, etc. */ + proxy_program_connect (a); +} - clib_memset (a, 0, sizeof (*a)); +static int +proxy_rx_callback (session_t *s) +{ + proxy_session_side_ctx_t *sc; + svm_fifo_t *ao_tx_fifo; + proxy_session_t *ps; + proxy_worker_t *wrk; - ps->server_rx_fifo = rx_fifo; - ps->server_tx_fifo = tx_fifo; - ps->active_open_establishing = 1; - ps_index = ps->ps_index; + ASSERT (s->thread_index == vlib_get_thread_index ()); - clib_spinlock_unlock_if_init (&pm->sessions_lock); + wrk = proxy_worker_get (s->thread_index); + sc = proxy_session_side_ctx_get (wrk, s->opaque); - clib_memcpy (&a->sep_ext, &pm->client_sep, sizeof (pm->client_sep)); - a->api_context = ps_index; - a->app_index = pm->active_open_app_index; + if (PREDICT_FALSE (sc->state < PROXY_SC_S_ESTABLISHED)) + { + proxy_main_t *pm = &proxy_main; - if (proxy_transport_needs_crypto (a->sep.transport_proto)) + if (sc->state == PROXY_SC_S_CREATED) { - session_endpoint_alloc_ext_cfg (&a->sep_ext, - TRANSPORT_ENDPT_EXT_CFG_CRYPTO); - a->sep_ext.ext_cfg->crypto.ckpair_index = pm->ckpair_index; + proxy_session_start_connect (sc, s); + sc->state = PROXY_SC_S_CONNECTING; + return 0; } - proxy_call_main_thread (a); + clib_spinlock_lock_if_init (&pm->sessions_lock); + + ps = proxy_session_get (sc->ps_index); + sc->pair = ps->ao; + + clib_spinlock_unlock_if_init (&pm->sessions_lock); + + if (sc->pair.session_handle == SESSION_INVALID_HANDLE) + return 0; + + sc->state = PROXY_SC_S_ESTABLISHED; } + ao_tx_fifo = s->rx_fifo; + + /* + * Send event for active open tx fifo + */ + if (svm_fifo_set_event (ao_tx_fifo)) + session_program_tx_io_evt (sc->pair.session_handle, SESSION_IO_EVT_TX); + + if (svm_fifo_max_enqueue (ao_tx_fifo) <= TCP_MSS) + svm_fifo_add_want_deq_ntf (ao_tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF); + return 0; } @@ -418,20 +650,20 @@ static void proxy_force_ack (void *handlep) { transport_connection_t *tc; - session_t *ao_s; + session_t *s; - ao_s = session_get_from_handle (pointer_to_uword (handlep)); - if (session_get_transport_proto (ao_s) != TRANSPORT_PROTO_TCP) + s = session_get_from_handle (pointer_to_uword (handlep)); + if (session_get_transport_proto (s) != TRANSPORT_PROTO_TCP) return; - tc = session_get_transport (ao_s); + tc = session_get_transport (s); tcp_send_ack ((tcp_connection_t *) tc); } static int proxy_tx_callback (session_t * proxy_s) { - proxy_main_t *pm = &proxy_main; - proxy_session_t *ps; + proxy_session_side_ctx_t *sc; + proxy_worker_t *wrk; u32 min_free; min_free = clib_min (svm_fifo_size (proxy_s->tx_fifo) >> 3, 128 << 10); @@ -441,21 +673,17 @@ proxy_tx_callback (session_t * proxy_s) return 0; } - clib_spinlock_lock_if_init (&pm->sessions_lock); - - ps = proxy_session_get (proxy_s->opaque); - - if (ps->vpp_active_open_handle == SESSION_INVALID_HANDLE) - goto unlock; + wrk = proxy_worker_get (proxy_s->thread_index); + sc = proxy_session_side_ctx_get (wrk, proxy_s->opaque); + if (sc->state < PROXY_SC_S_ESTABLISHED) + return 0; /* Force ack on active open side to update rcv wnd. Make sure it's done on * the right thread */ - void *arg = uword_to_pointer (ps->vpp_active_open_handle, void *); - session_send_rpc_evt_to_thread (ps->server_rx_fifo->master_thread_index, - proxy_force_ack, arg); - -unlock: - clib_spinlock_unlock_if_init (&pm->sessions_lock); + void *arg = uword_to_pointer (sc->pair.session_handle, void *); + session_send_rpc_evt_to_thread ( + session_thread_from_handle (sc->pair.session_handle), proxy_force_ack, + arg); return 0; } @@ -464,7 +692,10 @@ static void proxy_cleanup_callback (session_t * s, session_cleanup_ntf_t ntf) { if (ntf == SESSION_CLEANUP_TRANSPORT) - return; + { + proxy_try_side_ctx_cleanup (s); + return; + } proxy_try_delete_session (s, 0 /* is_active_open */ ); } @@ -490,10 +721,17 @@ active_open_alloc_session_fifos (session_t *s) clib_spinlock_lock_if_init (&pm->sessions_lock); + /* Active open opaque is pointing at proxy session */ ps = proxy_session_get (s->opaque); - txf = ps->server_rx_fifo; - rxf = ps->server_tx_fifo; + if (ps->po_disconnected) + { + clib_spinlock_unlock_if_init (&pm->sessions_lock); + return SESSION_E_ALLOC; + } + + txf = ps->po.rx_fifo; + rxf = ps->po.tx_fifo; /* * Reset the active-open tx-fifo master indices so the active-open session @@ -524,31 +762,43 @@ active_open_connected_callback (u32 app_index, u32 opaque, { proxy_main_t *pm = &proxy_main; proxy_session_t *ps; - u8 thread_index = vlib_get_thread_index (); - - /* - * Setup proxy session handle. - */ - clib_spinlock_lock_if_init (&pm->sessions_lock); - - ps = proxy_session_get (opaque); + proxy_worker_t *wrk; + proxy_session_side_ctx_t *sc; + session_t *po_s; + transport_proto_t tp; /* Connection failed */ if (err) { - vnet_disconnect_args_t _a, *a = &_a; + clib_spinlock_lock_if_init (&pm->sessions_lock); - a->handle = ps->vpp_server_handle; - a->app_index = pm->server_app_index; - vnet_disconnect_session (a); - ps->po_disconnected = 1; - } - else - { - ps->vpp_active_open_handle = session_handle (s); - ps->active_open_establishing = 0; + ps = proxy_session_get (opaque); + po_s = session_get_from_handle (ps->po.session_handle); + tp = session_get_transport_proto (po_s); + if (tp == TRANSPORT_PROTO_HTTP) + { + proxy_send_http_resp (po_s, HTTP_STATUS_BAD_GATEWAY, 0); + } + ps->ao_disconnected = 1; + proxy_session_close_po (ps); + + clib_spinlock_unlock_if_init (&pm->sessions_lock); + + return 0; } + wrk = proxy_worker_get (s->thread_index); + + clib_spinlock_lock_if_init (&pm->sessions_lock); + + ps = proxy_session_get (opaque); + + ps->ao.rx_fifo = s->rx_fifo; + ps->ao.tx_fifo = s->tx_fifo; + ps->ao.session_handle = session_handle (s); + + ps->active_open_establishing = 0; + /* Passive open session was already closed! */ if (ps->po_disconnected) { @@ -558,21 +808,136 @@ active_open_connected_callback (u32 app_index, u32 opaque, return -1; } - s->opaque = opaque; + po_s = session_get_from_handle (ps->po.session_handle); + tp = session_get_transport_proto (po_s); + + sc = proxy_session_side_ctx_alloc (wrk); + sc->pair = ps->po; + sc->ps_index = ps->ps_index; clib_spinlock_unlock_if_init (&pm->sessions_lock); - /* - * Send event for active open tx fifo - */ - ASSERT (s->thread_index == thread_index); - if (svm_fifo_set_event (s->tx_fifo)) - session_send_io_evt_to_thread (s->tx_fifo, SESSION_IO_EVT_TX); + sc->state = PROXY_SC_S_ESTABLISHED; + s->opaque = sc->sc_index; + sc->is_http = tp == TRANSPORT_PROTO_HTTP ? 1 : 0; + + if (tp == TRANSPORT_PROTO_HTTP) + { + proxy_send_http_resp (po_s, HTTP_STATUS_OK, 0); + } + else + { + /* + * Send event for active open tx fifo + */ + ASSERT (s->thread_index == vlib_get_thread_index ()); + if (svm_fifo_set_event (s->tx_fifo)) + session_program_tx_io_evt (session_handle (s), SESSION_IO_EVT_TX); + } return 0; } static void +active_open_migrate_po_fixup_rpc (void *arg) +{ + u32 ps_index = pointer_to_uword (arg); + proxy_session_side_ctx_t *po_sc; + proxy_main_t *pm = &proxy_main; + session_handle_t po_sh; + proxy_worker_t *wrk; + proxy_session_t *ps; + session_t *po_s; + + wrk = proxy_worker_get (vlib_get_thread_index ()); + + clib_spinlock_lock_if_init (&pm->sessions_lock); + + ps = proxy_session_get (ps_index); + + po_s = session_get_from_handle (ps->po.session_handle); + po_s->rx_fifo = ps->po.rx_fifo; + po_s->tx_fifo = ps->po.tx_fifo; + + po_sc = proxy_session_side_ctx_get (wrk, po_s->opaque); + po_sc->pair = ps->ao; + po_sh = ps->po.session_handle; + + clib_spinlock_unlock_if_init (&pm->sessions_lock); + + session_program_tx_io_evt (po_sh, SESSION_IO_EVT_TX); +} + +static void +active_open_migrate_rpc (void *arg) +{ + u32 ps_index = pointer_to_uword (arg); + proxy_main_t *pm = &proxy_main; + proxy_session_side_ctx_t *sc; + proxy_worker_t *wrk; + proxy_session_t *ps; + session_t *s; + + wrk = proxy_worker_get (vlib_get_thread_index ()); + sc = proxy_session_side_ctx_alloc (wrk); + + clib_spinlock_lock_if_init (&pm->sessions_lock); + + ps = proxy_session_get (ps_index); + sc->ps_index = ps->ps_index; + + s = session_get_from_handle (ps->ao.session_handle); + s->opaque = sc->sc_index; + s->flags &= ~SESSION_F_IS_MIGRATING; + + /* Fixup passive open session because of migration and zc */ + ps->ao.rx_fifo = ps->po.tx_fifo = s->rx_fifo; + ps->ao.tx_fifo = ps->po.rx_fifo = s->tx_fifo; + + ps->po.tx_fifo->shr->master_session_index = + session_index_from_handle (ps->po.session_handle); + ps->po.tx_fifo->master_thread_index = + session_thread_from_handle (ps->po.session_handle); + + sc->pair = ps->po; + + clib_spinlock_unlock_if_init (&pm->sessions_lock); + + session_send_rpc_evt_to_thread ( + session_thread_from_handle (sc->pair.session_handle), + active_open_migrate_po_fixup_rpc, uword_to_pointer (sc->ps_index, void *)); +} + +static void +active_open_migrate_callback (session_t *s, session_handle_t new_sh) +{ + proxy_main_t *pm = &proxy_main; + proxy_session_side_ctx_t *sc; + proxy_session_t *ps; + proxy_worker_t *wrk; + + wrk = proxy_worker_get (s->thread_index); + sc = proxy_session_side_ctx_get (wrk, s->opaque); + + /* NOTE: this is just an example. ZC makes this migration rather + * tedious. Probably better approaches could be found */ + clib_spinlock_lock_if_init (&pm->sessions_lock); + + ps = proxy_session_get (sc->ps_index); + ps->ao.session_handle = new_sh; + ps->ao.rx_fifo = 0; + ps->ao.tx_fifo = 0; + + clib_spinlock_unlock_if_init (&pm->sessions_lock); + + session_send_rpc_evt_to_thread (session_thread_from_handle (new_sh), + active_open_migrate_rpc, + uword_to_pointer (sc->ps_index, void *)); + + proxy_session_side_ctx_free (wrk, sc); +} + +static void active_open_reset_callback (session_t * s) { proxy_try_close_session (s, 1 /* is_active_open */ ); @@ -618,10 +983,8 @@ active_open_rx_callback (session_t * s) static int active_open_tx_callback (session_t * ao_s) { - proxy_main_t *pm = &proxy_main; - transport_connection_t *tc; - proxy_session_t *ps; - session_t *proxy_s; + proxy_session_side_ctx_t *sc; + proxy_worker_t *wrk; u32 min_free; min_free = clib_min (svm_fifo_size (ao_s->tx_fifo) >> 3, 128 << 10); @@ -631,23 +994,27 @@ active_open_tx_callback (session_t * ao_s) return 0; } - clib_spinlock_lock_if_init (&pm->sessions_lock); + wrk = proxy_worker_get (ao_s->thread_index); + sc = proxy_session_side_ctx_get (wrk, ao_s->opaque); - ps = proxy_session_get_if_valid (ao_s->opaque); - if (!ps) - goto unlock; - - if (ps->vpp_server_handle == ~0) - goto unlock; - - proxy_s = session_get_from_handle (ps->vpp_server_handle); - - /* Force ack on proxy side to update rcv wnd */ - tc = session_get_transport (proxy_s); - tcp_send_ack ((tcp_connection_t *) tc); + if (sc->state < PROXY_SC_S_ESTABLISHED) + return 0; -unlock: - clib_spinlock_unlock_if_init (&pm->sessions_lock); + if (sc->is_http) + { + /* notify HTTP transport */ + session_t *po = session_get_from_handle (sc->pair.session_handle); + session_send_io_evt_to_thread_custom ( + &po->session_index, po->thread_index, SESSION_IO_EVT_RX); + } + else + { + /* Force ack on proxy side to update rcv wnd */ + void *arg = uword_to_pointer (sc->pair.session_handle, void *); + session_send_rpc_evt_to_thread ( + session_thread_from_handle (sc->pair.session_handle), proxy_force_ack, + arg); + } return 0; } @@ -664,6 +1031,7 @@ active_open_cleanup_callback (session_t * s, session_cleanup_ntf_t ntf) static session_cb_vft_t active_open_clients = { .session_reset_callback = active_open_reset_callback, .session_connected_callback = active_open_connected_callback, + .session_migrate_callback = active_open_migrate_callback, .session_accept_callback = active_open_create_callback, .session_disconnect_callback = active_open_disconnect_callback, .session_cleanup_callback = active_open_cleanup_callback, @@ -756,22 +1124,33 @@ proxy_server_listen () { proxy_main_t *pm = &proxy_main; vnet_listen_args_t _a, *a = &_a; - int rv; + int rv, need_crypto; clib_memset (a, 0, sizeof (*a)); a->app_index = pm->server_app_index; clib_memcpy (&a->sep_ext, &pm->server_sep, sizeof (pm->server_sep)); - if (proxy_transport_needs_crypto (a->sep.transport_proto)) + /* Make sure listener is marked connected for transports like udp */ + a->sep_ext.transport_flags = TRANSPORT_CFG_F_CONNECTED; + need_crypto = proxy_transport_needs_crypto (a->sep.transport_proto); + if (need_crypto) + { + transport_endpt_ext_cfg_t *ext_cfg = session_endpoint_add_ext_cfg ( + &a->sep_ext, TRANSPORT_ENDPT_EXT_CFG_CRYPTO, + sizeof (transport_endpt_crypto_cfg_t)); + ext_cfg->crypto.ckpair_index = pm->ckpair_index; + } + /* set http timeout for connect-proxy */ + if (pm->server_sep.transport_proto == TRANSPORT_PROTO_HTTP) { - session_endpoint_alloc_ext_cfg (&a->sep_ext, - TRANSPORT_ENDPT_EXT_CFG_CRYPTO); - a->sep_ext.ext_cfg->crypto.ckpair_index = pm->ckpair_index; + transport_endpt_ext_cfg_t *ext_cfg = session_endpoint_add_ext_cfg ( + &a->sep_ext, TRANSPORT_ENDPT_EXT_CFG_HTTP, sizeof (ext_cfg->opaque)); + ext_cfg->opaque = pm->idle_timeout; } rv = vnet_listen (a); - if (a->sep_ext.ext_cfg) - clib_mem_free (a->sep_ext.ext_cfg); + if (need_crypto) + session_endpoint_free_ext_cfgs (&a->sep_ext); return rv; } @@ -797,15 +1176,25 @@ proxy_server_create (vlib_main_t * vm) { vlib_thread_main_t *vtm = vlib_get_thread_main (); proxy_main_t *pm = &proxy_main; + proxy_worker_t *wrk; u32 num_threads; int i; + if (vlib_num_workers ()) + clib_spinlock_init (&pm->sessions_lock); + num_threads = 1 /* main thread */ + vtm->n_threads; vec_validate (pm->rx_buf, num_threads - 1); for (i = 0; i < num_threads; i++) vec_validate (pm->rx_buf[i], pm->rcv_buffer_size); + vec_validate (pm->workers, vlib_num_workers ()); + vec_foreach (wrk, pm->workers) + { + clib_spinlock_init (&wrk->pending_connects_lock); + } + proxy_server_add_ckpair (); if (proxy_server_attach ()) @@ -813,11 +1202,6 @@ proxy_server_create (vlib_main_t * vm) clib_warning ("failed to attach server app"); return -1; } - if (proxy_server_listen ()) - { - clib_warning ("failed to start listening"); - return -1; - } if (active_open_attach ()) { clib_warning ("failed to attach active open app"); @@ -849,9 +1233,6 @@ proxy_server_create_command_fn (vlib_main_t * vm, unformat_input_t * input, pm->private_segment_count = 0; pm->segment_size = 512 << 20; - if (vlib_num_workers ()) - clib_spinlock_init (&pm->sessions_lock); - if (!unformat_user (input, unformat_line_input, line_input)) return 0; @@ -883,6 +1264,8 @@ proxy_server_create_command_fn (vlib_main_t * vm, unformat_input_t * input, vec_add1 (server_uri, 0); else if (unformat (line_input, "client-uri %s", &client_uri)) vec_add1 (client_uri, 0); + else if (unformat (line_input, "idle-timeout %d", &pm->idle_timeout)) + ; else { error = clib_error_return (0, "unknown input `%U'", @@ -897,35 +1280,45 @@ proxy_server_create_command_fn (vlib_main_t * vm, unformat_input_t * input, default_server_uri); server_uri = format (0, "%s%c", default_server_uri, 0); } - if (!client_uri) - { - clib_warning ("No client-uri provided, Using default: %s", - default_client_uri); - client_uri = format (0, "%s%c", default_client_uri, 0); - } - if (parse_uri ((char *) server_uri, &pm->server_sep)) { error = clib_error_return (0, "Invalid server uri %v", server_uri); goto done; } - if (parse_uri ((char *) client_uri, &pm->client_sep)) + + /* http proxy get target within request */ + if (pm->server_sep.transport_proto != TRANSPORT_PROTO_HTTP) { - error = clib_error_return (0, "Invalid client uri %v", client_uri); - goto done; + if (!client_uri) + { + clib_warning ("No client-uri provided, Using default: %s", + default_client_uri); + client_uri = format (0, "%s%c", default_client_uri, 0); + } + if (parse_uri ((char *) client_uri, &pm->client_sep)) + { + error = clib_error_return (0, "Invalid client uri %v", client_uri); + goto done; + } } - vnet_session_enable_disable (vm, 1 /* turn on session and transport */ ); - - rv = proxy_server_create (vm); - switch (rv) + if (pm->server_app_index == APP_INVALID_INDEX) { - case 0: - break; - default: - error = clib_error_return (0, "server_create returned %d", rv); + session_enable_disable_args_t args = { .is_en = 1, + .rt_engine_type = + RT_BACKEND_ENGINE_RULE_TABLE }; + vnet_session_enable_disable (vm, &args); + rv = proxy_server_create (vm); + if (rv) + { + error = clib_error_return (0, "server_create returned %d", rv); + goto done; + } } + if (proxy_server_listen ()) + error = clib_error_return (0, "failed to start listening"); + done: unformat_free (line_input); vec_free (client_uri); @@ -933,14 +1326,14 @@ done: return error; } -VLIB_CLI_COMMAND (proxy_create_command, static) = -{ +VLIB_CLI_COMMAND (proxy_create_command, static) = { .path = "test proxy server", - .short_help = "test proxy server [server-uri <tcp://ip/port>]" - "[client-uri <tcp://ip/port>][fifo-size <nn>[k|m]]" - "[max-fifo-size <nn>[k|m]][high-watermark <nn>]" - "[low-watermark <nn>][rcv-buf-size <nn>][prealloc-fifos <nn>]" - "[private-segment-size <mem>][private-segment-count <nn>]", + .short_help = "test proxy server [server-uri <proto://ip/port>]" + "[client-uri <tcp://ip/port>][fifo-size <nn>[k|m]]" + "[max-fifo-size <nn>[k|m]][high-watermark <nn>]" + "[low-watermark <nn>][rcv-buf-size <nn>][prealloc-fifos <nn>]" + "[private-segment-size <mem>][private-segment-count <nn>]" + "[idle-timeout <nn>]", .function = proxy_server_create_command_fn, }; @@ -950,6 +1343,8 @@ proxy_main_init (vlib_main_t * vm) proxy_main_t *pm = &proxy_main; pm->server_client_index = ~0; pm->active_open_client_index = ~0; + pm->server_app_index = APP_INVALID_INDEX; + pm->idle_timeout = 600; /* connect-proxy default idle timeout 10 minutes */ return 0; } diff --git a/src/plugins/hs_apps/proxy.h b/src/plugins/hs_apps/proxy.h index 26f4de2f729..75567e4c1ba 100644 --- a/src/plugins/hs_apps/proxy.h +++ b/src/plugins/hs_apps/proxy.h @@ -26,23 +26,57 @@ #include <vnet/session/session.h> #include <vnet/session/application_interface.h> +#define foreach_proxy_session_side_state \ + _ (CREATED, "created") \ + _ (CONNECTING, "connecting") \ + _ (ESTABLISHED, "establiehed") \ + _ (CLOSED, "closed") + +typedef enum proxy_session_side_state_ +{ +#define _(sym, str) PROXY_SC_S_##sym, + foreach_proxy_session_side_state +#undef _ +} proxy_session_side_state_t; +typedef struct proxy_session_side_ +{ + session_handle_t session_handle; + svm_fifo_t *rx_fifo; + svm_fifo_t *tx_fifo; +} proxy_session_side_t; + +typedef struct proxy_session_side_ctx_ +{ + proxy_session_side_t pair; + proxy_session_side_state_t state; + u32 sc_index; + u32 ps_index; + u8 is_http; +} proxy_session_side_ctx_t; + typedef struct { - svm_fifo_t *server_rx_fifo; - svm_fifo_t *server_tx_fifo; + proxy_session_side_t po; /**< passive open side */ + proxy_session_side_t ao; /**< active open side */ - session_handle_t vpp_server_handle; - session_handle_t vpp_active_open_handle; volatile int active_open_establishing; volatile int po_disconnected; volatile int ao_disconnected; u32 ps_index; - u32 po_thread_index; } proxy_session_t; +typedef struct proxy_worker_ +{ + proxy_session_side_ctx_t *ctx_pool; + clib_spinlock_t pending_connects_lock; + vnet_connect_args_t *pending_connects; + vnet_connect_args_t *burst_connects; +} proxy_worker_t; + typedef struct { + proxy_worker_t *workers; /**< per-thread data */ proxy_session_t *sessions; /**< session pool, shared */ clib_spinlock_t sessions_lock; /**< lock for session pool */ u8 **rx_buf; /**< intermediate rx buffers */ @@ -63,6 +97,7 @@ typedef struct u32 private_segment_count; /**< Number of private fifo segs */ u64 segment_size; /**< size of fifo segs */ u8 prealloc_fifos; /**< Request fifo preallocation */ + 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; @@ -75,6 +110,13 @@ typedef struct extern proxy_main_t proxy_main; +static inline proxy_worker_t * +proxy_worker_get (u32 thread_index) +{ + proxy_main_t *pm = &proxy_main; + return vec_elt_at_index (pm->workers, thread_index); +} + #endif /* __included_proxy_h__ */ /* diff --git a/src/plugins/hs_apps/sapi/vpp_echo_common.c b/src/plugins/hs_apps/sapi/vpp_echo_common.c index 5ce04d1b75b..09ba583cf78 100644 --- a/src/plugins/hs_apps/sapi/vpp_echo_common.c +++ b/src/plugins/hs_apps/sapi/vpp_echo_common.c @@ -330,8 +330,8 @@ format_transport_proto (u8 * s, va_list * args) case TRANSPORT_PROTO_UDP: s = format (s, "UDP"); break; - case TRANSPORT_PROTO_NONE: - s = format (s, "NONE"); + case TRANSPORT_PROTO_CT: + s = format (s, "CT"); break; case TRANSPORT_PROTO_TLS: s = format (s, "TLS"); diff --git a/src/plugins/hs_apps/test_builtins.c b/src/plugins/hs_apps/test_builtins.c new file mode 100644 index 00000000000..c314e71b5df --- /dev/null +++ b/src/plugins/hs_apps/test_builtins.c @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2024 Cisco Systems, Inc. + */ + +#include <http_static/http_static.h> +#include <vppinfra/tw_timer_2t_1w_2048sl.h> + +typedef struct +{ + u32 stop_timer_handle; + hss_session_handle_t sh; +} tw_timer_elt_t; + +typedef struct tb_main_ +{ + tw_timer_elt_t *delayed_resps; + tw_timer_wheel_2t_1w_2048sl_t tw; + hss_session_send_fn send_data; + u8 *test_data; +} tb_main_t; + +static tb_main_t tb_main; + +static uword +test_builtins_timer_process (vlib_main_t *vm, vlib_node_runtime_t *rt, + vlib_frame_t *f) +{ + tb_main_t *tbm = &tb_main; + f64 now, timeout = 1.0; + uword *event_data = 0; + uword __clib_unused event_type; + + while (1) + { + vlib_process_wait_for_event_or_clock (vm, timeout); + now = vlib_time_now (vm); + event_type = vlib_process_get_events (vm, (uword **) &event_data); + + /* expire timers */ + tw_timer_expire_timers_2t_1w_2048sl (&tbm->tw, now); + + vec_reset_length (event_data); + } + return 0; +} + +VLIB_REGISTER_NODE (test_builtins_timer_process_node) = { + .function = test_builtins_timer_process, + .type = VLIB_NODE_TYPE_PROCESS, + .name = "test-builtins-timer-process", + .state = VLIB_NODE_STATE_DISABLED, +}; + +static void +send_data_to_hss (hss_session_handle_t sh, u8 *data, u8 free_vec_data) +{ + tb_main_t *tbm = &tb_main; + hss_url_handler_args_t args = {}; + + args.sh = sh; + args.data = data; + args.data_len = vec_len (data); + args.ct = HTTP_CONTENT_TEXT_PLAIN; + args.sc = HTTP_STATUS_OK; + args.free_vec_data = free_vec_data; + + tbm->send_data (&args); +} + +static hss_url_handler_rc_t +handle_get_test1 (hss_url_handler_args_t *args) +{ + u8 *data; + + clib_warning ("get request on test1"); + data = format (0, "hello"); + send_data_to_hss (args->sh, data, 1); + + return HSS_URL_HANDLER_ASYNC; +} + +static hss_url_handler_rc_t +handle_get_test2 (hss_url_handler_args_t *args) +{ + u8 *data; + + clib_warning ("get request on test2"); + data = format (0, "some data"); + send_data_to_hss (args->sh, data, 1); + + return HSS_URL_HANDLER_ASYNC; +} + +static void +delayed_resp_cb (u32 *expired_timers) +{ + tb_main_t *tbm = &tb_main; + int i; + u32 pool_index; + tw_timer_elt_t *e; + u8 *data; + + for (i = 0; i < vec_len (expired_timers); i++) + { + pool_index = expired_timers[i] & 0x7FFFFFFF; + e = pool_elt_at_index (tbm->delayed_resps, pool_index); + clib_warning ("sending delayed data"); + data = format (0, "delayed data"); + send_data_to_hss (e->sh, data, 1); + pool_put (tbm->delayed_resps, e); + } +} + +static hss_url_handler_rc_t +handle_get_test_delayed (hss_url_handler_args_t *args) +{ + tb_main_t *tbm = &tb_main; + tw_timer_elt_t *e; + + clib_warning ("get request on test_delayed"); + pool_get (tbm->delayed_resps, e); + e->sh = args->sh; + e->stop_timer_handle = + tw_timer_start_2t_1w_2048sl (&tbm->tw, e - tbm->delayed_resps, 0, 5); + + return HSS_URL_HANDLER_ASYNC; +} + +static hss_url_handler_rc_t +handle_post_test3 (hss_url_handler_args_t *args) +{ + send_data_to_hss (args->sh, 0, 0); + return HSS_URL_HANDLER_ASYNC; +} + +static hss_url_handler_rc_t +handle_get_64bytes (hss_url_handler_args_t *args) +{ + tb_main_t *tbm = &tb_main; + send_data_to_hss (args->sh, tbm->test_data, 0); + return HSS_URL_HANDLER_ASYNC; +} + +static void +test_builtins_init (vlib_main_t *vm) +{ + tb_main_t *tbm = &tb_main; + hss_register_url_fn fp; + vlib_node_t *n; + + fp = vlib_get_plugin_symbol ("http_static_plugin.so", + "hss_register_url_handler"); + + if (fp == 0) + { + clib_warning ("http_static_plugin.so not loaded..."); + return; + } + + tbm->test_data = format ( + 0, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + + (*fp) (handle_get_test1, "test1", HTTP_REQ_GET); + (*fp) (handle_get_test2, "test2", HTTP_REQ_GET); + (*fp) (handle_get_test_delayed, "test_delayed", HTTP_REQ_GET); + (*fp) (handle_post_test3, "test3", HTTP_REQ_POST); + (*fp) (handle_get_64bytes, "64B", HTTP_REQ_GET); + + tbm->send_data = + vlib_get_plugin_symbol ("http_static_plugin.so", "hss_session_send_data"); + + tw_timer_wheel_init_2t_1w_2048sl (&tbm->tw, delayed_resp_cb, 1.0, ~0); + + vlib_node_set_state (vm, test_builtins_timer_process_node.index, + VLIB_NODE_STATE_POLLING); + n = vlib_get_node (vm, test_builtins_timer_process_node.index); + vlib_start_process (vm, n->runtime_index); +} + +static clib_error_t * +test_builtins_enable_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + test_builtins_init (vm); + return 0; +} + +VLIB_CLI_COMMAND (test_builtins_enable_command, static) = { + .path = "test-url-handler enable", + .short_help = "test-url-handler enable", + .function = test_builtins_enable_command_fn, +}; diff --git a/src/plugins/hs_apps/vcl/vcl_test.h b/src/plugins/hs_apps/vcl/vcl_test.h index 0ce27ef84e2..11667fb144a 100644 --- a/src/plugins/hs_apps/vcl/vcl_test.h +++ b/src/plugins/hs_apps/vcl/vcl_test.h @@ -124,7 +124,7 @@ typedef struct typedef struct { - const vcl_test_proto_vft_t *protos[VPPCOM_PROTO_SRTP + 1]; + const vcl_test_proto_vft_t *protos[VPPCOM_PROTO_HTTP + 1]; uint32_t ckpair_index; hs_test_cfg_t cfg; vcl_test_wrk_t *wrk; @@ -420,6 +420,39 @@ vcl_test_write (vcl_test_session_t *ts, void *buf, uint32_t nbytes) return (tx_bytes); } +static inline int +vcl_test_write_ds (vcl_test_session_t *ts) +{ + vcl_test_stats_t *stats = &ts->stats; + int tx_bytes; + + do + { + stats->tx_xacts++; + if (ts->ds[1].len) + tx_bytes = vppcom_session_write_segments (ts->fd, ts->ds, 2); + else + tx_bytes = vppcom_session_write_segments (ts->fd, ts->ds, 1); + + if (tx_bytes < 0) + errno = -tx_bytes; + if ((tx_bytes == 0) || + ((tx_bytes < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))) + stats->rx_eagain++; + } + while ((tx_bytes == 0) || + ((tx_bytes < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))); + + if (tx_bytes < 0) + { + vterr ("vppcom_session_write_segments()", -errno); + } + else + stats->tx_bytes += tx_bytes; + + return (tx_bytes); +} + static inline void dump_help (void) { diff --git a/src/plugins/hs_apps/vcl/vcl_test_client.c b/src/plugins/hs_apps/vcl/vcl_test_client.c index a4a10b562ff..8bac1f00b9d 100644 --- a/src/plugins/hs_apps/vcl/vcl_test_client.c +++ b/src/plugins/hs_apps/vcl/vcl_test_client.c @@ -419,13 +419,8 @@ vtc_worker_run_select (vcl_test_client_worker_t *wrk) if (vcm->incremental_stats) vtc_inc_stats_check (ts); } - if ((!check_rx && ts->stats.tx_bytes >= ts->cfg.total_bytes) || - (check_rx && ts->stats.rx_bytes >= ts->cfg.total_bytes)) - { - clock_gettime (CLOCK_REALTIME, &ts->stats.stop); - ts->is_done = 1; - n_active_sessions--; - } + if (vtc_session_check_is_done (ts, check_rx)) + n_active_sessions -= 1; } } diff --git a/src/plugins/hs_apps/vcl/vcl_test_protos.c b/src/plugins/hs_apps/vcl/vcl_test_protos.c index cd1ac2b24f4..9c81c5f17a1 100644 --- a/src/plugins/hs_apps/vcl/vcl_test_protos.c +++ b/src/plugins/hs_apps/vcl/vcl_test_protos.c @@ -14,6 +14,23 @@ */ #include <hs_apps/vcl/vcl_test.h> +#include <http/http.h> +#include <http/http_header_names.h> +#include <http/http_content_types.h> + +typedef enum vcl_test_http_state_ +{ + VCL_TEST_HTTP_IDLE = 0, + VCL_TEST_HTTP_IN_PROGRESS, + VCL_TEST_HTTP_COMPLETED, +} vcl_test_http_state_t; + +typedef struct vcl_test_http_ctx_t +{ + u8 is_server; + vcl_test_http_state_t test_state; + u64 rem_data; +} vcl_test_http_ctx_t; static int vt_tcp_connect (vcl_test_session_t *ts, vppcom_endpt_t *endpt) @@ -978,6 +995,418 @@ static const vcl_test_proto_vft_t vcl_test_srtp = { VCL_TEST_REGISTER_PROTO (VPPCOM_PROTO_SRTP, vcl_test_srtp); +static void +vt_http_session_init (vcl_test_session_t *ts, u8 is_server) +{ + vcl_test_http_ctx_t *http_ctx; + + http_ctx = malloc (sizeof (vcl_test_http_ctx_t)); + memset (http_ctx, 0, sizeof (*http_ctx)); + http_ctx->is_server = is_server; + ts->opaque = http_ctx; +} + +static inline void +vt_http_send_reply_msg (vcl_test_session_t *ts, http_status_code_t status) +{ + http_msg_t msg; + int rv = 0; + + memset (&msg, 0, sizeof (http_msg_t)); + msg.type = HTTP_MSG_REPLY; + msg.code = status; + + vppcom_data_segment_t segs[1] = { { (u8 *) &msg, sizeof (msg) } }; + + do + { + rv = vppcom_session_write_segments (ts->fd, segs, 1); + + if (rv < 0) + { + errno = -rv; + if (errno == EAGAIN || errno == EWOULDBLOCK) + continue; + + vterr ("vppcom_session_write()", -errno); + break; + } + } + while (rv <= 0); +} + +static inline int +vt_process_http_server_read_msg (vcl_test_session_t *ts, void *buf, + uint32_t nbytes) +{ + http_msg_t msg; + u8 *target_path = 0; + vcl_test_http_ctx_t *vcl_test_http_ctx = (vcl_test_http_ctx_t *) ts->opaque; + vcl_test_stats_t *stats = &ts->stats; + int rv = 0; + + do + { + stats->rx_xacts++; + rv = vppcom_session_read (ts->fd, buf, nbytes); + + if (rv <= 0) + { + errno = -rv; + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + stats->rx_eagain++; + continue; + } + + vterr ("vppcom_session_read()", -errno); + return 0; + } + + if (PREDICT_TRUE (vcl_test_http_ctx->test_state == + VCL_TEST_HTTP_IN_PROGRESS)) + { + vcl_test_http_ctx->rem_data -= rv; + + if (vcl_test_http_ctx->rem_data == 0) + { + vcl_test_http_ctx->test_state = VCL_TEST_HTTP_COMPLETED; + vt_http_send_reply_msg (ts, HTTP_STATUS_OK); + } + } + else if (PREDICT_FALSE (vcl_test_http_ctx->test_state == + VCL_TEST_HTTP_IDLE)) + { + msg = *(http_msg_t *) buf; + + /* verify that we have received http post request from client */ + if (msg.type != HTTP_MSG_REQUEST || msg.method_type != HTTP_REQ_POST) + { + vt_http_send_reply_msg (ts, HTTP_STATUS_METHOD_NOT_ALLOWED); + vterr ("error! only POST requests allowed from client", 0); + return 0; + } + + if (msg.data.target_form != HTTP_TARGET_ORIGIN_FORM) + { + vt_http_send_reply_msg (ts, HTTP_STATUS_BAD_REQUEST); + vterr ("error! http target not in origin form", 0); + return 0; + } + + /* validate target path syntax */ + if (msg.data.target_path_len) + { + vec_validate (target_path, msg.data.target_path_len - 1); + memcpy (target_path, + buf + sizeof (msg) + msg.data.target_path_offset - 1, + msg.data.target_path_len + 1); + if (http_validate_abs_path_syntax (target_path, 0)) + { + vt_http_send_reply_msg (ts, HTTP_STATUS_BAD_REQUEST); + vterr ("error! target path is not absolute", 0); + vec_free (target_path); + return 0; + } + vec_free (target_path); + } + + /* read body */ + if (msg.data.body_len) + { + vcl_test_http_ctx->rem_data = msg.data.body_len; + /* | <http_msg_t> | <target> | <headers> | <body> | */ + vcl_test_http_ctx->rem_data -= + (rv - sizeof (msg) - msg.data.body_offset); + vcl_test_http_ctx->test_state = VCL_TEST_HTTP_IN_PROGRESS; + } + } + + if (rv < nbytes) + stats->rx_incomp++; + } + while (rv <= 0); + + stats->rx_bytes += rv; + return (rv); +} + +static inline int +vt_process_http_client_read_msg (vcl_test_session_t *ts, void *buf, + uint32_t nbytes) +{ + http_msg_t msg; + int rv = 0; + + do + { + rv = vppcom_session_read (ts->fd, buf, nbytes); + + if (rv < 0) + { + errno = -rv; + if (errno == EAGAIN || errno == EWOULDBLOCK) + continue; + + vterr ("vppcom_session_read()", -errno); + break; + } + } + while (!rv); + + msg = *(http_msg_t *) buf; + + if (msg.type == HTTP_MSG_REPLY && msg.code == HTTP_STATUS_OK) + vtinf ("received 200 OK from server"); + else + vterr ("received unexpected reply from server", 0); + + return (rv); +} + +static inline int +vt_process_http_client_write_msg (vcl_test_session_t *ts, void *buf, + uint32_t nbytes) +{ + http_msg_t msg; + http_header_t *req_headers = 0; + u8 *headers_buf = 0; + u8 *target; + vcl_test_http_ctx_t *vcl_test_http_ctx = (vcl_test_http_ctx_t *) ts->opaque; + vcl_test_stats_t *stats = &ts->stats; + int rv = 0; + + if (PREDICT_TRUE (vcl_test_http_ctx->test_state == + VCL_TEST_HTTP_IN_PROGRESS)) + { + do + { + rv = vppcom_session_write ( + ts->fd, buf, clib_min (nbytes, vcl_test_http_ctx->rem_data)); + + if (rv <= 0) + { + errno = -rv; + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + stats->tx_eagain++; + continue; + } + + vterr ("vppcom_session_write()", -errno); + return 0; + } + + vcl_test_http_ctx->rem_data -= rv; + + if (vcl_test_http_ctx->rem_data == 0) + { + vcl_test_http_ctx->test_state = VCL_TEST_HTTP_COMPLETED; + vtinf ("client finished sending %ld bytes of data", + ts->cfg.total_bytes); + } + + if (rv < nbytes) + stats->tx_incomp++; + } + while (rv <= 0); + } + + else if (PREDICT_FALSE (vcl_test_http_ctx->test_state == VCL_TEST_HTTP_IDLE)) + { + http_add_header ( + &req_headers, http_header_name_token (HTTP_HEADER_CONTENT_TYPE), + http_content_type_token (HTTP_CONTENT_APP_OCTET_STREAM)); + headers_buf = http_serialize_headers (req_headers); + vec_free (req_headers); + + memset (&msg, 0, sizeof (http_msg_t)); + msg.type = HTTP_MSG_REQUEST; + msg.method_type = HTTP_REQ_POST; + + /* target */ + msg.data.target_form = HTTP_TARGET_ORIGIN_FORM; + target = (u8 *) "/vcl_test_http\0"; + msg.data.target_path_len = strlen ((char *) target); + + /* headers */ + msg.data.headers_offset = msg.data.target_path_len; + msg.data.headers_len = vec_len (headers_buf); + + /* body */ + msg.data.body_offset = msg.data.headers_offset + msg.data.headers_len; + msg.data.body_len = ts->cfg.total_bytes; + + msg.data.len = + msg.data.target_path_len + msg.data.headers_len + msg.data.body_len; + msg.data.type = HTTP_MSG_DATA_INLINE; + + vppcom_data_segment_t segs[3] = { { (u8 *) &msg, sizeof (msg) }, + { target, strlen ((char *) target) }, + { headers_buf, + vec_len (headers_buf) } }; + + do + { + rv = vppcom_session_write_segments (ts->fd, segs, 3); + + if (rv <= 0) + { + errno = -rv; + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + stats->tx_eagain++; + continue; + } + + vterr ("vppcom_session_write_segments()", -errno); + vec_free (headers_buf); + return 0; + } + } + while (rv <= 0); + + vcl_test_http_ctx->test_state = VCL_TEST_HTTP_IN_PROGRESS; + vcl_test_http_ctx->rem_data = ts->cfg.total_bytes; + vec_free (headers_buf); + } + + stats->tx_bytes += rv; + return (rv); +} + +static inline int +vt_process_http_server_write_msg (vcl_test_session_t *ts, void *buf, + uint32_t nbytes) +{ + return 0; +} + +static inline int +vt_http_read (vcl_test_session_t *ts, void *buf, uint32_t nbytes) +{ + vcl_test_http_ctx_t *vcl_test_http_ctx = (vcl_test_http_ctx_t *) ts->opaque; + + if (vcl_test_http_ctx->is_server) + return vt_process_http_server_read_msg (ts, buf, nbytes); + else + return vt_process_http_client_read_msg (ts, buf, nbytes); +} + +static inline int +vt_http_write (vcl_test_session_t *ts, void *buf, uint32_t nbytes) +{ + vcl_test_http_ctx_t *vcl_test_http_ctx = (vcl_test_http_ctx_t *) ts->opaque; + + if (vcl_test_http_ctx->is_server) + return vt_process_http_server_write_msg (ts, buf, nbytes); + else + return vt_process_http_client_write_msg (ts, buf, nbytes); +} + +static int +vt_http_connect (vcl_test_session_t *ts, vppcom_endpt_t *endpt) +{ + uint32_t flags, flen; + int rv; + + ts->fd = vppcom_session_create (VPPCOM_PROTO_HTTP, ts->noblk_connect); + if (ts->fd < 0) + { + vterr ("vppcom_session_create()", ts->fd); + return ts->fd; + } + + rv = vppcom_session_connect (ts->fd, endpt); + if (rv < 0 && rv != VPPCOM_EINPROGRESS) + { + vterr ("vppcom_session_connect()", rv); + return rv; + } + + ts->read = vt_http_read; + ts->write = vt_http_write; + + if (!ts->noblk_connect) + { + flags = O_NONBLOCK; + flen = sizeof (flags); + vppcom_session_attr (ts->fd, VPPCOM_ATTR_SET_FLAGS, &flags, &flen); + vtinf ("Test session %d (fd %d) connected.", ts->session_index, ts->fd); + } + + vt_http_session_init (ts, 0 /* is_server */); + + return 0; +} + +static int +vt_http_listen (vcl_test_session_t *ts, vppcom_endpt_t *endpt) +{ + int rv; + + ts->fd = vppcom_session_create (VPPCOM_PROTO_HTTP, 1 /* is_nonblocking */); + if (ts->fd < 0) + { + vterr ("vppcom_session_create()", ts->fd); + return ts->fd; + } + + rv = vppcom_session_bind (ts->fd, endpt); + if (rv < 0) + { + vterr ("vppcom_session_bind()", rv); + return rv; + } + + rv = vppcom_session_listen (ts->fd, 10); + if (rv < 0) + { + vterr ("vppcom_session_listen()", rv); + return rv; + } + + return 0; +} + +static int +vt_http_accept (int listen_fd, vcl_test_session_t *ts) +{ + int client_fd; + + client_fd = vppcom_session_accept (listen_fd, &ts->endpt, 0); + if (client_fd < 0) + { + vterr ("vppcom_session_accept()", client_fd); + return client_fd; + } + + ts->fd = client_fd; + ts->is_open = 1; + ts->read = vt_http_read; + ts->write = vt_http_write; + + vt_http_session_init (ts, 1 /* is_server */); + + return 0; +} + +static int +vt_http_close (vcl_test_session_t *ts) +{ + free (ts->opaque); + return 0; +} + +static const vcl_test_proto_vft_t vcl_test_http = { + .open = vt_http_connect, + .listen = vt_http_listen, + .accept = vt_http_accept, + .close = vt_http_close, +}; + +VCL_TEST_REGISTER_PROTO (VPPCOM_PROTO_HTTP, vcl_test_http); + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/plugins/hs_apps/vcl/vcl_test_server.c b/src/plugins/hs_apps/vcl/vcl_test_server.c index d17a2089ba7..008539f2585 100644 --- a/src/plugins/hs_apps/vcl/vcl_test_server.c +++ b/src/plugins/hs_apps/vcl/vcl_test_server.c @@ -282,11 +282,7 @@ vts_server_process_rx (vcl_test_session_t *conn, int rx_bytes) if (conn->cfg.test == HS_TEST_TYPE_BI) { if (vsm->use_ds) - { - (void) vcl_test_write (conn, conn->ds[0].data, conn->ds[0].len); - if (conn->ds[1].len) - (void) vcl_test_write (conn, conn->ds[1].data, conn->ds[1].len); - } + (void) vcl_test_write_ds (conn); else (void) vcl_test_write (conn, conn->rxbuf, rx_bytes); } @@ -420,36 +416,41 @@ static void vcl_test_init_endpoint_addr (vcl_test_server_main_t * vsm) { struct sockaddr_storage *servaddr = &vsm->servaddr; - memset (servaddr, 0, sizeof (*servaddr)); if (vsm->server_cfg.address_ip6) { struct sockaddr_in6 *server_addr = (struct sockaddr_in6 *) servaddr; - server_addr->sin6_family = AF_INET6; - server_addr->sin6_addr = in6addr_any; - server_addr->sin6_port = htons (vsm->server_cfg.port); + vsm->server_cfg.endpt.is_ip4 = 0; + vsm->server_cfg.endpt.ip = (uint8_t *) &server_addr->sin6_addr; + vsm->server_cfg.endpt.port = htons (vsm->server_cfg.port); } else { struct sockaddr_in *server_addr = (struct sockaddr_in *) servaddr; - server_addr->sin_family = AF_INET; - server_addr->sin_addr.s_addr = htonl (INADDR_ANY); - server_addr->sin_port = htons (vsm->server_cfg.port); + vsm->server_cfg.endpt.is_ip4 = 1; + vsm->server_cfg.endpt.ip = (uint8_t *) &server_addr->sin_addr; + vsm->server_cfg.endpt.port = htons (vsm->server_cfg.port); } +} + +static void +vcl_test_clear_endpoint_addr (vcl_test_server_main_t *vsm) +{ + struct sockaddr_storage *servaddr = &vsm->servaddr; + + memset (&vsm->servaddr, 0, sizeof (vsm->servaddr)); if (vsm->server_cfg.address_ip6) { struct sockaddr_in6 *server_addr = (struct sockaddr_in6 *) servaddr; - vsm->server_cfg.endpt.is_ip4 = 0; - vsm->server_cfg.endpt.ip = (uint8_t *) &server_addr->sin6_addr; - vsm->server_cfg.endpt.port = (uint16_t) server_addr->sin6_port; + server_addr->sin6_family = AF_INET6; + server_addr->sin6_addr = in6addr_any; } else { struct sockaddr_in *server_addr = (struct sockaddr_in *) servaddr; - vsm->server_cfg.endpt.is_ip4 = 1; - vsm->server_cfg.endpt.ip = (uint8_t *) &server_addr->sin_addr; - vsm->server_cfg.endpt.port = (uint16_t) server_addr->sin_port; + server_addr->sin_family = AF_INET; + server_addr->sin_addr.s_addr = htonl (INADDR_ANY); } } @@ -460,9 +461,10 @@ vcl_test_server_process_opts (vcl_test_server_main_t * vsm, int argc, int v, c; vsm->server_cfg.proto = VPPCOM_PROTO_TCP; + vcl_test_clear_endpoint_addr (vsm); opterr = 0; - while ((c = getopt (argc, argv, "6DLsw:hp:S")) != -1) + while ((c = getopt (argc, argv, "6DLsw:hp:SB:")) != -1) switch (c) { case '6': @@ -473,7 +475,22 @@ vcl_test_server_process_opts (vcl_test_server_main_t * vsm, int argc, if (vppcom_unformat_proto (&vsm->server_cfg.proto, optarg)) vtwrn ("Invalid vppcom protocol %s, defaulting to TCP", optarg); break; - + case 'B': + if (vsm->server_cfg.address_ip6) + { + if (inet_pton ( + AF_INET6, optarg, + &((struct sockaddr_in6 *) &vsm->servaddr)->sin6_addr) != 1) + vtwrn ("couldn't parse ipv6 addr %s", optarg); + } + else + { + if (inet_pton ( + AF_INET, optarg, + &((struct sockaddr_in *) &vsm->servaddr)->sin_addr) != 1) + vtwrn ("couldn't parse ipv4 addr %s", optarg); + } + break; case 'D': vsm->server_cfg.proto = VPPCOM_PROTO_UDP; break; diff --git a/src/plugins/http/CMakeLists.txt b/src/plugins/http/CMakeLists.txt index d9cd84a3955..075b8d6817b 100644 --- a/src/plugins/http/CMakeLists.txt +++ b/src/plugins/http/CMakeLists.txt @@ -17,3 +17,8 @@ add_vpp_plugin(http http_buffer.c http_timer.c ) + +add_vpp_plugin(http_unittest + SOURCES + test/http_test.c +) diff --git a/src/plugins/http/http.c b/src/plugins/http/http.c index 22aaaeb59b5..1ea5a08fbf6 100644 --- a/src/plugins/http/http.c +++ b/src/plugins/http/http.c @@ -16,11 +16,11 @@ #include <http/http.h> #include <vnet/session/session.h> #include <http/http_timer.h> +#include <http/http_status_codes.h> static http_main_t http_main; #define HTTP_FIFO_THRESH (16 << 10) -#define CONTENT_LEN_STR "Content-Length: " /* HTTP state machine result */ typedef enum http_sm_result_t_ @@ -30,67 +30,58 @@ typedef enum http_sm_result_t_ HTTP_SM_ERROR = -1, } http_sm_result_t; -const char *http_status_code_str[] = { -#define _(c, s, str) str, - foreach_http_status_code -#undef _ -}; - -const char *http_content_type_str[] = { -#define _(s, ext, str) str, - foreach_http_content_type -#undef _ -}; - const http_buffer_type_t msg_to_buf_type[] = { [HTTP_MSG_DATA_INLINE] = HTTP_BUFFER_FIFO, [HTTP_MSG_DATA_PTR] = HTTP_BUFFER_PTR, }; -u8 * -format_http_state (u8 *s, va_list *va) +static u8 * +format_http_req_state (u8 *s, va_list *va) { - http_state_t state = va_arg (*va, http_state_t); + http_req_state_t state = va_arg (*va, http_req_state_t); + u8 *t = 0; switch (state) { - case HTTP_STATE_IDLE: - return format (s, "idle"); - case HTTP_STATE_WAIT_APP_METHOD: - return format (s, "wait app method"); - case HTTP_STATE_WAIT_SERVER_REPLY: - return format (s, "wait server reply"); - case HTTP_STATE_CLIENT_IO_MORE_DATA: - return format (s, "client io more data"); - case HTTP_STATE_WAIT_CLIENT_METHOD: - return format (s, "wait client method"); - case HTTP_STATE_WAIT_APP_REPLY: - return format (s, "wait app reply"); - case HTTP_STATE_APP_IO_MORE_DATA: - return format (s, "app io more data"); - default: - break; - } - return format (s, "unknown"); -} - -#define http_state_change(_hc, _state) \ +#define _(n, s, str) \ + case HTTP_REQ_STATE_##s: \ + t = (u8 *) str; \ + break; + foreach_http_req_state +#undef _ + default : return format (s, "unknown"); + } + return format (s, "%s", t); +} + +#define http_req_state_change(_hc, _state) \ do \ { \ - HTTP_DBG (1, "changing http state %U -> %U", format_http_state, \ - (_hc)->http_state, format_http_state, _state); \ - (_hc)->http_state = _state; \ + HTTP_DBG (1, "changing http req state: %U -> %U", \ + format_http_req_state, (_hc)->req_state, \ + format_http_req_state, _state); \ + ASSERT ((_hc)->req_state != HTTP_REQ_STATE_TUNNEL); \ + (_hc)->req_state = _state; \ } \ while (0) -static inline int -http_state_is_tx_valid (http_conn_t *hc) +static u8 * +format_http_conn_state (u8 *s, va_list *args) { - http_state_t state = hc->http_state; - return (state == HTTP_STATE_APP_IO_MORE_DATA || - state == HTTP_STATE_CLIENT_IO_MORE_DATA || - state == HTTP_STATE_WAIT_APP_REPLY || - state == HTTP_STATE_WAIT_APP_METHOD); + http_conn_t *hc = va_arg (*args, http_conn_t *); + u8 *t = 0; + + switch (hc->state) + { +#define _(s, str) \ + case HTTP_CONN_STATE_##s: \ + t = (u8 *) str; \ + break; + foreach_http_conn_state +#undef _ + default : return format (s, "unknown"); + } + return format (s, "%s", t); } static inline http_worker_t * @@ -121,6 +112,15 @@ http_conn_get_w_thread (u32 hc_index, u32 thread_index) return pool_elt_at_index (wrk->conn_pool, hc_index); } +static inline http_conn_t * +http_conn_get_w_thread_if_valid (u32 hc_index, u32 thread_index) +{ + http_worker_t *wrk = http_worker_get (thread_index); + if (pool_is_free_index (wrk->conn_pool, hc_index)) + return 0; + return pool_elt_at_index (wrk->conn_pool, hc_index); +} + void http_conn_free (http_conn_t *hc) { @@ -128,6 +128,35 @@ http_conn_free (http_conn_t *hc) pool_put (wrk->conn_pool, hc); } +static inline http_conn_t * +http_ho_conn_get (u32 ho_hc_index) +{ + http_main_t *hm = &http_main; + return pool_elt_at_index (hm->ho_conn_pool, ho_hc_index); +} + +void +http_ho_conn_free (http_conn_t *ho_hc) +{ + http_main_t *hm = &http_main; + pool_put (hm->ho_conn_pool, ho_hc); +} + +static inline u32 +http_ho_conn_alloc (void) +{ + http_main_t *hm = &http_main; + http_conn_t *hc; + + pool_get_aligned_safe (hm->ho_conn_pool, hc, CLIB_CACHE_LINE_BYTES); + clib_memset (hc, 0, sizeof (*hc)); + hc->h_hc_index = hc - hm->ho_conn_pool; + hc->h_pa_session_handle = SESSION_INVALID_HANDLE; + hc->h_tc_session_handle = SESSION_INVALID_HANDLE; + hc->timeout = HTTP_CONN_TIMEOUT; + return hc->h_hc_index; +} + static u32 http_listener_alloc (void) { @@ -136,6 +165,7 @@ http_listener_alloc (void) pool_get_zero (hm->listener_pool, lhc); lhc->c_c_index = lhc - hm->listener_pool; + lhc->timeout = HTTP_CONN_TIMEOUT; return lhc->c_c_index; } @@ -171,20 +201,47 @@ http_disconnect_transport (http_conn_t *hc) } static void +http_conn_invalidate_timer_cb (u32 hs_handle) +{ + http_conn_t *hc; + + hc = + http_conn_get_w_thread_if_valid (hs_handle & 0x00FFFFFF, hs_handle >> 24); + + HTTP_DBG (1, "hc [%u]%x", hs_handle >> 24, hs_handle & 0x00FFFFFF); + if (!hc) + { + HTTP_DBG (1, "already deleted"); + return; + } + + hc->timer_handle = HTTP_TIMER_HANDLE_INVALID; + hc->pending_timer = 1; +} + +static void http_conn_timeout_cb (void *hc_handlep) { http_conn_t *hc; uword hs_handle; hs_handle = pointer_to_uword (hc_handlep); - hc = http_conn_get_w_thread (hs_handle & 0x00FFFFFF, hs_handle >> 24); + hc = + http_conn_get_w_thread_if_valid (hs_handle & 0x00FFFFFF, hs_handle >> 24); - HTTP_DBG (1, "terminate thread %d index %d hs %llx", hs_handle >> 24, - hs_handle & 0x00FFFFFF, hc); + HTTP_DBG (1, "hc [%u]%x", hs_handle >> 24, hs_handle & 0x00FFFFFF); if (!hc) - return; + { + HTTP_DBG (1, "already deleted"); + return; + } + + if (!hc->pending_timer) + { + HTTP_DBG (1, "timer not pending"); + return; + } - hc->timer_handle = ~0; session_transport_closing_notify (&hc->connection); http_disconnect_transport (hc); } @@ -204,6 +261,7 @@ http_ts_accept_callback (session_t *ts) hc_index = http_conn_alloc_w_thread (ts->thread_index); hc = http_conn_get_w_thread (hc_index, ts->thread_index); clib_memcpy_fast (hc, lhc, sizeof (*lhc)); + hc->timer_handle = HTTP_TIMER_HANDLE_INVALID; hc->c_thread_index = ts->thread_index; hc->h_hc_index = hc_index; @@ -211,7 +269,7 @@ http_ts_accept_callback (session_t *ts) hc->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP; hc->state = HTTP_CONN_STATE_ESTABLISHED; - http_state_change (hc, HTTP_STATE_WAIT_CLIENT_METHOD); + http_req_state_change (hc, HTTP_REQ_STATE_WAIT_TRANSPORT_METHOD); ts->session_state = SESSION_STATE_READY; ts->opaque = hc_index; @@ -236,6 +294,7 @@ http_ts_accept_callback (session_t *ts) if ((rv = app_worker_init_accepted (as))) { HTTP_DBG (1, "failed to allocate fifos"); + hc->h_pa_session_handle = SESSION_INVALID_HANDLE; session_free (as); return rv; } @@ -277,7 +336,7 @@ http_ts_connected_callback (u32 http_app_index, u32 ho_hc_index, session_t *ts, app_worker_t *app_wrk; int rv; - ho_hc = http_conn_get_w_thread (ho_hc_index, 0); + ho_hc = http_ho_conn_get (ho_hc_index); ASSERT (ho_hc->state == HTTP_CONN_STATE_CONNECTING); if (err) @@ -295,12 +354,13 @@ http_ts_connected_callback (u32 http_app_index, u32 ho_hc_index, session_t *ts, clib_memcpy_fast (hc, ho_hc, sizeof (*hc)); + hc->timer_handle = HTTP_TIMER_HANDLE_INVALID; hc->c_thread_index = ts->thread_index; hc->h_tc_session_handle = session_handle (ts); hc->c_c_index = new_hc_index; hc->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP; hc->state = HTTP_CONN_STATE_ESTABLISHED; - http_state_change (hc, HTTP_STATE_WAIT_APP_METHOD); + http_req_state_change (hc, HTTP_REQ_STATE_WAIT_APP_METHOD); ts->session_state = SESSION_STATE_READY; ts->opaque = new_hc_index; @@ -316,8 +376,8 @@ http_ts_connected_callback (u32 http_app_index, u32 ho_hc_index, session_t *ts, as->session_type = session_type_from_proto_and_ip ( TRANSPORT_PROTO_HTTP, session_type_is_ip4 (ts->session_type)); - HTTP_DBG (1, "half-open hc index %d, hc index %d", ho_hc_index, - new_hc_index); + HTTP_DBG (1, "half-open hc index %x, hc [%u]%x", ho_hc_index, + ts->thread_index, new_hc_index); app_wrk = app_worker_get (hc->h_pa_wrk_index); if (!app_wrk) @@ -363,7 +423,7 @@ http_ts_reset_callback (session_t *ts) hc->state = HTTP_CONN_STATE_CLOSED; http_buffer_free (&hc->tx_buf); - http_state_change (hc, HTTP_STATE_WAIT_CLIENT_METHOD); + http_req_state_change (hc, HTTP_REQ_STATE_WAIT_TRANSPORT_METHOD); session_transport_reset_notify (&hc->connection); http_disconnect_transport (hc); @@ -374,47 +434,54 @@ http_ts_reset_callback (session_t *ts) */ static const char *http_error_template = "HTTP/1.1 %s\r\n" "Date: %U GMT\r\n" - "Content-Type: text/html\r\n" "Connection: close\r\n" - "Pragma: no-cache\r\n" "Content-Length: 0\r\n\r\n"; -static const char *http_redirect_template = "HTTP/1.1 %s\r\n"; - /** * http response boilerplate */ static const char *http_response_template = "HTTP/1.1 %s\r\n" "Date: %U GMT\r\n" - "Expires: %U GMT\r\n" - "Server: %v\r\n" - "Content-Type: %s\r\n" - "Content-Length: %lu\r\n\r\n"; + "Server: %v\r\n"; -static const char *http_request_template = "GET %s HTTP/1.1\r\n" - "User-Agent: %v\r\n" - "Accept: */*\r\n\r\n"; +static const char *content_len_template = "Content-Length: %llu\r\n"; + +/** + * http request boilerplate + */ +static const char *http_get_request_template = "GET %s HTTP/1.1\r\n" + "Host: %v\r\n" + "User-Agent: %v\r\n" + "%s"; + +static const char *http_post_request_template = "POST %s HTTP/1.1\r\n" + "Host: %v\r\n" + "User-Agent: %v\r\n" + "Content-Length: %llu\r\n" + "%s"; static u32 -http_send_data (http_conn_t *hc, u8 *data, u32 length, u32 offset) +http_send_data (http_conn_t *hc, u8 *data, u32 length) { const u32 max_burst = 64 << 10; session_t *ts; u32 to_send; - int sent; + int rv; ts = session_get_from_handle (hc->h_tc_session_handle); - to_send = clib_min (length - offset, max_burst); - sent = svm_fifo_enqueue (ts->tx_fifo, to_send, data + offset); - - if (sent <= 0) - return offset; + to_send = clib_min (length, max_burst); + rv = svm_fifo_enqueue (ts->tx_fifo, to_send, data); + if (rv <= 0) + { + clib_warning ("svm_fifo_enqueue failed, rv %d", rv); + return 0; + } if (svm_fifo_set_event (ts->tx_fifo)) - session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX); + session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX); - return (offset + sent); + return rv; } static void @@ -430,33 +497,56 @@ http_send_error (http_conn_t *hc, http_status_code_t ec) now = clib_timebase_now (&hm->timebase); data = format (0, http_error_template, http_status_code_str[ec], format_clib_timebase_time, now); - http_send_data (hc, data, vec_len (data), 0); + HTTP_DBG (3, "%v", data); + http_send_data (hc, data, vec_len (data)); vec_free (data); } static int http_read_message (http_conn_t *hc) { - u32 max_deq, cursize; + u32 max_deq; session_t *ts; int n_read; ts = session_get_from_handle (hc->h_tc_session_handle); - cursize = vec_len (hc->rx_buf); max_deq = svm_fifo_max_dequeue (ts->rx_fifo); if (PREDICT_FALSE (max_deq == 0)) return -1; - vec_validate (hc->rx_buf, cursize + max_deq - 1); - n_read = svm_fifo_dequeue (ts->rx_fifo, max_deq, hc->rx_buf + cursize); + vec_validate (hc->rx_buf, max_deq - 1); + n_read = svm_fifo_peek (ts->rx_fifo, 0, max_deq, hc->rx_buf); ASSERT (n_read == max_deq); + HTTP_DBG (1, "read %u bytes from rx_fifo", n_read); + + return 0; +} + +static void +http_read_message_drop (http_conn_t *hc, u32 len) +{ + session_t *ts; + + ts = session_get_from_handle (hc->h_tc_session_handle); + svm_fifo_dequeue_drop (ts->rx_fifo, len); + vec_reset_length (hc->rx_buf); if (svm_fifo_is_empty (ts->rx_fifo)) svm_fifo_unset_event (ts->rx_fifo); +} - vec_set_len (hc->rx_buf, cursize + n_read); - return 0; +static void +http_read_message_drop_all (http_conn_t *hc) +{ + session_t *ts; + + ts = session_get_from_handle (hc->h_tc_session_handle); + svm_fifo_dequeue_drop_all (ts->rx_fifo); + vec_reset_length (hc->rx_buf); + + if (svm_fifo_is_empty (ts->rx_fifo)) + svm_fifo_unset_event (ts->rx_fifo); } /** @@ -501,13 +591,18 @@ v_find_index (u8 *vec, u32 offset, u32 num, char *str) static void http_identify_optional_query (http_conn_t *hc) { - u32 pos = vec_search (hc->rx_buf, '?'); - if (~0 != pos) + int i; + for (i = hc->target_path_offset; + i < (hc->target_path_offset + hc->target_path_len); i++) { - hc->target_query_offset = pos + 1; - hc->target_query_len = - hc->target_path_offset + hc->target_path_len - hc->target_query_offset; - hc->target_path_len = hc->target_path_len - hc->target_query_len - 1; + if (hc->rx_buf[i] == '?') + { + hc->target_query_offset = i + 1; + hc->target_query_len = hc->target_path_offset + hc->target_path_len - + hc->target_query_offset; + hc->target_path_len = hc->target_path_len - hc->target_query_len - 1; + break; + } } } @@ -563,18 +658,19 @@ static int http_parse_request_line (http_conn_t *hc, http_status_code_t *ec) { int i, target_len; - u32 next_line_offset; + u32 next_line_offset, method_offset; /* request-line = method SP request-target SP HTTP-version CRLF */ - i = v_find_index (hc->rx_buf, 0, 0, "\r\n"); + i = v_find_index (hc->rx_buf, 8, 0, "\r\n"); if (i < 0) { clib_warning ("request line incomplete"); *ec = HTTP_STATUS_BAD_REQUEST; return -1; } - HTTP_DBG (0, "request line length: %d", i); - next_line_offset = i + 2; + HTTP_DBG (2, "request line length: %d", i); + hc->control_data_len = i + 2; + next_line_offset = hc->control_data_len; /* there should be at least one more CRLF */ if (vec_len (hc->rx_buf) < (next_line_offset + 2)) @@ -584,24 +680,47 @@ http_parse_request_line (http_conn_t *hc, http_status_code_t *ec) return -1; } + /* + * RFC9112 2.2: + * In the interest of robustness, a server that is expecting to receive and + * parse a request-line SHOULD ignore at least one empty line (CRLF) + * received prior to the request-line. + */ + method_offset = hc->rx_buf[0] == '\r' && hc->rx_buf[1] == '\n' ? 2 : 0; /* parse method */ - if ((i = v_find_index (hc->rx_buf, 0, next_line_offset, "GET ")) >= 0) + if (!memcmp (hc->rx_buf + method_offset, "GET ", 4)) { HTTP_DBG (0, "GET method"); hc->method = HTTP_REQ_GET; - hc->target_path_offset = i + 4; + hc->target_path_offset = method_offset + 4; } - else if ((i = v_find_index (hc->rx_buf, 0, next_line_offset, "POST ")) >= 0) + else if (!memcmp (hc->rx_buf + method_offset, "POST ", 5)) { HTTP_DBG (0, "POST method"); hc->method = HTTP_REQ_POST; - hc->target_path_offset = i + 5; + hc->target_path_offset = method_offset + 5; + } + else if (!memcmp (hc->rx_buf + method_offset, "CONNECT ", 8)) + { + HTTP_DBG (0, "CONNECT method"); + hc->method = HTTP_REQ_CONNECT; + hc->target_path_offset = method_offset + 8; + hc->is_tunnel = 1; } else { - clib_warning ("method not implemented: %8v", hc->rx_buf); - *ec = HTTP_STATUS_NOT_IMPLEMENTED; - return -1; + if (hc->rx_buf[method_offset] - 'A' <= 'Z' - 'A') + { + clib_warning ("method not implemented: %8v", hc->rx_buf); + *ec = HTTP_STATUS_NOT_IMPLEMENTED; + return -1; + } + else + { + clib_warning ("not method name: %8v", hc->rx_buf); + *ec = HTTP_STATUS_BAD_REQUEST; + return -1; + } } /* find version */ @@ -631,7 +750,9 @@ http_parse_request_line (http_conn_t *hc, http_status_code_t *ec) } /* parse request-target */ + HTTP_DBG (2, "http at %d", i); target_len = i - hc->target_path_offset; + HTTP_DBG (2, "target_len %d", target_len); if (target_len < 1) { clib_warning ("request-target not present"); @@ -647,10 +768,116 @@ http_parse_request_line (http_conn_t *hc, http_status_code_t *ec) *ec = HTTP_STATUS_BAD_REQUEST; return -1; } - HTTP_DBG (0, "request-target path length: %u", hc->target_path_len); - HTTP_DBG (0, "request-target path offset: %u", hc->target_path_offset); - HTTP_DBG (0, "request-target query length: %u", hc->target_query_len); - HTTP_DBG (0, "request-target query offset: %u", hc->target_query_offset); + HTTP_DBG (2, "request-target path length: %u", hc->target_path_len); + HTTP_DBG (2, "request-target path offset: %u", hc->target_path_offset); + HTTP_DBG (2, "request-target query length: %u", hc->target_query_len); + HTTP_DBG (2, "request-target query offset: %u", hc->target_query_offset); + + /* set buffer offset to nex line start */ + hc->rx_buf_offset = next_line_offset; + + return 0; +} + +#define expect_char(c) \ + if (*p++ != c) \ + { \ + clib_warning ("unexpected character"); \ + return -1; \ + } + +#define parse_int(val, mul) \ + do \ + { \ + if (!isdigit (*p)) \ + { \ + clib_warning ("expected digit"); \ + return -1; \ + } \ + val += mul * (*p++ - '0'); \ + } \ + while (0) + +static int +http_parse_status_line (http_conn_t *hc) +{ + int i; + u32 next_line_offset; + u8 *p, *end; + u16 status_code = 0; + + i = v_find_index (hc->rx_buf, 0, 0, "\r\n"); + /* status-line = HTTP-version SP status-code SP [ reason-phrase ] CRLF */ + if (i < 0) + { + clib_warning ("status line incomplete"); + return -1; + } + HTTP_DBG (2, "status line length: %d", i); + if (i < 12) + { + clib_warning ("status line too short (%d)", i); + return -1; + } + hc->control_data_len = i + 2; + next_line_offset = hc->control_data_len; + p = hc->rx_buf; + end = hc->rx_buf + i; + + /* there should be at least one more CRLF */ + if (vec_len (hc->rx_buf) < (next_line_offset + 2)) + { + clib_warning ("malformed message, too short"); + return -1; + } + + /* parse version */ + expect_char ('H'); + expect_char ('T'); + expect_char ('T'); + expect_char ('P'); + expect_char ('/'); + expect_char ('1'); + expect_char ('.'); + if (!isdigit (*p++)) + { + clib_warning ("invalid HTTP minor version"); + return -1; + } + + /* skip space(s) */ + if (*p != ' ') + { + clib_warning ("no space after HTTP version"); + return -1; + } + do + { + p++; + if (p == end) + { + clib_warning ("no status code"); + return -1; + } + } + while (*p == ' '); + + /* parse status code */ + if ((end - p) < 3) + { + clib_warning ("not enough characters for status code"); + return -1; + } + parse_int (status_code, 100); + parse_int (status_code, 10); + parse_int (status_code, 1); + if (status_code < 100 || status_code > 599) + { + clib_warning ("invalid status code %d", status_code); + return -1; + } + hc->status_code = status_code; + HTTP_DBG (0, "status code: %d", hc->status_code); /* set buffer offset to nex line start */ hc->rx_buf_offset = next_line_offset; @@ -668,8 +895,9 @@ http_identify_headers (http_conn_t *hc, http_status_code_t *ec) (hc->rx_buf[hc->rx_buf_offset + 1] == '\n')) { /* just another CRLF -> no headers */ - HTTP_DBG (0, "no headers"); + HTTP_DBG (2, "no headers"); hc->headers_len = 0; + hc->control_data_len += 2; return 0; } @@ -683,8 +911,9 @@ http_identify_headers (http_conn_t *hc, http_status_code_t *ec) } hc->headers_offset = hc->rx_buf_offset; hc->headers_len = i - hc->rx_buf_offset + 2; - HTTP_DBG (0, "headers length: %u", hc->headers_len); - HTTP_DBG (0, "headers offset: %u", hc->headers_offset); + hc->control_data_len += (hc->headers_len + 2); + HTTP_DBG (2, "headers length: %u", hc->headers_len); + HTTP_DBG (2, "headers offset: %u", hc->headers_offset); return 0; } @@ -692,15 +921,20 @@ http_identify_headers (http_conn_t *hc, http_status_code_t *ec) static int http_identify_message_body (http_conn_t *hc, http_status_code_t *ec) { - unformat_input_t input; - int i, len; - u8 *line; + int i, value_len; + u8 *end, *p, *value_start; + u64 body_len = 0, digit; hc->body_len = 0; if (hc->headers_len == 0) { - HTTP_DBG (0, "no header, no message-body"); + HTTP_DBG (2, "no header, no message-body"); + return 0; + } + if (hc->is_tunnel) + { + HTTP_DBG (2, "tunnel, no message-body"); return 0; } @@ -711,7 +945,7 @@ http_identify_message_body (http_conn_t *hc, http_status_code_t *ec) "Content-Length:"); if (i < 0) { - HTTP_DBG (0, "Content-Length header not present, no message-body"); + HTTP_DBG (2, "Content-Length header not present, no message-body"); return 0; } hc->rx_buf_offset = i + 15; @@ -723,89 +957,91 @@ http_identify_message_body (http_conn_t *hc, http_status_code_t *ec) *ec = HTTP_STATUS_BAD_REQUEST; return -1; } - len = i - hc->rx_buf_offset; - if (len < 1) + value_len = i - hc->rx_buf_offset; + if (value_len < 1) { clib_warning ("invalid header, content length value missing"); *ec = HTTP_STATUS_BAD_REQUEST; return -1; } - line = vec_new (u8, len); - clib_memcpy (line, hc->rx_buf + hc->rx_buf_offset, len); - HTTP_DBG (0, "%v", line); - - unformat_init_vector (&input, line); - if (!unformat (&input, "%lu", &hc->body_len)) + end = hc->rx_buf + hc->rx_buf_offset + value_len; + p = hc->rx_buf + hc->rx_buf_offset; + /* skip leading whitespace */ + while (1) { - clib_warning ("failed to unformat content length value"); - *ec = HTTP_STATUS_BAD_REQUEST; - return -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--; } - unformat_free (&input); - - hc->body_offset = hc->headers_offset + hc->headers_len + 2; - HTTP_DBG (0, "body length: %u", hc->body_len); - HTTP_DBG (0, "body offset: %u", hc->body_offset); - - return 0; -} - -static int -http_parse_header (http_conn_t *hc, int *content_length) -{ - unformat_input_t input; - int i, len; - u8 *line; - - i = v_find_index (hc->rx_buf, hc->rx_buf_offset, 0, CONTENT_LEN_STR); - if (i < 0) + value_start = p; + /* skip trailing whitespace */ + p = value_start + value_len - 1; + while (*p == ' ' || *p == '\t') { - clib_warning ("cannot find '%s' in the header!", CONTENT_LEN_STR); - return -1; + p--; + value_len--; } - hc->rx_buf_offset = i; - - i = v_find_index (hc->rx_buf, hc->rx_buf_offset, 0, "\n"); - if (i < 0) + if (value_len < 1) { - clib_warning ("end of line missing; incomplete data"); + clib_warning ("value not found"); + *ec = HTTP_STATUS_BAD_REQUEST; return -1; } - len = i - hc->rx_buf_offset; - line = vec_new (u8, len); - clib_memcpy (line, hc->rx_buf + hc->rx_buf_offset, len); - - unformat_init_vector (&input, line); - if (!unformat (&input, CONTENT_LEN_STR "%d", content_length)) + p = value_start; + for (i = 0; i < value_len; i++) { - clib_warning ("failed to unformat content length!"); - return -1; + /* check for digit */ + if (!isdigit (*p)) + { + clib_warning ("expected digit"); + *ec = HTTP_STATUS_BAD_REQUEST; + return -1; + } + digit = *p - '0'; + u64 new_body_len = body_len * 10 + digit; + /* check for overflow */ + if (new_body_len < body_len) + { + clib_warning ("too big number, overflow"); + *ec = HTTP_STATUS_BAD_REQUEST; + return -1; + } + body_len = new_body_len; + p++; } - unformat_free (&input); - /* skip rest of the header */ - hc->rx_buf_offset += len; - i = v_find_index (hc->rx_buf, hc->rx_buf_offset, 0, "<html>"); - if (i < 0) - { - clib_warning ("<html> tag not found"); - return -1; - } - hc->rx_buf_offset = i; + hc->body_len = body_len; + + hc->body_offset = hc->headers_offset + hc->headers_len + 2; + HTTP_DBG (2, "body length: %llu", hc->body_len); + HTTP_DBG (2, "body offset: %u", hc->body_offset); return 0; } static http_sm_result_t -http_state_wait_server_reply (http_conn_t *hc, transport_send_params_t *sp) +http_req_state_wait_transport_reply (http_conn_t *hc, + transport_send_params_t *sp) { - int i, rv, content_length; + int rv; http_msg_t msg = {}; app_worker_t *app_wrk; session_t *as; + u32 len, max_enq, body_sent; + http_status_code_t ec; + http_main_t *hm = &http_main; rv = http_read_message (hc); @@ -816,72 +1052,75 @@ http_state_wait_server_reply (http_conn_t *hc, transport_send_params_t *sp) return HTTP_SM_STOP; } + HTTP_DBG (3, "%v", hc->rx_buf); + if (vec_len (hc->rx_buf) < 8) { clib_warning ("response buffer too short"); goto error; } - if ((i = v_find_index (hc->rx_buf, 0, 0, "200 OK")) >= 0) + rv = http_parse_status_line (hc); + if (rv) + goto error; + + rv = http_identify_headers (hc, &ec); + if (rv) + goto error; + + rv = http_identify_message_body (hc, &ec); + if (rv) + goto error; + + /* send at least "control data" which is necessary minimum, + * if there is some space send also portion of body */ + as = session_get_from_handle (hc->h_pa_session_handle); + max_enq = svm_fifo_max_enqueue (as->rx_fifo); + max_enq -= sizeof (msg); + if (max_enq < hc->control_data_len) { - msg.type = HTTP_MSG_REPLY; - msg.content_type = HTTP_CONTENT_TEXT_HTML; - msg.code = HTTP_STATUS_OK; - msg.data.type = HTTP_MSG_DATA_INLINE; - msg.data.len = 0; + clib_warning ("not enough room for control data in app's rx fifo"); + goto error; + } + len = clib_min (max_enq, vec_len (hc->rx_buf)); - rv = http_parse_header (hc, &content_length); - if (rv) - { - clib_warning ("failed to parse http reply"); - goto error; - } - msg.data.len = content_length; - u32 dlen = vec_len (hc->rx_buf) - hc->rx_buf_offset; - as = session_get_from_handle (hc->h_pa_session_handle); - svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) }, - { &hc->rx_buf[hc->rx_buf_offset], dlen } }; - - rv = svm_fifo_enqueue_segments (as->rx_fifo, segs, 2, - 0 /* allow partial */); - if (rv < 0) - { - clib_warning ("error enqueue"); - return HTTP_SM_ERROR; - } + msg.type = HTTP_MSG_REPLY; + msg.code = hm->sc_by_u16[hc->status_code]; + msg.data.headers_offset = hc->headers_offset; + msg.data.headers_len = hc->headers_len; + msg.data.body_offset = hc->body_offset; + msg.data.body_len = hc->body_len; + msg.data.type = HTTP_MSG_DATA_INLINE; + msg.data.len = len; - hc->rx_buf_offset += dlen; - hc->to_recv = content_length - dlen; + svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) }, + { hc->rx_buf, len } }; - if (hc->rx_buf_offset == vec_len (hc->rx_buf)) - { - vec_reset_length (hc->rx_buf); - hc->rx_buf_offset = 0; - } + rv = svm_fifo_enqueue_segments (as->rx_fifo, segs, 2, 0 /* allow partial */); + ASSERT (rv == (sizeof (msg) + len)); - if (hc->to_recv == 0) - { - hc->rx_buf_offset = 0; - vec_reset_length (hc->rx_buf); - http_state_change (hc, HTTP_STATE_WAIT_APP_METHOD); - } - else - { - http_state_change (hc, HTTP_STATE_CLIENT_IO_MORE_DATA); - } + http_read_message_drop (hc, len); - app_wrk = app_worker_get_if_valid (as->app_wrk_index); - if (app_wrk) - app_worker_rx_notify (app_wrk, as); - return HTTP_SM_STOP; + body_sent = len - hc->control_data_len; + hc->to_recv = hc->body_len - body_sent; + if (hc->to_recv == 0) + { + /* all sent, we are done */ + http_req_state_change (hc, HTTP_REQ_STATE_WAIT_APP_METHOD); } else { - clib_warning ("Unknown http method %v", hc->rx_buf); - goto error; + /* stream rest of the response body */ + http_req_state_change (hc, HTTP_REQ_STATE_TRANSPORT_IO_MORE_DATA); } + app_wrk = app_worker_get_if_valid (as->app_wrk_index); + if (app_wrk) + app_worker_rx_notify (app_wrk, as); + return HTTP_SM_STOP; + error: + http_read_message_drop_all (hc); session_transport_closing_notify (&hc->connection); session_transport_closed_notify (&hc->connection); http_disconnect_transport (hc); @@ -889,14 +1128,16 @@ error: } static http_sm_result_t -http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp) +http_req_state_wait_transport_method (http_conn_t *hc, + transport_send_params_t *sp) { http_status_code_t ec; app_worker_t *app_wrk; http_msg_t msg; session_t *as; int rv; - u32 len; + u32 len, max_enq, body_sent; + u64 max_deq; rv = http_read_message (hc); @@ -904,7 +1145,7 @@ http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp) if (rv) return HTTP_SM_STOP; - HTTP_DBG (0, "%v", hc->rx_buf); + HTTP_DBG (3, "%v", hc->rx_buf); if (vec_len (hc->rx_buf) < 8) { @@ -924,11 +1165,23 @@ http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp) if (rv) goto error; - len = vec_len (hc->rx_buf); + /* send at least "control data" which is necessary minimum, + * if there is some space send also portion of body */ + as = session_get_from_handle (hc->h_pa_session_handle); + max_enq = svm_fifo_max_enqueue (as->rx_fifo); + if (max_enq < hc->control_data_len) + { + clib_warning ("not enough room for control data in app's rx fifo"); + ec = HTTP_STATUS_INTERNAL_ERROR; + goto error; + } + /* do not dequeue more than one HTTP request, we do not support pipelining */ + max_deq = + clib_min (hc->control_data_len + hc->body_len, vec_len (hc->rx_buf)); + len = clib_min (max_enq, max_deq); msg.type = HTTP_MSG_REQUEST; msg.method_type = hc->method; - msg.content_type = HTTP_CONTENT_TEXT_HTML; msg.data.type = HTTP_MSG_DATA_INLINE; msg.data.len = len; msg.data.target_form = hc->target_form; @@ -944,19 +1197,24 @@ http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp) svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) }, { hc->rx_buf, len } }; - as = session_get_from_handle (hc->h_pa_session_handle); rv = svm_fifo_enqueue_segments (as->rx_fifo, segs, 2, 0 /* allow partial */); - if (rv < 0 || rv != sizeof (msg) + len) + ASSERT (rv == (sizeof (msg) + len)); + + body_sent = len - hc->control_data_len; + hc->to_recv = hc->body_len - body_sent; + if (hc->to_recv == 0) { - clib_warning ("failed app enqueue"); - /* This should not happen as we only handle 1 request per session, - * and fifo is allocated, but going forward we should consider - * rescheduling */ - return HTTP_SM_ERROR; + /* drop everything, we do not support pipelining */ + http_read_message_drop_all (hc); + /* all sent, we are done */ + http_req_state_change (hc, HTTP_REQ_STATE_WAIT_APP_REPLY); + } + else + { + http_read_message_drop (hc, len); + /* stream rest of the response body */ + http_req_state_change (hc, HTTP_REQ_STATE_TRANSPORT_IO_MORE_DATA); } - - vec_free (hc->rx_buf); - http_state_change (hc, HTTP_STATE_WAIT_APP_REPLY); app_wrk = app_worker_get_if_valid (as->app_wrk_index); if (app_wrk) @@ -965,7 +1223,7 @@ http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp) return HTTP_SM_STOP; error: - + http_read_message_drop_all (hc); http_send_error (hc, ec); session_transport_closing_notify (&hc->connection); http_disconnect_transport (hc); @@ -974,16 +1232,18 @@ error: } static http_sm_result_t -http_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp) +http_req_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp) { http_main_t *hm = &http_main; - u8 *header; - u32 offset; + u8 *response; + u32 sent; f64 now; session_t *as; http_status_code_t sc; http_msg_t msg; int rv; + http_sm_result_t sm_result = HTTP_SM_ERROR; + http_req_state_t next_state = HTTP_REQ_STATE_WAIT_TRANSPORT_METHOD; as = session_get_from_handle (hc->h_pa_session_handle); @@ -1004,84 +1264,114 @@ http_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp) goto error; } - http_buffer_init (&hc->tx_buf, msg_to_buf_type[msg.data.type], as->tx_fifo, - msg.data.len); + if (msg.code >= HTTP_N_STATUS) + { + clib_warning ("unsupported status code: %d", msg.code); + return HTTP_SM_ERROR; + } /* - * Add headers. For now: + * Add "protocol layer" headers: * - current time - * - expiration time * - server name - * - content type * - data length */ now = clib_timebase_now (&hm->timebase); + response = format (0, http_response_template, http_status_code_str[msg.code], + /* Date */ + format_clib_timebase_time, now, + /* 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->is_tunnel && http_status_code_str[msg.code][0] == '2') + { + ASSERT (msg.data.body_len == 0); + next_state = HTTP_REQ_STATE_TUNNEL; + /* cleanup some stuff we don't need anymore in tunnel mode */ + http_conn_timer_stop (hc); + vec_free (hc->rx_buf); + http_buffer_free (&hc->tx_buf); + } + else + response = format (response, content_len_template, msg.data.body_len); - switch (msg.code) - { - case HTTP_STATUS_NOT_FOUND: - case HTTP_STATUS_METHOD_NOT_ALLOWED: - case HTTP_STATUS_BAD_REQUEST: - case HTTP_STATUS_INTERNAL_ERROR: - case HTTP_STATUS_FORBIDDEN: - case HTTP_STATUS_OK: - header = - format (0, http_response_template, http_status_code_str[msg.code], - /* Date */ - format_clib_timebase_time, now, - /* Expires */ - format_clib_timebase_time, now + 600.0, - /* Server */ - hc->app_name, - /* Content type */ - http_content_type_str[msg.content_type], - /* Length */ - msg.data.len); - break; - case HTTP_STATUS_MOVED: - header = - format (0, http_redirect_template, http_status_code_str[msg.code]); - /* Location: http(s)://new-place already queued up as data */ - break; - default: - clib_warning ("unsupported status code: %d", msg.code); - return HTTP_SM_ERROR; + /* Add headers from app (if any) */ + if (msg.data.headers_len) + { + HTTP_DBG (0, "got headers from app, len %d", msg.data.headers_len); + if (msg.data.type == HTTP_MSG_DATA_PTR) + { + uword app_headers_ptr; + rv = svm_fifo_dequeue (as->tx_fifo, sizeof (app_headers_ptr), + (u8 *) &app_headers_ptr); + ASSERT (rv == sizeof (app_headers_ptr)); + vec_append (response, uword_to_pointer (app_headers_ptr, u8 *)); + } + else + { + u32 orig_len = vec_len (response); + vec_resize (response, msg.data.headers_len); + u8 *p = response + orig_len; + rv = svm_fifo_dequeue (as->tx_fifo, msg.data.headers_len, p); + ASSERT (rv == msg.data.headers_len); + } + } + else + { + /* No headers from app */ + response = format (response, "\r\n"); } + HTTP_DBG (3, "%v", response); - offset = http_send_data (hc, header, vec_len (header), 0); - if (offset != vec_len (header)) + sent = http_send_data (hc, response, vec_len (response)); + if (sent != vec_len (response)) { - clib_warning ("couldn't send response header!"); + clib_warning ("sending status-line and headers failed!"); sc = HTTP_STATUS_INTERNAL_ERROR; - vec_free (header); + vec_free (response); goto error; } - vec_free (header); + vec_free (response); - /* Start sending the actual data */ - http_state_change (hc, HTTP_STATE_APP_IO_MORE_DATA); + if (msg.data.body_len) + { + /* Start sending the actual data */ + http_buffer_init (&hc->tx_buf, msg_to_buf_type[msg.data.type], + as->tx_fifo, msg.data.body_len); + next_state = HTTP_REQ_STATE_APP_IO_MORE_DATA; + sm_result = HTTP_SM_CONTINUE; + } + else + { + /* No response body, we are done */ + sm_result = HTTP_SM_STOP; + } + + http_req_state_change (hc, next_state); - ASSERT (sp->max_burst_size >= offset); - sp->max_burst_size -= offset; - return HTTP_SM_CONTINUE; + ASSERT (sp->max_burst_size >= sent); + sp->max_burst_size -= sent; + return sm_result; error: - clib_warning ("unexpected msg type from app %u", msg.type); http_send_error (hc, sc); - http_state_change (hc, HTTP_STATE_WAIT_CLIENT_METHOD); session_transport_closing_notify (&hc->connection); http_disconnect_transport (hc); return HTTP_SM_STOP; } static http_sm_result_t -http_state_wait_app_method (http_conn_t *hc, transport_send_params_t *sp) +http_req_state_wait_app_method (http_conn_t *hc, transport_send_params_t *sp) { http_msg_t msg; session_t *as; - u8 *buf = 0, *request; - u32 offset; + u8 *target_buff = 0, *request = 0, *target; + u32 sent; int rv; + http_sm_result_t sm_result = HTTP_SM_ERROR; + http_req_state_t next_state; as = session_get_from_handle (hc->h_pa_session_handle); @@ -1100,42 +1390,136 @@ http_state_wait_app_method (http_conn_t *hc, transport_send_params_t *sp) goto error; } - /* currently we support only GET method */ - if (msg.method_type != HTTP_REQ_GET) + /* read request target */ + if (msg.data.type == HTTP_MSG_DATA_PTR) + { + uword target_ptr; + rv = svm_fifo_dequeue (as->tx_fifo, sizeof (target_ptr), + (u8 *) &target_ptr); + ASSERT (rv == sizeof (target_ptr)); + target = uword_to_pointer (target_ptr, u8 *); + } + else + { + vec_validate (target_buff, msg.data.target_path_len - 1); + rv = + svm_fifo_dequeue (as->tx_fifo, msg.data.target_path_len, target_buff); + ASSERT (rv == msg.data.target_path_len); + target = target_buff; + } + + /* currently we support only GET and POST method */ + if (msg.method_type == HTTP_REQ_GET) + { + if (msg.data.body_len) + { + clib_warning ("GET request shouldn't include data"); + goto error; + } + /* + * Add "protocol layer" headers: + * - host + * - user agent + */ + request = format (0, http_get_request_template, + /* target */ + target, + /* Host */ + hc->host, + /* User-Agent */ + hc->app_name, + /* Any headers from app? */ + msg.data.headers_len ? "" : "\r\n"); + + next_state = HTTP_REQ_STATE_WAIT_TRANSPORT_REPLY; + sm_result = HTTP_SM_STOP; + } + else if (msg.method_type == HTTP_REQ_POST) + { + if (!msg.data.body_len) + { + clib_warning ("POST request should include data"); + goto error; + } + /* + * Add "protocol layer" headers: + * - host + * - user agent + * - content length + */ + request = format (0, http_post_request_template, + /* target */ + target, + /* Host */ + hc->host, + /* User-Agent */ + hc->app_name, + /* Content-Length */ + msg.data.body_len, + /* Any headers from app? */ + msg.data.headers_len ? "" : "\r\n"); + + http_buffer_init (&hc->tx_buf, msg_to_buf_type[msg.data.type], + as->tx_fifo, msg.data.body_len); + + next_state = HTTP_REQ_STATE_APP_IO_MORE_DATA; + sm_result = HTTP_SM_CONTINUE; + } + else { clib_warning ("unsupported method %d", msg.method_type); goto error; } - vec_validate (buf, msg.data.len - 1); - rv = svm_fifo_dequeue (as->tx_fifo, msg.data.len, buf); - ASSERT (rv == msg.data.len); + /* Add headers from app (if any) */ + if (msg.data.headers_len) + { + HTTP_DBG (0, "got headers from app, len %d", msg.data.headers_len); + if (msg.data.type == HTTP_MSG_DATA_PTR) + { + uword app_headers_ptr; + rv = svm_fifo_dequeue (as->tx_fifo, sizeof (app_headers_ptr), + (u8 *) &app_headers_ptr); + ASSERT (rv == sizeof (app_headers_ptr)); + vec_append (request, uword_to_pointer (app_headers_ptr, u8 *)); + } + else + { + u32 orig_len = vec_len (request); + vec_resize (request, msg.data.headers_len); + u8 *p = request + orig_len; + rv = svm_fifo_dequeue (as->tx_fifo, msg.data.headers_len, p); + ASSERT (rv == msg.data.headers_len); + } + } + HTTP_DBG (3, "%v", request); - request = format (0, http_request_template, buf, hc->app_name); - offset = http_send_data (hc, request, vec_len (request), 0); - if (offset != vec_len (request)) + sent = http_send_data (hc, request, vec_len (request)); + if (sent != vec_len (request)) { - clib_warning ("sending request failed!"); + clib_warning ("sending request-line and headers failed!"); + sm_result = HTTP_SM_ERROR; goto error; } - http_state_change (hc, HTTP_STATE_WAIT_SERVER_REPLY); - - vec_free (buf); - vec_free (request); - - return HTTP_SM_STOP; + http_req_state_change (hc, next_state); + goto done; error: svm_fifo_dequeue_drop_all (as->tx_fifo); session_transport_closing_notify (&hc->connection); session_transport_closed_notify (&hc->connection); http_disconnect_transport (hc); - return HTTP_SM_ERROR; + +done: + vec_free (target_buff); + vec_free (request); + return sm_result; } static http_sm_result_t -http_state_client_io_more_data (http_conn_t *hc, transport_send_params_t *sp) +http_req_state_transport_io_more_data (http_conn_t *hc, + transport_send_params_t *sp) { session_t *as, *ts; app_worker_t *app_wrk; @@ -1182,18 +1566,18 @@ http_state_client_io_more_data (http_conn_t *hc, transport_send_params_t *sp) clib_warning ("http protocol error: received more data than expected"); session_transport_closing_notify (&hc->connection); http_disconnect_transport (hc); - http_state_change (hc, HTTP_STATE_WAIT_APP_METHOD); + http_req_state_change (hc, HTTP_REQ_STATE_WAIT_APP_METHOD); return HTTP_SM_ERROR; } hc->to_recv -= rv; - HTTP_DBG (1, "drained %d from ts; remains %d", rv, hc->to_recv); + HTTP_DBG (1, "drained %d from ts; remains %lu", rv, hc->to_recv); + /* Finished transaction: + * server back to HTTP_REQ_STATE_WAIT_APP_REPLY + * client to HTTP_REQ_STATE_WAIT_APP_METHOD */ if (hc->to_recv == 0) - { - hc->rx_buf_offset = 0; - vec_reset_length (hc->rx_buf); - http_state_change (hc, HTTP_STATE_WAIT_APP_METHOD); - } + http_req_state_change (hc, hc->is_server ? HTTP_REQ_STATE_WAIT_APP_REPLY : + HTTP_REQ_STATE_WAIT_APP_METHOD); app_wrk = app_worker_get_if_valid (as->app_wrk_index); if (app_wrk) @@ -1206,7 +1590,7 @@ http_state_client_io_more_data (http_conn_t *hc, transport_send_params_t *sp) } static http_sm_result_t -http_state_app_io_more_data (http_conn_t *hc, transport_send_params_t *sp) +http_req_state_app_io_more_data (http_conn_t *hc, transport_send_params_t *sp) { u32 max_send = 64 << 10, n_segs; http_buffer_t *hb = &hc->tx_buf; @@ -1231,7 +1615,7 @@ http_state_app_io_more_data (http_conn_t *hc, transport_send_params_t *sp) if (!http_buffer_is_drained (hb)) { if (sent && svm_fifo_set_event (ts->tx_fifo)) - session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX); + session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX); if (svm_fifo_max_enqueue (ts->tx_fifo) < HTTP_FIFO_THRESH) { @@ -1245,37 +1629,160 @@ http_state_app_io_more_data (http_conn_t *hc, transport_send_params_t *sp) else { if (sent && svm_fifo_set_event (ts->tx_fifo)) - session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX_FLUSH); - - /* Finished transaction, back to HTTP_STATE_WAIT_METHOD */ - http_state_change (hc, HTTP_STATE_WAIT_CLIENT_METHOD); + session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX_FLUSH); + + /* Finished transaction: + * server back to HTTP_REQ_STATE_WAIT_TRANSPORT_METHOD + * client to HTTP_REQ_STATE_WAIT_TRANSPORT_REPLY */ + http_req_state_change (hc, hc->is_server ? + HTTP_REQ_STATE_WAIT_TRANSPORT_METHOD : + HTTP_REQ_STATE_WAIT_TRANSPORT_REPLY); http_buffer_free (&hc->tx_buf); } return HTTP_SM_STOP; } +static http_sm_result_t +http_req_state_tunnel_rx (http_conn_t *hc, transport_send_params_t *sp) +{ + u32 max_deq, max_enq, max_read, n_segs = 2; + svm_fifo_seg_t segs[n_segs]; + int n_written = 0; + session_t *as, *ts; + app_worker_t *app_wrk; + + HTTP_DBG (1, "tunnel received data from client"); + + as = session_get_from_handle (hc->h_pa_session_handle); + ts = session_get_from_handle (hc->h_tc_session_handle); + + max_deq = svm_fifo_max_dequeue (ts->rx_fifo); + if (PREDICT_FALSE (max_deq == 0)) + { + HTTP_DBG (1, "max_deq == 0"); + return HTTP_SM_STOP; + } + max_enq = svm_fifo_max_enqueue (as->rx_fifo); + if (max_enq == 0) + { + HTTP_DBG (1, "app's rx fifo full"); + svm_fifo_add_want_deq_ntf (as->rx_fifo, SVM_FIFO_WANT_DEQ_NOTIF); + return HTTP_SM_STOP; + } + max_read = clib_min (max_enq, max_deq); + svm_fifo_segments (ts->rx_fifo, 0, segs, &n_segs, max_read); + n_written = svm_fifo_enqueue_segments (as->rx_fifo, segs, n_segs, 0); + ASSERT (n_written > 0); + HTTP_DBG (1, "transfered %u bytes", n_written); + svm_fifo_dequeue_drop (ts->rx_fifo, n_written); + app_wrk = app_worker_get_if_valid (as->app_wrk_index); + if (app_wrk) + app_worker_rx_notify (app_wrk, as); + if (svm_fifo_max_dequeue_cons (ts->rx_fifo)) + session_program_rx_io_evt (session_handle (ts)); + + return HTTP_SM_STOP; +} + +static http_sm_result_t +http_req_state_tunnel_tx (http_conn_t *hc, transport_send_params_t *sp) +{ + u32 max_deq, max_enq, max_read, n_segs = 2; + svm_fifo_seg_t segs[n_segs]; + session_t *as, *ts; + int n_written = 0; + + HTTP_DBG (1, "tunnel received data from target"); + + as = session_get_from_handle (hc->h_pa_session_handle); + ts = session_get_from_handle (hc->h_tc_session_handle); + + max_deq = svm_fifo_max_dequeue_cons (as->tx_fifo); + if (PREDICT_FALSE (max_deq == 0)) + { + HTTP_DBG (1, "max_deq == 0"); + goto check_fifo; + } + max_enq = svm_fifo_max_enqueue_prod (ts->tx_fifo); + if (max_enq == 0) + { + HTTP_DBG (1, "ts tx fifo full"); + goto check_fifo; + } + max_read = clib_min (max_enq, max_deq); + max_read = clib_min (max_read, sp->max_burst_size); + svm_fifo_segments (as->tx_fifo, 0, segs, &n_segs, max_read); + n_written = svm_fifo_enqueue_segments (ts->tx_fifo, segs, n_segs, 0); + ASSERT (n_written > 0); + HTTP_DBG (1, "transfered %u bytes", n_written); + sp->bytes_dequeued += n_written; + sp->max_burst_size -= n_written; + svm_fifo_dequeue_drop (as->tx_fifo, n_written); + if (svm_fifo_set_event (ts->tx_fifo)) + session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX); + +check_fifo: + /* Deschedule and wait for deq notification if ts fifo is almost full */ + if (svm_fifo_max_enqueue (ts->tx_fifo) < HTTP_FIFO_THRESH) + { + svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF); + transport_connection_deschedule (&hc->connection); + sp->flags |= TRANSPORT_SND_F_DESCHED; + } + + return HTTP_SM_STOP; +} + typedef http_sm_result_t (*http_sm_handler) (http_conn_t *, transport_send_params_t *sp); -static http_sm_handler state_funcs[HTTP_N_STATES] = { - 0, /* idle state */ - http_state_wait_app_method, - http_state_wait_client_method, - http_state_wait_server_reply, - http_state_wait_app_reply, - http_state_client_io_more_data, - http_state_app_io_more_data, +static http_sm_handler tx_state_funcs[HTTP_REQ_N_STATES] = { + 0, /* idle */ + http_req_state_wait_app_method, + 0, /* wait transport reply */ + 0, /* transport io more data */ + 0, /* wait transport method */ + http_req_state_wait_app_reply, + http_req_state_app_io_more_data, + http_req_state_tunnel_tx, }; -static void -http_req_run_state_machine (http_conn_t *hc, transport_send_params_t *sp) +static_always_inline int +http_req_state_is_tx_valid (http_conn_t *hc) +{ + return tx_state_funcs[hc->req_state] ? 1 : 0; +} + +static http_sm_handler rx_state_funcs[HTTP_REQ_N_STATES] = { + 0, /* idle */ + 0, /* wait app method */ + http_req_state_wait_transport_reply, + http_req_state_transport_io_more_data, + http_req_state_wait_transport_method, + 0, /* wait app reply */ + 0, /* app io more data */ + http_req_state_tunnel_rx, +}; + +static_always_inline int +http_req_state_is_rx_valid (http_conn_t *hc) +{ + return rx_state_funcs[hc->req_state] ? 1 : 0; +} + +static_always_inline void +http_req_run_state_machine (http_conn_t *hc, transport_send_params_t *sp, + u8 is_tx) { http_sm_result_t res; do { - res = state_funcs[hc->http_state](hc, sp); + if (is_tx) + res = tx_state_funcs[hc->req_state](hc, sp); + else + res = rx_state_funcs[hc->req_state](hc, sp); if (res == HTTP_SM_ERROR) { HTTP_DBG (1, "error in state machine %d", res); @@ -1293,20 +1800,29 @@ http_ts_rx_callback (session_t *ts) { http_conn_t *hc; + HTTP_DBG (1, "hc [%u]%x", ts->thread_index, ts->opaque); + hc = http_conn_get_w_thread (ts->opaque, ts->thread_index); - if (!hc) + + if (hc->state == HTTP_CONN_STATE_CLOSED) { - clib_warning ("http connection not found (ts %d)", ts->opaque); - return -1; + HTTP_DBG (1, "conn closed"); + svm_fifo_dequeue_drop_all (ts->rx_fifo); + return 0; } - if (hc->state == HTTP_CONN_STATE_CLOSED) + if (!http_req_state_is_rx_valid (hc)) { - svm_fifo_dequeue_drop_all (ts->tx_fifo); + clib_warning ("hc [%u]%x invalid rx state: http req state " + "'%U', session state '%U'", + ts->thread_index, ts->opaque, format_http_req_state, + hc->req_state, format_http_conn_state, hc); + svm_fifo_dequeue_drop_all (ts->rx_fifo); return 0; } - http_req_run_state_machine (hc, 0); + HTTP_DBG (1, "run state machine"); + http_req_run_state_machine (hc, 0, 0); if (hc->state == HTTP_CONN_STATE_TRANSPORT_CLOSED) { @@ -1322,6 +1838,7 @@ http_ts_builtin_tx_callback (session_t *ts) http_conn_t *hc; hc = http_conn_get_w_thread (ts->opaque, ts->thread_index); + HTTP_DBG (1, "transport connection reschedule"); transport_connection_reschedule (&hc->connection); return 0; @@ -1336,21 +1853,36 @@ http_ts_cleanup_callback (session_t *ts, session_cleanup_ntf_t ntf) return; hc = http_conn_get_w_thread (ts->opaque, ts->thread_index); - if (!hc) - { - clib_warning ("no http connection for %u", ts->session_index); - return; - } + + HTTP_DBG (1, "going to free hc [%u]%x", ts->thread_index, ts->opaque); vec_free (hc->rx_buf); http_buffer_free (&hc->tx_buf); - http_conn_timer_stop (hc); + + if (hc->pending_timer == 0) + http_conn_timer_stop (hc); session_transport_delete_notify (&hc->connection); + + if (!hc->is_server) + { + vec_free (hc->app_name); + vec_free (hc->host); + } http_conn_free (hc); } +static void +http_ts_ho_cleanup_callback (session_t *ts) +{ + http_conn_t *ho_hc; + HTTP_DBG (1, "half open: %x", ts->opaque); + ho_hc = http_ho_conn_get (ts->opaque); + session_half_open_delete_notify (&ho_hc->connection); + http_ho_conn_free (ho_hc); +} + int http_add_segment_callback (u32 client_index, u64 segment_handle) { @@ -1370,6 +1902,7 @@ static session_cb_vft_t http_app_cb_vft = { .session_connected_callback = http_ts_connected_callback, .session_reset_callback = http_ts_reset_callback, .session_cleanup_callback = http_ts_cleanup_callback, + .half_open_cleanup_callback = http_ts_ho_cleanup_callback, .add_segment_callback = http_add_segment_callback, .del_segment_callback = http_del_segment_callback, .builtin_app_rx_callback = http_ts_rx_callback, @@ -1392,8 +1925,6 @@ http_transport_enable (vlib_main_t *vm, u8 is_en) return 0; } - vec_validate (hm->wrk, vlib_num_workers ()); - clib_memset (a, 0, sizeof (*a)); clib_memset (options, 0, sizeof (options)); @@ -1415,10 +1946,16 @@ http_transport_enable (vlib_main_t *vm, u8 is_en) hm->app_index = a->app_index; vec_free (a->name); + if (hm->is_init) + return 0; + + vec_validate (hm->wrk, vlib_num_workers ()); + clib_timebase_init (&hm->timebase, 0 /* GMT */, CLIB_TIMEBASE_DAYLIGHT_NONE, &vm->clib_time /* share the system clock */); - http_timers_init (vm, http_conn_timeout_cb); + http_timers_init (vm, http_conn_timeout_cb, http_conn_invalidate_timer_cb); + hm->is_init = 1; return 0; } @@ -1433,6 +1970,8 @@ http_transport_connect (transport_endpoint_cfg_t *tep) http_conn_t *hc; int error; u32 hc_index; + session_t *ho; + transport_endpt_ext_cfg_t *ext_cfg; app_worker_t *app_wrk = app_worker_get (sep->app_wrk_index); clib_memset (cargs, 0, sizeof (*cargs)); @@ -1442,23 +1981,48 @@ http_transport_connect (transport_endpoint_cfg_t *tep) app = application_get (app_wrk->app_index); cargs->sep_ext.ns_index = app->ns_index; - hc_index = http_conn_alloc_w_thread (0 /* ts->thread_index */); - hc = http_conn_get_w_thread (hc_index, 0); + hc_index = http_ho_conn_alloc (); + hc = http_ho_conn_get (hc_index); hc->h_pa_wrk_index = sep->app_wrk_index; hc->h_pa_app_api_ctx = sep->opaque; hc->state = HTTP_CONN_STATE_CONNECTING; cargs->api_context = hc_index; + ext_cfg = session_endpoint_get_ext_cfg (sep, TRANSPORT_ENDPT_EXT_CFG_HTTP); + if (ext_cfg) + { + HTTP_DBG (1, "app set timeout %u", ext_cfg->opaque); + hc->timeout = ext_cfg->opaque; + } + + hc->is_server = 0; + if (vec_len (app->name)) hc->app_name = vec_dup (app->name); else hc->app_name = format (0, "VPP HTTP client"); + if (sep->is_ip4) + hc->host = format (0, "%U:%d", format_ip4_address, &sep->ip.ip4, + clib_net_to_host_u16 (sep->port)); + else + hc->host = format (0, "%U:%d", format_ip6_address, &sep->ip.ip6, + clib_net_to_host_u16 (sep->port)); + HTTP_DBG (1, "hc ho_index %x", hc_index); if ((error = vnet_connect (cargs))) return error; + ho = session_alloc_for_half_open (&hc->connection); + ho->app_wrk_index = app_wrk->wrk_index; + ho->ho_index = app_worker_add_half_open (app_wrk, session_handle (ho)); + ho->opaque = sep->opaque; + ho->session_type = + session_type_from_proto_and_ip (TRANSPORT_PROTO_HTTP, sep->is_ip4); + hc->h_tc_session_handle = cargs->sh; + hc->c_s_index = ho->session_index; + return 0; } @@ -1470,11 +2034,12 @@ http_start_listen (u32 app_listener_index, transport_endpoint_cfg_t *tep) http_main_t *hm = &http_main; session_endpoint_cfg_t *sep; app_worker_t *app_wrk; - transport_proto_t tp; + transport_proto_t tp = TRANSPORT_PROTO_TCP; app_listener_t *al; application_t *app; http_conn_t *lhc; u32 lhc_index; + transport_endpt_ext_cfg_t *ext_cfg; sep = (session_endpoint_cfg_t *) tep; @@ -1484,7 +2049,13 @@ http_start_listen (u32 app_listener_index, transport_endpoint_cfg_t *tep) args->app_index = hm->app_index; args->sep_ext = *sep; args->sep_ext.ns_index = app->ns_index; - tp = sep->ext_cfg ? TRANSPORT_PROTO_TLS : TRANSPORT_PROTO_TCP; + + ext_cfg = session_endpoint_get_ext_cfg (sep, TRANSPORT_ENDPT_EXT_CFG_CRYPTO); + if (ext_cfg) + { + HTTP_DBG (1, "app set tls"); + tp = TRANSPORT_PROTO_TLS; + } args->sep_ext.transport_proto = tp; if (vnet_listen (args)) @@ -1493,6 +2064,13 @@ http_start_listen (u32 app_listener_index, transport_endpoint_cfg_t *tep) lhc_index = http_listener_alloc (); lhc = http_listener_get (lhc_index); + ext_cfg = session_endpoint_get_ext_cfg (sep, TRANSPORT_ENDPT_EXT_CFG_HTTP); + if (ext_cfg && ext_cfg->opaque) + { + HTTP_DBG (1, "app set timeout %u", ext_cfg->opaque); + lhc->timeout = ext_cfg->opaque; + } + /* Grab transport connection listener and link to http listener */ lhc->h_tc_session_handle = args->handle; al = app_listener_get_w_handle (lhc->h_tc_session_handle); @@ -1506,6 +2084,8 @@ http_start_listen (u32 app_listener_index, transport_endpoint_cfg_t *tep) lhc->c_s_index = app_listener_index; lhc->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP; + lhc->is_server = 1; + if (vec_len (app->name)) lhc->app_name = vec_dup (app->name); else @@ -1542,7 +2122,7 @@ http_transport_close (u32 hc_index, u32 thread_index) session_t *as; http_conn_t *hc; - HTTP_DBG (1, "App disconnecting %x", hc_index); + HTTP_DBG (1, "App disconnecting [%u]%x", thread_index, hc_index); hc = http_conn_get_w_thread (hc_index, thread_index); if (hc->state == HTTP_CONN_STATE_CONNECTING) @@ -1592,22 +2172,26 @@ http_app_tx_callback (void *session, transport_send_params_t *sp) u32 max_burst_sz, sent; http_conn_t *hc; - HTTP_DBG (1, "app session conn index %x", as->connection_index); + HTTP_DBG (1, "hc [%u]%x", as->thread_index, as->connection_index); hc = http_conn_get_w_thread (as->connection_index, as->thread_index); - if (!http_state_is_tx_valid (hc)) + + max_burst_sz = sp->max_burst_size * TRANSPORT_PACER_MIN_MSS; + sp->max_burst_size = max_burst_sz; + + if (!http_req_state_is_tx_valid (hc)) { - if (hc->state != HTTP_CONN_STATE_CLOSED) - clib_warning ("app data req state '%U' session state %u", - format_http_state, hc->http_state, hc->state); + clib_warning ("hc [%u]%x invalid tx state: http req state " + "'%U', session state '%U'", + as->thread_index, as->connection_index, + format_http_req_state, hc->req_state, + format_http_conn_state, hc); svm_fifo_dequeue_drop_all (as->tx_fifo); return 0; } - max_burst_sz = sp->max_burst_size * TRANSPORT_PACER_MIN_MSS; - sp->max_burst_size = max_burst_sz; - - http_req_run_state_machine (hc, sp); + HTTP_DBG (1, "run state machine"); + http_req_run_state_machine (hc, sp, 1); if (hc->state == HTTP_CONN_STATE_APP_CLOSED) { @@ -1620,6 +2204,18 @@ http_app_tx_callback (void *session, transport_send_params_t *sp) return sent > 0 ? clib_max (sent / TRANSPORT_PACER_MIN_MSS, 1) : 0; } +static int +http_app_rx_evt_cb (transport_connection_t *tc) +{ + http_conn_t *hc = (http_conn_t *) tc; + HTTP_DBG (1, "hc [%u]%x", vlib_get_thread_index (), hc->h_hc_index); + + if (hc->req_state == HTTP_REQ_STATE_TUNNEL) + http_req_state_tunnel_rx (hc, 0); + + return 0; +} + static void http_transport_get_endpoint (u32 hc_index, u32 thread_index, transport_endpoint_t *tep, u8 is_lcl) @@ -1662,36 +2258,6 @@ format_http_listener (u8 *s, va_list *args) } static u8 * -format_http_conn_state (u8 *s, va_list *args) -{ - http_conn_t *hc = va_arg (*args, http_conn_t *); - - switch (hc->state) - { - case HTTP_CONN_STATE_LISTEN: - s = format (s, "LISTEN"); - break; - case HTTP_CONN_STATE_CONNECTING: - s = format (s, "CONNECTING"); - break; - case HTTP_CONN_STATE_ESTABLISHED: - s = format (s, "ESTABLISHED"); - break; - case HTTP_CONN_STATE_TRANSPORT_CLOSED: - s = format (s, "TRANSPORT_CLOSED"); - break; - case HTTP_CONN_STATE_APP_CLOSED: - s = format (s, "APP_CLOSED"); - break; - case HTTP_CONN_STATE_CLOSED: - s = format (s, "CLOSED"); - break; - } - - return s; -} - -static u8 * format_http_transport_connection (u8 *s, va_list *args) { u32 tc_index = va_arg (*args, u32); @@ -1728,18 +2294,61 @@ format_http_transport_listener (u8 *s, va_list *args) return s; } +static u8 * +format_http_transport_half_open (u8 *s, va_list *args) +{ + u32 ho_index = va_arg (*args, u32); + u32 __clib_unused thread_index = va_arg (*args, u32); + u32 __clib_unused verbose = va_arg (*args, u32); + http_conn_t *ho_hc; + session_t *tcp_ho; + + ho_hc = http_ho_conn_get (ho_index); + tcp_ho = session_get_from_handle (ho_hc->h_tc_session_handle); + + s = format (s, "[%d:%d][H] half-open app_wrk %u ts %d:%d", + ho_hc->c_thread_index, ho_hc->c_s_index, ho_hc->h_pa_wrk_index, + tcp_ho->thread_index, tcp_ho->session_index); + return s; +} + +static transport_connection_t * +http_transport_get_ho (u32 ho_hc_index) +{ + http_conn_t *ho_hc; + + HTTP_DBG (1, "half open: %x", ho_hc_index); + ho_hc = http_ho_conn_get (ho_hc_index); + return &ho_hc->connection; +} + +static void +http_transport_cleanup_ho (u32 ho_hc_index) +{ + http_conn_t *ho_hc; + + HTTP_DBG (1, "half open: %x", ho_hc_index); + ho_hc = http_ho_conn_get (ho_hc_index); + session_cleanup_half_open (ho_hc->h_tc_session_handle); + http_ho_conn_free (ho_hc); +} + static const transport_proto_vft_t http_proto = { .enable = http_transport_enable, .connect = http_transport_connect, .start_listen = http_start_listen, .stop_listen = http_stop_listen, .close = http_transport_close, + .cleanup_ho = http_transport_cleanup_ho, .custom_tx = http_app_tx_callback, + .app_rx_evt = http_app_rx_evt_cb, .get_connection = http_transport_get_connection, .get_listener = http_transport_get_listener, + .get_half_open = http_transport_get_ho, .get_transport_endpoint = http_transport_get_endpoint, .format_connection = format_http_transport_connection, .format_listener = format_http_transport_listener, + .format_half_open = format_http_transport_half_open, .transport_options = { .name = "http", .short_name = "H", @@ -1752,6 +2361,7 @@ static clib_error_t * http_transport_init (vlib_main_t *vm) { http_main_t *hm = &http_main; + int i; transport_register_protocol (TRANSPORT_PROTO_HTTP, &http_proto, FIB_PROTOCOL_IP4, ~0); @@ -1763,7 +2373,26 @@ http_transport_init (vlib_main_t *vm) hm->first_seg_size = 32 << 20; hm->fifo_size = 512 << 10; - return 0; + /* Setup u16 to http_status_code_t map */ + /* Unrecognized status code is equivalent to the x00 status */ + vec_validate (hm->sc_by_u16, 599); + for (i = 100; i < 200; i++) + hm->sc_by_u16[i] = HTTP_STATUS_CONTINUE; + for (i = 200; i < 300; i++) + hm->sc_by_u16[i] = HTTP_STATUS_OK; + for (i = 300; i < 400; i++) + hm->sc_by_u16[i] = HTTP_STATUS_MULTIPLE_CHOICES; + for (i = 400; i < 500; i++) + hm->sc_by_u16[i] = HTTP_STATUS_BAD_REQUEST; + for (i = 500; i < 600; i++) + hm->sc_by_u16[i] = HTTP_STATUS_INTERNAL_ERROR; + + /* Registered status codes */ +#define _(c, s, str) hm->sc_by_u16[c] = HTTP_STATUS_##s; + foreach_http_status_code +#undef _ + + return 0; } VLIB_INIT_FUNCTION (http_transport_init); diff --git a/src/plugins/http/http.h b/src/plugins/http/http.h index e3ee93b6291..b293c125465 100644 --- a/src/plugins/http/http.h +++ b/src/plugins/http/http.h @@ -51,32 +51,52 @@ typedef struct http_conn_id_ STATIC_ASSERT (sizeof (http_conn_id_t) <= TRANSPORT_CONN_ID_LEN, "ctx id must be less than TRANSPORT_CONN_ID_LEN"); +typedef struct +{ + char *base; + uword len; +} http_token_t; + +#define http_token_lit(s) (s), sizeof (s) - 1 + +#define foreach_http_conn_state \ + _ (LISTEN, "listen") \ + _ (CONNECTING, "connecting") \ + _ (ESTABLISHED, "established") \ + _ (TRANSPORT_CLOSED, "transport-closed") \ + _ (APP_CLOSED, "app-closed") \ + _ (CLOSED, "closed") + typedef enum http_conn_state_ { - HTTP_CONN_STATE_LISTEN, - HTTP_CONN_STATE_CONNECTING, - HTTP_CONN_STATE_ESTABLISHED, - HTTP_CONN_STATE_TRANSPORT_CLOSED, - HTTP_CONN_STATE_APP_CLOSED, - HTTP_CONN_STATE_CLOSED +#define _(s, str) HTTP_CONN_STATE_##s, + foreach_http_conn_state +#undef _ } http_conn_state_t; -typedef enum http_state_ +#define foreach_http_req_state \ + _ (0, IDLE, "idle") \ + _ (1, WAIT_APP_METHOD, "wait app method") \ + _ (2, WAIT_TRANSPORT_REPLY, "wait transport reply") \ + _ (3, TRANSPORT_IO_MORE_DATA, "transport io more data") \ + _ (4, WAIT_TRANSPORT_METHOD, "wait transport method") \ + _ (5, WAIT_APP_REPLY, "wait app reply") \ + _ (6, APP_IO_MORE_DATA, "app io more data") \ + _ (7, TUNNEL, "tunnel") + +typedef enum http_req_state_ { - HTTP_STATE_IDLE = 0, - HTTP_STATE_WAIT_APP_METHOD, - HTTP_STATE_WAIT_CLIENT_METHOD, - HTTP_STATE_WAIT_SERVER_REPLY, - HTTP_STATE_WAIT_APP_REPLY, - HTTP_STATE_CLIENT_IO_MORE_DATA, - HTTP_STATE_APP_IO_MORE_DATA, - HTTP_N_STATES, -} http_state_t; +#define _(n, s, str) HTTP_REQ_STATE_##s = n, + foreach_http_req_state +#undef _ + HTTP_REQ_N_STATES +} http_req_state_t; typedef enum http_req_method_ { HTTP_REQ_GET = 0, HTTP_REQ_POST, + HTTP_REQ_CONNECT, } http_req_method_t; typedef enum http_msg_type_ @@ -94,85 +114,87 @@ typedef enum http_target_form_ } http_target_form_t; #define foreach_http_content_type \ - _ (APP_7Z, ".7z", "application / x - 7z - compressed") \ - _ (APP_DOC, ".doc", "application / msword") \ + _ (APP_7Z, ".7z", "application/x-7z-compressed") \ + _ (APP_DOC, ".doc", "application/msword") \ _ (APP_DOCX, ".docx", \ - "application / vnd.openxmlformats - " \ + "application/vnd.openxmlformats-" \ "officedocument.wordprocessingml.document") \ - _ (APP_EPUB, ".epub", "application / epub + zip") \ - _ (APP_FONT, ".eot", "application / vnd.ms - fontobject") \ - _ (APP_JAR, ".jar", "application / java - archive") \ - _ (APP_JSON, ".json", "application / json") \ - _ (APP_JSON_LD, ".jsonld", "application / ld + json") \ - _ (APP_MPKG, ".mpkg", "application / vnd.apple.installer + xml") \ - _ (APP_ODP, ".odp", "application / vnd.oasis.opendocument.presentation") \ - _ (APP_ODS, ".ods", "application / vnd.oasis.opendocument.spreadsheet") \ - _ (APP_ODT, ".odt", "application / vnd.oasis.opendocument.text") \ - _ (APP_OGX, ".ogx", "application / ogg") \ - _ (APP_PDF, ".pdf", "application / pdf") \ - _ (APP_PHP, ".php", "application / x - httpd - php") \ - _ (APP_PPT, ".ppt", "application / vnd.ms - powerpoint") \ - _ (APP_PPTX, ".pptx", "application / vnd.ms - powerpoint") \ - _ (APP_RAR, ".rar", "application / vnd.rar") \ - _ (APP_RTF, ".rtf", "application / rtf") \ - _ (APP_SH, ".sh", "application / x - sh") \ - _ (APP_TAR, ".tar", "application / x - tar") \ - _ (APP_VSD, ".vsd", "application / vnd.visio") \ - _ (APP_XHTML, ".xhtml", "application / xhtml + xml") \ - _ (APP_XLS, ".xls", "application / vnd.ms - excel") \ - _ (APP_XML, ".xml", "application / xml") \ + _ (APP_EPUB, ".epub", "application/epub+zip") \ + _ (APP_FONT, ".eot", "application/vnd.ms-fontobject") \ + _ (APP_JAR, ".jar", "application/java-archive") \ + _ (APP_JSON, ".json", "application/json") \ + _ (APP_JSON_LD, ".jsonld", "application/ld+json") \ + _ (APP_MPKG, ".mpkg", "application/vnd.apple.installer+xml") \ + _ (APP_ODP, ".odp", "application/vnd.oasis.opendocument.presentation") \ + _ (APP_ODS, ".ods", "application/vnd.oasis.opendocument.spreadsheet") \ + _ (APP_ODT, ".odt", "application/vnd.oasis.opendocument.text") \ + _ (APP_OGX, ".ogx", "application/ogg") \ + _ (APP_PDF, ".pdf", "application/pdf") \ + _ (APP_PHP, ".php", "application/x-httpd-php") \ + _ (APP_PPT, ".ppt", "application/vnd.ms-powerpoint") \ + _ (APP_PPTX, ".pptx", "application/vnd.ms-powerpoint") \ + _ (APP_RAR, ".rar", "application/vnd.rar") \ + _ (APP_RTF, ".rtf", "application/rtf") \ + _ (APP_SH, ".sh", "application/x-sh") \ + _ (APP_TAR, ".tar", "application/x-tar") \ + _ (APP_VSD, ".vsd", "application/vnd.visio") \ + _ (APP_XHTML, ".xhtml", "application/xhtml+xml") \ + _ (APP_XLS, ".xls", "application/vnd.ms-excel") \ + _ (APP_XML, ".xml", "application/xml") \ _ (APP_XSLX, ".xlsx", \ - "application / vnd.openxmlformats - officedocument.spreadsheetml.sheet") \ - _ (APP_XUL, ".xul", "application / vnd.mozilla.xul + xml") \ - _ (APP_ZIP, ".zip", "application / zip") \ - _ (AUDIO_AAC, ".aac", "audio / aac") \ - _ (AUDIO_CD, ".cda", "application / x - cdf") \ - _ (AUDIO_WAV, ".wav", "audio / wav") \ - _ (AUDIO_WEBA, ".weba", "audio / webm") \ - _ (AUDO_MIDI, ".midi", "audio / midi") \ - _ (AUDO_MID, ".mid", "audo / midi") \ - _ (AUDO_MP3, ".mp3", "audio / mpeg") \ - _ (AUDO_OGA, ".oga", "audio / ogg") \ - _ (AUDO_OPUS, ".opus", "audio / opus") \ - _ (APP_OCTET_STREAM, ".bin", "application / octet - stream") \ - _ (BZIP2, ".bz2", "application / x - bzip2") \ - _ (BZIP, ".bz", "application / x - bzip") \ - _ (FONT_OTF, ".otf", "font / otf") \ - _ (FONT_TTF, ".ttf", "font / ttf") \ - _ (FONT_WOFF2, ".woff2", "font / woff2") \ - _ (FONT_WOFF, ".woff", "font / woff") \ - _ (GZIP, ".gz", "application / gzip") \ - _ (IMAGE_AVIF, ".avif", "image / avif") \ - _ (IMAGE_BMP, ".bmp", "image / bmp") \ - _ (IMAGE_GIF, ".gif", "image / gif") \ - _ (IMAGE_ICON, ".ico", "image / vnd.microsoft.icon") \ - _ (IMAGE_JPEG, ".jpeg", "image / jpeg") \ - _ (IMAGE_JPG, ".jpg", "image / jpeg") \ - _ (IMAGE_PNG, ".png", "image / png") \ - _ (IMAGE_SVG, ".svg", "image / svg + xml") \ - _ (IMAGE_TIFF, ".tiff", "image / tiff") \ - _ (IMAGE_TIF, ".tif", "image / tiff") \ - _ (IMAGE_WEBP, ".webp", "image / webp") \ - _ (SCRIPT_CSH, ".csh", "application / x - csh") \ - _ (TEXT_ABIWORD, ".abw", "application / x - abiword") \ - _ (TEXT_ARCHIVE, ".arc", "application / x - freearc") \ - _ (TEXT_AZW, ".azw", "application / vnd.amazon.ebook") \ - _ (TEXT_CALENDAR, ".ics", "text / calendar") \ - _ (TEXT_CSS, ".css", "text / css") \ - _ (TEXT_CSV, ".csv", "text / csv") \ - _ (TEXT_HTM, ".htm", "text / html") \ - _ (TEXT_HTML, ".html", "text / html") \ - _ (TEXT_JS, ".js", "text / javascript") \ - _ (TEXT_MJS, ".mjs", "text / javascript") \ - _ (TEXT_PLAIN, ".txt", "text / plain") \ - _ (VIDEO_3GP2, ".3g2", "video / 3gpp2") \ - _ (VIDEO_3GP, ".3gp", "video / 3gpp") \ - _ (VIDEO_AVI, ".avi", "video / x - msvideo") \ - _ (VIDEO_MP4, ".mp4", "video / mp4") \ - _ (VIDEO_MPEG, ".mpeg", "video / mpeg") \ - _ (VIDEO_OGG, ".ogv", "video / ogg") \ - _ (VIDEO_TS, ".ts", "video / mp2t") \ - _ (VIDEO_WEBM, ".webm", "video / webm") + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") \ + _ (APP_XUL, ".xul", "application/vnd.mozilla.xul+xml") \ + _ (APP_X_WWW_FORM_URLENCODED, ".invalid", \ + "application/x-www-form-urlencoded") \ + _ (APP_ZIP, ".zip", "application/zip") \ + _ (AUDIO_AAC, ".aac", "audio/aac") \ + _ (AUDIO_CD, ".cda", "application/x-cdf") \ + _ (AUDIO_WAV, ".wav", "audio/wav") \ + _ (AUDIO_WEBA, ".weba", "audio/webm") \ + _ (AUDO_MIDI, ".midi", "audio/midi") \ + _ (AUDO_MID, ".mid", "audo/midi") \ + _ (AUDO_MP3, ".mp3", "audio/mpeg") \ + _ (AUDO_OGA, ".oga", "audio/ogg") \ + _ (AUDO_OPUS, ".opus", "audio/opus") \ + _ (APP_OCTET_STREAM, ".bin", "application/octet-stream") \ + _ (BZIP2, ".bz2", "application/x-bzip2") \ + _ (BZIP, ".bz", "application/x-bzip") \ + _ (FONT_OTF, ".otf", "font/otf") \ + _ (FONT_TTF, ".ttf", "font/ttf") \ + _ (FONT_WOFF2, ".woff2", "font/woff2") \ + _ (FONT_WOFF, ".woff", "font/woff") \ + _ (GZIP, ".gz", "application/gzip") \ + _ (IMAGE_AVIF, ".avif", "image/avif") \ + _ (IMAGE_BMP, ".bmp", "image/bmp") \ + _ (IMAGE_GIF, ".gif", "image/gif") \ + _ (IMAGE_ICON, ".ico", "image/vnd.microsoft.icon") \ + _ (IMAGE_JPEG, ".jpeg", "image/jpeg") \ + _ (IMAGE_JPG, ".jpg", "image/jpeg") \ + _ (IMAGE_PNG, ".png", "image/png") \ + _ (IMAGE_SVG, ".svg", "image/svg+xml") \ + _ (IMAGE_TIFF, ".tiff", "image/tiff") \ + _ (IMAGE_TIF, ".tif", "image/tiff") \ + _ (IMAGE_WEBP, ".webp", "image/webp") \ + _ (SCRIPT_CSH, ".csh", "application/x-csh") \ + _ (TEXT_ABIWORD, ".abw", "application/x-abiword") \ + _ (TEXT_ARCHIVE, ".arc", "application/x-freearc") \ + _ (TEXT_AZW, ".azw", "application/vnd.amazon.ebook") \ + _ (TEXT_CALENDAR, ".ics", "text/calendar") \ + _ (TEXT_CSS, ".css", "text/css") \ + _ (TEXT_CSV, ".csv", "text/csv") \ + _ (TEXT_HTM, ".htm", "text/html") \ + _ (TEXT_HTML, ".html", "text/html") \ + _ (TEXT_JS, ".js", "text/javascript") \ + _ (TEXT_MJS, ".mjs", "text/javascript") \ + _ (TEXT_PLAIN, ".txt", "text/plain") \ + _ (VIDEO_3GP2, ".3g2", "video/3gpp2") \ + _ (VIDEO_3GP, ".3gp", "video/3gpp") \ + _ (VIDEO_AVI, ".avi", "video/x-msvideo") \ + _ (VIDEO_MP4, ".mp4", "video/mp4") \ + _ (VIDEO_MPEG, ".mpeg", "video/mpeg") \ + _ (VIDEO_OGG, ".ogv", "video/ogg") \ + _ (VIDEO_TS, ".ts", "video/mp2t") \ + _ (VIDEO_WEBM, ".webm", "video/webm") typedef enum http_content_type_ { @@ -235,50 +257,100 @@ typedef enum http_status_code_ HTTP_N_STATUS } http_status_code_t; -#define HTTP_HEADER_ACCEPT "Accept" -#define HTTP_HEADER_ACCEPT_CHARSET "Accept-Charset" -#define HTTP_HEADER_ACCEPT_ENCODING "Accept-Encoding" -#define HTTP_HEADER_ACCEPT_LANGUAGE "Accept-Language" -#define HTTP_HEADER_ACCEPT_RANGES "Accept-Ranges" -#define HTTP_HEADER_ALLOW "Allow" -#define HTTP_HEADER_AUTHENTICATION_INFO "Authentication-Info" -#define HTTP_HEADER_AUTHORIZATION "Authorization" -#define HTTP_HEADER_CLOSE "Close" -#define HTTP_HEADER_CONNECTION "Connection" -#define HTTP_HEADER_CONTENT_ENCODING "Content-Encoding" -#define HTTP_HEADER_CONTENT_LANGUAGE "Content-Language" -#define HTTP_HEADER_CONTENT_LENGTH "Content-Length" -#define HTTP_HEADER_CONTENT_LOCATION "Content-Location" -#define HTTP_HEADER_CONTENT_RANGE "Content-Range" -#define HTTP_HEADER_CONTENT_TYPE "Content-Type" -#define HTTP_HEADER_DATE "Date" -#define HTTP_HEADER_ETAG "ETag" -#define HTTP_HEADER_EXPECT "Expect" -#define HTTP_HEADER_FROM "From" -#define HTTP_HEADER_HOST "Host" -#define HTTP_HEADER_IF_MATCH "If-Match" -#define HTTP_HEADER_IF_MODIFIED_SINCE "If-Modified-Since" -#define HTTP_HEADER_IF_NONE_MATCH "If-None-Match" -#define HTTP_HEADER_IF_RANGE "If-Range" -#define HTTP_HEADER_IF_UNMODIFIED_SINCE "If-Unmodified-Since" -#define HTTP_HEADER_LAST_MODIFIED "Last-Modified" -#define HTTP_HEADER_LOCATION "Location" -#define HTTP_HEADER_MAX_FORWARDS "Max-Forwards" -#define HTTP_HEADER_PROXY_AUTHENTICATE "Proxy-Authenticate" -#define HTTP_HEADER_PROXY_AUTHENTICATION_INFO "Proxy-Authentication-Info" -#define HTTP_HEADER_PROXY_AUTHORIZATION "Proxy-Authorization" -#define HTTP_HEADER_RANGE "Range" -#define HTTP_HEADER_REFERER "Referer" -#define HTTP_HEADER_RETRY_AFTER "Retry-After" -#define HTTP_HEADER_SERVER "Server" -#define HTTP_HEADER_TE "TE" -#define HTTP_HEADER_TRAILER "Trailer" -#define HTTP_HEADER_TRANSFER_ENCODING "Transfer-Encoding" -#define HTTP_HEADER_UPGRADE "Upgrade" -#define HTTP_HEADER_USER_AGENT "User-Agent" -#define HTTP_HEADER_VARY "Vary" -#define HTTP_HEADER_VIA "Via" -#define HTTP_HEADER_WWW_AUTHENTICATE "WWW-Authenticate" +#define foreach_http_header_name \ + _ (ACCEPT, "Accept") \ + _ (ACCEPT_CHARSET, "Accept-Charset") \ + _ (ACCEPT_ENCODING, "Accept-Encoding") \ + _ (ACCEPT_LANGUAGE, "Accept-Language") \ + _ (ACCEPT_RANGES, "Accept-Ranges") \ + _ (ACCESS_CONTROL_ALLOW_CREDENTIALS, "Access-Control-Allow-Credentials") \ + _ (ACCESS_CONTROL_ALLOW_HEADERS, "Access-Control-Allow-Headers") \ + _ (ACCESS_CONTROL_ALLOW_METHODS, "Access-Control-Allow-Methods") \ + _ (ACCESS_CONTROL_ALLOW_ORIGIN, "Access-Control-Allow-Origin") \ + _ (ACCESS_CONTROL_EXPOSE_HEADERS, "Access-Control-Expose-Headers") \ + _ (ACCESS_CONTROL_MAX_AGE, "Access-Control-Max-Age") \ + _ (ACCESS_CONTROL_REQUEST_HEADERS, "Access-Control-Request-Headers") \ + _ (ACCESS_CONTROL_REQUEST_METHOD, "Access-Control-Request-Method") \ + _ (AGE, "Age") \ + _ (ALLOW, "Allow") \ + _ (ALPN, "ALPN") \ + _ (ALT_SVC, "Alt-Svc") \ + _ (ALT_USED, "Alt-Used") \ + _ (ALTERNATES, "Alternates") \ + _ (AUTHENTICATION_CONTROL, "Authentication-Control") \ + _ (AUTHENTICATION_INFO, "Authentication-Info") \ + _ (AUTHORIZATION, "Authorization") \ + _ (CACHE_CONTROL, "Cache-Control") \ + _ (CACHE_STATUS, "Cache-Status") \ + _ (CAPSULE_PROTOCOL, "Capsule-Protocol") \ + _ (CDN_CACHE_CONTROL, "CDN-Cache-Control") \ + _ (CDN_LOOP, "CDN-Loop") \ + _ (CLIENT_CERT, "Client-Cert") \ + _ (CLIENT_CERT_CHAIN, "Client-Cert-Chain") \ + _ (CLOSE, "Close") \ + _ (CONNECTION, "Connection") \ + _ (CONTENT_DIGEST, "Content-Digest") \ + _ (CONTENT_DISPOSITION, "Content-Disposition") \ + _ (CONTENT_ENCODING, "Content-Encoding") \ + _ (CONTENT_LANGUAGE, "Content-Language") \ + _ (CONTENT_LENGTH, "Content-Length") \ + _ (CONTENT_LOCATION, "Content-Location") \ + _ (CONTENT_RANGE, "Content-Range") \ + _ (CONTENT_TYPE, "Content-Type") \ + _ (COOKIE, "Cookie") \ + _ (DATE, "Date") \ + _ (DIGEST, "Digest") \ + _ (DPOP, "DPoP") \ + _ (DPOP_NONCE, "DPoP-Nonce") \ + _ (EARLY_DATA, "Early-Data") \ + _ (ETAG, "ETag") \ + _ (EXPECT, "Expect") \ + _ (EXPIRES, "Expires") \ + _ (FORWARDED, "Forwarded") \ + _ (FROM, "From") \ + _ (HOST, "Host") \ + _ (IF_MATCH, "If-Match") \ + _ (IF_MODIFIED_SINCE, "If-Modified-Since") \ + _ (IF_NONE_MATCH, "If-None-Match") \ + _ (IF_RANGE, "If-Range") \ + _ (IF_UNMODIFIED_SINCE, "If-Unmodified-Since") \ + _ (KEEP_ALIVE, "Keep-Alive") \ + _ (LAST_MODIFIED, "Last-Modified") \ + _ (LINK, "Link") \ + _ (LOCATION, "Location") \ + _ (MAX_FORWARDS, "Max-Forwards") \ + _ (ORIGIN, "Origin") \ + _ (PRIORITY, "Priority") \ + _ (PROXY_AUTHENTICATE, "Proxy-Authenticate") \ + _ (PROXY_AUTHENTICATION_INFO, "Proxy-Authentication-Info") \ + _ (PROXY_AUTHORIZATION, "Proxy-Authorization") \ + _ (PROXY_STATUS, "Proxy-Status") \ + _ (RANGE, "Range") \ + _ (REFERER, "Referer") \ + _ (REPR_DIGEST, "Repr-Digest") \ + _ (SET_COOKIE, "Set-Cookie") \ + _ (SIGNATURE, "Signature") \ + _ (SIGNATURE_INPUT, "Signature-Input") \ + _ (STRICT_TRANSPORT_SECURITY, "Strict-Transport-Security") \ + _ (RETRY_AFTER, "Retry-After") \ + _ (SERVER, "Server") \ + _ (TE, "TE") \ + _ (TRAILER, "Trailer") \ + _ (TRANSFER_ENCODING, "Transfer-Encoding") \ + _ (UPGRADE, "Upgrade") \ + _ (USER_AGENT, "User-Agent") \ + _ (VARY, "Vary") \ + _ (VIA, "Via") \ + _ (WANT_CONTENT_DIGEST, "Want-Content-Digest") \ + _ (WANT_REPR_DIGEST, "Want-Repr-Digest") \ + _ (WWW_AUTHENTICATE, "WWW-Authenticate") + +typedef enum http_header_name_ +{ +#define _(sym, str) HTTP_HEADER_##sym, + foreach_http_header_name +#undef _ +} http_header_name_t; typedef enum http_msg_data_type_ { @@ -298,7 +370,7 @@ typedef struct http_msg_data_ u32 headers_offset; u32 headers_len; u32 body_offset; - u32 body_len; + u64 body_len; u8 data[0]; } http_msg_data_t; @@ -310,7 +382,6 @@ typedef struct http_msg_ http_req_method_t method_type; http_status_code_t code; }; - http_content_type_t content_type; http_msg_data_t data; } http_msg_t; @@ -329,18 +400,23 @@ typedef struct http_tc_ http_conn_state_t state; u32 timer_handle; + u32 timeout; + u8 pending_timer; u8 *app_name; + u8 *host; + u8 is_server; /* * Current request */ - http_state_t http_state; + http_req_state_t req_state; http_req_method_t method; u8 *rx_buf; u32 rx_buf_offset; http_buffer_t tx_buf; - u32 to_recv; + u64 to_recv; u32 bytes_dequeued; + u32 control_data_len; /* start line + headers + empty line */ http_target_form_t target_form; u32 target_path_offset; u32 target_path_len; @@ -349,7 +425,9 @@ typedef struct http_tc_ u32 headers_offset; u32 headers_len; u32 body_offset; - u32 body_len; + u64 body_len; + u16 status_code; + u8 is_tunnel; } http_conn_t; typedef struct http_worker_ @@ -361,14 +439,17 @@ typedef struct http_main_ { http_worker_t *wrk; http_conn_t *listener_pool; + http_conn_t *ho_conn_pool; u32 app_index; clib_timebase_t timebase; + u16 *sc_by_u16; /* * Runtime config */ u8 debug_level; + u8 is_init; /* * Config @@ -379,9 +460,10 @@ typedef struct http_main_ } http_main_t; always_inline int -_validate_target_syntax (u8 *target, int is_query, int *is_encoded) +_validate_target_syntax (u8 *target, u32 len, int is_query, int *is_encoded) { - int i, encoded = 0; + int encoded = 0; + u32 i; static uword valid_chars[4] = { /* !$&'()*+,-./0123456789:;= */ @@ -392,7 +474,7 @@ _validate_target_syntax (u8 *target, int is_query, int *is_encoded) 0x0000000000000000, }; - for (i = 0; i < vec_len (target); i++) + for (i = 0; i < len; i++) { if (clib_bitmap_get_no_check (valid_chars, target[i])) continue; @@ -403,7 +485,7 @@ _validate_target_syntax (u8 *target, int is_query, int *is_encoded) /* pct-encoded = "%" HEXDIG HEXDIG */ if (target[i] == '%') { - if ((i + 2) > vec_len (target)) + if ((i + 2) >= len) return -1; if (!isxdigit (target[i + 1]) || !isxdigit (target[i + 2])) return -1; @@ -422,7 +504,7 @@ _validate_target_syntax (u8 *target, int is_query, int *is_encoded) /** * An "absolute-path" rule validation (RFC9110 section 4.1). * - * @param path Target path to validate. + * @param path Vector of target path to validate. * @param is_encoded Return flag that indicates if percent-encoded (optional). * * @return @c 0 on success. @@ -430,13 +512,13 @@ _validate_target_syntax (u8 *target, int is_query, int *is_encoded) always_inline int http_validate_abs_path_syntax (u8 *path, int *is_encoded) { - return _validate_target_syntax (path, 0, is_encoded); + return _validate_target_syntax (path, vec_len (path), 0, is_encoded); } /** * A "query" rule validation (RFC3986 section 2.1). * - * @param query Target query to validate. + * @param query Vector of target query to validate. * @param is_encoded Return flag that indicates if percent-encoded (optional). * * @return @c 0 on success. @@ -444,7 +526,7 @@ http_validate_abs_path_syntax (u8 *path, int *is_encoded) always_inline int http_validate_query_syntax (u8 *query, int *is_encoded) { - return _validate_target_syntax (query, 1, is_encoded); + return _validate_target_syntax (query, vec_len (query), 1, is_encoded); } #define htoi(x) (isdigit (x) ? (x - '0') : (tolower (x) - 'a' + 10)) @@ -453,18 +535,19 @@ http_validate_query_syntax (u8 *query, int *is_encoded) * Decode percent-encoded data. * * @param src Data to decode. + * @param len Length of data to decode. * * @return New vector with decoded data. * * The caller is always responsible to free the returned vector. */ always_inline u8 * -http_percent_decode (u8 *src) +http_percent_decode (u8 *src, u32 len) { - int i; + u32 i; u8 *decoded_uri = 0; - for (i = 0; i < vec_len (src); i++) + for (i = 0; i < len; i++) { if (src[i] == '%') { @@ -668,11 +751,17 @@ 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_t *headers; + http_header_ht_t *headers; uword *value_by_name; } http_header_table_t; @@ -684,7 +773,7 @@ typedef struct always_inline void http_free_header_table (http_header_table_t *ht) { - http_header_t *header; + http_header_ht_t *header; vec_foreach (header, ht->headers) { vec_free (header->name); @@ -712,7 +801,7 @@ http_parse_headers (u8 *headers, http_header_table_t **header_table) u8 *pos, *end, *name_start, *value_start, *name; u32 name_len, value_len; int rv; - http_header_t *header; + http_header_ht_t *header; http_header_table_t *ht; uword *p; @@ -778,7 +867,7 @@ always_inline const char * http_get_header (http_header_table_t *header_table, const char *name) { uword *p; - http_header_t *header; + http_header_ht_t *header; p = hash_get_mem (header_table->value_by_name, name); if (p) @@ -790,6 +879,346 @@ http_get_header (http_header_table_t *header_table, const char *name) return 0; } +/** + * Add header to the list. + * + * @param headers Header list. + * @param name Pointer to header's name buffer. + * @param name_len Length of the name. + * @param value Pointer to header's value buffer. + * @param value_len Length of the value. + * + * @note Headers added at protocol layer: Date, Server, Content-Length + */ +always_inline void +http_add_header (http_header_t **headers, const char *name, uword name_len, + const char *value, uword value_len) +{ + http_header_t *header; + vec_add2 (*headers, header, 1); + header->name.base = (char *) name; + header->name.len = name_len; + header->value.base = (char *) value; + header->value.len = value_len; +} + +/** + * Serialize the header list. + * + * @param headers Header list to serialize. + * + * @return New vector with serialized headers. + * + * The caller is always responsible to free the returned vector. + */ +always_inline u8 * +http_serialize_headers (http_header_t *headers) +{ + u8 *headers_buf = 0, *dst; + u32 headers_buf_len = 2; + http_header_t *header; + + vec_foreach (header, headers) + headers_buf_len += header->name.len + header->value.len + 4; + + vec_validate (headers_buf, headers_buf_len - 1); + dst = headers_buf; + + vec_foreach (header, headers) + { + clib_memcpy (dst, header->name.base, header->name.len); + dst += header->name.len; + *dst++ = ':'; + *dst++ = ' '; + clib_memcpy (dst, header->value.base, header->value.len); + dst += header->value.len; + *dst++ = '\r'; + *dst++ = '\n'; + } + *dst++ = '\r'; + *dst = '\n'; + return headers_buf; +} + +typedef struct +{ + ip46_address_t ip; + u16 port; + u8 is_ip4; +} http_uri_t; + +always_inline int +http_parse_authority_form_target (u8 *target, http_uri_t *authority) +{ + unformat_input_t input; + u32 port; + int rv = 0; + + unformat_init_vector (&input, vec_dup (target)); + if (unformat (&input, "[%U]:%d", unformat_ip6_address, &authority->ip.ip6, + &port)) + { + authority->port = clib_host_to_net_u16 (port); + authority->is_ip4 = 0; + } + else if (unformat (&input, "%U:%d", unformat_ip4_address, &authority->ip.ip4, + &port)) + { + authority->port = clib_host_to_net_u16 (port); + authority->is_ip4 = 1; + } + /* TODO reg-name resolution */ + else + { + clib_warning ("unsupported format '%v'", target); + rv = -1; + } + unformat_free (&input); + return rv; +} + +always_inline u8 * +http_serialize_authority_form_target (http_uri_t *authority) +{ + u8 *s; + + if (authority->is_ip4) + s = format (0, "%U:%d", format_ip4_address, &authority->ip.ip4, + clib_net_to_host_u16 (authority->port)); + else + s = format (0, "[%U]:%d", format_ip6_address, &authority->ip.ip6, + clib_net_to_host_u16 (authority->port)); + + return s; +} + +typedef enum http_url_scheme_ +{ + HTTP_URL_SCHEME_HTTP, + HTTP_URL_SCHEME_HTTPS, +} http_url_scheme_t; + +typedef struct +{ + http_url_scheme_t scheme; + u16 port; + u32 host_offset; + u32 host_len; + u32 path_offset; + u32 path_len; + u8 host_is_ip6; +} http_url_t; + +always_inline int +_parse_port (u8 **pos, u8 *end, u16 *port) +{ + u32 value = 0; + u8 *p = *pos; + + if (!isdigit (*p)) + return -1; + value = *p - '0'; + p++; + + while (p != end) + { + if (!isdigit (*p)) + break; + value = value * 10 + *p - '0'; + if (value > CLIB_U16_MAX) + return -1; + p++; + } + *pos = p; + *port = clib_host_to_net_u16 ((u16) value); + return 0; +} + +/** + * An "absolute-form" URL parsing. + * + * @param url Vector of target URL to validate. + * @param parsed Parsed URL metadata in case of success. + * + * @return @c 0 on success. + */ +always_inline int +http_parse_absolute_form (u8 *url, http_url_t *parsed) +{ + u8 *token_start, *token_end, *end; + int is_encoded = 0; + + static uword valid_chars[4] = { + /* -.0123456789 */ + 0x03ff600000000000, + /* ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz */ + 0x07fffffe07fffffe, + 0x0000000000000000, + 0x0000000000000000, + }; + + if (vec_len (url) < 9) + { + clib_warning ("uri too short"); + return -1; + } + + clib_memset (parsed, 0, sizeof (*parsed)); + + end = url + vec_len (url); + + /* parse scheme */ + if (!memcmp (url, "http:// ", 7)) + { + parsed->scheme = HTTP_URL_SCHEME_HTTP; + parsed->port = clib_host_to_net_u16 (80); + parsed->host_offset = 7; + } + else if (!memcmp (url, "https:// ", 8)) + { + parsed->scheme = HTTP_URL_SCHEME_HTTPS; + parsed->port = clib_host_to_net_u16 (443); + parsed->host_offset = 8; + } + else + { + clib_warning ("invalid scheme"); + return -1; + } + token_start = url + parsed->host_offset; + + /* parse host */ + if (*token_start == '[') + /* IPv6 address */ + { + parsed->host_is_ip6 = 1; + parsed->host_offset++; + token_end = ++token_start; + while (1) + { + if (token_end == end) + { + clib_warning ("invalid host, IPv6 addr not terminated with ']'"); + return -1; + } + else if (*token_end == ']') + { + parsed->host_len = token_end - token_start; + token_start = token_end + 1; + break; + } + else if (*token_end != ':' && *token_end != '.' && + !isxdigit (*token_end)) + { + clib_warning ("invalid character '%u'", *token_end); + return -1; + } + token_end++; + } + } + else + { + token_end = token_start; + while (token_end != end && *token_end != ':' && *token_end != '/') + { + if (!clib_bitmap_get_no_check (valid_chars, *token_end)) + { + clib_warning ("invalid character '%u'", *token_end); + return -1; + } + token_end++; + } + parsed->host_len = token_end - token_start; + token_start = token_end; + } + + if (!parsed->host_len) + { + clib_warning ("zero length host"); + return -1; + } + + /* parse port, if any */ + if (token_start != end && *token_start == ':') + { + token_end = ++token_start; + if (_parse_port (&token_end, end, &parsed->port)) + { + clib_warning ("invalid port"); + return -1; + } + token_start = token_end; + } + + if (token_start == end) + return 0; + + token_start++; /* drop leading slash */ + parsed->path_offset = token_start - url; + parsed->path_len = end - token_start; + + if (parsed->path_len) + return _validate_target_syntax (token_start, parsed->path_len, 0, + &is_encoded); + + return 0; +} + +/** + * Parse target host and port of UDP tunnel over HTTP. + * + * @param path Path in format "{target_host}/{target_port}/". + * @param path_len Length of given path. + * @param parsed Parsed target in case of success.. + * + * @return @c 0 on success. + * + * @note Only IPv4 literals and IPv6 literals supported. + */ +always_inline int +http_parse_masque_host_port (u8 *path, u32 path_len, http_uri_t *parsed) +{ + u8 *p, *end, *decoded_host; + u32 host_len; + unformat_input_t input; + + p = path; + end = path + path_len; + clib_memset (parsed, 0, sizeof (*parsed)); + + while (p != end && *p != '/') + p++; + + host_len = p - path; + if (!host_len || (host_len == path_len) || (host_len + 1 == path_len)) + return -1; + decoded_host = http_percent_decode (path, host_len); + unformat_init_vector (&input, decoded_host); + if (unformat (&input, "%U", unformat_ip4_address, &parsed->ip.ip4)) + parsed->is_ip4 = 1; + else if (unformat (&input, "%U", unformat_ip6_address, &parsed->ip.ip6)) + parsed->is_ip4 = 0; + else + { + unformat_free (&input); + clib_warning ("unsupported target_host format"); + return -1; + } + unformat_free (&input); + + p++; + if (_parse_port (&p, end, &parsed->port)) + { + clib_warning ("invalid port"); + return -1; + } + + if (p == end || *p != '/') + return -1; + + return 0; +} + #endif /* SRC_PLUGINS_HTTP_HTTP_H_ */ /* diff --git a/src/plugins/http/http_buffer.c b/src/plugins/http/http_buffer.c index f3dc308dbf8..bc1b8c08630 100644 --- a/src/plugins/http/http_buffer.c +++ b/src/plugins/http/http_buffer.c @@ -173,7 +173,7 @@ buf_ptr_drain (http_buffer_t *hb, u32 len) bf->segs[1].data += len; bf->segs[0].len -= len; - HTTP_DBG (1, "drained %u left %u", len, bf->segs[1].len); + HTTP_DBG (1, "drained %u left %u", len, bf->segs[0].len); if (!bf->segs[0].len) { diff --git a/src/plugins/http/http_content_types.h b/src/plugins/http/http_content_types.h new file mode 100644 index 00000000000..ddc02566db7 --- /dev/null +++ b/src/plugins/http/http_content_types.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2024 Cisco Systems, Inc. + */ + +#ifndef SRC_PLUGINS_HTTP_HTTP_CONTENT_TYPES_H_ +#define SRC_PLUGINS_HTTP_HTTP_CONTENT_TYPES_H_ + +#include <http/http.h> + +static http_token_t http_content_types[] = { +#define _(s, ext, str) { http_token_lit (str) }, + foreach_http_content_type +#undef _ +}; + +#define http_content_type_token(e) \ + http_content_types[e].base, http_content_types[e].len + +#endif /* SRC_PLUGINS_HTTP_HTTP_CONTENT_TYPES_H_ */ diff --git a/src/plugins/http/http_header_names.h b/src/plugins/http/http_header_names.h new file mode 100644 index 00000000000..99acac786db --- /dev/null +++ b/src/plugins/http/http_header_names.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2024 Cisco Systems, Inc. + */ + +#ifndef SRC_PLUGINS_HTTP_HTTP_HEADER_NAMES_H_ +#define SRC_PLUGINS_HTTP_HTTP_HEADER_NAMES_H_ + +#include <http/http.h> + +static http_token_t http_header_names[] = { +#define _(sym, str) { http_token_lit (str) }, + foreach_http_header_name +#undef _ +}; + +#define http_header_name_token(e) \ + http_header_names[e].base, http_header_names[e].len + +#define http_header_name_str(e) http_header_names[e].base + +#endif /* SRC_PLUGINS_HTTP_HTTP_HEADER_NAMES_H_ */ diff --git a/src/plugins/http/http_plugin.rst b/src/plugins/http/http_plugin.rst index c4c4d2c8234..f86c796bd83 100644 --- a/src/plugins/http/http_plugin.rst +++ b/src/plugins/http/http_plugin.rst @@ -9,14 +9,15 @@ Overview -------- This plugin adds the HTTP protocol to VPP's Host Stack. -As a result parsing of HTTP/1 request or response is available for internal VPP applications. +As a result parsing and serializing of HTTP/1 requests or responses are available for internal VPP applications. 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_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``. It relies on the hoststack constructs and uses ``http_msg_data_t`` data structure for passing metadata to/from applications. @@ -82,7 +83,7 @@ Example bellow validates "absolute-path" rule, as described in RFC9110 section 4 } if (is_encoded) { - u8 *decoded = http_percent_decode (target_path); + u8 *decoded = http_percent_decode (target_path, vec_len (target_path)); vec_free (target_path); target_path = decoded; } @@ -121,6 +122,7 @@ Following example shows how to parse headers: .. code-block:: C + #include <http/http_header_names.h> if (msg.data.headers_len) { u8 *headers = 0; @@ -134,7 +136,7 @@ Following example shows how to parse headers: /* your error handling */ } /* get Accept header */ - const char *accept_value = http_get_header (ht, HTTP_HEADER_ACCEPT); + const char *accept_value = http_get_header (ht, http_header_name_str (HTTP_HEADER_ACCEPT)); if (accept_value) { /* do something interesting */ @@ -143,14 +145,393 @@ Following example shows how to parse headers: vec_free (headers); } -Finally application reads body: +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``. +We will add following members to our session context structure: .. code-block:: C - u8 *body = 0; - if (msg.data.body_len) + typedef struct + { + /* ... */ + u64 to_recv; + u8 *resp_body; + } session_ctx_t; + +First we prepare vector for response body, do it only once when you are reading metadata: + +.. code-block:: C + + /* drop everything up to body */ + svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.body_offset); + ctx->to_recv = msg.data.body_len; + /* prepare vector for response body */ + vec_validate (ctx->resp_body, msg.data.body_len - 1); + vec_reset_length (ctx->resp_body); + +Now we can start reading body content, following block of code could be executed multiple times: + +.. code-block:: C + + /* dequeue */ + u32 n_deq = svm_fifo_max_dequeue (ts->rx_fifo); + /* current offset */ + u64 curr = vec_len (ctx->resp_body); + rv = svm_fifo_dequeue (ts->rx_fifo, n_deq, ctx->resp_body + curr); + ASSERT (rv == n_deq); + /* update length of the vector */ + vec_set_len (ctx->resp_body, curr + n_deq); + /* update number of remaining bytes to receive */ + ctx->to_recv -= rv; + /* check if all data received */ + if (ctx->to_recv == 0) + { + /* we are done */ + /* send 200 OK response */ + } + +Sending data +"""""""""""""" + +When server application sends response back to HTTP layer it starts with message metadata, followed by optional serialized headers and finally body (if any). + +Application should set following items: + +* Status code +* target form +* header section offset and length +* body offset and length + +Application could pass headers back to HTTP layer. Header list is created dynamically as vector of ``http_header_t``, +where we store only pointers to buffers (zero copy). +Well known header names are predefined. +The list is serialized just before you send buffer to HTTP layer. + +.. note:: + Following headers are added at protocol layer and **MUST NOT** be set by application: Date, Server, Content-Length + +Following example shows how to create headers section: + +.. code-block:: C + + #include <http/http.h> + #include <http/http_header_names.h> + #include <http/http_content_types.h> + http_header_t *resp_headers = 0; + u8 *headers_buf = 0; + http_add_header (resp_headers, + http_header_name_token (HTTP_HEADER_CONTENT_TYPE), + http_content_type_token (HTTP_CONTENT_TEXT_HTML)); + http_add_header (resp_headers, + http_header_name_token (HTTP_HEADER_CACHE_CONTROL), + http_token_lit ("max-age=600")); + http_add_header (resp_headers, + http_header_name_token (HTTP_HEADER_LOCATION), + (const char *) redirect, vec_len (redirect)); + headers_buf = http_serialize_headers (resp_headers); + +The example below show how to create and send response HTTP message metadata: + +.. code-block:: C + + http_msg_t msg; + msg.type = HTTP_MSG_REPLY; + msg.code = HTTP_STATUS_MOVED + msg.data.headers_offset = 0; + msg.data.headers_len = vec_len (headers_buf); + msg.data.type = HTTP_MSG_DATA_INLINE; + msg.data.body_len = vec_len (tx_buf); + msg.data.body_offset = msg.data.headers_len; + msg.data.len = msg.data.body_len + msg.data.headers_len; + ts = session_get (hs->vpp_session_index, hs->thread_index); + rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg); + ASSERT (rv == sizeof (msg)); + +Next you will send your serialized headers: + +.. code-block:: C + + rv = svm_fifo_enqueue (ts->tx_fifo, vec_len (headers_buf), headers_buf); + ASSERT (rv == msg.data.headers_len); + vec_free (headers_buf); + +Finally application sends response body: + +.. code-block:: C + + rv = svm_fifo_enqueue (ts->tx_fifo, vec_len (tx_buf), tx_buf); + if (rv != vec_len (hs->tx_buf)) + { + hs->tx_offset = rv; + svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF); + } + else { - vec_validate (body, msg.data.body_len - 1); - rv = svm_fifo_peek (ts->rx_fifo, msg.data.body_offset, msg.data.body_len, body); - ASSERT (rv == msg.data.body_len); + vec_free (tx_buf); } + if (svm_fifo_set_event (ts->tx_fifo)) + session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX); + +Examples above shows how to send body and headers by copy, alternatively you could pass them as pointer: + +.. code-block:: C + + msg.data.type = HTTP_MSG_DATA_PTR; + /* code omitted for brevity */ + if (msg.data.headers_len) + { + uword headers = pointer_to_uword (headers_buf); + rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (headers), (u8 *) &headers); + ASSERT (rv == sizeof (headers)); + } + uword data = pointer_to_uword (tx_buf); + rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (data), (u8 *) &data); + ASSERT (rv == sizeof (data)); + +In this case you need to free data when you receive next request or when session is closed. + + +Client application +^^^^^^^^^^^^^^^^^^ + +Client application opens connection with vnet URI where transport protocol is set to ``http``. + +Sending data +"""""""""""""" + +HTTP request is sent when connection is successfully established in ``session_connected_callback``. + +When client application sends message to HTTP layer it starts with message metadata, followed by request target, optional headers and body (if any) buffers. + +Application should set following items: + +* HTTP method +* target form, offset and length +* header section offset and length +* body offset and length + +Application could pass headers to HTTP layer. Header list is created dynamically as vector of ``http_header_t``, +where we store only pointers to buffers (zero copy). +Well known header names are predefined. +The list is serialized just before you send buffer to HTTP layer. + +.. note:: + Following headers are added at protocol layer and **MUST NOT** be set by application: Host, User-Agent + + +The example below shows how to create headers section: + +.. code-block:: C + + #include <http/http.h> + #include <http/http_header_names.h> + #include <http/http_content_types.h> + http_header_t *req_headers = 0; + u8 *headers_buf = 0; + http_add_header (req_headers, + http_header_name_token (HTTP_HEADER_ACCEPT), + http_content_type_token (HTTP_CONTENT_TEXT_HTML)); + headers_buf = http_serialize_headers (req_headers); + vec_free (hs->req_headers); + +Following example shows how to set message metadata: + +.. code-block:: C + + http_msg_t msg; + msg.type = HTTP_MSG_REQUEST; + msg.method_type = HTTP_REQ_GET; + msg.data.headers_offset = 0; + /* request target */ + msg.data.target_form = HTTP_TARGET_ORIGIN_FORM; + msg.data.target_path_offset = 0; + msg.data.target_path_len = vec_len (target); + /* custom headers */ + msg.data.headers_offset = msg.data.target_path_len; + msg.data.headers_len = vec_len (headers_buf); + /* no request body because we are doing GET request */ + msg.data.body_len = 0; + /* data type and total length */ + msg.data.type = HTTP_MSG_DATA_INLINE; + msg.data.len = msg.data.target_path_len + msg.data.headers_len + msg.data.body_len; + +Finally application sends everything to HTTP layer: + +.. code-block:: C + + svm_fifo_seg_t segs[3] = { { (u8 *) &msg, sizeof (msg) }, /* message metadata */ + { target, vec_len (target) }, /* request target */ + { headers_buf, vec_len (headers_buf) } }; /* serialized headers */ + rv = svm_fifo_enqueue_segments (as->tx_fifo, segs, 3, 0 /* allow partial */); + vec_free (headers_buf); + if (rv < 0 || rv != sizeof (msg) + msg.data.len) + { + clib_warning ("failed app enqueue"); + return -1; + } + if (svm_fifo_set_event (as->tx_fifo)) + session_program_tx_io_evt (as->handle, SESSION_IO_EVT_TX); + +Examples above shows how to send buffers by copy, alternatively you could pass them as pointer: + +.. code-block:: C + + msg.data.type = HTTP_MSG_DATA_PTR; + msg.method_type = HTTP_REQ_POST; + msg.data.body_len = vec_len (data); + /* code omitted for brevity */ + uword target = pointer_to_uword (target); + uword headers = pointer_to_uword (headers_buf); + uword body = pointer_to_uword (data); + svm_fifo_seg_t segs[4] = { + { (u8 *) &msg, sizeof (msg) }, + { (u8 *) &target, sizeof (target) }, + { (u8 *) &headers, sizeof (headers) }, + { (u8 *) &body, sizeof (body) }, + }; + rv = svm_fifo_enqueue_segments (s->tx_fifo, segs, 4, 0 /* allow partial */); + ASSERT (rv == (sizeof (msg) + sizeof (target) + sizeof (headers) + sizeof (body))); + +In this case you need to free data when you receive response or when session is closed. + +Receiving data +"""""""""""""" + +HTTP plugin sends message header with metadata for parsing, in form of offset and length, followed by all data bytes as received from transport. + +Application will get pre-parsed following items: + +* status code +* header section offset and length +* body offset and length + +The example below reads HTTP message header in ``builtin_app_rx_callback``, which is first step application should do: + +.. code-block:: C + + #include <http/http.h> + http_msg_t msg; + rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg); + ASSERT (rv == sizeof (msg)); + +As next step application might validate message type and status code: + +.. code-block:: C + + if (msg.type != HTTP_MSG_REPLY) + { + /* your error handling */ + } + if (msg.code != HTTP_STATUS_OK) + { + /* your error handling */ + /* of course you can continue with steps bellow */ + /* you might be interested in some headers or body content (if any) */ + } + +Headers are parsed using a generic algorithm, independent of the individual header names. +When header is repeated, its combined value consists of all values separated by comma, concatenated in order as received. +Following example shows how to parse headers: + +.. code-block:: C + + #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); + rv = svm_fifo_peek (ts->rx_fifo, msg.data.headers_offset, + msg.data.headers_len, headers); + ASSERT (rv == msg.data.headers_len); + if (http_parse_headers (headers, &ht)) + { + /* your error handling */ + } + /* get Content-Type header */ + const char *content_type = http_get_header (ht, http_header_name_str (HTTP_HEADER_CONTENT_TYPE)); + if (content_type) + { + /* do something interesting */ + } + http_free_header_table (ht); + vec_free (headers); + } + +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``. +We will add following members to our session context structure: + +.. code-block:: C + + typedef struct + { + /* ... */ + u64 to_recv; + u8 *resp_body; + } session_ctx_t; + +First we prepare vector for response body, do it only once when you are reading metadata: + +.. code-block:: C + + /* drop everything up to body */ + svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.body_offset); + ctx->to_recv = msg.data.body_len; + /* prepare vector for response body */ + vec_validate (ctx->resp_body, msg.data.body_len - 1); + vec_reset_length (ctx->resp_body); + +Now we can start reading body content, following block of code could be executed multiple times: + +.. code-block:: C + + /* dequeue */ + u32 max_deq = svm_fifo_max_dequeue (ts->rx_fifo); + u32 n_deq = clib_min (to_recv, max_deq); + /* current offset */ + u64 curr = vec_len (ctx->resp_body); + rv = svm_fifo_dequeue (ts->rx_fifo, n_deq, ctx->resp_body + curr); + if (rv < 0 || rv != n_deq) + { + /* your error handling */ + } + /* update length of the vector */ + vec_set_len (ctx->resp_body, curr + n_deq); + /* update number of remaining bytes to receive */ + ASSERT (to_recv >= rv); + ctx->to_recv -= rv; + /* check if all data received */ + if (ctx->to_recv == 0) + { + /* we are done */ + /* close the session if you don't want to send another request */ + /* and update state machine... */ + } + +HTTP timeout +^^^^^^^^^^^^ + +HTTP plugin sets session inactivity timeout by default to 60 seconds. +Client and server applications can pass custom timeout value (in seconds) using extended configuration when doing connect or start listening respectively. +You just need to add extended configuration to session endpoint configuration which is part of ``vnet_connect_args_t`` and ``vnet_listen_args_t``. +HTTP plugin use ``opaque`` member of ``transport_endpt_ext_cfg_t``, unsigned 32bit integer seems to be sufficient (allowing the timeout to be set up to 136 years). + +The example below sets HTTP session timeout to 30 seconds (server application): + +.. code-block:: C + + vnet_listen_args_t _a, *a = &_a; + session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL; + transport_endpt_ext_cfg_t *ext_cfg; + int rv; + clib_memset (a, 0, sizeof (*a)); + clib_memcpy (&a->sep_ext, &sep, sizeof (sep)); + /* add new extended config entry */ + ext_cfg = session_endpoint_add_ext_cfg ( + &a->sep_ext, TRANSPORT_ENDPT_EXT_CFG_HTTP, sizeof (ext_cfg->opaque)); + /* your custom timeout value in seconds */ + ext_cfg->opaque = 30; + /* rest of the settings omitted for brevity */ + rv = vnet_listen (a); + /* don't forget to free extended config */ + session_endpoint_free_ext_cfgs (&a->sep_ext); + /* ... */ diff --git a/src/plugins/http/http_status_codes.h b/src/plugins/http/http_status_codes.h new file mode 100644 index 00000000000..100095c8f42 --- /dev/null +++ b/src/plugins/http/http_status_codes.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2024 Cisco Systems, Inc. + */ + +#ifndef SRC_PLUGINS_HTTP_HTTP_STATUS_CODES_H_ +#define SRC_PLUGINS_HTTP_HTTP_STATUS_CODES_H_ + +#include <http/http.h> + +static const char *http_status_code_str[] = { +#define _(c, s, str) str, + foreach_http_status_code +#undef _ +}; + +static inline u8 * +format_http_status_code (u8 *s, va_list *va) +{ + http_status_code_t status_code = va_arg (*va, http_status_code_t); + if (status_code < HTTP_N_STATUS) + s = format (s, "%s", http_status_code_str[status_code]); + else + s = format (s, "invalid status code %d", status_code); + return s; +} + +#endif /* SRC_PLUGINS_HTTP_HTTP_STATUS_CODES_H_ */ diff --git a/src/plugins/http/http_timer.c b/src/plugins/http/http_timer.c index c8fc6328855..580f31657a9 100644 --- a/src/plugins/http/http_timer.c +++ b/src/plugins/http/http_timer.c @@ -29,7 +29,15 @@ http_timer_process_expired_cb (u32 *expired_timers) { /* Get session handle. The first bit is the timer id */ hs_handle = expired_timers[i] & 0x7FFFFFFF; - session_send_rpc_evt_to_thread (hs_handle >> 24, twc->cb_fn, + twc->invalidate_cb (hs_handle); + } + for (i = 0; i < vec_len (expired_timers); i++) + { + /* Get session handle. The first bit is the timer id */ + hs_handle = expired_timers[i] & 0x7FFFFFFF; + HTTP_DBG (1, "rpc to hc [%u]%x", hs_handle >> 24, + hs_handle & 0x00FFFFFF); + session_send_rpc_evt_to_thread (hs_handle >> 24, twc->rpc_cb, uword_to_pointer (hs_handle, void *)); } } @@ -66,17 +74,19 @@ VLIB_REGISTER_NODE (http_timer_process_node) = { }; void -http_timers_init (vlib_main_t *vm, http_conn_timeout_fn *cb_fn) +http_timers_init (vlib_main_t *vm, http_conn_timeout_fn *rpc_cb, + http_conn_invalidate_timer_fn *invalidate_cb) { http_tw_ctx_t *twc = &http_tw_ctx; vlib_node_t *n; - if (twc->tw.timers) - return; + ASSERT (twc->tw.timers == 0); + tw_timer_wheel_init_2t_1w_2048sl (&twc->tw, http_timer_process_expired_cb, 1.0 /* timer interval */, ~0); clib_spinlock_init (&twc->tw_lock); - twc->cb_fn = cb_fn; + twc->rpc_cb = rpc_cb; + twc->invalidate_cb = invalidate_cb; vlib_node_set_state (vm, http_timer_process_node.index, VLIB_NODE_STATE_POLLING); diff --git a/src/plugins/http/http_timer.h b/src/plugins/http/http_timer.h index eec5a4595fe..43d20d004d8 100644 --- a/src/plugins/http/http_timer.h +++ b/src/plugins/http/http_timer.h @@ -19,34 +19,37 @@ #include <http/http.h> #include <vppinfra/tw_timer_2t_1w_2048sl.h> -#define HTTP_CONN_TIMEOUT 60 +#define HTTP_CONN_TIMEOUT 60 +#define HTTP_TIMER_HANDLE_INVALID ((u32) ~0) typedef void (http_conn_timeout_fn) (void *); +typedef void (http_conn_invalidate_timer_fn) (u32 hs_handle); typedef struct http_tw_ctx_ { tw_timer_wheel_2t_1w_2048sl_t tw; clib_spinlock_t tw_lock; - http_conn_timeout_fn *cb_fn; + http_conn_timeout_fn *rpc_cb; + http_conn_invalidate_timer_fn *invalidate_cb; } http_tw_ctx_t; extern http_tw_ctx_t http_tw_ctx; -void http_timers_init (vlib_main_t *vm, http_conn_timeout_fn *cb_fn); +void http_timers_init (vlib_main_t *vm, http_conn_timeout_fn *rpc_cb, + http_conn_invalidate_timer_fn *invalidate_cb); static inline void http_conn_timer_start (http_conn_t *hc) { http_tw_ctx_t *twc = &http_tw_ctx; u32 hs_handle; - u64 timeout; - timeout = HTTP_CONN_TIMEOUT; + ASSERT (hc->timer_handle == HTTP_TIMER_HANDLE_INVALID); hs_handle = hc->c_thread_index << 24 | hc->c_c_index; clib_spinlock_lock (&twc->tw_lock); hc->timer_handle = - tw_timer_start_2t_1w_2048sl (&twc->tw, hs_handle, 0, timeout); + tw_timer_start_2t_1w_2048sl (&twc->tw, hs_handle, 0, hc->timeout); clib_spinlock_unlock (&twc->tw_lock); } @@ -55,12 +58,13 @@ http_conn_timer_stop (http_conn_t *hc) { http_tw_ctx_t *twc = &http_tw_ctx; - if (hc->timer_handle == ~0) + hc->pending_timer = 0; + if (hc->timer_handle == HTTP_TIMER_HANDLE_INVALID) return; clib_spinlock_lock (&twc->tw_lock); tw_timer_stop_2t_1w_2048sl (&twc->tw, hc->timer_handle); - hc->timer_handle = ~0; + hc->timer_handle = HTTP_TIMER_HANDLE_INVALID; clib_spinlock_unlock (&twc->tw_lock); } @@ -68,15 +72,17 @@ static inline void http_conn_timer_update (http_conn_t *hc) { http_tw_ctx_t *twc = &http_tw_ctx; - u64 timeout; - - if (hc->timer_handle == ~0) - return; - - timeout = HTTP_CONN_TIMEOUT; + u32 hs_handle; clib_spinlock_lock (&twc->tw_lock); - tw_timer_update_2t_1w_2048sl (&twc->tw, hc->timer_handle, timeout); + if (hc->timer_handle != HTTP_TIMER_HANDLE_INVALID) + tw_timer_update_2t_1w_2048sl (&twc->tw, hc->timer_handle, hc->timeout); + else + { + hs_handle = hc->c_thread_index << 24 | hc->c_c_index; + hc->timer_handle = + tw_timer_start_2t_1w_2048sl (&twc->tw, hs_handle, 0, hc->timeout); + } clib_spinlock_unlock (&twc->tw_lock); } diff --git a/src/plugins/http/test/http_test.c b/src/plugins/http/test/http_test.c new file mode 100644 index 00000000000..d4ac8f46f29 --- /dev/null +++ b/src/plugins/http/test/http_test.c @@ -0,0 +1,360 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2024 Cisco Systems, Inc. + */ + +#include <vnet/plugin/plugin.h> +#include <vpp/app/version.h> +#include <http/http.h> + +#define HTTP_TEST_I(_cond, _comment, _args...) \ + ({ \ + int _evald = (_cond); \ + if (!(_evald)) \ + { \ + vlib_cli_output (vm, "FAIL:%d: " _comment "\n", __LINE__, ##_args); \ + } \ + else \ + { \ + vlib_cli_output (vm, "PASS:%d: " _comment "\n", __LINE__, ##_args); \ + } \ + _evald; \ + }) + +#define HTTP_TEST(_cond, _comment, _args...) \ + { \ + if (!HTTP_TEST_I (_cond, _comment, ##_args)) \ + { \ + return 1; \ + } \ + } + +static int +http_test_authority_form (vlib_main_t *vm) +{ + u8 *target = 0, *formated_target = 0; + http_uri_t authority; + int rv; + + target = format (0, "10.10.2.45:20"); + rv = http_parse_authority_form_target (target, &authority); + HTTP_TEST ((rv == 0), "'%v' should be valid", target); + formated_target = http_serialize_authority_form_target (&authority); + rv = vec_cmp (target, formated_target); + HTTP_TEST ((rv == 0), "'%v' should match '%v'", target, formated_target); + vec_free (target); + vec_free (formated_target); + + target = format (0, "[dead:beef::1234]:443"); + rv = http_parse_authority_form_target (target, &authority); + HTTP_TEST ((rv == 0), "'%v' should be valid", target); + formated_target = http_serialize_authority_form_target (&authority); + rv = vec_cmp (target, formated_target); + HTTP_TEST ((rv == 0), "'%v' should match '%v'", target, formated_target); + vec_free (target); + vec_free (formated_target); + + target = format (0, "example.com:80"); + rv = http_parse_authority_form_target (target, &authority); + HTTP_TEST ((rv != 0), "'%v' reg-name not supported", target); + vec_free (target); + + target = format (0, "10.10.2.45"); + rv = http_parse_authority_form_target (target, &authority); + HTTP_TEST ((rv != 0), "'%v' should be invalid", target); + vec_free (target); + + target = format (0, "1000.10.2.45:20"); + rv = http_parse_authority_form_target (target, &authority); + HTTP_TEST ((rv != 0), "'%v' should be invalid", target); + vec_free (target); + + target = format (0, "[xyz0::1234]:443"); + rv = http_parse_authority_form_target (target, &authority); + HTTP_TEST ((rv != 0), "'%v' should be invalid", target); + vec_free (target); + + return 0; +} + +static int +http_test_absolute_form (vlib_main_t *vm) +{ + u8 *url = 0; + http_url_t parsed_url; + int rv; + + url = format (0, "https://example.org/.well-known/masque/udp/1.2.3.4/123/"); + rv = http_parse_absolute_form (url, &parsed_url); + HTTP_TEST ((rv == 0), "'%v' should be valid", url); + HTTP_TEST ((parsed_url.scheme == HTTP_URL_SCHEME_HTTPS), + "scheme should be https"); + HTTP_TEST ((parsed_url.host_is_ip6 == 0), "host_is_ip6=%u should be 0", + parsed_url.host_is_ip6); + HTTP_TEST ((parsed_url.host_offset == strlen ("https://")), + "host_offset=%u should be %u", parsed_url.host_offset, + strlen ("https://")); + HTTP_TEST ((parsed_url.host_len == strlen ("example.org")), + "host_len=%u should be %u", parsed_url.host_len, + strlen ("example.org")); + HTTP_TEST ((clib_net_to_host_u16 (parsed_url.port) == 443), + "port=%u should be 443", clib_net_to_host_u16 (parsed_url.port)); + HTTP_TEST ((parsed_url.path_offset == strlen ("https://example.org/")), + "path_offset=%u should be %u", parsed_url.path_offset, + strlen ("https://example.org/")); + HTTP_TEST ( + (parsed_url.path_len == strlen (".well-known/masque/udp/1.2.3.4/123/")), + "path_len=%u should be %u", parsed_url.path_len, + strlen (".well-known/masque/udp/1.2.3.4/123/")); + vec_free (url); + + url = format (0, "http://vpp-example.org"); + rv = http_parse_absolute_form (url, &parsed_url); + HTTP_TEST ((rv == 0), "'%v' should be valid", url); + HTTP_TEST ((parsed_url.scheme == HTTP_URL_SCHEME_HTTP), + "scheme should be http"); + HTTP_TEST ((parsed_url.host_is_ip6 == 0), "host_is_ip6=%u should be 0", + parsed_url.host_is_ip6); + HTTP_TEST ((parsed_url.host_offset == strlen ("http://")), + "host_offset=%u should be %u", parsed_url.host_offset, + strlen ("http://")); + HTTP_TEST ((parsed_url.host_len == strlen ("vpp-example.org")), + "host_len=%u should be %u", parsed_url.host_len, + strlen ("vpp-example.org")); + HTTP_TEST ((clib_net_to_host_u16 (parsed_url.port) == 80), + "port=%u should be 80", clib_net_to_host_u16 (parsed_url.port)); + HTTP_TEST ((parsed_url.path_len == 0), "path_len=%u should be 0", + parsed_url.path_len); + vec_free (url); + + url = format (0, "http://1.2.3.4:8080/abcd"); + rv = http_parse_absolute_form (url, &parsed_url); + HTTP_TEST ((rv == 0), "'%v' should be valid", url); + HTTP_TEST ((parsed_url.scheme == HTTP_URL_SCHEME_HTTP), + "scheme should be http"); + HTTP_TEST ((parsed_url.host_is_ip6 == 0), "host_is_ip6=%u should be 0", + parsed_url.host_is_ip6); + HTTP_TEST ((parsed_url.host_offset == strlen ("http://")), + "host_offset=%u should be %u", parsed_url.host_offset, + strlen ("http://")); + HTTP_TEST ((parsed_url.host_len == strlen ("1.2.3.4")), + "host_len=%u should be %u", parsed_url.host_len, + strlen ("1.2.3.4")); + HTTP_TEST ((clib_net_to_host_u16 (parsed_url.port) == 8080), + "port=%u should be 8080", clib_net_to_host_u16 (parsed_url.port)); + HTTP_TEST ((parsed_url.path_offset == strlen ("http://1.2.3.4:8080/")), + "path_offset=%u should be %u", parsed_url.path_offset, + strlen ("http://1.2.3.4:8080/")); + HTTP_TEST ((parsed_url.path_len == strlen ("abcd")), + "path_len=%u should be %u", parsed_url.path_len, strlen ("abcd")); + vec_free (url); + + url = format (0, "https://[dead:beef::1234]/abcd"); + rv = http_parse_absolute_form (url, &parsed_url); + HTTP_TEST ((rv == 0), "'%v' should be valid", url); + HTTP_TEST ((parsed_url.scheme == HTTP_URL_SCHEME_HTTPS), + "scheme should be https"); + HTTP_TEST ((parsed_url.host_is_ip6 == 1), "host_is_ip6=%u should be 1", + parsed_url.host_is_ip6); + HTTP_TEST ((parsed_url.host_offset == strlen ("https://[")), + "host_offset=%u should be %u", parsed_url.host_offset, + strlen ("https://[")); + HTTP_TEST ((parsed_url.host_len == strlen ("dead:beef::1234")), + "host_len=%u should be %u", parsed_url.host_len, + strlen ("dead:beef::1234")); + HTTP_TEST ((clib_net_to_host_u16 (parsed_url.port) == 443), + "port=%u should be 443", clib_net_to_host_u16 (parsed_url.port)); + HTTP_TEST ((parsed_url.path_offset == strlen ("https://[dead:beef::1234]/")), + "path_offset=%u should be %u", parsed_url.path_offset, + strlen ("https://[dead:beef::1234]/")); + HTTP_TEST ((parsed_url.path_len == strlen ("abcd")), + "path_len=%u should be %u", parsed_url.path_len, strlen ("abcd")); + vec_free (url); + + url = format (0, "http://[::ffff:192.0.2.128]:8080/"); + rv = http_parse_absolute_form (url, &parsed_url); + HTTP_TEST ((rv == 0), "'%v' should be valid", url); + HTTP_TEST ((parsed_url.scheme == HTTP_URL_SCHEME_HTTP), + "scheme should be http"); + HTTP_TEST ((parsed_url.host_is_ip6 == 1), "host_is_ip6=%u should be 1", + parsed_url.host_is_ip6); + HTTP_TEST ((parsed_url.host_offset == strlen ("http://[")), + "host_offset=%u should be %u", parsed_url.host_offset, + strlen ("http://[")); + HTTP_TEST ((parsed_url.host_len == strlen ("::ffff:192.0.2.128")), + "host_len=%u should be %u", parsed_url.host_len, + strlen ("::ffff:192.0.2.128")); + HTTP_TEST ((clib_net_to_host_u16 (parsed_url.port) == 8080), + "port=%u should be 8080", clib_net_to_host_u16 (parsed_url.port)); + HTTP_TEST ((parsed_url.path_len == 0), "path_len=%u should be 0", + parsed_url.path_len); + vec_free (url); + + url = format (0, "http://[dead:beef::1234/abc"); + rv = http_parse_absolute_form (url, &parsed_url); + HTTP_TEST ((rv != 0), "'%v' should be invalid", url); + vec_free (url); + + url = format (0, "http://[dead|beef::1234]/abc"); + rv = http_parse_absolute_form (url, &parsed_url); + HTTP_TEST ((rv != 0), "'%v' should be invalid", url); + vec_free (url); + + url = format (0, "http:example.org:8080/abcd"); + rv = http_parse_absolute_form (url, &parsed_url); + HTTP_TEST ((rv != 0), "'%v' should be invalid", url); + vec_free (url); + + url = format (0, "htt://example.org:8080/abcd"); + rv = http_parse_absolute_form (url, &parsed_url); + HTTP_TEST ((rv != 0), "'%v' should be invalid", url); + vec_free (url); + + url = format (0, "http://"); + rv = http_parse_absolute_form (url, &parsed_url); + HTTP_TEST ((rv != 0), "'%v' should be invalid", url); + vec_free (url); + + url = format (0, "http:///abcd"); + rv = http_parse_absolute_form (url, &parsed_url); + HTTP_TEST ((rv != 0), "'%v' should be invalid", url); + vec_free (url); + + url = format (0, "http://example.org:808080/abcd"); + rv = http_parse_absolute_form (url, &parsed_url); + HTTP_TEST ((rv != 0), "'%v' should be invalid", url); + vec_free (url); + + url = format (0, "http://example.org/a%%3Xbcd"); + rv = http_parse_absolute_form (url, &parsed_url); + HTTP_TEST ((rv != 0), "'%v' should be invalid", url); + vec_free (url); + + url = format (0, "http://example.org/a%%3"); + rv = http_parse_absolute_form (url, &parsed_url); + HTTP_TEST ((rv != 0), "'%v' should be invalid", url); + vec_free (url); + + url = format (0, "http://example.org/a[b]cd"); + rv = http_parse_absolute_form (url, &parsed_url); + HTTP_TEST ((rv != 0), "'%v' should be invalid", url); + vec_free (url); + + url = format (0, "http://exa[m]ple.org/abcd"); + rv = http_parse_absolute_form (url, &parsed_url); + HTTP_TEST ((rv != 0), "'%v' should be invalid", url); + vec_free (url); + + return 0; +} + +static int +http_test_parse_masque_host_port (vlib_main_t *vm) +{ + u8 *path = 0; + http_uri_t target; + int rv; + + path = format (0, "10.10.2.45/443/"); + rv = http_parse_masque_host_port (path, vec_len (path), &target); + HTTP_TEST ((rv == 0), "'%v' should be valid", path); + HTTP_TEST ((target.is_ip4 == 1), "is_ip4=%d should be 1", target.is_ip4); + HTTP_TEST ((clib_net_to_host_u16 (target.port) == 443), + "port=%u should be 443", clib_net_to_host_u16 (target.port)); + HTTP_TEST ((target.ip.ip4.data[0] == 10 && target.ip.ip4.data[1] == 10 && + target.ip.ip4.data[2] == 2 && target.ip.ip4.data[3] == 45), + "target.ip=%U should be 10.10.2.45", format_ip4_address, + &target.ip.ip4); + vec_free (path); + + path = format (0, "dead%%3Abeef%%3A%%3A1234/80/"); + rv = http_parse_masque_host_port (path, vec_len (path), &target); + HTTP_TEST ((rv == 0), "'%v' should be valid", path); + HTTP_TEST ((target.is_ip4 == 0), "is_ip4=%d should be 0", target.is_ip4); + HTTP_TEST ((clib_net_to_host_u16 (target.port) == 80), + "port=%u should be 80", clib_net_to_host_u16 (target.port)); + HTTP_TEST ((clib_net_to_host_u16 (target.ip.ip6.as_u16[0]) == 0xdead && + clib_net_to_host_u16 (target.ip.ip6.as_u16[1]) == 0xbeef && + target.ip.ip6.as_u16[2] == 0 && target.ip.ip6.as_u16[3] == 0 && + target.ip.ip6.as_u16[4] == 0 && target.ip.ip6.as_u16[5] == 0 && + target.ip.ip6.as_u16[6] == 0 && + clib_net_to_host_u16 (target.ip.ip6.as_u16[7]) == 0x1234), + "target.ip=%U should be dead:beef::1234", format_ip6_address, + &target.ip.ip6); + vec_free (path); + + path = format (0, "example.com/443/"); + rv = http_parse_masque_host_port (path, vec_len (path), &target); + HTTP_TEST ((rv != 0), "'%v' reg-name not supported", path); + vec_free (path); + + path = format (0, "10.10.2.45/443443/"); + rv = http_parse_masque_host_port (path, vec_len (path), &target); + HTTP_TEST ((rv != 0), "'%v' should be invalid", path); + vec_free (path); + + path = format (0, "/443/"); + rv = http_parse_masque_host_port (path, vec_len (path), &target); + HTTP_TEST ((rv != 0), "'%v' should be invalid", path); + vec_free (path); + + path = format (0, "10.10.2.45/"); + rv = http_parse_masque_host_port (path, vec_len (path), &target); + HTTP_TEST ((rv != 0), "'%v' should be invalid", path); + vec_free (path); + + path = format (0, "10.10.2.45"); + rv = http_parse_masque_host_port (path, vec_len (path), &target); + HTTP_TEST ((rv != 0), "'%v' should be invalid", path); + vec_free (path); + + path = format (0, "10.10.2.45/443"); + rv = http_parse_masque_host_port (path, vec_len (path), &target); + HTTP_TEST ((rv != 0), "'%v' should be invalid", path); + vec_free (path); + + return 0; +} + +static clib_error_t * +test_http_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + int res = 0; + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "authority-form")) + res = http_test_authority_form (vm); + else if (unformat (input, "absolute-form")) + res = http_test_absolute_form (vm); + else if (unformat (input, "parse-masque-host-port")) + res = http_test_parse_masque_host_port (vm); + else if (unformat (input, "all")) + { + if ((res = http_test_authority_form (vm))) + goto done; + if ((res = http_test_absolute_form (vm))) + goto done; + if ((res = http_test_parse_masque_host_port (vm))) + goto done; + } + else + break; + } + +done: + if (res) + return clib_error_return (0, "HTTP unit test failed"); + return 0; +} + +VLIB_CLI_COMMAND (test_http_command) = { + .path = "test http", + .short_help = "http unit tests", + .function = test_http_command_fn, +}; + +VLIB_PLUGIN_REGISTER () = { + .version = VPP_BUILD_VER, + .description = "HTTP - Unit Test", + .default_disabled = 1, +}; diff --git a/src/plugins/http_static/builtinurl/json_urls.c b/src/plugins/http_static/builtinurl/json_urls.c index 8578be147f3..19c5245e4b2 100644 --- a/src/plugins/http_static/builtinurl/json_urls.c +++ b/src/plugins/http_static/builtinurl/json_urls.c @@ -45,6 +45,7 @@ handle_get_version (hss_url_handler_args_t *args) args->data = s; args->data_len = vec_len (s); + args->ct = HTTP_CONTENT_APP_JSON; args->free_vec_data = 1; return HSS_URL_HANDLER_OK; } @@ -117,6 +118,7 @@ handle_get_interface_stats (hss_url_handler_args_t *args) out: args->data = s; args->data_len = vec_len (s); + args->ct = HTTP_CONTENT_APP_JSON; args->free_vec_data = 1; vec_free (sw_if_indices); vec_free (stats); @@ -157,6 +159,7 @@ handle_get_interface_list (hss_url_handler_args_t *args) args->data = s; args->data_len = vec_len (s); + args->ct = HTTP_CONTENT_APP_JSON; args->free_vec_data = 1; return HSS_URL_HANDLER_OK; } diff --git a/src/plugins/http_static/http_cache.c b/src/plugins/http_static/http_cache.c index 8b9751b7f78..2e63e335d47 100644 --- a/src/plugins/http_static/http_cache.c +++ b/src/plugins/http_static/http_cache.c @@ -17,6 +17,8 @@ #include <vppinfra/bihash_template.c> #include <vppinfra/unix.h> #include <vlib/vlib.h> +#include <sys/stat.h> +#include <vppinfra/time_range.h> static void hss_cache_lock (hss_cache_t *hc) @@ -153,7 +155,7 @@ lru_update (hss_cache_t *hc, hss_cache_entry_t *ep, f64 now) static void hss_cache_attach_entry (hss_cache_t *hc, u32 ce_index, u8 **data, - u64 *data_len) + u64 *data_len, u8 **last_modified) { hss_cache_entry_t *ce; @@ -162,6 +164,7 @@ hss_cache_attach_entry (hss_cache_t *hc, u32 ce_index, u8 **data, ce->inuse++; *data = ce->data; *data_len = vec_len (ce->data); + *last_modified = ce->last_modified; /* Update the cache entry, mark it in-use */ lru_update (hc, ce, vlib_time_now (vlib_get_main ())); @@ -209,16 +212,15 @@ hss_cache_lookup (hss_cache_t *hc, u8 *path) u32 hss_cache_lookup_and_attach (hss_cache_t *hc, u8 *path, u8 **data, - u64 *data_len) + u64 *data_len, u8 **last_modified) { u32 ce_index; - /* Make sure nobody removes the entry while we look it up */ hss_cache_lock (hc); ce_index = hss_cache_lookup (hc, path); if (ce_index != ~0) - hss_cache_attach_entry (hc, ce_index, data, data_len); + hss_cache_attach_entry (hc, ce_index, data, data_len, last_modified); hss_cache_unlock (hc); @@ -260,6 +262,7 @@ hss_cache_do_evictions (hss_cache_t *hc) hc->cache_evictions++; vec_free (ce->filename); vec_free (ce->data); + vec_free (ce->last_modified); if (hc->debug_level > 1) clib_warning ("pool put index %d", ce - hc->cache_pool); @@ -271,13 +274,15 @@ hss_cache_do_evictions (hss_cache_t *hc) } u32 -hss_cache_add_and_attach (hss_cache_t *hc, u8 *path, u8 **data, u64 *data_len) +hss_cache_add_and_attach (hss_cache_t *hc, u8 *path, u8 **data, u64 *data_len, + u8 **last_modified) { BVT (clib_bihash_kv) kv; hss_cache_entry_t *ce; clib_error_t *error; u8 *file_data; u32 ce_index; + struct stat dm; hss_cache_lock (hc); @@ -298,11 +303,17 @@ hss_cache_add_and_attach (hss_cache_t *hc, u8 *path, u8 **data, u64 *data_len) pool_get_zero (hc->cache_pool, ce); ce->filename = vec_dup (path); ce->data = file_data; + if (stat ((char *) path, &dm) == 0) + { + ce->last_modified = + format (0, "%U GMT", format_clib_timebase_time, (f64) dm.st_mtime); + } /* Attach cache entry without additional lock */ ce->inuse++; *data = file_data; *data_len = vec_len (file_data); + *last_modified = ce->last_modified; lru_add (hc, ce, vlib_time_now (vlib_get_main ())); hc->cache_size += vec_len (ce->data); @@ -364,6 +375,7 @@ hss_cache_clear (hss_cache_t *hc) hc->cache_evictions++; vec_free (ce->filename); vec_free (ce->data); + vec_free (ce->last_modified); if (hc->debug_level > 1) clib_warning ("pool put index %d", ce - hc->cache_pool); pool_put (hc->cache_pool, ce); @@ -421,19 +433,19 @@ format_hss_cache (u8 *s, va_list *args) { s = format (s, "cache size %lld bytes, limit %lld bytes, evictions %lld", hc->cache_size, hc->cache_limit, hc->cache_evictions); - return 0; + return s; } vm = vlib_get_main (); now = vlib_time_now (vm); - s = format (s, "%U", format_hss_cache_entry, 0 /* header */, now); + s = format (s, "%U\n", format_hss_cache_entry, 0 /* header */, now); for (index = hc->first_index; index != ~0;) { ce = pool_elt_at_index (hc->cache_pool, index); index = ce->next_index; - s = format (s, "%U", format_hss_cache_entry, ce, now); + s = format (s, "%U\n", format_hss_cache_entry, ce, now); } s = format (s, "%40s%12lld", "Total Size", hc->cache_size); diff --git a/src/plugins/http_static/http_cache.h b/src/plugins/http_static/http_cache.h index a89ed5e7e94..21f71a924d5 100644 --- a/src/plugins/http_static/http_cache.h +++ b/src/plugins/http_static/http_cache.h @@ -22,6 +22,9 @@ typedef struct hss_cache_entry_ { /** Name of the file */ u8 *filename; + /** Last modified date, format: + * <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT */ + u8 *last_modified; /** Contents of the file, as a u8 * vector */ u8 *data; /** Last time the cache entry was used */ @@ -58,9 +61,9 @@ typedef struct hss_cache_ } hss_cache_t; u32 hss_cache_lookup_and_attach (hss_cache_t *hc, u8 *path, u8 **data, - u64 *data_len); + u64 *data_len, u8 **last_modified); u32 hss_cache_add_and_attach (hss_cache_t *hc, u8 *path, u8 **data, - u64 *data_len); + u64 *data_len, u8 **last_modified); void hss_cache_detach_entry (hss_cache_t *hc, u32 ce_index); u32 hss_cache_clear (hss_cache_t *hc); void hss_cache_init (hss_cache_t *hc, uword cache_size, u8 debug_level); diff --git a/src/plugins/http_static/http_static.api b/src/plugins/http_static/http_static.api index 4d6d8bfe9b5..60c0369848d 100644 --- a/src/plugins/http_static/http_static.api +++ b/src/plugins/http_static/http_static.api @@ -2,20 +2,59 @@ /** \file This file defines static http server control-plane API messages */ -option version = "2.1.0"; + +option version = "2.3.0"; + +/** \brief Configure and enable the static http server + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param fifo_size - size (in bytes) of the session FIFOs + @param cache_size_limit - size (in bytes) of the in-memory file data cache + @param max_age - how long a response is considered fresh (in seconds) + @param prealloc_fifos - number of preallocated fifos (usually 0) + @param private_segment_size - fifo segment size (usually 0) + @param www_root - html root path + @param uri - bind URI, defaults to "tcp://0.0.0.0/80" +*/ + +autoreply define http_static_enable_v2 { + option deprecated; + + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + /* Typical options */ + u32 fifo_size; + u32 cache_size_limit; + u32 max_age [default=600]; + /* Unusual options */ + u32 prealloc_fifos; + u32 private_segment_size; + + /* Root of the html path */ + string www_root[256]; + /* The bind URI */ + string uri[256]; +}; /** \brief Configure and enable the static http server @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @param fifo_size - size (in bytes) of the session FIFOs @param cache_size_limit - size (in bytes) of the in-memory file data cache + @param max_age - how long a response is considered fresh (in seconds) + @param keepalive_timeout - timeout during which client connection will stay open (in seconds) @param prealloc_fifos - number of preallocated fifos (usually 0) @param private_segment_size - fifo segment size (usually 0) @param www_root - html root path @param uri - bind URI, defaults to "tcp://0.0.0.0/80" */ -autoreply define http_static_enable { +autoreply define http_static_enable_v3 { + option deprecated; + /* Client identifier, set from api_main.my_client_index */ u32 client_index; @@ -24,6 +63,8 @@ autoreply define http_static_enable { /* Typical options */ u32 fifo_size; u32 cache_size_limit; + u32 max_age [default=600]; + u32 keepalive_timeout [default=60]; /* Unusual options */ u32 prealloc_fifos; u32 private_segment_size; diff --git a/src/plugins/http_static/http_static.c b/src/plugins/http_static/http_static.c index 8f8fe37b7c1..464fd27e90d 100644 --- a/src/plugins/http_static/http_static.c +++ b/src/plugins/http_static/http_static.c @@ -66,7 +66,8 @@ hss_register_url_handler (hss_url_handler_fn fp, const char *url, */ static int hss_enable_api (u32 fifo_size, u32 cache_limit, u32 prealloc_fifos, - u32 private_segment_size, u8 *www_root, u8 *uri) + u32 private_segment_size, u8 *www_root, u8 *uri, u32 max_age, + u32 keepalive_timeout) { hss_main_t *hsm = &hss_main; int rv; @@ -77,6 +78,8 @@ hss_enable_api (u32 fifo_size, u32 cache_limit, u32 prealloc_fifos, hsm->private_segment_size = private_segment_size; hsm->www_root = format (0, "%s%c", www_root, 0); hsm->uri = format (0, "%s%c", uri, 0); + hsm->max_age = max_age; + hsm->keepalive_timeout = keepalive_timeout; if (vec_len (hsm->www_root) < 2) return VNET_API_ERROR_INVALID_VALUE; @@ -84,7 +87,10 @@ hss_enable_api (u32 fifo_size, u32 cache_limit, u32 prealloc_fifos, if (hsm->app_index != ~0) return VNET_API_ERROR_APP_ALREADY_ATTACHED; - vnet_session_enable_disable (hsm->vlib_main, 1 /* turn on TCP, etc. */); + session_enable_disable_args_t args = { .is_en = 1, + .rt_engine_type = + RT_BACKEND_ENGINE_RULE_TABLE }; + vnet_session_enable_disable (hsm->vlib_main, &args); rv = hss_create (hsm->vlib_main); switch (rv) @@ -100,22 +106,41 @@ hss_enable_api (u32 fifo_size, u32 cache_limit, u32 prealloc_fifos, } /* API message handler */ -static void vl_api_http_static_enable_t_handler - (vl_api_http_static_enable_t * mp) +static void +vl_api_http_static_enable_v2_t_handler (vl_api_http_static_enable_v2_t *mp) { - vl_api_http_static_enable_reply_t *rmp; + vl_api_http_static_enable_v2_reply_t *rmp; hss_main_t *hsm = &hss_main; int rv; mp->uri[ARRAY_LEN (mp->uri) - 1] = 0; mp->www_root[ARRAY_LEN (mp->www_root) - 1] = 0; - rv = - hss_enable_api (ntohl (mp->fifo_size), ntohl (mp->cache_size_limit), - ntohl (mp->prealloc_fifos), - ntohl (mp->private_segment_size), mp->www_root, mp->uri); + rv = hss_enable_api (ntohl (mp->fifo_size), ntohl (mp->cache_size_limit), + ntohl (mp->prealloc_fifos), + ntohl (mp->private_segment_size), mp->www_root, mp->uri, + ntohl (mp->max_age), HSS_DEFAULT_KEEPALIVE_TIMEOUT); - REPLY_MACRO (VL_API_HTTP_STATIC_ENABLE_REPLY); + REPLY_MACRO (VL_API_HTTP_STATIC_ENABLE_V2_REPLY); +} + +/* API message handler */ +static void +vl_api_http_static_enable_v3_t_handler (vl_api_http_static_enable_v3_t *mp) +{ + vl_api_http_static_enable_v3_reply_t *rmp; + hss_main_t *hsm = &hss_main; + int rv; + + mp->uri[ARRAY_LEN (mp->uri) - 1] = 0; + mp->www_root[ARRAY_LEN (mp->www_root) - 1] = 0; + + rv = hss_enable_api (ntohl (mp->fifo_size), ntohl (mp->cache_size_limit), + ntohl (mp->prealloc_fifos), + ntohl (mp->private_segment_size), mp->www_root, mp->uri, + ntohl (mp->max_age), ntohl (mp->keepalive_timeout)); + + REPLY_MACRO (VL_API_HTTP_STATIC_ENABLE_V3_REPLY); } #include <http_static/http_static.api.c> diff --git a/src/plugins/http_static/http_static.h b/src/plugins/http_static/http_static.h index 8f336e83d58..5e0654fae24 100644 --- a/src/plugins/http_static/http_static.h +++ b/src/plugins/http_static/http_static.h @@ -23,6 +23,9 @@ #include <vppinfra/error.h> #include <http_static/http_cache.h> +#define HSS_DEFAULT_MAX_AGE 600 +#define HSS_DEFAULT_KEEPALIVE_TIMEOUT 60 + /** @file http_static.h * Static http server definitions */ @@ -45,13 +48,15 @@ typedef struct /** Data length */ u64 data_len; /** Current data send offset */ - u32 data_offset; + u64 data_offset; /** Need to free data in detach_cache_entry */ int free_data; /** File cache pool index */ u32 cache_pool_index; - /** Content type, e.g. text, text/javascript, etc. */ - http_content_type_t content_type; + /** Response header list */ + http_header_t *resp_headers; + /** Serialized headers to send */ + u8 *headers_buf; } hss_session_t; typedef struct hss_session_handle_ @@ -91,6 +96,7 @@ typedef struct hss_url_handler_args_ uword data_len; u8 free_vec_data; http_status_code_t sc; + http_content_type_t ct; }; }; } hss_url_handler_args_t; @@ -153,6 +159,12 @@ typedef struct u8 enable_url_handlers; /** Max cache size before LRU occurs */ u64 cache_size; + /** How long a response is considered fresh (in seconds) */ + u32 max_age; + /** Formatted max_age: "max-age=xyz" */ + u8 *max_age_formatted; + /** Timeout during which client connection will stay open */ + u32 keepalive_timeout; /** hash table of file extensions to mime types string indices */ uword *mime_type_indices_by_file_extensions; diff --git a/src/plugins/http_static/http_static_test.c b/src/plugins/http_static/http_static_test.c index 3503a1b0812..edb016f9e05 100644 --- a/src/plugins/http_static/http_static_test.c +++ b/src/plugins/http_static/http_static_test.c @@ -18,6 +18,7 @@ #include <vlibapi/api.h> #include <vlibmemory/api.h> #include <vppinfra/error.h> +#include <http_static/http_static.h> uword unformat_sw_if_index (unformat_input_t * input, va_list * args); @@ -38,10 +39,10 @@ http_static_test_main_t http_static_test_main; #include <vlibapi/vat_helper_macros.h> static int -api_http_static_enable (vat_main_t * vam) +api_http_static_enable_v2 (vat_main_t *vam) { unformat_input_t *line_input = vam->input; - vl_api_http_static_enable_t *mp; + vl_api_http_static_enable_v2_t *mp; u64 tmp; u8 *www_root = 0; u8 *uri = 0; @@ -49,6 +50,7 @@ api_http_static_enable (vat_main_t * vam) u32 private_segment_size = 0; u32 fifo_size = 8 << 10; u32 cache_size_limit = 1 << 20; + u32 max_age = HSS_DEFAULT_MAX_AGE; int ret; /* Parse args required to build the message */ @@ -88,7 +90,8 @@ api_http_static_enable (vat_main_t * vam) } cache_size_limit = (u32) tmp; } - + else if (unformat (line_input, "max-age %d", &max_age)) + ; else if (unformat (line_input, "uri %s", &uri)) ; else @@ -107,16 +110,110 @@ api_http_static_enable (vat_main_t * vam) if (uri == 0) uri = format (0, "tcp://0.0.0.0/80%c", 0); + /* Construct the API message */ + M (HTTP_STATIC_ENABLE_V2, mp); + strncpy_s ((char *) mp->www_root, 256, (const char *) www_root, 256); + strncpy_s ((char *) mp->uri, 256, (const char *) uri, 256); + mp->fifo_size = ntohl (fifo_size); + mp->cache_size_limit = ntohl (cache_size_limit); + mp->prealloc_fifos = ntohl (prealloc_fifos); + mp->private_segment_size = ntohl (private_segment_size); + mp->max_age = ntohl (max_age); + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + return ret; +} +static int +api_http_static_enable_v3 (vat_main_t *vam) +{ + unformat_input_t *line_input = vam->input; + vl_api_http_static_enable_v3_t *mp; + u64 tmp; + u8 *www_root = 0; + u8 *uri = 0; + u32 prealloc_fifos = 0; + u32 private_segment_size = 0; + u32 fifo_size = 8 << 10; + u32 cache_size_limit = 1 << 20; + u32 max_age = HSS_DEFAULT_MAX_AGE; + u32 keepalive_timeout = HSS_DEFAULT_KEEPALIVE_TIMEOUT; + int ret; + + /* Parse args required to build the message */ + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "www-root %s", &www_root)) + ; + else if (unformat (line_input, "prealloc-fifos %d", &prealloc_fifos)) + ; + else if (unformat (line_input, "private-segment-size %U", + unformat_memory_size, &tmp)) + { + if (tmp >= 0x100000000ULL) + { + errmsg ("private segment size %llu, too large", tmp); + return -99; + } + private_segment_size = (u32) tmp; + } + else if (unformat (line_input, "fifo-size %U", unformat_memory_size, + &tmp)) + { + if (tmp >= 0x100000000ULL) + { + errmsg ("fifo-size %llu, too large", tmp); + return -99; + } + fifo_size = (u32) tmp; + } + else if (unformat (line_input, "cache-size %U", unformat_memory_size, + &tmp)) + { + if (tmp < (128ULL << 10)) + { + errmsg ("cache-size must be at least 128kb"); + return -99; + } + cache_size_limit = (u32) tmp; + } + else if (unformat (line_input, "max-age %d", &max_age)) + ; + else if (unformat (line_input, "keepalive-timeout %d", + &keepalive_timeout)) + ; + else if (unformat (line_input, "uri %s", &uri)) + ; + else + { + errmsg ("unknown input `%U'", format_unformat_error, line_input); + return -99; + } + } + + if (www_root == 0) + { + errmsg ("Must specify www-root"); + return -99; + } + + if (uri == 0) + uri = format (0, "tcp://0.0.0.0/80%c", 0); /* Construct the API message */ - M (HTTP_STATIC_ENABLE, mp); + M (HTTP_STATIC_ENABLE_V3, mp); strncpy_s ((char *) mp->www_root, 256, (const char *) www_root, 256); strncpy_s ((char *) mp->uri, 256, (const char *) uri, 256); mp->fifo_size = ntohl (fifo_size); mp->cache_size_limit = ntohl (cache_size_limit); mp->prealloc_fifos = ntohl (prealloc_fifos); mp->private_segment_size = ntohl (private_segment_size); + mp->max_age = ntohl (max_age); + mp->keepalive_timeout = ntohl (keepalive_timeout); /* send it... */ S (mp); diff --git a/src/plugins/http_static/static_server.c b/src/plugins/http_static/static_server.c index 26224989332..9cc3f5dd658 100644 --- a/src/plugins/http_static/static_server.c +++ b/src/plugins/http_static/static_server.c @@ -19,6 +19,9 @@ #include <sys/stat.h> #include <unistd.h> +#include <http/http_header_names.h> +#include <http/http_content_types.h> + /** @file static_server.c * Static http server, sufficient to serve .html / .css / .js content. */ @@ -55,8 +58,6 @@ hss_session_free (hss_session_t *hs) { hss_main_t *hsm = &hss_main; - pool_put (hsm->sessions[hs->thread_index], hs); - if (CLIB_DEBUG) { u32 save_thread_index; @@ -65,6 +66,8 @@ hss_session_free (hss_session_t *hs) memset (hs, 0xfa, sizeof (*hs)); hs->thread_index = save_thread_index; } + + pool_put (hsm->sessions[hs->thread_index], hs); } /** \brief Disconnect a session @@ -83,48 +86,87 @@ start_send_data (hss_session_t *hs, http_status_code_t status) { http_msg_t msg; session_t *ts; + u8 *headers_buf = 0; + u32 n_enq; + u64 to_send; int rv; ts = session_get (hs->vpp_session_index, hs->thread_index); + if (vec_len (hs->resp_headers)) + { + headers_buf = http_serialize_headers (hs->resp_headers); + vec_free (hs->resp_headers); + msg.data.headers_offset = 0; + msg.data.headers_len = vec_len (headers_buf); + } + else + { + msg.data.headers_offset = 0; + msg.data.headers_len = 0; + } + msg.type = HTTP_MSG_REPLY; msg.code = status; - msg.content_type = hs->content_type; - msg.data.len = hs->data_len; + msg.data.body_len = hs->data_len; + msg.data.len = msg.data.body_len + msg.data.headers_len; - if (hs->data_len > hss_main.use_ptr_thresh) + if (msg.data.len > hss_main.use_ptr_thresh) { msg.data.type = HTTP_MSG_DATA_PTR; rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg); ASSERT (rv == sizeof (msg)); + if (msg.data.headers_len) + { + hs->headers_buf = headers_buf; + uword headers = pointer_to_uword (hs->headers_buf); + rv = + svm_fifo_enqueue (ts->tx_fifo, sizeof (headers), (u8 *) &headers); + ASSERT (rv == sizeof (headers)); + } + + if (!msg.data.body_len) + goto done; + uword data = pointer_to_uword (hs->data); rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (data), (u8 *) &data); - ASSERT (rv == sizeof (sizeof (data))); + ASSERT (rv == sizeof (data)); goto done; } msg.data.type = HTTP_MSG_DATA_INLINE; + msg.data.body_offset = msg.data.headers_len; rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg); ASSERT (rv == sizeof (msg)); - if (!msg.data.len) + if (msg.data.headers_len) + { + rv = svm_fifo_enqueue (ts->tx_fifo, vec_len (headers_buf), headers_buf); + ASSERT (rv == msg.data.headers_len); + vec_free (headers_buf); + } + + if (!msg.data.body_len) goto done; - rv = svm_fifo_enqueue (ts->tx_fifo, hs->data_len, hs->data); + to_send = hs->data_len; + n_enq = clib_min (svm_fifo_size (ts->tx_fifo), to_send); - if (rv != hs->data_len) + rv = svm_fifo_enqueue (ts->tx_fifo, n_enq, hs->data); + + if (rv < to_send) { - hs->data_offset = rv; + hs->data_offset = (rv > 0) ? rv : 0; svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF); } done: if (svm_fifo_set_event (ts->tx_fifo)) - session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX); + session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX); } __clib_export void @@ -142,6 +184,15 @@ hss_session_send_data (hss_url_handler_args_t *args) hs->data = args->data; hs->data_len = args->data_len; hs->free_data = args->free_vec_data; + + /* Set content type only if we have some response data */ + if (hs->data_len) + { + http_add_header (&hs->resp_headers, + http_header_name_token (HTTP_HEADER_CONTENT_TYPE), + http_content_type_token (args->ct)); + } + start_send_data (hs, args->sc); } @@ -217,7 +268,6 @@ try_url_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt, http_status_code_t sc = HTTP_STATUS_OK; hss_url_handler_args_t args = {}; uword *p, *url_table; - http_content_type_t type; int rv; if (!hsm->enable_url_handlers || !target_path) @@ -229,8 +279,6 @@ try_url_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt, target_path = format (target_path, "index.html"); } - type = content_type_from_request (target_path); - /* Look for built-in GET / POST handlers */ url_table = (rt == HTTP_REQ_GET) ? hsm->get_url_handlers : hsm->post_url_handlers; @@ -263,17 +311,24 @@ try_url_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt, { clib_warning ("builtin handler %llx hit on %s '%s' but failed!", p[0], (rt == HTTP_REQ_GET) ? "GET" : "POST", target_path); - sc = HTTP_STATUS_NOT_FOUND; + sc = HTTP_STATUS_BAD_GATEWAY; } hs->data = args.data; hs->data_len = args.data_len; hs->free_data = args.free_vec_data; - hs->content_type = type; + + /* Set content type only if we have some response data */ + if (hs->data_len) + { + http_add_header (&hs->resp_headers, + http_header_name_token (HTTP_HEADER_CONTENT_TYPE), + http_content_type_token (args.ct)); + } start_send_data (hs, sc); - if (!hs->data) + if (!hs->data_len) hss_session_disconnect_transport (hs); return 0; @@ -337,18 +392,20 @@ try_index_file (hss_main_t *hsm, hss_session_t *hs, u8 *path) } redirect = - format (0, - "Location: http%s://%U%s%s\r\n\r\n", - proto == TRANSPORT_PROTO_TLS ? "s" : "", format_ip46_address, - &endpt.ip, endpt.is_ip4, print_port ? port_str : (u8 *) "", path); + format (0, "http%s://%U%s%s", proto == TRANSPORT_PROTO_TLS ? "s" : "", + format_ip46_address, &endpt.ip, endpt.is_ip4, + print_port ? port_str : (u8 *) "", path); if (hsm->debug_level > 0) clib_warning ("redirect: %s", redirect); vec_free (port_str); - hs->data = redirect; - hs->data_len = vec_len (redirect); + http_add_header (&hs->resp_headers, + http_header_name_token (HTTP_HEADER_LOCATION), + (const char *) redirect, vec_len (redirect)); + hs->data = redirect; /* TODO: find better way */ + hs->data_len = 0; hs->free_data = 1; return HTTP_STATUS_MOVED; @@ -362,13 +419,12 @@ try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt, u8 *path, *sanitized_path; u32 ce_index; http_content_type_t type; + u8 *last_modified; /* Feature not enabled */ if (!hsm->www_root) return -1; - type = content_type_from_request (target); - /* Remove dot segments to prevent path traversal */ sanitized_path = http_path_remove_dot_segments (target); @@ -388,8 +444,8 @@ try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt, hs->data_offset = 0; - ce_index = - hss_cache_lookup_and_attach (&hsm->cache, path, &hs->data, &hs->data_len); + ce_index = hss_cache_lookup_and_attach (&hsm->cache, path, &hs->data, + &hs->data_len, &last_modified); if (ce_index == ~0) { if (!file_path_is_valid (path)) @@ -408,8 +464,8 @@ try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt, sc = try_index_file (hsm, hs, path); goto done; } - ce_index = - hss_cache_add_and_attach (&hsm->cache, path, &hs->data, &hs->data_len); + ce_index = hss_cache_add_and_attach (&hsm->cache, path, &hs->data, + &hs->data_len, &last_modified); if (ce_index == ~0) { sc = HTTP_STATUS_INTERNAL_ERROR; @@ -420,11 +476,25 @@ try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt, hs->path = path; hs->cache_pool_index = ce_index; + /* Set following headers only for happy path: + * Content-Type + * Cache-Control max-age + */ + type = content_type_from_request (target); + http_add_header (&hs->resp_headers, + http_header_name_token (HTTP_HEADER_CONTENT_TYPE), + http_content_type_token (type)); + http_add_header ( + &hs->resp_headers, http_header_name_token (HTTP_HEADER_CACHE_CONTROL), + (const char *) hsm->max_age_formatted, vec_len (hsm->max_age_formatted)); + http_add_header (&hs->resp_headers, + http_header_name_token (HTTP_HEADER_LAST_MODIFIED), + (const char *) last_modified, vec_len (last_modified)); + done: vec_free (sanitized_path); - hs->content_type = type; start_send_data (hs, sc); - if (!hs->data) + if (!hs->data_len) hss_session_disconnect_transport (hs); return 0; @@ -459,6 +529,8 @@ hss_ts_rx_callback (session_t *ts) if (hs->free_data) vec_free (hs->data); hs->data = 0; + hs->resp_headers = 0; + vec_free (hs->headers_buf); /* Read the http message header */ rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg); @@ -467,6 +539,9 @@ hss_ts_rx_callback (session_t *ts) if (msg.type != HTTP_MSG_REQUEST || (msg.method_type != HTTP_REQ_GET && msg.method_type != HTTP_REQ_POST)) { + http_add_header (&hs->resp_headers, + http_header_name_token (HTTP_HEADER_ALLOW), + http_token_lit ("GET, POST")); start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED); goto done; } @@ -531,7 +606,8 @@ static int hss_ts_tx_callback (session_t *ts) { hss_session_t *hs; - u32 to_send; + u32 n_enq; + u64 to_send; int rv; hs = hss_session_get (ts->thread_index, ts->opaque); @@ -539,7 +615,9 @@ hss_ts_tx_callback (session_t *ts) return 0; to_send = hs->data_len - hs->data_offset; - rv = svm_fifo_enqueue (ts->tx_fifo, to_send, hs->data + hs->data_offset); + n_enq = clib_min (svm_fifo_size (ts->tx_fifo), to_send); + + rv = svm_fifo_enqueue (ts->tx_fifo, n_enq, hs->data + hs->data_offset); if (rv <= 0) { @@ -554,7 +632,7 @@ hss_ts_tx_callback (session_t *ts) } if (svm_fifo_set_event (ts->tx_fifo)) - session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX); + session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX); return 0; } @@ -648,6 +726,7 @@ hss_ts_cleanup (session_t *s, session_cleanup_ntf_t ntf) hs->data = 0; hs->data_offset = 0; hs->free_data = 0; + vec_free (hs->headers_buf); vec_free (hs->path); hss_session_free (hs); @@ -671,7 +750,7 @@ hss_attach () hss_main_t *hsm = &hss_main; u64 options[APP_OPTIONS_N_OPTIONS]; vnet_app_attach_args_t _a, *a = &_a; - u32 segment_size = 128 << 20; + u64 segment_size = 128 << 20; clib_memset (a, 0, sizeof (*a)); clib_memset (options, 0, sizeof (options)); @@ -728,6 +807,7 @@ hss_listen (void) vnet_listen_args_t _a, *a = &_a; char *uri = "tcp://0.0.0.0/80"; u8 need_crypto; + transport_endpt_ext_cfg_t *ext_cfg; int rv; clib_memset (a, 0, sizeof (*a)); @@ -744,17 +824,21 @@ hss_listen (void) sep.transport_proto = TRANSPORT_PROTO_HTTP; clib_memcpy (&a->sep_ext, &sep, sizeof (sep)); + ext_cfg = session_endpoint_add_ext_cfg ( + &a->sep_ext, TRANSPORT_ENDPT_EXT_CFG_HTTP, sizeof (ext_cfg->opaque)); + ext_cfg->opaque = hsm->keepalive_timeout; + if (need_crypto) { - session_endpoint_alloc_ext_cfg (&a->sep_ext, - TRANSPORT_ENDPT_EXT_CFG_CRYPTO); - a->sep_ext.ext_cfg->crypto.ckpair_index = hsm->ckpair_index; + ext_cfg = session_endpoint_add_ext_cfg ( + &a->sep_ext, TRANSPORT_ENDPT_EXT_CFG_CRYPTO, + sizeof (transport_endpt_crypto_cfg_t)); + ext_cfg->crypto.ckpair_index = hsm->ckpair_index; } rv = vnet_listen (a); - if (need_crypto) - clib_mem_free (a->sep_ext.ext_cfg); + session_endpoint_free_ext_cfgs (&a->sep_ext); return rv; } @@ -798,6 +882,8 @@ hss_create (vlib_main_t *vm) if (hsm->enable_url_handlers) hss_url_handlers_init (hsm); + hsm->max_age_formatted = format (0, "max-age=%d", hsm->max_age); + return 0; } @@ -818,6 +904,8 @@ hss_create_command_fn (vlib_main_t *vm, unformat_input_t *input, hsm->private_segment_size = 0; hsm->fifo_size = 0; hsm->cache_size = 10 << 20; + hsm->max_age = HSS_DEFAULT_MAX_AGE; + hsm->keepalive_timeout = HSS_DEFAULT_KEEPALIVE_TIMEOUT; /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) @@ -842,6 +930,9 @@ hss_create_command_fn (vlib_main_t *vm, unformat_input_t *input, ; else if (unformat (line_input, "debug %d", &hsm->debug_level)) ; + else if (unformat (line_input, "keepalive-timeout %d", + &hsm->keepalive_timeout)) + ; else if (unformat (line_input, "debug")) hsm->debug_level = 1; else if (unformat (line_input, "ptr-thresh %U", unformat_memory_size, @@ -849,6 +940,8 @@ hss_create_command_fn (vlib_main_t *vm, unformat_input_t *input, ; else if (unformat (line_input, "url-handlers")) hsm->enable_url_handlers = 1; + else if (unformat (line_input, "max-age %d", &hsm->max_age)) + ; else { error = clib_error_return (0, "unknown input `%U'", @@ -877,7 +970,10 @@ no_input: goto done; } - vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */ ); + session_enable_disable_args_t args = { .is_en = 1, + .rt_engine_type = + RT_BACKEND_ENGINE_RULE_TABLE }; + vnet_session_enable_disable (vm, &args); if ((rv = hss_create (vm))) { @@ -900,14 +996,16 @@ done: * http static server www-root /tmp/www uri tcp://0.0.0.0/80 cache-size 2m * @cliend * @cliexcmd{http static server www-root <path> [prealloc-fios <nn>] - * [private-segment-size <nnMG>] [fifo-size <nbytes>] [uri <uri>]} + * [private-segment-size <nnMG>] [fifo-size <nbytes>] [uri <uri>] + * [keepalive-timeout <nn>]} ?*/ VLIB_CLI_COMMAND (hss_create_command, static) = { .path = "http static server", .short_help = "http static server www-root <path> [prealloc-fifos <nn>]\n" - "[private-segment-size <nnMG>] [fifo-size <nbytes>] [uri <uri>]\n" - "[ptr-thresh <nn>] [url-handlers] [debug [nn]]\n", + "[private-segment-size <nnMG>] [fifo-size <nbytes>] [max-age <nseconds>]\n" + "[uri <uri>] [ptr-thresh <nn>] [url-handlers] [debug [nn]]\n" + "[keepalive-timeout <nn>]\n", .function = hss_create_command_fn, }; @@ -917,7 +1015,7 @@ format_hss_session (u8 *s, va_list *args) hss_session_t *hs = va_arg (*args, hss_session_t *); int __clib_unused verbose = va_arg (*args, int); - s = format (s, "\n path %s, data length %u, data_offset %u", + s = format (s, "\n path %s, data length %llu, data_offset %llu", hs->path ? hs->path : (u8 *) "[none]", hs->data_len, hs->data_offset); return s; diff --git a/src/plugins/ikev2/CMakeLists.txt b/src/plugins/ikev2/CMakeLists.txt index 568271ed7d9..dd2b49d6651 100644 --- a/src/plugins/ikev2/CMakeLists.txt +++ b/src/plugins/ikev2/CMakeLists.txt @@ -27,6 +27,7 @@ add_vpp_plugin(ikev2 ikev2_crypto.c ikev2_format.c ikev2_payload.c + ikev2_handoff.c API_FILES ikev2_types.api diff --git a/src/plugins/ikev2/ikev2.api b/src/plugins/ikev2/ikev2.api index de276e7f3ea..e2ff8fb8268 100644 --- a/src/plugins/ikev2/ikev2.api +++ b/src/plugins/ikev2/ikev2.api @@ -658,6 +658,12 @@ counters ikev2 { units "packets"; description "IKE AUTH SA requests received"; }; + handoff { + severity info; + type counter64; + units "packets"; + description "IKE packets handoff"; + }; }; paths { "/err/ikev2-ip4" "ike"; diff --git a/src/plugins/ikev2/ikev2.c b/src/plugins/ikev2/ikev2.c index 9bea2c96d12..f66469a24d1 100644 --- a/src/plugins/ikev2/ikev2.c +++ b/src/plugins/ikev2/ikev2.c @@ -97,6 +97,7 @@ format_ikev2_gen_sa_error (u8 * s, va_list * args) typedef enum { IKEV2_NEXT_IP4_LOOKUP, + IKEV2_NEXT_IP4_HANDOFF, IKEV2_NEXT_IP4_ERROR_DROP, IKEV2_IP4_N_NEXT, } ikev2_ip4_next_t; @@ -104,6 +105,7 @@ typedef enum typedef enum { IKEV2_NEXT_IP6_LOOKUP, + IKEV2_NEXT_IP6_HANDOFF, IKEV2_NEXT_IP6_ERROR_DROP, IKEV2_IP6_N_NEXT, } ikev2_ip6_next_t; @@ -3187,6 +3189,7 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; u16 nexts[VLIB_FRAME_SIZE], *next = nexts; ikev2_main_per_thread_data_t *ptd = ikev2_get_per_thread_data (); + u32 thread_index = vm->thread_index; ikev2_stats_t _stats, *stats = &_stats; int res; @@ -3213,6 +3216,14 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node, int ip_hdr_sz = 0; int is_req = 0; + if (PREDICT_TRUE (thread_index != km->handoff_thread)) + { + vlib_node_increment_counter (vm, node->node_index, + IKEV2_ERROR_HANDOFF, 1); + + next[0] = is_ip4 ? IKEV2_NEXT_IP4_HANDOFF : IKEV2_NEXT_IP6_HANDOFF; + goto out; + } if (natt) { u8 *ptr = vlib_buffer_get_current (b0); @@ -3723,6 +3734,8 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node, ikev2_delete_sa (ptd, sa0); } + + out: if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && (b0->flags & VLIB_BUFFER_IS_TRACED))) { @@ -3775,6 +3788,7 @@ VLIB_REGISTER_NODE (ikev2_node_ip4,static) = { .n_next_nodes = IKEV2_IP4_N_NEXT, .next_nodes = { [IKEV2_NEXT_IP4_LOOKUP] = "ip4-lookup", + [IKEV2_NEXT_IP4_HANDOFF] = "ikev2-ip4-handoff", [IKEV2_NEXT_IP4_ERROR_DROP] = "error-drop", }, }; @@ -3792,6 +3806,7 @@ VLIB_REGISTER_NODE (ikev2_node_ip4_natt,static) = { .n_next_nodes = IKEV2_IP4_N_NEXT, .next_nodes = { [IKEV2_NEXT_IP4_LOOKUP] = "ip4-lookup", + [IKEV2_NEXT_IP4_HANDOFF] = "ikev2-ip4-natt-handoff", [IKEV2_NEXT_IP4_ERROR_DROP] = "error-drop", }, }; @@ -3809,6 +3824,7 @@ VLIB_REGISTER_NODE (ikev2_node_ip6,static) = { .n_next_nodes = IKEV2_IP6_N_NEXT, .next_nodes = { [IKEV2_NEXT_IP6_LOOKUP] = "ip6-lookup", + [IKEV2_NEXT_IP4_HANDOFF] = "ikev2-ip6-handoff", [IKEV2_NEXT_IP6_ERROR_DROP] = "error-drop", }, }; @@ -5126,6 +5142,8 @@ ikev2_init (vlib_main_t * vm) km->liveness_period = IKEV2_LIVENESS_PERIOD_CHECK; km->liveness_max_retries = IKEV2_LIVENESS_RETRIES; + km->handoff_thread = vlib_num_workers () ? 1 : 0; + return 0; } @@ -5133,6 +5151,31 @@ VLIB_INIT_FUNCTION (ikev2_init) = { .runs_after = VLIB_INITS ("ipsec_init", "ipsec_punt_init"), }; +static clib_error_t * +ikev2_config (vlib_main_t *vm, unformat_input_t *input) +{ + ikev2_main_t *km = &ikev2_main; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "handoff-thread %d", &km->handoff_thread)) + { + if (km->handoff_thread > vlib_num_workers ()) + { + return clib_error_return (0, "wrong handoff-thread %d", + km->handoff_thread); + } + } + else + return clib_error_return (0, "unknown input `%U'", format_unformat_error, + input); + } + + return 0; +} + +VLIB_CONFIG_FUNCTION (ikev2_config, "ikev2"); + static u8 ikev2_mngr_process_child_sa (ikev2_sa_t * sa, ikev2_child_sa_t * csa, u8 del_old_ids) @@ -5447,6 +5490,7 @@ ikev2_send_informational_request (ikev2_sa_t * sa) } dp = sa->dst_port ? sa->dst_port : ikev2_get_port (sa); + ikev2_send_ike (km->vlib_main, src, dst, bi0, len, ikev2_get_port (sa), dp, sa->sw_if_index); } @@ -5625,6 +5669,15 @@ ikev2_lazy_init (ikev2_main_t *km) if (!km->dns_resolve_name_ptr) ikev2_log_error ("cannot load symbols from dns plugin"); + km->handoff_ip4_fq_index = + vlib_frame_queue_main_init (ikev2_node_ip4.index, 0); + + km->handoff_ip4_natt_fq_index = + vlib_frame_queue_main_init (ikev2_node_ip4_natt.index, 0); + + km->handoff_ip6_fq_index = + vlib_frame_queue_main_init (ikev2_node_ip6.index, 0); + /* wake up ikev2 process */ vlib_process_signal_event (vlib_get_first_main (), ikev2_mngr_process_node.index, 0, 0); diff --git a/src/plugins/ikev2/ikev2_api.c b/src/plugins/ikev2/ikev2_api.c index c9608aa660b..e09bde3cbe2 100644 --- a/src/plugins/ikev2/ikev2_api.c +++ b/src/plugins/ikev2/ikev2_api.c @@ -577,6 +577,7 @@ vl_api_ikev2_child_sa_dump_t_handler (vl_api_ikev2_child_sa_dump_t * mp) vec_foreach (child, sa->childs) { u32 child_sa_index = child - sa->childs; + sai = ikev2_encode_sa_index (sai, tkm - im->per_thread_data); send_child_sa (child, mp, child_sa_index, sai); } } diff --git a/src/plugins/ikev2/ikev2_crypto.c b/src/plugins/ikev2/ikev2_crypto.c index 3d4ad0a28ed..58167e2322e 100644 --- a/src/plugins/ikev2/ikev2_crypto.c +++ b/src/plugins/ikev2/ikev2_crypto.c @@ -481,15 +481,14 @@ ikev2_encrypt_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa, int BN_bn2binpad (const BIGNUM * a, unsigned char *to, int tolen) { - int r = BN_bn2bin (a, to); + int r = BN_num_bytes (a); ASSERT (tolen >= r); int pad = tolen - r; if (pad) { - vec_insert (to, pad, 0); clib_memset (to, 0, pad); - vec_dec_len (to, pad); } + BN_bn2bin (a, to + pad); return tolen; } #endif diff --git a/src/plugins/ikev2/ikev2_handoff.c b/src/plugins/ikev2/ikev2_handoff.c new file mode 100644 index 00000000000..8f55985bce8 --- /dev/null +++ b/src/plugins/ikev2/ikev2_handoff.c @@ -0,0 +1,165 @@ +/* + * 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. + */ + +#include <vlib/vlib.h> +#include <vnet/vnet.h> +#include <ikev2/ikev2_priv.h> + +extern ikev2_main_t ikev2_main; + +#define foreach_ikev2_handoff_error _ (CONGESTION_DROP, "congestion drop") + +typedef enum +{ +#define _(sym, str) IKEV2_HANDOFF_ERROR_##sym, + foreach_ikev2_handoff_error +#undef _ + IKEV2_HANDOFF_N_ERROR, +} ikev2_handoff_error_t; + +static char *ikev2_handoff_error_strings[] = { +#define _(sym, string) string, + foreach_ikev2_handoff_error +#undef _ +}; + +typedef struct ikev2_handoff_trace_t_ +{ + u32 current_worker_index; + u32 next_worker_index; +} ikev2_handoff_trace_t; + +u8 * +format_ikev2_handoff_trace (u8 *s, va_list *args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + + ikev2_handoff_trace_t *t = va_arg (*args, ikev2_handoff_trace_t *); + s = format (s, "ikev2 handoff %d to %d", t->current_worker_index, + t->next_worker_index); + return s; +} + +static_always_inline uword +ikev2_handoff_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame, u32 fq_index) +{ + ikev2_main_t *km = &ikev2_main; + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; + u16 thread_indices[VLIB_FRAME_SIZE], *ti; + u32 n_enq, n_left_from, *from; + u32 this_thread; + + this_thread = vm->thread_index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + vlib_get_buffers (vm, from, bufs, n_left_from); + + b = bufs; + ti = thread_indices; + + while (n_left_from > 0) + { + ti[0] = km->handoff_thread; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + b[0]->flags & VLIB_BUFFER_IS_TRACED)) + { + ikev2_handoff_trace_t *t = + vlib_add_trace (vm, node, b[0], sizeof (*t)); + t->current_worker_index = this_thread; + t->next_worker_index = ti[0]; + } + n_left_from--; + ti++; + b++; + } + + n_enq = vlib_buffer_enqueue_to_thread (vm, node, fq_index, from, + thread_indices, frame->n_vectors, 1); + + if (n_enq < frame->n_vectors) + vlib_node_increment_counter (vm, node->node_index, + IKEV2_HANDOFF_ERROR_CONGESTION_DROP, + frame->n_vectors - n_enq); + return n_enq; +} + +/* Do worker handoff based on the ikev2's thread_index */ +VLIB_NODE_FN (ikev2_ip4_handoff) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *from_frame) +{ + ikev2_main_t *km = &ikev2_main; + + return ikev2_handoff_inline (vm, node, from_frame, km->handoff_ip4_fq_index); +} + +VLIB_NODE_FN (ikev2_ip4_natt_handoff) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *from_frame) +{ + ikev2_main_t *km = &ikev2_main; + + return ikev2_handoff_inline (vm, node, from_frame, + km->handoff_ip4_natt_fq_index); +} + +VLIB_NODE_FN (ikev2_ip6_handoff) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *from_frame) +{ + ikev2_main_t *km = &ikev2_main; + + return ikev2_handoff_inline (vm, node, from_frame, km->handoff_ip6_fq_index); +} + +VLIB_REGISTER_NODE (ikev2_ip4_handoff) = { + .name = "ikev2-ip4-handoff", + .vector_size = sizeof (u32), + .format_trace = format_ikev2_handoff_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(ikev2_handoff_error_strings), + .error_strings = ikev2_handoff_error_strings, + .n_next_nodes = 1, + .next_nodes = { + [0] = "error-drop", + }, +}; + +VLIB_REGISTER_NODE (ikev2_ip4_natt_handoff) = { + .name = "ikev2-ip4-natt-handoff", + .vector_size = sizeof (u32), + .format_trace = format_ikev2_handoff_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(ikev2_handoff_error_strings), + .error_strings = ikev2_handoff_error_strings, + .n_next_nodes = 1, + .next_nodes = { + [0] = "error-drop", + }, +}; + +VLIB_REGISTER_NODE (ikev2_ip6_handoff) = { + .name = "ikev2-ip6-handoff", + .vector_size = sizeof (u32), + .format_trace = format_ikev2_handoff_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(ikev2_handoff_error_strings), + .error_strings = ikev2_handoff_error_strings, + .n_next_nodes = 1, + .next_nodes = { + [0] = "error-drop", + }, +}; diff --git a/src/plugins/ikev2/ikev2_priv.h b/src/plugins/ikev2/ikev2_priv.h index 0639809e9b1..96313182552 100644 --- a/src/plugins/ikev2/ikev2_priv.h +++ b/src/plugins/ikev2/ikev2_priv.h @@ -571,6 +571,12 @@ typedef struct /* punt handle for IPsec NATT IPSEC_PUNT_IP4_SPI_UDP_0 reason */ vlib_punt_hdl_t punt_hdl; + /** Worker handoff */ + u32 handoff_thread; + u32 handoff_ip4_fq_index; + u32 handoff_ip4_natt_fq_index; + u32 handoff_ip6_fq_index; + } ikev2_main_t; extern ikev2_main_t ikev2_main; diff --git a/src/plugins/linux-cp/lcp_interface.c b/src/plugins/linux-cp/lcp_interface.c index b1c0ad5a022..61665ad4146 100644 --- a/src/plugins/linux-cp/lcp_interface.c +++ b/src/plugins/linux-cp/lcp_interface.c @@ -1001,7 +1001,8 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name, clib_max (1, lcp_get_default_num_queues (0 /* is_tx */)), .num_tx_queues = clib_max (1, lcp_get_default_num_queues (1 /* is_tx */)), - .id = hw->hw_if_index, + .id = ~0, + .auto_id_offset = 4096, .sw_if_index = ~0, .rx_ring_sz = 256, .tx_ring_sz = 256, diff --git a/src/plugins/mactime/builtins.c b/src/plugins/mactime/builtins.c index c487d0375bf..f726d3c03ed 100644 --- a/src/plugins/mactime/builtins.c +++ b/src/plugins/mactime/builtins.c @@ -147,6 +147,7 @@ handle_get_mactime (hss_url_handler_args_t *args) args->data = s; args->data_len = vec_len (s); + args->ct = HTTP_CONTENT_APP_JSON; args->free_vec_data = 1; return HSS_URL_HANDLER_OK; } diff --git a/src/plugins/map/ip6_map_t.c b/src/plugins/map/ip6_map_t.c index 51853d619e6..f8d894a013a 100644 --- a/src/plugins/map/ip6_map_t.c +++ b/src/plugins/map/ip6_map_t.c @@ -151,9 +151,8 @@ ip6_map_t_icmp (vlib_main_t * vm, vnet_buffer (p0)->map_t.map_domain_index); ctx0.d = d0; ctx0.sender_port = 0; - if (!ip6_get_port - (vm, p0, ip60, p0->current_length, NULL, &ctx0.sender_port, - NULL, NULL, NULL, NULL)) + if (!ip6_get_port (vm, p0, ip60, p0->current_length, NULL, + &ctx0.sender_port, NULL, NULL, NULL, NULL, NULL)) { // In case of 1:1 mapping, we don't care about the port if (!(d0->ea_bits_len == 0 && d0->rules)) diff --git a/src/plugins/marvell/CMakeLists.txt b/src/plugins/marvell/CMakeLists.txt deleted file mode 100644 index b48ac72aa08..00000000000 --- a/src/plugins/marvell/CMakeLists.txt +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) 2018 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. - -if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*)") - return() -endif() - -find_path(MUSDK_INCLUDE_DIR NAMES mv_std.h) -find_library(MUSDK_LIB NAMES libmusdk.a) - -if(MUSDK_INCLUDE_DIR AND MUSDK_LIB) - get_filename_component(MUSDK_LIB_DIR ${MUSDK_LIB} DIRECTORY) - set(MUSDK_LINK_FLAGS "-Wl,--whole-archive,${MUSDK_LIB_DIR}/libmusdk.a,--no-whole-archive") - add_vpp_plugin(marvell - SOURCES - plugin.c - pp2/cli.c - pp2/format.c - pp2/input.c - pp2/output.c - pp2/pp2.c - pp2/pp2_api.c - - API_FILES - pp2/pp2.api - - API_TEST_SOURCES - pp2/pp2_test.c - - LINK_FLAGS - ${MUSDK_LINK_FLAGS} - ) - include_directories(${MUSDK_INCLUDE_DIR}) - message(STATUS "Found Marvell MUSDK in ${MUSDK_INCLUDE_DIR}") -else() - message(WARNING "Marvell MUSDK not found - marvell_plugin disabled") -endif() diff --git a/src/plugins/marvell/README.rst b/src/plugins/marvell/README.rst deleted file mode 100644 index 19cf1c49d0e..00000000000 --- a/src/plugins/marvell/README.rst +++ /dev/null @@ -1,85 +0,0 @@ -Marvell device plugin -===================== - -Overview --------- - -This plugins provides native device support for Marvell PP2 network -device, by use of Marvell Usermode SDK -(`MUSDK <https://github.com/MarvellEmbeddedProcessors/musdk-marvell>`__). -Code is developed and tested on -`MACCHIATObin <http://macchiatobin.net>`__ board. - -Prerequisites -------------- - -Plugins depends on installed MUSDK and Marvell provided linux -`kernel <https://github.com/MarvellEmbeddedProcessors/linux-marvell>`__ -with MUSDK provided kernel patches (see ``patches/linux`` in musdk repo -and relevant documentation. Kernel version used: **4.14.22 -armada-18.09.3** MUSDK version used: **armada-18.09.3** Following kernel -modules from MUSDK must be loaded for plugin to work: \* -``musdk_cma.ko`` \* ``mv_pp_uio.ko`` - -Musdk 18.09.3 compilation steps -------------------------------- - -:: - - ./bootstrap - ./configure --prefix=/opt/vpp/external/aarch64/ CFLAGS="-Wno-error=unused-result -g -fPIC" --enable-shared=no - sed -i -e 's/marvell,mv-pp-uio/generic-uio/' modules/pp2/mv_pp_uio.c - sed -i -e 's/O_CREAT/O_CREAT, S_IRUSR | S_IWUSR/' src/lib/file_utils.c - make - sudo make install - -Usage ------ - -Interface Creation -~~~~~~~~~~~~~~~~~~ - -Interfaces are dynamically created with following CLI: - -:: - - create interface marvell pp2 name eth0 - set interface state mv-ppio-0/0 up - -Where ``eth0`` is linux interface name and ``mv-ppio-X/Y`` is VPP -interface name where X is PP2 device ID and Y is PPIO ID Interface needs -to be assigned to MUSDK in FDT configuration and linux interface state -must be up. - -Interface Deletion -~~~~~~~~~~~~~~~~~~ - -Interface can be deleted with following CLI: - -:: - - delete interface marvell pp2 <interface name> - -Interface Statistics -~~~~~~~~~~~~~~~~~~~~ - -Interface statistics can be displayed with -``sh hardware-interface mv-ppio0/0`` command. - -Interaction with DPDK plugin -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This plugin doesn’t have any dependency on DPDK or DPDK plugin but it -can work with DPDK plugin enabled or disabled. It is observed that -performance is better around 30% when DPDK plugin is disabled, as DPDK -plugin registers own buffer manager, which needs to deal with additional -metadata in each packet. - -DPKD plugin can be disabled by adding following config to the -startup.conf. - -:: - - plugins { - dpdk_plugin.so { disable } - } diff --git a/src/plugins/marvell/pp2/cli.c b/src/plugins/marvell/pp2/cli.c deleted file mode 100644 index 5072a3c035b..00000000000 --- a/src/plugins/marvell/pp2/cli.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - *------------------------------------------------------------------ - * Copyright (c) 2018 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 <stdint.h> -#include <net/if.h> -#include <sys/ioctl.h> -#include <inttypes.h> - -#include <vlib/vlib.h> -#include <vlib/unix/unix.h> -#include <vnet/ethernet/ethernet.h> - -#include <marvell/pp2/pp2.h> - -static clib_error_t * -mrvl_pp2_create_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - mrvl_pp2_create_if_args_t args = { 0 }; - unsigned int val; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "name %s", &args.name)) - ; - else if (unformat (line_input, "rx-queue-size %u", &val)) - args.rx_q_sz = val; - else if (unformat (line_input, "tx-queue-size %u", &val)) - args.tx_q_sz = val; - else - return clib_error_return (0, "unknown input `%U'", - format_unformat_error, input); - } - unformat_free (line_input); - - - mrvl_pp2_create_if (&args); - - vec_free (args.name); - - return args.error; -} - -VLIB_CLI_COMMAND (mrvl_pp2_create_command, static) = { - .path = "create interface marvell pp2", - .short_help = "create interface marvell pp2 [name <ifname>] [rx-queue-size slots] [tx-queue-size slots]", - .function = mrvl_pp2_create_command_fn, -}; - -static clib_error_t * -mrvl_pp2_delete_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - u32 sw_if_index = ~0; - vnet_hw_interface_t *hw; - mrvl_pp2_main_t *mm = &mrvl_pp2_main; - mrvl_pp2_if_t *dif; - vnet_main_t *vnm = vnet_get_main (); - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "sw_if_index %d", &sw_if_index)) - ; - else if (unformat (line_input, "%U", unformat_vnet_sw_interface, - vnm, &sw_if_index)) - ; - else - return clib_error_return (0, "unknown input `%U'", - format_unformat_error, input); - } - unformat_free (line_input); - - if (sw_if_index == ~0) - return clib_error_return (0, - "please specify interface name or sw_if_index"); - - hw = vnet_get_sup_hw_interface_api_visible_or_null (vnm, sw_if_index); - if (hw == NULL || mrvl_pp2_device_class.index != hw->dev_class_index) - return clib_error_return (0, "not a Marvell PP2 interface"); - - dif = pool_elt_at_index (mm->interfaces, hw->dev_instance); - - mrvl_pp2_delete_if (dif); - - return 0; -} - -VLIB_CLI_COMMAND (mrvl_pp2_delete_command, static) = { - .path = "delete interface marvell pp2", - .short_help = "delete interface marvell pp2 " - "{<interface> | sw_if_index <sw_idx>}", - .function = mrvl_pp2_delete_command_fn, -}; - -clib_error_t * -mrvl_pp2_cli_init (vlib_main_t * vm) -{ - /* initialize binary API */ - mrvl_pp2_plugin_api_hookup (vm); - - return 0; -} - -VLIB_INIT_FUNCTION (mrvl_pp2_cli_init); - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/marvell/pp2/format.c b/src/plugins/marvell/pp2/format.c deleted file mode 100644 index 877010ea561..00000000000 --- a/src/plugins/marvell/pp2/format.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - *------------------------------------------------------------------ - * Copyright (c) 2018 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 <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <sys/ioctl.h> - -#include <vlib/vlib.h> -#include <vlib/unix/unix.h> -#include <vnet/plugin/plugin.h> -#include <marvell/pp2/pp2.h> - -static inline u32 -mrvl_get_u32_bits (void *start, int offset, int first, int last) -{ - u32 value = *(u32 *) (((u8 *) start) + offset); - if ((last == 0) && (first == 31)) - return value; - value >>= last; - value &= (1 << (first - last + 1)) - 1; - return value; -} - -u8 * -format_mrvl_pp2_interface_name (u8 * s, va_list * args) -{ - mrvl_pp2_main_t *ppm = &mrvl_pp2_main; - u32 dev_instance = va_arg (*args, u32); - mrvl_pp2_if_t *ppif = pool_elt_at_index (ppm->interfaces, dev_instance); - return format (s, "mv-ppio-%d/%d", ppif->ppio->pp2_id, ppif->ppio->port_id); -} - -#define foreach_ppio_statistics_entry \ - _(rx_packets) \ - _(rx_fullq_dropped) \ - _(rx_bm_dropped) \ - _(rx_early_dropped) \ - _(rx_fifo_dropped) \ - _(rx_cls_dropped) \ - _(tx_packets) - -#define foreach_ppio_inq_statistics_entry \ - _(enq_desc) \ - _(drop_early) \ - _(drop_fullq) \ - _(drop_bm) - -#define foreach_ppio_outq_statistics_entry \ - _(enq_desc) \ - _(enq_dec_to_ddr) \ - _(enq_buf_to_ddr) \ - _(deq_desc) - -u8 * -format_mrvl_pp2_interface (u8 * s, va_list * args) -{ - mrvl_pp2_main_t *ppm = &mrvl_pp2_main; - u32 dev_instance = va_arg (*args, u32); - u32 indent = format_get_indent (s); - mrvl_pp2_if_t *ppif = pool_elt_at_index (ppm->interfaces, dev_instance); - struct pp2_ppio_statistics stat; - int i; - u8 *s2 = 0; - - pp2_ppio_get_statistics (ppif->ppio, &stat, 0); - -#define _(c) if (stat.c) \ - s2 = format (s2, "\n%U%-25U%16Ld", \ - format_white_space, indent + 2, \ - format_c_identifier, #c, stat.c); - foreach_ppio_statistics_entry; - - if (vec_len (s2)) - s = format (s, "Interface statistics:%v", s2); - vec_reset_length (s2); - - vec_foreach_index (i, ppif->inqs) - { - struct pp2_ppio_inq_statistics stat = { 0 }; - pp2_ppio_inq_get_statistics (ppif->ppio, 0, i, &stat, 0); - - foreach_ppio_inq_statistics_entry; - - if (vec_len (s2)) - s = format (s, "\n%UInput queue %u statistics:%v", - format_white_space, indent, i, s2); - vec_reset_length (s2); - } - vec_foreach_index (i, ppif->outqs) - { - struct pp2_ppio_outq_statistics stat = { 0 }; - - pp2_ppio_outq_get_statistics (ppif->ppio, i, &stat, 0); - - foreach_ppio_outq_statistics_entry; - - if (vec_len (s2)) - s = format (s, "\n%UOutput queue %u statistics:%v", - format_white_space, indent, i, s2); - vec_reset_length (s2); - } -#undef _ - vec_free (s2); - return s; -} - -#define foreach_pp2_rx_desc_field \ - _(0x00, 6, 0, l3_offset) \ - _(0x00, 12, 8, ip_hdlen) \ - _(0x00, 14, 13, ec) \ - _(0x00, 15, 15, es) \ - _(0x00, 19, 16, pool_id) \ - _(0x00, 21, 21, hwf_sync) \ - _(0x00, 22, 22, l4_chk_ok) \ - _(0x00, 23, 23, ip_frg) \ - _(0x00, 24, 24, ipv4_hdr_err) \ - _(0x00, 27, 25, l4_info) \ - _(0x00, 30, 28, l3_info) \ - _(0x00, 31, 31, buf_header) \ - _(0x04, 5, 0, lookup_id) \ - _(0x04, 8, 6, cpu_code) \ - _(0x04, 9, 9, pppoe) \ - _(0x04, 11, 10, l3_cast_info) \ - _(0x04, 13, 12, l2_cast_info) \ - _(0x04, 15, 14, vlan_info) \ - _(0x04, 31, 16, byte_count) \ - _(0x08, 11, 0, gem_port_id) \ - _(0x08, 13, 12, color) \ - _(0x08, 14, 14, gop_sop_u) \ - _(0x08, 15, 15, key_hash_enable) \ - _(0x08, 31, 16, l4chk) \ - _(0x0c, 31, 0, timestamp) \ - _(0x10, 31, 0, buf_phys_ptr_lo) \ - _(0x14, 7, 0, buf_phys_ptr_hi) \ - _(0x14, 31, 8, key_hash) \ - _(0x18, 31, 0, buf_virt_ptr_lo) \ - _(0x1c, 7, 0, buf_virt_ptr_hi) \ - _(0x1c, 14, 8, buf_qset_no) \ - _(0x1c, 15, 15, buf_type) \ - _(0x1c, 21, 16, mod_dscp) \ - _(0x1c, 24, 22, mod_pri) \ - _(0x1c, 25, 25, mdscp) \ - _(0x1c, 26, 26, mpri) \ - _(0x1c, 27, 27, mgpid) \ - _(0x1c, 31, 29, port_num) - -u8 * -format_mrvl_pp2_input_trace (u8 * s, va_list * args) -{ - vlib_main_t *vm = va_arg (*args, vlib_main_t *); - vlib_node_t *node = va_arg (*args, vlib_node_t *); - mrvl_pp2_input_trace_t *t = va_arg (*args, mrvl_pp2_input_trace_t *); - vnet_main_t *vnm = vnet_get_main (); - vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, t->hw_if_index); - u32 indent = format_get_indent (s); - struct pp2_ppio_desc *d = &t->desc; - u32 r32; - - s = format (s, "pp2: %v (%d) next-node %U", - hi->name, t->hw_if_index, format_vlib_next_node_name, vm, - node->index, t->next_index); - s = format (s, "\n%U", format_white_space, indent + 2); - -#define _(a, b, c, n) \ - r32 = mrvl_get_u32_bits (d, a, b, c); \ - if (r32 > 9) \ - s = format (s, "%s %u (0x%x)", #n, r32, r32); \ - else \ - s = format (s, "%s %u", #n,r32); \ - if (format_get_indent (s) > 72) \ - s = format (s, "\n%U", format_white_space, indent + 2); \ - else s = format (s, " "); - - foreach_pp2_rx_desc_field; -#undef _ - return s; -} - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/marvell/pp2/input.c b/src/plugins/marvell/pp2/input.c deleted file mode 100644 index 2545f91becb..00000000000 --- a/src/plugins/marvell/pp2/input.c +++ /dev/null @@ -1,392 +0,0 @@ -/* - *------------------------------------------------------------------ - * Copyright (c) 2018 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. - *------------------------------------------------------------------ - */ - -#define _GNU_SOURCE -#include <stdint.h> -#include <net/if.h> -#include <sys/ioctl.h> -#include <sys/uio.h> - -#include <vlib/vlib.h> -#include <vlib/unix/unix.h> -#include <vnet/ethernet/ethernet.h> -#include <vnet/devices/devices.h> -#include <vnet/interface/rx_queue_funcs.h> - -#include <marvell/pp2/pp2.h> - -#define foreach_mrvl_pp2_input_error \ - _(PPIO_RECV, "pp2_ppio_recv error") \ - _(BPOOL_GET_NUM_BUFFS, "pp2_bpool_get_num_buffs error") \ - _(BPOOL_PUT_BUFFS, "pp2_bpool_put_buffs error") \ - _(BUFFER_ALLOC, "buffer alloc error") \ - _(MAC_CE, "MAC error (CRC error)") \ - _(MAC_OR, "overrun error") \ - _(MAC_RSVD, "unknown MAC error") \ - _(MAC_RE, "resource error") \ - _(IP_HDR, "ip4 header error") - -typedef enum -{ -#define _(f,s) MRVL_PP2_INPUT_ERROR_##f, - foreach_mrvl_pp2_input_error -#undef _ - MRVL_PP2_INPUT_N_ERROR, -} mrvl_pp2_input_error_t; - -static __clib_unused char *mrvl_pp2_input_error_strings[] = { -#define _(n,s) s, - foreach_mrvl_pp2_input_error -#undef _ -}; - -static_always_inline void -mrvl_pp2_input_trace (vlib_main_t * vm, vlib_node_runtime_t * node, u32 next0, - vlib_buffer_t * b0, uword * n_trace, - mrvl_pp2_if_t * ppif, struct pp2_ppio_desc *d) -{ - if (PREDICT_TRUE ( - vlib_trace_buffer (vm, node, next0, b0, /* follow_chain */ 0))) - { - mrvl_pp2_input_trace_t *tr; - vlib_set_trace_count (vm, node, --(*n_trace)); - tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); - tr->next_index = next0; - tr->hw_if_index = ppif->hw_if_index; - clib_memcpy_fast (&tr->desc, d, sizeof (struct pp2_ppio_desc)); - } -} - -static_always_inline u16 -mrvl_pp2_set_buf_data_len_flags (vlib_buffer_t * b, struct pp2_ppio_desc *d, - u32 add_flags) -{ - u16 len; - len = pp2_ppio_inq_desc_get_pkt_len (d); - b->total_length_not_including_first_buffer = 0; - b->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID | add_flags; - - if (add_flags & VNET_BUFFER_F_L2_HDR_OFFSET_VALID) - vnet_buffer (b)->l2_hdr_offset = 2; - - if (add_flags & VNET_BUFFER_F_L3_HDR_OFFSET_VALID) - { - u16 offset = DM_RXD_GET_L3_OFF (d); - vnet_buffer (b)->l3_hdr_offset = offset; - b->current_data = offset; - b->current_length = len - offset + 2; - } - else - { - b->current_data = 2; - b->current_length = len; - } - - if (add_flags & VNET_BUFFER_F_L3_HDR_OFFSET_VALID) - vnet_buffer (b)->l4_hdr_offset = vnet_buffer (b)->l3_hdr_offset + - DM_RXD_GET_IPHDR_LEN (d) * 4; - - return len; -} - -static_always_inline u16 -mrvl_pp2_next_from_desc (vlib_node_runtime_t * node, struct pp2_ppio_desc * d, - vlib_buffer_t * b, u32 * next) -{ - u8 l3_info; - /* ES bit set means MAC error - drop and count */ - if (PREDICT_FALSE (DM_RXD_GET_ES (d))) - { - *next = VNET_DEVICE_INPUT_NEXT_DROP; - u8 ec = DM_RXD_GET_EC (d); - if (ec == 0) - b->error = node->errors[MRVL_PP2_INPUT_ERROR_MAC_CE]; - else if (ec == 1) - b->error = node->errors[MRVL_PP2_INPUT_ERROR_MAC_OR]; - else if (ec == 2) - b->error = node->errors[MRVL_PP2_INPUT_ERROR_MAC_RSVD]; - else if (ec == 3) - b->error = node->errors[MRVL_PP2_INPUT_ERROR_MAC_RE]; - return mrvl_pp2_set_buf_data_len_flags (b, d, 0); - } - l3_info = DM_RXD_GET_L3_PRS_INFO (d); - - /* ipv4 packet can be value 1, 2 or 3 */ - if (PREDICT_TRUE ((l3_info - 1) < 3)) - { - if (PREDICT_FALSE (DM_RXD_GET_L3_IP4_HDR_ERR (d) != 0)) - { - *next = VNET_DEVICE_INPUT_NEXT_DROP; - b->error = node->errors[MRVL_PP2_INPUT_ERROR_IP_HDR]; - return mrvl_pp2_set_buf_data_len_flags (b, d, 0); - } - *next = VNET_DEVICE_INPUT_NEXT_IP4_NCS_INPUT; - return mrvl_pp2_set_buf_data_len_flags - (b, d, - VNET_BUFFER_F_L2_HDR_OFFSET_VALID | - VNET_BUFFER_F_L3_HDR_OFFSET_VALID | - VNET_BUFFER_F_L4_HDR_OFFSET_VALID | VNET_BUFFER_F_IS_IP4); - } - - /* ipv4 packet can be value 4 or 5 */ - if (PREDICT_TRUE ((l3_info - 4) < 2)) - { - *next = VNET_DEVICE_INPUT_NEXT_IP6_INPUT; - return mrvl_pp2_set_buf_data_len_flags - (b, d, - VNET_BUFFER_F_L2_HDR_OFFSET_VALID | - VNET_BUFFER_F_L3_HDR_OFFSET_VALID | - VNET_BUFFER_F_L4_HDR_OFFSET_VALID | VNET_BUFFER_F_IS_IP6); - } - - *next = VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT; - return mrvl_pp2_set_buf_data_len_flags (b, d, - VNET_BUFFER_F_L2_HDR_OFFSET_VALID); -} - -static_always_inline uword -mrvl_pp2_device_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * frame, mrvl_pp2_if_t * ppif, - u16 qid) -{ - vnet_main_t *vnm = vnet_get_main (); - mrvl_pp2_main_t *ppm = &mrvl_pp2_main; - u32 thread_index = vm->thread_index; - mrvl_pp2_inq_t *inq = vec_elt_at_index (ppif->inqs, qid); - uword n_trace = vlib_get_trace_count (vm, node); - mrvl_pp2_per_thread_data_t *ptd = - vec_elt_at_index (ppm->per_thread_data, thread_index); - u32 next_index = VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT; - u32 sw_if_index[VLIB_N_RX_TX]; - u32 n_rx_packets = 0; - u32 n_rx_bytes = 0; - u32 *to_next = 0; - struct pp2_ppio_desc *d; - u16 n_desc = VLIB_FRAME_SIZE; - u32 n_bufs; - u32 *buffers; - int i; - - vec_validate_aligned (ptd->descs, n_desc, CLIB_CACHE_LINE_BYTES); - if (PREDICT_FALSE (pp2_ppio_recv (ppif->ppio, 0, qid, ptd->descs, &n_desc))) - { - vlib_error_count (vm, node->node_index, MRVL_PP2_INPUT_ERROR_PPIO_RECV, - 1); - n_desc = 0; - } - n_rx_packets = n_desc; - - for (i = 0; i < n_desc; i++) - ptd->buffers[i] = pp2_ppio_inq_desc_get_cookie (&ptd->descs[i]); - - d = ptd->descs; - buffers = ptd->buffers; - sw_if_index[VLIB_RX] = ppif->sw_if_index; - sw_if_index[VLIB_TX] = (u32) ~ 0; - while (n_desc) - { - u32 n_left_to_next; - vlib_buffer_t *b0, *b1; - u32 bi0, bi1; - u32 next0, next1; - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - while (n_desc >= 4 && n_left_to_next >= 2) - { - /* prefetch */ - bi0 = buffers[0]; - bi1 = buffers[1]; - to_next[0] = bi0; - to_next[1] = bi1; - b0 = vlib_get_buffer (vm, bi0); - b1 = vlib_get_buffer (vm, bi1); - - if (PREDICT_TRUE (ppif->per_interface_next_index == ~0)) - { - n_rx_bytes += mrvl_pp2_next_from_desc (node, d, b0, &next0); - n_rx_bytes += mrvl_pp2_next_from_desc (node, d + 1, b1, &next1); - vnet_feature_start_device_input (ppif->sw_if_index, &next0, b0); - vnet_feature_start_device_input (ppif->sw_if_index, &next1, b1); - } - else - { - n_rx_bytes += mrvl_pp2_set_buf_data_len_flags (b0, d, 0); - n_rx_bytes += mrvl_pp2_set_buf_data_len_flags (b1, d + 1, 0); - next0 = next1 = ppif->per_interface_next_index; - } - - clib_memcpy_fast (vnet_buffer (b0)->sw_if_index, sw_if_index, - sizeof (sw_if_index)); - clib_memcpy_fast (vnet_buffer (b1)->sw_if_index, sw_if_index, - sizeof (sw_if_index)); - - if (PREDICT_FALSE (n_trace > 0)) - { - mrvl_pp2_input_trace (vm, node, next0, b0, &n_trace, ppif, d); - if (n_trace > 0) - mrvl_pp2_input_trace (vm, node, next1, b1, &n_trace, ppif, - d + 1); - } - - to_next += 2; - n_left_to_next -= 2; - d += 2; - buffers += 2; - n_desc -= 2; - - /* enqueue */ - vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next, - n_left_to_next, bi0, bi1, next0, - next1); - - } - while (n_desc && n_left_to_next) - { - u32 bi0 = buffers[0]; - to_next[0] = bi0; - b0 = vlib_get_buffer (vm, bi0); - - if (PREDICT_TRUE (ppif->per_interface_next_index == ~0)) - { - n_rx_bytes += mrvl_pp2_next_from_desc (node, d, b0, &next0); - vnet_feature_start_device_input (ppif->sw_if_index, &next0, b0); - } - else - { - n_rx_bytes += mrvl_pp2_set_buf_data_len_flags (b0, d, 0); - next0 = ppif->per_interface_next_index; - } - - clib_memcpy_fast (vnet_buffer (b0)->sw_if_index, sw_if_index, - sizeof (sw_if_index)); - - if (PREDICT_FALSE (n_trace > 0)) - mrvl_pp2_input_trace (vm, node, next0, b0, &n_trace, ppif, d); - - to_next += 1; - n_left_to_next--; - d++; - buffers++; - n_desc--; - - /* enqueue */ - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, - n_left_to_next, bi0, next0); - } - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - vlib_increment_combined_counter (vnm-> - interface_main.combined_sw_if_counters + - VNET_INTERFACE_COUNTER_RX, thread_index, - ppif->hw_if_index, n_rx_packets, - n_rx_bytes); - - if (PREDICT_FALSE (pp2_bpool_get_num_buffs (inq->bpool, &n_bufs))) - { - vlib_error_count (vm, node->node_index, - MRVL_PP2_INPUT_ERROR_BPOOL_GET_NUM_BUFFS, 1); - goto done; - } - - n_bufs = inq->size - n_bufs; - while (n_bufs >= MRVL_PP2_BUFF_BATCH_SZ) - { - u16 n_alloc, i; - struct buff_release_entry *e = ptd->bre; - u32 *buffers = ptd->buffers; - - n_alloc = vlib_buffer_alloc (vm, ptd->buffers, MRVL_PP2_BUFF_BATCH_SZ); - i = n_alloc; - - if (PREDICT_FALSE (n_alloc == 0)) - { - vlib_error_count (vm, node->node_index, - MRVL_PP2_INPUT_ERROR_BUFFER_ALLOC, 1); - goto done; - } - - while (i--) - { - u32 bi = buffers[0]; - vlib_buffer_t *b = vlib_get_buffer (vm, bi); - e->buff.addr = vlib_buffer_get_pa (vm, b) - 64; - e->buff.cookie = bi; - e->bpool = inq->bpool; - e++; - buffers++; - } - - i = n_alloc; - if (PREDICT_FALSE (pp2_bpool_put_buffs (ptd->hif, ptd->bre, &i))) - { - vlib_error_count (vm, node->node_index, - MRVL_PP2_INPUT_ERROR_BPOOL_PUT_BUFFS, 1); - vlib_buffer_free (vm, ptd->buffers, n_alloc); - goto done; - } - - if (PREDICT_FALSE (i != n_alloc)) - vlib_buffer_free (vm, ptd->buffers + i, n_alloc - i); - - n_bufs -= i; - } - -done: - return n_rx_packets; -} - -uword -mrvl_pp2_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - u32 n_rx = 0; - mrvl_pp2_main_t *ppm = &mrvl_pp2_main; - vnet_hw_if_rxq_poll_vector_t *pv; - - pv = vnet_hw_if_get_rxq_poll_vector (vm, node); - - for (int i = 0; i < vec_len (pv); i++) - { - mrvl_pp2_if_t *ppif; - ppif = vec_elt_at_index (ppm->interfaces, pv[i].dev_instance); - if (ppif->flags & MRVL_PP2_IF_F_ADMIN_UP) - n_rx += - mrvl_pp2_device_input_inline (vm, node, frame, ppif, pv[i].queue_id); - } - return n_rx; -} - -VLIB_REGISTER_NODE (mrvl_pp2_input_node) = { - .function = mrvl_pp2_input_fn, - .flags = VLIB_NODE_FLAG_TRACE_SUPPORTED, - .name = "mrvl-pp2-input", - .sibling_of = "device-input", - .format_trace = format_mrvl_pp2_input_trace, - .type = VLIB_NODE_TYPE_INPUT, - .state = VLIB_NODE_STATE_POLLING, - .n_errors = MRVL_PP2_INPUT_N_ERROR, - .error_strings = mrvl_pp2_input_error_strings, -}; - - - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/marvell/pp2/output.c b/src/plugins/marvell/pp2/output.c deleted file mode 100644 index 911b2f55a17..00000000000 --- a/src/plugins/marvell/pp2/output.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - *------------------------------------------------------------------ - * Copyright (c) 2018 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 <stdint.h> -#include <net/if.h> -#include <sys/ioctl.h> -#include <sys/uio.h> - -#include <vlib/vlib.h> -#include <vlib/unix/unix.h> -#include <vnet/ethernet/ethernet.h> -#include <vnet/devices/devices.h> - -#include <marvell/pp2/pp2.h> - -uword -mrvl_pp2_interface_tx (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - mrvl_pp2_main_t *ppm = &mrvl_pp2_main; - vnet_interface_output_runtime_t *rd = (void *) node->runtime_data; - mrvl_pp2_if_t *ppif = pool_elt_at_index (ppm->interfaces, rd->dev_instance); - u32 thread_index = vm->thread_index; - mrvl_pp2_per_thread_data_t *ptd = - vec_elt_at_index (ppm->per_thread_data, thread_index); - u8 qid = thread_index; - mrvl_pp2_outq_t *outq = vec_elt_at_index (ppif->outqs, qid); - u32 *buffers = vlib_frame_vector_args (frame); - u16 n_desc = frame->n_vectors, n_left = n_desc, n_sent = n_desc, n_done; - struct pp2_ppio_desc *d; - u16 mask = outq->size - 1; - - if (PREDICT_FALSE (pp2_ppio_get_num_outq_done (ppif->ppio, ptd->hif, qid, - &n_done))) - { - n_done = 0; - vlib_error_count (vm, node->node_index, - MRVL_PP2_TX_ERROR_PPIO_GET_NUM_OUTQ_DONE, 1); - } - - if (n_done) - { - u16 n_free = clib_min (n_done, outq->size - (outq->tail & mask)); - vlib_buffer_free (vm, outq->buffers + (outq->tail & mask), n_free); - if (PREDICT_FALSE (n_free < n_done)) - vlib_buffer_free (vm, outq->buffers, n_done - n_free); - outq->tail += n_done; - } - - vec_validate_aligned (ptd->descs, n_left, CLIB_CACHE_LINE_BYTES); - d = ptd->descs; - while (n_left) - { - u32 bi0 = buffers[0]; - vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0); - u64 paddr = vlib_buffer_get_pa (vm, b0); - - pp2_ppio_outq_desc_reset (d); - pp2_ppio_outq_desc_set_phys_addr (d, paddr + b0->current_data); - pp2_ppio_outq_desc_set_pkt_offset (d, 0); - pp2_ppio_outq_desc_set_pkt_len (d, b0->current_length); - d++; - buffers++; - n_left--; - } - - if (pp2_ppio_send (ppif->ppio, ptd->hif, qid, ptd->descs, &n_sent)) - { - n_sent = 0; - vlib_error_count (vm, node->node_index, MRVL_PP2_TX_ERROR_PPIO_SEND, 1); - } - - /* free unsent buffers */ - if (PREDICT_FALSE (n_sent != n_desc)) - { - vlib_buffer_free (vm, vlib_frame_vector_args (frame) + n_sent, - frame->n_vectors - n_sent); - vlib_error_count (vm, node->node_index, MRVL_PP2_TX_ERROR_NO_FREE_SLOTS, - frame->n_vectors - n_sent); - } - - /* store buffer index for each enqueued packet into the ring - so we can know what to free after packet is sent */ - if (n_sent) - { - u16 slot = outq->head & mask; - buffers = vlib_frame_vector_args (frame); - u16 n_copy = clib_min (outq->size - slot, n_sent); - - vlib_buffer_copy_indices (outq->buffers + slot, buffers, n_copy); - if (PREDICT_FALSE (n_copy < n_sent)) - clib_memcpy_fast (outq->buffers, buffers + n_copy, - (n_sent - n_copy) * sizeof (u32)); - - outq->head += n_sent; - } - - return n_sent; -} - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/marvell/pp2/pp2.c b/src/plugins/marvell/pp2/pp2.c deleted file mode 100644 index 030ab9b4496..00000000000 --- a/src/plugins/marvell/pp2/pp2.c +++ /dev/null @@ -1,403 +0,0 @@ -/* - *------------------------------------------------------------------ - * Copyright (c) 2018 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 <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <sys/ioctl.h> - -#include <vlib/vlib.h> -#include <vlib/unix/unix.h> -#include <vnet/plugin/plugin.h> -#include <marvell/pp2/pp2.h> -#include <vnet/interface/rx_queue_funcs.h> - -/* size of DMA memory used by musdk (not used for buffers) */ -#define MV_SYS_DMA_MEM_SZ (2 << 20) -/* number of HIFs reserved (first X) */ -#define NUM_HIFS_RSVD 4 -/* number of buffer pools reserved (first X) */ -#define NUM_BPOOLS_RSVD 7 - -mrvl_pp2_main_t mrvl_pp2_main; -extern vnet_device_class_t ppa2_device_class; - -static void -mrvl_pp2_main_deinit () -{ - mrvl_pp2_main_t *ppm = &mrvl_pp2_main; - int i; - vec_foreach_index (i, ppm->per_thread_data) - { - mrvl_pp2_per_thread_data_t *ptd = vec_elt_at_index (ppm->per_thread_data, - i); - if (ptd->hif) - pp2_hif_deinit (ptd->hif); - vec_free (ptd->descs); - } - vec_free (ppm->per_thread_data); - pp2_deinit (); - mv_sys_dma_mem_destroy (); -} - -static clib_error_t * -mrvl_pp2_main_init () -{ - mrvl_pp2_main_t *ppm = &mrvl_pp2_main; - vlib_thread_main_t *tm = vlib_get_thread_main (); - clib_error_t *err = 0; - struct pp2_init_params init_params = { 0 }; - int i, rv; - u8 *s = 0; - - rv = mv_sys_dma_mem_init (MV_SYS_DMA_MEM_SZ); - if (rv) - return clib_error_return (0, "mv_sys_dma_mem_init failed, rv = %u", rv); - - init_params.hif_reserved_map = ((1 << NUM_HIFS_RSVD) - 1); - init_params.bm_pool_reserved_map = ((1 << NUM_BPOOLS_RSVD) - 1); - rv = pp2_init (&init_params); - if (rv) - { - err = clib_error_return (0, "mrvl_pp2_init failed, rv = %u", rv); - goto done; - } - - vec_validate_aligned (ppm->per_thread_data, tm->n_vlib_mains - 1, - CLIB_CACHE_LINE_BYTES); - - vec_foreach_index (i, ppm->per_thread_data) - { - mrvl_pp2_per_thread_data_t *ptd = vec_elt_at_index (ppm->per_thread_data, - i); - struct pp2_hif_params hif_params = { 0 }; - vec_reset_length (s); - s = format (s, "hif-%d%c", NUM_HIFS_RSVD + i, 0); - hif_params.match = (char *) s; - hif_params.out_size = 2048; /* FIXME */ - if (pp2_hif_init (&hif_params, &ptd->hif)) - { - err = clib_error_return (0, "hif '%s' init failed", s); - goto done; - } - } - -done: - if (err) - mrvl_pp2_main_deinit (); - vec_free (s); - return err; -} - -static u32 -mrvl_pp2_eth_flag_change (vnet_main_t * vnm, vnet_hw_interface_t * hi, - u32 flags) -{ - /* nothing for now */ - return 0; -} - -void -mrvl_pp2_delete_if (mrvl_pp2_if_t * ppif) -{ - vlib_main_t *vm = vlib_get_main (); - vnet_main_t *vnm = vnet_get_main (); - mrvl_pp2_main_t *ppm = &mrvl_pp2_main; - mrvl_pp2_outq_t *outq; - mrvl_pp2_inq_t *inq; - - if (ppif->hw_if_index != ~0) - ethernet_delete_interface (vnm, ppif->hw_if_index); - - if (ppif->ppio) - { - pp2_ppio_disable (ppif->ppio); - pp2_ppio_deinit (ppif->ppio); - } - - /* free buffers hanging in the tx ring */ - vec_foreach (outq, ppif->outqs) - { - while (outq->tail < outq->head) - { - u16 slot = outq->tail & (outq->size - 1); - vlib_buffer_free (vm, outq->buffers + slot, 1); - outq->tail++; - } - vec_free (outq->buffers); - } - vec_free (ppif->outqs); - - /* free buffers hangin in the rx buffer pool */ - vec_foreach (inq, ppif->inqs) - if (inq->bpool) - { - u32 n_bufs = 0; - pp2_bpool_get_num_buffs (inq->bpool, &n_bufs); - while (n_bufs--) - { - struct pp2_buff_inf binf; - if (pp2_bpool_get_buff (ppm->per_thread_data[0].hif, inq->bpool, - &binf) == 0) - { - u32 bi = binf.cookie; - vlib_buffer_free (vm, &bi, 1); - } - } - pp2_bpool_deinit (inq->bpool); - } - vec_free (ppif->inqs); - - - pool_put (ppm->interfaces, ppif); - - if (pool_elts (ppm->interfaces) == 0) - mrvl_pp2_main_deinit (); -} - -void -mrvl_pp2_create_if (mrvl_pp2_create_if_args_t * args) -{ - vlib_main_t *vm = vlib_get_main (); - vnet_main_t *vnm = vnet_get_main (); - vlib_thread_main_t *tm = vlib_get_thread_main (); - vnet_eth_interface_registration_t eir = {}; - mrvl_pp2_main_t *ppm = &mrvl_pp2_main; - struct pp2_bpool_params bpool_params = { 0 }; - struct pp2_ppio_params ppio_params = { 0 }; - struct pp2_ppio_inq_params inq_params = { 0 }; - vnet_sw_interface_t *sw; - mrvl_pp2_if_t *ppif = 0; - u8 pp2_id, port_id, *s = 0; - eth_addr_t mac_addr; - u8 n_outqs, n_inqs = 1; - int i; - - if (tm->n_vlib_mains > PP2_PPIO_MAX_NUM_OUTQS) - { - args->rv = VNET_API_ERROR_INIT_FAILED; - args->error = clib_error_return (0, "number of threads (main + workers)" - " is bigger than number of output " - "queues (%u)", PP2_PPIO_MAX_NUM_OUTQS); - return; - } - n_outqs = tm->n_vlib_mains; - - /* defaults */ - args->tx_q_sz = args->tx_q_sz ? args->tx_q_sz : 2 * VLIB_FRAME_SIZE; - args->rx_q_sz = args->rx_q_sz ? args->rx_q_sz : 2 * VLIB_FRAME_SIZE; - - if (vec_len (ppm->per_thread_data) == 0) - { - if ((args->error = mrvl_pp2_main_init ()) != 0) - { - args->rv = VNET_API_ERROR_INIT_FAILED; - return; - } - } - - pool_get_zero (ppm->interfaces, ppif); - ppif->dev_instance = ppif - ppm->interfaces; - ppif->hw_if_index = ~0; - vec_validate_aligned (ppif->inqs, n_inqs - 1, CLIB_CACHE_LINE_BYTES); - vec_validate_aligned (ppif->outqs, n_outqs - 1, CLIB_CACHE_LINE_BYTES); - - for (i = 0; i < n_inqs; i++) - { - mrvl_pp2_inq_t *inq = vec_elt_at_index (ppif->inqs, i); - inq->size = args->rx_q_sz; - } - for (i = 0; i < n_outqs; i++) - { - mrvl_pp2_outq_t *outq = vec_elt_at_index (ppif->outqs, i); - outq->size = args->tx_q_sz; - vec_validate_aligned (outq->buffers, outq->size, CLIB_CACHE_LINE_BYTES); - } - - if (pp2_netdev_get_ppio_info ((char *) args->name, &pp2_id, &port_id)) - { - args->rv = VNET_API_ERROR_INVALID_INTERFACE; - args->error = clib_error_return (0, "Invalid interface '%s'", - args->name); - goto error; - } - - /* FIXME bpool bit select per pp */ - s = format (s, "pool-%d:%d%c", pp2_id, pp2_id + 8, 0); - bpool_params.match = (char *) s; - bpool_params.buff_len = vlib_buffer_get_default_data_size (vm); - /* FIXME +64 ? */ - if (pp2_bpool_init (&bpool_params, &ppif->inqs[0].bpool)) - { - args->rv = VNET_API_ERROR_INIT_FAILED; - args->error = clib_error_return (0, "bpool '%s' init failed", s); - goto error; - } - vec_reset_length (s); - - s = format (s, "ppio-%d:%d%c", pp2_id, port_id, 0); - ppio_params.match = (char *) s; - ppio_params.type = PP2_PPIO_T_NIC; - inq_params.size = args->rx_q_sz; - ppio_params.inqs_params.num_tcs = 1; - ppio_params.inqs_params.tcs_params[0].pkt_offset = 0; - ppio_params.inqs_params.tcs_params[0].num_in_qs = n_inqs; - ppio_params.inqs_params.tcs_params[0].inqs_params = &inq_params; - ppio_params.inqs_params.tcs_params[0].pools[0][0] = ppif->inqs[0].bpool; - ppio_params.outqs_params.num_outqs = n_outqs; - for (i = 0; i < n_outqs; i++) - { - ppio_params.outqs_params.outqs_params[i].weight = 1; - ppio_params.outqs_params.outqs_params[i].size = args->tx_q_sz; - } - if (pp2_ppio_init (&ppio_params, &ppif->ppio)) - { - args->rv = VNET_API_ERROR_INIT_FAILED; - args->error = clib_error_return (0, "ppio '%s' init failed", s); - goto error; - } - vec_reset_length (s); - - if (pp2_ppio_get_mac_addr (ppif->ppio, mac_addr)) - { - args->rv = VNET_API_ERROR_INIT_FAILED; - args->error = - clib_error_return (0, "%s: pp2_ppio_get_mac_addr failed", s); - goto error; - } - - eir.dev_class_index = mrvl_pp2_device_class.index; - eir.dev_instance = ppif->dev_instance; - eir.address = mac_addr; - eir.cb.flag_change = mrvl_pp2_eth_flag_change; - ppif->hw_if_index = vnet_eth_register_interface (vnm, &eir); - - sw = vnet_get_hw_sw_interface (vnm, ppif->hw_if_index); - ppif->sw_if_index = sw->sw_if_index; - ppif->per_interface_next_index = ~0; - args->sw_if_index = sw->sw_if_index; - vnet_hw_if_set_input_node (vnm, ppif->hw_if_index, - mrvl_pp2_input_node.index); - /* FIXME: only one RX queue ? */ - ppif->inqs[0].queue_index = vnet_hw_if_register_rx_queue ( - vnm, ppif->hw_if_index, 0, VNET_HW_IF_RXQ_THREAD_ANY); - - vnet_hw_if_set_rx_queue_mode (vnm, ppif->inqs[0].queue_index, - VNET_HW_IF_RX_MODE_POLLING); - vnet_hw_if_update_runtime_data (vnm, ppif->hw_if_index); - vnet_hw_interface_set_flags (vnm, ppif->hw_if_index, - VNET_HW_INTERFACE_FLAG_LINK_UP); - goto done; - -error: - mrvl_pp2_delete_if (ppif); -done: - vec_free (s); -} - -static clib_error_t * -mrvl_pp2_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, - u32 flags) -{ - mrvl_pp2_main_t *ppm = &mrvl_pp2_main; - vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); - mrvl_pp2_if_t *ppif = pool_elt_at_index (ppm->interfaces, hw->dev_instance); - static clib_error_t *error = 0; - int is_up = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) != 0; - int rv; - - if (is_up) - rv = pp2_ppio_enable (ppif->ppio); - else - rv = pp2_ppio_disable (ppif->ppio); - - if (rv) - return clib_error_return (0, "failed to %s interface", - is_up ? "enable" : "disable"); - - if (is_up) - ppif->flags |= MRVL_PP2_IF_F_ADMIN_UP; - else - ppif->flags &= ~MRVL_PP2_IF_F_ADMIN_UP; - - return error; -} - -static void -mrvl_pp2_clear_interface_counters (u32 instance) -{ - mrvl_pp2_main_t *ppm = &mrvl_pp2_main; - mrvl_pp2_if_t *ppif = pool_elt_at_index (ppm->interfaces, instance); - struct pp2_ppio_statistics stats; - - pp2_ppio_get_statistics (ppif->ppio, &stats, 1); -} - -static void -mrvl_pp2_set_interface_next_node (vnet_main_t * vnm, u32 hw_if_index, - u32 node_index) -{ - mrvl_pp2_main_t *ppm = &mrvl_pp2_main; - vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); - mrvl_pp2_if_t *ppif = pool_elt_at_index (ppm->interfaces, hw->dev_instance); - - /* Shut off redirection */ - if (node_index == ~0) - { - ppif->per_interface_next_index = node_index; - return; - } - - ppif->per_interface_next_index = - vlib_node_add_next (vlib_get_main (), mrvl_pp2_input_node.index, - node_index); -} - -static char *mrvl_pp2_tx_func_error_strings[] = { -#define _(n,s) s, - foreach_mrvl_pp2_tx_func_error -#undef _ -}; - -VNET_DEVICE_CLASS (mrvl_pp2_device_class,) = -{ - .name = "Marvell PPv2 interface", - .format_device_name = format_mrvl_pp2_interface_name, - .format_device = format_mrvl_pp2_interface, - .tx_function = mrvl_pp2_interface_tx, - .tx_function_n_errors = MRVL_PP2_TX_N_ERROR, - .tx_function_error_strings = mrvl_pp2_tx_func_error_strings, - .admin_up_down_function = mrvl_pp2_interface_admin_up_down, - .clear_counters = mrvl_pp2_clear_interface_counters, - .rx_redirect_to_node = mrvl_pp2_set_interface_next_node, -}; - -static clib_error_t * -mrvl_pp2_init (vlib_main_t * vm) -{ - return 0; -} - -VLIB_INIT_FUNCTION (mrvl_pp2_init); - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/marvell/pp2/pp2.h b/src/plugins/marvell/pp2/pp2.h deleted file mode 100644 index abb8e573a37..00000000000 --- a/src/plugins/marvell/pp2/pp2.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - *------------------------------------------------------------------ - * Copyright (c) 2018 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. - *------------------------------------------------------------------ - */ - -#define MVCONF_DBG_LEVEL 0 -#define MVCONF_PP2_BPOOL_COOKIE_SIZE 32 -#define MVCONF_PP2_BPOOL_DMA_ADDR_SIZE 64 -#define MVCONF_DMA_PHYS_ADDR_T_SIZE 64 -#define MVCONF_SYS_DMA_UIO -#define MVCONF_TYPES_PUBLIC -#define MVCONF_DMA_PHYS_ADDR_T_PUBLIC - -#include <vlib/vlib.h> - -#include "mv_std.h" -#include "env/mv_sys_dma.h" -#include "drivers/mv_pp2.h" -#include <drivers/mv_pp2_bpool.h> -#include <drivers/mv_pp2_ppio.h> - -typedef struct -{ - CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); - u16 size; - u32 queue_index; - struct pp2_bpool *bpool; -} mrvl_pp2_inq_t; - -typedef struct -{ - CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); - u16 size; - u32 *buffers; - u16 head; - u16 tail; -} mrvl_pp2_outq_t; - -typedef struct -{ - CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); - u32 flags; -#define MRVL_PP2_IF_F_ADMIN_UP (1 << 0) - struct pp2_ppio *ppio; - u32 per_interface_next_index; - - mrvl_pp2_inq_t *inqs; - mrvl_pp2_outq_t *outqs; - - u32 dev_instance; - u32 sw_if_index; - u32 hw_if_index; -} mrvl_pp2_if_t; - -#define MRVL_PP2_BUFF_BATCH_SZ VLIB_FRAME_SIZE - -typedef struct -{ - CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); - struct pp2_hif *hif; - struct pp2_ppio_desc *descs; - struct buff_release_entry bre[MRVL_PP2_BUFF_BATCH_SZ]; - u32 buffers[VLIB_FRAME_SIZE]; -} mrvl_pp2_per_thread_data_t; - -typedef struct -{ - mrvl_pp2_if_t *interfaces; - mrvl_pp2_per_thread_data_t *per_thread_data; - - /* API message ID base */ - u16 msg_id_base; -} mrvl_pp2_main_t; - -extern vnet_device_class_t mrvl_pp2_device_class; -extern mrvl_pp2_main_t mrvl_pp2_main; - -typedef struct -{ - u8 *name; - u16 rx_q_sz; - u16 tx_q_sz; - - /* return */ - i32 rv; - u32 sw_if_index; - clib_error_t *error; -} mrvl_pp2_create_if_args_t; - -void mrvl_pp2_create_if (mrvl_pp2_create_if_args_t * args); -void mrvl_pp2_delete_if (mrvl_pp2_if_t * dfif); -clib_error_t *mrvl_pp2_plugin_api_hookup (vlib_main_t * vm); - -/* output.c */ - -#define foreach_mrvl_pp2_tx_func_error \ - _(NO_FREE_SLOTS, "no free tx slots") \ - _(PPIO_SEND, "pp2_ppio_send errors") \ - _(PPIO_GET_NUM_OUTQ_DONE, "pp2_ppio_get_num_outq_done errors") - -typedef enum -{ -#define _(f,s) MRVL_PP2_TX_ERROR_##f, - foreach_mrvl_pp2_tx_func_error -#undef _ - MRVL_PP2_TX_N_ERROR, -} mrvl_pp2_tx_func_error_t; - -uword mrvl_pp2_interface_tx (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * frame); - -/* input.c */ - -typedef struct -{ - u32 next_index; - u32 hw_if_index; - struct pp2_ppio_desc desc; -} mrvl_pp2_input_trace_t; - -extern vlib_node_registration_t mrvl_pp2_input_node; - -/* format.c */ -format_function_t format_mrvl_pp2_input_trace; -format_function_t format_mrvl_pp2_interface; -format_function_t format_mrvl_pp2_interface_name; - - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/marvell/pp2/pp2_api.c b/src/plugins/marvell/pp2/pp2_api.c deleted file mode 100644 index c1f3a9e1d1d..00000000000 --- a/src/plugins/marvell/pp2/pp2_api.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - *------------------------------------------------------------------ - * Copyright (c) 2019 Arm Limited. - * 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 <vlib/unix/unix.h> -#include <vnet/ethernet/ethernet.h> - -#include <marvell/pp2/pp2.h> - -#include <vlibapi/api.h> -#include <vlibmemory/api.h> - -/* define message IDs */ -#include <marvell/pp2/pp2.api_enum.h> -#include <marvell/pp2/pp2.api_types.h> - -#define REPLY_MSG_ID_BASE (pp2->msg_id_base) -#include <vlibapi/api_helper_macros.h> - -static void -vl_api_mrvl_pp2_create_t_handler (vl_api_mrvl_pp2_create_t * mp) -{ - mrvl_pp2_main_t *pp2 = &mrvl_pp2_main; - mrvl_pp2_create_if_args_t args = { 0 }; - vl_api_mrvl_pp2_create_reply_t *rmp; - int rv; - - args.name = format (0, "%s", mp->if_name); - args.rx_q_sz = ntohs (mp->rx_q_sz); - args.tx_q_sz = ntohs (mp->tx_q_sz); - mrvl_pp2_create_if (&args); - rv = args.rv; - vec_free (args.name); - if (args.error) - { - clib_error_free (args.error); - } - REPLY_MACRO2 (VL_API_MRVL_PP2_CREATE_REPLY, - ({ rmp->sw_if_index = ntohl (args.sw_if_index); })); -} - -static void -vl_api_mrvl_pp2_delete_t_handler (vl_api_mrvl_pp2_delete_t * mp) -{ - vnet_main_t *vnm = vnet_get_main (); - vnet_hw_interface_t *hw; - mrvl_pp2_main_t *pp2 = &mrvl_pp2_main; - vl_api_mrvl_pp2_delete_reply_t *rmp; - mrvl_pp2_if_t *dif; - int rv = 0; - mp->sw_if_index = ntohl (mp->sw_if_index); - hw = vnet_get_sup_hw_interface (vnm, mp->sw_if_index); - if (hw == NULL || mrvl_pp2_device_class.index != hw->dev_class_index) - { - rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; - goto reply; - } - - dif = pool_elt_at_index (pp2->interfaces, hw->dev_instance); - - mrvl_pp2_delete_if (dif); - -reply: - REPLY_MACRO (VL_API_MRVL_PP2_DELETE_REPLY); -} - -#include <marvell/pp2/pp2.api.c> -/* set up the API message handling tables */ -clib_error_t * -mrvl_pp2_plugin_api_hookup (vlib_main_t * vm) -{ - mrvl_pp2_main_t *pp2 = &mrvl_pp2_main; - - /* ask for a correctly-sized block of API message decode slots */ - pp2->msg_id_base = setup_message_id_table (); - - return 0; -} - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/marvell/pp2/pp2_test.c b/src/plugins/marvell/pp2/pp2_test.c deleted file mode 100644 index 26a9e9a6e34..00000000000 --- a/src/plugins/marvell/pp2/pp2_test.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - *------------------------------------------------------------------ - * Copyright (c) 2019 Arm Limited. - * 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 <vlib/unix/unix.h> -#include <vnet/ethernet/ethernet.h> - -#include <vat/vat.h> -#include <vlibapi/api.h> -#include <vlibmemory/api.h> - -#include <vppinfra/error.h> -#include <marvell/pp2/pp2.h> - -#define __plugin_msg_base pp2_test_main.msg_id_base -#include <vlibapi/vat_helper_macros.h> - -/* declare message IDs */ -#include <marvell/pp2/pp2.api_enum.h> -#include <marvell/pp2/pp2.api_types.h> - -typedef struct -{ - /* API message ID base */ - u16 msg_id_base; - vat_main_t *vat_main; -} pp2_test_main_t; - -pp2_test_main_t pp2_test_main; - -/* mrvl_pp2 create API */ -static int -api_mrvl_pp2_create (vat_main_t * vam) -{ - unformat_input_t *i = vam->input; - vl_api_mrvl_pp2_create_t *mp; - mrvl_pp2_create_if_args_t args; - int ret; - u16 size; - - clib_memset (&args, 0, sizeof (mrvl_pp2_create_if_args_t)); - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) - { - if (unformat (i, "name %s", &args.name)) - ; - else if (unformat (i, "rx-queue-size %u", &size)) - args.rx_q_sz = size; - else if (unformat (i, "tx-queue-size %u", &size)) - args.tx_q_sz = size; - else - { - clib_warning ("unknown input '%U'", format_unformat_error, i); - return -99; - } - } - - M (MRVL_PP2_CREATE, mp); - - strncpy_s ((char *) mp->if_name, ARRAY_LEN (mp->if_name), - (char *) (args.name), strlen ((char *) args.name)); - mp->rx_q_sz = clib_host_to_net_u16 (args.rx_q_sz); - mp->tx_q_sz = clib_host_to_net_u16 (args.tx_q_sz); - - S (mp); - W (ret); - - vec_free (args.name); - - return ret; -} - -/* mrvl_pp2 create reply handler */ -static void -vl_api_mrvl_pp2_create_reply_t_handler (vl_api_mrvl_pp2_create_reply_t * mp) -{ - vat_main_t *vam = pp2_test_main.vat_main; - i32 retval = ntohl (mp->retval); - - if (retval == 0) - { - fformat (vam->ofp, "created mrvl_pp2 with sw_if_index %d\n", - ntohl (mp->sw_if_index)); - } - - vam->retval = retval; - vam->result_ready = 1; - vam->regenerate_interface_table = 1; -} - - -/* mrvl_pp2 delete API */ -static int -api_mrvl_pp2_delete (vat_main_t * vam) -{ - unformat_input_t *i = vam->input; - //vnet_main_t *vnm = vnet_get_main (); - vl_api_mrvl_pp2_delete_t *mp; - u32 sw_if_index = 0; - int ret; - - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) - { - if (unformat (i, "sw_if_index %d", &sw_if_index)) - ; - else - { - clib_warning ("unknown input '%U'", format_unformat_error, i); - return -99; - } - } - - M (MRVL_PP2_DELETE, mp); - - mp->sw_if_index = clib_host_to_net_u32 (sw_if_index); - - S (mp); - W (ret); - - return ret; -} - -#include <marvell/pp2/pp2.api_test.c> - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/mss_clamp/mss_clamp.c b/src/plugins/mss_clamp/mss_clamp.c index cdac5456641..f1c435a347b 100644 --- a/src/plugins/mss_clamp/mss_clamp.c +++ b/src/plugins/mss_clamp/mss_clamp.c @@ -46,7 +46,7 @@ mssc_enable_disable_feat (u32 sw_if_index, u8 dir4, u8 dir6, int enable) sw_if_index, enable, 0, 0); } -int +__clib_export int mssc_enable_disable (u32 sw_if_index, u8 dir4, u8 dir6, u16 mss4, u16 mss6) { mssc_main_t *cm = &mssc_main; @@ -81,7 +81,7 @@ mssc_enable_disable (u32 sw_if_index, u8 dir4, u8 dir6, u16 mss4, u16 mss6) return rv; } -int +__clib_export int mssc_get_mss (u32 sw_if_index, u8 *dir4, u8 *dir6, u16 *mss4, u16 *mss6) { mssc_main_t *cm = &mssc_main; diff --git a/src/plugins/npt66/npt66.api b/src/plugins/npt66/npt66.api index 63640ac2097..dab09cda31f 100644 --- a/src/plugins/npt66/npt66.api +++ b/src/plugins/npt66/npt66.api @@ -36,5 +36,16 @@ counters npt66 { units "packets"; description "packet translation failed"; }; - + icmp6_checksum { + severity error; + type counter64; + units "packets"; + description "ICMP6 checksum validation failed"; + }; + icmp6_truncated { + severity error; + type counter64; + units "packets"; + description "ICMP6 packet truncated"; + }; };
\ No newline at end of file diff --git a/src/plugins/npt66/npt66_node.c b/src/plugins/npt66/npt66_node.c index f74f9143998..0d0c475f2c3 100644 --- a/src/plugins/npt66/npt66_node.c +++ b/src/plugins/npt66/npt66_node.c @@ -127,10 +127,7 @@ npt66_translate (ip6_header_t *ip, npt66_binding_t *binding, int dir) if (!ip6_prefix_cmp (ip->src_address, binding->internal, binding->internal_plen)) { - clib_warning ( - "npt66_translate: src address is not internal (%U -> %U)", - format_ip6_address, &ip->src_address, format_ip6_address, - &ip->dst_address); + /* Packet is not for us */ goto done; } ip->src_address = ip6_prefix_copy (ip->src_address, binding->external, @@ -144,10 +141,7 @@ npt66_translate (ip6_header_t *ip, npt66_binding_t *binding, int dir) if (!ip6_prefix_cmp (ip->dst_address, binding->external, binding->external_plen)) { - clib_warning ( - "npt66_translate: dst address is not external (%U -> %U)", - format_ip6_address, &ip->src_address, format_ip6_address, - &ip->dst_address); + /* Packet is not for us */ goto done; } ip->dst_address = ip6_prefix_copy (ip->dst_address, binding->internal, @@ -162,7 +156,7 @@ done: static int npt66_icmp6_translate (vlib_buffer_t *b, ip6_header_t *outer_ip, icmp46_header_t *icmp, npt66_binding_t *binding, - int dir) + int dir, u32 *error) { ip6_header_t *ip = (ip6_header_t *) (icmp + 2); int rv = 0; @@ -171,7 +165,7 @@ npt66_icmp6_translate (vlib_buffer_t *b, ip6_header_t *outer_ip, if (clib_net_to_host_u16 (outer_ip->payload_length) < sizeof (icmp46_header_t) + 4 + sizeof (ip6_header_t)) { - clib_warning ("ICMP6 payload too short"); + *error = NPT66_ERROR_ICMP6_TRUNCATED; return -1; } @@ -181,7 +175,7 @@ npt66_icmp6_translate (vlib_buffer_t *b, ip6_header_t *outer_ip, sum16 = ip6_tcp_udp_icmp_compute_checksum (vm, b, outer_ip, &bogus_length); if (sum16 != 0 && sum16 != 0xffff) { - clib_warning ("ICMP6 checksum failed"); + *error = NPT66_ERROR_ICMP6_CHECKSUM; return -1; } if (dir == VLIB_RX) @@ -189,10 +183,7 @@ npt66_icmp6_translate (vlib_buffer_t *b, ip6_header_t *outer_ip, if (!ip6_prefix_cmp (ip->src_address, binding->external, binding->external_plen)) { - clib_warning ( - "npt66_icmp6_translate: src address is not internal (%U -> %U)", - format_ip6_address, &ip->src_address, format_ip6_address, - &ip->dst_address); + /* Not for us */ goto done; } ip->src_address = ip6_prefix_copy (ip->src_address, binding->internal, @@ -206,10 +197,7 @@ npt66_icmp6_translate (vlib_buffer_t *b, ip6_header_t *outer_ip, if (!ip6_prefix_cmp (ip->dst_address, binding->external, binding->external_plen)) { - clib_warning ( - "npt66_icmp6_translate: dst address is not external (%U -> %U)", - format_ip6_address, &ip->src_address, format_ip6_address, - &ip->dst_address); + /* Not for us */ goto done; } ip->dst_address = ip6_prefix_copy (ip->dst_address, binding->internal, @@ -217,8 +205,8 @@ npt66_icmp6_translate (vlib_buffer_t *b, ip6_header_t *outer_ip, rv = npt66_adjust_checksum (binding->internal_plen, false, binding->delta, &ip->dst_address); } -done: +done: return rv; } @@ -243,10 +231,12 @@ npt66_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node, n_left_from = frame->n_vectors; vlib_get_buffers (vm, from, b, n_left_from); npt66_binding_t *binding; + u32 translated = 0; /* Stage 1: build vector of flow hash (based on lookup mask) */ while (n_left_from > 0) { + u32 error = NPT66_ERROR_TRANSLATION; u32 sw_if_index = vnet_buffer (b[0])->sw_if_index[dir]; u32 iph_offset = dir == VLIB_TX ? vnet_buffer (b[0])->ip.save_rewrite_length : 0; @@ -261,28 +251,26 @@ npt66_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node, icmp46_header_t *icmp = (icmp46_header_t *) (ip + 1); if (ip->protocol == IP_PROTOCOL_ICMP6 && icmp->type < 128) { - rv = npt66_icmp6_translate (b[0], ip, icmp, binding, dir); + rv = npt66_icmp6_translate (b[0], ip, icmp, binding, dir, &error); if (rv < 0) { - clib_warning ("ICMP6 npt66_translate failed"); *next = NPT66_NEXT_DROP; + b[0]->error = node->errors[error]; goto next; } } - rv = npt66_translate (ip, binding, dir); + rv = npt66_translate (ip, binding, dir); if (rv < 0) { - vlib_node_increment_counter (vm, node->node_index, - NPT66_ERROR_TRANSLATION, 1); + b[0]->error = node->errors[error]; *next = NPT66_NEXT_DROP; goto next; } - else if (dir == VLIB_TX) - vlib_node_increment_counter (vm, node->node_index, NPT66_ERROR_TX, 1); else - vlib_node_increment_counter (vm, node->node_index, NPT66_ERROR_RX, 1); - + { + translated++; + } next: next += 1; n_left_from -= 1; @@ -321,6 +309,9 @@ npt66_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node, break; } } + vlib_node_increment_counter ( + vm, node->node_index, dir == VLIB_TX ? NPT66_ERROR_TX : NPT66_ERROR_RX, + translated); vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors); return frame->n_vectors; @@ -338,17 +329,17 @@ VLIB_NODE_FN (npt66_output_node) } VLIB_REGISTER_NODE(npt66_input_node) = { - .name = "npt66-input", - .vector_size = sizeof(u32), - .format_trace = format_npt66_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = NPT66_N_ERROR, - .error_counters = npt66_error_counters, - .n_next_nodes = NPT66_N_NEXT, - .next_nodes = - { - [NPT66_NEXT_DROP] = "error-drop", - }, + .name = "npt66-input", + .vector_size = sizeof(u32), + .format_trace = format_npt66_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = NPT66_N_ERROR, + .error_counters = npt66_error_counters, + .n_next_nodes = NPT66_N_NEXT, + .next_nodes = + { + [NPT66_NEXT_DROP] = "error-drop", + }, }; VLIB_REGISTER_NODE (npt66_output_node) = { diff --git a/src/plugins/builtinurl/CMakeLists.txt b/src/plugins/osi/CMakeLists.txt index ddbca5e50f1..8ab014770ea 100644 --- a/src/plugins/builtinurl/CMakeLists.txt +++ b/src/plugins/osi/CMakeLists.txt @@ -1,5 +1,4 @@ - -# Copyright (c) <current-year> <your-organization> +# Copyright (c) 2023 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: @@ -12,15 +11,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -add_vpp_plugin(builtinurl - SOURCES - builtins.c - builtinurl.c - builtinurl.h +add_vpp_plugin(osi - API_FILES - builtinurl.api + SOURCES + osi.c + node.c + pg.c + plugin.c - API_TEST_SOURCES - builtinurl_test.c + INSTALL_HEADERS + osi.h ) diff --git a/src/plugins/osi/FEATURE.yaml b/src/plugins/osi/FEATURE.yaml new file mode 100644 index 00000000000..337be1c7146 --- /dev/null +++ b/src/plugins/osi/FEATURE.yaml @@ -0,0 +1,11 @@ +--- +name: OSI plugin +maintainer: + - community <vpp-dev@lists.fd.io> +features: + - Adds support for OSI protocols (SAP types) + - Registered as input protocol for PPP, HDLC, and LLC +missing: + - No tests for this feature currently exist +description: "" +state: experimental diff --git a/src/plugins/osi/node.c b/src/plugins/osi/node.c new file mode 100644 index 00000000000..a36b1525e0e --- /dev/null +++ b/src/plugins/osi/node.c @@ -0,0 +1,335 @@ +/* + * 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. + */ +/* + * osi_node.c: osi 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 <osi/osi.h> +#include <vnet/ppp/ppp.h> +#include <vnet/hdlc/hdlc.h> +#include <vnet/llc/llc.h> + +#define foreach_osi_input_next \ + _ (PUNT, "error-punt") \ + _ (DROP, "error-drop") + +typedef enum +{ +#define _(s,n) OSI_INPUT_NEXT_##s, + foreach_osi_input_next +#undef _ + OSI_INPUT_N_NEXT, +} osi_input_next_t; + +typedef struct +{ + u8 packet_data[32]; +} osi_input_trace_t; + +static u8 * +format_osi_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 *); + osi_input_trace_t *t = va_arg (*va, osi_input_trace_t *); + + s = format (s, "%U", format_osi_header, t->packet_data); + + return s; +} + +static uword +osi_input (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * from_frame) +{ + osi_main_t *lm = &osi_main; + u32 n_left_from, next_index, *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 (osi_input_trace_t)); + + next_index = node->cached_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; + osi_header_t *h0, *h1; + u8 next0, next1, enqueue_code; + + /* Prefetch next iteration. */ + { + vlib_buffer_t *b2, *b3; + + b2 = vlib_get_buffer (vm, from[2]); + b3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (b2, LOAD); + vlib_prefetch_buffer_header (b3, LOAD); + + CLIB_PREFETCH (b2->data, sizeof (h0[0]), LOAD); + CLIB_PREFETCH (b3->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); + + next0 = lm->input_next_by_protocol[h0->protocol]; + next1 = lm->input_next_by_protocol[h1->protocol]; + + b0->error = + node->errors[next0 == + OSI_INPUT_NEXT_DROP ? OSI_ERROR_UNKNOWN_PROTOCOL : + OSI_ERROR_NONE]; + b1->error = + node->errors[next1 == + OSI_INPUT_NEXT_DROP ? OSI_ERROR_UNKNOWN_PROTOCOL : + OSI_ERROR_NONE]; + + enqueue_code = (next0 != next_index) + 2 * (next1 != next_index); + + 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, next0, bi0); + break; + + case 2: + /* A A B */ + to_next -= 1; + n_left_to_next += 1; + vlib_set_next_frame_buffer (vm, node, next1, 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, next0, bi0); + vlib_set_next_frame_buffer (vm, node, next1, bi1); + if (next0 == next1) + { + vlib_put_next_frame (vm, node, next_index, + n_left_to_next); + next_index = next1; + 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; + osi_header_t *h0; + u8 next0; + + 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); + + next0 = lm->input_next_by_protocol[h0->protocol]; + + b0->error = + node->errors[next0 == + OSI_INPUT_NEXT_DROP ? OSI_ERROR_UNKNOWN_PROTOCOL : + OSI_ERROR_NONE]; + + /* Sent packet to wrong next? */ + if (PREDICT_FALSE (next0 != next_index)) + { + /* Return old frame; remove incorrectly enqueued packet. */ + vlib_put_next_frame (vm, node, next_index, n_left_to_next + 1); + + /* Send to correct next. */ + next_index = next0; + 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 *osi_error_strings[] = { +#define _(f,s) s, + foreach_osi_error +#undef _ +}; + +VLIB_REGISTER_NODE (osi_input_node) = { + .function = osi_input, + .name = "osi-input", + /* Takes a vector of packets. */ + .vector_size = sizeof (u32), + + .n_errors = OSI_N_ERROR, + .error_strings = osi_error_strings, + + .n_next_nodes = OSI_INPUT_N_NEXT, + .next_nodes = { +#define _(s,n) [OSI_INPUT_NEXT_##s] = n, + foreach_osi_input_next +#undef _ + }, + + .format_buffer = format_osi_header_with_length, + .format_trace = format_osi_input_trace, + .unformat_buffer = unformat_osi_header, +}; + +static void +osi_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_osi_header_with_length; + n->unformat_buffer = unformat_osi_header; + pn->unformat_edit = unformat_pg_osi_header; +} + +static clib_error_t * +osi_input_init (vlib_main_t * vm) +{ + clib_error_t *error = 0; + osi_main_t *lm = &osi_main; + + if ((error = vlib_call_init_function (vm, osi_init))) + return error; + + osi_setup_node (vm, osi_input_node.index); + + { + int i; + for (i = 0; i < ARRAY_LEN (lm->input_next_by_protocol); i++) + lm->input_next_by_protocol[i] = OSI_INPUT_NEXT_DROP; + } + + ppp_register_input_protocol (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); + llc_register_input_protocol (vm, LLC_PROTOCOL_osi_layer2, + osi_input_node.index); + llc_register_input_protocol (vm, LLC_PROTOCOL_osi_layer3, + osi_input_node.index); + llc_register_input_protocol (vm, LLC_PROTOCOL_osi_layer4, + osi_input_node.index); + llc_register_input_protocol (vm, LLC_PROTOCOL_osi_layer5, + osi_input_node.index); + + return 0; +} + +VLIB_INIT_FUNCTION (osi_input_init); + +void +osi_register_input_protocol (osi_protocol_t protocol, u32 node_index) +{ + osi_main_t *lm = &osi_main; + vlib_main_t *vm = lm->vlib_main; + osi_protocol_info_t *pi; + + { + clib_error_t *error = vlib_call_init_function (vm, osi_input_init); + if (error) + clib_error_report (error); + } + + pi = osi_get_protocol_info (lm, protocol); + pi->node_index = node_index; + pi->next_index = vlib_node_add_next (vm, osi_input_node.index, node_index); + + lm->input_next_by_protocol[protocol] = pi->next_index; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/osi/osi.c b/src/plugins/osi/osi.c new file mode 100644 index 00000000000..67c7053f388 --- /dev/null +++ b/src/plugins/osi/osi.c @@ -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. + */ +/* + * osi.c: osi 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 <osi/osi.h> + +/* Global main structure. */ +osi_main_t osi_main; + +u8 * +format_osi_protocol (u8 * s, va_list * args) +{ + osi_protocol_t p = va_arg (*args, u32); + osi_main_t *pm = &osi_main; + osi_protocol_info_t *pi = osi_get_protocol_info (pm, p); + + if (pi) + s = format (s, "%s", pi->name); + else + s = format (s, "0x%02x", p); + + return s; +} + +u8 * +format_osi_header_with_length (u8 * s, va_list * args) +{ + osi_main_t *pm = &osi_main; + osi_header_t *h = va_arg (*args, osi_header_t *); + u32 max_header_bytes = va_arg (*args, u32); + osi_protocol_t p = h->protocol; + u32 indent, header_bytes; + + header_bytes = sizeof (h[0]); + if (max_header_bytes != 0 && header_bytes > max_header_bytes) + return format (s, "osi header truncated"); + + indent = format_get_indent (s); + + s = format (s, "OSI %U", format_osi_protocol, p); + + if (max_header_bytes != 0 && header_bytes > max_header_bytes) + { + osi_protocol_info_t *pi = osi_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_osi_header (u8 * s, va_list * args) +{ + osi_header_t *h = va_arg (*args, osi_header_t *); + return format (s, "%U", format_osi_header_with_length, h, 0); +} + +/* Returns osi protocol as an int in host byte order. */ +uword +unformat_osi_protocol (unformat_input_t * input, va_list * args) +{ + u8 *result = va_arg (*args, u8 *); + osi_main_t *pm = &osi_main; + int p, i; + + /* Numeric type. */ + if (unformat (input, "0x%x", &p) || unformat (input, "%d", &p)) + { + if (p >= (1 << 8)) + return 0; + *result = p; + return 1; + } + + /* Named type. */ + if (unformat_user (input, unformat_vlib_number_by_name, + pm->protocol_info_by_name, &i)) + { + osi_protocol_info_t *pi = vec_elt_at_index (pm->protocol_infos, i); + *result = pi->protocol; + return 1; + } + + return 0; +} + +uword +unformat_osi_header (unformat_input_t * input, va_list * args) +{ + u8 **result = va_arg (*args, u8 **); + osi_header_t _h, *h = &_h; + u8 p; + + if (!unformat (input, "%U", unformat_osi_protocol, &p)) + return 0; + + h->protocol = 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 void +add_protocol (osi_main_t * pm, osi_protocol_t protocol, char *protocol_name) +{ + osi_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 * +osi_init (vlib_main_t * vm) +{ + osi_main_t *pm = &osi_main; + + clib_memset (pm, 0, sizeof (pm[0])); + pm->vlib_main = vm; + + pm->protocol_info_by_name = hash_create_string (0, sizeof (uword)); + pm->protocol_info_by_protocol = hash_create (0, sizeof (uword)); + +#define _(f,n) add_protocol (pm, OSI_PROTOCOL_##f, #f); + foreach_osi_protocol; +#undef _ + + return vlib_call_init_function (vm, osi_input_init); +} + +/* init order dependency: llc_init -> osi_init -> snap_init*/ +/* Otherwise, osi_input_init will wipe out e.g. the snap init */ +VLIB_INIT_FUNCTION (osi_init) = { + .init_order = VLIB_INITS ("llc_init", "osi_init", "snap_init"), +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/osi/osi.h b/src/plugins/osi/osi.h new file mode 100644 index 00000000000..fb248ed9cc5 --- /dev/null +++ b/src/plugins/osi/osi.h @@ -0,0 +1,156 @@ +/* + * 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. + */ +/* + * osi.h: OSI definitions + * + * 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_osi_h +#define included_osi_h + +#include <vnet/vnet.h> + +#define foreach_osi_protocol \ + _ (null, 0x0) \ + _ (x_29, 0x01) \ + _ (x_633, 0x03) \ + _ (q_931, 0x08) \ + _ (q_933, 0x08) \ + _ (q_2931, 0x09) \ + _ (q_2119, 0x0c) \ + _ (snap, 0x80) \ + _ (clnp, 0x81) \ + _ (esis, 0x82) \ + _ (isis, 0x83) \ + _ (idrp, 0x85) \ + _ (x25_esis, 0x8a) \ + _ (iso10030, 0x8c) \ + _ (iso11577, 0x8d) \ + _ (ip6, 0x8e) \ + _ (compressed, 0xb0) \ + _ (sndcf, 0xc1) \ + _ (ip4, 0xcc) \ + _ (ppp, 0xcf) + +typedef enum +{ +#define _(f,n) OSI_PROTOCOL_##f = n, + foreach_osi_protocol +#undef _ +} osi_protocol_t; + +typedef struct +{ + u8 protocol; + + u8 payload[0]; +} osi_header_t; + +typedef struct +{ + /* Name (a c string). */ + char *name; + + /* OSI protocol (SAP type). */ + osi_protocol_t protocol; + + /* Node which handles this type. */ + u32 node_index; + + /* Next index for this type. */ + u32 next_index; +} osi_protocol_info_t; + +#define foreach_osi_error \ + _ (NONE, "no error") \ + _ (UNKNOWN_PROTOCOL, "unknown osi protocol") + +typedef enum +{ +#define _(f,s) OSI_ERROR_##f, + foreach_osi_error +#undef _ + OSI_N_ERROR, +} osi_error_t; + +typedef struct +{ + vlib_main_t *vlib_main; + + osi_protocol_info_t *protocol_infos; + + /* Hash tables mapping name/protocol to protocol info index. */ + uword *protocol_info_by_name, *protocol_info_by_protocol; + + /* osi-input next index indexed by protocol. */ + u8 input_next_by_protocol[256]; +} osi_main_t; + +always_inline osi_protocol_info_t * +osi_get_protocol_info (osi_main_t * m, osi_protocol_t protocol) +{ + uword *p = hash_get (m->protocol_info_by_protocol, protocol); + return p ? vec_elt_at_index (m->protocol_infos, p[0]) : 0; +} + +extern osi_main_t osi_main; + +/* Register given node index to take input for given osi type. */ +void osi_register_input_protocol (osi_protocol_t protocol, u32 node_index); + +format_function_t format_osi_protocol; +format_function_t format_osi_header; +format_function_t format_osi_header_with_length; + +/* Parse osi protocol as 0xXXXX or protocol name. */ +unformat_function_t unformat_osi_protocol; + +/* Parse osi header. */ +unformat_function_t unformat_osi_header; +unformat_function_t unformat_pg_osi_header; + +void osi_register_input_protocol (osi_protocol_t protocol, u32 node_index); + +format_function_t format_osi_header; + +#endif /* included_osi_h */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/osi/pg.c b/src/plugins/osi/pg.c new file mode 100644 index 00000000000..3bac693c127 --- /dev/null +++ b/src/plugins/osi/pg.c @@ -0,0 +1,106 @@ +/* + * 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. + */ +/* + * osi_pg.c: packet generator osi 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 <osi/osi.h> + +typedef struct +{ + pg_edit_t protocol; +} pg_osi_header_t; + +static inline void +pg_osi_header_init (pg_osi_header_t * e) +{ + pg_edit_init (&e->protocol, osi_header_t, protocol); +} + +uword +unformat_pg_osi_header (unformat_input_t * input, va_list * args) +{ + pg_stream_t *s = va_arg (*args, pg_stream_t *); + pg_osi_header_t *h; + u32 group_index, error; + + h = pg_create_edit_group (s, sizeof (h[0]), sizeof (osi_header_t), + &group_index); + pg_osi_header_init (h); + + error = 1; + if (!unformat (input, "%U", + unformat_pg_edit, unformat_osi_protocol, &h->protocol)) + goto done; + + { + osi_main_t *pm = &osi_main; + osi_protocol_info_t *pi = 0; + pg_node_t *pg_node = 0; + + if (h->protocol.type == PG_EDIT_FIXED) + { + u8 t = *h->protocol.values[PG_EDIT_LO]; + pi = osi_get_protocol_info (pm, 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/marvell/plugin.c b/src/plugins/osi/plugin.c index ed90776ba95..5fc412e093e 100644 --- a/src/plugins/marvell/plugin.c +++ b/src/plugins/osi/plugin.c @@ -1,6 +1,7 @@ /* - *------------------------------------------------------------------ - * Copyright (c) 2018 Cisco and/or its affiliates. + * plugin.c: osi + * + * Copyright (c) 2023 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: @@ -12,22 +13,11 @@ * 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> - VLIB_PLUGIN_REGISTER () = { .version = VPP_BUILD_VER, - .description = "Marvell PP2 Device Driver", -}; - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ + .description = "OSI plugin", +};
\ No newline at end of file diff --git a/src/plugins/prom/prom.c b/src/plugins/prom/prom.c index 934e8480d3c..475e98b1038 100644 --- a/src/plugins/prom/prom.c +++ b/src/plugins/prom/prom.c @@ -191,6 +191,7 @@ send_data_to_hss (hss_session_handle_t sh) args.sh = sh; args.data = vec_dup (pm->stats); args.data_len = vec_len (pm->stats); + args.ct = HTTP_CONTENT_TEXT_PLAIN; args.sc = HTTP_STATUS_OK; args.free_vec_data = 1; @@ -207,7 +208,7 @@ static uword prom_scraper_process (vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f) { - uword *event_data = 0, event_type; + uword *event_data = 0, event_type, *sh_as_uword; prom_main_t *pm = &prom_main; hss_session_handle_t sh; f64 timeout = 10000.0; @@ -222,12 +223,15 @@ prom_scraper_process (vlib_main_t *vm, vlib_node_runtime_t *rt, /* timeout, do nothing */ break; case PROM_SCRAPER_EVT_RUN: - sh.as_u64 = event_data[0]; vec_reset_length (pm->stats); pm->stats = scrape_stats_segment (pm->stats, pm->stats_patterns, pm->used_only); - session_send_rpc_evt_to_thread_force (sh.thread_index, - send_data_to_hss_rpc, &sh); + vec_foreach (sh_as_uword, event_data) + { + sh.as_u64 = (u64) *sh_as_uword; + session_send_rpc_evt_to_thread_force ( + sh.thread_index, send_data_to_hss_rpc, sh_as_uword); + } pm->last_scrape = vlib_time_now (vm); break; default: diff --git a/src/plugins/pvti/CMakeLists.txt b/src/plugins/pvti/CMakeLists.txt new file mode 100644 index 00000000000..900b662d54a --- /dev/null +++ b/src/plugins/pvti/CMakeLists.txt @@ -0,0 +1,40 @@ +# 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(pvti + SOURCES + pvti_if.c + pvti.c + input.h + input.c + input-main.c + output.h + output.c + output-main.c + bypass.h + bypass.c + bypass-main.c + api.c + pvti.h + + MULTIARCH_SOURCES + input.c + output.c + bypass.c + + API_FILES + pvti.api + + # API_TEST_SOURCES + # pvti_test.c +) diff --git a/src/plugins/pvti/FEATURE.yaml b/src/plugins/pvti/FEATURE.yaml new file mode 100644 index 00000000000..52dbe5b7c1b --- /dev/null +++ b/src/plugins/pvti/FEATURE.yaml @@ -0,0 +1,8 @@ +--- +name: Packet Vector Tunnel +maintainer: Andrew Yourtchenko <ayourtch@gmail.com> +features: + - support inner MTU up to ~8K over standard 1280..1500 MTU substrate +description: "Large MTU Tunnels" +state: development +properties: [API, CLI] diff --git a/src/plugins/pvti/api.c b/src/plugins/pvti/api.c new file mode 100644 index 00000000000..cda39ad44e8 --- /dev/null +++ b/src/plugins/pvti/api.c @@ -0,0 +1,137 @@ + +#include <vnet/vnet.h> +#include <vlibmemory/api.h> + +#include <vnet/format_fns.h> +#include <vnet/ip/ip_types_api.h> +#include <vlibapi/api.h> + +#include <pvti/pvti.api_enum.h> +#include <pvti/pvti.api_types.h> + +#include <pvti/pvti.h> +#include <pvti/pvti_if.h> + +#define REPLY_MSG_ID_BASE pvm->msg_id_base +#include <vlibapi/api_helper_macros.h> + +typedef struct +{ + vl_api_registration_t *reg; + u32 context; +} pvti_if_details_ctx_t; + +typedef struct +{ + +} pvti_interface_dump_ctx_t; + +static walk_rc_t +pvti_if_send_details (index_t pvtii, void *data) +{ + vl_api_pvti_interface_details_t *rmp; + pvti_if_details_ctx_t *ctx = data; + const pvti_if_t *pvi; + + pvi = pvti_if_get (pvtii); + + rmp = vl_msg_api_alloc_zero (sizeof (*rmp)); + rmp->_vl_msg_id = + htons (VL_API_PVTI_INTERFACE_DETAILS + pvti_main.msg_id_base); + + rmp->interface.sw_if_index = htonl (pvi->sw_if_index); + rmp->interface.local_port = htons (pvi->local_port); + rmp->interface.remote_port = htons (pvi->remote_port); + rmp->interface.underlay_mtu = htons (pvi->underlay_mtu); + + ip_address_encode2 (&pvi->local_ip, &rmp->interface.local_ip); + ip_address_encode2 (&pvi->remote_ip, &rmp->interface.remote_ip); + + rmp->context = ctx->context; + + vl_api_send_msg (ctx->reg, (u8 *) rmp); + + return (WALK_CONTINUE); +} + +static void +vl_api_pvti_interface_dump_t_handler (vl_api_pvti_interface_dump_t *mp) +{ + vl_api_registration_t *reg; + // pvti_main_t *pvm = &pvti_main; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (reg == 0) + return; + + pvti_if_details_ctx_t ctx = { + .reg = reg, + .context = mp->context, + }; + + u32 sw_if_index = ntohl (mp->sw_if_index); + if (sw_if_index == ~0) + pvti_if_walk (pvti_if_send_details, &ctx); + else + { + index_t pvtii = pvti_if_find_by_sw_if_index (sw_if_index); + if (pvtii != INDEX_INVALID) + pvti_if_send_details (pvtii, &ctx); + } +} + +static void +vl_api_pvti_interface_create_t_handler (vl_api_pvti_interface_create_t *mp) +{ + vl_api_pvti_interface_create_reply_t *rmp; + pvti_main_t *pvm = &pvti_main; + int rv = ~0; + u32 sw_if_index = ~0; + ip_address_t local_ip; + ip_address_t remote_ip; + + ip_address_decode2 (&mp->interface.local_ip, &local_ip); + ip_address_decode2 (&mp->interface.remote_ip, &remote_ip); + u16 lport = clib_host_to_net_u16 (mp->interface.local_port); + u16 rport = clib_host_to_net_u16 (mp->interface.remote_port); + u16 underlay_mtu = clib_host_to_net_u16 (mp->interface.underlay_mtu); + u32 underlay_fib_index = + clib_host_to_net_u32 (mp->interface.underlay_fib_index); + pvti_peer_address_method_t peer_address_method = + mp->interface.peer_address_from_payload ? PVTI_PEER_ADDRESS_FROM_PAYLOAD : + PVTI_PEER_ADDRESS_FIXED; + + if (underlay_mtu == 0) + { + underlay_mtu = 1500; + } + + rv = + pvti_if_create (&local_ip, lport, &remote_ip, rport, peer_address_method, + underlay_mtu, underlay_fib_index, &sw_if_index); + + REPLY_MACRO2 (VL_API_PVTI_INTERFACE_CREATE_REPLY, + { rmp->sw_if_index = htonl (sw_if_index); }); +} + +static void +vl_api_pvti_interface_delete_t_handler (vl_api_pvti_interface_delete_t *mp) +{ + vl_api_pvti_interface_delete_reply_t *rmp; + pvti_main_t *pvm = &pvti_main; + int rv = 0; + + rv = pvti_if_delete (ntohl (mp->sw_if_index)); + REPLY_MACRO (VL_API_PVTI_INTERFACE_DELETE_REPLY); +} + +/* API definitions */ +#include <pvti/pvti.api.c> + +void +pvti_api_init () +{ + pvti_main_t *pvm = &pvti_main; + /* Add our API messages to the global name_crc hash table */ + pvm->msg_id_base = setup_message_id_table (); +} diff --git a/src/plugins/pvti/bypass-main.c b/src/plugins/pvti/bypass-main.c new file mode 100644 index 00000000000..db79ccd2113 --- /dev/null +++ b/src/plugins/pvti/bypass-main.c @@ -0,0 +1,79 @@ +/* + * 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 <pvti/bypass.h> + +/* packet trace format function */ +static u8 * +format_pvti_bypass_trace (u8 *s, va_list *args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + pvti_bypass_trace_t *t = va_arg (*args, pvti_bypass_trace_t *); + + s = format (s, "PVTI-BYPASS: sw_if_index %d, next index %d\n", + t->sw_if_index, t->next_index); + s = format (s, " src %U sport %d dport %d\n", format_ip_address, + &t->remote_ip, t->remote_port, t->local_port); + s = format (s, " seq: %d", t->seq); + return s; +} + +vlib_node_registration_t pvti4_bypass_node; +vlib_node_registration_t pvti6_bypass_node; + +static char *pvti_bypass_error_strings[] = { +#define _(sym, string) string, + foreach_pvti_bypass_error +#undef _ +}; + +VLIB_REGISTER_NODE (pvti4_bypass_node) = +{ + .name = "ip4-pvti-bypass", + .vector_size = sizeof (u32), + .format_trace = format_pvti_bypass_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(pvti_bypass_error_strings), + .error_strings = pvti_bypass_error_strings, + + .n_next_nodes = PVTI_BYPASS_N_NEXT, + + .next_nodes = { + [PVTI_BYPASS_NEXT_DROP] = "error-drop", + [PVTI_BYPASS_NEXT_PVTI_INPUT] = "pvti4-input", + }, + +}; + +VLIB_REGISTER_NODE (pvti6_bypass_node) = +{ + .name = "ip6-pvti-bypass", + .vector_size = sizeof (u32), + .format_trace = format_pvti_bypass_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(pvti_bypass_error_strings), + .error_strings = pvti_bypass_error_strings, + + .n_next_nodes = PVTI_BYPASS_N_NEXT, + + .next_nodes = { + [PVTI_BYPASS_NEXT_DROP] = "error-drop", + [PVTI_BYPASS_NEXT_PVTI_INPUT] = "pvti6-input", + }, + +}; diff --git a/src/plugins/pvti/bypass.c b/src/plugins/pvti/bypass.c new file mode 100644 index 00000000000..14c976439eb --- /dev/null +++ b/src/plugins/pvti/bypass.c @@ -0,0 +1,202 @@ +/* + * 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/vnet.h> +#include <vnet/pg/pg.h> +#include <vppinfra/error.h> +#include <pvti/pvti.h> +#include <pvti/pvti_if.h> +#include <pvti/bypass.h> + +always_inline u16 +pvti_bypass_node_common (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame, bool is_ip6) +{ + u32 n_left_from, *from, *to_next; + pvti_bypass_next_t next_index; + vlib_node_runtime_t *error_node = + vlib_node_get_runtime (vm, ip4_input_node.index); + + u32 pkts_processed = 0; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_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 > 0 && n_left_to_next > 0) + { + vlib_buffer_t *b0; + u32 sw_if_index0 = 0; + ip4_header_t *ip40; + ip6_header_t *ip60; + udp_header_t *udp0; + u32 bi0, ip_len0, udp_len0, flags0, next0; + u8 error0, good_udp0, proto0; + i32 len_diff0; + + bi0 = to_next[0] = from[0]; + from += 1; + n_left_from -= 1; + to_next += 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + /* setup the packet for the next feature */ + vnet_feature_next (&next0, b0); + + if (is_ip6) + { + ip60 = vlib_buffer_get_current (b0); + } + else + { + ip40 = vlib_buffer_get_current (b0); + } + + if (is_ip6) + { + proto0 = ip60->protocol; + } + else + { + /* Treat IP frag packets as "experimental" protocol for now */ + proto0 = ip4_is_fragment (ip40) ? 0xfe : ip40->protocol; + } + + /* Process packet 0 */ + if (proto0 != IP_PROTOCOL_UDP) + goto exit; /* not UDP packet */ + + if (is_ip6) + udp0 = ip6_next_header (ip60); + else + udp0 = ip4_next_header (ip40); + + /* look up the destination ip and port */ + u32 pvti_index0 = INDEX_INVALID; + if (is_ip6) + { + pvti_index0 = pvti_if_find_by_remote_ip6_and_port ( + &ip60->src_address, clib_net_to_host_u16 (udp0->src_port)); + } + else + { + pvti_index0 = pvti_if_find_by_remote_ip4_and_port ( + &ip40->src_address, clib_net_to_host_u16 (udp0->src_port)); + } + if (pvti_index0 == INDEX_INVALID) + goto exit; + + flags0 = b0->flags; + good_udp0 = (flags0 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0; + + /* Don't verify UDP checksum for packets with explicit zero checksum. + */ + good_udp0 |= udp0->checksum == 0; + + /* Verify UDP length */ + if (is_ip6) + ip_len0 = clib_net_to_host_u16 (ip60->payload_length); + else + ip_len0 = clib_net_to_host_u16 (ip40->length); + udp_len0 = clib_net_to_host_u16 (udp0->length); + len_diff0 = ip_len0 - udp_len0; + + /* Verify UDP checksum */ + if (PREDICT_FALSE (!good_udp0)) + { + if (is_ip6) + flags0 = ip6_tcp_udp_icmp_validate_checksum (vm, b0); + else + flags0 = ip4_tcp_udp_validate_checksum (vm, b0); + good_udp0 = (flags0 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0; + } + + if (is_ip6) + { + error0 = good_udp0 ? 0 : IP6_ERROR_UDP_CHECKSUM; + error0 = (len_diff0 >= 0) ? error0 : IP6_ERROR_UDP_LENGTH; + } + else + { + error0 = good_udp0 ? 0 : IP4_ERROR_UDP_CHECKSUM; + error0 = (len_diff0 >= 0) ? error0 : IP4_ERROR_UDP_LENGTH; + } + + next0 = error0 ? PVTI_BYPASS_NEXT_DROP : PVTI_BYPASS_NEXT_PVTI_INPUT; + b0->error = error0 ? error_node->errors[error0] : 0; + + /* pvtiX-input node expect current at PVTI header */ + if (is_ip6) + vlib_buffer_advance (b0, sizeof (ip6_header_t) + + sizeof (udp_header_t)); + else + vlib_buffer_advance (b0, sizeof (ip4_header_t) + + sizeof (udp_header_t)); + exit: + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + pvti_bypass_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next0; + t->seq = 0; // clib_net_to_host_u32 (pvti0->seq); + if (is_ip6) + { + } + else + { + t->remote_ip.ip.ip4 = ip40->src_address; + t->remote_ip.version = AF_IP4; + } + // t->local_port = h0->udp.dst_port; + // t->remote_port = h0->udp.src_port; + } + + pkts_processed += 1; + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + PVTI_BYPASS_ERROR_PROCESSED, pkts_processed); + return frame->n_vectors; +} + +VLIB_NODE_FN (pvti4_bypass_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return pvti_bypass_node_common (vm, node, frame, 0); +} + +VLIB_NODE_FN (pvti6_bypass_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return pvti_bypass_node_common (vm, node, frame, 1); +} diff --git a/src/plugins/pvti/bypass.h b/src/plugins/pvti/bypass.h new file mode 100644 index 00000000000..611d5770ad3 --- /dev/null +++ b/src/plugins/pvti/bypass.h @@ -0,0 +1,53 @@ +/* + * 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. + */ +#ifndef __included_pvti_bypass_h__ +#define __included_pvti_bypass_h__ + +#include <vlib/vlib.h> +#include <vnet/vnet.h> +#include <vnet/pg/pg.h> +#include <vppinfra/error.h> +#include <pvti/pvti.h> +#include <pvti/pvti_if.h> + +typedef struct +{ + u32 next_index; + u32 sw_if_index; + ip_address_t remote_ip; + u16 remote_port; + u16 local_port; + u32 seq; +} pvti_bypass_trace_t; + +#define foreach_pvti_bypass_error \ + _ (PROCESSED, "PVTI bypass tunnel packets processed") + +typedef enum +{ +#define _(sym, str) PVTI_BYPASS_ERROR_##sym, + foreach_pvti_bypass_error +#undef _ + PVTI_BYPASS_N_ERROR, +} pvti_bypass_error_t; + +typedef enum +{ + PVTI_BYPASS_NEXT_DROP, + PVTI_BYPASS_NEXT_PVTI_INPUT, + PVTI_BYPASS_N_NEXT, +} pvti_bypass_next_t; + +#endif // pvti_bypass_h diff --git a/src/plugins/pvti/input-main.c b/src/plugins/pvti/input-main.c new file mode 100644 index 00000000000..8ab8b18dd7c --- /dev/null +++ b/src/plugins/pvti/input-main.c @@ -0,0 +1,115 @@ +/* + * 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 <pvti/input.h> + +static char *pvti_input_error_strings[] = { +#define _(sym, string) string, + foreach_pvti_input_error +#undef _ +}; + +#define _(f, s) s, +static char *pvti_input_trace_type_names[] = { foreach_pvti_input_trace_type }; +#undef _ + +static char * +get_pvti_trace_type_name (u8 ptype) +{ + if (ptype < PVTI_INPUT_TRACE_N_TYPES) + { + return pvti_input_trace_type_names[ptype]; + } + else + { + return "unknown"; + } +} + +/* packet trace format function */ +static u8 * +format_pvti_input_trace (u8 *s, va_list *args) +{ + int i; + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + pvti_input_trace_t *t = va_arg (*args, pvti_input_trace_t *); + + u32 indent = format_get_indent (s); + + s = format (s, + "PVTI-IN: sw_if_index %d, next index %d, trace_type: %s(%d), " + "chunkcnt: %d\n", + t->sw_if_index, t->next_index, + get_pvti_trace_type_name (t->trace_type), t->trace_type, + t->chunk_count); + s = format (s, " src %U sport %d dport %d\n", format_ip_address, + &t->remote_ip, t->remote_port, t->local_port); + s = format (s, " seq: %d, chunk_count: %d\n", t->seq, t->chunk_count); + u16 max = t->chunk_count > MAX_CHUNKS ? MAX_CHUNKS : t->chunk_count; + for (i = 0; i < max; i++) + { + s = format (s, " %02d: sz %d\n", i, t->chunks[i].total_chunk_length); + } + s = format (s, "\n%U%U", format_white_space, indent, + format_ip_adjacency_packet_data, t->packet_data, + sizeof (t->packet_data)); + + return s; +} + +vlib_node_registration_t pvti4_input_node; +vlib_node_registration_t pvti6_input_node; + +VLIB_REGISTER_NODE (pvti4_input_node) = +{ + .name = "pvti4-input", + .vector_size = sizeof (u32), + .format_trace = format_pvti_input_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(pvti_input_error_strings), + .error_strings = pvti_input_error_strings, + + .n_next_nodes = PVTI_INPUT_N_NEXT, + + .next_nodes = { + [PVTI_INPUT_NEXT_DROP] = "error-drop", + [PVTI_INPUT_NEXT_IP4_INPUT] = "ip4-input-no-checksum", + [PVTI_INPUT_NEXT_IP6_INPUT] = "ip6-input", + [PVTI_INPUT_NEXT_PUNT] = "error-punt", + }, + +}; +VLIB_REGISTER_NODE (pvti6_input_node) = +{ + .name = "pvti6-input", + .vector_size = sizeof (u32), + .format_trace = format_pvti_input_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(pvti_input_error_strings), + .error_strings = pvti_input_error_strings, + + .n_next_nodes = PVTI_INPUT_N_NEXT, + + .next_nodes = { + [PVTI_INPUT_NEXT_DROP] = "error-drop", + [PVTI_INPUT_NEXT_IP4_INPUT] = "ip4-input-no-checksum", + [PVTI_INPUT_NEXT_IP6_INPUT] = "ip6-input", + [PVTI_INPUT_NEXT_PUNT] = "error-punt", + }, + +}; diff --git a/src/plugins/pvti/input.c b/src/plugins/pvti/input.c new file mode 100644 index 00000000000..6a8806e2795 --- /dev/null +++ b/src/plugins/pvti/input.c @@ -0,0 +1,496 @@ +/* + * 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/vnet.h> +#include <vnet/pg/pg.h> +#include <vppinfra/error.h> +#include <pvti/pvti.h> +#include <pvti/pvti_if.h> +#include <pvti/input.h> + +always_inline void +pvti_enqueue_rx_bi_to_next_and_trace (vlib_main_t *vm, + vlib_node_runtime_t *node, + pvti_per_thread_data_t *ptd, u32 bi0, + u16 next0) +{ + vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0); + + if (PREDICT_TRUE (vlib_trace_buffer (vm, node, next0, b0, + /* follow_chain */ 0))) + { + pvti_input_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->next_index = next0; + t->trace_type = PVTI_INPUT_TRACE_decap; + clib_memcpy (t->packet_data, vlib_buffer_get_current (b0), + sizeof (t->packet_data)); + } + vec_add1 (ptd->pending_rx_buffers, bi0); + vec_add1 (ptd->pending_rx_nexts, next0); +} + +always_inline pvti_rx_peer_t * +pvti_try_find_or_create_rx_peer (pvti_per_thread_data_t *ptd, + vlib_buffer_t *b0, bool is_ip6) +{ + pvti_rx_peer_t *peer; + + ip_address_t remote_ip = { 0 }; + u16 remote_port; + if (is_ip6) + { + pvti_ip6_encap_header_t *h0 = + ((pvti_ip6_encap_header_t *) vlib_buffer_get_current (b0)) - 1; + ip_address_set (&remote_ip, &h0->ip6.src_address, AF_IP6); + remote_port = clib_net_to_host_u16 (h0->udp.src_port); + } + else + { + pvti_ip4_encap_header_t *h0 = + ((pvti_ip4_encap_header_t *) vlib_buffer_get_current (b0)) - 1; + ip_address_set (&remote_ip, &h0->ip4.src_address, AF_IP4); + remote_port = clib_net_to_host_u16 (h0->udp.src_port); + } + + pool_foreach (peer, ptd->rx_peers) + { + if (peer->remote_port == remote_port && + 0 == ip_address_cmp (&remote_ip, &peer->remote_ip)) + { + if (peer->deleted) + { + // The peer has been marked as deleted - wipe it. + clib_memset (peer, 0xca, sizeof (*peer)); + pool_put (ptd->rx_peers, peer); + continue; + } + return peer; + } + } + + index_t pvti_if_index0 = + pvti_if_find_by_remote_ip_and_port (&remote_ip, remote_port); + if (INDEX_INVALID == pvti_if_index0) + { + // no suitable interface found, bail + return 0; + } + pvti_if_t *pvti_if0 = pvti_if_get (pvti_if_index0); + + pvti_rx_peer_t new_peer = { + .local_ip = pvti_if0->local_ip, + .local_port = pvti_if0->local_port, + .remote_ip = remote_ip, + .remote_port = remote_port, + .pvti_if_index = pvti_if_index0, + .rx_streams = { { 0 } }, + }; + pvti_rx_peer_t *rx_new_peer; + pool_get (ptd->rx_peers, rx_new_peer); + *rx_new_peer = new_peer; + + int i; + for (i = 0; i < MAX_RX_STREAMS; i++) + { + rx_new_peer->rx_streams[i].rx_bi0 = INDEX_INVALID; + rx_new_peer->rx_streams[i].rx_bi0_first = INDEX_INVALID; + rx_new_peer->rx_streams[i].rx_next0 = 0; + } + + return rx_new_peer; +} + +always_inline u16 +pvti_input_node_common (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame, bool is_ip6) +{ + u32 n_left_from, *from; + pvti_chunk_header_t *chunks[MAX_CHUNKS]; + u32 pkts_processed = 0; + u32 pkts_decapsulated = 0; + u32 decap_failed_no_buffers = 0; + + pvti_main_t *pvm = &pvti_main; + + u32 thread_index = vlib_get_thread_index (); + pvti_per_thread_data_t *ptd = + vec_elt_at_index (pvm->per_thread_data[is_ip6], thread_index); + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + + while (n_left_from > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0 = PVTI_INPUT_NEXT_DROP; + u32 sw_if_index0; + u8 true_chunk_count = 0; + u8 max_chunk_count; + + bi0 = from[0]; + from += 1; + n_left_from -= 1; + + b0 = vlib_get_buffer (vm, bi0); + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + pvti_ip4_encap_header_t *h0 = + ((pvti_ip4_encap_header_t *) vlib_buffer_get_current (b0)) - 1; + pvti_rx_peer_t *pvti_rx_peer0 = + pvti_try_find_or_create_rx_peer (ptd, b0, is_ip6); + if (!pvti_rx_peer0) + { + b0->error = node->errors[PVTI_INPUT_ERROR_PEER]; + goto drop_and_maybe_trace; + } + + b0 = vlib_get_buffer (vm, bi0); + pvti_packet_header_t *pvti0 = vlib_buffer_get_current (b0); + u8 stream_index = pvti0->stream_index; + max_chunk_count = + pvti0->chunk_count < MAX_CHUNKS ? pvti0->chunk_count : MAX_CHUNKS; + u16 pvti_packet_header_sz0 = + pvti0->pad_bytes + offsetof (pvti_packet_header_t, pad); + if (b0->current_length < pvti_packet_header_sz0) + { + b0->error = node->errors[PVTI_INPUT_ERROR_PACKET_TOO_SHORT]; + goto drop_and_maybe_trace; + } + vlib_buffer_advance (b0, pvti_packet_header_sz0); + + if (max_chunk_count == 0) + { + b0->error = node->errors[PVTI_INPUT_ERROR_NOCHUNKS]; + goto drop_and_maybe_trace; + } + if (pvti0->reass_chunk_count > max_chunk_count) + { + b0->error = node->errors[PVTI_INPUT_ERROR_TOOMANYREASS]; + goto drop_and_maybe_trace; + } + pvti_per_rx_stream_data_t *rx_stream0 = + &pvti_rx_peer0->rx_streams[stream_index]; + + u32 new_seq0 = clib_net_to_host_u32 (pvti0->seq); + if (new_seq0 == rx_stream0->last_rx_seq + 1) + { + /* Sequence# matches, we can attempt adding the leading chunks to + * reassembly */ + rx_stream0->last_rx_seq = new_seq0; + + while ((b0->current_length > 0) && + true_chunk_count < pvti0->reass_chunk_count) + { + /* attempt to either incorporate the first chunk into + * reassembly or skip it. */ + pvti_chunk_header_t *pvc0 = vlib_buffer_get_current (b0); + const u16 chunk_payload_length = + clib_net_to_host_u16 (pvc0->total_chunk_length) - + sizeof (*pvc0); + vlib_buffer_advance (b0, sizeof (*pvc0)); + + if (rx_stream0->rx_bi0 == INDEX_INVALID) + { + clib_warning ( + "RX internal error: not-first chunk but no wip block"); + } + else + { + + vlib_buffer_t *rb0 = + vlib_get_buffer (vm, rx_stream0->rx_bi0); + u16 allowed_length = + PVTI_RX_MAX_LENGTH - rb0->current_length; + if (allowed_length > chunk_payload_length) + { + // simple case - there is space in the buffer to fit + // the whole chunk + void *tail = + vlib_buffer_put_uninit (rb0, chunk_payload_length); + clib_memcpy (tail, vlib_buffer_get_current (b0), + chunk_payload_length); + } + else + { + // The current chunk can not fit - need to make two + // copies, one into the current buffer, and one into + // a newly allocated chained buffer. + void *tail = + vlib_buffer_put_uninit (rb0, allowed_length); + clib_memcpy (tail, vlib_buffer_get_current (b0), + allowed_length); + u16 remaining_payload_length = + chunk_payload_length - allowed_length; + u32 nrbi0 = pvti_get_new_buffer (vm); + if (INDEX_INVALID == nrbi0) + { + ASSERT (0); // FIXME what the recovery is + // supposed to look like ? + } + else + { + // link up the new buffer and copy the remainder + // there + vlib_buffer_t *nrb0 = vlib_get_buffer (vm, nrbi0); + rb0->flags |= VLIB_BUFFER_NEXT_PRESENT; + rb0->flags &= ~VLIB_BUFFER_TOTAL_LENGTH_VALID; + rb0->next_buffer = nrbi0; + rx_stream0->rx_bi0 = nrbi0; + void *tail = vlib_buffer_put_uninit ( + nrb0, remaining_payload_length); + clib_memcpy (tail, + vlib_buffer_get_current (b0) + + allowed_length, + remaining_payload_length); + } + } + pvti_rx_peer0->rx_streams[stream_index] + .rx_received_inner_length += chunk_payload_length; + if (pvti_rx_peer0->rx_streams[stream_index] + .rx_received_inner_length == + pvti_rx_peer0->rx_streams[stream_index] + .rx_expected_inner_length) + { + next0 = rx_stream0->rx_next0; + pvti_enqueue_rx_bi_to_next_and_trace ( + vm, node, ptd, rx_stream0->rx_bi0_first, next0); + pkts_decapsulated += 1; + + // clean out the current reassemly state + rx_stream0->rx_bi0 = INDEX_INVALID; + rx_stream0->rx_bi0_first = INDEX_INVALID; + pvti_rx_peer0->rx_streams[stream_index] + .rx_received_inner_length = 0; + pvti_rx_peer0->rx_streams[stream_index] + .rx_expected_inner_length = 0; + rx_stream0->rx_next0 = 0; + } + } + chunks[true_chunk_count] = pvc0; + true_chunk_count += 1; + vlib_buffer_advance (b0, chunk_payload_length); + } + } + else + { + /* Sequence does not match, skip the reassembly chunks and reset + * the reassembly state */ + + while ((b0->current_length > 0) && + true_chunk_count < pvti0->reass_chunk_count) + { + /* skip the reassembly chunks */ + pvti_chunk_header_t *pvc0 = vlib_buffer_get_current (b0); + chunks[true_chunk_count] = pvc0; + true_chunk_count += 1; + vlib_buffer_advance ( + b0, clib_net_to_host_u16 (pvc0->total_chunk_length)); + } + // FIXME: discard the current reassembly state, reset the seq# + if (rx_stream0->rx_bi0_first != INDEX_INVALID) + { + clib_warning ("RX PVTI: discard chunk being reassembled"); + vlib_buffer_free_one (vm, rx_stream0->rx_bi0_first); + rx_stream0->rx_bi0 = INDEX_INVALID; + rx_stream0->rx_bi0_first = INDEX_INVALID; + rx_stream0->rx_received_inner_length = 0; + rx_stream0->rx_expected_inner_length = 0; + rx_stream0->rx_next0 = 0; + } + } + + while ((b0->current_length > 0) && true_chunk_count < max_chunk_count) + { + if (b0->current_length < sizeof (pvti_chunk_header_t)) + { + clib_warning ("RX ERR: length too short for a chunk"); + break; + } + pvti_chunk_header_t *pvc0 = vlib_buffer_get_current (b0); + chunks[true_chunk_count] = pvc0; + true_chunk_count += 1; + u16 total_chunk_length = + clib_net_to_host_u16 (pvc0->total_chunk_length); + if (b0->current_length < total_chunk_length) + { + clib_warning ("RX ERR: length 0x%x too big for a chunk", + true_chunk_count); + break; + } + u8 *pkt = (u8 *) (pvc0 + 1); + u16 inner_length; + if (rx_stream0->rx_bi0_first != INDEX_INVALID) + { + vlib_buffer_free_one (vm, rx_stream0->rx_bi0_first); + rx_stream0->rx_bi0 = INDEX_INVALID; + rx_stream0->rx_bi0_first = INDEX_INVALID; + rx_stream0->rx_received_inner_length = 0; + rx_stream0->rx_expected_inner_length = 0; + rx_stream0->rx_next0 = 0; + } + + switch (*pkt & 0xf0) + { + case 0x40: + next0 = PVTI_INPUT_NEXT_IP4_INPUT; + inner_length = clib_net_to_host_u16 (*((u16 *) (pkt + 2))); + break; + case 0x60: + next0 = PVTI_INPUT_NEXT_IP6_INPUT; + inner_length = clib_net_to_host_u16 (*((u16 *) (pkt + 4))) + + sizeof (ip6_header_t); + break; + default: + next0 = PVTI_INPUT_NEXT_DROP; + vlib_buffer_advance (b0, total_chunk_length); + continue; + } + vlib_buffer_advance (b0, sizeof (pvti_chunk_header_t)); + + if (inner_length + sizeof (pvti_chunk_header_t) > total_chunk_length) + { + /* FIXME: the packet size is larger than the chunk -> it's a + * first fragment */ + // enqueue the chunk and finish packet processing. + // There must be no active reassembly. + ASSERT (rx_stream0->rx_bi0_first == INDEX_INVALID); + rx_stream0->rx_next0 = next0; + rx_stream0->rx_bi0 = bi0; + rx_stream0->rx_bi0_first = bi0; + rx_stream0->rx_expected_inner_length = inner_length; + rx_stream0->rx_received_inner_length = + total_chunk_length - sizeof (pvti_chunk_header_t); + rx_stream0->last_rx_seq = new_seq0; + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + pvti_input_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->next_index = ~0; + t->trace_type = PVTI_INPUT_TRACE_enqueue; + clib_memcpy (t->packet_data, vlib_buffer_get_current (b0), + sizeof (t->packet_data)); + } + goto continue_outer; + } + + u32 nbi0 = pvti_get_new_buffer (vm); + if (INDEX_INVALID == nbi0) + { + decap_failed_no_buffers += 1; + continue; + }; + vlib_buffer_t *nb0 = vlib_get_buffer (vm, nbi0); + pvti_if_t *pvti_if0 = pvti_if_get (pvti_rx_peer0->pvti_if_index); + vnet_buffer (nb0)->sw_if_index[VLIB_RX] = pvti_if0->sw_if_index; + void *new_packet = vlib_buffer_put_uninit (nb0, inner_length); + clib_memcpy (new_packet, pvc0 + 1, inner_length); + vlib_buffer_advance (b0, inner_length); + + pvti_enqueue_rx_bi_to_next_and_trace (vm, node, ptd, nbi0, next0); + pkts_decapsulated += 1; + } + /* we have processed all the chunks from the buffer, but the buffer + * remains. Free it. */ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + pvti_input_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->next_index = ~0; + t->trace_type = PVTI_INPUT_TRACE_free; + t->seq = clib_net_to_host_u32 (pvti0->seq); + t->chunk_count = pvti0->chunk_count; + u8 chunk_count = + pvti0->chunk_count < MAX_CHUNKS ? pvti0->chunk_count : MAX_CHUNKS; + for (int i = 0; i < chunk_count; i++) + { + t->chunks[i].total_chunk_length = + clib_net_to_host_u16 (chunks[i]->total_chunk_length); + } + clib_memcpy (t->packet_data, vlib_buffer_get_current (b0), + sizeof (t->packet_data)); + } + vlib_buffer_free_one (vm, bi0); + + continue_outer: + pkts_processed += 1; + continue; + + drop_and_maybe_trace: + next0 = PVTI_INPUT_NEXT_DROP; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + int i; + pvti_input_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->trace_type = PVTI_INPUT_TRACE_drop; + t->next_index = next0; + t->remote_ip.ip.ip4 = h0->ip4.src_address; + t->remote_ip.version = AF_IP4; + t->local_port = h0->udp.dst_port; + t->remote_port = h0->udp.src_port; + if (!pvti_rx_peer0) + { + t->seq = 0xdeaddead; + } + else + { + t->seq = clib_net_to_host_u32 (pvti0->seq); + t->chunk_count = pvti0->chunk_count; + u8 chunk_count = pvti0->chunk_count < MAX_CHUNKS ? + pvti0->chunk_count : + MAX_CHUNKS; + for (i = 0; i < chunk_count; i++) + { + t->chunks[i].total_chunk_length = + clib_net_to_host_u16 (chunks[i]->total_chunk_length); + } + } + } + + pkts_processed += 1; + vec_add1 (ptd->pending_rx_buffers, bi0); + vec_add1 (ptd->pending_rx_nexts, next0); + } + + vlib_buffer_enqueue_to_next_vec (vm, node, &ptd->pending_rx_buffers, + &ptd->pending_rx_nexts, + vec_len (ptd->pending_rx_nexts)); + vec_reset_length (ptd->pending_rx_buffers); + vec_reset_length (ptd->pending_rx_nexts); + + vlib_node_increment_counter (vm, node->node_index, + PVTI_INPUT_ERROR_PROCESSED, pkts_processed); + vlib_node_increment_counter ( + vm, node->node_index, PVTI_INPUT_ERROR_DECAPSULATED, pkts_decapsulated); + vlib_node_increment_counter (vm, node->node_index, + PVTI_INPUT_ERROR_NO_BUFFERS, + decap_failed_no_buffers); + return frame->n_vectors; +} + +VLIB_NODE_FN (pvti4_input_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return pvti_input_node_common (vm, node, frame, 0); +} + +VLIB_NODE_FN (pvti6_input_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return pvti_input_node_common (vm, node, frame, 1); +} diff --git a/src/plugins/pvti/input.h b/src/plugins/pvti/input.h new file mode 100644 index 00000000000..02a186cde05 --- /dev/null +++ b/src/plugins/pvti/input.h @@ -0,0 +1,87 @@ +/* + * 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. + */ +#ifndef __included_pvti_input_h__ +#define __included_pvti_input_h__ + +#include <vlib/vlib.h> +#include <vnet/vnet.h> +#include <vnet/pg/pg.h> +#include <vppinfra/error.h> +#include <pvti/pvti.h> +#include <pvti/pvti_if.h> + +typedef struct +{ + u16 total_chunk_length; +} pvti_input_chunk_t; + +#define MAX_CHUNKS 32 +#define PVTI_RX_MAX_LENGTH 2048 + +typedef struct +{ + u32 next_index; + u32 sw_if_index; + ip_address_t remote_ip; + u16 remote_port; + u16 local_port; + u32 seq; + pvti_input_chunk_t chunks[MAX_CHUNKS]; + u8 chunk_count; + u8 trace_type; + u8 packet_data[64]; +} pvti_input_trace_t; + +#define foreach_pvti_input_trace_type \ + _ (drop, "drop") \ + _ (decap, "decapsulate") \ + _ (free, "free") \ + _ (enqueue, "enqueue") + +typedef enum +{ +#define _(f, s) PVTI_INPUT_TRACE_##f, + foreach_pvti_input_trace_type +#undef _ + PVTI_INPUT_TRACE_N_TYPES, +} pvti_input_trace_type_t; + +#define foreach_pvti_input_error \ + _ (PROCESSED, "PVTI tunneled packets processed") \ + _ (DECAPSULATED, "PVTI inner packets decapsulated") \ + _ (PEER, "Could not find a peer") \ + _ (NOCHUNKS, "Packet has no chunks") \ + _ (NO_BUFFERS, "No buffers available to decapsulate") \ + _ (TOOMANYREASS, "Packet has more reassembly chunks than total") \ + _ (PACKET_TOO_SHORT, "Packet too short") + +typedef enum +{ +#define _(sym, str) PVTI_INPUT_ERROR_##sym, + foreach_pvti_input_error +#undef _ + PVTI_INPUT_N_ERROR, +} pvti_input_error_t; + +typedef enum +{ + PVTI_INPUT_NEXT_DROP, + PVTI_INPUT_NEXT_IP4_INPUT, + PVTI_INPUT_NEXT_IP6_INPUT, + PVTI_INPUT_NEXT_PUNT, + PVTI_INPUT_N_NEXT, +} pvti_input_next_t; + +#endif // pvti_input_h diff --git a/src/plugins/pvti/output-main.c b/src/plugins/pvti/output-main.c new file mode 100644 index 00000000000..ae4ae5f8e98 --- /dev/null +++ b/src/plugins/pvti/output-main.c @@ -0,0 +1,85 @@ +/* + * 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 <pvti/output.h> + +/* packet trace format function */ +static u8 * +format_pvti_output_trace (u8 *s, va_list *args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + pvti_output_trace_t *t = va_arg (*args, pvti_output_trace_t *); + + u32 indent = format_get_indent (s); + s = + format (s, "PVTI-OUT(%d): sw_if_index %d, next index %d, underlay_mtu %d,", + t->trace_type, t->sw_if_index, t->next_index, t->underlay_mtu); + s = format (s, "\n%U stream_index %d, bi0_max_current_length %d, tx_seq %d", + format_white_space, indent, t->stream_index, + t->bi0_max_current_length, t->tx_seq); + s = format (s, "\n%U%U", format_white_space, indent, + format_ip_adjacency_packet_data, t->packet_data, + sizeof (t->packet_data)); + + return s; +} + +vlib_node_registration_t pvti_output_node; + +static char *pvti_output_error_strings[] = { +#define _(sym, string) string, + foreach_pvti_output_error +#undef _ +}; + +VLIB_REGISTER_NODE (pvti4_output_node) = +{ + .name = "pvti4-output", + .vector_size = sizeof (u32), + .format_trace = format_pvti_output_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(pvti_output_error_strings), + .error_strings = pvti_output_error_strings, + + .n_next_nodes = PVTI_OUTPUT_N_NEXT, + + .next_nodes = { + [PVTI_OUTPUT_NEXT_DROP] = "error-drop", + [PVTI_OUTPUT_NEXT_INTERFACE_OUTPUT] = "adj-midchain-tx", + [PVTI_OUTPUT_NEXT_IP4_LOOKUP] = "ip4-lookup", + [PVTI_OUTPUT_NEXT_IP6_LOOKUP] = "ip6-lookup", + }, + +}; +VLIB_REGISTER_NODE (pvti6_output_node) = +{ + .name = "pvti6-output", + .vector_size = sizeof (u32), + .format_trace = format_pvti_output_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(pvti_output_error_strings), + .error_strings = pvti_output_error_strings, + + .n_next_nodes = PVTI_OUTPUT_N_NEXT, + + .next_nodes = { + [PVTI_OUTPUT_NEXT_DROP] = "error-drop", + [PVTI_OUTPUT_NEXT_INTERFACE_OUTPUT] = "adj-midchain-tx", + [PVTI_OUTPUT_NEXT_IP4_LOOKUP] = "ip4-lookup", + [PVTI_OUTPUT_NEXT_IP6_LOOKUP] = "ip6-lookup", + }, + +}; diff --git a/src/plugins/pvti/output.c b/src/plugins/pvti/output.c new file mode 100644 index 00000000000..1939c6f585a --- /dev/null +++ b/src/plugins/pvti/output.c @@ -0,0 +1,543 @@ +/* + * 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/vnet.h> +#include <vnet/pg/pg.h> +#include <vppinfra/error.h> +#include <pvti/pvti.h> +#include <pvti/pvti_if.h> +#include <pvti/output.h> + +static_always_inline u32 +ip6_vtcfl (u8 stream_index) +{ + u32 vtcfl = 0x6 << 28; + vtcfl |= stream_index; + + return (clib_host_to_net_u32 (vtcfl)); +} + +always_inline vlib_buffer_t * +pvti_alloc_new_tx_buffer (vlib_main_t *vm) +{ + u32 bi0 = INDEX_INVALID; + if (vlib_buffer_alloc (vm, &bi0, 1) != 1) + { + return 0; + } + vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0); + b0->current_data = 0; + b0->current_length = 0; + return b0; +} + +always_inline bool +pvti_find_or_try_create_tx_peer (vlib_main_t *vm, pvti_per_thread_data_t *ptd, + pvti_if_t *pvti_if0, ip_address_t *remote_ip, + u16 remote_port, u32 *out_index) +{ + + pvti_tx_peer_t *peer; + pool_foreach (peer, ptd->tx_peers) + { + if (peer->remote_port == remote_port && + 0 == ip_address_cmp (remote_ip, &peer->remote_ip)) + { + if (peer->deleted) + { + // Bad luck, the peer has been deleted. + u32 boi0 = vlib_get_buffer_index (vm, peer->bo0); + if (peer->bo0) + { + vlib_buffer_free (vm, &boi0, 1); + } + clib_memset (peer, 0xca, sizeof (*peer)); + pool_put (ptd->tx_peers, peer); + continue; + } + *out_index = peer - ptd->tx_peers; + return 1; + } + } + + ip_address_family_t dst_ver = ip_addr_version (&pvti_if0->remote_ip); + + u16 pvti_encap_overhead = (dst_ver == AF_IP6) ? + sizeof (pvti_ip6_encap_header_t) : + sizeof (pvti_ip4_encap_header_t); + + u16 pvti_packet_overhead = + pvti_encap_overhead + sizeof (pvti_packet_header_t) + PVTI_ALIGN_BYTES; + + ASSERT (pvti_if0->underlay_mtu > pvti_packet_overhead); + + u32 bo0_max_current_length = pvti_if0->underlay_mtu - pvti_packet_overhead; + + vlib_buffer_t *bo0 = pvti_alloc_new_tx_buffer (vm); + + if (!bo0) + { + return 0; + } + + pvti_tx_peer_t new_peer = { + .local_ip = pvti_if0->local_ip, + .remote_ip = *remote_ip, + .local_port = pvti_if0->local_port, + .remote_port = remote_port, + .underlay_mtu = pvti_if0->underlay_mtu, + .underlay_fib_index = pvti_if0->underlay_fib_index, + .bo0_max_current_length = bo0_max_current_length, + .pvti_if_index = pvti_if_get_index (pvti_if0), + .deleted = 0, + .bo0 = bo0, + .chunk_count = 0, + .reass_chunk_count = 0, + .current_tx_seq = 42, + }; + + pvti_tx_peer_t *tx_new_peer; + pool_get (ptd->tx_peers, tx_new_peer); + + *tx_new_peer = new_peer; + *out_index = tx_new_peer - ptd->tx_peers; + return 1; +} + +always_inline bool +pvti_try_get_tx_peer_index (vlib_main_t *vm, pvti_per_thread_data_t *ptd, + pvti_if_t *pvti_if0, vlib_buffer_t *b0, + bool is_ip6, u32 *out_index) +{ + if (pvti_if0->peer_address_from_payload) + { + ip_address_t remote_ip = { 0 }; + if (is_ip6) + { + ip6_header_t *ip6 = vlib_buffer_get_current (b0); + ip_address_set (&remote_ip, &ip6->dst_address, AF_IP6); + } + else + { + ip4_header_t *ip4 = vlib_buffer_get_current (b0); + ip_address_set (&remote_ip, &ip4->dst_address, AF_IP4); + } + return pvti_find_or_try_create_tx_peer ( + vm, ptd, pvti_if0, &remote_ip, pvti_if0->remote_port, out_index); + } + else + { + return pvti_find_or_try_create_tx_peer ( + vm, ptd, pvti_if0, &pvti_if0->remote_ip, pvti_if0->remote_port, + out_index); + } + /* not reached */ +} + +always_inline void +pvti_finalize_chunk (pvti_tx_peer_t *tx_peer, + pvti_chunk_header_t *chunk_header, u8 *tail, + bool is_reassembly_chunk) +{ + clib_memset (chunk_header, 0xab, sizeof (pvti_chunk_header_t)); + chunk_header->total_chunk_length = + clib_host_to_net_u16 (tail - (u8 *) chunk_header); + tx_peer->chunk_count++; + if (is_reassembly_chunk) + { + tx_peer->reass_chunk_count++; + } +} + +always_inline pvti_output_next_t +encap_pvti_buffer_ip46 (vlib_main_t *vm, vlib_node_runtime_t *node, + pvti_tx_peer_t *tx_peer, int is_ip6) +{ + ip_address_family_t src_ver = ip_addr_version (&tx_peer->local_ip); + ip_address_family_t dst_ver = ip_addr_version (&tx_peer->remote_ip); + u8 stream_index = 0; + + ASSERT (src_ver == dst_ver); + bool is_ip6_encap = (AF_IP6 == src_ver); + + vlib_buffer_t *b0 = tx_peer->bo0; + vlib_buffer_advance (b0, + -(sizeof (pvti_packet_header_t) + PVTI_ALIGN_BYTES)); + + pvti_packet_header_t *pvti0 = vlib_buffer_get_current (b0); + clib_memset (pvti0, 0xca, sizeof (*pvti0) + PVTI_ALIGN_BYTES); + pvti0->pad_bytes = PVTI_ALIGN_BYTES; + + pvti0->seq = clib_host_to_net_u32 (tx_peer->current_tx_seq); + pvti0->stream_index = stream_index; + pvti0->reass_chunk_count = tx_peer->reass_chunk_count; + pvti0->chunk_count = tx_peer->chunk_count; + pvti0->mandatory_flags_mask = 0; + pvti0->flags_value = 0; + + if (is_ip6_encap) + { + vlib_buffer_advance (b0, -(sizeof (pvti_ip6_encap_header_t))); + if (b0->current_data < -VLIB_BUFFER_PRE_DATA_SIZE) + { + // undo the change + vlib_buffer_advance (b0, (sizeof (pvti_ip6_encap_header_t))); + b0->error = node->errors[PVTI_OUTPUT_ERROR_NO_PRE_SPACE]; + return PVTI_OUTPUT_NEXT_DROP; + } + pvti_ip6_encap_header_t *ve = vlib_buffer_get_current (b0); + + ve->udp.src_port = clib_host_to_net_u16 (tx_peer->local_port); + ve->udp.dst_port = clib_host_to_net_u16 (tx_peer->remote_port); + ve->udp.length = clib_host_to_net_u16 ( + b0->current_length - offsetof (pvti_ip6_encap_header_t, udp)); + ve->udp.checksum = 0; + + ve->ip6.ip_version_traffic_class_and_flow_label = + ip6_vtcfl (stream_index); + ve->ip6.payload_length = ve->udp.length; + ve->ip6.protocol = 17; + ve->ip6.hop_limit = 128; + ip_address_copy_addr (&ve->ip6.src_address, &tx_peer->local_ip); + ip_address_copy_addr (&ve->ip6.dst_address, &tx_peer->remote_ip); + } + else + { + vlib_buffer_advance (b0, -(sizeof (pvti_ip4_encap_header_t))); + if (b0->current_data < -VLIB_BUFFER_PRE_DATA_SIZE) + { + // undo the change + vlib_buffer_advance (b0, (sizeof (pvti_ip4_encap_header_t))); + b0->error = node->errors[PVTI_OUTPUT_ERROR_NO_PRE_SPACE]; + return PVTI_OUTPUT_NEXT_DROP; + } + pvti_ip4_encap_header_t *ve = vlib_buffer_get_current (b0); + + ve->udp.src_port = clib_host_to_net_u16 (tx_peer->local_port); + ve->udp.dst_port = clib_host_to_net_u16 (tx_peer->remote_port); + ve->udp.length = clib_host_to_net_u16 ( + b0->current_length - offsetof (pvti_ip4_encap_header_t, udp)); + ve->udp.checksum = 0; + + ve->ip4.ip_version_and_header_length = 0x45; + ve->ip4.tos = 0; + ve->ip4.length = clib_host_to_net_u16 (b0->current_length); + ve->ip4.fragment_id = + clib_host_to_net_u16 (tx_peer->current_tx_seq & 0xffff); + ve->ip4.flags_and_fragment_offset = 0; + ve->ip4.ttl = 128; + ve->ip4.protocol = 17; + + ve->ip4.dst_address.as_u32 = ip_addr_v4 (&tx_peer->remote_ip).data_u32; + ve->ip4.src_address.as_u32 = ip_addr_v4 (&tx_peer->local_ip).data_u32; + ve->ip4.checksum = ip4_header_checksum (&ve->ip4); + } + + // This is important, if not reset, causes a crash + vnet_buffer (b0)->sw_if_index[VLIB_TX] = tx_peer->underlay_fib_index; + + // vnet_buffer (b0)->oflags |= VNET_BUFFER_OFFLOAD_F_IP_CKSUM; + return is_ip6_encap ? PVTI_OUTPUT_NEXT_IP6_LOOKUP : + PVTI_OUTPUT_NEXT_IP4_LOOKUP; +} + +always_inline void +pvti_enqueue_tx_and_trace (vlib_main_t *vm, vlib_node_runtime_t *node, + pvti_per_thread_data_t *ptd, vlib_buffer_t *b0, + u16 next0, u8 stream_index, pvti_tx_peer_t *tx_peer) +{ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + tx_peer->is_bo0_traced)) + { + if (PREDICT_TRUE ( + vlib_trace_buffer (vm, node, next0, b0, /* follow_chain */ 0))) + { + + pvti_output_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->next_index = next0; + t->underlay_mtu = tx_peer->underlay_mtu; + t->stream_index = stream_index; + t->trace_type = 1; + t->bi0_max_current_length = tx_peer->bo0_max_current_length; + t->tx_seq = tx_peer->current_tx_seq; + clib_memcpy (t->packet_data, vlib_buffer_get_current (b0), + sizeof (t->packet_data)); + } + } + u32 bi0 = vlib_get_buffer_index (vm, b0); + vec_add1 (ptd->pending_tx_buffers, bi0); + vec_add1 (ptd->pending_tx_nexts, next0); +} + +always_inline void +pvti_enqueue_tx_drop_and_trace (vlib_main_t *vm, vlib_node_runtime_t *node, + pvti_per_thread_data_t *ptd, vlib_buffer_t *b0, + u8 stream_index) +{ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + pvti_output_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->next_index = PVTI_OUTPUT_NEXT_DROP; + t->stream_index = stream_index; + t->trace_type = 0; + clib_memcpy (t->packet_data, vlib_buffer_get_current (b0), + sizeof (t->packet_data)); + } + u32 bi0 = vlib_get_buffer_index (vm, b0); + vec_add1 (ptd->pending_tx_buffers, bi0); + vec_add1 (ptd->pending_tx_nexts, PVTI_OUTPUT_NEXT_DROP); +} + +always_inline bool +pvti_flush_peer_and_recharge (vlib_main_t *vm, vlib_node_runtime_t *node, + pvti_per_thread_data_t *ptd, u32 tx_peer_index, + u8 stream_index, const bool is_ip6) +{ + pvti_tx_peer_t *tx_peer = pool_elt_at_index (ptd->tx_peers, tx_peer_index); + u16 next0 = encap_pvti_buffer_ip46 (vm, node, tx_peer, is_ip6); + + pvti_enqueue_tx_and_trace (vm, node, ptd, tx_peer->bo0, next0, stream_index, + tx_peer); + + tx_peer->bo0 = pvti_alloc_new_tx_buffer (vm); + tx_peer->reass_chunk_count = 0; + tx_peer->chunk_count = 0; + tx_peer->current_tx_seq++; + + return 1; +} + +always_inline u16 +pvti_output_node_common (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame, const bool is_ip6) +{ + pvti_main_t *pvm = &pvti_main; + + u32 n_left_from, *from; + u32 pkts_encapsulated = 0; + u32 pkts_processed = 0; + u32 pkts_chopped = 0; + u32 pkts_overflow = 0; + u32 pkts_overflow_cantfit = 0; + + bool is_node_traced = (node->flags & VLIB_NODE_FLAG_TRACE) ? 1 : 0; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + + u8 stream_index = pvti_get_stream_index (is_ip6); + + u32 thread_index = vlib_get_thread_index (); + pvti_per_thread_data_t *ptd = + vec_elt_at_index (pvm->per_thread_data[is_ip6], thread_index); + + vlib_buffer_t *ibufs[VLIB_FRAME_SIZE], **ib = ibufs; + + vlib_get_buffers (vm, from, ibufs, n_left_from); + + n_left_from = frame->n_vectors; + while (1 && n_left_from > 0) + { + n_left_from -= 1; + vlib_buffer_t *b0 = ib[0]; + ib++; + u32 bi0 = vlib_get_buffer_index (vm, b0); + bool is_b0_traced = + is_node_traced && ((b0->flags & VLIB_BUFFER_IS_TRACED) ? 1 : 0); + pkts_processed += 1; + + u32 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX]; + u32 pvti_index0 = pvti_if_find_by_sw_if_index (sw_if_index0); + if (pvti_index0 == INDEX_INVALID) + { + b0->error = node->errors[PVTI_OUTPUT_ERROR_PEER]; + pvti_enqueue_tx_drop_and_trace (vm, node, ptd, b0, stream_index); + continue; + } + pvti_if_t *pvti_if0 = pvti_if_get (pvti_index0); + u32 tx_peer_index; + if (!pvti_try_get_tx_peer_index (vm, ptd, pvti_if0, b0, is_ip6, + &tx_peer_index)) + { + b0->error = node->errors[PVTI_OUTPUT_ERROR_MAKE_PEER]; + pvti_enqueue_tx_drop_and_trace (vm, node, ptd, b0, stream_index); + continue; + } + pvti_tx_peer_t *tx_peer = &ptd->tx_peers[tx_peer_index]; + + u32 b0_len = vlib_buffer_length_in_chain (vm, b0); + u32 total_chunk_len = sizeof (pvti_chunk_header_t) + b0_len; + + if (tx_peer->bo0_max_current_length >= + tx_peer->bo0->current_length + total_chunk_len) + { + /* Happy case, we can fit the entire new chunk */ + pvti_chunk_header_t *chunk_header = vlib_buffer_put_uninit ( + tx_peer->bo0, sizeof (pvti_chunk_header_t)); + u8 *tail = vlib_buffer_put_uninit (tx_peer->bo0, b0_len); + vlib_buffer_t *b0_curr; + b0_curr = b0; + while (b0_len > 0) + { + clib_memcpy (tail, vlib_buffer_get_current (b0_curr), + b0_curr->current_length); + tail += b0_curr->current_length; + b0_len -= b0_curr->current_length; + ASSERT ((b0_len == 0) || + (b0_curr->flags & VLIB_BUFFER_NEXT_PRESENT)); + if (b0_curr->flags & VLIB_BUFFER_NEXT_PRESENT) + { + b0_curr = vlib_get_buffer (vm, b0_curr->next_buffer); + } + } + tx_peer->is_bo0_traced |= is_b0_traced; + pvti_finalize_chunk (tx_peer, chunk_header, tail, false); + } + else + { + bool is_reassembly = false; + /* FIXME: here, flush a packet if we want to avoid fragmenting it */ +#define PVTI_TINY_PACKET_SZ 20 + int threshold_len = + sizeof (pvti_chunk_header_t) + PVTI_TINY_PACKET_SZ; + + /* Can we fit anything meaningful into bo0 ? if not - flush */ + if (tx_peer->bo0_max_current_length <= + tx_peer->bo0->current_length + threshold_len) + { + if (!pvti_flush_peer_and_recharge (vm, node, ptd, tx_peer_index, + stream_index, is_ip6)) + { + b0->error = node->errors[PVTI_OUTPUT_ERROR_RECHARGE0]; + pvti_enqueue_tx_drop_and_trace (vm, node, ptd, b0, + stream_index); + continue; + } + pkts_encapsulated += 1; + } + + pvti_chunk_header_t *chunk_header = vlib_buffer_put_uninit ( + tx_peer->bo0, sizeof (pvti_chunk_header_t)); + + u8 *tail; + vlib_buffer_t *b0_curr; + /* append the chained buffers and flush as necessary */ + b0_curr = b0; + + int curr_b0_start_offset = 0; + + while (b0_len > 0) + { + ASSERT (tx_peer->bo0_max_current_length > + tx_peer->bo0->current_length); + int copy_len = + clib_min (b0_curr->current_length - curr_b0_start_offset, + tx_peer->bo0_max_current_length - + tx_peer->bo0->current_length); + tail = vlib_buffer_put_uninit (tx_peer->bo0, copy_len); + clib_memcpy (tail, + (u8 *) vlib_buffer_get_current (b0_curr) + + curr_b0_start_offset, + copy_len); + tail += copy_len; + b0_len -= copy_len; + // Advance the start offset or reset it if we copied the entire + // block + curr_b0_start_offset = + curr_b0_start_offset + copy_len == b0_curr->current_length ? + 0 : + curr_b0_start_offset + copy_len; + ASSERT ((b0_len == 0) || (curr_b0_start_offset > 0) || + (b0_curr->flags & VLIB_BUFFER_NEXT_PRESENT)); + if (curr_b0_start_offset > 0) + { + pvti_finalize_chunk (tx_peer, chunk_header, tail, + is_reassembly); + tx_peer->is_bo0_traced |= is_b0_traced; + if (!pvti_flush_peer_and_recharge ( + vm, node, ptd, tx_peer_index, stream_index, is_ip6)) + { + b0->error = node->errors[PVTI_OUTPUT_ERROR_RECHARGE1]; + pvti_enqueue_tx_drop_and_trace (vm, node, ptd, b0, + stream_index); + continue; + } + pkts_encapsulated += 1; + /* next chunk(s) will be reassembly until the next block */ + is_reassembly = true; + chunk_header = vlib_buffer_put_uninit ( + tx_peer->bo0, sizeof (pvti_chunk_header_t)); + } + else + { + if ((b0_curr->flags & VLIB_BUFFER_NEXT_PRESENT)) + { + b0_curr = vlib_get_buffer (vm, b0_curr->next_buffer); + } + else + { + pvti_finalize_chunk (tx_peer, chunk_header, tail, + is_reassembly); + tx_peer->is_bo0_traced |= is_b0_traced; + } + } + } + } + vlib_buffer_free_one (vm, bi0); + } + + int i; + for (i = 0; i < vec_len (ptd->tx_peers); i++) + { + if (ptd->tx_peers[i].chunk_count) + { + pvti_flush_peer_and_recharge (vm, node, ptd, i, stream_index, + is_ip6); + pkts_encapsulated += 1; + } + } + + vlib_buffer_enqueue_to_next_vec (vm, node, &ptd->pending_tx_buffers, + &ptd->pending_tx_nexts, + vec_len (ptd->pending_tx_nexts)); + vec_reset_length (ptd->pending_tx_buffers); + vec_reset_length (ptd->pending_tx_nexts); + + vlib_node_increment_counter ( + vm, node->node_index, PVTI_OUTPUT_ERROR_ENCAPSULATED, pkts_encapsulated); + vlib_node_increment_counter (vm, node->node_index, + PVTI_OUTPUT_ERROR_PROCESSED, pkts_processed); + vlib_node_increment_counter (vm, node->node_index, PVTI_OUTPUT_ERROR_CHOPPED, + pkts_chopped); + vlib_node_increment_counter (vm, node->node_index, + PVTI_OUTPUT_ERROR_OVERFLOW, pkts_overflow); + vlib_node_increment_counter (vm, node->node_index, + PVTI_OUTPUT_ERROR_OVERFLOW_CANTFIT, + pkts_overflow_cantfit); + return frame->n_vectors; +} + +VLIB_NODE_FN (pvti4_output_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return pvti_output_node_common (vm, node, frame, 0); +} + +VLIB_NODE_FN (pvti6_output_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return pvti_output_node_common (vm, node, frame, 1); +} diff --git a/src/plugins/pvti/output.h b/src/plugins/pvti/output.h new file mode 100644 index 00000000000..95e78ba9720 --- /dev/null +++ b/src/plugins/pvti/output.h @@ -0,0 +1,75 @@ +/* + * 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. + */ +#ifndef __included_pvti_output_h__ +#define __included_pvti_output_h__ + +#include <vlib/vlib.h> +#include <vnet/vnet.h> +#include <vnet/pg/pg.h> +#include <vppinfra/error.h> +#include <pvti/pvti.h> +#include <pvti/pvti_if.h> + +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u32 tx_seq; + u16 underlay_mtu; + u16 bi0_max_current_length; + u8 stream_index; + u8 trace_type; + u8 packet_data[96]; +} pvti_output_trace_t; + +#define foreach_pvti_output_error \ + _ (NONE, "No error") \ + _ (PROCESSED, "Packets processed") \ + _ (ENCAPSULATED, "Packets encapsulated") \ + _ (PEER, "No peer found") \ + _ (MAKE_PEER, "Could not make peer") \ + _ (RECHARGE0, "Could not recharge 0") \ + _ (RECHARGE1, "Could not recharge 1") \ + _ (NO_PRE_SPACE, "Not enought pre-data space") \ + _ (CHOPPED, "Packets chopped") \ + _ (OVERFLOW, "Packets overflowed") \ + _ (OVERFLOW_CANTFIT, "Packets overflowed and cant fit excess") + +typedef enum +{ +#define _(sym, str) PVTI_OUTPUT_ERROR_##sym, + foreach_pvti_output_error +#undef _ + PVTI_OUTPUT_N_ERROR, +} pvti_output_error_t; + +typedef enum +{ + PVTI_INDEPENDENT_CHUNK = 0, + PVTI_REASS_CHUNK, +} pvti_chunk_type_t; + +#define MAX_CURR_LEN_UNKNOWN 0xffff + +typedef enum +{ + PVTI_OUTPUT_NEXT_DROP, + PVTI_OUTPUT_NEXT_INTERFACE_OUTPUT, + PVTI_OUTPUT_NEXT_IP4_LOOKUP, + PVTI_OUTPUT_NEXT_IP6_LOOKUP, + PVTI_OUTPUT_N_NEXT, +} pvti_output_next_t; + +#endif // pvti_output_h diff --git a/src/plugins/pvti/pvti.api b/src/plugins/pvti/pvti.api new file mode 100644 index 00000000000..859ed1ab6b0 --- /dev/null +++ b/src/plugins/pvti/pvti.api @@ -0,0 +1,111 @@ +/* + * pvti.api - binary API skeleton + * + * 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. + */ + +/** + * @file pvti.api + * @brief VPP control-plane API messages. + * + * This file defines VPP control-plane binary API messages which are generally + * called through a shared memory interface. + */ + +/* Version and type recitations */ + +option version = "0.0.1"; +import "vnet/interface_types.api"; +import "vnet/ip/ip_types.api"; + +/** \brief A composite type uniquely defining a PVTI tunnel. + @param sw_if_index - ignored on create/delete, present in details. + @param src_ip - Source IP address + @param src_port - Source UDP port + @param dst_ip - Destination IP address + @param dst_port - Destination UDP port + @param underlay_mtu - Underlay MTU for packet splitting/coalescing + @param underlay_fib_index - Underlay FIB index to be used after encap +*/ +typedef pvti_tunnel +{ + vl_api_interface_index_t sw_if_index; + vl_api_address_t local_ip; + u16 local_port; + vl_api_address_t remote_ip; + bool peer_address_from_payload; + u16 remote_port; + u16 underlay_mtu; + u32 underlay_fib_index; +}; + + +/** @brief API to enable / disable pvti on an interface + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param enable_disable - 1 to enable, 0 to disable the feature + @param sw_if_index - interface handle +*/ + +define pvti_interface_create +{ + option status="in_progress"; + + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + vl_api_pvti_tunnel_t interface; +}; + +define pvti_interface_create_reply +{ + option status="in_progress"; + u32 context; + i32 retval; + + /* Index for the newly created interface */ + vl_api_interface_index_t sw_if_index; +}; + +autoreply define pvti_interface_delete { + option status="in_progress"; + + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + vl_api_interface_index_t sw_if_index; +}; + + +define pvti_interface_dump +{ + option status="in_progress"; + u32 client_index; + u32 context; + vl_api_interface_index_t sw_if_index; +}; + +define pvti_interface_details +{ + option status="in_progress"; + u32 context; + vl_api_pvti_tunnel_t interface; +}; + + diff --git a/src/plugins/pvti/pvti.c b/src/plugins/pvti/pvti.c new file mode 100644 index 00000000000..646276dec09 --- /dev/null +++ b/src/plugins/pvti/pvti.c @@ -0,0 +1,479 @@ +/* + * 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 <vnet/vnet.h> +#include <vnet/plugin/plugin.h> +#include <vnet/fib/fib_table.h> +#include <pvti/pvti.h> + +#include <vlibapi/api.h> +#include <vlibmemory/api.h> +#include <vpp/app/version.h> +#include <stdbool.h> + +#include <pvti/pvti.api_enum.h> +#include <pvti/pvti.api_types.h> + +#include <pvti/pvti_if.h> + +#define REPLY_MSG_ID_BASE pmp->msg_id_base +#include <vlibapi/api_helper_macros.h> +#include <vnet/ip/ip_format_fns.h> + +pvti_main_t pvti_main; + +u8 * +format_pvti_tx_peer_ptr (u8 *s, va_list *args) +{ + pvti_tx_peer_t *peer = va_arg (*args, pvti_tx_peer_t *); + + s = format ( + s, + "[%p]%s local:%U:%d remote:%U:%d underlay_mtu:%d underlay_fib_idx:%d " + "pvti_idx:%d b0_max_clen:%d cseq:%d chunk_count:%d reass_chunk_count:%d", + peer, peer->deleted ? " DELETED" : "", format_ip46_address, + &peer->local_ip, IP46_TYPE_ANY, peer->local_port, format_ip46_address, + &peer->remote_ip, IP46_TYPE_ANY, peer->remote_port, peer->underlay_mtu, + peer->underlay_fib_index, peer->pvti_if_index, + peer->bo0_max_current_length, peer->current_tx_seq, peer->chunk_count, + peer->reass_chunk_count); + + return (s); +} + +u8 * +format_pvti_rx_peer_ptr (u8 *s, va_list *args) +{ + pvti_rx_peer_t *peer = va_arg (*args, pvti_rx_peer_t *); + + s = format (s, "[%p]%s local:%U:%d remote:%U:%d pvti_idx:%d", peer, + peer->deleted ? " DELETED" : "", format_ip46_address, + &peer->local_ip, IP46_TYPE_ANY, peer->local_port, + format_ip46_address, &peer->remote_ip, IP46_TYPE_ANY, + peer->remote_port, peer->pvti_if_index); + + return (s); +} + +void +pvti_verify_initialized (pvti_main_t *pvm) +{ + if (!pvm->is_initialized) + { + const int n_threads = vlib_get_n_threads (); + vec_validate (pvm->per_thread_data[0], n_threads - 1); + vec_validate (pvm->per_thread_data[1], n_threads - 1); + pvm->is_initialized = 1; + } +} + +void +vnet_int_pvti_bypass_mode (u32 sw_if_index, u8 is_ip6, u8 is_enable) +{ + pvti_main_t *pvm = &pvti_main; + + if (pool_is_free_index (pvm->vnet_main->interface_main.sw_interfaces, + sw_if_index)) + return; + + pvti_verify_initialized (pvm); + + is_enable = !!is_enable; + + if (is_ip6) + { + if (clib_bitmap_get (pvm->bm_ip6_bypass_enabled_by_sw_if, sw_if_index) != + is_enable) + { + vnet_feature_enable_disable ("ip6-unicast", "ip6-pvti-bypass", + sw_if_index, is_enable, 0, 0); + pvm->bm_ip6_bypass_enabled_by_sw_if = clib_bitmap_set ( + pvm->bm_ip6_bypass_enabled_by_sw_if, sw_if_index, is_enable); + } + } + else + { + if (clib_bitmap_get (pvm->bm_ip4_bypass_enabled_by_sw_if, sw_if_index) != + is_enable) + { + vnet_feature_enable_disable ("ip4-unicast", "ip4-pvti-bypass", + sw_if_index, is_enable, 0, 0); + pvm->bm_ip4_bypass_enabled_by_sw_if = clib_bitmap_set ( + pvm->bm_ip4_bypass_enabled_by_sw_if, sw_if_index, is_enable); + } + } +} + +static clib_error_t * +set_ip_pvti_bypass (u32 is_ip6, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + vnet_main_t *vnm = vnet_get_main (); + clib_error_t *error = 0; + u32 sw_if_index, is_enable; + + sw_if_index = ~0; + is_enable = 1; + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat_user (line_input, unformat_vnet_sw_interface, vnm, + &sw_if_index)) + ; + else if (unformat (line_input, "del")) + is_enable = 0; + else + { + error = unformat_parse_error (line_input); + goto done; + } + } + + if (~0 == sw_if_index) + { + error = clib_error_return (0, "unknown interface `%U'", + format_unformat_error, line_input); + goto done; + } + + vnet_int_pvti_bypass_mode (sw_if_index, is_ip6, is_enable); + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +set_ip4_pvti_bypass (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + return set_ip_pvti_bypass (0, input, cmd); +} + +VLIB_CLI_COMMAND (set_interface_ip_pvti_bypass_command, static) = { + .path = "set interface ip pvti-bypass", + .function = set_ip4_pvti_bypass, + .short_help = "set interface ip pvti-bypass <interface> [del]", +}; + +static clib_error_t * +set_ip6_pvti_bypass (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + return set_ip_pvti_bypass (1, input, cmd); +} + +VLIB_CLI_COMMAND (set_interface_ip6_pvti_bypass_command, static) = { + .path = "set interface ip6 pvti-bypass", + .function = set_ip6_pvti_bypass, + .short_help = "set interface ip6 pvti-bypass <interface> [del]", +}; + +static clib_error_t * +pvti_interface_create_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + + // pvti_main_t * pmp = &pvti_main; + u32 sw_if_index = ~0; + int rv = 0; + ip_address_t peer_ip = { 0 }; + ip_address_t local_ip = { 0 }; + u32 peer_port = 0; + u32 local_port = 12345; + u32 underlay_mtu = 1500; + u32 underlay_fib_index = ~0; + u32 underlay_table_id = ~0; + pvti_peer_address_method_t peer_address_method = PVTI_PEER_ADDRESS_FIXED; + bool peer_set = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "peer %U %d %d", unformat_ip_address, &peer_ip, + &peer_port, &local_port)) + { + peer_set = 1; + } + else if (unformat (line_input, "underlay-mtu %d", &underlay_mtu)) + { + // MTU set + } + else if (unformat (line_input, "local-ip %U", unformat_ip_address, + &local_ip)) + { + // local IP set + } + else if (unformat (line_input, "underlay-fib %d", &underlay_fib_index)) + { + // underlay fib set + } + else if (unformat (line_input, "peer-address-from-payload")) + { + peer_address_method = PVTI_PEER_ADDRESS_FROM_PAYLOAD; + } + else if (unformat (line_input, "underlay-table %d", &underlay_table_id)) + { + fib_protocol_t fib_proto = FIB_PROTOCOL_IP4; + if (peer_ip.version == AF_IP6) + { + fib_proto = FIB_PROTOCOL_IP6; + } + u32 fib_index = fib_table_find (fib_proto, underlay_table_id); + + if (~0 == fib_index) + { + error = clib_error_return (0, "Nonexistent table id %d", + underlay_table_id); + goto done; + } + underlay_fib_index = fib_index; + } + else + break; + } + if (!peer_set) + { + error = clib_error_return (0, "Please specify a peer..."); + goto done; + } + + rv = pvti_if_create (&local_ip, local_port, &peer_ip, peer_port, + peer_address_method, underlay_mtu, underlay_fib_index, + &sw_if_index); + + switch (rv) + { + case 0: + break; + + case VNET_API_ERROR_INVALID_SW_IF_INDEX: + error = clib_error_return (0, "Invalid interface"); + break; + + default: + error = clib_error_return (0, "pvti_if_create returned %d", rv); + } +done: + unformat_free (line_input); + return error; +} + +static clib_error_t * +pvti_interface_delete_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + // pvti_main_t * pmp = &pvti_main; + u32 sw_if_index = ~0; + int rv = 0; + bool if_index_set = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "if-index %d", &sw_if_index)) + { + if_index_set = 1; + } + else + break; + } + if (!if_index_set) + return clib_error_return (0, "Please specify a sw_if_index..."); + + rv = pvti_if_delete (sw_if_index); + + switch (rv) + { + case 0: + break; + + case VNET_API_ERROR_INVALID_SW_IF_INDEX: + return clib_error_return (0, "Invalid interface"); + break; + + default: + return clib_error_return (0, "pvti_if_delete returned %d", rv); + } + return 0; +} + +VLIB_CLI_COMMAND (pvti_interface_create_command, static) = { + .path = "pvti interface create", + .short_help = + "pvti interface create peer <remote-ip> <remote-port> <local-port> [ " + "local-ip <ip-addr> ][ underlay-mtu <MTU>][underlay-table " + "<table-index>][inderlay-fib <fib-index>]", + .function = pvti_interface_create_command_fn, +}; + +VLIB_CLI_COMMAND (pvti_interface_delete_command, static) = { + .path = "pvti interface delete", + .short_help = "pvti interface delete if-index <sw-ifindex>", + .function = pvti_interface_delete_command_fn, +}; + +static clib_error_t * +pvti_show_interface_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + pvti_if_t *pvti_if; + vec_foreach (pvti_if, pvti_main.if_pool) + { + int index = pvti_if - pvti_main.if_pool; + vlib_cli_output (vm, "%U", format_pvti_if, index); + }; + return 0; +} + +static clib_error_t * +pvti_show_tx_peers_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + pvti_per_thread_data_t *ptd; + int is_ip6; + for (is_ip6 = 0; is_ip6 <= 1; is_ip6++) + { + vec_foreach (ptd, pvti_main.per_thread_data[is_ip6]) + { + vlib_cli_output (vm, "thread %d (%s)", + ptd - pvti_main.per_thread_data[is_ip6], + is_ip6 ? "IPv6" : "IPv4"); + pvti_tx_peer_t *peer; + vec_foreach (peer, ptd->tx_peers) + { + vlib_cli_output (vm, " %U", format_pvti_tx_peer_ptr, peer); + } + } + } + return 0; +} + +static clib_error_t * +pvti_show_rx_peers_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + pvti_per_thread_data_t *ptd; + int is_ip6; + for (is_ip6 = 0; is_ip6 <= 1; is_ip6++) + { + vec_foreach (ptd, pvti_main.per_thread_data[is_ip6]) + { + vlib_cli_output (vm, "thread %d (%s)", + ptd - pvti_main.per_thread_data[is_ip6], + is_ip6 ? "IPv6" : "IPv4"); + pvti_rx_peer_t *peer; + vec_foreach (peer, ptd->rx_peers) + { + vlib_cli_output (vm, " %U", format_pvti_rx_peer_ptr, peer); + } + } + } + return 0; +} + +VLIB_CLI_COMMAND (pvti_show_interface_command, static) = { + .path = "show pvti interface", + .short_help = "show pvti interface", + .function = pvti_show_interface_command_fn, +}; + +VLIB_CLI_COMMAND (pvti_show_tx_peers_command, static) = { + .path = "show pvti tx peers", + .short_help = "show pvti tx peers", + .function = pvti_show_tx_peers_command_fn, +}; + +VLIB_CLI_COMMAND (pvti_show_rx_peers_command, static) = { + .path = "show pvti rx peers", + .short_help = "show pvti rx peers", + .function = pvti_show_rx_peers_command_fn, +}; + +void pvti_api_init (); + +VNET_FEATURE_INIT (pvti4_bypass, static) = { + .arc_name = "ip4-unicast", + .node_name = "ip4-pvti-bypass", + .runs_before = 0, +}; + +VNET_FEATURE_INIT (pvti6_bypass, static) = { + .arc_name = "ip6-unicast", + .node_name = "ip6-pvti-bypass", + .runs_before = 0, +}; + +static clib_error_t * +pvti_early_config (vlib_main_t *vm, unformat_input_t *input) +{ + u8 *runs_before = 0; + int rbi = 0; + if (vec_len (vnet_feat_pvti4_bypass.runs_before) == 0) + { + rbi = 0; + } + else + { + rbi = vec_len (vnet_feat_pvti4_bypass.runs_before) - 1; + } + vec_validate (vnet_feat_pvti4_bypass.runs_before, rbi); + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "runs-before %v", &runs_before)) + { + vec_add1 (runs_before, 0); + vnet_feat_pvti4_bypass.runs_before[rbi] = (char *) runs_before; + vec_add1 (vnet_feat_pvti4_bypass.runs_before, 0); + } + else + return clib_error_return (0, "unknown input"); + } + + return NULL; +} + +VLIB_EARLY_CONFIG_FUNCTION (pvti_early_config, "pvti"); + +static clib_error_t * +pvti_init (vlib_main_t *vm) +{ + pvti_main_t *pmp = &pvti_main; + clib_error_t *error = 0; + + pmp->vlib_main = vm; + pmp->vnet_main = vnet_get_main (); + pmp->is_initialized = 0; + + pvti_api_init (); + return error; +} + +VLIB_INIT_FUNCTION (pvti_init); + +VLIB_PLUGIN_REGISTER () = { + .version = VPP_BUILD_VER, + .description = "Packet Vector Tunnel Interface plugin", +}; diff --git a/src/plugins/pvti/pvti.h b/src/plugins/pvti/pvti.h new file mode 100644 index 00000000000..ac097c5ecca --- /dev/null +++ b/src/plugins/pvti/pvti.h @@ -0,0 +1,257 @@ +/* + * pvti.h - skeleton vpp engine plug-in header file + * + * 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. + */ +#ifndef __included_pvti_h__ +#define __included_pvti_h__ + +#include <vnet/vnet.h> +#include <vnet/ip/ip.h> +#include <vnet/ethernet/ethernet.h> + +#include <vppinfra/hash.h> +#include <vppinfra/error.h> + +#define VPP_MAX_THREADS (1 << 8) + +#define MAX_RX_STREAMS 256 + +#define PVTI_ALIGN_BYTES 9 + +typedef CLIB_PACKED (struct { + u32 seq; + u8 stream_index; // set to the cpu# on the sending side + u8 chunk_count; + u8 reass_chunk_count; // number of chunks in the front that are related to + // previously started buffer + // mandatory_flags_mask highlights which of the flags cause packet drop if + // not understood, and which of them can be just ignored. + u8 mandatory_flags_mask; + u8 flags_value; + u8 pad_bytes; + u8 pad[0]; +}) pvti_packet_header_t; + +typedef CLIB_PACKED (struct { + ip4_header_t ip4; + udp_header_t udp; + // not part of encap header pvti_packet_header_t pv; +}) pvti_ip4_encap_header_t; + +typedef CLIB_PACKED (struct { + ip6_header_t ip6; + udp_header_t udp; + // not part of encap header pvti_packet_header_t pv; +}) pvti_ip6_encap_header_t; + +typedef CLIB_PACKED (struct { + u16 total_chunk_length; + // More fragments: this chunk is not the last block fragment +#define CHUNK_FLAGS_MF (1 << 0) + // More blocks: this block has chained blocks that follow +#define CHUNK_FLAGS_MB (1 << 1) + u16 _pad0; + u32 _pad1; + u8 chunk_data[0]; +}) pvti_chunk_header_t; + +typedef struct +{ + // a buffer being built from the smaller packets + u32 bi0; + + // how big can this buffer grow + u32 bi0_max_current_length; + + // how many chunks are already in the buffer + u8 chunk_count; + // leading reassembly chunk count + u8 reass_chunk_count; + + u32 current_tx_seq; +} pvti_per_tx_stream_data_t; + +typedef struct +{ + /* The seq# that we last processed */ + u32 last_rx_seq; + + // a current buffer that is being reassembled + u32 rx_bi0; + // The root buffer, most of the times == rx_bi0 except in the case of chained + // buffers. + u32 rx_bi0_first; + + // Next index for dispatch when the reassembly is done + u16 rx_next0; + // expected totall inner length for the packet + u16 rx_expected_inner_length; + u16 rx_received_inner_length; + +} pvti_per_rx_stream_data_t; + +typedef struct +{ + ip_address_t local_ip; + ip_address_t remote_ip; + u16 remote_port; + u16 local_port; + u16 underlay_mtu; + u32 underlay_fib_index; + + u32 pvti_if_index; + bool deleted; + bool is_bo0_traced; + + u32 bo0_max_current_length; + + u8 chunk_count; + u8 reass_chunk_count; + u32 current_tx_seq; + vlib_buffer_t *bo0; + +} pvti_tx_peer_t; + +typedef struct +{ + ip_address_t local_ip; + ip_address_t remote_ip; + u16 remote_port; + u16 local_port; + + pvti_per_rx_stream_data_t rx_streams[MAX_RX_STREAMS]; + + u32 pvti_if_index; + bool deleted; +} pvti_rx_peer_t; + +typedef struct +{ + /* pool of destination-based structures which are used to build the packets + */ + pvti_tx_peer_t *tx_peers; + + /* vector of buffers to send */ + u32 *pending_tx_buffers; + u16 *pending_tx_nexts; + /* pool of source-based structures for the remote peers' data tracking + */ + pvti_rx_peer_t *rx_peers; + + /* vector of buffers being decapsulated */ + u32 *pending_rx_buffers; + u16 *pending_rx_nexts; + +} pvti_per_thread_data_t; + +typedef struct +{ + ip_address_t local_ip; + ip_address_t remote_ip; + u16 remote_port; + u16 local_port; + u16 underlay_mtu; + u32 underlay_fib_index; + bool peer_address_from_payload; + u64 created_at; + + u32 sw_if_index; + u32 hw_if_index; + + // per-stream data for TX + pvti_per_tx_stream_data_t tx_streams[256]; + pvti_per_rx_stream_data_t rx_streams[256]; + +} pvti_if_t; + +typedef struct +{ + /* API message ID base */ + u16 msg_id_base; + + /* have we initialized the data structures ? */ + bool is_initialized; + + /* interface pool */ + pvti_if_t *if_pool; + + /* if_index in the pool above by sw_if_index */ + index_t *if_index_by_sw_if_index; + + /* indices by port */ + index_t **if_indices_by_port; + + /* per-thread data, ip4[0] and ip6[1] */ + pvti_per_thread_data_t *per_thread_data[2]; + + /* on/off switch for the periodic function */ + u8 periodic_timer_enabled; + /* Node index, non-zero if the periodic process has been created */ + u32 periodic_node_index; + + /* graph node state */ + uword *bm_ip4_bypass_enabled_by_sw_if; + uword *bm_ip6_bypass_enabled_by_sw_if; + + /* convenience */ + vlib_main_t *vlib_main; + vnet_main_t *vnet_main; + ethernet_main_t *ethernet_main; +} pvti_main_t; + +extern pvti_main_t pvti_main; + +extern vlib_node_registration_t pvti_node; +extern vlib_node_registration_t pvti4_input_node; +extern vlib_node_registration_t pvti4_output_node; +extern vlib_node_registration_t pvti6_input_node; +extern vlib_node_registration_t pvti6_output_node; +extern vlib_node_registration_t pvti_periodic_node; + +always_inline u8 +pvti_get_stream_index (int is_ip6) +{ + u32 thread_index = vlib_get_thread_index (); + + ASSERT ((thread_index & 0xffffff80) == 0); + + u8 stream_index = (thread_index & 0x7f) | (is_ip6 ? 0x80 : 0); + return stream_index; +} + +/* attempt to get a new buffer */ +always_inline u32 +pvti_get_new_buffer (vlib_main_t *vm) +{ + u32 bi0 = INDEX_INVALID; + if (vlib_buffer_alloc (vm, &bi0, 1) != 1) + { + return INDEX_INVALID; + } + vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0); + b0->current_data = 0; + b0->current_length = 0; + return bi0; +} + +/* Periodic function events */ +#define PVTI_EVENT1 1 +#define PVTI_EVENT2 2 +#define PVTI_EVENT_PERIODIC_ENABLE_DISABLE 3 + +void pvti_create_periodic_process (pvti_main_t *); +void pvti_verify_initialized (pvti_main_t *pvm); + +#endif /* __included_pvti_h__ */ diff --git a/src/plugins/pvti/pvti_if.c b/src/plugins/pvti/pvti_if.c new file mode 100644 index 00000000000..4f83994a1a4 --- /dev/null +++ b/src/plugins/pvti/pvti_if.c @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2020 Doc.ai 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 <vnet/adj/adj_midchain.h> +#include <vnet/udp/udp.h> + +#include <pvti/pvti.h> +#include <pvti/pvti_if.h> + +static u8 * +format_pvti_if_name (u8 *s, va_list *args) +{ + u32 dev_instance = va_arg (*args, u32); + // wg_if_t *wgi = wg_if_get (dev_instance); + return format (s, "pvti%d", dev_instance); +} + +u8 * +format_pvti_if (u8 *s, va_list *args) +{ + index_t pvtii = va_arg (*args, u32); + pvti_if_t *pvti_if = pvti_if_get (pvtii); + + s = format ( + s, "[%d] %U local:%U:%d remote:%U:%d underlay_mtu:%d underlay_fib_idx:%d", + pvtii, format_vnet_sw_if_index_name, vnet_get_main (), + pvti_if->sw_if_index, format_ip46_address, &pvti_if->local_ip, + IP46_TYPE_ANY, pvti_if->local_port, format_ip46_address, + &pvti_if->remote_ip, IP46_TYPE_ANY, pvti_if->remote_port, + pvti_if->underlay_mtu, pvti_if->underlay_fib_index); + + return (s); +} + +index_t +pvti_if_find_by_sw_if_index (u32 sw_if_index) +{ + if (vec_len (pvti_main.if_index_by_sw_if_index) <= sw_if_index) + return INDEX_INVALID; + u32 ti = pvti_main.if_index_by_sw_if_index[sw_if_index]; + if (ti == ~0) + return INDEX_INVALID; + + return (ti); +} + +index_t +pvti_if_find_by_remote_ip4_and_port (ip4_address_t *remote_ip4, + u16 remote_port) +{ + pvti_if_t *ifc; + pool_foreach (ifc, pvti_main.if_pool) + { + if ((ifc->remote_port == remote_port) && + (ifc->remote_ip.version == AF_IP4) && + ((ifc->remote_ip.ip.ip4.as_u32 == remote_ip4->as_u32) || + ifc->peer_address_from_payload)) + { + return (ifc - pvti_main.if_pool); + } + } + return INDEX_INVALID; +} + +index_t +pvti_if_find_by_remote_ip6_and_port (ip6_address_t *remote_ip6, + u16 remote_port) +{ + pvti_if_t *ifc; + pool_foreach (ifc, pvti_main.if_pool) + { + if ((ifc->remote_port == remote_port) && + (ifc->remote_ip.version == AF_IP6) && + ((0 == memcmp (&ifc->remote_ip.ip.ip6, remote_ip6, + sizeof (*remote_ip6))) || + ifc->peer_address_from_payload)) + { + return (ifc - pvti_main.if_pool); + } + } + return INDEX_INVALID; +} + +index_t +pvti_if_find_by_remote_ip_and_port (ip_address_t *remote_ip, u16 remote_port) +{ + pvti_if_t *ifc; + pool_foreach (ifc, pvti_main.if_pool) + { + if ((ifc->remote_port == remote_port) && + (ifc->peer_address_from_payload || + (0 == ip_address_cmp (remote_ip, &ifc->remote_ip)))) + { + return (ifc - pvti_main.if_pool); + } + } + return INDEX_INVALID; +} + +static void +pvti_add_tidx_by_port (index_t t_index, u16 port) +{ + pvti_main_t *pvm = &pvti_main; + vec_validate_init_empty (pvm->if_indices_by_port, port, NULL); + vec_add1 (pvm->if_indices_by_port[port], t_index); +} + +static void +pvti_del_tidx_by_port (index_t t_index, u16 port) +{ + pvti_main_t *pvm = &pvti_main; + index_t *ii; + if (!pvm->if_indices_by_port) + { + return; + } + if (port >= vec_len (pvm->if_indices_by_port)) + { + return; + } + if (vec_len (pvm->if_indices_by_port[port]) == 0) + { + ALWAYS_ASSERT (pvm->if_indices_by_port[port] > 0); + /* not reached */ + return; + } + + vec_foreach (ii, pvm->if_indices_by_port[port]) + { + if (*ii == t_index) + { + vec_del1 (pvm->if_indices_by_port[port], + pvm->if_indices_by_port[port] - ii); + break; + } + } +} + +static u32 +pvti_get_tunnel_count_by_port (u16 port) +{ + pvti_main_t *pvm = &pvti_main; + if (!pvm->if_indices_by_port) + { + return 0; + } + return vec_len (vec_elt (pvm->if_indices_by_port, port)); +} + +static clib_error_t * +pvti_if_admin_up_down (vnet_main_t *vnm, u32 hw_if_index, u32 flags) +{ + // vnet_hw_interface_t *hi; + u32 hw_flags; + + // hi = vnet_get_hw_interface (vnm, hw_if_index); + hw_flags = + (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ? VNET_HW_INTERFACE_FLAG_LINK_UP : + 0); + vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags); + + return (NULL); +} + +void +pvti_if_update_adj (vnet_main_t *vnm, u32 sw_if_index, adj_index_t ai) +{ + + /* Convert any neighbour adjacency that has a next-hop reachable through + * the wg interface into a midchain. This is to avoid sending ARP/ND to + * resolve the next-hop address via the wg interface. Then, if one of the + * peers has matching prefix among allowed prefixes, the midchain will be + * updated to the corresponding one. + */ + adj_nbr_midchain_update_rewrite (ai, NULL, NULL, ADJ_FLAG_NONE, NULL); + + // wgii = wg_if_find_by_sw_if_index (sw_if_index); + // wg_if_peer_walk (wg_if_get (wgii), wg_peer_if_adj_change, &ai); +} + +VNET_DEVICE_CLASS (pvti_if_device_class) = { + .name = "Packet Vectorizer Tunnel", + .format_device_name = format_pvti_if_name, + .admin_up_down_function = pvti_if_admin_up_down, +}; + +VNET_HW_INTERFACE_CLASS (pvti_hw_interface_class) = { + .name = "PVTunnel", + .update_adjacency = pvti_if_update_adj, + .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P, + // .flags = VNET_HW_INTERFACE_CLASS_FLAG_NBMA, +}; + +int +pvti_if_create (ip_address_t *local_ip, u16 local_port, + ip_address_t *remote_ip, u16 remote_port, + pvti_peer_address_method_t peer_address_method, + u16 underlay_mtu, u32 underlay_fib_index, u32 *sw_if_indexp) +{ + vnet_main_t *vnm = vnet_get_main (); + pvti_main_t *pvm = &pvti_main; + u32 hw_if_index; + vnet_hw_interface_t *hi; + pvti_verify_initialized (pvm); + + pvti_if_t *pvti_if; + + ASSERT (sw_if_indexp); + + *sw_if_indexp = (u32) ~0; + + pool_get_zero (pvti_main.if_pool, pvti_if); + pvti_if->local_ip = *local_ip; + pvti_if->local_port = local_port; + pvti_if->remote_ip = *remote_ip; + if (peer_address_method == PVTI_PEER_ADDRESS_FROM_PAYLOAD) + { + pvti_if->peer_address_from_payload = 1; + } + pvti_if->remote_port = remote_port; + pvti_if->underlay_mtu = underlay_mtu; + pvti_if->underlay_fib_index = underlay_fib_index; + pvti_if->created_at = clib_cpu_time_now (); + + /* tunnel index (or instance) */ + u32 t_idx = pvti_if - pvti_main.if_pool; + + hw_if_index = + vnet_register_interface (vnm, pvti_if_device_class.index, t_idx, + pvti_hw_interface_class.index, t_idx); + + pvti_if->hw_if_index = hw_if_index; + + hi = vnet_get_hw_interface (vnm, hw_if_index); + pvti_if->sw_if_index = *sw_if_indexp = hi->sw_if_index; + + vec_validate_init_empty (pvm->if_index_by_sw_if_index, hi->sw_if_index, + INDEX_INVALID); + + vec_elt (pvm->if_index_by_sw_if_index, hi->sw_if_index) = t_idx; + pvti_if_t *pvti_if0 = pool_elt_at_index (pvti_main.if_pool, t_idx); + int i; + for (i = 0; i < 256; i++) + { + pvti_if0->tx_streams[i].bi0 = INDEX_INVALID; + pvti_if0->tx_streams[i].current_tx_seq = 42; + + pvti_if0->rx_streams[i].rx_bi0 = INDEX_INVALID; + pvti_if0->rx_streams[i].rx_bi0_first = INDEX_INVALID; + } + + /* + int is_ip6 = 0; + u32 encap_index = !is_ip6 ? + pvti4_output_node.index : pvti6_output_node.index; + vnet_set_interface_output_node (vnm, pvti_if->hw_if_index, encap_index); + */ + vnet_set_interface_l3_output_node (vnm->vlib_main, hi->sw_if_index, + (u8 *) "pvti4-output"); + + pvti_add_tidx_by_port (t_idx, local_port); + if (1 == pvti_get_tunnel_count_by_port (local_port)) + { + clib_warning ("Registering local port %d", local_port); + udp_register_dst_port (vlib_get_main (), local_port, + pvti4_input_node.index, UDP_IP4); + udp_register_dst_port (vlib_get_main (), local_port, + pvti6_input_node.index, UDP_IP6); + } + else + { + clib_warning ("Not registering the port"); + } + + vnet_hw_interface_set_flags (vnm, pvti_if->hw_if_index, + VNET_HW_INTERFACE_FLAG_LINK_UP); + + return 0; +} + +void +pvti_if_walk (pvti_if_walk_cb_t fn, void *data) +{ + index_t pvtii; + + pool_foreach_index (pvtii, pvti_main.if_pool) + { + if (WALK_STOP == fn (pvtii, data)) + break; + } +} + +int +pvti_if_delete (u32 sw_if_index) +{ + vnet_main_t *vnm = vnet_get_main (); + pvti_main_t *pvm = &pvti_main; + + if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index)) + return VNET_API_ERROR_INVALID_SW_IF_INDEX; + + vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index); + if (hw == 0 || hw->dev_class_index != pvti_if_device_class.index) + return VNET_API_ERROR_INVALID_VALUE; + + pvti_if_t *ifc; + bool found = 0; + pool_foreach (ifc, pvm->if_pool) + { + if (ifc->sw_if_index == sw_if_index) + { + found = 1; + break; + } + } + if (!found) + { + return VNET_API_ERROR_INVALID_VALUE_2; + } + index_t tidx = ifc - pvm->if_pool; + + u16 local_port = ifc->local_port; + pvti_del_tidx_by_port (tidx, local_port); + pvm->if_index_by_sw_if_index[sw_if_index] = INDEX_INVALID; + + if (0 == pvti_get_tunnel_count_by_port (local_port)) + { + udp_unregister_dst_port (vlib_get_main (), local_port, 1); + udp_unregister_dst_port (vlib_get_main (), local_port, 0); + } + + vnet_reset_interface_l3_output_node (vnm->vlib_main, sw_if_index); + vnet_delete_hw_interface (vnm, hw->hw_if_index); + pool_put (pvti_main.if_pool, ifc); + + /* mark per-thread peers as deleted */ + pvti_per_thread_data_t *ptd; + + vec_foreach (ptd, pvm->per_thread_data[0]) + { + pvti_tx_peer_t *peer; + vec_foreach (peer, ptd->tx_peers) + { + if (tidx == peer->pvti_if_index) + { + peer->deleted = 1; + } + } + } + vec_foreach (ptd, pvm->per_thread_data[1]) + { + pvti_tx_peer_t *peer; + vec_foreach (peer, ptd->tx_peers) + { + if (tidx == peer->pvti_if_index) + { + peer->deleted = 1; + } + } + } + + return 0; +} diff --git a/src/plugins/pvti/pvti_if.h b/src/plugins/pvti/pvti_if.h new file mode 100644 index 00000000000..44bf22ce825 --- /dev/null +++ b/src/plugins/pvti/pvti_if.h @@ -0,0 +1,47 @@ +#ifndef PVTI_IF_H +#define PVTI_IF_H + +#include <vnet/interface_funcs.h> + +typedef enum +{ + PVTI_PEER_ADDRESS_FIXED = 0, + PVTI_PEER_ADDRESS_FROM_PAYLOAD +} pvti_peer_address_method_t; + +typedef walk_rc_t (*pvti_if_walk_cb_t) (index_t wgi, void *data); +void pvti_if_walk (pvti_if_walk_cb_t fn, void *data); + +int pvti_if_create (ip_address_t *local_ip, u16 local_port, + ip_address_t *remote_ip, u16 remote_port, + pvti_peer_address_method_t peer_address_method, + u16 underlay_mtu, u32 underlay_fib_index, + u32 *sw_if_indexp); +index_t pvti_if_find_by_sw_if_index (u32 sw_if_index); +index_t pvti_if_find_by_remote_ip4_and_port (ip4_address_t *remote_ip4, + u16 remote_port); +index_t pvti_if_find_by_remote_ip6_and_port (ip6_address_t *remote_ip4, + u16 remote_port); + +index_t pvti_if_find_by_remote_ip_and_port (ip_address_t *remote_ip, + u16 remote_port); + +int pvti_if_delete (u32 sw_if_index); + +u8 *format_pvti_if (u8 *s, va_list *args); + +static_always_inline pvti_if_t * +pvti_if_get (index_t pvtii) +{ + if (INDEX_INVALID == pvtii) + return (NULL); + return (pool_elt_at_index (pvti_main.if_pool, pvtii)); +} + +static_always_inline index_t +pvti_if_get_index (pvti_if_t *pvti_if) +{ + return pvti_if - pvti_main.if_pool; +} + +#endif diff --git a/src/plugins/quic/CMakeLists.txt b/src/plugins/quic/CMakeLists.txt index 65bdc32a239..4f408ca66d3 100644 --- a/src/plugins/quic/CMakeLists.txt +++ b/src/plugins/quic/CMakeLists.txt @@ -18,7 +18,7 @@ if(NOT OPENSSL_FOUND) endif() unset(QUIC_LINK_LIBRARIES) -set(EXPECTED_QUICLY_VERSION "0.1.4-vpp") +set(EXPECTED_QUICLY_VERSION "0.1.5-vpp") vpp_find_path(QUICLY_INCLUDE_DIR NAMES quicly.h) vpp_find_path(PICOTLS_INCLUDE_DIR NAMES picotls.h) diff --git a/src/plugins/quic/quic.c b/src/plugins/quic/quic.c index 3f7a3426069..15a5263284a 100644 --- a/src/plugins/quic/quic.c +++ b/src/plugins/quic/quic.c @@ -250,7 +250,6 @@ quic_init_crypto_context (crypto_context_t * crctx, quic_ctx_t * ctx) ptls_ctx->cipher_suites = qm->quic_ciphers[ctx->crypto_engine]; ptls_ctx->certificates.list = NULL; ptls_ctx->certificates.count = 0; - ptls_ctx->esni = NULL; ptls_ctx->on_client_hello = NULL; ptls_ctx->emit_certificate = NULL; ptls_ctx->sign_certificate = NULL; @@ -718,7 +717,6 @@ quic_send_packets (quic_ctx_t * ctx) session_t *udp_session; quicly_conn_t *conn; size_t num_packets, i, max_packets; - quicly_address_t dest, src; u32 n_sent = 0; int err = 0; @@ -744,17 +742,16 @@ quic_send_packets (quic_ctx_t * ctx) break; num_packets = max_packets; - if ((err = quicly_send (conn, &dest, &src, packets, &num_packets, buf, - sizeof (buf)))) + if ((err = quicly_send (conn, &ctx->rmt_ip, &ctx->lcl_ip, packets, + &num_packets, buf, sizeof (buf)))) goto quicly_error; for (i = 0; i != num_packets; ++i) { - if ((err = - quic_send_datagram (udp_session, &packets[i], &dest, &src))) - goto quicly_error; - + if ((err = quic_send_datagram (udp_session, &packets[i], + &ctx->rmt_ip, &ctx->lcl_ip))) + goto quicly_error; } n_sent += num_packets; } @@ -1332,14 +1329,16 @@ quic_connect_connection (session_endpoint_cfg_t * sep) quic_ctx_t *ctx; app_worker_t *app_wrk; application_t *app; + transport_endpt_ext_cfg_t *ext_cfg; int error; - if (!sep->ext_cfg) + ext_cfg = session_endpoint_get_ext_cfg (sep, TRANSPORT_ENDPT_EXT_CFG_CRYPTO); + if (!ext_cfg) return SESSION_E_NOEXTCFG; /* Use pool on thread 1 if we have workers because of UDP */ thread_index = transport_cl_thread (); - ccfg = &sep->ext_cfg->crypto; + ccfg = &ext_cfg->crypto; clib_memset (cargs, 0, sizeof (*cargs)); ctx_index = quic_ctx_alloc (thread_index); @@ -1475,13 +1474,15 @@ quic_start_listen (u32 quic_listen_session_index, quic_ctx_t *lctx; u32 lctx_index; app_listener_t *app_listener; + transport_endpt_ext_cfg_t *ext_cfg; int rv; sep = (session_endpoint_cfg_t *) tep; - if (!sep->ext_cfg) + ext_cfg = session_endpoint_get_ext_cfg (sep, TRANSPORT_ENDPT_EXT_CFG_CRYPTO); + if (!ext_cfg) return SESSION_E_NOEXTCFG; - ccfg = &sep->ext_cfg->crypto; + ccfg = &ext_cfg->crypto; app_wrk = app_worker_get (sep->app_wrk_index); app = application_get (app_wrk->app_index); QUIC_DBG (2, "Called quic_start_listen for app %d", app_wrk->app_index); @@ -1844,10 +1845,10 @@ quic_udp_session_connected_callback (u32 quic_app_index, u32 ctx_index, quic_build_sockaddr (sa, &salen, &tc->rmt_ip, tc->rmt_port, tc->is_ip4); quicly_ctx = quic_get_quicly_ctx_from_ctx (ctx); - ret = quicly_connect (&ctx->conn, quicly_ctx, (char *) ctx->srv_hostname, - sa, NULL, &quic_main.wrk_ctx[thread_index].next_cid, + ret = quicly_connect (&ctx->conn, quicly_ctx, (char *) ctx->srv_hostname, sa, + NULL, &quic_main.wrk_ctx[thread_index].next_cid, ptls_iovec_init (NULL, 0), &quic_main.hs_properties, - NULL); + NULL, NULL); ++quic_main.wrk_ctx[thread_index].next_cid.master_id; /* Save context handle in quicly connection */ quic_store_conn_ctx (ctx->conn, ctx); @@ -2099,10 +2100,9 @@ quic_accept_connection (quic_rx_packet_ctx_t * pctx) } quicly_ctx = quic_get_quicly_ctx_from_ctx (ctx); - if ((rv = quicly_accept (&conn, quicly_ctx, NULL, &pctx->sa, - &pctx->packet, NULL, - &quic_main.wrk_ctx[pctx->thread_index].next_cid, - NULL))) + if ((rv = quicly_accept ( + &conn, quicly_ctx, NULL, &pctx->sa, &pctx->packet, NULL, + &quic_main.wrk_ctx[pctx->thread_index].next_cid, NULL, NULL))) { /* Invalid packet, pass */ assert (conn == NULL); @@ -2188,12 +2188,8 @@ quic_reset_connection (u64 udp_session_handle, quic_rx_packet_ctx_t * pctx) packet.iov_len = payload_len; packet.iov_base = payload; - struct _st_quicly_conn_public_t *conn = - (struct _st_quicly_conn_public_t *) qctx->conn; - udp_session = session_get_from_handle (udp_session_handle); - rv = quic_send_datagram (udp_session, &packet, &conn->remote.address, - &conn->local.address); + rv = quic_send_datagram (udp_session, &packet, &qctx->rmt_ip, &qctx->lcl_ip); quic_set_udp_tx_evt (udp_session); return rv; } diff --git a/src/plugins/quic/quic.h b/src/plugins/quic/quic.h index 2c5a21c01a4..081bcb120e9 100644 --- a/src/plugins/quic/quic.h +++ b/src/plugins/quic/quic.h @@ -179,6 +179,8 @@ typedef struct quic_ctx_ ptls_aead_context_t *aead_ctx; } ingress_keys; int key_phase_ingress; + quicly_address_t rmt_ip; + quicly_address_t lcl_ip; } quic_ctx_t; diff --git a/src/plugins/snort/CMakeLists.txt b/src/plugins/snort/CMakeLists.txt index bd9dcdc4fdd..3fc2bd625a4 100644 --- a/src/plugins/snort/CMakeLists.txt +++ b/src/plugins/snort/CMakeLists.txt @@ -7,6 +7,10 @@ add_vpp_plugin(snort dequeue.c main.c cli.c + snort_api.c + + API_FILES + snort.api MULTIARCH_SOURCES enqueue.c diff --git a/src/plugins/snort/cli.c b/src/plugins/snort/cli.c index 08740f41b37..4b6dbc742a7 100644 --- a/src/plugins/snort/cli.c +++ b/src/plugins/snort/cli.c @@ -25,6 +25,7 @@ snort_create_instance_command_fn (vlib_main_t *vm, unformat_input_t *input, u8 *name = 0; u32 queue_size = 1024; u8 drop_on_diconnect = 1; + int rv = 0; /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) @@ -60,8 +61,30 @@ snort_create_instance_command_fn (vlib_main_t *vm, unformat_input_t *input, goto done; } - err = snort_instance_create (vm, (char *) name, min_log2 (queue_size), - drop_on_diconnect); + rv = snort_instance_create (vm, (char *) name, min_log2 (queue_size), + drop_on_diconnect); + + switch (rv) + { + case 0: + break; + case VNET_API_ERROR_ENTRY_ALREADY_EXISTS: + err = clib_error_return (0, "instance '%s' already exists", name); + break; + case VNET_API_ERROR_SYSCALL_ERROR_1: + err = clib_error_return (0, "memory fd failure: %U", format_clib_error, + clib_mem_get_last_error ()); + break; + case VNET_API_ERROR_SYSCALL_ERROR_2: + err = clib_error_return (0, "ftruncate failure"); + break; + case VNET_API_ERROR_SYSCALL_ERROR_3: + err = clib_error_return (0, "mmap failure"); + break; + default: + err = clib_error_return (0, "snort_instance_create returned %d", rv); + break; + } done: vec_free (name); @@ -77,6 +100,118 @@ VLIB_CLI_COMMAND (snort_create_instance_command, static) = { }; static clib_error_t * +snort_disconnect_instance_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *err = 0; + u8 *name = 0; + snort_instance_t *si; + int rv = 0; + + if (!unformat_user (input, unformat_line_input, line_input)) + return clib_error_return (0, "please specify instance name"); + + if (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + unformat (line_input, "%s", &name); + + if (!name) + { + err = clib_error_return (0, "please specify instance name"); + goto done; + } + + si = snort_get_instance_by_name ((char *) name); + if (!si) + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + else + rv = snort_instance_disconnect (vm, si->index); + + switch (rv) + { + case 0: + break; + case VNET_API_ERROR_NO_SUCH_ENTRY: + err = clib_error_return (0, "unknown instance '%s'", name); + break; + case VNET_API_ERROR_FEATURE_DISABLED: + err = clib_error_return (0, "instance '%s' is not connected", name); + break; + case VNET_API_ERROR_INVALID_VALUE: + err = clib_error_return (0, "failed to disconnect a broken client"); + break; + default: + err = clib_error_return (0, "snort_instance_disconnect returned %d", rv); + break; + } + +done: + vec_free (name); + unformat_free (line_input); + return err; +} + +VLIB_CLI_COMMAND (snort_disconnect_instance_command, static) = { + .path = "snort disconnect instance", + .short_help = "snort disconnect instance <name>", + .function = snort_disconnect_instance_command_fn, +}; + +static clib_error_t * +snort_delete_instance_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *err = 0; + u8 *name = 0; + int rv = 0; + + if (!unformat_user (input, unformat_line_input, line_input)) + return clib_error_return (0, "please specify instance name"); + + if (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + unformat (line_input, "%s", &name); + + if (!name) + { + err = clib_error_return (0, "please specify instance name"); + goto done; + } + + snort_instance_t *si = snort_get_instance_by_name ((char *) name); + if (!si) + err = clib_error_return (0, "unknown instance '%s' requested", name); + else + rv = snort_instance_delete (vm, si->index); + + switch (rv) + { + case 0: + break; + case VNET_API_ERROR_NO_SUCH_ENTRY: + err = clib_error_return (0, "instance '%s' deletion failure", name); + break; + case VNET_API_ERROR_INSTANCE_IN_USE: + err = clib_error_return (0, "instance '%s' has connected client", name); + break; + default: + err = clib_error_return (0, "snort_instance_delete returned %d", rv); + break; + } + +done: + vec_free (name); + unformat_free (line_input); + return err; +} + +VLIB_CLI_COMMAND (snort_delete_instance_command, static) = { + .path = "snort delete instance", + .short_help = "snort delete instance <name>", + .function = snort_delete_instance_command_fn, +}; + +static clib_error_t * snort_attach_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd) { @@ -86,6 +221,7 @@ snort_attach_command_fn (vlib_main_t *vm, unformat_input_t *input, u8 *name = 0; u32 sw_if_index = ~0; snort_attach_dir_t dir = SNORT_INOUT; + int rv = 0; /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) @@ -124,8 +260,29 @@ snort_attach_command_fn (vlib_main_t *vm, unformat_input_t *input, goto done; } - err = - snort_interface_enable_disable (vm, (char *) name, sw_if_index, 1, dir); + rv = snort_interface_enable_disable (vm, (char *) name, sw_if_index, 1, dir); + + switch (rv) + { + case 0: + break; + case VNET_API_ERROR_FEATURE_ALREADY_ENABLED: + /* already attached to same instance */ + break; + case VNET_API_ERROR_INSTANCE_IN_USE: + err = clib_error_return (0, + "interface %U already assigned to " + "an instance", + format_vnet_sw_if_index_name, vnm, sw_if_index); + break; + case VNET_API_ERROR_NO_SUCH_ENTRY: + err = clib_error_return (0, "unknown instance '%s'", name); + break; + default: + err = clib_error_return (0, "snort_interface_enable_disable returned %d", + rv); + break; + } done: vec_free (name); @@ -148,6 +305,7 @@ snort_detach_command_fn (vlib_main_t *vm, unformat_input_t *input, vnet_main_t *vnm = vnet_get_main (); clib_error_t *err = 0; u32 sw_if_index = ~0; + int rv = 0; /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) @@ -172,7 +330,23 @@ snort_detach_command_fn (vlib_main_t *vm, unformat_input_t *input, goto done; } - err = snort_interface_enable_disable (vm, 0, sw_if_index, 0, SNORT_INOUT); + rv = snort_interface_enable_disable (vm, 0, sw_if_index, 0, SNORT_INOUT); + + switch (rv) + { + case 0: + break; + case VNET_API_ERROR_INVALID_INTERFACE: + err = clib_error_return (0, + "interface %U is not assigned to snort " + "instance!", + format_vnet_sw_if_index_name, vnm, sw_if_index); + break; + default: + err = clib_error_return (0, "snort_interface_enable_disable returned %d", + rv); + break; + } done: unformat_free (line_input); @@ -213,7 +387,7 @@ snort_show_interfaces_command_fn (vlib_main_t *vm, unformat_input_t *input, snort_instance_t *si; u32 *index; - vlib_cli_output (vm, "interface\tsnort instance"); + vlib_cli_output (vm, "interface\t\tsnort instance"); vec_foreach (index, sm->instance_by_sw_if_index) { if (index[0] != ~0) @@ -237,7 +411,18 @@ snort_show_clients_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd) { snort_main_t *sm = &snort_main; - vlib_cli_output (vm, "number of clients: %d", pool_elts (sm->clients)); + u32 n_clients = pool_elts (sm->clients); + snort_client_t *c; + snort_instance_t *si; + + vlib_cli_output (vm, "number of clients: %d", n_clients); + if (n_clients) + vlib_cli_output (vm, "client snort instance"); + pool_foreach (c, sm->clients) + { + si = vec_elt_at_index (sm->instances, c->instance_index); + vlib_cli_output (vm, "%6d %s", c - sm->clients, si->name); + } return 0; } @@ -251,14 +436,16 @@ static clib_error_t * snort_mode_polling_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd) { - return snort_set_node_mode (vm, VLIB_NODE_STATE_POLLING); + snort_set_node_mode (vm, VLIB_NODE_STATE_POLLING); + return 0; } static clib_error_t * snort_mode_interrupt_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd) { - return snort_set_node_mode (vm, VLIB_NODE_STATE_INTERRUPT); + snort_set_node_mode (vm, VLIB_NODE_STATE_INTERRUPT); + return 0; } VLIB_CLI_COMMAND (snort_mode_polling_command, static) = { diff --git a/src/plugins/snort/daq_vpp.c b/src/plugins/snort/daq_vpp.c index 386092a0382..6fc0bf5506a 100644 --- a/src/plugins/snort/daq_vpp.c +++ b/src/plugins/snort/daq_vpp.c @@ -10,6 +10,7 @@ #include <sys/socket.h> #include <sys/un.h> #include <sys/mman.h> +#include <sys/time.h> #include <errno.h> #include <sys/epoll.h> @@ -521,6 +522,7 @@ vpp_daq_msg_receive_one (VPP_Context_t *vc, VPPQueuePair *qp, { uint32_t n_recv, n_left; uint32_t head, next, mask = qp->queue_size - 1; + struct timeval tv; if (max_recv == 0) return 0; @@ -535,11 +537,14 @@ vpp_daq_msg_receive_one (VPP_Context_t *vc, VPPQueuePair *qp, n_left = n_recv = max_recv; } + gettimeofday (&tv, NULL); while (n_left--) { uint32_t desc_index = qp->enq_ring[next & mask]; daq_vpp_desc_t *d = qp->descs + desc_index; VPPDescData *dd = qp->desc_data + desc_index; + dd->pkthdr.ts.tv_sec = tv.tv_sec; + dd->pkthdr.ts.tv_usec = tv.tv_usec; dd->pkthdr.pktlen = d->length; dd->pkthdr.address_space_id = d->address_space_id; dd->msg.data = vc->bpools[d->buffer_pool].base + d->offset; diff --git a/src/plugins/snort/dequeue.c b/src/plugins/snort/dequeue.c index 31745de404c..bc301f6888b 100644 --- a/src/plugins/snort/dequeue.c +++ b/src/plugins/snort/dequeue.c @@ -307,7 +307,7 @@ snort_deq_node_polling (vlib_main_t *vm, vlib_node_runtime_t *node, snort_qpair_t *qp; snort_instance_t *si; - vec_foreach (si, sm->instances) + pool_foreach (si, sm->instances) { qp = vec_elt_at_index (si->qpairs, vm->thread_index); u32 ready = __atomic_load_n (&qp->ready, __ATOMIC_ACQUIRE); diff --git a/src/plugins/snort/enqueue.c b/src/plugins/snort/enqueue.c index 409c0e49078..ce4f34491ec 100644 --- a/src/plugins/snort/enqueue.c +++ b/src/plugins/snort/enqueue.c @@ -133,7 +133,7 @@ snort_enq_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node, nexts, n_processed); } - vec_foreach (si, sm->instances) + pool_foreach (si, sm->instances) { u32 head, freelist_len, n_pending, n_enq, mask; u64 ctr = 1; diff --git a/src/plugins/snort/main.c b/src/plugins/snort/main.c index 2430fcdc5c2..50bff027a13 100644 --- a/src/plugins/snort/main.c +++ b/src/plugins/snort/main.c @@ -3,10 +3,24 @@ */ #include <vlib/vlib.h> +#include <vlibapi/api_types.h> #include <vnet/plugin/plugin.h> #include <vpp/app/version.h> #include <snort/snort.h> +#include <snort/snort.api_enum.h> +#include <snort/snort.api_types.h> + +#include <vnet/ip/ip_types_api.h> +#include <vnet/format_fns.h> + +#include <vlibapi/api_helper_macros.h> + +#include <vnet/vnet.h> + +#include <vlibapi/api.h> +#include <vlibmemory/api.h> + #include <sys/eventfd.h> snort_main_t snort_main; @@ -18,6 +32,12 @@ VLIB_REGISTER_LOG_CLASS (snort_log, static) = { #define log_debug(fmt, ...) vlib_log_debug (snort_log.class, fmt, __VA_ARGS__) #define log_err(fmt, ...) vlib_log_err (snort_log.class, fmt, __VA_ARGS__) +snort_main_t * +snort_get_main () +{ + return &snort_main; +} + static void snort_client_disconnect (clib_file_t *uf) { @@ -45,7 +65,38 @@ snort_client_disconnect (clib_file_t *uf) pool_put (sm->clients, c); } -static snort_instance_t * +int +snort_instance_disconnect (vlib_main_t *vm, u32 instance_index) +{ + snort_main_t *sm = &snort_main; + snort_instance_t *si; + snort_client_t *client; + clib_file_main_t *fm = &file_main; + clib_file_t *uf = 0; + int rv = 0; + + si = snort_get_instance_by_index (instance_index); + if (!si) + return VNET_API_ERROR_NO_SUCH_ENTRY; + if (si->client_index == ~0) + return VNET_API_ERROR_FEATURE_DISABLED; + + client = pool_elt_at_index (sm->clients, si->client_index); + uf = clib_file_get (fm, client->file_index); + if (uf) + snort_client_disconnect (uf); + else + { + log_err ("failed to disconnect a broken client from" + "instance '%s'", + si->name); + rv = VNET_API_ERROR_INVALID_VALUE; + } + + return rv; +} + +snort_instance_t * snort_get_instance_by_name (char *name) { snort_main_t *sm = &snort_main; @@ -54,7 +105,16 @@ snort_get_instance_by_name (char *name) return 0; return vec_elt_at_index (sm->instances, p[0]); - ; +} + +snort_instance_t * +snort_get_instance_by_index (u32 instance_index) +{ + snort_main_t *sm = &snort_main; + + if (pool_is_free_index (sm->instances, instance_index)) + return 0; + return pool_elt_at_index (sm->instances, instance_index); } static clib_error_t * @@ -110,6 +170,8 @@ snort_conn_fd_read_ready (clib_file_t *uf) snort_client_disconnect (uf); return 0; } + snort_freelist_init (qp->freelist); + *qp->enq_head = *qp->deq_head = qp->next_desc = 0; } base = (u8 *) si->shm_base; @@ -281,14 +343,13 @@ snort_listener_init (vlib_main_t *vm) return 0; } -clib_error_t * +int snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz, u8 drop_on_disconnect) { vlib_thread_main_t *tm = vlib_get_thread_main (); snort_main_t *sm = &snort_main; snort_instance_t *si; - clib_error_t *err = 0; u32 index, i; u8 *base = CLIB_MEM_VM_MAP_FAILED; u32 size; @@ -296,9 +357,10 @@ snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz, u32 qpair_mem_sz = 0; u32 qsz = 1 << log2_queue_sz; u8 align = CLIB_CACHE_LINE_BYTES; + int rv = 0; if (snort_get_instance_by_name (name)) - return clib_error_return (0, "instance already exists"); + return VNET_API_ERROR_ENTRY_ALREADY_EXISTS; /* descriptor table */ qpair_mem_sz += round_pow2 (qsz * sizeof (daq_vpp_desc_t), align); @@ -316,14 +378,13 @@ snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz, if (fd == -1) { - err = clib_error_return (0, "memory fd failure: %U", format_clib_error, - clib_mem_get_last_error ()); + rv = VNET_API_ERROR_SYSCALL_ERROR_1; goto done; } if ((ftruncate (fd, size)) == -1) { - err = clib_error_return (0, "ftruncate failure"); + rv = VNET_API_ERROR_SYSCALL_ERROR_2; goto done; } @@ -331,7 +392,7 @@ snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz, if (base == CLIB_MEM_VM_MAP_FAILED) { - err = clib_error_return (0, "mmap failure"); + rv = VNET_API_ERROR_SYSCALL_ERROR_3; goto done; } @@ -399,17 +460,17 @@ snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz, sm->input_mode); done: - if (err) + if (rv) { if (base != CLIB_MEM_VM_MAP_FAILED) clib_mem_vm_unmap (base); if (fd != -1) close (fd); } - return err; + return rv; } -clib_error_t * +int snort_interface_enable_disable (vlib_main_t *vm, char *instance_name, u32 sw_if_index, int is_enable, snort_attach_dir_t snort_dir) @@ -417,16 +478,16 @@ snort_interface_enable_disable (vlib_main_t *vm, char *instance_name, snort_main_t *sm = &snort_main; vnet_main_t *vnm = vnet_get_main (); snort_instance_t *si; - clib_error_t *err = 0; u64 fa_data; u32 index; + int rv = 0; if (is_enable) { if ((si = snort_get_instance_by_name (instance_name)) == 0) { - err = clib_error_return (0, "unknown instance '%s'", instance_name); - goto done; + log_err ("unknown instance '%s'", instance_name); + return VNET_API_ERROR_NO_SUCH_ENTRY; } vec_validate_init_empty (sm->instance_by_sw_if_index, sw_if_index, ~0); @@ -434,12 +495,13 @@ snort_interface_enable_disable (vlib_main_t *vm, char *instance_name, index = sm->instance_by_sw_if_index[sw_if_index]; if (index != ~0) { + if (index == si->index) + rv = VNET_API_ERROR_FEATURE_ALREADY_ENABLED; + else + rv = VNET_API_ERROR_INSTANCE_IN_USE; si = vec_elt_at_index (sm->instances, index); - err = clib_error_return (0, - "interface %U already assgined to " - "instance '%s'", - format_vnet_sw_if_index_name, vnm, - sw_if_index, si->name); + log_err ("interface %U already assgined to instance '%s'", + format_vnet_sw_if_index_name, vnm, sw_if_index, si->name); goto done; } @@ -462,11 +524,9 @@ snort_interface_enable_disable (vlib_main_t *vm, char *instance_name, if (sw_if_index >= vec_len (sm->instance_by_sw_if_index) || sm->instance_by_sw_if_index[sw_if_index] == ~0) { - err = - clib_error_return (0, - "interface %U is not assigned to snort " - "instance!", - format_vnet_sw_if_index_name, vnm, sw_if_index); + rv = VNET_API_ERROR_INVALID_INTERFACE; + log_err ("interface %U is not assigned to snort instance!", + format_vnet_sw_if_index_name, vnm, sw_if_index); goto done; } index = sm->instance_by_sw_if_index[sw_if_index]; @@ -488,12 +548,66 @@ snort_interface_enable_disable (vlib_main_t *vm, char *instance_name, } done: - if (err) - log_err ("%U", format_clib_error, err); - return 0; + return rv; } -clib_error_t * +static int +snort_strip_instance_interfaces (vlib_main_t *vm, u32 instance_index) +{ + snort_main_t *sm = &snort_main; + u32 *index; + int rv = 0; + + vec_foreach (index, sm->instance_by_sw_if_index) + { + if (*index == instance_index) + rv = snort_interface_enable_disable ( + vm, NULL, index - sm->instance_by_sw_if_index, 0, 0); + if (rv) + break; + } + + return rv; +} + +int +snort_instance_delete (vlib_main_t *vm, u32 instance_index) +{ + snort_main_t *sm = &snort_main; + snort_instance_t *si; + snort_qpair_t *qp; + int rv = 0; + + si = snort_get_instance_by_index (instance_index); + if (!si) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + if (si->client_index != ~0) + return VNET_API_ERROR_INSTANCE_IN_USE; + + if ((rv = snort_strip_instance_interfaces (vm, si->index))) + return rv; + + hash_unset_mem (sm->instance_by_name, si->name); + + clib_mem_vm_unmap (si->shm_base); + close (si->shm_fd); + + vec_foreach (qp, si->qpairs) + { + clib_file_del_by_index (&file_main, qp->deq_fd_file_index); + } + + log_debug ("deleting instance '%s'", si->name); + + vec_free (si->qpairs); + vec_free (si->name); + pool_put (sm->instances, si); + + return rv; +} + +int snort_set_node_mode (vlib_main_t *vm, u32 mode) { int i; diff --git a/src/plugins/snort/snort.api b/src/plugins/snort/snort.api new file mode 100644 index 00000000000..5c65f79e68a --- /dev/null +++ b/src/plugins/snort/snort.api @@ -0,0 +1,226 @@ +option version = "1.0.0"; + +import "vnet/interface_types.api"; +import "vnet/ip/ip_types.api"; + +define snort_instance_create { + u32 client_index; + u32 context; + u32 queue_size; + u8 drop_on_disconnect; + string name[]; +}; + +define snort_instance_create_reply { + u32 context; + i32 retval; + u32 instance_index; +}; + +define snort_instance_delete { + u32 client_index; + u32 context; + u32 instance_index; +}; + +define snort_instance_delete_reply { + u32 context; + i32 retval; +}; + +define snort_client_disconnect { + u32 client_index; + u32 context; + u32 snort_client_index; +}; + +define snort_client_disconnect_reply { + u32 context; + i32 retval; +}; + +define snort_instance_disconnect { + u32 client_index; + u32 context; + u32 instance_index; +}; + +define snort_instance_disconnect_reply { + u32 context; + i32 retval; +}; + +define snort_interface_attach { + u32 client_index; + u32 context; + u32 instance_index; + u32 sw_if_index; + u8 snort_dir; +}; + +define snort_interface_attach_reply { + u32 context; + i32 retval; +}; + +define snort_interface_detach { + u32 client_index; + u32 context; + u32 sw_if_index; +}; + +define snort_interface_detach_reply { + u32 context; + i32 retval; +}; + +define snort_input_mode_get { + u32 client_index; + u32 context; +}; + +define snort_input_mode_get_reply { + u32 context; + i32 retval; + u32 snort_mode; +}; + +define snort_input_mode_set { + u32 client_index; + u32 context; + u8 input_mode; +}; + +define snort_input_mode_set_reply { + u32 context; + i32 retval; +}; + +service { + rpc snort_instance_get returns snort_instance_get_reply + stream snort_instance_details; +}; + +/** \brief Get snort instance(s). + @param client_index - opaque cookie to identify the sender. + @param context - sender context + @param cursor - current iterator value (all requested). + @param instance_index - instance index (~0 for all). +*/ +define snort_instance_get +{ + u32 client_index; + u32 context; + u32 cursor; + u32 instance_index; +}; + +/** \brief Reply for snort instance(s). + @param context - sender context + @param retval - return code for the request. + @param cursor - iterator value to continue with (if there is more). +*/ +define snort_instance_get_reply +{ + u32 context; + i32 retval; + u32 cursor; +}; + +/** \brief Details of a snort instance. + @param context - sender context + @param instance - snort instance info. +*/ +define snort_instance_details { + u32 context; + u32 instance_index; + u32 shm_size; + u32 shm_fd; + u8 drop_on_disconnect; + u32 snort_client_index; + string name[]; +}; + +service { + rpc snort_interface_get returns snort_interface_get_reply + stream snort_interface_details; +}; + +/** \brief Get snort interface(s). + @param client_index - opaque cookie to identify the sender. + @param context - sender context + @param cursor - current iterator value (all requested). + @param sw_if_index - sw if index (~0 for all). +*/ +define snort_interface_get +{ + u32 client_index; + u32 context; + u32 cursor; + u32 sw_if_index; +}; + +/** \brief Reply for snort interface(s). + @param context - sender context + @param retval - return code for the request. + @param cursor - iterator value to continue with (if there is more). +*/ +define snort_interface_get_reply +{ + u32 context; + i32 retval; + u32 cursor; +}; + +/** \brief Details of a snort interface. + @param context - sender context + @param sw_if_index - interface index + @param instance_index - snort instance the interface is attached to. +*/ +define snort_interface_details { + u32 context; + u32 sw_if_index; + u32 instance_index; +}; + +service { + rpc snort_client_get returns snort_client_get_reply + stream snort_client_details; +}; + +/** \brief Get snort clients. + @param client_index - opaque cookie to identify the sender. + @param context - sender context + @param cursor - current iterator value (all requested). + @param client_index (~0 for all). +*/ +define snort_client_get +{ + u32 client_index; + u32 context; + u32 cursor; + u32 snort_client_index; +}; + +/** \brief Reply for snort clients. + @param context - sender context + @param retval - return code for the request. + @param cursor - iterator value to continue with (if there is more). +*/ +define snort_client_get_reply +{ + u32 context; + i32 retval; + u32 cursor; +}; + +/** \brief Details of a snort client. + @param context - sender context + @param client index + @param instance_index - snort instance of the client. +*/ +define snort_client_details { + u32 context; + u32 client_index; + u32 instance_index; +}; diff --git a/src/plugins/snort/snort.h b/src/plugins/snort/snort.h index 79299aa6d91..c7e856c0127 100644 --- a/src/plugins/snort/snort.h +++ b/src/plugins/snort/snort.h @@ -7,6 +7,7 @@ #include <vppinfra/error.h> #include <vppinfra/socket.h> +#include <vppinfra/file.h> #include <vlib/vlib.h> #include <snort/daq_vpp.h> @@ -78,8 +79,11 @@ typedef struct snort_per_thread_data_t *per_thread_data; u32 input_mode; u8 *socket_name; + /* API message ID base */ + u16 msg_id_base; } snort_main_t; +extern clib_file_main_t file_main; extern snort_main_t snort_main; extern vlib_node_registration_t snort_enq_node; extern vlib_node_registration_t snort_deq_node; @@ -103,13 +107,17 @@ typedef enum } /* functions */ -clib_error_t *snort_instance_create (vlib_main_t *vm, char *name, - u8 log2_queue_sz, u8 drop_on_disconnect); -clib_error_t *snort_interface_enable_disable (vlib_main_t *vm, - char *instance_name, - u32 sw_if_index, int is_enable, - snort_attach_dir_t dir); -clib_error_t *snort_set_node_mode (vlib_main_t *vm, u32 mode); +snort_main_t *snort_get_main (); +snort_instance_t *snort_get_instance_by_index (u32 instance_index); +snort_instance_t *snort_get_instance_by_name (char *name); +int snort_instance_create (vlib_main_t *vm, char *name, u8 log2_queue_sz, + u8 drop_on_disconnect); +int snort_interface_enable_disable (vlib_main_t *vm, char *instance_name, + u32 sw_if_index, int is_enable, + snort_attach_dir_t dir); +int snort_set_node_mode (vlib_main_t *vm, u32 mode); +int snort_instance_delete (vlib_main_t *vm, u32 instance_index); +int snort_instance_disconnect (vlib_main_t *vm, u32 instance_index); always_inline void snort_freelist_init (u32 *fl) diff --git a/src/plugins/snort/snort_api.c b/src/plugins/snort/snort_api.c new file mode 100644 index 00000000000..adad0d8763f --- /dev/null +++ b/src/plugins/snort/snort_api.c @@ -0,0 +1,405 @@ +#include <vlib/vlib.h> +#include <vnet/plugin/plugin.h> +#include <snort/snort.h> +#include <vlibapi/api_types.h> + +#include <snort/snort.api_enum.h> +#include <snort/snort.api_types.h> + +#include <vlibmemory/api.h> +#include <vnet/ip/ip_types_api.h> +#include <vnet/format_fns.h> +#include <vnet/api_errno.h> + +/** + * Base message ID fot the plugin + */ +static u32 snort_base_msg_id; +#define REPLY_MSG_ID_BASE snort_base_msg_id + +#include <vlibapi/api_helper_macros.h> + +#include <vnet/vnet.h> + +#include <vlibapi/api.h> +#include <sys/eventfd.h> + +VLIB_REGISTER_LOG_CLASS (snort_log, static) = { + .class_name = "snort", +}; + +#define log_debug(fmt, ...) vlib_log_debug (snort_log.class, fmt, __VA_ARGS__) +#define log_err(fmt, ...) vlib_log_err (snort_log.class, fmt, __VA_ARGS__) + +static void +vl_api_snort_instance_create_t_handler (vl_api_snort_instance_create_t *mp) +{ + vlib_main_t *vm = vlib_get_main (); + vl_api_snort_instance_create_reply_t *rmp; + char *name = vl_api_from_api_to_new_c_string (&mp->name); + u32 queue_sz = clib_net_to_host_u32 (mp->queue_size); + u8 drop_on_disconnect = mp->drop_on_disconnect; + int rv = 0; + u32 instance_index = ~0; + snort_instance_t *si; + + rv = + snort_instance_create (vm, name, min_log2 (queue_sz), drop_on_disconnect); + + if ((si = snort_get_instance_by_name (name))) + { + instance_index = si->index; + } + + REPLY_MACRO2 (VL_API_SNORT_INSTANCE_CREATE_REPLY, ({ + rmp->instance_index = clib_host_to_net_u32 (instance_index); + })); +} + +static void +vl_api_snort_instance_delete_t_handler (vl_api_snort_instance_delete_t *mp) +{ + vlib_main_t *vm = vlib_get_main (); + vl_api_snort_instance_delete_reply_t *rmp; + u32 instance_index = clib_net_to_host_u32 (mp->instance_index); + int rv; + + rv = snort_instance_delete (vm, instance_index); + + REPLY_MACRO (VL_API_SNORT_INSTANCE_DELETE_REPLY); +} + +static void +vl_api_snort_interface_attach_t_handler (vl_api_snort_interface_attach_t *mp) +{ + vlib_main_t *vm = vlib_get_main (); + vl_api_snort_interface_attach_reply_t *rmp; + u32 instance_index = clib_net_to_host_u32 (mp->instance_index); + snort_instance_t *instance = 0; + u32 sw_if_index = clib_net_to_host_u32 (mp->sw_if_index); + u8 snort_dir = mp->snort_dir; + int rv = VNET_API_ERROR_NO_SUCH_ENTRY; + + if (sw_if_index == INDEX_INVALID) + rv = VNET_API_ERROR_NO_MATCHING_INTERFACE; + else + { + instance = snort_get_instance_by_index (instance_index); + if (instance) + rv = snort_interface_enable_disable (vm, (char *) instance->name, + sw_if_index, 1 /* is_enable */, + snort_dir); + } + + REPLY_MACRO (VL_API_SNORT_INTERFACE_ATTACH_REPLY); +} + +static void +send_snort_instance_details (const snort_instance_t *instance, + vl_api_registration_t *rp, u32 context) +{ + vl_api_snort_instance_details_t *rmp; + u32 name_len = vec_len (instance->name); + + REPLY_MACRO_DETAILS5 ( + VL_API_SNORT_INSTANCE_DETAILS, name_len, rp, context, ({ + rmp->instance_index = clib_host_to_net_u32 (instance->index); + vl_api_vec_to_api_string (instance->name, &rmp->name); + rmp->snort_client_index = clib_host_to_net_u32 (instance->client_index); + rmp->shm_size = clib_host_to_net_u32 (instance->shm_size); + rmp->shm_fd = clib_host_to_net_u32 (instance->shm_fd); + rmp->drop_on_disconnect = instance->drop_on_disconnect; + })); +} + +static void +vl_api_snort_instance_get_t_handler (vl_api_snort_instance_get_t *mp) +{ + snort_main_t *sm = snort_get_main (); + snort_instance_t *instance = 0; + vl_api_snort_instance_get_reply_t *rmp; + u32 instance_index; + int rv = 0; + + instance_index = clib_net_to_host_u32 (mp->instance_index); + + if (instance_index == INDEX_INVALID) + { + /* clang-format off */ + REPLY_AND_DETAILS_MACRO ( + VL_API_SNORT_INSTANCE_GET_REPLY, sm->instances, ({ + instance = pool_elt_at_index (sm->instances, cursor); + send_snort_instance_details (instance, rp, mp->context); + })); + /* clang-format on */ + } + else + { + instance = snort_get_instance_by_index (instance_index); + + if (instance) + { + vl_api_registration_t *rp = + vl_api_client_index_to_registration (mp->client_index); + + if (rp == NULL) + { + return; + } + + send_snort_instance_details (instance, rp, mp->context); + } + else + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + } + + /* clang-format off */ + REPLY_MACRO2 (VL_API_SNORT_INSTANCE_GET_REPLY, ({ + rmp->cursor = INDEX_INVALID; + })); + /* clang-format on */ + } +} + +static void +send_snort_interface_details (u32 sw_if_index, u32 instance_index, + vl_api_registration_t *rp, u32 context) +{ + vl_api_snort_interface_details_t *rmp; + + if (instance_index != ~0) + { + REPLY_MACRO_DETAILS4 (VL_API_SNORT_INTERFACE_DETAILS, rp, context, ({ + rmp->instance_index = + clib_host_to_net_u32 (instance_index); + rmp->sw_if_index = + clib_host_to_net_u32 (sw_if_index); + })); + } +} + +static void +vl_api_snort_interface_get_t_handler (vl_api_snort_interface_get_t *mp) +{ + snort_main_t *sm = snort_get_main (); + vl_api_snort_interface_get_reply_t *rmp; + u32 sw_if_index; + u32 *index; + int rv = 0; + + sw_if_index = clib_net_to_host_u32 (mp->sw_if_index); + + if (sw_if_index == INDEX_INVALID) + { + /* clang-format off */ + if (vec_len (sm->instance_by_sw_if_index) == 0) + { + REPLY_MACRO2 (VL_API_SNORT_INTERFACE_GET_REPLY, ({ rmp->cursor = ~0; })); + return; + } + + REPLY_AND_DETAILS_VEC_MACRO( + VL_API_SNORT_INTERFACE_GET_REPLY, + sm->instance_by_sw_if_index, + mp, rmp, rv, ({ + index = vec_elt_at_index (sm->instance_by_sw_if_index, cursor); + send_snort_interface_details (cursor, *index, rp, mp->context); + })) + /* clang-format on */ + } + else + { + index = vec_elt_at_index (sm->instance_by_sw_if_index, sw_if_index); + if (snort_get_instance_by_index (index[0])) + { + vl_api_registration_t *rp = + vl_api_client_index_to_registration (mp->client_index); + + if (rp == NULL) + { + return; + } + + send_snort_interface_details (sw_if_index, *index, rp, mp->context); + } + else + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + } + + /* clang-format off */ + REPLY_MACRO2 (VL_API_SNORT_INTERFACE_GET_REPLY, ({ + rmp->cursor = INDEX_INVALID; + })); + /* clang-format on */ + } +} + +static void +send_snort_client_details (const snort_client_t *client, + vl_api_registration_t *rp, u32 context) +{ + snort_main_t *sm = snort_get_main (); + vl_api_snort_client_details_t *rmp; + snort_instance_t *instance; + + if (client->instance_index == ~0) + { + return; + } + + instance = pool_elt_at_index (sm->instances, client->instance_index); + if (instance) + { + REPLY_MACRO_DETAILS4 (VL_API_SNORT_CLIENT_DETAILS, rp, context, ({ + rmp->instance_index = + clib_host_to_net_u32 (client->instance_index); + rmp->client_index = + clib_host_to_net_u32 (client - sm->clients); + })); + } +} + +static void +vl_api_snort_client_get_t_handler (vl_api_snort_client_get_t *mp) +{ + snort_main_t *sm = snort_get_main (); + snort_client_t *client; + vl_api_snort_client_get_reply_t *rmp; + u32 client_index; + int rv = 0; + + client_index = clib_net_to_host_u32 (mp->snort_client_index); + + if (client_index == INDEX_INVALID) + { + /* clang-format off */ + REPLY_AND_DETAILS_MACRO ( + VL_API_SNORT_CLIENT_GET_REPLY, sm->clients, ({ + client = pool_elt_at_index (sm->clients, cursor); + send_snort_client_details (client, rp, mp->context); + })); + /* clang-format on */ + } + else + { + client = pool_elt_at_index (sm->clients, client_index); + + if (client) + { + vl_api_registration_t *rp = + vl_api_client_index_to_registration (mp->client_index); + + if (rp == NULL) + { + return; + } + + send_snort_client_details (client, rp, mp->context); + } + else + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + } + + /* clang-format off */ + REPLY_MACRO2 (VL_API_SNORT_CLIENT_GET_REPLY, ({ + rmp->cursor = INDEX_INVALID; + })); + /* clang-format on */ + } +} + +static void +vl_api_snort_client_disconnect_t_handler (vl_api_snort_client_disconnect_t *mp) +{ + vlib_main_t *vm = vlib_get_main (); + snort_main_t *sm = snort_get_main (); + snort_client_t *client; + vl_api_snort_client_disconnect_reply_t *rmp; + u32 client_index = clib_net_to_host_u32 (mp->snort_client_index); + int rv = 0; + + if (pool_is_free_index (sm->clients, client_index)) + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + } + else + { + client = pool_elt_at_index (sm->clients, client_index); + rv = snort_instance_disconnect (vm, client->instance_index); + } + + REPLY_MACRO (VL_API_SNORT_CLIENT_DISCONNECT_REPLY); +} + +static void +vl_api_snort_instance_disconnect_t_handler ( + vl_api_snort_instance_disconnect_t *mp) +{ + vlib_main_t *vm = vlib_get_main (); + vl_api_snort_instance_disconnect_reply_t *rmp; + u32 instance_index = clib_net_to_host_u32 (mp->instance_index); + int rv = snort_instance_disconnect (vm, instance_index); + + REPLY_MACRO (VL_API_SNORT_INSTANCE_DISCONNECT_REPLY); +} + +static void +vl_api_snort_interface_detach_t_handler (vl_api_snort_interface_detach_t *mp) +{ + vlib_main_t *vm = vlib_get_main (); + vl_api_snort_interface_detach_reply_t *rmp; + u32 sw_if_index = clib_net_to_host_u32 (mp->sw_if_index); + int rv = VNET_API_ERROR_NO_MATCHING_INTERFACE; + + if (sw_if_index != INDEX_INVALID) + rv = snort_interface_enable_disable (vm, NULL, sw_if_index, + 0 /* is_enable */, SNORT_INOUT); + + REPLY_MACRO (VL_API_SNORT_INTERFACE_DETACH_REPLY); +} + +static void +vl_api_snort_input_mode_get_t_handler (vl_api_snort_input_mode_get_t *mp) +{ + snort_main_t *sm = &snort_main; + vl_api_snort_input_mode_get_reply_t *rmp; + int rv = 0; + + REPLY_MACRO2 (VL_API_SNORT_INPUT_MODE_GET_REPLY, ({ + rmp->snort_mode = clib_host_to_net_u32 (sm->input_mode); + })); +} + +static void +vl_api_snort_input_mode_set_t_handler (vl_api_snort_input_mode_set_t *mp) +{ + vlib_main_t *vm = vlib_get_main (); + vl_api_snort_input_mode_set_reply_t *rmp; + u8 mode = mp->input_mode; + int rv = 0; + + if (mode != VLIB_NODE_STATE_INTERRUPT && mode != VLIB_NODE_STATE_POLLING) + { + clib_error_return (0, "invalid input mode %u", mode); + } + snort_set_node_mode (vm, mode); + + REPLY_MACRO (VL_API_SNORT_INPUT_MODE_SET_REPLY); +} + +/* API definitions */ +#include <snort/snort.api.c> + +clib_error_t * +snort_init_api (vlib_main_t *vm) +{ + /* Add our API messages to the global name_crc hash table */ + snort_base_msg_id = setup_message_id_table (); + + return NULL; +} + +VLIB_INIT_FUNCTION (snort_init_api); diff --git a/src/plugins/srtp/srtp.c b/src/plugins/srtp/srtp.c index bb54e672918..6862301d2d2 100644 --- a/src/plugins/srtp/srtp.c +++ b/src/plugins/srtp/srtp.c @@ -641,10 +641,12 @@ srtp_connect (transport_endpoint_cfg_t *tep) application_t *app; srtp_tc_t *ctx; u32 ctx_index; + transport_endpt_ext_cfg_t *ext_cfg; int rv; sep = (session_endpoint_cfg_t *) tep; - if (!sep->ext_cfg) + ext_cfg = session_endpoint_get_ext_cfg (sep, TRANSPORT_ENDPT_EXT_CFG_NONE); + if (!ext_cfg) return SESSION_E_NOEXTCFG; app_wrk = app_worker_get (sep->app_wrk_index); @@ -658,7 +660,7 @@ srtp_connect (transport_endpoint_cfg_t *tep) ctx->srtp_ctx_handle = ctx_index; ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP; - srtp_init_policy (ctx, (transport_endpt_cfg_srtp_t *) sep->ext_cfg->data); + srtp_init_policy (ctx, (transport_endpt_cfg_srtp_t *) ext_cfg->data); clib_memcpy_fast (&cargs->sep, sep, sizeof (session_endpoint_t)); cargs->sep.transport_proto = TRANSPORT_PROTO_UDP; @@ -723,9 +725,11 @@ srtp_start_listen (u32 app_listener_index, transport_endpoint_cfg_t *tep) app_listener_t *al; srtp_tc_t *lctx; u32 lctx_index; + transport_endpt_ext_cfg_t *ext_cfg; sep = (session_endpoint_cfg_t *) tep; - if (!sep->ext_cfg) + ext_cfg = session_endpoint_get_ext_cfg (sep, TRANSPORT_ENDPT_EXT_CFG_NONE); + if (!ext_cfg) return SESSION_E_NOEXTCFG; app_wrk = app_worker_get (sep->app_wrk_index); @@ -756,7 +760,7 @@ srtp_start_listen (u32 app_listener_index, transport_endpoint_cfg_t *tep) lctx->c_s_index = app_listener_index; lctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP; - srtp_init_policy (lctx, (transport_endpt_cfg_srtp_t *) sep->ext_cfg->data); + srtp_init_policy (lctx, (transport_endpt_cfg_srtp_t *) ext_cfg->data); SRTP_DBG (1, "Started listening %d", lctx_index); return lctx_index; diff --git a/src/plugins/tlsmbedtls/tls_mbedtls.c b/src/plugins/tlsmbedtls/tls_mbedtls.c index af04f1adeb0..2f4757e28a1 100644 --- a/src/plugins/tlsmbedtls/tls_mbedtls.c +++ b/src/plugins/tlsmbedtls/tls_mbedtls.c @@ -396,6 +396,8 @@ mbedtls_ctx_handshake_rx (tls_ctx_t * ctx) if (mc->ssl.state != MBEDTLS_SSL_HANDSHAKE_OVER) return 0; + ctx->flags |= TLS_CONN_F_HS_DONE; + /* * Handshake complete */ @@ -532,17 +534,10 @@ mbedtls_ctx_read (tls_ctx_t * ctx, session_t * tls_session) return enq; } -static u8 -mbedtls_handshake_is_over (tls_ctx_t * ctx) -{ - mbedtls_ctx_t *mc = (mbedtls_ctx_t *) ctx; - return (mc->ssl.state == MBEDTLS_SSL_HANDSHAKE_OVER); -} - static int mbedtls_transport_close (tls_ctx_t * ctx) { - if (!mbedtls_handshake_is_over (ctx)) + if (!(ctx->flags & TLS_CONN_F_HS_DONE)) { session_close (session_get_from_handle (ctx->tls_session_handle)); return 0; @@ -554,7 +549,7 @@ mbedtls_transport_close (tls_ctx_t * ctx) static int mbedtls_transport_reset (tls_ctx_t *ctx) { - if (!mbedtls_handshake_is_over (ctx)) + if (!(ctx->flags & TLS_CONN_F_HS_DONE)) { session_close (session_get_from_handle (ctx->tls_session_handle)); return 0; @@ -590,7 +585,6 @@ const static tls_engine_vft_t mbedtls_engine = { .ctx_init_client = mbedtls_ctx_init_client, .ctx_write = mbedtls_ctx_write, .ctx_read = mbedtls_ctx_read, - .ctx_handshake_is_over = mbedtls_handshake_is_over, .ctx_start_listen = mbedtls_start_listen, .ctx_stop_listen = mbedtls_stop_listen, .ctx_transport_close = mbedtls_transport_close, diff --git a/src/plugins/tlsopenssl/tls_openssl.c b/src/plugins/tlsopenssl/tls_openssl.c index 5d172a0adcf..c8e685f20c5 100644 --- a/src/plugins/tlsopenssl/tls_openssl.c +++ b/src/plugins/tlsopenssl/tls_openssl.c @@ -1037,15 +1037,6 @@ openssl_ctx_init_server (tls_ctx_t * ctx) return 0; } -static u8 -openssl_handshake_is_over (tls_ctx_t * ctx) -{ - openssl_ctx_t *mc = (openssl_ctx_t *) ctx; - if (!mc->ssl) - return 0; - return SSL_is_init_finished (mc->ssl); -} - static int openssl_transport_close (tls_ctx_t * ctx) { @@ -1054,7 +1045,7 @@ openssl_transport_close (tls_ctx_t * ctx) return 0; #endif - if (!openssl_handshake_is_over (ctx)) + if (!(ctx->flags & TLS_CONN_F_HS_DONE)) { openssl_handle_handshake_failure (ctx); return 0; @@ -1066,7 +1057,7 @@ openssl_transport_close (tls_ctx_t * ctx) static int openssl_transport_reset (tls_ctx_t *ctx) { - if (!openssl_handshake_is_over (ctx)) + if (!(ctx->flags & TLS_CONN_F_HS_DONE)) { openssl_handle_handshake_failure (ctx); return 0; @@ -1166,7 +1157,6 @@ const static tls_engine_vft_t openssl_engine = { .ctx_init_client = openssl_ctx_init_client, .ctx_write = openssl_ctx_write, .ctx_read = openssl_ctx_read, - .ctx_handshake_is_over = openssl_handshake_is_over, .ctx_start_listen = openssl_start_listen, .ctx_stop_listen = openssl_stop_listen, .ctx_transport_close = openssl_transport_close, @@ -1286,7 +1276,10 @@ tls_openssl_set_command_fn (vlib_main_t * vm, unformat_input_t * input, } else { - vnet_session_enable_disable (vm, 1); + session_enable_disable_args_t args = { .is_en = 1, + .rt_engine_type = + RT_BACKEND_ENGINE_RULE_TABLE }; + vnet_session_enable_disable (vm, &args); if (openssl_engine_register (engine_name, engine_alg, async) < 0) { return clib_error_return (0, "Failed to register %s polling", diff --git a/src/plugins/tlspicotls/CMakeLists.txt b/src/plugins/tlspicotls/CMakeLists.txt index e60a0e0ebd4..3d16ded34ce 100644 --- a/src/plugins/tlspicotls/CMakeLists.txt +++ b/src/plugins/tlspicotls/CMakeLists.txt @@ -10,7 +10,7 @@ endif() # it's reasonable to make this check to avoid breaking # existing builds when upgrading the quicly/picotls # versions -set(EXPECTED_QUICLY_VERSION "0.1.4-vpp") +set(EXPECTED_QUICLY_VERSION "0.1.5-vpp") vpp_find_path(QUICLY_INCLUDE_DIR NAMES quicly.h) diff --git a/src/plugins/tlspicotls/tls_picotls.c b/src/plugins/tlspicotls/tls_picotls.c index 7375b928206..9459cb776b5 100644 --- a/src/plugins/tlspicotls/tls_picotls.c +++ b/src/plugins/tlspicotls/tls_picotls.c @@ -88,14 +88,6 @@ picotls_lctx_get (u32 lctx_index) return pool_elt_at_index (picotls_main.lctx_pool, lctx_index); } -static u8 -picotls_handshake_is_over (tls_ctx_t * ctx) -{ - picotls_ctx_t *ptls_ctx = (picotls_ctx_t *) ctx; - assert (ptls_ctx->tls); - return ptls_handshake_is_complete (ptls_ctx->tls); -} - static int picotls_try_handshake_write (picotls_ctx_t * ptls_ctx, session_t * tls_session, ptls_buffer_t * buf) @@ -194,7 +186,7 @@ picotls_confirm_app_close (tls_ctx_t * ctx) static int picotls_transport_close (tls_ctx_t * ctx) { - if (!picotls_handshake_is_over (ctx)) + if (!(ctx->flags & TLS_CONN_F_HS_DONE)) { picotls_handle_handshake_failure (ctx); return 0; @@ -206,7 +198,7 @@ picotls_transport_close (tls_ctx_t * ctx) static int picotls_transport_reset (tls_ctx_t *ctx) { - if (!picotls_handshake_is_over (ctx)) + if (!(ctx->flags & TLS_CONN_F_HS_DONE)) { picotls_handle_handshake_failure (ctx); return 0; @@ -435,7 +427,7 @@ picotls_ctx_read (tls_ctx_t *ctx, session_t *tcp_session) if (PREDICT_FALSE (!ptls_handshake_is_complete (ptls_ctx->tls))) { picotls_do_handshake (ptls_ctx, tcp_session); - if (picotls_handshake_is_over (ctx)) + if (ctx->flags & TLS_CONN_F_HS_DONE) { if (ptls_is_server (ptls_ctx->tls)) { @@ -750,7 +742,6 @@ const static tls_engine_vft_t picotls_engine = { .ctx_free = picotls_ctx_free, .ctx_get = picotls_ctx_get, .ctx_get_w_thread = picotls_ctx_get_w_thread, - .ctx_handshake_is_over = picotls_handshake_is_over, .ctx_start_listen = picotls_start_listen, .ctx_stop_listen = picotls_stop_listen, .ctx_init_server = picotls_ctx_init_server, diff --git a/src/plugins/tracenode/node.c b/src/plugins/tracenode/node.c index 444d93f1708..c56df589826 100644 --- a/src/plugins/tracenode/node.c +++ b/src/plugins/tracenode/node.c @@ -55,23 +55,19 @@ tracenode_inline (vlib_main_t *vm, vlib_node_runtime_t *node, /* enqueue b0 to the current next frame */ vnet_feature_next_u16 (next, b[0]); - /* buffer already traced */ - if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED)) - goto skip; - if (is_pcap && vnet_is_packet_pcaped (pp, b[0], ~0)) { pcap_add_buffer (&pp->pcap_main, vm, from0[0], pp->max_bytes_per_pkt); } - else if (!is_pcap && vlib_trace_buffer (vm, node, next[0], b[0], - 1 /* follow_chain */)) + else if (!is_pcap && !(b[0]->flags & VLIB_BUFFER_IS_TRACED) && + vlib_trace_buffer (vm, node, next[0], b[0], + 1 /* follow_chain */)) { tracenode_trace_t *tr = vlib_add_trace (vm, node, b[0], sizeof *tr); tr->sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_RX]; } - skip: b++; from0++; next++; diff --git a/src/plugins/unittest/fib_test.c b/src/plugins/unittest/fib_test.c index fbac809d726..491d135322c 100644 --- a/src/plugins/unittest/fib_test.c +++ b/src/plugins/unittest/fib_test.c @@ -10264,7 +10264,57 @@ fib_test_inherit (void) &l99_o_10_10_10_3), "%U via interposer label", format_fib_prefix,&pfx_10_10_10_21_s_32); + fib_table_entry_special_remove(0, + &pfx_10_10_10_0_s_24, + FIB_SOURCE_SPECIAL); + + const ip46_address_t nh_0_0_0_0 = { + .ip4.as_u32 = clib_host_to_net_u32(0x00000000), + }; + const fib_prefix_t pfx_0_0_0_0_s_0 = { + .fp_len = 0, + .fp_proto = FIB_PROTOCOL_IP4, + .fp_addr = nh_0_0_0_0, + }; + /* we have prio(API) < prio(hi_src) < prio(SPECIAL) */ + /* Add/remove an interposer source from the top of the subtrie. The + * interposer source is inherited. + */ + fib_table_entry_special_dpo_add(0, + &pfx_0_0_0_0_s_0, + hi_src, + (FIB_ENTRY_FLAG_COVERED_INHERIT | + FIB_ENTRY_FLAG_INTERPOSE), + &interposer); + /* + * Add/remove an interposer source from the top of the subtrie. The + * interposer source is inherited, the previous inheritance is discarded. + */ + fib_table_entry_special_dpo_add(0, + &pfx_10_10_10_0_s_24, + FIB_SOURCE_SPECIAL, + (FIB_ENTRY_FLAG_COVERED_INHERIT | + FIB_ENTRY_FLAG_INTERPOSE), + &interposer); + /* force a tree walk */ + fib_table_entry_update_one_path(0, + &pfx_0_0_0_0_s_0, + FIB_SOURCE_API, + FIB_ENTRY_FLAG_NONE, + DPO_PROTO_IP4, + &nh_10_10_10_3, + tm->hw[0]->sw_if_index, + ~0, + 1, + NULL, + FIB_ROUTE_PATH_FLAG_NONE); + fib_table_entry_special_remove(0, + &pfx_10_10_10_0_s_24, + FIB_SOURCE_SPECIAL); + fib_table_entry_special_remove(0, + &pfx_0_0_0_0_s_0, + hi_src); /* * cleanup */ @@ -10275,6 +10325,7 @@ fib_test_inherit (void) fib_table_entry_delete(0, &pfx_10_10_10_0_s_24, FIB_SOURCE_API); fib_table_entry_delete(0, &pfx_10_10_0_0_s_16, FIB_SOURCE_API); fib_table_entry_delete(0, &pfx_10_10_10_0_s_24, FIB_SOURCE_SPECIAL); + fib_table_entry_delete(0, &pfx_0_0_0_0_s_0, FIB_SOURCE_API); adj_unlock(ai_10_10_10_1); adj_unlock(ai_10_10_10_2); adj_unlock(ai_10_10_10_3); diff --git a/src/plugins/unittest/segment_manager_test.c b/src/plugins/unittest/segment_manager_test.c index a106470ee48..29da662e007 100644 --- a/src/plugins/unittest/segment_manager_test.c +++ b/src/plugins/unittest/segment_manager_test.c @@ -739,8 +739,11 @@ segment_manager_test (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd_arg) { int res = 0; + session_enable_disable_args_t args = { .is_en = 1, + .rt_engine_type = + RT_BACKEND_ENGINE_RULE_TABLE }; - vnet_session_enable_disable (vm, 1); + vnet_session_enable_disable (vm, &args); while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { diff --git a/src/plugins/unittest/session_test.c b/src/plugins/unittest/session_test.c index 33649662e5e..7702e817070 100644 --- a/src/plugins/unittest/session_test.c +++ b/src/plugins/unittest/session_test.c @@ -13,13 +13,11 @@ * limitations under the License. */ -#include <vnet/session/application_namespace.h> -#include <vnet/session/application_interface.h> +#include <arpa/inet.h> #include <vnet/session/application.h> #include <vnet/session/session.h> -#include <vnet/session/session_rules_table.h> -#include <vnet/tcp/tcp.h> #include <sys/epoll.h> +#include <vnet/session/session_rules_table.h> #define SESSION_TEST_I(_cond, _comment, _args...) \ ({ \ @@ -133,7 +131,8 @@ session_create_lookpback (u32 table_id, u32 * sw_if_index, if (table_id != 0) { - ip_table_create (FIB_PROTOCOL_IP4, table_id, 0, 0); + ip_table_create (FIB_PROTOCOL_IP4, table_id, 0 /* is_api */, + 1 /* create_mfib */, 0); ip_table_bind (FIB_PROTOCOL_IP4, *sw_if_index, table_id); } @@ -774,10 +773,37 @@ session_test_namespace (vlib_main_t * vm, unformat_input_t * input) return 0; } +static void +session_test_disable_rt_backend_engine (vlib_main_t *vm) +{ + session_enable_disable_args_t args = { .is_en = 0, + .rt_engine_type = + RT_BACKEND_ENGINE_DISABLE }; + vnet_session_enable_disable (vm, &args); +} + +static void +session_test_enable_rule_table_engine (vlib_main_t *vm) +{ + session_enable_disable_args_t args = { .is_en = 1, + .rt_engine_type = + RT_BACKEND_ENGINE_RULE_TABLE }; + vnet_session_enable_disable (vm, &args); +} + +static void +session_test_enable_sdl_engine (vlib_main_t *vm) +{ + session_enable_disable_args_t args = { .is_en = 1, + .rt_engine_type = + RT_BACKEND_ENGINE_SDL }; + vnet_session_enable_disable (vm, &args); +} + static int session_test_rule_table (vlib_main_t * vm, unformat_input_t * input) { - session_rules_table_t _srt, *srt = &_srt; + session_table_t *st = session_table_alloc (); u16 lcl_port = 1234, rmt_port = 4321; u32 action_index = 1, res; ip4_address_t lcl_lkup, rmt_lkup; @@ -795,8 +821,13 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input) } } - clib_memset (srt, 0, sizeof (*srt)); - session_rules_table_init (srt); + session_test_disable_rt_backend_engine (vm); + session_test_enable_rule_table_engine (vm); + + session_table_init (st, FIB_PROTOCOL_MAX); + vec_add1 (st->appns_index, + app_namespace_index (app_namespace_get_default ())); + session_rules_table_init (st, FIB_PROTOCOL_MAX); ip4_address_t lcl_ip = { .as_u32 = clib_host_to_net_u32 (0x01020304), @@ -835,12 +866,13 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input) .action_index = action_index++, .is_add = 1, }; - error = session_rules_table_add_del (srt, &args); + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); SESSION_TEST ((error == 0), "Add 1.2.3.4/16 1234 5.6.7.8/16 4321 action %d", action_index - 1); - res = - session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip, lcl_port, rmt_port); + res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP, + &lcl_ip, &rmt_ip, lcl_port, rmt_port); SESSION_TEST ((res == 1), "Lookup 1.2.3.4 1234 5.6.7.8 4321, action should " "be 1: %d", res); @@ -851,13 +883,15 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input) args.lcl.fp_addr.ip4 = lcl_ip; args.lcl.fp_len = 24; args.action_index = action_index++; - error = session_rules_table_add_del (srt, &args); + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); SESSION_TEST ((error == 0), "Add 1.2.3.4/24 1234 5.6.7.8/16 4321 action %d", action_index - 1); args.rmt.fp_addr.ip4 = rmt_ip; args.rmt.fp_len = 24; args.action_index = action_index++; - error = session_rules_table_add_del (srt, &args); + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); SESSION_TEST ((error == 0), "Add 1.2.3.4/24 1234 5.6.7.8/24 4321 action %d", action_index - 1); @@ -869,13 +903,15 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input) args.rmt.fp_addr.ip4 = rmt_ip2; args.rmt.fp_len = 16; args.action_index = action_index++; - error = session_rules_table_add_del (srt, &args); + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); SESSION_TEST ((error == 0), "Add 2.2.2.2/24 1234 6.6.6.6/16 4321 action %d", action_index - 1); args.lcl.fp_addr.ip4 = lcl_ip3; args.rmt.fp_addr.ip4 = rmt_ip3; args.action_index = action_index++; - error = session_rules_table_add_del (srt, &args); + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); SESSION_TEST ((error == 0), "Add 3.3.3.3/24 1234 7.7.7.7/16 4321 action %d", action_index - 1); @@ -885,7 +921,8 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input) args.lcl.fp_addr.ip4 = lcl_ip3; args.rmt.fp_addr.ip4 = rmt_ip3; args.action_index = action_index++; - error = session_rules_table_add_del (srt, &args); + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); SESSION_TEST ((error == 0), "overwrite 3.3.3.3/24 1234 7.7.7.7/16 4321 " "action %d", action_index - 1); @@ -893,23 +930,22 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input) * Lookup 1.2.3.4/32 1234 5.6.7.8/32 4321, 1.2.2.4/32 1234 5.6.7.9/32 4321 * and 3.3.3.3 1234 7.7.7.7 4321 */ - res = - session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip, lcl_port, rmt_port); + res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP, + &lcl_ip, &rmt_ip, lcl_port, rmt_port); SESSION_TEST ((res == 3), "Lookup 1.2.3.4 1234 5.6.7.8 4321 action " "should be 3: %d", res); lcl_lkup.as_u32 = clib_host_to_net_u32 (0x01020204); rmt_lkup.as_u32 = clib_host_to_net_u32 (0x05060709); - res = - session_rules_table_lookup4 (srt, &lcl_lkup, - &rmt_lkup, lcl_port, rmt_port); + res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP, + &lcl_lkup, &rmt_lkup, lcl_port, rmt_port); SESSION_TEST ((res == 1), "Lookup 1.2.2.4 1234 5.6.7.9 4321, action " "should be 1: %d", res); - res = - session_rules_table_lookup4 (srt, &lcl_ip3, &rmt_ip3, lcl_port, rmt_port); + res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP, + &lcl_ip3, &rmt_ip3, lcl_port, rmt_port); SESSION_TEST ((res == 6), "Lookup 3.3.3.3 1234 7.7.7.7 4321, action " "should be 6 (updated): %d", res); @@ -925,17 +961,17 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input) args.lcl_port = 0; args.rmt_port = 0; args.action_index = action_index++; - error = session_rules_table_add_del (srt, &args); + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); SESSION_TEST ((error == 0), "Add 1.2.3.4/24 * 5.6.7.8/24 * action %d", action_index - 1); - res = - session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip, lcl_port, rmt_port); + res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP, + &lcl_ip, &rmt_ip, lcl_port, rmt_port); SESSION_TEST ((res == 7), "Lookup 1.2.3.4 1234 5.6.7.8 4321, action should" " be 7 (lpm dst): %d", res); - res = - session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip, - lcl_port + 1, rmt_port); + res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP, + &lcl_ip, &rmt_ip, lcl_port + 1, rmt_port); SESSION_TEST ((res == 7), "Lookup 1.2.3.4 1235 5.6.7.8 4321, action should " "be 7: %d", res); @@ -947,7 +983,8 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input) * 1.2.3.4 1235 5.6.7.8 4322 */ args.is_add = 0; - error = session_rules_table_add_del (srt, &args); + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); SESSION_TEST ((error == 0), "Del 1.2.3.4/24 * 5.6.7.8/24 *"); args.lcl.fp_addr.ip4 = lcl_ip; @@ -958,7 +995,8 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input) args.rmt_port = 0; args.action_index = action_index++; args.is_add = 1; - error = session_rules_table_add_del (srt, &args); + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); SESSION_TEST ((error == 0), "Add 1.2.3.4/16 * 5.6.7.8/16 * action %d", action_index - 1); @@ -970,27 +1008,28 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input) args.rmt_port = rmt_port; args.action_index = action_index++; args.is_add = 1; - error = session_rules_table_add_del (srt, &args); + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); SESSION_TEST ((error == 0), "Add 1.2.3.4/24 1235 5.6.7.8/24 4321 action %d", action_index - 1); if (verbose) - session_rules_table_cli_dump (vm, srt, FIB_PROTOCOL_IP4); + session_rules_table_cli_dump (vm, st->srtg_handle, TRANSPORT_PROTO_TCP, + FIB_PROTOCOL_IP4); - res = - session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip, lcl_port, rmt_port); + res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP, + &lcl_ip, &rmt_ip, lcl_port, rmt_port); SESSION_TEST ((res == 3), "Lookup 1.2.3.4 1234 5.6.7.8 4321, action should " "be 3: %d", res); - res = - session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip, - lcl_port + 1, rmt_port); + res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP, + &lcl_ip, &rmt_ip, lcl_port + 1, rmt_port); SESSION_TEST ((res == 9), "Lookup 1.2.3.4 1235 5.6.7.8 4321, action should " "be 9: %d", res); res = - session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip, - lcl_port + 1, rmt_port + 1); + session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP, &lcl_ip, + &rmt_ip, lcl_port + 1, rmt_port + 1); SESSION_TEST ((res == 8), "Lookup 1.2.3.4 1235 5.6.7.8 4322, action should " "be 8: %d", res); @@ -1004,10 +1043,11 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input) args.lcl.fp_len = 16; args.rmt.fp_len = 16; args.is_add = 0; - error = session_rules_table_add_del (srt, &args); + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); SESSION_TEST ((error == 0), "Del 1.2.0.0/16 1234 5.6.0.0/16 4321"); - res = - session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip, lcl_port, rmt_port); + res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP, + &lcl_ip, &rmt_ip, lcl_port, rmt_port); SESSION_TEST ((res == 3), "Lookup 1.2.3.4 1234 5.6.7.8 4321, action should " "be 3: %d", res); @@ -1015,10 +1055,11 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input) args.lcl_port = 0; args.rmt_port = 0; args.is_add = 0; - error = session_rules_table_add_del (srt, &args); + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); SESSION_TEST ((error == 0), "Del 1.2.0.0/16 * 5.6.0.0/16 *"); - res = - session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip, lcl_port, rmt_port); + res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP, + &lcl_ip, &rmt_ip, lcl_port, rmt_port); SESSION_TEST ((res == 3), "Lookup 1.2.3.4 1234 5.6.7.8 4321, action should " "be 3: %d", res); @@ -1033,12 +1074,15 @@ session_test_rule_table (vlib_main_t * vm, unformat_input_t * input) args.lcl_port = 1234; args.rmt_port = 4321; args.is_add = 0; - error = session_rules_table_add_del (srt, &args); + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); SESSION_TEST ((error == 0), "Del 1.2.3.4/24 1234 5.6.7.5/24"); - res = - session_rules_table_lookup4 (srt, &lcl_ip, &rmt_ip, lcl_port, rmt_port); + res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP, + &lcl_ip, &rmt_ip, lcl_port, rmt_port); SESSION_TEST ((res == 2), "Action should be 2: %d", res); + session_table_free (st, FIB_PROTOCOL_MAX); + return 0; } @@ -1074,6 +1118,9 @@ session_test_rules (vlib_main_t * vm, unformat_input_t * input) } } + session_test_disable_rt_backend_engine (vm); + session_test_enable_rule_table_engine (vm); + server_sep.is_ip4 = 1; server_sep.port = placeholder_port; clib_memset (options, 0, sizeof (options)); @@ -2079,11 +2126,14 @@ session_get_memory_usage (void) clib_mem_heap_t *heap = clib_mem_get_per_cpu_heap (); u8 *s = 0; char *ss; - f32 used; + f32 used = 0.0; s = format (s, "%U\n", format_clib_mem_heap, heap, 0); ss = strstr ((char *) s, "used:"); - sscanf (ss, "used: %f", &used); + if (ss) + sscanf (ss, "used: %f", &used); + else + clib_warning ("substring 'used:' not found from show memory"); vec_free (s); return (used); } @@ -2111,15 +2161,19 @@ session_test_enable_disable (vlib_main_t *vm, unformat_input_t *input) /* warm up */ for (i = 0; i < 10; i++) { - vnet_session_enable_disable (vm, 0); - vnet_session_enable_disable (vm, 1); + session_test_disable_rt_backend_engine (vm); + session_test_enable_sdl_engine (vm); + session_test_disable_rt_backend_engine (vm); + session_test_enable_rule_table_engine (vm); } was_using = session_get_memory_usage (); for (i = 0; i < iteration; i++) { - vnet_session_enable_disable (vm, 0); - vnet_session_enable_disable (vm, 1); + session_test_disable_rt_backend_engine (vm); + session_test_enable_sdl_engine (vm); + session_test_disable_rt_backend_engine (vm); + session_test_enable_rule_table_engine (vm); } now_using = session_get_memory_usage (); @@ -2130,13 +2184,266 @@ session_test_enable_disable (vlib_main_t *vm, unformat_input_t *input) return 0; } +static int +session_test_sdl (vlib_main_t *vm, unformat_input_t *input) +{ + session_table_t *st = session_table_alloc (); + u16 lcl_port = 0, rmt_port = 0; + u32 action_index = 1, res; + int verbose = 0, error; + ip4_address_t rmt_ip; + const char ip_str_1234[] = "1.2.3.4"; + inet_pton (AF_INET, ip_str_1234, &rmt_ip); + ip4_address_t lcl_ip = { + .as_u32 = clib_host_to_net_u32 (0x0), + }; + ip6_address_t lcl_ip6 = { + .as_u64 = { 0, 0 }, + }; + fib_prefix_t rmt_pref = { + .fp_addr.ip4.as_u32 = rmt_ip.as_u32, + .fp_len = 16, + .fp_proto = FIB_PROTOCOL_IP4, + }; + fib_prefix_t lcl_pref = { + .fp_addr.ip4.as_u32 = lcl_ip.as_u32, + .fp_len = 0, + .fp_proto = 0, + }; + session_rule_table_add_del_args_t args = { + .lcl = lcl_pref, + .rmt = rmt_pref, + .lcl_port = lcl_port, + .rmt_port = rmt_port, + .action_index = action_index++, + .is_add = 1, + }; + const char ip_str_1200[] = "1.2.0.0"; + const char ip_str_1230[] = "1.2.3.0"; + const char ip_str_1111[] = "1.1.1.1"; + const char ip6_str[] = "2501:0db8:85a3:0000:0000:8a2e:0371:1"; + const char ip6_str2[] = "2501:0db8:85a3:0000:0000:8a2e:0372:1"; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "verbose")) + verbose = 1; + else + { + vlib_cli_output (vm, "parse error: '%U'", format_unformat_error, + input); + return -1; + } + } + + session_test_disable_rt_backend_engine (vm); + session_test_enable_sdl_engine (vm); + + session_table_init (st, FIB_PROTOCOL_MAX); + vec_add1 (st->appns_index, + app_namespace_index (app_namespace_get_default ())); + session_rules_table_init (st, FIB_PROTOCOL_MAX); + + /* Add 1.2.0.0/16 */ + args.rmt.fp_len = 16; + inet_pton (AF_INET, ip_str_1200, &args.rmt.fp_addr.ip4.as_u32); + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); + SESSION_TEST ((error == 0), "Add %s/%d action %d", ip_str_1200, + args.rmt.fp_len, action_index - 1); + + /* Lookup 1.2.3.4 */ + res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP, + &lcl_ip, &rmt_ip, lcl_port, rmt_port); + SESSION_TEST ((res == action_index - 1), + "Lookup %s, action should " + "be 1: %d", + ip_str_1234, action_index - 1); + + /* + * Add 1.2.3.0/24 + */ + args.rmt.fp_len = 24; + inet_pton (AF_INET, ip_str_1230, &args.rmt.fp_addr.ip4.as_u32); + args.action_index = action_index++; + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); + SESSION_TEST ((error == 0), "Add %s/%d action %d", ip_str_1230, + args.rmt.fp_len, action_index - 1); + + /* Lookup 1.2.3.4 */ + res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP, + &lcl_ip, &rmt_ip, lcl_port, rmt_port); + SESSION_TEST ((res == action_index - 1), + "Lookup %s, action should " + "be 2: %d", + ip_str_1234, action_index - 1); + + /* look up 1.1.1.1, should be -1 (invalid index) */ + inet_pton (AF_INET, ip_str_1111, &rmt_ip); + res = session_rules_table_lookup4 (st->srtg_handle, TRANSPORT_PROTO_TCP, + &lcl_ip, &rmt_ip, lcl_port, rmt_port); + SESSION_TEST ((res == SESSION_TABLE_INVALID_INDEX), + "Lookup %s, action should " + "be -1: %d", + ip_str_1111, res); + + /* Add again 1.2.0.0/16, should be rejected */ + args.rmt.fp_len = 16; + inet_pton (AF_INET, ip_str_1200, &args.rmt.fp_addr.ip4.as_u32); + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); + SESSION_TEST ((error == SESSION_E_IPINUSE), "Add %s/%d action %d", + ip_str_1200, args.rmt.fp_len, error); + /* + * Add 0.0.0.0/0, should get an error + */ + args.rmt.fp_len = 0; + args.rmt.fp_addr.ip4.as_u32 = 0; + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); + SESSION_TEST ((error == SESSION_E_IPINUSE), "Add 0.0.0.0/%d action %d", + args.rmt.fp_len, error); + + /* delete 0.0.0.0 should be rejected */ + args.is_add = 0; + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); + SESSION_TEST ((error == SESSION_E_NOROUTE), "Del 0.0.0.0/%d action %d", + args.rmt.fp_len, error); + if (verbose) + session_rules_table_cli_dump (vm, st->srtg_handle, TRANSPORT_PROTO_TCP, + FIB_PROTOCOL_IP4); + + /* + * Clean up + * Delete 1.2.0.0/16 + * Delete 1.2.3.0/24 + */ + inet_pton (AF_INET, ip_str_1200, &args.rmt.fp_addr.ip4.as_u32); + args.rmt.fp_len = 16; + args.is_add = 0; + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); + SESSION_TEST ((error == 0), "Del %s/%d should 0: %d", ip_str_1200, + args.rmt.fp_len, error); + + inet_pton (AF_INET, ip_str_1230, &args.rmt.fp_addr.ip4.as_u32); + args.rmt.fp_len = 24; + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); + SESSION_TEST ((error == 0), "Del %s/%d, should be 0: %d", ip_str_1230, + args.rmt.fp_len, error); + if (verbose) + session_rules_table_cli_dump (vm, st->srtg_handle, TRANSPORT_PROTO_TCP, + FIB_PROTOCOL_IP4); + + /* ip6 tests */ + + /* + * Add ip6 2001:0db8:85a3:0000:0000:8a2e:0371:1/124 + */ + ip6_address_t lcl_lkup; + inet_pton (AF_INET6, ip6_str, &args.rmt.fp_addr.ip6); + args.rmt.fp_len = 124; + args.rmt.fp_proto = FIB_PROTOCOL_IP6; + args.action_index = action_index++; + args.is_add = 1; + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); + SESSION_TEST ((error == 0), "Add %s/%d action %d", ip6_str, args.rmt.fp_len, + action_index - 1); + if (verbose) + session_rules_table_cli_dump (vm, st->srtg_handle, TRANSPORT_PROTO_TCP, + FIB_PROTOCOL_IP6); + + /* Lookup 2001:0db8:85a3:0000:0000:8a2e:0371:1 */ + res = session_rules_table_lookup6 (st->srtg_handle, TRANSPORT_PROTO_TCP, + &lcl_ip6, &args.rmt.fp_addr.ip6, lcl_port, + rmt_port); + SESSION_TEST ((res == action_index - 1), + "Lookup %s action should " + "be 3: %d", + ip6_str, action_index - 1); + + /* Lookup 2001:0db8:85a3:0000:0000:8a2e:0372:1 */ + inet_pton (AF_INET6, ip6_str2, &lcl_lkup); + res = session_rules_table_lookup6 (st->srtg_handle, TRANSPORT_PROTO_TCP, + &lcl_ip6, &lcl_lkup, lcl_port, rmt_port); + SESSION_TEST ((res == SESSION_TABLE_INVALID_INDEX), + "Lookup %s action should " + "be -1: %d", + ip6_str2, res); + + /* + * del ip6 2001:0db8:85a3:0000:0000:8a2e:0371:1/124 + */ + args.is_add = 0; + args.rmt.fp_len = 124; + error = + session_rules_table_add_del (st->srtg_handle, TRANSPORT_PROTO_TCP, &args); + SESSION_TEST ((error == 0), "del %s/%d, should be 0: %d", ip6_str, + args.rmt.fp_len, error); + if (verbose) + session_rules_table_cli_dump (vm, st->srtg_handle, TRANSPORT_PROTO_TCP, + FIB_PROTOCOL_IP6); + + session_table_free (st, FIB_PROTOCOL_MAX); + + return 0; +} + +static int +session_test_ext_cfg (vlib_main_t *vm, unformat_input_t *input) +{ + session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL; + transport_endpt_ext_cfg_t *ext_cfg; + + ext_cfg = session_endpoint_add_ext_cfg (&sep, TRANSPORT_ENDPT_EXT_CFG_HTTP, + sizeof (ext_cfg->opaque)); + ext_cfg->opaque = 60; + + ext_cfg = + session_endpoint_add_ext_cfg (&sep, TRANSPORT_ENDPT_EXT_CFG_CRYPTO, + sizeof (transport_endpt_crypto_cfg_t)); + ext_cfg->crypto.ckpair_index = 1; + + ext_cfg = session_endpoint_add_ext_cfg (&sep, TRANSPORT_ENDPT_EXT_CFG_NONE, + sizeof (ext_cfg->opaque)); + ext_cfg->opaque = 345; + + ext_cfg = session_endpoint_get_ext_cfg (&sep, TRANSPORT_ENDPT_EXT_CFG_HTTP); + SESSION_TEST ((ext_cfg != 0), + "TRANSPORT_ENDPT_EXT_CFG_HTTP should be present"); + SESSION_TEST ((ext_cfg->opaque == 60), + "TRANSPORT_ENDPT_EXT_CFG_HTTP opaque value should be 60: %u", + ext_cfg->opaque); + ext_cfg = + session_endpoint_get_ext_cfg (&sep, TRANSPORT_ENDPT_EXT_CFG_CRYPTO); + SESSION_TEST ((ext_cfg != 0), + "TRANSPORT_ENDPT_EXT_CFG_CRYPTO should be present"); + SESSION_TEST ( + (ext_cfg->crypto.ckpair_index == 1), + "TRANSPORT_ENDPT_EXT_CFG_HTTP ckpair_index value should be 1: %u", + ext_cfg->crypto.ckpair_index); + ext_cfg = session_endpoint_get_ext_cfg (&sep, TRANSPORT_ENDPT_EXT_CFG_NONE); + SESSION_TEST ((ext_cfg != 0), + "TRANSPORT_ENDPT_EXT_CFG_NONE should be present"); + SESSION_TEST ((ext_cfg->opaque == 345), + "TRANSPORT_ENDPT_EXT_CFG_HTTP opaque value should be 345: %u", + ext_cfg->opaque); + session_endpoint_free_ext_cfgs (&sep); + + return 0; +} + static clib_error_t * session_test (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd_arg) { int res = 0; - vnet_session_enable_disable (vm, 1); + session_test_enable_rule_table_engine (vm); while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { @@ -2158,6 +2465,10 @@ session_test (vlib_main_t * vm, res = session_test_mq_basic (vm, input); else if (unformat (input, "enable-disable")) res = session_test_enable_disable (vm, input); + else if (unformat (input, "sdl")) + res = session_test_sdl (vm, input); + else if (unformat (input, "ext-cfg")) + res = session_test_ext_cfg (vm, input); else if (unformat (input, "all")) { if ((res = session_test_basic (vm, input))) @@ -2176,6 +2487,10 @@ session_test (vlib_main_t * vm, goto done; if ((res = session_test_mq_basic (vm, input))) goto done; + if ((res = session_test_sdl (vm, input))) + goto done; + if ((res = session_test_ext_cfg (vm, input))) + goto done; if ((res = session_test_enable_disable (vm, input))) goto done; } diff --git a/src/plugins/unittest/tcp_test.c b/src/plugins/unittest/tcp_test.c index 34033a0b622..bd39474ce93 100644 --- a/src/plugins/unittest/tcp_test.c +++ b/src/plugins/unittest/tcp_test.c @@ -1550,8 +1550,11 @@ tcp_test (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd_arg) { int res = 0; + session_enable_disable_args_t args = { .is_en = 1, + .rt_engine_type = + RT_BACKEND_ENGINE_RULE_TABLE }; - vnet_session_enable_disable (vm, 1); + vnet_session_enable_disable (vm, &args); while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { diff --git a/src/plugins/unittest/util_test.c b/src/plugins/unittest/util_test.c index 53384e55494..5b7e30bc21f 100644 --- a/src/plugins/unittest/util_test.c +++ b/src/plugins/unittest/util_test.c @@ -101,6 +101,36 @@ VLIB_CLI_COMMAND (test_hash_command, static) = .function = test_hash_command_fn, }; +static void * +leak_memory_fn (void *args) +{ + u8 *p = 0; + vec_validate (p, 100); + p = 0; + return 0; +} + +static clib_error_t * +test_mem_leak_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + /* do memory leak from thread, so no 'unix_cli' in traceback */ + pthread_t thread; + int rv = pthread_create (&thread, NULL, leak_memory_fn, 0); + if (rv) + { + return clib_error_return (0, "pthread_create failed"); + } + + return 0; +} + +VLIB_CLI_COMMAND (test_mem_leak_command, static) = { + .path = "test mem-leak", + .short_help = "leak some memory", + .function = test_mem_leak_command_fn, +}; + /* * fd.io coding-style-patch-verification: ON * |