From 694265d4f10dc86bd27bfd29a2b7c49440aeb6b5 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Wed, 10 Aug 2016 01:55:36 -0700 Subject: VPP-202: 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. Change-Id: Ia4cf9ed407bf663770e0d8905c0ad44ce73bd23b Signed-off-by: Matus Fabian --- vnet/Makefile.am | 12 + vnet/vnet/buffer.h | 2 +- vnet/vnet/ipsec-gre/dir.dox | 18 ++ vnet/vnet/ipsec-gre/error.def | 26 +++ vnet/vnet/ipsec-gre/interface.c | 301 ++++++++++++++++++++++++ vnet/vnet/ipsec-gre/ipsec_gre.c | 412 +++++++++++++++++++++++++++++++++ vnet/vnet/ipsec-gre/ipsec_gre.h | 114 +++++++++ vnet/vnet/ipsec-gre/ipsec_gre_doc.md | 74 ++++++ vnet/vnet/ipsec-gre/node.c | 433 +++++++++++++++++++++++++++++++++++ vnet/vnet/ipsec/esp_decrypt.c | 14 +- vnet/vnet/ipsec/esp_encrypt.c | 33 +-- vnet/vnet/ipsec/ipsec.c | 19 ++ vnet/vnet/ipsec/ipsec.h | 15 ++ vnet/vnet/ipsec/ipsec_cli.c | 2 + vnet/vnet/ipsec/ipsec_if.c | 62 +++++ vnet/vnet/ipsec/ipsec_if_in.c | 2 + vnet/vnet/ipsec/ipsec_input.c | 10 +- 17 files changed, 1527 insertions(+), 22 deletions(-) create mode 100644 vnet/vnet/ipsec-gre/dir.dox create mode 100644 vnet/vnet/ipsec-gre/error.def create mode 100644 vnet/vnet/ipsec-gre/interface.c create mode 100644 vnet/vnet/ipsec-gre/ipsec_gre.c create mode 100644 vnet/vnet/ipsec-gre/ipsec_gre.h create mode 100644 vnet/vnet/ipsec-gre/ipsec_gre_doc.md create mode 100644 vnet/vnet/ipsec-gre/node.c (limited to 'vnet') diff --git a/vnet/Makefile.am b/vnet/Makefile.am index 472fc71ee63..72bdd11f54b 100644 --- a/vnet/Makefile.am +++ b/vnet/Makefile.am @@ -415,6 +415,18 @@ nobase_include_HEADERS += \ vnet/vxlan-gpe/vxlan_gpe_packet.h \ vnet/vxlan-gpe/vxlan_gpe_error.def +######################################## +# Tunnel protocol: ipsec+gre +######################################## +libvnet_la_SOURCES += \ + vnet/ipsec-gre/ipsec_gre.c \ + vnet/ipsec-gre/node.c \ + vnet/ipsec-gre/interface.c + +nobase_include_HEADERS += \ + vnet/ipsec-gre/ipsec_gre.h \ + vnet/ipsec-gre/error.def + ######################################## # LISP control plane: lisp-cp ######################################## diff --git a/vnet/vnet/buffer.h b/vnet/vnet/buffer.h index 3fcdf074e36..cab716c23a8 100644 --- a/vnet/vnet/buffer.h +++ b/vnet/vnet/buffer.h @@ -212,7 +212,7 @@ typedef struct /* interface output features */ struct { - u32 ipsec_spd_index; + u32 ipsec_flags; u32 ipsec_sad_index; u32 unused[3]; u32 bitmap; diff --git a/vnet/vnet/ipsec-gre/dir.dox b/vnet/vnet/ipsec-gre/dir.dox new file mode 100644 index 00000000000..e6ffd10b01b --- /dev/null +++ b/vnet/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/vnet/vnet/ipsec-gre/error.def b/vnet/vnet/ipsec-gre/error.def new file mode 100644 index 00000000000..0d7b4686f8a --- /dev/null +++ b/vnet/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 error.def + * @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/vnet/vnet/ipsec-gre/interface.c b/vnet/vnet/ipsec-gre/interface.c new file mode 100644 index 00000000000..3e5d3954341 --- /dev/null +++ b/vnet/vnet/ipsec-gre/interface.c @@ -0,0 +1,301 @@ +/* + * 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 interface.c + * @brief L2-GRE over IPSec tunnel interface. + * + * Creates ipsec-gre tunnel interface. + * Provides a command line interface so humans can interact with VPP. + */ + +#include +#include +#include +#include +#include +#include + +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 VNET_API_ERROR_ + */ +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); + + 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-encrypt", + 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; + 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 dst " + "local-sa remote-sa [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/vnet/vnet/ipsec-gre/ipsec_gre.c b/vnet/vnet/ipsec-gre/ipsec_gre.c new file mode 100644 index 00000000000..24ec6f4e9d6 --- /dev/null +++ b/vnet/vnet/ipsec-gre/ipsec_gre.c @@ -0,0 +1,412 @@ +/* + * 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 ipsec_gre.c + * @brief L2-GRE over IPSec packet processing. + * + * Add GRE header to thr packet and send it to the esp-encrypt node. +*/ + +#include +#include + +ipsec_gre_main_t ipsec_gre_main; + +/** + * @brief IPv4 and GRE header. + * +*/ +/* *INDENT-OFF* */ +typedef CLIB_PACKED (struct +{ + ip4_header_t ip4; + gre_header_t gre; +}) ip4_and_gre_header_t; +/* *INDENT-OFF* */ + +/** + * @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 + * + * Uses: + * - node->runtime_data + * - Match tunnel by rd->dev_instance in IPSec-GRE tunnels + * pool. + * + * Sets: + * - vnet_buffer(b)->output_features.ipsec_sad_index + * - Set IPSec Security Association for packet encryption. + * - vnet_buffer(b)->sw_if_index[VLIB_TX] + * - Reset output sw_if_index. + * + * Nexd Index: + * - 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)->output_features.ipsec_sad_index = t->local_sa; + vnet_buffer (b1)->output_features.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)->output_features.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/vnet/vnet/ipsec-gre/ipsec_gre.h b/vnet/vnet/ipsec-gre/ipsec_gre.h new file mode 100644 index 00000000000..2b66c6a6e8a --- /dev/null +++ b/vnet/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 ipsec_gre.h + * @brief L2-GRE over IPSec packet processing. +*/ + +#ifndef included_ipsec_gre_h +#define included_ipsec_gre_h + +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#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/vnet/vnet/ipsec-gre/ipsec_gre_doc.md b/vnet/vnet/ipsec-gre/ipsec_gre_doc.md new file mode 100644 index 00000000000..e9e5fdd9929 --- /dev/null +++ b/vnet/vnet/ipsec-gre/ipsec_gre_doc.md @@ -0,0 +1,74 @@ +# VPP L2-GRE over IPsec implementation + +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-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 dst local-sa remote-sa [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 dst local_sa remote_sa [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 ] + +sw_if_index: software interface index of the IPSec-GRE tunnel interface + diff --git a/vnet/vnet/ipsec-gre/node.c b/vnet/vnet/ipsec-gre/node.c new file mode 100644 index 00000000000..131025521f3 --- /dev/null +++ b/vnet/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 node.c + * @brief L2-GRE over IPSec packet processing. + * + * Removes GRE header from the packet and send it to the l2-input node. +*/ + +#include +#include +#include +#include + +#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 frame vlib_frame_t whose contents should be dispatched. + * + * @par Graph mechanics: buffer metadata, next index usage + * + * Uses: + * - ip->src_address and ip->dst_address + * - Match tunnel by source and destination addresses in GRE IP header. + * + * Sets: + * - vnet_buffer(b)->gre.src + * - Save tunnel source IPv4 address. + * - vnet_buffer(b)->gre.dst + * - Save tunnel destination IPv4 address. + * - vnet_buffer(b)->sw_if_index[VLIB_RX] + * - Set input sw_if_index to IPSec-GRE tunnel for learning. + * + * Next Index: + * - 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); diff --git a/vnet/vnet/ipsec/esp_decrypt.c b/vnet/vnet/ipsec/esp_decrypt.c index c350508917b..6c7f27f078c 100644 --- a/vnet/vnet/ipsec/esp_decrypt.c +++ b/vnet/vnet/ipsec/esp_decrypt.c @@ -27,7 +27,8 @@ #define foreach_esp_decrypt_next \ _(DROP, "error-drop") \ _(IP4_INPUT, "ip4-input") \ -_(IP6_INPUT, "ip6-input") +_(IP6_INPUT, "ip6-input") \ +_(IPSEC_GRE_INPUT, "ipsec-gre-input") #define _(v, s) ESP_DECRYPT_NEXT_##v, typedef enum @@ -421,7 +422,10 @@ esp_decrypt_node_fn (vlib_main_t * vm, if (PREDICT_TRUE (tunnel_mode)) { if (PREDICT_TRUE (f0->next_header == IP_PROTOCOL_IP_IN_IP)) - next0 = ESP_DECRYPT_NEXT_IP4_INPUT; + { + next0 = ESP_DECRYPT_NEXT_IP4_INPUT; + oh4 = vlib_buffer_get_current (o_b0); + } else if (f0->next_header == IP_PROTOCOL_IPV6) next0 = ESP_DECRYPT_NEXT_IP6_INPUT; else @@ -471,6 +475,12 @@ esp_decrypt_node_fn (vlib_main_t * vm, } } + /* for IPSec-GRE tunnel next node is ipsec-gre-input */ + if (PREDICT_FALSE + ((vnet_buffer (i_b0)->output_features.ipsec_flags) & + IPSEC_FLAG_IPSEC_GRE_TUNNEL)) + next0 = ESP_DECRYPT_NEXT_IPSEC_GRE_INPUT; + to_next[0] = o_bi0; to_next += 1; diff --git a/vnet/vnet/ipsec/esp_encrypt.c b/vnet/vnet/ipsec/esp_encrypt.c index 45b4b3bb72b..0516d43f09b 100644 --- a/vnet/vnet/ipsec/esp_encrypt.c +++ b/vnet/vnet/ipsec/esp_encrypt.c @@ -248,6 +248,8 @@ esp_encrypt_node_fn (vlib_main_t * vm, oh6_0->esp.spi = clib_net_to_host_u32 (sa0->spi); oh6_0->esp.seq = clib_net_to_host_u32 (sa0->seq); ip_proto = ih6_0->ip6.protocol; + + next0 = ESP_ENCRYPT_NEXT_IP6_INPUT; } else { @@ -268,6 +270,8 @@ esp_encrypt_node_fn (vlib_main_t * vm, oh0->esp.spi = clib_net_to_host_u32 (sa0->spi); oh0->esp.seq = clib_net_to_host_u32 (sa0->seq); ip_proto = ih0->ip4.protocol; + + next0 = ESP_ENCRYPT_NEXT_IP4_INPUT; } if (PREDICT_TRUE @@ -276,8 +280,6 @@ esp_encrypt_node_fn (vlib_main_t * vm, oh0->ip4.src_address.as_u32 = sa0->tunnel_src_addr.ip4.as_u32; oh0->ip4.dst_address.as_u32 = sa0->tunnel_dst_addr.ip4.as_u32; - /* in tunnel mode send it back to FIB */ - next0 = ESP_ENCRYPT_NEXT_IP4_INPUT; vnet_buffer (o_b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; } else if (is_ipv6 && sa0->is_tunnel && sa0->is_tunnel_ip6) @@ -291,25 +293,26 @@ esp_encrypt_node_fn (vlib_main_t * vm, oh6_0->ip6.dst_address.as_u64[1] = sa0->tunnel_dst_addr.ip6.as_u64[1]; - /* in tunnel mode send it back to FIB */ - next0 = ESP_ENCRYPT_NEXT_IP6_INPUT; vnet_buffer (o_b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; } else { - transport_mode = 1; - ethernet_header_t *ieh0, *oeh0; - ieh0 = (ethernet_header_t *) i_b0->data; - oeh0 = (ethernet_header_t *) o_b0->data; - clib_memcpy (oeh0, ieh0, sizeof (ethernet_header_t)); vlib_buffer_advance (i_b0, ip_hdr_size); next_hdr_type = ip_proto; - next0 = ESP_ENCRYPT_NEXT_INTERFACE_OUTPUT; - o_b0->flags |= BUFFER_OUTPUT_FEAT_DONE; - vnet_buffer (o_b0)->sw_if_index[VLIB_TX] = - vnet_buffer (i_b0)->sw_if_index[VLIB_TX]; - vnet_buffer (o_b0)->output_features.bitmap = - vnet_buffer (i_b0)->output_features.bitmap; + if (vnet_buffer (i_b0)->sw_if_index[VLIB_TX] != ~0) + { + transport_mode = 1; + ethernet_header_t *ieh0, *oeh0; + ieh0 = (ethernet_header_t *) i_b0->data; + oeh0 = (ethernet_header_t *) o_b0->data; + clib_memcpy (oeh0, ieh0, sizeof (ethernet_header_t)); + next0 = ESP_ENCRYPT_NEXT_INTERFACE_OUTPUT; + o_b0->flags |= BUFFER_OUTPUT_FEAT_DONE; + vnet_buffer (o_b0)->sw_if_index[VLIB_TX] = + vnet_buffer (i_b0)->sw_if_index[VLIB_TX]; + vnet_buffer (o_b0)->output_features.bitmap = + vnet_buffer (i_b0)->output_features.bitmap; + } } ASSERT (sa0->crypto_alg < IPSEC_CRYPTO_N_ALG); diff --git a/vnet/vnet/ipsec/ipsec.c b/vnet/vnet/ipsec/ipsec.c index bf9dd973f86..1b3b9ffcc38 100644 --- a/vnet/vnet/ipsec/ipsec.c +++ b/vnet/vnet/ipsec/ipsec.c @@ -24,6 +24,17 @@ #include #include +u32 +ipsec_get_sa_index_by_sa_id (u32 sa_id) +{ + ipsec_main_t *im = &ipsec_main; + uword *p = hash_get (im->sa_index_by_sa_id, sa_id); + if (!p) + return ~0; + + return p[0]; +} + int ipsec_set_interface_spd (vlib_main_t * vm, u32 sw_if_index, u32 spd_id, int is_add) @@ -391,6 +402,7 @@ ipsec_is_sa_used (u32 sa_index) ipsec_main_t *im = &ipsec_main; ipsec_spd_t *spd; ipsec_policy_t *p; + ipsec_tunnel_if_t *t; /* *INDENT-OFF* */ pool_foreach(spd, im->spds, ({ @@ -402,6 +414,13 @@ ipsec_is_sa_used (u32 sa_index) } })); })); + + pool_foreach(t, im->tunnel_interfaces, ({ + if (t->input_sa_index == sa_index) + return 1; + if (t->output_sa_index == sa_index) + return 1; + })); /* *INDENT-ON* */ return 0; diff --git a/vnet/vnet/ipsec/ipsec.h b/vnet/vnet/ipsec/ipsec.h index 5b88c8278fb..fd3e8a361d1 100644 --- a/vnet/vnet/ipsec/ipsec.h +++ b/vnet/vnet/ipsec/ipsec.h @@ -16,6 +16,8 @@ #include #endif +#define IPSEC_FLAG_IPSEC_GRE_TUNNEL (1 << 0) + #define foreach_ipsec_policy_action \ _(0, BYPASS, "bypass") \ _(1, DISCARD, "discard") \ @@ -127,6 +129,15 @@ typedef struct u8 remote_integ_key[128]; } ipsec_add_del_tunnel_args_t; +typedef struct +{ + u8 is_add; + u32 local_sa_id; + u32 remote_sa_id; + ip4_address_t local_ip; + ip4_address_t remote_ip; +} ipsec_add_del_ipsec_gre_tunnel_args_t; + typedef enum { IPSEC_IF_SET_KEY_TYPE_NONE, @@ -243,6 +254,7 @@ int ipsec_add_del_policy (vlib_main_t * vm, ipsec_policy_t * policy, int ipsec_add_del_sa (vlib_main_t * vm, ipsec_sa_t * new_sa, int is_add); int ipsec_set_sa_key (vlib_main_t * vm, ipsec_sa_t * sa_update); +u32 ipsec_get_sa_index_by_sa_id (u32 sa_id); u8 *format_ipsec_if_output_trace (u8 * s, va_list * args); u8 *format_ipsec_policy_action (u8 * s, va_list * args); u8 *format_ipsec_crypto_alg (u8 * s, va_list * args); @@ -254,6 +266,9 @@ uword unformat_ipsec_integ_alg (unformat_input_t * input, va_list * args); /*u32 ipsec_add_del_tunnel_if (vnet_main_t * vnm, ipsec_add_del_tunnel_args_t * args); */ int ipsec_add_del_tunnel_if (ipsec_add_del_tunnel_args_t * args); +int ipsec_add_del_ipsec_gre_tunnel (vnet_main_t * vnm, + ipsec_add_del_ipsec_gre_tunnel_args_t * + args); int ipsec_set_interface_key (vnet_main_t * vnm, u32 hw_if_index, ipsec_if_set_key_type_t type, u8 alg, u8 * key); diff --git a/vnet/vnet/ipsec/ipsec_cli.c b/vnet/vnet/ipsec/ipsec_cli.c index 8b15110af0d..785e040b006 100644 --- a/vnet/vnet/ipsec/ipsec_cli.c +++ b/vnet/vnet/ipsec/ipsec_cli.c @@ -561,6 +561,8 @@ show_ipsec_command_fn (vlib_main_t * vm, vlib_cli_output (vm, "tunnel interfaces"); /* *INDENT-OFF* */ pool_foreach (t, im->tunnel_interfaces, ({ + if (t->hw_if_index == ~0) + continue; hi = vnet_get_hw_interface (im->vnet_main, t->hw_if_index); vlib_cli_output(vm, " %s seq", hi->name); sa = pool_elt_at_index(im->sad, t->output_sa_index); diff --git a/vnet/vnet/ipsec/ipsec_if.c b/vnet/vnet/ipsec/ipsec_if.c index 475b7bda2bc..f4fad8d95b3 100644 --- a/vnet/vnet/ipsec/ipsec_if.c +++ b/vnet/vnet/ipsec/ipsec_if.c @@ -194,6 +194,68 @@ ipsec_add_del_tunnel_if_internal (vnet_main_t * vnm, return 0; } +int +ipsec_add_del_ipsec_gre_tunnel (vnet_main_t * vnm, + ipsec_add_del_ipsec_gre_tunnel_args_t * args) +{ + ipsec_tunnel_if_t *t = 0; + ipsec_main_t *im = &ipsec_main; + uword *p; + ipsec_sa_t *sa; + u64 key; + u32 isa, osa; + + p = hash_get (im->sa_index_by_sa_id, args->local_sa_id); + if (!p) + return VNET_API_ERROR_INVALID_VALUE; + isa = p[0]; + + p = hash_get (im->sa_index_by_sa_id, args->remote_sa_id); + if (!p) + return VNET_API_ERROR_INVALID_VALUE; + osa = p[0]; + sa = pool_elt_at_index (im->sad, p[0]); + + if (sa->is_tunnel) + key = (u64) sa->tunnel_dst_addr.ip4.as_u32 << 32 | (u64) sa->spi; + else + key = (u64) args->remote_ip.as_u32 << 32 | (u64) sa->spi; + + p = hash_get (im->ipsec_if_pool_index_by_key, key); + + if (args->is_add) + { + /* check if same src/dst pair exists */ + if (p) + return VNET_API_ERROR_INVALID_VALUE; + + pool_get_aligned (im->tunnel_interfaces, t, CLIB_CACHE_LINE_BYTES); + memset (t, 0, sizeof (*t)); + + t->input_sa_index = isa; + t->output_sa_index = osa; + t->hw_if_index = ~0; + hash_set (im->ipsec_if_pool_index_by_key, key, + t - im->tunnel_interfaces); + + /*1st interface, register protocol */ + if (pool_elts (im->tunnel_interfaces) == 1) + ip4_register_protocol (IP_PROTOCOL_IPSEC_ESP, + ipsec_if_input_node.index); + } + else + { + /* check if exists */ + if (!p) + return VNET_API_ERROR_INVALID_VALUE; + + t = pool_elt_at_index (im->tunnel_interfaces, p[0]); + hash_unset (im->ipsec_if_pool_index_by_key, key); + pool_put (im->tunnel_interfaces, t); + } + return 0; +} + int ipsec_set_interface_key (vnet_main_t * vnm, u32 hw_if_index, ipsec_if_set_key_type_t type, u8 alg, u8 * key) diff --git a/vnet/vnet/ipsec/ipsec_if_in.c b/vnet/vnet/ipsec/ipsec_if_in.c index e5574421cfa..07d4bf30352 100644 --- a/vnet/vnet/ipsec/ipsec_if_in.c +++ b/vnet/vnet/ipsec/ipsec_if_in.c @@ -113,6 +113,8 @@ ipsec_if_input_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, t = pool_elt_at_index (im->tunnel_interfaces, p[0]); vnet_buffer (b0)->output_features.ipsec_sad_index = t->input_sa_index; + vnet_buffer (b0)->output_features.ipsec_flags = + t->hw_if_index == ~0 ? IPSEC_FLAG_IPSEC_GRE_TUNNEL : 0; vlib_buffer_advance (b0, ip4_header_bytes (ip0)); next0 = IPSEC_IF_INPUT_NEXT_ESP_DECRYPT; } diff --git a/vnet/vnet/ipsec/ipsec_input.c b/vnet/vnet/ipsec/ipsec_input.c index 2bc4e2b4999..8364e226ae1 100644 --- a/vnet/vnet/ipsec/ipsec_input.c +++ b/vnet/vnet/ipsec/ipsec_input.c @@ -239,11 +239,11 @@ ipsec_input_ip4_node_fn (vlib_main_t * vm, ipsec_policy_t *p0; p0 = ipsec_input_protect_policy_match (spd0, clib_net_to_host_u32 - (ip0->src_address. - as_u32), + (ip0-> + src_address.as_u32), clib_net_to_host_u32 - (ip0->dst_address. - as_u32), + (ip0-> + dst_address.as_u32), clib_net_to_host_u32 (esp0->spi)); @@ -253,6 +253,7 @@ ipsec_input_ip4_node_fn (vlib_main_t * vm, p0->counter.bytes += clib_net_to_host_u16 (ip0->length); vnet_buffer (b0)->output_features.ipsec_sad_index = p0->sa_index; + vnet_buffer (b0)->output_features.ipsec_flags = 0; next0 = IPSEC_INPUT_NEXT_ESP_DECRYPT; vlib_buffer_advance (b0, ip4_header_bytes (ip0)); goto trace0; @@ -382,6 +383,7 @@ VLIB_NODE_FUNCTION_MULTIARCH (ipsec_input_ip4_node, ipsec_input_ip4_node_fn) p0->counter.bytes += header_size; vnet_buffer (b0)->output_features.ipsec_sad_index = p0->sa_index; + vnet_buffer (b0)->output_features.ipsec_flags = 0; next0 = IPSEC_INPUT_NEXT_ESP_DECRYPT; vlib_buffer_advance (b0, header_size); goto trace0; -- cgit 1.2.3-korg