diff options
-rw-r--r-- | src/configure.ac | 1 | ||||
-rw-r--r-- | src/plugins/Makefile.am | 4 | ||||
-rw-r--r-- | src/plugins/stn.am | 26 | ||||
-rw-r--r-- | src/plugins/stn/stn.api | 61 | ||||
-rw-r--r-- | src/plugins/stn/stn.c | 484 | ||||
-rw-r--r-- | src/plugins/stn/stn.h | 65 | ||||
-rw-r--r-- | src/plugins/stn/stn_api.c | 231 |
7 files changed, 872 insertions, 0 deletions
diff --git a/src/configure.ac b/src/configure.ac index f06df6ba519..f90ff9686af 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -191,6 +191,7 @@ PLUGIN_ENABLED(memif) PLUGIN_ENABLED(pppoe) PLUGIN_ENABLED(sixrd) PLUGIN_ENABLED(nat) +PLUGIN_ENABLED(stn) ############################################################################### # Dependency checks diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index 205bfe6d5bb..bd77c45f631 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -78,6 +78,10 @@ if ENABLE_NAT_PLUGIN include nat.am endif +if ENABLE_STN_PLUGIN +include stn.am +endif + include ../suffix-rules.mk # Remove *.la files diff --git a/src/plugins/stn.am b/src/plugins/stn.am new file mode 100644 index 00000000000..e2f1a179b18 --- /dev/null +++ b/src/plugins/stn.am @@ -0,0 +1,26 @@ +# Copyright (c) 2016 Cisco Systems, Inc. +# 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. + +vppplugins_LTLIBRARIES += stn_plugin.la + +stn_plugin_la_SOURCES = stn/stn.c stn/stn_api.c + +BUILT_SOURCES += \ + stn/stn.api.h \ + stn/stn.api.json + +noinst_HEADERS += stn/stn.h + +API_FILES += stn/stn.api + +# vi:syntax=automake
\ No newline at end of file diff --git a/src/plugins/stn/stn.api b/src/plugins/stn/stn.api new file mode 100644 index 00000000000..07d214d852a --- /dev/null +++ b/src/plugins/stn/stn.api @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017 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 stn.api + * @brief VPP control-plane API messages for STN plugin. + * + */ + + +/** \brief Add/del STN rules + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_ip4 - 1 if address type is IPv4 + @param ip_address - STN rule IP address + @param sw_if_index - Interface index + @param is_add - 1 if add, 0 if delete +*/ +autoreply manual_print define stn_add_del_rule { + u32 client_index; + u32 context; + u8 is_ip4; + u8 ip_address[16]; + u32 sw_if_index; + u8 is_add; +}; + +/** \brief Dump STN rules + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define stn_rules_dump { + u32 client_index; + u32 context; +}; + +/** \brief STN response to rules request + @param context - sender context, to match reply w/ request + @param is_ip4 - 1 if address type is IPv4 + @param ip_address - IP address + @param sw_if_index - Interface index +*/ +define stn_rule_details { + u32 context; + u8 is_ip4; + u8 ip_address[16]; + u32 sw_if_index; +}; + diff --git a/src/plugins/stn/stn.c b/src/plugins/stn/stn.c new file mode 100644 index 00000000000..a9e6a98ebed --- /dev/null +++ b/src/plugins/stn/stn.c @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2017 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 <stn/stn.h> + +#include <vnet/plugin/plugin.h> +#include <vpp/app/version.h> +#include <vnet/ip/format.h> +#include <vnet/ethernet/packet.h> +#include <vnet/udp/udp.h> +#include <vnet/tcp/tcp.h> + +stn_main_t stn_main; +static vlib_node_registration_t stn_ip4_punt; +static vlib_node_registration_t stn_ip6_punt; + +static u8 stn_hw_addr_local[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; +static u8 stn_hw_addr_dst[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}; + +static ethernet_header_t stn_ip4_ethernet_header = {}; +static ethernet_header_t stn_ip6_ethernet_header = {}; + +typedef struct { + clib_bihash_kv_16_8_t kv; +} stn_ip46_punt_trace_t; + +static u8 * +format_stn_rule (u8 * s, va_list * args) +{ + stn_rule_t *r = va_arg (*args, stn_rule_t *); + stn_main_t *stn = &stn_main; + u32 indent = format_get_indent (s); + u32 node_index = ip46_address_is_ip4(&r->address)?stn_ip4_punt.index:stn_ip6_punt.index; + vlib_node_t *next_node = vlib_get_next_node(vlib_get_main(), node_index, r->next_node_index); + s = format (s, "rule_index: %d\n", r - stn->rules); + s = format (s, "%Uaddress: %U\n", format_white_space, indent, + format_ip46_address, &r->address, IP46_TYPE_ANY); + s = format (s, "%Uiface: %U (%d)\n", format_white_space, indent, + format_vnet_sw_if_index_name, vnet_get_main(), r->sw_if_index, + r->sw_if_index); + s = format (s, "%Unext_node: %s (%d)", format_white_space, indent, + next_node->name, next_node->index); + return s; +} + +static_always_inline u8 * +format_stn_ip46_punt_trace (u8 * s, va_list * args, u8 is_ipv4) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + stn_ip46_punt_trace_t *t = va_arg (*args, stn_ip46_punt_trace_t *); + u32 indent = format_get_indent (s); + + format (s, "dst_address: %U\n", format_ip46_address, + (ip46_address_t *)&t->kv.key, IP46_TYPE_ANY); + + if (t->kv.value == ~(0L)) + { + s = format (s, "%Urule: none", format_white_space, indent); + } + else + { + s = format (s, "%Urule:\n%U%U", format_white_space, indent, + format_white_space, indent + 2, + format_stn_rule, &stn_main.rules[t->kv.value]); + } + return s; +} + +static void +stn_punt_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + u32 n_left_from, *from, next_index, *to_next, n_left_to_next; + stn_main_t *stn = &stn_main; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* Single loop */ + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 pi0; + vlib_buffer_t *p0; + u32 next0; + + pi0 = to_next[0] = from[0]; + from += 1; + n_left_from -= 1; + to_next += 1; + n_left_to_next -= 1; + + p0 = vlib_get_buffer (vm, pi0); + + ip4_header_t *ip = vlib_buffer_get_current(p0); + if ((ip->ip_version_and_header_length & 0xf0) == 0x40) + next0 = stn->punt_to_stn_ip4_next_index; + else + next0 = stn->punt_to_stn_ip6_next_index; + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, pi0, next0); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } +} + +typedef enum +{ + STN_IP_PUNT_DROP, + STN_IP_PUNT_N_NEXT, +} stn_ip_punt_next_t; + +static_always_inline uword +stn_ip46_punt_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame, + u8 is_ipv4) +{ + u32 n_left_from, *from, next_index, *to_next, n_left_to_next; + stn_main_t *stn = &stn_main; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* Single loop */ + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 pi0; + vlib_buffer_t *p0; + clib_bihash_kv_16_8_t kv; + u32 next0 = STN_IP_PUNT_DROP; + + pi0 = to_next[0] = from[0]; + from += 1; + n_left_from -= 1; + to_next += 1; + n_left_to_next -= 1; + + p0 = vlib_get_buffer (vm, pi0); + + if (is_ipv4) + { + ip4_header_t *hdr = (ip4_header_t *) vlib_buffer_get_current(p0); + ip46_address_set_ip4((ip46_address_t *)kv.key, &hdr->dst_address); + } + else + { + ip6_header_t *hdr = (ip6_header_t *) vlib_buffer_get_current(p0); + kv.key[0] = hdr->dst_address.as_u64[0]; + kv.key[1] = hdr->dst_address.as_u64[1]; + } + + kv.value = ~(0L); + clib_bihash_search_inline_16_8 (&stn->rule_by_address_table, &kv); + if (kv.value != ~(0L)) + { + ethernet_header_t *eth; + stn_rule_t *r = &stn->rules[kv.value]; + vnet_buffer(p0)->sw_if_index[VLIB_TX] = r->sw_if_index; + next0 = r->next_node_index; + vlib_buffer_advance(p0, -sizeof(*eth)); + eth = (ethernet_header_t *) vlib_buffer_get_current(p0); + if (is_ipv4) + clib_memcpy(eth, &stn_ip4_ethernet_header, sizeof(*eth)); + else + clib_memcpy(eth, &stn_ip6_ethernet_header, sizeof(*eth)); + } + + if (PREDICT_FALSE (p0->flags & VLIB_BUFFER_IS_TRACED)) + { + stn_ip46_punt_trace_t *tr = + vlib_add_trace (vm, node, p0, sizeof (*tr)); + tr->kv = kv; + } + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, pi0, next0); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + + +#define foreach_stn_ip_punt_error \ + _(NONE, "no error") + +typedef enum { +#define _(sym,str) STN_IP_punt_ERROR_##sym, + foreach_stn_ip_punt_error +#undef _ + STN_IP_PUNT_N_ERROR, +} ila_error_t; + +static char *stn_ip_punt_error_strings[] = { +#define _(sym,string) string, + foreach_stn_ip_punt_error +#undef _ +}; + +u8 * +format_stn_ip6_punt_trace (u8 * s, va_list * args) +{ + return format_stn_ip46_punt_trace (s, args, 0); +} + +static uword +stn_ip6_punt_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return stn_ip46_punt_fn(vm, node, frame, 0); +} + +/** *INDENT-OFF* */ +VLIB_REGISTER_NODE (stn_ip6_punt, static) = +{ + .function = stn_ip6_punt_fn, + .name = "stn-ip6-punt", + .vector_size = sizeof (u32), + .format_trace = format_stn_ip6_punt_trace, + .n_errors = STN_IP_PUNT_N_ERROR, + .error_strings = stn_ip_punt_error_strings, + .n_next_nodes = STN_IP_PUNT_N_NEXT, + .next_nodes = + { + [STN_IP_PUNT_DROP] = "error-drop" + }, +}; +/** *INDENT-ON* */ + +u8 * +format_stn_ip4_punt_trace (u8 * s, va_list * args) +{ + return format_stn_ip46_punt_trace (s, args, 1); +} + +static uword +stn_ip4_punt_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return stn_ip46_punt_fn(vm, node, frame, 1); +} + +/** *INDENT-OFF* */ +VLIB_REGISTER_NODE (stn_ip4_punt, static) = +{ + .function = stn_ip4_punt_fn, + .name = "stn-ip4-punt", + .vector_size = sizeof (u32), + .format_trace = format_stn_ip4_punt_trace, + .n_errors = STN_IP_PUNT_N_ERROR, + .error_strings = stn_ip_punt_error_strings, + .n_next_nodes = STN_IP_PUNT_N_NEXT, + .next_nodes = + { + [STN_IP_PUNT_DROP] = "error-drop", + }, +}; +/** *INDENT-ON* */ + +clib_error_t * +stn_init (vlib_main_t * vm) +{ + stn_main_t *stn = &stn_main; + stn->rules = 0; + clib_bihash_init_16_8(&stn->rule_by_address_table, "stn addresses", + 1024, 1<<20); + + clib_memcpy(stn_ip4_ethernet_header.dst_address, stn_hw_addr_dst, 6); + clib_memcpy(stn_ip4_ethernet_header.src_address, stn_hw_addr_local, 6); + stn_ip4_ethernet_header.type = clib_host_to_net_u16(ETHERNET_TYPE_IP4); + + clib_memcpy(stn_ip6_ethernet_header.dst_address, stn_hw_addr_dst, 6); + clib_memcpy(stn_ip6_ethernet_header.src_address, stn_hw_addr_local, 6); + stn_ip6_ethernet_header.type = clib_host_to_net_u16(ETHERNET_TYPE_IP6); + + u32 punt_node_index = vlib_get_node_by_name(vm, (u8 *)"error-punt")->index; + stn->punt_to_stn_ip4_next_index = + vlib_node_add_next(vm, punt_node_index, stn_ip4_punt.index); + stn->punt_to_stn_ip6_next_index = + vlib_node_add_next(vm, punt_node_index, stn_ip6_punt.index); + + return stn_api_init (vm, stn); + + return NULL; +} + +VLIB_INIT_FUNCTION (stn_init); + +/* *INDENT-OFF* */ +VLIB_PLUGIN_REGISTER () = { + .version = VPP_BUILD_VER, + .description = "VPP Steals the NIC for Container integration", +}; +/* *INDENT-ON* */ + +int stn_rule_add_del (stn_rule_add_del_args_t *args) +{ + vnet_main_t *vnm = vnet_get_main(); + vlib_main_t *vm = vlib_get_main(); + stn_main_t *stn = &stn_main; + + stn_rule_t *r = NULL; + clib_bihash_kv_16_8_t kv; + kv.key[0] = args->address.as_u64[0]; + kv.key[1] = args->address.as_u64[1]; + + if (clib_bihash_search_inline_16_8 (&stn->rule_by_address_table, &kv) == 0) + { + r = &stn->rules[kv.value]; + } + else if (!args->del) + { + pool_get(stn->rules, r); + kv.value = r - stn->rules; + clib_bihash_add_del_16_8(&stn->rule_by_address_table, &kv, 1); + r->address = args->address; + + stn->n_rules++; + if (stn->n_rules == 1) + { + foreach_vlib_main({ + this_vlib_main->os_punt_frame = stn_punt_fn; + }); + udp_punt_unknown(vm, 0, 1); + udp_punt_unknown(vm, 1, 1); + tcp_punt_unknown(vm, 0, 1); + tcp_punt_unknown(vm, 1, 1); + } + } + + if (!args->del) + { + /* Getting output node and adding it as next */ + u32 output_node_index = + vnet_tx_node_index_for_sw_interface(vnm, args->sw_if_index); + u32 node_index = ip46_address_is_ip4(&args->address)? + stn_ip4_punt.index : stn_ip6_punt.index; + + r->sw_if_index = args->sw_if_index; + r->next_node_index = + vlib_node_add_next(vm, node_index, output_node_index); + + /* enabling forwarding on the output node (might not be done since + * it is unnumbered) */ + vnet_feature_enable_disable("ip4-unicast", "ip4-lookup", args->sw_if_index, + 1, 0, 0); + vnet_feature_enable_disable("ip6-unicast", "ip6-lookup", args->sw_if_index, + 1, 0, 0); + vnet_feature_enable_disable("ip4-unicast", "ip4-drop", args->sw_if_index, + 0, 0, 0); + vnet_feature_enable_disable("ip6-unicast", "ip6-drop", args->sw_if_index, + 0, 0, 0); + } + else if (r) + { + clib_bihash_add_del_16_8(&stn->rule_by_address_table, &kv, 0); + pool_put(stn->rules, r); + + stn->n_rules--; + if (stn->n_rules == 0) + { + foreach_vlib_main({ + this_vlib_main->os_punt_frame = NULL; + }); + } + } + else + { + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + + return 0; +} + +static clib_error_t * +show_stn_rules_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + stn_main_t *stn = &stn_main; + u8 *s = 0; + stn_rule_t *rule; + pool_foreach(rule, stn->rules, { + s = format (s, "- %U\n", format_stn_rule, rule); + }); + + vlib_cli_output(vm, "%v", s); + + vec_free(s); + return NULL; +} + +VLIB_CLI_COMMAND (show_stn_rules_command, static) = +{ + .path = "show stn rules", + .short_help = "", + .function = show_stn_rules_fn, +}; + +static clib_error_t * +stn_rule_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; + stn_rule_add_del_args_t args = {}; + u8 got_addr = 0; + u8 got_iface = 0; + int ret; + + 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, "address %U", unformat_ip46_address, + &args.address, IP46_TYPE_ANY)) + got_addr = 1; + else if (unformat + (line_input, "interface %U", unformat_vnet_sw_interface, + vnet_get_main(), &args.sw_if_index)) + got_iface = 1; + else if (unformat (line_input, "del")) + args.del = 1; + else + { + error = clib_error_return (0, "parse error: '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (!got_addr) + { + error = clib_error_return (0, "Missing address"); + goto done; + } + + if (!got_iface) + { + error = clib_error_return (0, "Missing interface"); + goto done; + } + + if ((ret = stn_rule_add_del (&args))) + { + error = clib_error_return (0, "stn_rule_add_del returned error %d", ret); + goto done; + } + +done: + unformat_free (line_input); + return error; +} + +VLIB_CLI_COMMAND (stn_rule_command, static) = +{ + .path = "stn rule", + .short_help = "address <addr> interface <iface> [del]", + .function = stn_rule_fn, +}; diff --git a/src/plugins/stn/stn.h b/src/plugins/stn/stn.h new file mode 100644 index 00000000000..a6f0a0a5998 --- /dev/null +++ b/src/plugins/stn/stn.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017 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 SRC_PLUGINS_STN_STN_H_ +#define SRC_PLUGINS_STN_STN_H_ + +#include <vlib/vlib.h> +#include <vnet/ip/ip4.h> +#include <vnet/ip/ip6.h> +#include <vppinfra/bihash_16_8.h> + +typedef struct { + ip46_address_t address; + u32 next_node_index; + u32 sw_if_index; +} stn_rule_t; + +typedef struct { + /* pool of stn rules */ + stn_rule_t *rules; + + /* number of rules */ + u32 n_rules; + + /* hash table used to retrieve the rule from the ip address */ + clib_bihash_16_8_t rule_by_address_table; + + u32 punt_to_stn_ip4_next_index; + u32 punt_to_stn_ip6_next_index; + + u16 msg_id_base; +} stn_main_t; + +typedef struct { + /** Destination address of intercepted packets */ + ip46_address_t address; + /** TX interface to send packets to */ + u32 sw_if_index; + /** Whether to delete the rule */ + u8 del; +} stn_rule_add_del_args_t; + +/** + * Add or delete an stn rule. + */ +int stn_rule_add_del (stn_rule_add_del_args_t *args); + +extern stn_main_t stn_main; + +clib_error_t * +stn_api_init (vlib_main_t * vm, stn_main_t * sm); + +#endif /* SRC_PLUGINS_STN_STN_H_ */ diff --git a/src/plugins/stn/stn_api.c b/src/plugins/stn/stn_api.c new file mode 100644 index 00000000000..02ea29b6443 --- /dev/null +++ b/src/plugins/stn/stn_api.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2017 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 <vlibapi/api.h> +#include <vlibmemory/api.h> + +#include <plugins/stn/stn.h> +#include <vnet/ip/format.h> + +#include <vppinfra/byte_order.h> + +/* define message IDs */ +#define vl_msg_id(n,h) n, +typedef enum +{ +#include <stn/stn.api.h> + /* We'll want to know how many messages IDs we need... */ + VL_MSG_FIRST_AVAILABLE, +} vl_msg_id_t; +#undef vl_msg_id + +/* define message structures */ +#define vl_typedefs +#include <stn/stn.api.h> +#undef vl_typedefs + +/* define generated endian-swappers */ +#define vl_endianfun +#include <stn/stn.api.h> +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) +#define vl_printfun +#include <stn/stn.api.h> +#undef vl_printfun + +/* Get the API version number */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include <nat/nat_all_api_h.h> +#undef vl_api_version + +#define REPLY_MSG_ID_BASE stn_main.msg_id_base +#include <vlibapi/api_helper_macros.h> + +/* Macro to finish up custom dump fns */ +#define FINISH \ + vec_add1 (s, 0); \ + vl_print (handle, (char *)s); \ + vec_free (s); \ + return handle; + +/** + * @brief API message custom-dump function + * @param mp vl_api_stn_add_del_rule_t * mp the api message + * @param handle void * print function handle + * @returns u8 * output string + */ +static void *vl_api_stn_add_del_rule_t_print + (vl_api_stn_add_del_rule_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: stn_add_del_rule "); + if (mp->is_ip4) + s = format (s, "address %U ", format_ip4_address, mp->ip_address); + else + s = format (s, "address %U ", format_ip6_address, mp->ip_address); + s = format (s, "sw_if_index %d is_add %d", mp->sw_if_index, mp->is_add); + + FINISH; +} + +static void +vl_api_stn_add_del_rule_t_handler (vl_api_stn_add_del_rule_t * mp) +{ + stn_rule_add_del_args_t args; + vl_api_stn_add_del_rule_reply_t *rmp; + int rv = 0; + + if (mp->is_ip4) + { + ip4_address_t a; + memcpy(&a, mp->ip_address, sizeof(a)); + ip46_address_set_ip4(&args.address, &a); + } + else + memcpy(&args.address.ip6, mp->ip_address, sizeof(ip6_address_t)); + + args.sw_if_index = clib_net_to_host_u32(mp->sw_if_index); + args.del = !mp->is_add; + + rv = stn_rule_add_del(&args); + + REPLY_MACRO (VL_API_STN_ADD_DEL_RULE_REPLY); +} + +static void +send_stn_rule (stn_rule_t *r, unix_shared_memory_queue_t *q, u32 context) +{ + vl_api_stn_rule_details_t *rmp; + + rmp = + vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + clib_host_to_net_u32 (VL_API_STN_RULES_DUMP + stn_main.msg_id_base); + + if (ip46_address_is_ip4(&r->address)) + { + clib_memcpy(rmp->ip_address, &r->address.ip4, sizeof(ip4_address_t)); + rmp->is_ip4 = 1; + } + else + { + clib_memcpy(rmp->ip_address, &r->address.ip6, sizeof(ip6_address_t)); + } + + rmp->context = context; + rmp->sw_if_index = clib_host_to_net_u32(r->sw_if_index); + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +vl_api_stn_rules_dump_t_handler (vl_api_stn_rules_dump_t *mp) +{ + unix_shared_memory_queue_t *q; + stn_main_t *stn = &stn_main; + stn_rule_t *r; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + pool_foreach (r, stn->rules, + ({ + + send_stn_rule (r, q, mp->context); + })); + /* *INDENT-ON* */ +} + + +/* List of message types that this plugin understands */ +#define foreach_stn_plugin_api_msg \ +_(STN_ADD_DEL_RULE, stn_add_del_rule) \ +_(STN_RULES_DUMP, stn_rules_dump) + +/** + * @brief Set up the API message handling tables + * @param vm vlib_main_t * vlib main data structure pointer + * @returns 0 to indicate all is well + */ +static clib_error_t * +stn_plugin_api_hookup (vlib_main_t * vm) +{ + stn_main_t *stn = &stn_main; +#define _(N,n) \ + vl_msg_api_set_handlers((VL_API_##N + stn->msg_id_base), \ + #n, \ + vl_api_##n##_t_handler, \ + vl_noop_handler, \ + vl_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_stn_plugin_api_msg; +#undef _ + + return 0; +} + +#define vl_msg_name_crc_list +#include <stn/stn.api.h> +#undef vl_msg_name_crc_list + +static void +setup_message_id_table (stn_main_t * stn, api_main_t * am) +{ +#define _(id,n,crc) \ + vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + stn->msg_id_base); + foreach_vl_msg_name_crc_stn; +#undef _ +} + +static void +plugin_custom_dump_configure (stn_main_t * stn) +{ +#define _(n,f) api_main.msg_print_handlers \ + [VL_API_##n + stn->msg_id_base] \ + = (void *) vl_api_##f##_t_print; + foreach_stn_plugin_api_msg; +#undef _ +} + +clib_error_t * +stn_api_init (vlib_main_t * vm, stn_main_t * sm) +{ + u8 *name; + clib_error_t *error = 0; + + name = format (0, "stn_%08x%c", api_version, 0); + + /* Ask for a correctly-sized block of API message decode slots */ + sm->msg_id_base = + vl_msg_api_get_msg_ids ((char *) name, VL_MSG_FIRST_AVAILABLE); + + error = stn_plugin_api_hookup (vm); + + /* Add our API messages to the global name_crc hash table */ + setup_message_id_table (sm, &api_main); + + plugin_custom_dump_configure (sm); + + vec_free (name); + + return error; +} |