diff options
author | Damjan Marion <damarion@cisco.com> | 2016-12-19 23:05:39 +0100 |
---|---|---|
committer | Damjan Marion <damarion@cisco.com> | 2016-12-28 12:25:14 +0100 |
commit | 7cd468a3d7dee7d6c92f69a0bb7061ae208ec727 (patch) | |
tree | 5de62f8dbd3a752f5a676ca600e43d2652d1ff1a /src/vnet/ipsec-gre | |
parent | 696f1adec0df3b8f161862566dd9c86174302658 (diff) |
Reorganize source tree to use single autotools instance
Change-Id: I7b51f88292e057c6443b12224486f2d0c9f8ae23
Signed-off-by: Damjan Marion <damarion@cisco.com>
Diffstat (limited to 'src/vnet/ipsec-gre')
-rw-r--r-- | src/vnet/ipsec-gre/dir.dox | 18 | ||||
-rw-r--r-- | src/vnet/ipsec-gre/error.def | 26 | ||||
-rw-r--r-- | src/vnet/ipsec-gre/interface.c | 311 | ||||
-rw-r--r-- | src/vnet/ipsec-gre/ipsec_gre.api | 79 | ||||
-rw-r--r-- | src/vnet/ipsec-gre/ipsec_gre.c | 407 | ||||
-rw-r--r-- | src/vnet/ipsec-gre/ipsec_gre.h | 114 | ||||
-rw-r--r-- | src/vnet/ipsec-gre/ipsec_gre_api.c | 190 | ||||
-rw-r--r-- | src/vnet/ipsec-gre/ipsec_gre_doc.md | 74 | ||||
-rw-r--r-- | src/vnet/ipsec-gre/node.c | 433 |
9 files changed, 1652 insertions, 0 deletions
diff --git a/src/vnet/ipsec-gre/dir.dox b/src/vnet/ipsec-gre/dir.dox new file mode 100644 index 00000000000..e6ffd10b01b --- /dev/null +++ b/src/vnet/ipsec-gre/dir.dox @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2016 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. + */ +/** + @dir vnet/vnet/ipsec-gre + @brief L2-GRE over IPSec tunnel interface implementation +*/ diff --git a/src/vnet/ipsec-gre/error.def b/src/vnet/ipsec-gre/error.def new file mode 100644 index 00000000000..d84e8ed1759 --- /dev/null +++ b/src/vnet/ipsec-gre/error.def @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2016 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 + * @brief L2-GRE over IPSec errors. + */ + + +ipsec_gre_error (NONE, "no error") +ipsec_gre_error (UNKNOWN_PROTOCOL, "unknown protocol") +ipsec_gre_error (UNSUPPORTED_VERSION, "unsupported version") +ipsec_gre_error (PKTS_DECAP, "GRE input packets decapsulated") +ipsec_gre_error (PKTS_ENCAP, "GRE output packets encapsulated") +ipsec_gre_error (NO_SUCH_TUNNEL, "GRE input packets dropped due to missing tunnel") diff --git a/src/vnet/ipsec-gre/interface.c b/src/vnet/ipsec-gre/interface.c new file mode 100644 index 00000000000..56832ee1006 --- /dev/null +++ b/src/vnet/ipsec-gre/interface.c @@ -0,0 +1,311 @@ +/* + * gre_interface.c: gre interfaces + * + * Copyright (c) 2016 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 + * @brief L2-GRE over IPSec tunnel interface. + * + * Creates ipsec-gre tunnel interface. + * Provides a command line interface so humans can interact with VPP. + */ + +#include <vnet/vnet.h> +#include <vnet/pg/pg.h> +#include <vnet/ipsec-gre/ipsec_gre.h> +#include <vnet/ip/format.h> +#include <vnet/ipsec/ipsec.h> + +#if DPDK_CRYPTO==1 +#include <vnet/devices/dpdk/ipsec/esp.h> +#define ESP_NODE "dpdk-esp-encrypt" +#else +#include <vnet/ipsec/esp.h> +#define ESP_NODE "esp-encrypt" +#endif + +u8 * +format_ipsec_gre_tunnel (u8 * s, va_list * args) +{ + ipsec_gre_tunnel_t *t = va_arg (*args, ipsec_gre_tunnel_t *); + ipsec_gre_main_t *gm = &ipsec_gre_main; + + s = format (s, + "[%d] %U (src) %U (dst) local-sa %d remote-sa %d", + t - gm->tunnels, + format_ip4_address, &t->tunnel_src, + format_ip4_address, &t->tunnel_dst, + t->local_sa_id, t->remote_sa_id); + return s; +} + +static clib_error_t * +show_ipsec_gre_tunnel_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + ipsec_gre_main_t *igm = &ipsec_gre_main; + ipsec_gre_tunnel_t *t; + + if (pool_elts (igm->tunnels) == 0) + vlib_cli_output (vm, "No IPSec GRE tunnels configured..."); + + /* *INDENT-OFF* */ + pool_foreach (t, igm->tunnels, + ({ + vlib_cli_output (vm, "%U", format_ipsec_gre_tunnel, t); + })); + /* *INDENT-ON* */ + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_ipsec_gre_tunnel_command, static) = { + .path = "show ipsec gre tunnel", + .function = show_ipsec_gre_tunnel_command_fn, +}; +/* *INDENT-ON* */ + +/* force inclusion from application's main.c */ +clib_error_t * +ipsec_gre_interface_init (vlib_main_t * vm) +{ + return 0; +} + +VLIB_INIT_FUNCTION (ipsec_gre_interface_init); + +/** + * @brief Add or delete ipsec-gre tunnel interface. + * + * @param *a vnet_ipsec_gre_add_del_tunnel_args_t - tunnel interface parameters + * @param *sw_if_indexp u32 - software interface index + * @return int - 0 if success otherwise <code>VNET_API_ERROR_</code> + */ +int +vnet_ipsec_gre_add_del_tunnel (vnet_ipsec_gre_add_del_tunnel_args_t * a, + u32 * sw_if_indexp) +{ + ipsec_gre_main_t *igm = &ipsec_gre_main; + vnet_main_t *vnm = igm->vnet_main; + ip4_main_t *im = &ip4_main; + ipsec_gre_tunnel_t *t; + vnet_hw_interface_t *hi; + u32 hw_if_index, sw_if_index; + u32 slot; + uword *p; + u64 key; + ipsec_add_del_ipsec_gre_tunnel_args_t args; + + memset (&args, 0, sizeof (args)); + args.is_add = a->is_add; + args.local_sa_id = a->lsa; + args.remote_sa_id = a->rsa; + args.local_ip.as_u32 = a->src.as_u32; + args.remote_ip.as_u32 = a->dst.as_u32; + + key = (u64) a->src.as_u32 << 32 | (u64) a->dst.as_u32; + p = hash_get (igm->tunnel_by_key, key); + + if (a->is_add) + { + /* check if same src/dst pair exists */ + if (p) + return VNET_API_ERROR_INVALID_VALUE; + + pool_get_aligned (igm->tunnels, t, CLIB_CACHE_LINE_BYTES); + memset (t, 0, sizeof (*t)); + + if (vec_len (igm->free_ipsec_gre_tunnel_hw_if_indices) > 0) + { + vnet_interface_main_t *im = &vnm->interface_main; + + hw_if_index = igm->free_ipsec_gre_tunnel_hw_if_indices + [vec_len (igm->free_ipsec_gre_tunnel_hw_if_indices) - 1]; + _vec_len (igm->free_ipsec_gre_tunnel_hw_if_indices) -= 1; + + hi = vnet_get_hw_interface (vnm, hw_if_index); + hi->dev_instance = t - igm->tunnels; + hi->hw_instance = hi->dev_instance; + + /* clear old stats of freed tunnel before reuse */ + sw_if_index = hi->sw_if_index; + vnet_interface_counter_lock (im); + vlib_zero_combined_counter + (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_TX], + sw_if_index); + vlib_zero_combined_counter + (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_RX], + sw_if_index); + vlib_zero_simple_counter + (&im->sw_if_counters[VNET_INTERFACE_COUNTER_DROP], sw_if_index); + vnet_interface_counter_unlock (im); + } + else + { + hw_if_index = vnet_register_interface + (vnm, ipsec_gre_device_class.index, t - igm->tunnels, + ipsec_gre_hw_interface_class.index, t - igm->tunnels); + hi = vnet_get_hw_interface (vnm, hw_if_index); + sw_if_index = hi->sw_if_index; + } + + t->hw_if_index = hw_if_index; + t->sw_if_index = sw_if_index; + t->local_sa_id = a->lsa; + t->remote_sa_id = a->rsa; + t->local_sa = ipsec_get_sa_index_by_sa_id (a->lsa); + t->remote_sa = ipsec_get_sa_index_by_sa_id (a->rsa); + + ip4_sw_interface_enable_disable (sw_if_index, 1); + + vec_validate_init_empty (igm->tunnel_index_by_sw_if_index, + sw_if_index, ~0); + igm->tunnel_index_by_sw_if_index[sw_if_index] = t - igm->tunnels; + + vec_validate (im->fib_index_by_sw_if_index, sw_if_index); + + hi->min_packet_bytes = 64 + sizeof (gre_header_t) + + sizeof (ip4_header_t) + sizeof (esp_header_t) + sizeof (esp_footer_t); + hi->per_packet_overhead_bytes = + /* preamble */ 8 + /* inter frame gap */ 12; + + /* Standard default gre MTU. */ + hi->max_l3_packet_bytes[VLIB_RX] = hi->max_l3_packet_bytes[VLIB_TX] = + 9000; + + clib_memcpy (&t->tunnel_src, &a->src, sizeof (t->tunnel_src)); + clib_memcpy (&t->tunnel_dst, &a->dst, sizeof (t->tunnel_dst)); + + hash_set (igm->tunnel_by_key, key, t - igm->tunnels); + + slot = vlib_node_add_named_next_with_slot + (vnm->vlib_main, hi->tx_node_index, ESP_NODE, + IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT); + + ASSERT (slot == IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT); + + } + else + { /* !is_add => delete */ + /* tunnel needs to exist */ + if (!p) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + t = pool_elt_at_index (igm->tunnels, p[0]); + + sw_if_index = t->sw_if_index; + ip4_sw_interface_enable_disable (sw_if_index, 0); + vnet_sw_interface_set_flags (vnm, sw_if_index, 0 /* down */ ); + /* make sure tunnel is removed from l2 bd or xconnect */ + set_int_l2_mode (igm->vlib_main, vnm, MODE_L3, sw_if_index, 0, 0, 0, 0); + vec_add1 (igm->free_ipsec_gre_tunnel_hw_if_indices, t->hw_if_index); + igm->tunnel_index_by_sw_if_index[sw_if_index] = ~0; + + hash_unset (igm->tunnel_by_key, key); + pool_put (igm->tunnels, t); + } + + if (sw_if_indexp) + *sw_if_indexp = sw_if_index; + + return ipsec_add_del_ipsec_gre_tunnel (vnm, &args); +} + +static clib_error_t * +create_ipsec_gre_tunnel_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + u8 is_add = 1; + u32 num_m_args = 0; + ip4_address_t src, dst; + u32 lsa = 0, rsa = 0; + vnet_ipsec_gre_add_del_tunnel_args_t _a, *a = &_a; + int rv; + u32 sw_if_index; + + /* 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, "del")) + is_add = 0; + else if (unformat (line_input, "src %U", unformat_ip4_address, &src)) + num_m_args++; + else if (unformat (line_input, "dst %U", unformat_ip4_address, &dst)) + num_m_args++; + else if (unformat (line_input, "local-sa %d", &lsa)) + num_m_args++; + else if (unformat (line_input, "remote-sa %d", &rsa)) + num_m_args++; + else + return clib_error_return (0, "unknown input `%U'", + format_unformat_error, input); + } + unformat_free (line_input); + + if (num_m_args < 4) + return clib_error_return (0, "mandatory argument(s) missing"); + + if (memcmp (&src, &dst, sizeof (src)) == 0) + return clib_error_return (0, "src and dst are identical"); + + memset (a, 0, sizeof (*a)); + a->is_add = is_add; + a->lsa = lsa; + a->rsa = rsa; + clib_memcpy (&a->src, &src, sizeof (src)); + clib_memcpy (&a->dst, &dst, sizeof (dst)); + + rv = vnet_ipsec_gre_add_del_tunnel (a, &sw_if_index); + + switch (rv) + { + case 0: + vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name, + vnet_get_main (), sw_if_index); + break; + case VNET_API_ERROR_INVALID_VALUE: + return clib_error_return (0, "GRE tunnel already exists..."); + default: + return clib_error_return (0, + "vnet_ipsec_gre_add_del_tunnel returned %d", + rv); + } + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (create_ipsec_gre_tunnel_command, static) = { + .path = "create ipsec gre tunnel", + .short_help = "create ipsec gre tunnel src <addr> dst <addr> " + "local-sa <id> remote-sa <id> [del]", + .function = create_ipsec_gre_tunnel_command_fn, +}; +/* *INDENT-ON* */ + +/* +* fd.io coding-style-patch-verification: ON +* +* Local Variables: +* eval: (c-set-style "gnu") +* End: +*/ diff --git a/src/vnet/ipsec-gre/ipsec_gre.api b/src/vnet/ipsec-gre/ipsec_gre.api new file mode 100644 index 00000000000..793bca0afcd --- /dev/null +++ b/src/vnet/ipsec-gre/ipsec_gre.api @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2015-2016 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. + */ + +/** \brief Add / del ipsec gre tunnel request + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param local_sa_id - local SA id + @param remote_sa_id - remote SA id + @param is_add - 1 if adding the tunnel, 0 if deleting + @param src_address - tunnel source address + @param dst_address - tunnel destination address +*/ +define ipsec_gre_add_del_tunnel { + u32 client_index; + u32 context; + u32 local_sa_id; + u32 remote_sa_id; + u8 is_add; + u8 src_address[4]; + u8 dst_address[4]; +}; + +/** \brief Reply for add / del ipsec gre tunnel request + @param context - returned sender context, to match reply w/ request + @param retval - return code + @param sw_if_index - software index of the new ipsec gre tunnel +*/ +define ipsec_gre_add_del_tunnel_reply { + u32 context; + i32 retval; + u32 sw_if_index; +}; + +/** \brief Dump ipsec gre tunnel table + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param tunnel_index - gre tunnel identifier or -1 in case of all tunnels +*/ +define ipsec_gre_tunnel_dump { + u32 client_index; + u32 context; + u32 sw_if_index; +}; + +/** \brief ipsec gre tunnel operational state response + @param context - returned sender context, to match reply w/ request + @param sw_if_index - software index of the ipsec gre tunnel + @param local_sa_id - local SA id + @param remote_sa_id - remote SA id + @param src_address - tunnel source address + @param dst_address - tunnel destination address +*/ +define ipsec_gre_tunnel_details { + u32 context; + u32 sw_if_index; + u32 local_sa_id; + u32 remote_sa_id; + u8 src_address[4]; + u8 dst_address[4]; +}; + +/* + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ +
\ No newline at end of file diff --git a/src/vnet/ipsec-gre/ipsec_gre.c b/src/vnet/ipsec-gre/ipsec_gre.c new file mode 100644 index 00000000000..a0b065ac283 --- /dev/null +++ b/src/vnet/ipsec-gre/ipsec_gre.c @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2016 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 + * @brief L2-GRE over IPSec packet processing. + * + * Add GRE header to thr packet and send it to the esp-encrypt node. +*/ + +#include <vnet/vnet.h> +#include <vnet/ipsec-gre/ipsec_gre.h> + +ipsec_gre_main_t ipsec_gre_main; + +/** + * @brief IPv4 and GRE header union. + * +*/ +typedef struct +{ + union + { + ip4_and_gre_header_t ip4_and_gre; + u64 as_u64[3]; + }; +} ip4_and_gre_union_t; + +/** + * @brief Packet trace. + * +*/ +typedef struct +{ + u32 tunnel_id; /**< Tunnel-id / index in tunnel vector */ + + u32 length; /**< pkt length */ + + ip4_address_t src; /**< tunnel src IPv4 address */ + ip4_address_t dst; /**< tunnel dst IPv4 address */ + + u32 sa_id; /**< tunnel IPSec SA id */ +} ipsec_gre_tx_trace_t; + +u8 * +format_ipsec_gre_tx_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 *); + ipsec_gre_tx_trace_t *t = va_arg (*args, ipsec_gre_tx_trace_t *); + + s = format (s, "GRE: tunnel %d len %d src %U dst %U sa-id %d", + t->tunnel_id, clib_net_to_host_u16 (t->length), + format_ip4_address, &t->src.as_u8, + format_ip4_address, &t->dst.as_u8, t->sa_id); + return s; +} + +/** + * @brief IPSec-GRE tunnel interface tx function. + * + * Add GRE header to the packet. + * + * @param vm vlib_main_t corresponding to the current thread. + * @param node vlib_node_runtime_t data for this node. + * @param frame vlib_frame_t whose contents should be dispatched. + * + * @par Graph mechanics: buffer metadata, next index usage + * + * <em>Uses:</em> + * - <code>node->runtime_data</code> + * - Match tunnel by <code>rd->dev_instance</code> in IPSec-GRE tunnels + * pool. + * + * <em>Sets:</em> + * - <code>vnet_buffer(b)->output_features.ipsec_sad_index</code> + * - Set IPSec Security Association for packet encryption. + * - <code>vnet_buffer(b)->sw_if_index[VLIB_TX]</code> + * - Reset output sw_if_index. + * + * <em>Nexd Index:</em> + * - Dispatches the packet to the esp-encrypt node. +*/ +static uword +ipsec_gre_interface_tx (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + ipsec_gre_main_t *igm = &ipsec_gre_main; + u32 next_index; + u32 *from, *to_next, n_left_from, n_left_to_next; + vnet_interface_output_runtime_t *rd = (void *) node->runtime_data; + ipsec_gre_tunnel_t *t = pool_elt_at_index (igm->tunnels, rd->dev_instance); + + /* Vector of buffer / pkt indices we're supposed to process */ + from = vlib_frame_vector_args (frame); + + /* Number of buffers / pkts */ + n_left_from = frame->n_vectors; + + /* Speculatively send the first buffer to the last disposition we used */ + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + /* set up to enqueue to our disposition with index = next_index */ + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* + * As long as we have enough pkts left to process two pkts + * and prefetch two pkts... + */ + while (n_left_from >= 4 && n_left_to_next >= 2) + { + vlib_buffer_t *b0, *b1; + ip4_header_t *ip0, *ip1; + ip4_and_gre_union_t *h0, *h1; + u32 bi0, next0, bi1, next1; + __attribute__ ((unused)) u8 error0, error1; + u16 gre_protocol0, gre_protocol1; + + /* Prefetch the next iteration */ + { + vlib_buffer_t *p2, *p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + /* + * Prefetch packet data. We expect to overwrite + * the inbound L2 header with an ip header and a + * gre header. Might want to prefetch the last line + * of rewrite space as well; need profile data + */ + CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE); + } + + /* Pick up the next two buffer indices */ + bi0 = from[0]; + bi1 = from[1]; + + /* Speculatively enqueue them where we sent the last buffer */ + 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); + + ip0 = vlib_buffer_get_current (b0); + gre_protocol0 = clib_net_to_host_u16 (0x01); + + ip1 = vlib_buffer_get_current (b1); + gre_protocol1 = clib_net_to_host_u16 (0x01); + + vlib_buffer_advance (b0, -sizeof (*h0)); + vlib_buffer_advance (b1, -sizeof (*h1)); + + h0 = vlib_buffer_get_current (b0); + h1 = vlib_buffer_get_current (b1); + h0->as_u64[0] = 0; + h0->as_u64[1] = 0; + h0->as_u64[2] = 0; + + h1->as_u64[0] = 0; + h1->as_u64[1] = 0; + h1->as_u64[2] = 0; + + ip0 = &h0->ip4_and_gre.ip4; + h0->ip4_and_gre.gre.protocol = gre_protocol0; + ip0->ip_version_and_header_length = 0x45; + ip0->ttl = 254; + ip0->protocol = IP_PROTOCOL_GRE; + + ip1 = &h1->ip4_and_gre.ip4; + h1->ip4_and_gre.gre.protocol = gre_protocol1; + ip1->ip_version_and_header_length = 0x45; + ip1->ttl = 254; + ip1->protocol = IP_PROTOCOL_GRE; + + ip0->length = + clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); + ip1->length = + clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1)); + ip0->src_address.as_u32 = t->tunnel_src.as_u32; + ip1->src_address.as_u32 = t->tunnel_src.as_u32; + ip0->dst_address.as_u32 = t->tunnel_dst.as_u32; + ip1->dst_address.as_u32 = t->tunnel_dst.as_u32; + ip0->checksum = ip4_header_checksum (ip0); + ip1->checksum = ip4_header_checksum (ip1); + + vnet_buffer (b0)->sw_if_index[VLIB_RX] = + vnet_buffer (b0)->sw_if_index[VLIB_TX]; + vnet_buffer (b1)->sw_if_index[VLIB_RX] = + vnet_buffer (b1)->sw_if_index[VLIB_TX]; + + vnet_buffer (b0)->ipsec.sad_index = t->local_sa; + vnet_buffer (b1)->ipsec.sad_index = t->local_sa; + + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0; + + next0 = IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT; + next1 = IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT; + error0 = IPSEC_GRE_ERROR_NONE; + error1 = IPSEC_GRE_ERROR_NONE; + + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + ipsec_gre_tx_trace_t *tr = vlib_add_trace (vm, node, + b0, sizeof (*tr)); + tr->tunnel_id = t - igm->tunnels; + tr->length = ip0->length; + tr->src.as_u32 = ip0->src_address.as_u32; + tr->dst.as_u32 = ip0->dst_address.as_u32; + tr->sa_id = t->local_sa_id; + } + + if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED)) + { + ipsec_gre_tx_trace_t *tr = vlib_add_trace (vm, node, + b1, sizeof (*tr)); + tr->tunnel_id = t - igm->tunnels; + tr->length = ip1->length; + tr->src.as_u32 = ip1->src_address.as_u32; + tr->dst.as_u32 = ip1->dst_address.as_u32; + tr->sa_id = t->local_sa_id; + } + + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + vlib_buffer_t *b0; + ip4_header_t *ip0; + ip4_and_gre_union_t *h0; + u32 bi0, next0; + __attribute__ ((unused)) u8 error0; + u16 gre_protocol0; + + 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); + + gre_protocol0 = clib_net_to_host_u16 (0x01); + + vlib_buffer_advance (b0, -sizeof (*h0)); + + h0 = vlib_buffer_get_current (b0); + h0->as_u64[0] = 0; + h0->as_u64[1] = 0; + h0->as_u64[2] = 0; + + ip0 = &h0->ip4_and_gre.ip4; + h0->ip4_and_gre.gre.protocol = gre_protocol0; + ip0->ip_version_and_header_length = 0x45; + ip0->ttl = 254; + ip0->protocol = IP_PROTOCOL_GRE; + ip0->length = + clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); + ip0->src_address.as_u32 = t->tunnel_src.as_u32; + ip0->dst_address.as_u32 = t->tunnel_dst.as_u32; + ip0->checksum = ip4_header_checksum (ip0); + + vnet_buffer (b0)->sw_if_index[VLIB_RX] = + vnet_buffer (b0)->sw_if_index[VLIB_TX]; + vnet_buffer (b0)->ipsec.sad_index = t->local_sa; + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + + next0 = IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT; + error0 = IPSEC_GRE_ERROR_NONE; + + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + ipsec_gre_tx_trace_t *tr = vlib_add_trace (vm, node, + b0, sizeof (*tr)); + tr->tunnel_id = t - igm->tunnels; + tr->length = ip0->length; + tr->src.as_u32 = ip0->src_address.as_u32; + tr->dst.as_u32 = ip0->dst_address.as_u32; + tr->sa_id = t->local_sa_id; + } + + 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, ipsec_gre_input_node.index, + IPSEC_GRE_ERROR_PKTS_ENCAP, frame->n_vectors); + + return frame->n_vectors; +} + +static clib_error_t * +ipsec_gre_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, + u32 flags) +{ + if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) + vnet_hw_interface_set_flags (vnm, hw_if_index, + VNET_HW_INTERFACE_FLAG_LINK_UP); + else + vnet_hw_interface_set_flags (vnm, hw_if_index, 0 /* down */ ); + + return /* no error */ 0; +} + +static u8 * +format_ipsec_gre_tunnel_name (u8 * s, va_list * args) +{ + u32 dev_instance = va_arg (*args, u32); + return format (s, "ipsec-gre%d", dev_instance); +} + +static u8 * +format_ipsec_gre_device (u8 * s, va_list * args) +{ + u32 dev_instance = va_arg (*args, u32); + CLIB_UNUSED (int verbose) = va_arg (*args, int); + + s = format (s, "IPSEC-GRE tunnel: id %d\n", dev_instance); + return s; +} + +/* *INDENT-OFF* */ +VNET_DEVICE_CLASS (ipsec_gre_device_class) = { + .name = "IPSec GRE tunnel device", + .format_device_name = format_ipsec_gre_tunnel_name, + .format_device = format_ipsec_gre_device, + .format_tx_trace = format_ipsec_gre_tx_trace, + .tx_function = ipsec_gre_interface_tx, + .admin_up_down_function = ipsec_gre_interface_admin_up_down, +}; + +VLIB_DEVICE_TX_FUNCTION_MULTIARCH (ipsec_gre_device_class, + ipsec_gre_interface_tx) + + +VNET_HW_INTERFACE_CLASS (ipsec_gre_hw_interface_class) = { + .name = "IPSEC-GRE", +}; +/* *INDENT-ON* */ + +static clib_error_t * +ipsec_gre_init (vlib_main_t * vm) +{ + ipsec_gre_main_t *igm = &ipsec_gre_main; + clib_error_t *error; + + memset (igm, 0, sizeof (igm[0])); + igm->vlib_main = vm; + igm->vnet_main = vnet_get_main (); + + if ((error = vlib_call_init_function (vm, ip_main_init))) + return error; + + if ((error = vlib_call_init_function (vm, ip4_lookup_init))) + return error; + + igm->tunnel_by_key = hash_create (0, sizeof (uword)); + + return vlib_call_init_function (vm, ipsec_gre_input_init); +} + +VLIB_INIT_FUNCTION (ipsec_gre_init); + +ipsec_gre_main_t * +ipsec_gre_get_main (vlib_main_t * vm) +{ + vlib_call_init_function (vm, ipsec_gre_init); + return &ipsec_gre_main; +} + +/* +* fd.io coding-style-patch-verification: ON +* +* Local Variables: +* eval: (c-set-style "gnu") +* End: +*/ diff --git a/src/vnet/ipsec-gre/ipsec_gre.h b/src/vnet/ipsec-gre/ipsec_gre.h new file mode 100644 index 00000000000..a2ca64b6f74 --- /dev/null +++ b/src/vnet/ipsec-gre/ipsec_gre.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2016 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 + * @brief L2-GRE over IPSec packet processing. +*/ + +#ifndef included_ipsec_gre_h +#define included_ipsec_gre_h + +#include <vnet/vnet.h> +#include <vnet/gre/packet.h> +#include <vnet/gre/gre.h> +#include <vnet/ip/ip.h> +#include <vnet/ip/ip4.h> +#include <vnet/ip/ip4_packet.h> +#include <vnet/pg/pg.h> +#include <vnet/ip/format.h> + +extern vnet_hw_interface_class_t ipsec_gre_hw_interface_class; + +/** + * @brief IPSec-GRE errors. + * +*/ +typedef enum +{ +#define ipsec_gre_error(n,s) IPSEC_GRE_ERROR_##n, +#include <vnet/ipsec-gre/error.def> +#undef ipsec_gre_error + IPSEC_GRE_N_ERROR, +} ipsec_gre_error_t; + +/** + * @brief IPSec-GRE tunnel parameters. + * +*/ +typedef struct +{ + ip4_address_t tunnel_src; /**< tunnel IPv4 src address */ + ip4_address_t tunnel_dst; /**< tunnel IPv4 dst address */ + u32 local_sa; /**< local IPSec SA index */ + u32 remote_sa; /**< remote IPSec SA index */ + u32 local_sa_id; /**< local IPSec SA id */ + u32 remote_sa_id; /**< remote IPSec SA id */ + u32 hw_if_index;; /**< hardware interface index */ + u32 sw_if_index;; /**< software interface index */ +} ipsec_gre_tunnel_t; + +/** + * @brief IPSec-GRE state. + * +*/ +typedef struct +{ + ipsec_gre_tunnel_t *tunnels; /**< pool of tunnel instances */ + + uword *tunnel_by_key; /**< hash mapping src/dst addr pair to tunnel */ + + u32 *free_ipsec_gre_tunnel_hw_if_indices; /**< free vlib hw_if_indices */ + + u32 *tunnel_index_by_sw_if_index; /**< mapping from sw_if_index to tunnel + index */ + + vlib_main_t *vlib_main; /**< convenience */ + vnet_main_t *vnet_main; /**< convenience */ +} ipsec_gre_main_t; + +ipsec_gre_main_t ipsec_gre_main; + +extern vlib_node_registration_t ipsec_gre_input_node; +extern vnet_device_class_t ipsec_gre_device_class; + +/* manually added to the interface output node in ipsec_gre.c */ +#define IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT 1 + +/** + * @brief IPSec-GRE tunnel add/del arguments. + * +*/ +typedef struct +{ + u8 is_add; /**< 1 - add, 0 - delete */ + + ip4_address_t src; /**< tunnel IPv4 src address */ + ip4_address_t dst; /**< tunnel IPv4 dst address */ + u32 lsa; /**< local IPSec SA id */ + u32 rsa; /**< remote IPSec SA id */ +} vnet_ipsec_gre_add_del_tunnel_args_t; + +int vnet_ipsec_gre_add_del_tunnel + (vnet_ipsec_gre_add_del_tunnel_args_t * a, u32 * sw_if_indexp); + +#endif /* included_ipsec_gre_h */ + +/* +* fd.io coding-style-patch-verification: ON +* +* Local Variables: +* eval: (c-set-style "gnu") +* End: +*/ diff --git a/src/vnet/ipsec-gre/ipsec_gre_api.c b/src/vnet/ipsec-gre/ipsec_gre_api.c new file mode 100644 index 00000000000..a7ea1490bae --- /dev/null +++ b/src/vnet/ipsec-gre/ipsec_gre_api.c @@ -0,0 +1,190 @@ +/* + *------------------------------------------------------------------ + * ipsec_gre_api.c - ipsec_gre api + * + * Copyright (c) 2016 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 <vlibmemory/api.h> + +#include <vnet/interface.h> +#include <vnet/api_errno.h> +#include <vnet/ipsec-gre/ipsec_gre.h> + +#include <vnet/vnet_msg_enum.h> + +#define vl_typedefs /* define message structures */ +#include <vnet/vnet_all_api_h.h> +#undef vl_typedefs + +#define vl_endianfun /* define message structures */ +#include <vnet/vnet_all_api_h.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 <vnet/vnet_all_api_h.h> +#undef vl_printfun + +#include <vlibapi/api_helper_macros.h> + +#define foreach_vpe_api_msg \ +_(IPSEC_GRE_ADD_DEL_TUNNEL, ipsec_gre_add_del_tunnel) \ +_(IPSEC_GRE_TUNNEL_DUMP, ipsec_gre_tunnel_dump) + +static void +vl_api_ipsec_gre_add_del_tunnel_t_handler (vl_api_ipsec_gre_add_del_tunnel_t * + mp) +{ + vl_api_ipsec_gre_add_del_tunnel_reply_t *rmp; + int rv = 0; + vnet_ipsec_gre_add_del_tunnel_args_t _a, *a = &_a; + u32 sw_if_index = ~0; + + /* Check src & dst are different */ + if (memcmp (mp->src_address, mp->dst_address, 4) == 0) + { + rv = VNET_API_ERROR_SAME_SRC_DST; + goto out; + } + + memset (a, 0, sizeof (*a)); + + /* ip addresses sent in network byte order */ + clib_memcpy (&(a->src), mp->src_address, 4); + clib_memcpy (&(a->dst), mp->dst_address, 4); + a->is_add = mp->is_add; + a->lsa = ntohl (mp->local_sa_id); + a->rsa = ntohl (mp->remote_sa_id); + + rv = vnet_ipsec_gre_add_del_tunnel (a, &sw_if_index); + +out: + /* *INDENT-OFF* */ + REPLY_MACRO2(VL_API_GRE_ADD_DEL_TUNNEL_REPLY, + ({ + rmp->sw_if_index = ntohl (sw_if_index); + })); + /* *INDENT-ON* */ +} + +static void send_ipsec_gre_tunnel_details + (ipsec_gre_tunnel_t * t, unix_shared_memory_queue_t * q, u32 context) +{ + vl_api_ipsec_gre_tunnel_details_t *rmp; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_IPSEC_GRE_TUNNEL_DETAILS); + clib_memcpy (rmp->src_address, &(t->tunnel_src), 4); + clib_memcpy (rmp->dst_address, &(t->tunnel_dst), 4); + rmp->sw_if_index = htonl (t->sw_if_index); + rmp->local_sa_id = htonl (t->local_sa_id); + rmp->remote_sa_id = htonl (t->remote_sa_id); + rmp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void vl_api_ipsec_gre_tunnel_dump_t_handler + (vl_api_ipsec_gre_tunnel_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + ipsec_gre_main_t *igm = &ipsec_gre_main; + ipsec_gre_tunnel_t *t; + u32 sw_if_index; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + { + return; + } + + sw_if_index = ntohl (mp->sw_if_index); + + if (~0 == sw_if_index) + { + /* *INDENT-OFF* */ + pool_foreach (t, igm->tunnels, + ({ + send_ipsec_gre_tunnel_details(t, q, mp->context); + })); + /* *INDENT-ON* */ + } + else + { + if ((sw_if_index >= vec_len (igm->tunnel_index_by_sw_if_index)) || + (~0 == igm->tunnel_index_by_sw_if_index[sw_if_index])) + { + return; + } + t = &igm->tunnels[igm->tunnel_index_by_sw_if_index[sw_if_index]]; + send_ipsec_gre_tunnel_details (t, q, mp->context); + } +} + +/* + * ipsec_gre_api_hookup + * Add vpe's API message handlers to the table. + * vlib has alread mapped shared memory and + * added the client registration handlers. + * See .../vlib-api/vlibmemory/memclnt_vlib.c:memclnt_process() + */ +#define vl_msg_name_crc_list +#include <vnet/vnet_all_api_h.h> +#undef vl_msg_name_crc_list + +static void +setup_message_id_table (api_main_t * am) +{ +#define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id); + foreach_vl_msg_name_crc_ipsec_gre; +#undef _ +} + +static clib_error_t * +ipsec_gre_api_hookup (vlib_main_t * vm) +{ + api_main_t *am = &api_main; + +#define _(N,n) \ + vl_msg_api_set_handlers(VL_API_##N, #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_vpe_api_msg; +#undef _ + + /* + * Set up the (msg_name, crc, message-id) table + */ + setup_message_id_table (am); + + return 0; +} + +VLIB_API_INIT_FUNCTION (ipsec_gre_api_hookup); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/ipsec-gre/ipsec_gre_doc.md b/src/vnet/ipsec-gre/ipsec_gre_doc.md new file mode 100644 index 00000000000..e1bb9cdab1a --- /dev/null +++ b/src/vnet/ipsec-gre/ipsec_gre_doc.md @@ -0,0 +1,74 @@ +# VPP L2-GRE over IPsec implementation {#ipsec_gre_doc} + +This is a memo intended to contain documentation of the VPP L2-GRE over IPsec implementation. +Everything that is not directly obvious should come here. + + +## L2-GRE over IPsec +GRE encapsulate layer 2 traffic and IPSec encrypt what is encapsulated by GRE. The whole point of L2-GRE over IPSec is to tunnel layer 2 over GRE and IPSec by bridging the physical interface with IPSec-GRE tunnel interface. + +There are 2 dedicated nodes for encapsulation: +* ipsec-gre<n>-tx - add GRE header +* esp-encrypt - encrypt GRE packet to ESP packet + +There are 3 dedicated nodes for decapsulation: +* ipsec-if-input - match IPSec SA by source IP address and SPI in ESP packet +* esp-decrypt - decrypt ESP packet +* ipsec-gre-input - remove GRE header + + +### Configuration + +L2-GRE over IPsec support the following CLI configuration command: + create ipsec gre tunnel src <addr> dst <addr> local-sa <id> remote-sa <id> [del] + +src: tunnel source IPv4 address +dst: tunnel destination IPv4 address +local-sa: tunnel local IPSec Security Association +remote-sa: tunnel remote IPSec Security Association +del: delete IPSec-GRE tunnel + +L2-GRE over IPsec support the following API configuration command: + ipsec_gre_add_del_tunnel src <addr> dst <addr> local_sa <sa-id> remote_sa <sa-id> [del] + +src: tunnel source IPv4 address +dst: tunnel destination IPv4 address +local_sa: tunnel local IPSec Security Association +remote_sa: tunnel remote IPSec Security Association +del: delete IPSec-GRE tunnel + + +### Configuration example + +Interface GigabitEthernet0/9/0 is in bridge with ipsec-gre0 tunnel interface, interface GigabitEthernet0/8/0 sending encapsulated and encrypted traffic. + +Configure IPv4 address on sending interface: +set int ip address GigabitEthernet0/8/0 192.168.1.1/24 + +Configure IPSec Security Associations: +ipsec sa add 10 spi 1001 esp crypto-key 4a506a794f574265564551694d653768 crypto-alg aes-cbc-128 integ-key 4339314b55523947594d6d3547666b45764e6a58 integ-alg sha1-96 +ipsec sa add 20 spi 1000 esp crypto-key 49517065716d6235726c734a4372466c crypto-alg aes-cbc-128 integ-key 307439636a5542735133595835546f68534e4f64 integ-alg sha1-96 + +Create IPSec-GRE tunnel: +create ipsec gre tunnel src 192.168.1.1 dst 192.168.1.2 local-sa 10 remote-sa 20 + +Set interfaces state: +set int state GigabitEthernet0/8/0 up +set int state GigabitEthernet0/9/0 up +set int state ipsec-gre0 up + +Bridge physical interface with IPSec-GRE tunnel interface: +set interface l2 bridge GigabitEthernet0/9/0 1 +set interface l2 bridge ipsec-gre0 1 + + +### Operational data + +L2-GRE over IPsec support the following CLI show command: + show ipsec gre tunnel + +L2-GRE over IPsec support the following API dump command: + ipsec_gre_tunnel_dump [sw_if_index <nn>] + +sw_if_index: software interface index of the IPSec-GRE tunnel interface + diff --git a/src/vnet/ipsec-gre/node.c b/src/vnet/ipsec-gre/node.c new file mode 100644 index 00000000000..d20f248a6c8 --- /dev/null +++ b/src/vnet/ipsec-gre/node.c @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2016 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 + * @brief L2-GRE over IPSec packet processing. + * + * Removes GRE header from the packet and sends it to the l2-input node. +*/ + +#include <vlib/vlib.h> +#include <vnet/pg/pg.h> +#include <vnet/ipsec-gre/ipsec_gre.h> +#include <vppinfra/sparse_vec.h> + +#define foreach_ipsec_gre_input_next \ +_(PUNT, "error-punt") \ +_(DROP, "error-drop") \ +_(L2_INPUT, "l2-input") + +typedef enum { +#define _(s,n) IPSEC_GRE_INPUT_NEXT_##s, + foreach_ipsec_gre_input_next +#undef _ + IPSEC_GRE_INPUT_N_NEXT, +} ipsec_gre_input_next_t; + +typedef struct { + u32 tunnel_id; + u32 length; + ip4_address_t src; + ip4_address_t dst; +} ipsec_gre_rx_trace_t; + +u8 * format_ipsec_gre_rx_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 *); + ipsec_gre_rx_trace_t * t = va_arg (*args, ipsec_gre_rx_trace_t *); + + s = format (s, "GRE: tunnel %d len %d src %U dst %U", + t->tunnel_id, clib_net_to_host_u16(t->length), + format_ip4_address, &t->src.as_u8, + format_ip4_address, &t->dst.as_u8); + return s; +} + +/** + * @brief L2-GRE over IPSec input node. + * @node ipsec-gre-input + * + * This node remove GRE header. + * + * @param vm vlib_main_t corresponding to the current thread. + * @param node vlib_node_runtime_t data for this node. + * @param from_frame vlib_frame_t whose contents should be dispatched. + * + * @par Graph mechanics: buffer metadata, next index usage + * + * <em>Uses:</em> + * - <code>ip->src_address</code> and <code>ip->dst_address</code> + * - Match tunnel by source and destination addresses in GRE IP header. + * + * <em>Sets:</em> + * - <code>vnet_buffer(b)->gre.src</code> + * - Save tunnel source IPv4 address. + * - <code>vnet_buffer(b)->gre.dst</code> + * - Save tunnel destination IPv4 address. + * - <code>vnet_buffer(b)->sw_if_index[VLIB_RX]</code> + * - Set input sw_if_index to IPSec-GRE tunnel for learning. + * + * <em>Next Index:</em> + * - Dispatches the packet to the l2-input node. +*/ +static uword +ipsec_gre_input (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + ipsec_gre_main_t * igm = &ipsec_gre_main; + u32 n_left_from, next_index, * from, * to_next; + u64 cached_tunnel_key = (u64) ~0; + u32 cached_tunnel_sw_if_index = 0, tunnel_sw_if_index; + + from = vlib_frame_vector_args (from_frame); + n_left_from = 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 >= 4 && n_left_to_next >= 2) + { + u32 bi0, bi1; + vlib_buffer_t * b0, * b1; + gre_header_t * h0, * h1; + u16 version0, version1, protocol0, protocol1; + int verr0, verr1; + u32 next0, next1; + ip4_header_t *ip0, *ip1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t * p2, * p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, sizeof (h0[0]), LOAD); + CLIB_PREFETCH (p3->data, sizeof (h1[0]), LOAD); + } + + bi0 = from[0]; + bi1 = from[1]; + to_next[0] = bi0; + to_next[1] = bi1; + from += 2; + to_next += 2; + n_left_to_next -= 2; + n_left_from -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + /* ip4_local hands us the ip header, not the gre header */ + ip0 = vlib_buffer_get_current (b0); + ip1 = vlib_buffer_get_current (b1); + + /* Save src + dst ip4 address */ + vnet_buffer(b0)->gre.src = ip0->src_address.as_u32; + vnet_buffer(b0)->gre.dst = ip0->dst_address.as_u32; + vnet_buffer(b1)->gre.src = ip1->src_address.as_u32; + vnet_buffer(b1)->gre.dst = ip1->dst_address.as_u32; + + vlib_buffer_advance (b0, sizeof (*ip0)); + vlib_buffer_advance (b1, sizeof (*ip1)); + + h0 = vlib_buffer_get_current (b0); + h1 = vlib_buffer_get_current (b1); + + protocol0 = clib_net_to_host_u16 (h0->protocol); + protocol1 = clib_net_to_host_u16 (h1->protocol); + if (PREDICT_TRUE(protocol0 == 0x0001)) + { + next0 = IPSEC_GRE_INPUT_NEXT_L2_INPUT; + b0->error = node->errors[IPSEC_GRE_ERROR_NONE]; + } + else + { + clib_warning("unknown GRE protocol: %d", protocol0); + b0->error = node->errors[IPSEC_GRE_ERROR_UNKNOWN_PROTOCOL]; + next0 = IPSEC_GRE_INPUT_NEXT_DROP; + } + if (PREDICT_TRUE(protocol1 == 0x0001)) + { + next1 = IPSEC_GRE_INPUT_NEXT_L2_INPUT; + b1->error = node->errors[IPSEC_GRE_ERROR_NONE]; + } + else + { + clib_warning("unknown GRE protocol: %d", protocol1); + b1->error = node->errors[IPSEC_GRE_ERROR_UNKNOWN_PROTOCOL]; + next1 = IPSEC_GRE_INPUT_NEXT_DROP; + } + + version0 = clib_net_to_host_u16 (h0->flags_and_version); + verr0 = version0 & GRE_VERSION_MASK; + version1 = clib_net_to_host_u16 (h1->flags_and_version); + verr1 = version1 & GRE_VERSION_MASK; + + b0->error = verr0 ? node->errors[IPSEC_GRE_ERROR_UNSUPPORTED_VERSION] + : b0->error; + next0 = verr0 ? IPSEC_GRE_INPUT_NEXT_DROP : next0; + b1->error = verr1 ? node->errors[IPSEC_GRE_ERROR_UNSUPPORTED_VERSION] + : b1->error; + next1 = verr1 ? IPSEC_GRE_INPUT_NEXT_DROP : next1; + + /* For L2 payload set input sw_if_index to GRE tunnel for learning */ + if (PREDICT_TRUE(next0 == IPSEC_GRE_INPUT_NEXT_L2_INPUT)) + { + u64 key = ((u64)(vnet_buffer(b0)->gre.dst) << 32) | + (u64)(vnet_buffer(b0)->gre.src); + + if (cached_tunnel_key != key) + { + vnet_hw_interface_t * hi; + ipsec_gre_tunnel_t * t; + uword * p; + + p = hash_get (igm->tunnel_by_key, key); + if (!p) + { + next0 = IPSEC_GRE_INPUT_NEXT_DROP; + b0->error = node->errors[IPSEC_GRE_ERROR_NO_SUCH_TUNNEL]; + goto drop0; + } + t = pool_elt_at_index (igm->tunnels, p[0]); + hi = vnet_get_hw_interface (igm->vnet_main, + t->hw_if_index); + tunnel_sw_if_index = hi->sw_if_index; + cached_tunnel_sw_if_index = tunnel_sw_if_index; + } + else + { + tunnel_sw_if_index = cached_tunnel_sw_if_index; + } + vnet_buffer(b0)->sw_if_index[VLIB_RX] = tunnel_sw_if_index; + } + +drop0: + /* For L2 payload set input sw_if_index to GRE tunnel for learning */ + if (PREDICT_TRUE(next1 == IPSEC_GRE_INPUT_NEXT_L2_INPUT)) + { + u64 key = ((u64)(vnet_buffer(b1)->gre.dst) << 32) | + (u64)(vnet_buffer(b1)->gre.src); + + if (cached_tunnel_key != key) + { + vnet_hw_interface_t * hi; + ipsec_gre_tunnel_t * t; + uword * p; + + p = hash_get (igm->tunnel_by_key, key); + if (!p) + { + next1 = IPSEC_GRE_INPUT_NEXT_DROP; + b1->error = node->errors[IPSEC_GRE_ERROR_NO_SUCH_TUNNEL]; + goto drop1; + } + t = pool_elt_at_index (igm->tunnels, p[0]); + hi = vnet_get_hw_interface (igm->vnet_main, + t->hw_if_index); + tunnel_sw_if_index = hi->sw_if_index; + cached_tunnel_sw_if_index = tunnel_sw_if_index; + } + else + { + tunnel_sw_if_index = cached_tunnel_sw_if_index; + } + vnet_buffer(b1)->sw_if_index[VLIB_RX] = tunnel_sw_if_index; + } + +drop1: + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + ipsec_gre_rx_trace_t *tr = vlib_add_trace (vm, node, + b0, sizeof (*tr)); + tr->tunnel_id = ~0; + tr->length = ip0->length; + tr->src.as_u32 = ip0->src_address.as_u32; + tr->dst.as_u32 = ip0->dst_address.as_u32; + } + + if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) + { + ipsec_gre_rx_trace_t *tr = vlib_add_trace (vm, node, + b1, sizeof (*tr)); + tr->tunnel_id = ~0; + tr->length = ip1->length; + tr->src.as_u32 = ip1->src_address.as_u32; + tr->dst.as_u32 = ip1->dst_address.as_u32; + } + + vlib_buffer_advance (b0, sizeof (*h0)); + vlib_buffer_advance (b1, sizeof (*h1)); + + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t * b0; + gre_header_t * h0; + ip4_header_t * ip0; + u16 version0, protocol0; + int verr0; + u32 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); + ip0 = vlib_buffer_get_current (b0); + + vnet_buffer(b0)->gre.src = ip0->src_address.as_u32; + vnet_buffer(b0)->gre.dst = ip0->dst_address.as_u32; + + vlib_buffer_advance (b0, sizeof (*ip0)); + + h0 = vlib_buffer_get_current (b0); + + protocol0 = clib_net_to_host_u16 (h0->protocol); + if (PREDICT_TRUE(protocol0 == 0x0001)) + { + next0 = IPSEC_GRE_INPUT_NEXT_L2_INPUT; + b0->error = node->errors[IPSEC_GRE_ERROR_NONE]; + } + else + { + clib_warning("unknown GRE protocol: %d", protocol0); + b0->error = node->errors[IPSEC_GRE_ERROR_UNKNOWN_PROTOCOL]; + next0 = IPSEC_GRE_INPUT_NEXT_DROP; + } + + version0 = clib_net_to_host_u16 (h0->flags_and_version); + verr0 = version0 & GRE_VERSION_MASK; + b0->error = verr0 ? node->errors[IPSEC_GRE_ERROR_UNSUPPORTED_VERSION] + : b0->error; + next0 = verr0 ? IPSEC_GRE_INPUT_NEXT_DROP : next0; + + /* For L2 payload set input sw_if_index to GRE tunnel for learning */ + if (PREDICT_FALSE(next0 == IPSEC_GRE_INPUT_NEXT_L2_INPUT)) + { + u64 key = ((u64)(vnet_buffer(b0)->gre.dst) << 32) | + (u64)(vnet_buffer(b0)->gre.src); + + if (cached_tunnel_key != key) + { + vnet_hw_interface_t * hi; + ipsec_gre_tunnel_t * t; + uword * p; + + p = hash_get (igm->tunnel_by_key, key); + if (!p) + { + next0 = IPSEC_GRE_INPUT_NEXT_DROP; + b0->error = node->errors[IPSEC_GRE_ERROR_NO_SUCH_TUNNEL]; + goto drop; + } + t = pool_elt_at_index (igm->tunnels, p[0]); + hi = vnet_get_hw_interface (igm->vnet_main, + t->hw_if_index); + tunnel_sw_if_index = hi->sw_if_index; + cached_tunnel_sw_if_index = tunnel_sw_if_index; + } + else + { + tunnel_sw_if_index = cached_tunnel_sw_if_index; + } + vnet_buffer(b0)->sw_if_index[VLIB_RX] = tunnel_sw_if_index; + } + +drop: + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + ipsec_gre_rx_trace_t *tr = vlib_add_trace (vm, node, + b0, sizeof (*tr)); + tr->tunnel_id = ~0; + tr->length = ip0->length; + tr->src.as_u32 = ip0->src_address.as_u32; + tr->dst.as_u32 = ip0->dst_address.as_u32; + } + + vlib_buffer_advance (b0, sizeof (*h0)); + + 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, ipsec_gre_input_node.index, + IPSEC_GRE_ERROR_PKTS_DECAP, from_frame->n_vectors); + return from_frame->n_vectors; +} + +static char * ipsec_gre_error_strings[] = { +#define ipsec_gre_error(n,s) s, +#include "error.def" +#undef ipsec_gre_error +}; + +VLIB_REGISTER_NODE (ipsec_gre_input_node) = { + .function = ipsec_gre_input, + .name = "ipsec-gre-input", + /* Takes a vector of packets. */ + .vector_size = sizeof (u32), + + .n_errors = IPSEC_GRE_N_ERROR, + .error_strings = ipsec_gre_error_strings, + + .n_next_nodes = IPSEC_GRE_INPUT_N_NEXT, + .next_nodes = { +#define _(s,n) [IPSEC_GRE_INPUT_NEXT_##s] = n, + foreach_ipsec_gre_input_next +#undef _ + }, + + .format_trace = format_ipsec_gre_rx_trace, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ipsec_gre_input_node, ipsec_gre_input) + +static clib_error_t * ipsec_gre_input_init (vlib_main_t * vm) +{ + { + clib_error_t * error; + error = vlib_call_init_function (vm, ipsec_gre_init); + if (error) + clib_error_report (error); + } + + return 0; +} + +VLIB_INIT_FUNCTION (ipsec_gre_input_init); |