/* * Copyright (c) 2015 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include ikev2_main_t ikev2_main; static int ikev2_delete_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa, ikev2_child_sa_t * child); #define ikev2_set_state(sa, v) do { \ (sa)->state = v; \ clib_warning("sa state changed to " #v); \ } while(0); typedef struct { u32 next_index; u32 sw_if_index; } ikev2_trace_t; static u8 * format_ikev2_trace (u8 * s, va_list * args) { CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); ikev2_trace_t *t = va_arg (*args, ikev2_trace_t *); s = format (s, "ikev2: sw_if_index %d, next index %d", t->sw_if_index, t->next_index); return s; } static vlib_node_registration_t ikev2_node; #define foreach_ikev2_error \ _(PROCESSED, "IKEv2 packets processed") \ _(IKE_SA_INIT_RETRANSMIT, "IKE_SA_INIT retransmit ") \ _(IKE_SA_INIT_IGNORE, "IKE_SA_INIT ignore (IKE SA already auth)") \ _(IKE_REQ_RETRANSMIT, "IKE request retransmit") \ _(IKE_REQ_IGNORE, "IKE request ignore (old msgid)") \ _(NOT_IKEV2, "Non IKEv2 packets received") typedef enum { #define _(sym,str) IKEV2_ERROR_##sym, foreach_ikev2_error #undef _ IKEV2_N_ERROR, } ikev2_error_t; static char *ikev2_error_strings[] = { #define _(sym,string) string, foreach_ikev2_error #undef _ }; typedef enum { IKEV2_NEXT_IP4_LOOKUP, IKEV2_NEXT_ERROR_DROP, IKEV2_N_NEXT, } ikev2_next_t; static ikev2_sa_transform_t * ikev2_find_transform_data (ikev2_sa_transform_t * t) { ikev2_main_t *km = &ikev2_main; ikev2_sa_transform_t *td; vec_foreach (td, km->supported_transforms) { if (td->type != t->type) continue; if (td->transform_id != t->transform_id) continue; if (td->type == IKEV2_TRANSFORM_TYPE_ENCR) { if (vec_len (t->attrs) != 4 || t->attrs[0] != 0x80 || t->attrs[1] != 14) continue; if (((t->attrs[2] << 8 | t->attrs[3]) / 8) != td->key_len) continue; } return td; } return 0; } static ikev2_sa_proposal_t * ikev2_select_proposal (ikev2_sa_proposal_t * proposals, ikev2_protocol_id_t prot_id) { ikev2_sa_proposal_t *rv = 0; ikev2_sa_proposal_t *proposal; ikev2_sa_transform_t *transform, *new_t; u8 mandatory_bitmap, optional_bitmap; if (prot_id == IKEV2_PROTOCOL_IKE) { mandatory_bitmap = (1 << IKEV2_TRANSFORM_TYPE_ENCR) | (1 << IKEV2_TRANSFORM_TYPE_PRF) | (1 << IKEV2_TRANSFORM_TYPE_INTEG) | (1 << IKEV2_TRANSFORM_TYPE_DH); optional_bitmap = mandatory_bitmap; } else if (prot_id == IKEV2_PROTOCOL_ESP) { mandatory_bitmap = (1 << IKEV2_TRANSFORM_TYPE_ENCR) | (1 << IKEV2_TRANSFORM_TYPE_ESN); optional_bitmap = mandatory_bitmap | (1 << IKEV2_TRANSFORM_TYPE_INTEG) | (1 << IKEV2_TRANSFORM_TYPE_DH); } else if (prot_id == IKEV2_PROTOCOL_AH) { mandatory_bitmap = (1 << IKEV2_TRANSFORM_TYPE_INTEG) | (1 << IKEV2_TRANSFORM_TYPE_ESN); optional_bitmap = mandatory_bitmap | (1 << IKEV2_TRANSFORM_TYPE_DH); } else return 0; vec_add2 (rv, proposal, 1); vec_foreach (proposal, proposals) { u8 bitmap = 0; if (proposal->protocol_id != prot_id) continue; vec_foreach (transform, proposal->transforms) { if ((1 << transform->type) & bitmap) continue; if (ikev2_find_transform_data (transform)) { bitmap |= 1 << transform->type; vec_add2 (rv->transforms, new_t, 1); clib_memcpy (new_t, transform, sizeof (*new_t)); new_t->attrs = vec_dup (transform->attrs); } } clib_warning ("bitmap is %x mandatory is %x optional is %x", bitmap, mandatory_bitmap, optional_bitmap); if ((bitmap & mandatory_bitmap) == mandatory_bitmap && (bitmap & ~optional_bitmap) == 0) { rv->proposal_num = proposal->proposal_num; rv->protocol_id = proposal->protocol_id; RAND_bytes ((u8 *) & rv->spi, sizeof (rv->spi)); goto done; } else { vec_free (rv->transforms); } } vec_free (rv); done: return rv; } ikev2_sa_transform_t * ikev2_sa_get_td_for_type (ikev2_sa_proposal_t * p, ikev2_transform_type_t type) { ikev2_sa_transform_t *t; if (!p) return 0; vec_foreach (t, p->transforms) { if (t->type == type) return ikev2_find_transform_data (t); } return 0; } ikev2_child_sa_t * ikev2_sa_get_child (ikev2_sa_t * sa, u32 spi, ikev2_protocol_id_t prot_id, int by_initiator) { ikev2_child_sa_t *c; vec_foreach (c, sa->childs) { ikev2_sa_proposal_t *proposal = by_initiator ? &c->i_proposals[0] : &c->r_proposals[0]; if (proposal && proposal->spi == spi && proposal->protocol_id == prot_id) return c; } return 0; } void ikev2_sa_free_proposal_vector (ikev2_sa_proposal_t ** v) { ikev2_sa_proposal_t *p; ikev2_sa_transform_t *t; if (!*v) return; vec_foreach (p, *v) { vec_foreach (t, p->transforms) { vec_free (t->attrs); } vec_free (p->transforms); } vec_free (*v); }; static void ikev2_sa_free_all_child_sa (ikev2_child_sa_t ** childs) { ikev2_child_sa_t *c; vec_foreach (c, *childs) { ikev2_sa_free_proposal_vector (&c->r_proposals); ikev2_sa_free_proposal_vector (&c->i_proposals); vec_free (c->sk_ai); vec_free (c->sk_ar); vec_free (c->sk_ei); vec_free (c->sk_er); } vec_free (*childs); } static void ikev2_sa_del_child_sa (ikev2_sa_t * sa, ikev2_child_sa_t * child) { ikev2_sa_free_proposal_vector (&child->r_proposals); ikev2_sa_free_proposal_vector (&child->i_proposals); vec_free (child->sk_ai); vec_free (child->sk_ar); vec_free (child->sk_ei); vec_free (child->sk_er); vec_del1 (sa->childs, child - sa->childs); } static void ikev2_sa_free_all_vec (ikev2_sa_t * sa) { vec_free (sa->i_nonce); vec_free (sa->i_dh_data); vec_free (sa->dh_shared_key); vec_free (sa->dh_private_key); ikev2_sa_free_proposal_vector (&sa->r_proposals); ikev2_sa_free_proposal_vector (&sa->i_proposals); vec_free (sa->sk_d); vec_free (sa->sk_ai); vec_free (sa->sk_ar); vec_free (sa->sk_ei); vec_free (sa->sk_er); vec_free (sa->sk_pi); vec_free (sa->sk_pr); vec_free (sa->i_id.data); vec_free (sa->i_auth.data); vec_free (sa->r_id.data); vec_free (sa->r_auth.data); if (sa->r_auth.key) EVP_PKEY_free (sa->r_auth.key); vec_free (sa->del); ikev2_sa_free_all_child_sa (&sa->childs); } static void ikev2_delete_sa (ikev2_sa_t * sa) { ikev2_main_t *km = &ikev2_main; u32 thread_index = vlib_get_thread_index (); uword *p; ikev2_sa_free_all_vec (sa); p = hash_get (km->per_thread_data[thread_index].sa_by_rspi, sa->rspi); if (p) { hash_unset (km->per_thread_data[thread_index].sa_by_rspi, sa->rspi); pool_put (km->per_thread_data[thread_index].sas, sa); } } static void ikev2_generate_sa_init_data (ikev2_sa_t * sa) { ikev2_sa_transform_t *t = 0, *t2; ikev2_main_t *km = &ikev2_main; if (sa->dh_group == IKEV2_TRANSFORM_DH_TYPE_NONE) { return; } /* check if received DH group is on our list of supported groups */ vec_foreach (t2, km->supported_transforms) { if (t2->type == IKEV2_TRANSFORM_TYPE_DH && sa->dh_group == t2->dh_type) { t = t2; break; } } if (!t) { clib_warning ("unknown dh data group %u (data len %u)", sa->dh_group, vec_len (sa->i_dh_data)); sa->dh_group = IKEV2_TRANSFORM_DH_TYPE_NONE; return; } if (sa->is_initiator) { /* generate rspi */ RAND_bytes ((u8 *) & sa->ispi, 8); /* generate nonce */ sa->i_nonce = vec_new (u8, IKEV2_NONCE_SIZE); RAND_bytes ((u8 *) sa->i_nonce, IKEV2_NONCE_SIZE); } else { /* generate rspi */ RAND_bytes ((u8 *) & sa->rspi, 8); /* generate nonce */ sa->r_nonce = vec_new (u8, IKEV2_NONCE_SIZE); RAND_bytes ((u8 *) sa->r_nonce, IKEV2_NONCE_SIZE); } /* generate dh keys */ ikev2_generate_dh (sa, t); } static void ikev2_complete_sa_data (ikev2_sa_t * sa, ikev2_sa_t * sai) { ikev2_sa_transform_t *t = 0, *t2; ikev2_main_t *km = &ikev2_main; /*move some data to the new SA */ #define _(A) ({void* __tmp__ = (A); (A) = 0; __tmp__;}) sa->i_nonce = _(sai->i_nonce); sa->i_dh_data = _(sai->i_dh_data); sa->dh_private_key = _(sai->dh_private_key); sa->iaddr.as_u32 = sai->iaddr.as_u32; sa->raddr.as_u32 = sai->raddr.as_u32; sa->is_initiator = sai->is_initiator; sa->profile = sai->profile; sa->i_id.type = sai->i_id.type; sa->i_id.data = _(sai->i_id.data); sa->i_auth.method = sai->i_auth.method; sa->i_auth.hex = sai->i_auth.hex; sa->i_auth.data = _(sai->i_auth.data); sa->i_auth.key = _(sai->i_auth.key); sa->last_sa_init_req_packet_data = _(sai->last_sa_init_req_packet_data); sa->childs = _(sai->childs); #undef _ if (sa->dh_group == IKEV2_TRANSFORM_DH_TYPE_NONE) { return; } /* check if received DH group is on our list of supported groups */ vec_foreach (t2, km->supported_transforms) { if (t2->type == IKEV2_TRANSFORM_TYPE_DH && sa->dh_group == t2->dh_type) { t = t2; break; } } if (!t) { clib_warning ("unknown dh data group %u (data len %u)", sa->dh_group, vec_len (sa->i_dh_data)); sa->dh_group = IKEV2_TRANSFORM_DH_TYPE_NONE; return; } /* generate dh keys */ ikev2_complete_dh (sa, t); } static void ikev2_calc_keys (ikev2_sa_t * sa) { u8 *tmp; /* calculate SKEYSEED = prf(Ni | Nr, g^ir) */ u8 *skeyseed = 0; u8 *s = 0; ikev2_sa_transform_t *tr_encr, *tr_prf, *tr_integ; tr_encr = ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_ENCR); tr_prf = ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_PRF); tr_integ = ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_INTEG); vec_append (s, sa->i_nonce); vec_append (s, sa->r_nonce); skeyseed = ikev2_calc_prf (tr_prf, s, sa->dh_shared_key); /* Calculate S = Ni | Nr | SPIi | SPIr */ u64 *spi; vec_add2 (s, tmp, 2 * sizeof (*spi)); spi = (u64 *) tmp; spi[0] = clib_host_to_net_u64 (sa->ispi); spi[1] = clib_host_to_net_u64 (sa->rspi); /* calculate PRFplus */ u8 *keymat; int len = tr_prf->key_trunc + /* SK_d */ tr_integ->key_len * 2 + /* SK_ai, SK_ar */ tr_encr->key_len * 2 + /* SK_ei, SK_er */ tr_prf->key_len * 2; /* SK_pi, SK_pr */ keymat = ikev2_calc_prfplus (tr_prf, skeyseed, s, len); vec_free (skeyseed); vec_free (s); int pos = 0; /* SK_d */ sa->sk_d = vec_new (u8, tr_prf->key_trunc); clib_memcpy (sa->sk_d, keymat + pos, tr_prf->key_trunc); pos += tr_prf->key_trunc; /* SK_ai */ sa->sk_ai = vec_new (u8, tr_integ->key_len); clib_memcpy (sa->sk_ai, keymat + pos, tr_integ->key_len); pos += tr_integ->key_len; /* SK_ar */ sa->sk_ar = vec_new (u8, tr_integ->key_len); clib_memcpy (sa->sk_ar, keymat + pos, tr_integ->key_len); pos += tr_integ->key_len; /* SK_ei */ sa->sk_ei = vec_new (u8, tr_encr->key_len); clib_memcpy (sa->sk_ei, keymat + pos, tr_encr->key_len); pos += tr_encr->key_len; /* SK_er */ sa->sk_er = vec_new (u8, tr_encr->key_len); clib_memcpy (sa->sk_er, keymat + pos, tr_encr->key_len); pos += tr_encr->key_len; /* SK_pi */ sa->sk_pi = vec_new (u8, tr_prf->key_len); clib_memcpy (sa->sk_pi, keymat + pos, tr_prf->key_len); pos += tr_prf->key_len; /* SK_pr */ sa->sk_pr = vec_new (u8, tr_prf->key_len); clib_memcpy (sa->sk_pr, keymat + pos, tr_prf->key_len); pos += tr_prf->key_len; vec_free (keymat); } static void ikev2_calc_child_keys (ikev2_sa_t * sa, ikev2_child_sa_t * child) { u8 *s = 0; ikev2_sa_transform_t *tr_prf, *ctr_encr, *ctr_integ; tr_prf = ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_PRF); ctr_encr = ikev2_sa_get_td_for_type (child->r_proposals, IKEV2_TRANSFORM_TYPE_ENCR); ctr_integ = ikev2_sa_get_td_for_type (child->r_proposals, IKEV2_TRANSFORM_TYPE_INTEG); vec_append (s, sa->i_nonce); vec_append (s, sa->r_nonce); /* calculate PRFplus */ u8 *keymat; int len = ctr_encr->key_len * 2 + ctr_integ->key_len * 2; keymat = ikev2_calc_prfplus (tr_prf, sa->sk_d, s, len); int pos = 0; /* SK_ei */ child->sk_ei = vec_new (u8, ctr_encr->key_len); clib_memcpy (child->sk_ei, keymat + pos, ctr_encr->key_len); pos += ctr_encr->key_len; /* SK_ai */ child->sk_ai = vec_new (u8, ctr_integ->key_len); clib_memcpy (child->sk_ai, keymat + pos, ctr_integ->key_len); pos += ctr_integ->key_len; /* SK_er */ child->sk_er = vec_new (u8, ctr_encr->key_len); clib_memcpy (child->sk_er, keymat + pos, ctr_encr->key_len); pos += ctr_encr->key_len; /* SK_ar */ child->sk_ar = vec_new (u8, ctr_integ->key_len); clib_memcpy (child->sk_ar, keymat + pos, ctr_integ->key_len); pos += ctr_integ->key_len; ASSERT (pos == len); vec_free (keymat); } static void ikev2_process_sa_init_req (vlib_main_t * vm, ikev2_sa_t * sa, ike_header_t * ike) { int p = 0; u32 len = clib_net_to_host_u32 (ike->length); u8 payload = ike->nextpayload; clib_warning ("ispi %lx rspi %lx nextpayload %x version %x " "exchange %x flags %x msgid %x length %u", clib_net_to_host_u64 (ike->ispi), clib_net_to_host_u64 (ike->rspi), payload, ike->version, ike->exchange, ike->flags, clib_net_to_host_u32 (ike->msgid), len); sa->ispi = clib_net_to_host_u64 (ike->ispi); /* store whole IKE payload - needed for PSK auth */ vec_free (sa->last_sa_init_req_packet_data); vec_add (sa->last_sa_init_req_packet_data, ike, len); while (p < len && payload != IKEV2_PAYLOAD_NONE) { ike_payload_header_t *ikep = (ike_payload_header_t *) & ike->payload[p]; u32 plen = clib_net_to_host_u16 (ikep->length); if (plen < sizeof (ike_payload_header_t)) return; if (payload == IKEV2_PAYLOAD_SA) { ikev2_sa_free_proposal_vector (&sa->i_proposals); sa->i_proposals = ikev2_parse_sa_payload (ikep); } else if (payload == IKEV2_PAYLOAD_KE) { ike_ke_payload_header_t *ke = (ike_ke_payload_header_t *) ikep; sa->dh_group = clib_net_to_host_u16 (ke->dh_group); vec_free (sa->i_dh_data); vec_add (sa->i_dh_data, ke->payload, plen - sizeof (*ke)); } else if (payload == IKEV2_PAYLOAD_NONCE) { vec_free (sa->i_nonce); vec_add (sa->i_nonce, ikep->payload, plen - sizeof (*ikep)); } else if (payload == IKEV2_PAYLOAD_NOTIFY) { ikev2_notify_t *n = ikev2_parse_notify_payload (ikep); vec_free (n); } else if (payload == IKEV2_PAYLOAD_VENDOR) { ikev2_parse_vendor_payload (ikep); } else { clib_warning ("unknown payload %u flags %x length %u", payload, ikep->flags, plen); if (ikep->flags & IKEV2_PAYLOAD_FLAG_CRITICAL) { ikev2_set_state (sa, IKEV2_STATE_NOTIFY_AND_DELETE); sa->unsupported_cp = payload; return; } } payload = ikep->nextpayload; p += plen; } ikev2_set_state (sa, IKEV2_STATE_SA_INIT); } static void ikev2_process_sa_init_resp (vlib_main_t * vm, ikev2_sa_t * sa, ike_header_t * ike) { int p = 0; u32 len = clib_net_to_host_u32 (ike->length); u8 payload = ike->nextpayload; clib_warning ("ispi %lx rspi %lx nextpayload %x version %x " "exchange %x flags %x msgid %x length %u", clib_net_to_host_u64 (ike->ispi), clib_net_to_host_u64 (ike->rspi), payload, ike->version, ike->exchange, ike->flags, clib_net_to_host_u32 (ike->msgid), len); sa->ispi = clib_net_to_host_u64 (ike->ispi); sa->rspi = clib_net_to_host_u64 (i
Overview
========

Virtual Topologies
------------------

CSIT VPP functional tests are executed in VM-based virtual topologies
created on demand using :abbr:`VIRL (Virtual Internet Routing Lab)`
simulation platform contributed by Cisco. VIRL runs on physical
baremetal servers hosted by LF FD.io project. Based on the packet path
thru SUT VMs, two distinct logical topology types are used for VPP DUT
data plane testing:

#. vNIC-to-vNIC switching topologies.
#. Nested-VM service switching topologies.

vNIC-to-vNIC Switching
~~~~~~~~~~~~~~~~~~~~~~

The simplest virtual topology for software data plane application like
VPP is vNIC-to-vNIC switching. Tested virtual topologies for 2-Node and
3-Node testbeds are shown in figures below.

.. only:: latex

    .. raw:: latex

        \begin{figure}[H]
            \centering
                \graphicspath{{../_tmp/src/vpp_functional_tests/}}
                \includegraphics[width=0.90\textwidth]{virtual-2n-nic2nic}
                \label{fig:virtual-2n-nic2nic}
        \end{figure}

.. only:: html

    .. figure:: ../vpp_functional_tests/virtual-2n-nic2nic.svg
        :alt: virtual-2n-nic2nic
        :align: center


.. only:: latex

    .. raw:: latex

        \begin{figure}[H]
            \centering
                \graphicspath{{../_tmp/src/vpp_functional_tests/}}
                \includegraphics[width=0.90\textwidth]{virtual-3n-nic2nic}
                \label{fig:virtual-3n-nic2nic}
        \end{figure}

.. only:: html

    .. figure:: ../vpp_functional_tests/virtual-3n-nic2nic.svg
        :alt: virtual-3n-nic2nic
        :align: center

SUT1 and SUT2 are two VMs (running Ubuntu or Centos, depending on the test
suite), TG is a Traffic Generator (running Ubuntu VM). SUTs run VPP
SW application in Linux user-mode as a Device Under Test (DUT) within
the VM. TG runs Scapy SW application as a packet Traffic Generator.
Network connectivity between SUTs and to TG is provided using virtual
NICs and VMs' virtio drivers.

Virtual testbeds are created on-demand whenever a verification job is
started (e.g. triggered by the gerrit patch submission) and destroyed
upon completion of all functional tests. Each node is a Virtual Machine
and each connection that is drawn on the diagram is available for use in
any test case. During the test execution, all nodes are reachable thru
the Management network connected to every node via dedicated virtual
NICs and virtual links (not shown above for clarity).

Nested-VM Service Switching
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Nested-VM (Virtual Machine) service switching topology test cases
require VPP DUT to communicate with nested-VM(s) over vhost-user virtual
interfaces. Nested-VM(s) is(are) created on SUT1 and/or SUT2 for the
duration of these particular test cases only. Virtual test topology with
nested-VM(s) is shown in the figure below.

.. only:: latex

    .. raw:: latex

        \begin{figure}[H]
            \centering
                \graphicspath{{../_tmp/src/vpp_functional_tests/}}
                \includegraphics[width=0.90\textwidth]{virtual-3n-vm-vhost}
                \label{fig:virtual-3n-vm-vhost}
        \end{figure}

.. only:: html

    .. figure:: ../vpp_functional_tests/virtual-3n-vm-vhost.svg
        :alt: virtual-3n-vm-vhost
        :align: center

Functional Tests Coverage
-------------------------

|csit-release| includes following VPP functionality tested in virtual VM
environment:

+-----------------------+----------------------------------------------+
| Functionality         |  Description                                 |
+=======================+==============================================+
| ACL                   | Ingress Access Control List security for L2  |
|                       | Bridge-Domain MAC switching, IPv4 routing,   |
|                       | IPv6 routing.                                |
+-----------------------+----------------------------------------------+
| COP                   | COP address white-list and black-list        |
|                       | filtering for IPv4 and IPv6 routing.         |
+-----------------------+----------------------------------------------+
| DHCP                  | Dynamic Host Control Protocol Client and     |
|                       | Proxy for IPv4 and IPv6 routing.             |
+-----------------------+----------------------------------------------+
| GRE                   | Generic Routing Encapsulation Overlay        |
|                       | Tunnels for IPv4.                            |
+-----------------------+----------------------------------------------+
| IPSec                 | IPSec tunnel and transport modes.            |
+-----------------------+----------------------------------------------+
| IPv4                  | IPv4 routing, RPF, ARP, Proxy ARP, ICMPv4.   |
+-----------------------+----------------------------------------------+
| IPv6                  | IPv6 routing, NS/ND, RA, ICMPv6.             |
+-----------------------+----------------------------------------------+
| L2BD                  | L2 Bridge-Domain switching for untagged      |
|                       | Ethernet, dot1q and dot1ad tagged.           |
+-----------------------+----------------------------------------------+
| L2XC                  | L2 Cross-Connect switching for untagged      |
|                       | Ethernet, dot1q and dot1ad tagged.           |
+-----------------------+----------------------------------------------+
| LISP                  | Locator/ID Separation Protocol overlay       |
|                       | tunnels and locator/id mapping control.      |
+-----------------------+----------------------------------------------+
| QoS Policer Metering  | Ingress packet rate metering and marking for |
|                       | IPv4, IPv6.                                  |
+-----------------------+----------------------------------------------+
| Softwire Tunnels      | IPv4-in-IPv6 softwire tunnels.               |
+-----------------------+----------------------------------------------+
| Tap Interface         | Baseline Linux tap interface tests.          |
+-----------------------+----------------------------------------------+
| IPFIX and SPAN        | Telemetry IPFIX netflow statistics and SPAN  |
|                       | port mirroring.                              |
+-----------------------+----------------------------------------------+
| uRPF Source Security  | Unicast Reverse Path Forwarding security for |
|                       | IPv4 and IPv6 routing.                       |
+-----------------------+----------------------------------------------+
| VLAN Tag Translation  | L2 VLAN tag translation 2to2, 2to1, 1to2,    |
|                       | 1to1.                                        |
+-----------------------+----------------------------------------------+
| VRF Routing           | Multi-context VRF IPVPN routing for IPv4 and |
|                       | IPv6.                                        |
+-----------------------+----------------------------------------------+
| VXLAN                 | VXLAN overlay tunneling for L2-over-IPv4 and |
|                       | -over-IPv6.                                  |
+-----------------------+----------------------------------------------+

Functional Tests Naming
-----------------------

|csit-release| follows a common structured naming convention for all
performance and system functional tests, introduced in CSIT-17.01.

The naming should be intuitive for majority of the tests. Complete
description of CSIT test naming convention is provided on
:ref:`csit_test_naming`.
s[0].r_proposals); sa->childs[0].r_proposals = ikev2_select_proposal (sa->childs[0].i_proposals, IKEV2_PROTOCOL_ESP); } } else { ikev2_set_state (sa, IKEV2_STATE_AUTH_FAILED); } vec_free (psk); vec_free (key_pad); } static void ikev2_sa_auth_init (ikev2_sa_t * sa) { ikev2_main_t *km = &ikev2_main; u8 *authmsg, *key_pad, *psk = 0, *auth = 0; ikev2_sa_transform_t *tr_prf; tr_prf = ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_PRF); /* only shared key and rsa signature */ if (!(sa->i_auth.method == IKEV2_AUTH_METHOD_SHARED_KEY_MIC || sa->i_auth.method == IKEV2_AUTH_METHOD_RSA_SIG)) { clib_warning ("unsupported authentication method %u", sa->i_auth.method); ikev2_set_state (sa, IKEV2_STATE_AUTH_FAILED); return; } key_pad = format (0, "%s", IKEV2_KEY_PAD); authmsg = ikev2_sa_generate_authmsg (sa, 0); psk = ikev2_calc_prf (tr_prf, sa->i_auth.data, key_pad); auth = ikev2_calc_prf (tr_prf, psk, authmsg); if (sa->i_auth.method == IKEV2_AUTH_METHOD_SHARED_KEY_MIC) { sa->i_auth.data = ikev2_calc_prf (tr_prf, psk, authmsg); sa->i_auth.method = IKEV2_AUTH_METHOD_SHARED_KEY_MIC; } else if (sa->i_auth.method == IKEV2_AUTH_METHOD_RSA_SIG) { sa->i_auth.data = ikev2_calc_sign (km->pkey, authmsg); sa->i_auth.method = IKEV2_AUTH_METHOD_RSA_SIG; } vec_free (psk); vec_free (key_pad); vec_free (auth); vec_free (authmsg); } static int ikev2_create_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa, ikev2_child_sa_t * child) { ipsec_add_del_tunnel_args_t a; ikev2_sa_transform_t *tr; ikev2_sa_proposal_t *proposals; u8 encr_type = 0; if (!child->r_proposals) { ikev2_set_state (sa, IKEV2_STATE_NO_PROPOSAL_CHOSEN); return 1; } memset (&a, 0, sizeof (a)); a.is_add = 1; if (sa->is_initiator) { a.local_ip.as_u32 = sa->iaddr.as_u32; a.remote_ip.as_u32 = sa->raddr.as_u32; proposals = child->i_proposals; a.local_spi = child->r_proposals[0].spi; a.remote_spi = child->i_proposals[0].spi; } else { a.local_ip.as_u32 = sa->raddr.as_u32; a.remote_ip.as_u32 = sa->iaddr.as_u32; proposals = child->r_proposals; a.local_spi = child->i_proposals[0].spi; a.remote_spi = child->r_proposals[0].spi; } a.anti_replay = 1; tr = ikev2_sa_get_td_for_type (proposals, IKEV2_TRANSFORM_TYPE_ESN); if (tr) a.esn = tr->esn_type; else a.esn = 0; tr = ikev2_sa_get_td_for_type (proposals, IKEV2_TRANSFORM_TYPE_ENCR); if (tr) { if (tr->encr_type == IKEV2_TRANSFORM_ENCR_TYPE_AES_CBC && tr->key_len) { switch (tr->key_len) { case 16: encr_type = IPSEC_CRYPTO_ALG_AES_CBC_128; break; case 24: encr_type = IPSEC_CRYPTO_ALG_AES_CBC_192; break; case 32: encr_type = IPSEC_CRYPTO_ALG_AES_CBC_256; break; default: ikev2_set_state (sa, IKEV2_STATE_NO_PROPOSAL_CHOSEN); return 1; break; } } else { ikev2_set_state (sa, IKEV2_STATE_NO_PROPOSAL_CHOSEN); return 1; } } else { ikev2_set_state (sa, IKEV2_STATE_NO_PROPOSAL_CHOSEN); return 1; } tr = ikev2_sa_get_td_for_type (proposals, IKEV2_TRANSFORM_TYPE_INTEG); if (tr) { if (tr->integ_type != IKEV2_TRANSFORM_INTEG_TYPE_AUTH_HMAC_SHA1_96) { ikev2_set_state (sa, IKEV2_STATE_NO_PROPOSAL_CHOSEN); return 1; } } else { ikev2_set_state (sa, IKEV2_STATE_NO_PROPOSAL_CHOSEN); return 1; } ikev2_calc_child_keys (sa, child); u8 *loc_ckey, *rem_ckey, *loc_ikey, *rem_ikey; if (sa->is_initiator) { loc_ikey = child->sk_ai; rem_ikey = child->sk_ar; loc_ckey = child->sk_ei; rem_ckey = child->sk_er; } else { loc_ikey = child->sk_ar; rem_ikey = child->sk_ai; loc_ckey = child->sk_er; rem_ckey = child->sk_ei; } a.integ_alg = IPSEC_INTEG_ALG_SHA1_96; a.local_integ_key_len = vec_len (loc_ikey); clib_memcpy (a.local_integ_key, loc_ikey, a.local_integ_key_len); a.remote_integ_key_len = vec_len (rem_ikey); clib_memcpy (a.remote_integ_key, rem_ikey, a.remote_integ_key_len); a.crypto_alg = encr_type; a.local_crypto_key_len = vec_len (loc_ckey); clib_memcpy (a.local_crypto_key, loc_ckey, a.local_crypto_key_len); a.remote_crypto_key_len = vec_len (rem_ckey); clib_memcpy (a.remote_crypto_key, rem_ckey, a.remote_crypto_key_len); if (sa->profile && sa->profile->lifetime) { child->time_to_expiration = vlib_time_now (vnm->vlib_main) + sa->profile->lifetime; if (sa->profile->lifetime_jitter) { // This is not much better than rand(3), which Coverity warns // is unsuitable for security applications; random_u32 is // however fast. If this perturbance to the expiration time // needs to use a better RNG then we may need to use something // like /dev/urandom which has significant overhead. u32 rnd = (u32) (vlib_time_now (vnm->vlib_main) * 1e6); rnd = random_u32 (&rnd); child->time_to_expiration += 1 + (rnd % sa->profile->lifetime_jitter); } } ipsec_add_del_tunnel_if (&a); return 0; } static int ikev2_delete_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa, ikev2_child_sa_t * child) { ipsec_add_del_tunnel_args_t a; if (sa->is_initiator) { if (!vec_len (child->i_proposals)) return 0; a.is_add = 0; a.local_ip.as_u32 = sa->iaddr.as_u32; a.remote_ip.as_u32 = sa->raddr.as_u32; a.local_spi = child->r_proposals[0].spi; a.remote_spi = child->i_proposals[0].spi; } else { if (!vec_len (child->r_proposals)) return 0; a.is_add = 0; a.local_ip.as_u32 = sa->raddr.as_u32; a.remote_ip.as_u32 = sa->iaddr.as_u32; a.local_spi = child->i_proposals[0].spi; a.remote_spi = child->r_proposals[0].spi; } ipsec_add_del_tunnel_if (&a); return 0; } static u32 ikev2_generate_message (ikev2_sa_t * sa, ike_header_t * ike, void *user) { v8 *integ = 0; ike_payload_header_t *ph; u16 plen; u32 tlen = 0; ikev2_sa_transform_t *tr_encr, *tr_integ; tr_encr = ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_ENCR); tr_integ = ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_INTEG); ikev2_payload_chain_t *chain = 0; ikev2_payload_new_chain (chain); if (ike->exchange == IKEV2_EXCHANGE_SA_INIT) { if (sa->r_proposals == 0) { ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_NO_PROPOSAL_CHOSEN, 0); ikev2_set_state (sa, IKEV2_STATE_NOTIFY_AND_DELETE); } else if (sa->dh_group == IKEV2_TRANSFORM_DH_TYPE_NONE) { u8 *data = vec_new (u8, 2); ikev2_sa_transform_t *tr_dh; tr_dh = ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_DH); ASSERT (tr_dh && tr_dh->dh_type); data[0] = (tr_dh->dh_type >> 8) & 0xff; data[1] = (tr_dh->dh_type) & 0xff; ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_INVALID_KE_PAYLOAD, data); vec_free (data); ikev2_set_state (sa, IKEV2_STATE_NOTIFY_AND_DELETE); } else if (sa->state == IKEV2_STATE_NOTIFY_AND_DELETE) { u8 *data = vec_new (u8, 1); data[0] = sa->unsupported_cp; ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_UNSUPPORTED_CRITICAL_PAYLOAD, data); vec_free (data); } else { ike->rspi = clib_host_to_net_u64 (sa->rspi); ikev2_payload_add_sa (chain, sa->r_proposals); ikev2_payload_add_ke (chain, sa->dh_group, sa->r_dh_data); ikev2_payload_add_nonce (chain, sa->r_nonce); } } else if (ike->exchange == IKEV2_EXCHANGE_IKE_AUTH) { if (sa->state == IKEV2_STATE_AUTHENTICATED) { ikev2_payload_add_id (chain, &sa->r_id, IKEV2_PAYLOAD_IDR); ikev2_payload_add_auth (chain, &sa->r_auth); ikev2_payload_add_sa (chain, sa->childs[0].r_proposals); ikev2_payload_add_ts (chain, sa->childs[0].tsi, IKEV2_PAYLOAD_TSI); ikev2_payload_add_ts (chain, sa->childs[0].tsr, IKEV2_PAYLOAD_TSR); } else if (sa->state == IKEV2_STATE_AUTH_FAILED) { ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_AUTHENTICATION_FAILED, 0); ikev2_set_state (sa, IKEV2_STATE_NOTIFY_AND_DELETE); } else if (sa->state == IKEV2_STATE_TS_UNACCEPTABLE) { ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_TS_UNACCEPTABLE, 0); ikev2_payload_add_id (chain, &sa->r_id, IKEV2_PAYLOAD_IDR); ikev2_payload_add_auth (chain, &sa->r_auth); } else if (sa->state == IKEV2_STATE_NO_PROPOSAL_CHOSEN) { ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_NO_PROPOSAL_CHOSEN, 0); ikev2_payload_add_id (chain, &sa->r_id, IKEV2_PAYLOAD_IDR); ikev2_payload_add_auth (chain, &sa->r_auth); ikev2_payload_add_ts (chain, sa->childs[0].tsi, IKEV2_PAYLOAD_TSI); ikev2_payload_add_ts (chain, sa->childs[0].tsr, IKEV2_PAYLOAD_TSR); } else if (sa->state == IKEV2_STATE_NOTIFY_AND_DELETE) { u8 *data = vec_new (u8, 1); data[0] = sa->unsupported_cp; ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_UNSUPPORTED_CRITICAL_PAYLOAD, data); vec_free (data); } else if (sa->state == IKEV2_STATE_SA_INIT) { ikev2_payload_add_id (chain, &sa->i_id, IKEV2_PAYLOAD_IDI); ikev2_payload_add_auth (chain, &sa->i_auth); ikev2_payload_add_sa (chain, sa->childs[0].i_proposals); ikev2_payload_add_ts (chain, sa->childs[0].tsi, IKEV2_PAYLOAD_TSI); ikev2_payload_add_ts (chain, sa->childs[0].tsr, IKEV2_PAYLOAD_TSR); } else { ikev2_set_state (sa, IKEV2_STATE_DELETED); goto done; } } else if (ike->exchange == IKEV2_EXCHANGE_INFORMATIONAL) { /* if pending delete */ if (sa->del) { if (sa->del[0].protocol_id == IKEV2_PROTOCOL_IKE) { if (sa->is_initiator) ikev2_payload_add_delete (chain, sa->del); /* The response to a request that deletes the IKE SA is an empty INFORMATIONAL response. */ ikev2_set_state (sa, IKEV2_STATE_NOTIFY_AND_DELETE); } /* The response to a request that deletes ESP or AH SAs will contain delete payloads for the paired SAs going in the other direction. */ else { ikev2_payload_add_delete (chain, sa->del); } vec_free (sa->del); sa->del = 0; } /* received N(AUTHENTICATION_FAILED) */ else if (sa->state == IKEV2_STATE_AUTH_FAILED) { ikev2_set_state (sa, IKEV2_STATE_DELETED); goto done; } /* received unsupported critical payload */ else if (sa->unsupported_cp) { u8 *data = vec_new (u8, 1); data[0] = sa->unsupported_cp; ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_UNSUPPORTED_CRITICAL_PAYLOAD, data); vec_free (data); sa->unsupported_cp = 0; } /* else send empty response */ } else if (ike->exchange == IKEV2_EXCHANGE_CREATE_CHILD_SA) { if (sa->is_initiator) { ikev2_sa_proposal_t *proposals = (ikev2_sa_proposal_t *) user; ikev2_notify_t notify; u8 *data = vec_new (u8, 4); memset (¬ify, 0, sizeof (notify)); notify.protocol_id = IKEV2_PROTOCOL_ESP; notify.spi = sa->childs[0].i_proposals->spi; *(u32 *) data = clib_host_to_net_u32 (notify.spi); ikev2_payload_add_sa (chain, proposals); ikev2_payload_add_nonce (chain, sa->i_nonce); ikev2_payload_add_ts (chain, sa->childs[0].tsi, IKEV2_PAYLOAD_TSI); ikev2_payload_add_ts (chain, sa->childs[0].tsr, IKEV2_PAYLOAD_TSR); ikev2_payload_add_notify_2 (chain, IKEV2_NOTIFY_MSG_REKEY_SA, data, ¬ify); vec_free (data); } else { if (sa->rekey) { ikev2_payload_add_sa (chain, sa->rekey[0].r_proposal); ikev2_payload_add_nonce (chain, sa->r_nonce); ikev2_payload_add_ts (chain, sa->rekey[0].tsi, IKEV2_PAYLOAD_TSI); ikev2_payload_add_ts (chain, sa->rekey[0].tsr, IKEV2_PAYLOAD_TSR); vec_del1 (sa->rekey, 0); } else if (sa->unsupported_cp) { u8 *data = vec_new (u8, 1); data[0] = sa->unsupported_cp; ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_UNSUPPORTED_CRITICAL_PAYLOAD, data); vec_free (data); sa->unsupported_cp = 0; } else { ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_NO_ADDITIONAL_SAS, 0); } } } /* IKEv2 header */ ike->version = IKE_VERSION_2; ike->nextpayload = IKEV2_PAYLOAD_SK; tlen = sizeof (*ike); if (sa->is_initiator) { ike->flags = IKEV2_HDR_FLAG_INITIATOR; sa->last_init_msg_id = clib_net_to_host_u32 (ike->msgid); } else { ike->flags = IKEV2_HDR_FLAG_RESPONSE; } if (ike->exchange == IKEV2_EXCHANGE_SA_INIT) { tlen += vec_len (chain->data); ike->nextpayload = chain->first_payload_type; ike->length = clib_host_to_net_u32 (tlen); clib_memcpy (ike->payload, chain->data, vec_len (chain->data)); /* store whole IKE payload - needed for PSK auth */ vec_free (sa->last_sa_init_res_packet_data); vec_add (sa->last_sa_init_res_packet_data, ike, tlen); } else { ikev2_payload_chain_add_padding (chain, tr_encr->block_size); /* SK payload */ plen = sizeof (*ph); ph = (ike_payload_header_t *) & ike->payload[0]; ph->nextpayload = chain->first_payload_type; ph->flags = 0; int enc_len = ikev2_encrypt_data (sa, chain->data, ph->payload); plen += enc_len; /* add space for hmac */ plen += tr_integ->key_trunc; tlen += plen; /* payload and total length */ ph->length = clib_host_to_net_u16 (plen); ike->length = clib_host_to_net_u32 (tlen); /* calc integrity data for whole packet except hash itself */ integ = ikev2_calc_integr (tr_integ, sa->is_initiator ? sa->sk_ai : sa->sk_ar, (u8 *) ike, tlen - tr_integ->key_trunc); clib_memcpy (ike->payload + tlen - tr_integ->key_trunc - sizeof (*ike), integ, tr_integ->key_trunc); /* store whole IKE payload - needed for retransmit */ vec_free (sa->last_res_packet_data); vec_add (sa->last_res_packet_data, ike, tlen); } done: ikev2_payload_destroy_chain (chain); vec_free (integ); return tlen; } static int ikev2_retransmit_sa_init (ike_header_t * ike, ip4_address_t iaddr, ip4_address_t raddr) { ikev2_main_t *km = &ikev2_main; ikev2_sa_t *sa; u32 thread_index = vlib_get_thread_index (); /* *INDENT-OFF* */ pool_foreach (sa, km->per_thread_data[thread_index].sas, ({ if (sa->ispi == clib_net_to_host_u64(ike->ispi) && sa->iaddr.as_u32 == iaddr.as_u32 && sa->raddr.as_u32 == raddr.as_u32) { int p = 0; u32 len = clib_net_to_host_u32(ike->length); u8 payload = ike->nextpayload; while (p < len && payload!= IKEV2_PAYLOAD_NONE) { ike_payload_header_t * ikep = (ike_payload_header_t *) &ike->payload[p]; u32 plen = clib_net_to_host_u16(ikep->length); if (plen < sizeof(ike_payload_header_t)) return -1; if (payload == IKEV2_PAYLOAD_NONCE) { if (!memcmp(sa->i_nonce, ikep->payload, plen - sizeof(*ikep))) { /* req is retransmit */ if (sa->state == IKEV2_STATE_SA_INIT) { ike_header_t * tmp; tmp = (ike_header_t*)sa->last_sa_init_res_packet_data; ike->ispi = tmp->ispi; ike->rspi = tmp->rspi; ike->nextpayload = tmp->nextpayload; ike->version = tmp->version; ike->exchange = tmp->exchange; ike->flags = tmp->flags; ike->msgid = tmp->msgid; ike->length = tmp->length; clib_memcpy(ike->payload, tmp->payload, clib_net_to_host_u32(tmp->length) - sizeof(*ike)); clib_warning("IKE_SA_INIT retransmit from %U to %U", format_ip4_address, &raddr, format_ip4_address, &iaddr); return 1; } /* else ignore req */ else { clib_warning("IKE_SA_INIT ignore from %U to %U", format_ip4_address, &raddr, format_ip4_address, &iaddr); return -1; } } } payload = ikep->nextpayload; p+=plen; } } })); /* *INDENT-ON* */ /* req is not retransmit */ return 0; } static int ikev2_retransmit_resp (ikev2_sa_t * sa, ike_header_t * ike) { u32 msg_id = clib_net_to_host_u32 (ike->msgid); /* new req */ if (msg_id > sa->last_msg_id) { sa->last_msg_id = msg_id; return 0; } /* retransmitted req */ else if (msg_id == sa->last_msg_id) { ike_header_t *tmp; tmp = (ike_header_t *) sa->last_res_packet_data; ike->ispi = tmp->ispi; ike->rspi = tmp->rspi; ike->nextpayload = tmp->nextpayload; ike->version = tmp->version; ike->exchange = tmp->exchange; ike->flags = tmp->flags; ike->msgid = tmp->msgid; ike->length = tmp->length; clib_memcpy (ike->payload, tmp->payload, clib_net_to_host_u32 (tmp->length) - sizeof (*ike)); clib_warning ("IKE msgid %u retransmit from %U to %U", msg_id, format_ip4_address, &sa->raddr, format_ip4_address, &sa->iaddr); return 1; } /* old req ignore */ else { clib_warning ("IKE msgid %u req ignore from %U to %U", msg_id, format_ip4_address, &sa->raddr, format_ip4_address, &sa->iaddr); return -1; } } static uword ikev2_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { u32 n_left_from, *from, *to_next; ikev2_next_t next_index; ikev2_main_t *km = &ikev2_main; u32 thread_index = vlib_get_thread_index (); from = vlib_frame_vector_args (frame); n_left_from = frame->n_vectors; next_index = node->cached_next_index; while (n_left_from > 0) { u32 n_left_to_next; vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); while (n_left_from > 0 && n_left_to_next > 0) { u32 bi0; vlib_buffer_t *b0; u32 next0 = IKEV2_NEXT_ERROR_DROP; u32 sw_if_index0; ip4_header_t *ip40; udp_header_t *udp0; ike_header_t *ike0; ikev2_sa_t *sa0 = 0; ikev2_sa_t sa; /* temporary store for SA */ int len = 0; int r; /* speculatively enqueue b0 to the current next frame */ 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); ike0 = vlib_buffer_get_current (b0); vlib_buffer_advance (b0, -sizeof (*udp0)); udp0 = vlib_buffer_get_current (b0); vlib_buffer_advance (b0, -sizeof (*ip40)); ip40 = vlib_buffer_get_current (b0); if (ike0->version != IKE_VERSION_2) { vlib_node_increment_counter (vm, ikev2_node.index, IKEV2_ERROR_NOT_IKEV2, 1); goto dispatch0; } if (ike0->exchange == IKEV2_EXCHANGE_SA_INIT) { sa0 = &sa; memset (sa0, 0, sizeof (*sa0)); if (ike0->flags & IKEV2_HDR_FLAG_INITIATOR) { if (ike0->rspi == 0) { sa0->raddr.as_u32 = ip40->dst_address.as_u32; sa0->iaddr.as_u32 = ip40->src_address.as_u32; r = ikev2_retransmit_sa_init (ike0, sa0->iaddr, sa0->raddr); if (r == 1) { vlib_node_increment_counter (vm, ikev2_node.index, IKEV2_ERROR_IKE_SA_INIT_RETRANSMIT, 1); len = clib_net_to_host_u32 (ike0->length); goto dispatch0; } else if (r == -1) { vlib_node_increment_counter (vm, ikev2_node.index, IKEV2_ERROR_IKE_SA_INIT_IGNORE, 1); goto dispatch0; } ikev2_process_sa_init_req (vm, sa0, ike0); if (sa0->state == IKEV2_STATE_SA_INIT) { ikev2_sa_free_proposal_vector (&sa0->r_proposals); sa0->r_proposals = ikev2_select_proposal (sa0->i_proposals, IKEV2_PROTOCOL_IKE); ikev2_generate_sa_init_data (sa0); } if (sa0->state == IKEV2_STATE_SA_INIT || sa0->state == IKEV2_STATE_NOTIFY_AND_DELETE) { len = ikev2_generate_message (sa0, ike0, 0); } if (sa0->state == IKEV2_STATE_SA_INIT) { /* add SA to the pool */ pool_get (km->per_thread_data[thread_index].sas, sa0); clib_memcpy (sa0, &sa, sizeof (*sa0)); hash_set (km-> per_thread_data[thread_index].sa_by_rspi, sa0->rspi, sa0 - km->per_thread_data[thread_index].sas); } else { ikev2_sa_free_all_vec (sa0); } } } else { ikev2_process_sa_init_resp (vm, sa0, ike0); if (sa0->state == IKEV2_STATE_SA_INIT) { ike0->exchange = IKEV2_EXCHANGE_IKE_AUTH; uword *p = hash_get (km->sa_by_ispi, ike0->ispi); if (p) { ikev2_sa_t *sai = pool_elt_at_index (km->sais, p[0]); ikev2_complete_sa_data (sa0, sai); ikev2_calc_keys (sa0); ikev2_sa_auth_init (sa0); len = ikev2_generate_message (sa0, ike0, 0); } } if (sa0->state == IKEV2_STATE_SA_INIT) { /* add SA to the pool */ pool_get (km->per_thread_data[thread_index].sas, sa0); clib_memcpy (sa0, &sa, sizeof (*sa0)); hash_set (km->per_thread_data[thread_index].sa_by_rspi, sa0->rspi, sa0 - km->per_thread_data[thread_index].sas); } else { ikev2_sa_free_all_vec (sa0); } } } else if (ike0->exchange == IKEV2_EXCHANGE_IKE_AUTH) { uword *p; p = hash_get (km->per_thread_data[thread_index].sa_by_rspi, clib_net_to_host_u64 (ike0->rspi)); if (p) { sa0 = pool_elt_at_index (km->per_thread_data[thread_index].sas, p[0]); r = ikev2_retransmit_resp (sa0, ike0); if (r == 1) { vlib_node_increment_counter (vm, ikev2_node.index, IKEV2_ERROR_IKE_REQ_RETRANSMIT, 1); len = clib_net_to_host_u32 (ike0->length); goto dispatch0; } else if (r == -1) { vlib_node_increment_counter (vm, ikev2_node.index, IKEV2_ERROR_IKE_REQ_IGNORE, 1); goto dispatch0; } ikev2_process_auth_req (vm, sa0, ike0); ikev2_sa_auth (sa0); if (sa0->state == IKEV2_STATE_AUTHENTICATED) { ikev2_initial_contact_cleanup (sa0); ikev2_sa_match_ts (sa0); if (sa0->state != IKEV2_STATE_TS_UNACCEPTABLE) ikev2_create_tunnel_interface (km->vnet_main, sa0, &sa0->childs[0]); } if (sa0->is_initiator) { uword *p = hash_get (km->sa_by_ispi, ike0->ispi); if (p) { ikev2_sa_t *sai = pool_elt_at_index (km->sais, p[0]); hash_unset (km->sa_by_ispi, sai->ispi); ikev2_sa_free_all_vec (sai); pool_put (km->sais, sai); } } else { len = ikev2_generate_message (sa0, ike0, 0); } } } else if (ike0->exchange == IKEV2_EXCHANGE_INFORMATIONAL) { uword *p; p = hash_get (km->per_thread_data[thread_index].sa_by_rspi, clib_net_to_host_u64 (ike0->rspi)); if (p) { sa0 = pool_elt_at_index (km->per_thread_data[thread_index].sas, p[0]); r = ikev2_retransmit_resp (sa0, ike0); if (r == 1) { vlib_node_increment_counter (vm, ikev2_node.index, IKEV2_ERROR_IKE_REQ_RETRANSMIT, 1); len = clib_net_to_host_u32 (ike0->length); goto dispatch0; } else if (r == -1) { vlib_node_increment_counter (vm, ikev2_node.index, IKEV2_ERROR_IKE_REQ_IGNORE, 1); goto dispatch0; } ikev2_process_informational_req (vm, sa0, ike0); if (sa0->del) { if (sa0->del[0].protocol_id != IKEV2_PROTOCOL_IKE) { ikev2_delete_t *d, *tmp, *resp = 0; vec_foreach (d, sa0->del) { ikev2_child_sa_t *ch_sa; ch_sa = ikev2_sa_get_child (sa0, d->spi, d->protocol_id, !sa0->is_initiator); if (ch_sa) { ikev2_delete_tunnel_interface (km->vnet_main, sa0, ch_sa); if (!sa0->is_initiator) { vec_add2 (resp, tmp, 1); tmp->protocol_id = d->protocol_id; tmp->spi = ch_sa->r_proposals[0].spi; } ikev2_sa_del_child_sa (sa0, ch_sa); } } if (!sa0->is_initiator) { vec_free (sa0->del); sa0->del = resp; } } } if (!sa0->is_initiator) { len = ikev2_generate_message (sa0, ike0, 0); } } } else if (ike0->exchange == IKEV2_EXCHANGE_CREATE_CHILD_SA) { uword *p; p = hash_get (km->per_thread_data[thread_index].sa_by_rspi, clib_net_to_host_u64 (ike0->rspi)); if (p) { sa0 = pool_elt_at_index (km->per_thread_data[thread_index].sas, p[0]); r = ikev2_retransmit_resp (sa0, ike0); if (r == 1) { vlib_node_increment_counter (vm, ikev2_node.index, IKEV2_ERROR_IKE_REQ_RETRANSMIT, 1); len = clib_net_to_host_u32 (ike0->length); goto dispatch0; } else if (r == -1) { vlib_node_increment_counter (vm, ikev2_node.index, IKEV2_ERROR_IKE_REQ_IGNORE, 1); goto dispatch0; } ikev2_process_create_child_sa_req (vm, sa0, ike0); if (sa0->rekey) { if (sa0->rekey[0].protocol_id != IKEV2_PROTOCOL_IKE) { ikev2_child_sa_t *child; vec_add2 (sa0->childs, child, 1); child->r_proposals = sa0->rekey[0].r_proposal; child->i_proposals = sa0->rekey[0].i_proposal; child->tsi = sa0->rekey[0].tsi; child->tsr = sa0->rekey[0].tsr; ikev2_create_tunnel_interface (km->vnet_main, sa0, child); } if (sa0->is_initiator) { vec_del1 (sa0->rekey, 0); } else { len = ikev2_generate_message (sa0, ike0, 0); } } } } else { clib_warning ("IKEv2 exchange %u packet received from %U to %U", ike0->exchange, format_ip4_address, ip40->src_address.as_u8, format_ip4_address, ip40->dst_address.as_u8); } dispatch0: /* if we are sending packet back, rewrite headers */ if (len) { next0 = IKEV2_NEXT_IP4_LOOKUP; if (sa0->is_initiator) { ip40->dst_address.as_u32 = sa0->raddr.as_u32; ip40->src_address.as_u32 = sa0->iaddr.as_u32; } else { ip40->dst_address.as_u32 = sa0->iaddr.as_u32; ip40->src_address.as_u32 = sa0->raddr.as_u32; } udp0->length = clib_host_to_net_u16 (len + sizeof (udp_header_t)); udp0->checksum = 0; b0->current_length = len + sizeof (ip4_header_t) + sizeof (udp_header_t); ip40->length = clib_host_to_net_u16 (b0->current_length); ip40->checksum = ip4_header_checksum (ip40); } /* delete sa */ if (sa0 && (sa0->state == IKEV2_STATE_DELETED || sa0->state == IKEV2_STATE_NOTIFY_AND_DELETE)) { ikev2_child_sa_t *c; vec_foreach (c, sa0->childs) ikev2_delete_tunnel_interface (km->vnet_main, sa0, c); ikev2_delete_sa (sa0); } sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && (b0->flags & VLIB_BUFFER_IS_TRACED))) { ikev2_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); t->sw_if_index = sw_if_index0; t->next_index = next0; } 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, ikev2_node.index, IKEV2_ERROR_PROCESSED, frame->n_vectors); return frame->n_vectors; } /* *INDENT-OFF* */ VLIB_REGISTER_NODE (ikev2_node,static) = { .function = ikev2_node_fn, .name = "ikev2", .vector_size = sizeof (u32), .format_trace = format_ikev2_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = ARRAY_LEN(ikev2_error_strings), .error_strings = ikev2_error_strings, .n_next_nodes = IKEV2_N_NEXT, .next_nodes = { [IKEV2_NEXT_IP4_LOOKUP] = "ip4-lookup", [IKEV2_NEXT_ERROR_DROP] = "error-drop", }, }; /* *INDENT-ON* */ static clib_error_t * ikev2_set_initiator_proposals (vlib_main_t * vm, ikev2_sa_t * sa, ikev2_transforms_set * ts, ikev2_sa_proposal_t ** proposals, int is_ike) { clib_error_t *r; ikev2_main_t *km = &ikev2_main; ikev2_sa_proposal_t *proposal; vec_add2 (*proposals, proposal, 1); ikev2_sa_transform_t *td; int error; /* Encryption */ error = 1; vec_foreach (td, km->supported_transforms) { if (td->type == IKEV2_TRANSFORM_TYPE_ENCR && td->encr_type == IKEV2_TRANSFORM_ENCR_TYPE_AES_CBC && td->key_len == ts->crypto_key_size / 8) { u16 attr[2]; attr[0] = clib_host_to_net_u16 (14 | (1 << 15)); attr[1] = clib_host_to_net_u16 (td->key_len << 3); vec_add (td->attrs, (u8 *) attr, 4); vec_add1 (proposal->transforms, *td); td->attrs = 0; error = 0; break; } } if (error) { r = clib_error_return (0, "Unsupported algorithm"); return r; } /* Integrity */ error = 1; vec_foreach (td, km->supported_transforms) { if (td->type == IKEV2_TRANSFORM_TYPE_INTEG && td->integ_type == IKEV2_TRANSFORM_INTEG_TYPE_AUTH_HMAC_SHA1_96) { vec_add1 (proposal->transforms, *td); error = 0; break; } } if (error) { r = clib_error_return (0, "Unsupported algorithm"); return r; } /* PRF */ if (is_ike) { error = 1; vec_foreach (td, km->supported_transforms) { if (td->type == IKEV2_TRANSFORM_TYPE_PRF && td->prf_type == IKEV2_TRANSFORM_PRF_TYPE_PRF_HMAC_SHA1) { vec_add1 (proposal->transforms, *td); error = 0; break; } } if (error) { r = clib_error_return (0, "Unsupported algorithm"); return r; } } /* DH */ error = 1; vec_foreach (td, km->supported_transforms) { if (td->type == IKEV2_TRANSFORM_TYPE_DH && td->dh_type == ts->dh_type) { vec_add1 (proposal->transforms, *td); if (is_ike) { sa->dh_group = td->dh_type; } error = 0; break; } } if (error) { r = clib_error_return (0, "Unsupported algorithm"); return r; } if (!is_ike) { error = 1; vec_foreach (td, km->supported_transforms) { if (td->type == IKEV2_TRANSFORM_TYPE_ESN) { vec_add1 (proposal->transforms, *td); error = 0; break; } } if (error) { r = clib_error_return (0, "Unsupported algorithm"); return r; } } return 0; } static ikev2_profile_t * ikev2_profile_index_by_name (u8 * name) { ikev2_main_t *km = &ikev2_main; uword *p; p = mhash_get (&km->profile_index_by_name, name); if (!p) return 0; return pool_elt_at_index (km->profiles, p[0]); } static void ikev2_send_ike (vlib_main_t * vm, ip4_address_t * src, ip4_address_t * dst, u32 bi0, u32 len) { ip4_header_t *ip40; udp_header_t *udp0; vlib_buffer_t *b0; vlib_frame_t *f; u32 *to_next; b0 = vlib_get_buffer (vm, bi0); vlib_buffer_advance (b0, -sizeof (udp_header_t)); udp0 = vlib_buffer_get_current (b0); vlib_buffer_advance (b0, -sizeof (ip4_header_t)); ip40 = vlib_buffer_get_current (b0); ip40->ip_version_and_header_length = 0x45; ip40->tos = 0; ip40->fragment_id = 0; ip40->flags_and_fragment_offset = 0; ip40->ttl = 0xff; ip40->protocol = IP_PROTOCOL_UDP; ip40->dst_address.as_u32 = dst->as_u32; ip40->src_address.as_u32 = src->as_u32; udp0->dst_port = clib_host_to_net_u16 (500); udp0->src_port = clib_host_to_net_u16 (500); udp0->length = clib_host_to_net_u16 (len + sizeof (udp_header_t)); udp0->checksum = 0; b0->current_length = len + sizeof (ip4_header_t) + sizeof (udp_header_t); ip40->length = clib_host_to_net_u16 (b0->current_length); ip40->checksum = ip4_header_checksum (ip40); /* send the request */ f = vlib_get_frame_to_node (vm, ip4_lookup_node.index); to_next = vlib_frame_vector_args (f); to_next[0] = bi0; f->n_vectors = 1; vlib_put_frame_to_node (vm, ip4_lookup_node.index, f); } static u32 ikev2_get_new_ike_header_buff (vlib_main_t * vm, ike_header_t ** ike) { u32 bi0; if (vlib_buffer_alloc (vm, &bi0, 1) != 1) { *ike = 0; return 0; } vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0); *ike = vlib_buffer_get_current (b0); return bi0; } clib_error_t * ikev2_set_local_key (vlib_main_t * vm, u8 * file) { ikev2_main_t *km = &ikev2_main; km->pkey = ikev2_load_key_file (file); if (km->pkey == NULL) return clib_error_return (0, "load key '%s' failed", file); return 0; } clib_error_t * ikev2_add_del_profile (vlib_main_t * vm, u8 * name, int is_add) { ikev2_main_t *km = &ikev2_main; ikev2_profile_t *p; if (is_add) { if (ikev2_profile_index_by_name (name)) return clib_error_return (0, "policy %v already exists", name); pool_get (km->profiles, p); memset (p, 0, sizeof (*p)); p->name = vec_dup (name); p->responder.sw_if_index = ~0; uword index = p - km->profiles; mhash_set_mem (&km->profile_index_by_name, name, &index, 0); } else { p = ikev2_profile_index_by_name (name); if (!p) return clib_error_return (0, "policy %v does not exists", name); vec_free (p->name); pool_put (km->profiles, p); mhash_unset (&km->profile_index_by_name, name, 0); } return 0; } clib_error_t * ikev2_set_profile_auth (vlib_main_t * vm, u8 * name, u8 auth_method, u8 * auth_data, u8 data_hex_format) { ikev2_profile_t *p; clib_error_t *r; p = ikev2_profile_index_by_name (name); if (!p) { r = clib_error_return (0, "unknown profile %v", name); return r; } vec_free (p->auth.data); p->auth.method = auth_method; p->auth.data = vec_dup (auth_data); p->auth.hex = data_hex_format; if (auth_method == IKEV2_AUTH_METHOD_RSA_SIG) { vec_add1 (p->auth.data, 0); if (p->auth.key) EVP_PKEY_free (p->auth.key); p->auth.key = ikev2_load_cert_file (auth_data); if (p->auth.key == NULL) return clib_error_return (0, "load cert '%s' failed", auth_data); } return 0; } clib_error_t * ikev2_set_profile_id (vlib_main_t * vm, u8 * name, u8 id_type, u8 * data, int is_local) { ikev2_profile_t *p; clib_error_t *r; if (id_type > IKEV2_ID_TYPE_ID_RFC822_ADDR && id_type < IKEV2_ID_TYPE_ID_KEY_ID) { r = clib_error_return (0, "unsupported identity type %U", format_ikev2_id_type, id_type); return r; } p = ikev2_profile_index_by_name (name); if (!p) { r = clib_error_return (0, "unknown profile %v", name); return r; } if (is_local) { vec_free (p->loc_id.data); p->loc_id.type = id_type; p->loc_id.data = vec_dup (data); } else { vec_free (p->rem_id.data); p->rem_id.type = id_type; p->rem_id.data = vec_dup (data); } return 0; } clib_error_t * ikev2_set_profile_ts (vlib_main_t * vm, u8 * name, u8 protocol_id, u16 start_port, u16 end_port, ip4_address_t start_addr, ip4_address_t end_addr, int is_local) { ikev2_profile_t *p; clib_error_t *r; p = ikev2_profile_index_by_name (name); if (!p) { r = clib_error_return (0, "unknown profile %v", name); return r; } if (is_local) { p->loc_ts.start_addr.as_u32 = start_addr.as_u32; p->loc_ts.end_addr.as_u32 = end_addr.as_u32; p->loc_ts.start_port = start_port; p->loc_ts.end_port = end_port; p->loc_ts.protocol_id = protocol_id; p->loc_ts.ts_type = 7; } else { p->rem_ts.start_addr.as_u32 = start_addr.as_u32; p->rem_ts.end_addr.as_u32 = end_addr.as_u32; p->rem_ts.start_port = start_port; p->rem_ts.end_port = end_port; p->rem_ts.protocol_id = protocol_id; p->rem_ts.ts_type = 7; } return 0; } clib_error_t * ikev2_set_profile_responder (vlib_main_t * vm, u8 * name, u32 sw_if_index, ip4_address_t ip4) { ikev2_profile_t *p; clib_error_t *r; p = ikev2_profile_index_by_name (name); if (!p) { r = clib_error_return (0, "unknown profile %v", name); return r; } p->responder.sw_if_index = sw_if_index; p->responder.ip4 = ip4; return 0; } clib_error_t * ikev2_set_profile_ike_transforms (vlib_main_t * vm, u8 * name, ikev2_transform_encr_type_t crypto_alg, ikev2_transform_integ_type_t integ_alg, ikev2_transform_dh_type_t dh_type, u32 crypto_key_size) { ikev2_profile_t *p; clib_error_t *r; p = ikev2_profile_index_by_name (name); if (!p) { r = clib_error_return (0, "unknown profile %v", name); return r; } p->ike_ts.crypto_alg = crypto_alg; p->ike_ts.integ_alg = integ_alg; p->ike_ts.dh_type = dh_type; p->ike_ts.crypto_key_size = crypto_key_size; return 0; } clib_error_t * ikev2_set_profile_esp_transforms (vlib_main_t * vm, u8 * name, ikev2_transform_encr_type_t crypto_alg, ikev2_transform_integ_type_t integ_alg, ikev2_transform_dh_type_t dh_type, u32 crypto_key_size) { ikev2_profile_t *p; clib_error_t *r; p = ikev2_profile_index_by_name (name); if (!p) { r = clib_error_return (0, "unknown profile %v", name); return r; } p->esp_ts.crypto_alg = crypto_alg; p->esp_ts.integ_alg = integ_alg; p->esp_ts.dh_type = dh_type; p->esp_ts.crypto_key_size = crypto_key_size; return 0; } clib_error_t * ikev2_set_profile_sa_lifetime (vlib_main_t * vm, u8 * name, u64 lifetime, u32 jitter, u32 handover, u64 maxdata) { ikev2_profile_t *p; clib_error_t *r; p = ikev2_profile_index_by_name (name); if (!p) { r = clib_error_return (0, "unknown profile %v", name); return r; } p->lifetime = lifetime; p->lifetime_jitter = jitter; p->handover = handover; p->lifetime_maxdata = maxdata; return 0; } clib_error_t * ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name) { ikev2_profile_t *p; clib_error_t *r; ip4_main_t *im = &ip4_main; ikev2_main_t *km = &ikev2_main; p = ikev2_profile_index_by_name (name); if (!p) { r = clib_error_return (0, "unknown profile %v", name); return r; } if (p->responder.sw_if_index == ~0 || p->responder.ip4.data_u32 == 0) { r = clib_error_return (0, "responder not set for profile %v", name); return r; } /* Create the Initiator Request */ { ike_header_t *ike0; u32 bi0 = 0; ip_lookup_main_t *lm = &im->lookup_main; u32 if_add_index0; int len = sizeof (ike_header_t); /* Get own iface IP */ if_add_index0 = lm->if_address_pool_index_by_sw_if_index[p->responder.sw_if_index]; ip_interface_address_t *if_add = pool_elt_at_index (lm->if_address_pool, if_add_index0); ip4_address_t *if_ip = ip_interface_address_get_address (lm, if_add); bi0 = ikev2_get_new_ike_header_buff (vm, &ike0); /* Prepare the SA and the IKE payload */ ikev2_sa_t sa; memset (&sa, 0, sizeof (ikev2_sa_t)); ikev2_payload_chain_t *chain = 0; ikev2_payload_new_chain (chain); /* Build the IKE proposal payload */ ikev2_sa_proposal_t *proposals = 0; ikev2_set_initiator_proposals (vm, &sa, &p->ike_ts, &proposals, 1); proposals[0].proposal_num = 1; proposals[0].protocol_id = IKEV2_PROTOCOL_IKE; /* Add and then cleanup proposal data */ ikev2_payload_add_sa (chain, proposals); ikev2_sa_free_proposal_vector (&proposals); sa.is_initiator = 1; sa.profile = p; sa.state = IKEV2_STATE_SA_INIT; ikev2_generate_sa_init_data (&sa); ikev2_payload_add_ke (chain, sa.dh_group, sa.i_dh_data); ikev2_payload_add_nonce (chain, sa.i_nonce); /* Build the child SA proposal */ vec_resize (sa.childs, 1); ikev2_set_initiator_proposals (vm, &sa, &p->esp_ts, &sa.childs[0].i_proposals, 0); sa.childs[0].i_proposals[0].proposal_num = 1; sa.childs[0].i_proposals[0].protocol_id = IKEV2_PROTOCOL_ESP; RAND_bytes ((u8 *) & sa.childs[0].i_proposals[0].spi, sizeof (sa.childs[0].i_proposals[0].spi)); /* Add NAT detection notification messages (mandatory) */ u8 nat_detection_source[8 + 8 + 4 + 2]; u8 *nat_detection_sha1 = vec_new (u8, 20); u64 tmpspi = clib_host_to_net_u64 (sa.ispi); clib_memcpy (&nat_detection_source[0], &tmpspi, sizeof (tmpspi)); tmpspi = clib_host_to_net_u64 (sa.rspi); clib_memcpy (&nat_detection_source[8], &tmpspi, sizeof (tmpspi)); u16 tmpport = clib_host_to_net_u16 (500); clib_memcpy (&nat_detection_source[8 + 8 + 4], &tmpport, sizeof (tmpport)); u32 tmpip = clib_host_to_net_u32 (if_ip->as_u32); clib_memcpy (&nat_detection_source[8 + 8], &tmpip, sizeof (tmpip)); SHA1 (nat_detection_source, sizeof (nat_detection_source), nat_detection_sha1); ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_NAT_DETECTION_SOURCE_IP, nat_detection_sha1); tmpip = clib_host_to_net_u32 (p->responder.ip4.as_u32); clib_memcpy (&nat_detection_source[8 + 8], &tmpip, sizeof (tmpip)); SHA1 (nat_detection_source, sizeof (nat_detection_source), nat_detection_sha1); ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_NAT_DETECTION_DESTINATION_IP, nat_detection_sha1); vec_free (nat_detection_sha1); u8 *sig_hash_algo = vec_new (u8, 8); u64 tmpsig = clib_host_to_net_u64 (0x0001000200030004); clib_memcpy (sig_hash_algo, &tmpsig, sizeof (tmpsig)); ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_SIGNATURE_HASH_ALGORITHMS, sig_hash_algo); vec_free (sig_hash_algo); /* Buffer update and bolierplate */ len += vec_len (chain->data); ike0->nextpayload = chain->first_payload_type; ike0->length = clib_host_to_net_u32 (len); clib_memcpy (ike0->payload, chain->data, vec_len (chain->data)); ikev2_payload_destroy_chain (chain); ike0->version = IKE_VERSION_2; ike0->flags = IKEV2_HDR_FLAG_INITIATOR; ike0->exchange = IKEV2_EXCHANGE_SA_INIT; ike0->ispi = sa.ispi; /* store whole IKE payload - needed for PSK auth */ vec_free (sa.last_sa_init_req_packet_data); vec_add (sa.last_sa_init_req_packet_data, ike0, len); /* add data to the SA then add it to the pool */ sa.iaddr.as_u32 = if_ip->as_u32; sa.raddr.as_u32 = p->responder.ip4.as_u32; sa.i_id.type = p->loc_id.type; sa.i_id.data = vec_dup (p->loc_id.data); sa.i_auth.method = p->auth.method; sa.i_auth.hex = p->auth.hex; sa.i_auth.data = vec_dup (p->auth.data); #if OPENSSL_VERSION_NUMBER >= 0x10100000L clib_memcpy (sa.i_auth.key, p->auth.key, EVP_PKEY_size (p->auth.key)); #else sa.i_auth.key = vec_dup (p->auth.key); #endif vec_add (sa.childs[0].tsi, &p->loc_ts, 1); vec_add (sa.childs[0].tsr, &p->rem_ts, 1); /* add SA to the pool */ ikev2_sa_t *sa0 = 0; pool_get (km->sais, sa0); clib_memcpy (sa0, &sa, sizeof (*sa0)); hash_set (km->sa_by_ispi, sa0->ispi, sa0 - km->sais); ikev2_send_ike (vm, if_ip, &p->responder.ip4, bi0, len); } return 0; } static void ikev2_delete_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa, ikev2_child_sa_t * csa) { /* Create the Initiator notification for child SA removal */ ikev2_main_t *km = &ikev2_main; ike_header_t *ike0; u32 bi0 = 0; int len; bi0 = ikev2_get_new_ike_header_buff (vm, &ike0); ike0->exchange = IKEV2_EXCHANGE_INFORMATIONAL; ike0->ispi = clib_host_to_net_u64 (sa->ispi); ike0->rspi = clib_host_to_net_u64 (sa->rspi); vec_resize (sa->del, 1); sa->del->protocol_id = IKEV2_PROTOCOL_ESP; sa->del->spi = csa->i_proposals->spi; ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id + 1); sa->last_init_msg_id = clib_net_to_host_u32 (ike0->msgid); len = ikev2_generate_message (sa, ike0, 0); ikev2_send_ike (vm, &sa->iaddr, &sa->raddr, bi0, len); /* delete local child SA */ ikev2_delete_tunnel_interface (km->vnet_main, sa, csa); ikev2_sa_del_child_sa (sa, csa); } clib_error_t * ikev2_initiate_delete_child_sa (vlib_main_t * vm, u32 ispi) { clib_error_t *r; ikev2_main_t *km = &ikev2_main; ikev2_main_per_thread_data_t *tkm; ikev2_sa_t *fsa = 0; ikev2_child_sa_t *fchild = 0; /* Search for the child SA */ vec_foreach (tkm, km->per_thread_data) { ikev2_sa_t *sa; if (fchild) break; /* *INDENT-OFF* */ pool_foreach (sa, tkm->sas, ({ fchild = ikev2_sa_get_child(sa, ispi, IKEV2_PROTOCOL_ESP, 1); if (fchild) { fsa = sa; break; } })); /* *INDENT-ON* */ } if (!fchild || !fsa) { r = clib_error_return (0, "Child SA not found"); return r; } else { ikev2_delete_child_sa_internal (vm, fsa, fchild); } return 0; } clib_error_t * ikev2_initiate_delete_ike_sa (vlib_main_t * vm, u64 ispi) { clib_error_t *r; ikev2_main_t *km = &ikev2_main; ikev2_main_per_thread_data_t *tkm; ikev2_sa_t *fsa = 0; ikev2_main_per_thread_data_t *ftkm = 0; /* Search for the IKE SA */ vec_foreach (tkm, km->per_thread_data) { ikev2_sa_t *sa; if (fsa) break; /* *INDENT-OFF* */ pool_foreach (sa, tkm->sas, ({ if (sa->ispi == ispi) { fsa = sa; ftkm = tkm; break; } })); /* *INDENT-ON* */ } if (!fsa) { r = clib_error_return (0, "IKE SA not found"); return r; } /* Create the Initiator notification for IKE SA removal */ { ike_header_t *ike0; u32 bi0 = 0; int len; bi0 = ikev2_get_new_ike_header_buff (vm, &ike0); ike0->exchange = IKEV2_EXCHANGE_INFORMATIONAL; ike0->ispi = clib_host_to_net_u64 (fsa->ispi); ike0->rspi = clib_host_to_net_u64 (fsa->rspi); vec_resize (fsa->del, 1); fsa->del->protocol_id = IKEV2_PROTOCOL_IKE; fsa->del->spi = ispi; ike0->msgid = clib_host_to_net_u32 (fsa->last_init_msg_id + 1); fsa->last_init_msg_id = clib_net_to_host_u32 (ike0->msgid); len = ikev2_generate_message (fsa, ike0, 0); ikev2_send_ike (vm, &fsa->iaddr, &fsa->raddr, bi0, len); } /* delete local SA */ ikev2_child_sa_t *c; vec_foreach (c, fsa->childs) { ikev2_delete_tunnel_interface (km->vnet_main, fsa, c); ikev2_sa_del_child_sa (fsa, c); } ikev2_sa_free_all_vec (fsa); uword *p = hash_get (ftkm->sa_by_rspi, fsa->rspi); if (p) { hash_unset (ftkm->sa_by_rspi, fsa->rspi); pool_put (ftkm->sas, fsa); } return 0; } static void ikev2_rekey_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa, ikev2_child_sa_t * csa) { /* Create the Initiator request for create child SA */ ike_header_t *ike0; u32 bi0 = 0; int len; bi0 = ikev2_get_new_ike_header_buff (vm, &ike0); ike0->version = IKE_VERSION_2; ike0->flags = IKEV2_HDR_FLAG_INITIATOR; ike0->exchange = IKEV2_EXCHANGE_CREATE_CHILD_SA; ike0->ispi = clib_host_to_net_u64 (sa->ispi); ike0->rspi = clib_host_to_net_u64 (sa->rspi); ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id + 1); sa->last_init_msg_id = clib_net_to_host_u32 (ike0->msgid); ikev2_rekey_t *rekey; vec_add2 (sa->rekey, rekey, 1); ikev2_sa_proposal_t *proposals = vec_dup (csa->i_proposals); /*need new ispi */ RAND_bytes ((u8 *) & proposals[0].spi, sizeof (proposals[0].spi)); rekey->spi = proposals[0].spi; rekey->ispi = csa->i_proposals->spi; len = ikev2_generate_message (sa, ike0, proposals); ikev2_send_ike (vm, &sa->iaddr, &sa->raddr, bi0, len); vec_free (proposals); } clib_error_t * ikev2_initiate_rekey_child_sa (vlib_main_t * vm, u32 ispi) { clib_error_t *r; ikev2_main_t *km = &ikev2_main; ikev2_main_per_thread_data_t *tkm; ikev2_sa_t *fsa = 0; ikev2_child_sa_t *fchild = 0; /* Search for the child SA */ vec_foreach (tkm, km->per_thread_data) { ikev2_sa_t *sa; if (fchild) break; /* *INDENT-OFF* */ pool_foreach (sa, tkm->sas, ({ fchild = ikev2_sa_get_child(sa, ispi, IKEV2_PROTOCOL_ESP, 1); if (fchild) { fsa = sa; break; } })); /* *INDENT-ON* */ } if (!fchild || !fsa) { r = clib_error_return (0, "Child SA not found"); return r; } else { ikev2_rekey_child_sa_internal (vm, fsa, fchild); } return 0; } clib_error_t * ikev2_init (vlib_main_t * vm) { ikev2_main_t *km = &ikev2_main; clib_error_t *error; vlib_thread_main_t *tm = vlib_get_thread_main (); int thread_id; memset (km, 0, sizeof (ikev2_main_t)); km->vnet_main = vnet_get_main (); km->vlib_main = vm; ikev2_crypto_init (km); mhash_init_vec_string (&km->profile_index_by_name, sizeof (uword)); vec_validate (km->per_thread_data, tm->n_vlib_mains - 1); for (thread_id = 0; thread_id < tm->n_vlib_mains - 1; thread_id++) { km->per_thread_data[thread_id].sa_by_rspi = hash_create (0, sizeof (uword)); } km->sa_by_ispi = hash_create (0, sizeof (uword)); if ((error = vlib_call_init_function (vm, ikev2_cli_init))) return error; udp_register_dst_port (vm, 500, ikev2_node.index, 1); return 0; } static u8 ikev2_mngr_process_child_sa (ikev2_sa_t * sa, ikev2_child_sa_t * csa) { ikev2_main_t *km = &ikev2_main; vlib_main_t *vm = km->vlib_main; f64 now = vlib_time_now (vm); u8 res = 0; if (sa->is_initiator && sa->profile && csa->time_to_expiration && now > csa->time_to_expiration) { if (!csa->is_expired || csa->rekey_retries > 0) { ikev2_rekey_child_sa_internal (vm, sa, csa); csa->time_to_expiration = now + sa->profile->handover; csa->is_expired = 1; if (csa->rekey_retries == 0) { csa->rekey_retries = 5; } else if (csa->rekey_retries > 0) { csa->rekey_retries--; clib_warning ("Rekeing Child SA 0x%x, retries left %d", csa->i_proposals->spi, csa->rekey_retries); if (csa->rekey_retries == 0) { csa->rekey_retries = -1; } } res |= 1; } else { csa->time_to_expiration = 0; ikev2_delete_child_sa_internal (vm, sa, csa); res |= 1; } } return res; } static void ikev2_mngr_process_ipsec_sa (ipsec_sa_t * ipsec_sa) { ikev2_main_t *km = &ikev2_main; vlib_main_t *vm = km->vlib_main; ikev2_main_per_thread_data_t *tkm; ikev2_sa_t *fsa = 0; ikev2_child_sa_t *fchild = 0; f64 now = vlib_time_now (vm); /* Search for the SA and child SA */ vec_foreach (tkm, km->per_thread_data) { ikev2_sa_t *sa; if (fchild) break; /* *INDENT-OFF* */ pool_foreach (sa, tkm->sas, ({ fchild = ikev2_sa_get_child(sa, ipsec_sa->spi, IKEV2_PROTOCOL_ESP, 1); if (fchild) { fsa = sa; break; } })); /* *INDENT-ON* */ } if (fchild && fsa && fsa->profile && fsa->profile->lifetime_maxdata) { if (!fchild->is_expired && ipsec_sa->total_data_size > fsa->profile->lifetime_maxdata) { fchild->time_to_expiration = now; } } } static vlib_node_registration_t ikev2_mngr_process_node; static uword ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) { ikev2_main_t *km = &ikev2_main; ipsec_main_t *im = &ipsec_main; while (1) { u8 req_sent = 0; vlib_process_wait_for_event_or_clock (vm, 1); vlib_process_get_events (vm, NULL); /* process ike child sas */ ikev2_main_per_thread_data_t *tkm; vec_foreach (tkm, km->per_thread_data) { ikev2_sa_t *sa; /* *INDENT-OFF* */ pool_foreach (sa, tkm->sas, ({ ikev2_child_sa_t *c; vec_foreach (c, sa->childs) { req_sent |= ikev2_mngr_process_child_sa(sa, c); } })); /* *INDENT-ON* */ } /* process ipsec sas */ ipsec_sa_t *sa; /* *INDENT-OFF* */ pool_foreach (sa, im->sad, ({ ikev2_mngr_process_ipsec_sa(sa); })); /* *INDENT-ON* */ if (req_sent) { vlib_process_wait_for_event_or_clock (vm, 5); vlib_process_get_events (vm, NULL); req_sent = 0; } } return 0; } /* *INDENT-OFF* */ VLIB_REGISTER_NODE (ikev2_mngr_process_node, static) = { .function = ikev2_mngr_process_fn, .type = VLIB_NODE_TYPE_PROCESS, .name = "ikev2-manager-process", }; /* *INDENT-ON* */ /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */