ipsec: Dedicated IPSec interface type

Type: feature

Signed-off-by: Neale Ranns <nranns@cisco.com>
Change-Id: Ie8bd50df163aea2798e9f9d35a13dcadc4a4a4b2
This commit is contained in:
Neale Ranns
2020-06-30 07:47:14 +00:00
committed by Dave Barach
parent 0c65f52bb9
commit dd4ccf2623
11 changed files with 1092 additions and 23 deletions
+1
View File
@@ -581,6 +581,7 @@ list(APPEND VNET_SOURCES
ipsec/ipsec_format.c
ipsec/ipsec_handoff.c
ipsec/ipsec_input.c
ipsec/ipsec_itf.c
ipsec/ipsec_punt.c
ipsec/ipsec_sa.c
ipsec/ipsec_spd.c
+50 -1
View File
@@ -20,6 +20,7 @@ import "vnet/ipsec/ipsec_types.api";
import "vnet/interface_types.api";
import "vnet/ip/ip_types.api";
import "vnet/interface_types.api";
import "vnet/tunnel/tunnel_types.api";
/** \brief IPsec: Add/delete Security Policy Database
@param client_index - opaque cookie to identify the sender
@@ -379,12 +380,60 @@ define ipsec_tunnel_if_add_del_reply {
vl_api_interface_index_t sw_if_index;
};
typedef ipsec_itf
{
u32 user_instance [default=0xffffffff];
vl_api_tunnel_mode_t mode;
vl_api_interface_index_t sw_if_index;
};
/** \brief Create an IPSec interface
*/
define ipsec_itf_create {
u32 client_index;
u32 context;
vl_api_ipsec_itf_t itf;
};
/** \brief Add IPsec interface interface response
@param context - sender context, to match reply w/ request
@param retval - return status
@param sw_if_index - sw_if_index of new interface (for successful add)
*/
define ipsec_itf_create_reply
{
u32 context;
i32 retval;
vl_api_interface_index_t sw_if_index;
};
autoreply define ipsec_itf_delete
{
u32 client_index;
u32 context;
vl_api_interface_index_t sw_if_index;
};
define ipsec_itf_dump
{
u32 client_index;
u32 context;
vl_api_interface_index_t sw_if_index;
};
define ipsec_itf_details
{
u32 context;
vl_api_ipsec_itf_t itf;
};
/** \brief Dump IPsec security association
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
@param sa_id - optional ID of an SA to dump, if ~0 dump all SAs in SAD
*/
define ipsec_sa_dump {
define ipsec_sa_dump
{
u32 client_index;
u32 context;
u32 sa_id;
+42
View File
@@ -25,6 +25,7 @@
#include <vnet/ip/ip.h>
#include <vnet/ip/ip_types_api.h>
#include <vnet/ipsec/ipsec_types_api.h>
#include <vnet/tunnel/tunnel_types_api.h>
#include <vnet/fib/fib.h>
#include <vnet/ipip/ipip.h>
@@ -33,6 +34,7 @@
#if WITH_LIBSSL > 0
#include <vnet/ipsec/ipsec.h>
#include <vnet/ipsec/ipsec_tun.h>
#include <vnet/ipsec/ipsec_itf.h>
#endif /* IPSEC */
#define vl_typedefs /* define message structures */
@@ -60,6 +62,9 @@ _(IPSEC_SA_DUMP, ipsec_sa_dump) \
_(IPSEC_SPDS_DUMP, ipsec_spds_dump) \
_(IPSEC_SPD_DUMP, ipsec_spd_dump) \
_(IPSEC_SPD_INTERFACE_DUMP, ipsec_spd_interface_dump) \
_(IPSEC_ITF_CREATE, ipsec_itf_create) \
_(IPSEC_ITF_DELETE, ipsec_itf_delete) \
_(IPSEC_ITF_DUMP, ipsec_itf_dump) \
_(IPSEC_TUNNEL_IF_ADD_DEL, ipsec_tunnel_if_add_del) \
_(IPSEC_TUNNEL_IF_SET_SA, ipsec_tunnel_if_set_sa) \
_(IPSEC_SELECT_BACKEND, ipsec_select_backend) \
@@ -736,6 +741,43 @@ done:
/* *INDENT-ON* */
}
static void
vl_api_ipsec_itf_create_t_handler (vl_api_ipsec_itf_create_t * mp)
{
vl_api_ipsec_itf_create_reply_t *rmp;
tunnel_mode_t mode;
u32 sw_if_index = ~0;
int rv;
rv = tunnel_mode_decode (mp->itf.mode, &mode);
if (!rv)
rv = ipsec_itf_create (ntohl (mp->itf.user_instance), mode, &sw_if_index);
/* *INDENT-OFF* */
REPLY_MACRO2 (VL_API_IPSEC_ITF_CREATE_REPLY,
({
rmp->sw_if_index = htonl (sw_if_index);
}));
/* *INDENT-ON* */
}
static void
vl_api_ipsec_itf_delete_t_handler (vl_api_ipsec_itf_delete_t * mp)
{
vl_api_ipsec_itf_delete_reply_t *rmp;
int rv;
rv = ipsec_itf_delete (ntohl (mp->sw_if_index));
REPLY_MACRO (VL_API_IPSEC_ITF_DELETE_REPLY);
}
static void
vl_api_ipsec_itf_dump_t_handler (vl_api_ipsec_itf_dump_t * mp)
{
}
typedef struct ipsec_sa_dump_match_ctx_t_
{
index_t sai;
+18 -2
View File
@@ -353,6 +353,21 @@ format_ipsec_tun_protect_index (u8 * s, va_list * args)
return (format (s, "%U", format_ipsec_tun_protect, itp));
}
u8 *
format_ipsec_tun_protect_flags (u8 * s, va_list * args)
{
ipsec_protect_flags_t flags = va_arg (*args, int);
if (IPSEC_PROTECT_NONE == flags)
s = format (s, "none");
#define _(a,b,c) \
else if (flags & IPSEC_PROTECT_##a) \
s = format (s, "%s", c); \
foreach_ipsec_protect_flags
#undef _
return (s);
}
u8 *
format_ipsec_tun_protect (u8 * s, va_list * args)
@@ -360,8 +375,9 @@ format_ipsec_tun_protect (u8 * s, va_list * args)
ipsec_tun_protect_t *itp = va_arg (*args, ipsec_tun_protect_t *);
u32 sai;
s = format (s, "%U", format_vnet_sw_if_index_name,
vnet_get_main (), itp->itp_sw_if_index);
s = format (s, "%U flags:[%U]", format_vnet_sw_if_index_name,
vnet_get_main (), itp->itp_sw_if_index,
format_ipsec_tun_protect_flags, itp->itp_flags);
if (!ip_address_is_zero (itp->itp_key))
s = format (s, ": %U", format_ip_address, itp->itp_key);
s = format (s, "\n output-sa:");
File diff suppressed because it is too large Load Diff
+117
View File
@@ -0,0 +1,117 @@
/*
* ipsec_itf.c: IPSec dedicated interface type
*
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __IPSEC_ITF_H__
#define __IPSEC_ITF_H__
#include <vnet/tunnel/tunnel.h>
#include <vnet/ipsec/ipsec_sa.h>
/**
* @brief A dedicated IPSec interface type
*
* In order to support route based VPNs one needs 3 elements: an interface,
* for routing to resolve routes through, an SA from the peer to describe
* security, and encap, to describe how to reach the peer. There are two
* ways one could model this:
*
* interface + encap + SA = (interface + encap) + SA =
* ipip-interface + SA transport mode
*
* or
*
* interface + encap + SA = interface + (encap + SA) =
* IPSec-interface + SA tunnel mode
*
* It's a question of where you add the parenthesis, from the perspective
* of the external user the effect is identical.
*
* The IPsec interface serves as the encap-free interface to be used
* in conjunction with an encap-describing tunnel mode SA.
*
* VPP supports both models, which modelshould you pick?
* A route based VPN could impose 0, 1 or 2 encaps. the support matrix for
* these use cases is:
*
* | 0 | 1 | 2 |
* --------------------------
* ipip | N | Y | Y |
* ipsec | P | Y | P |
*
* Where P = potentially.
* ipsec could potnetially support 0 encap (i.e. transport mode) since neither
* the interface nor the SA *requires* encap. However, for a route beased VPN
* to use transport mode is probably wrong since one shouldn't use thransport
* mode for transit traffic, since without encap it is not guaranteed to return.
* ipsec could potnetially support 2 encaps, but that would require the SA to
* describe both, something it does not do at this time.
*
* ipsec currently does not support:
* - multipoint interfaces
* but this is only because it is not yet implemented, rather than it cannot
* be done.
*
* Internally the difference is that the midchain adjacency for the IPSec
* interface has no associated encap (whereas for an ipip tunnel it describes
* the peer). Consequently, features on the output arc see packets without
* any encap. Since the protecting SAs are in tunnel mode,
* they apply the encap. The midchain adj is stacked only once the proctecting
* SA is known, since only then is the peer known. Otherwise the VLIB graph
* nodes used are the same:
* (routing) --> ipX-michain --> espX-encrypt --> adj-midchain-tx --> (routing)
* where X = 4 or 6.
*
* Some benefits to the ipsec interface:
* - it is slightly more efficient since the encapsulating IP header has
* its checksum updated only once.
* - even when the interface is admin up traffic cannot be sent to a peer
* unless the SA is available (since it's the SA that determines the
* encap). With ipip interfaces a client must use the admin state to
* prevent sending until the SA is available.
*
* The best recommendations i can make are:
* - pick a model that supports your use case
* - make sure any other features you wish to use are supported by the model
* - choose the model that best fits your control plane's model.
*
*
* gun reloaded, fire away.
*/
typedef struct ipsec_itf_t_
{
tunnel_mode_t ii_mode;
int ii_user_instance;
u32 ii_sw_if_index;
} __clib_packed ipsec_itf_t;
extern int ipsec_itf_create (u32 user_instance,
tunnel_mode_t mode, u32 * sw_if_indexp);
extern int ipsec_itf_delete (u32 sw_if_index);
extern void ipsec_itf_adj_stack (adj_index_t ai, u32 sai);
extern void ipsec_itf_adj_unstack (adj_index_t ai);
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/
#endif
+47 -14
View File
@@ -16,6 +16,7 @@
*/
#include <vnet/ipsec/ipsec_tun.h>
#include <vnet/ipsec/ipsec_itf.h>
#include <vnet/ipsec/esp.h>
#include <vnet/udp/udp.h>
#include <vnet/adj/adj_delegate.h>
@@ -126,14 +127,20 @@ ipsec_tun_protect_from_const_base (const adj_delegate_t * ad)
}
static u32
ipsec_tun_protect_get_adj_next (const ipsec_tun_protect_t * itp)
ipsec_tun_protect_get_adj_next (vnet_link_t linkt,
const ipsec_tun_protect_t * itp)
{
ipsec_main_t *im;
ipsec_sa_t *sa;
bool is_ip4;
u32 next;
is_ip4 = ip46_address_is_ip4 (&itp->itp_tun.src);
if (itp->itp_flags & IPSEC_PROTECT_ITF)
is_ip4 = linkt == VNET_LINK_IP4;
else
is_ip4 = ip46_address_is_ip4 (&itp->itp_tun.src);
sa = ipsec_sa_get (itp->itp_out_sa);
im = &ipsec_main;
@@ -169,7 +176,7 @@ ipsec_tun_protect_add_adj (adj_index_t ai, const ipsec_tun_protect_t * itp)
{
ipsec_tun_protect_sa_by_adj_index[ai] = itp->itp_out_sa;
adj_nbr_midchain_update_next_node
(ai, ipsec_tun_protect_get_adj_next (itp));
(ai, ipsec_tun_protect_get_adj_next (adj_get_link_type (ai), itp));
}
}
@@ -249,6 +256,9 @@ ipsec_tun_protect_adj_add (adj_index_t ai, void *arg)
itp - ipsec_tun_protect_pool);
ipsec_tun_protect_add_adj (ai, itp);
if (itp->itp_flags & IPSEC_PROTECT_ITF)
ipsec_itf_adj_stack (ai, itp->itp_out_sa);
return (ADJ_WALK_RC_CONTINUE);
}
@@ -349,9 +359,14 @@ ipsec_tun_protect_rx_db_remove (ipsec_main_t * im,
static adj_walk_rc_t
ipsec_tun_protect_adj_remove (adj_index_t ai, void *arg)
{
ipsec_tun_protect_t *itp = arg;
adj_delegate_remove (ai, ipsec_tun_adj_delegate_type);
ipsec_tun_protect_add_adj (ai, NULL);
if (itp->itp_flags & IPSEC_PROTECT_ITF)
ipsec_itf_adj_unstack (ai);
return (ADJ_WALK_RC_CONTINUE);
}
@@ -404,8 +419,11 @@ ipsec_tun_protect_set_crypto_addr (ipsec_tun_protect_t * itp)
{
itp->itp_crypto.src = sa->tunnel_dst_addr;
itp->itp_crypto.dst = sa->tunnel_src_addr;
ipsec_sa_set_IS_PROTECT (sa);
itp->itp_flags |= IPSEC_PROTECT_ENCAPED;
if (!(itp->itp_flags & IPSEC_PROTECT_ITF))
{
ipsec_sa_set_IS_PROTECT (sa);
itp->itp_flags |= IPSEC_PROTECT_ENCAPED;
}
}
else
{
@@ -657,6 +675,7 @@ ipsec_tun_protect_update (u32 sw_if_index,
pool_get_zero (ipsec_tun_protect_pool, itp);
itp->itp_sw_if_index = sw_if_index;
itp->itp_ai = ADJ_INDEX_INVALID;
itp->itp_n_sa_in = vec_len (sas_in);
for (ii = 0; ii < itp->itp_n_sa_in; ii++)
@@ -673,7 +692,24 @@ ipsec_tun_protect_update (u32 sw_if_index,
if (rv)
goto out;
if (ip46_address_is_zero (&itp->itp_tun.dst))
if (ip46_address_is_zero (&itp->itp_tun.src))
{
/* must be one of thos pesky ipsec interfaces that has no encap.
* the encap then MUST comefrom the tunnel mode SA.
*/
ipsec_sa_t *sa;
sa = ipsec_sa_get (itp->itp_out_sa);
if (!ipsec_sa_is_set_IS_TUNNEL (sa))
{
rv = VNET_API_ERROR_INVALID_DST_ADDRESS;
goto out;
}
itp->itp_flags |= IPSEC_PROTECT_ITF;
}
else if (ip46_address_is_zero (&itp->itp_tun.dst))
{
/* tunnel has no destination address, presumably because it's p2mp
in which case we use the nh that this is protection for */
@@ -690,7 +726,7 @@ ipsec_tun_protect_update (u32 sw_if_index,
/*
* add to the tunnel DB for ingress
* - if the SA is in trasnport mode, then the packates will arrivw
* - if the SA is in trasnport mode, then the packates will arrive
* with the IP src,dst of the protected tunnel, in which case we can
* simply strip the IP header and hand the payload to the protocol
* appropriate input handler
@@ -752,6 +788,9 @@ ipsec_tun_protect_del (u32 sw_if_index, const ip_address_t * nh)
itp = ipsec_tun_protect_get (itpi);
ipsec_tun_protect_unconfig (im, itp);
if (ADJ_INDEX_INVALID != itp->itp_ai)
adj_unlock (itp->itp_ai);
clib_mem_free (itp->itp_key);
pool_put (ipsec_tun_protect_pool, itp);
@@ -828,13 +867,7 @@ ipsec_tun_protect_adj_delegate_adj_created (adj_index_t ai)
itpi = ipsec_tun_protect_find (adj->rewrite_header.sw_if_index, &ip);
if (INDEX_INVALID != itpi)
{
const ipsec_tun_protect_t *itp;
itp = ipsec_tun_protect_get (itpi);
adj_delegate_add (adj_get (ai), ipsec_tun_adj_delegate_type, itpi);
ipsec_tun_protect_add_adj (ai, itp);
}
ipsec_tun_protect_adj_add (ai, ipsec_tun_protect_get (itpi));
}
static u8 *
+12 -2
View File
@@ -47,12 +47,21 @@ typedef CLIB_PACKED(struct {
extern u8 *format_ipsec4_tunnel_key (u8 * s, va_list * args);
extern u8 *format_ipsec6_tunnel_key (u8 * s, va_list * args);
#define foreach_ipsec_protect_flags \
_(L2, 1, "l2") \
_(ENCAPED, 2, "encapped") \
_(ITF, 4, "itf") \
typedef enum ipsec_protect_flags_t_
{
IPSEC_PROTECT_L2 = (1 << 0),
IPSEC_PROTECT_ENCAPED = (1 << 1),
IPSEC_PROTECT_NONE = 0,
#define _(a,b,c) IPSEC_PROTECT_##a = b,
foreach_ipsec_protect_flags
#undef _
} __clib_packed ipsec_protect_flags_t;
extern u8 *format_ipsec_tun_protect_flags (u8 * s, va_list * args);
typedef struct ipsec_ep_t_
{
ip46_address_t src;
@@ -76,6 +85,7 @@ typedef struct ipsec_tun_protect_t_
ipsec_ep_t itp_crypto;
ipsec_protect_flags_t itp_flags;
adj_index_t itp_ai;
ipsec_ep_t itp_tun;
+2
View File
@@ -914,6 +914,7 @@ class IpsecTun4(object):
def verify_tun_64(self, p, count=1):
self.vapi.cli("clear errors")
self.vapi.cli("clear ipsec sa")
try:
send_pkts = self.gen_encrypt_pkts6(p, p.scapy_tun_sa, self.tun_if,
src=p.remote_tun_if_host6,
@@ -1104,6 +1105,7 @@ class IpsecTun6(object):
def verify_tun_46(self, p, count=1):
""" ipsec 4o6 tunnel basic test """
self.vapi.cli("clear errors")
self.vapi.cli("clear ipsec sa")
try:
send_pkts = self.gen_encrypt_pkts(p, p.scapy_tun_sa, self.tun_if,
src=p.remote_tun_if_host4,
File diff suppressed because it is too large Load Diff
+39
View File
@@ -1,6 +1,7 @@
from vpp_object import VppObject
from ipaddress import ip_address
from vpp_papi import VppEnum
from vpp_interface import VppInterface
try:
text_type = unicode
@@ -368,3 +369,41 @@ class VppIpsecTunProtect(VppObject):
self.nh == str(b.tun.nh):
return True
return False
class VppIpsecInterface(VppInterface):
"""
VPP IPSec interface
"""
def __init__(self, test, mode=None):
super(VppIpsecInterface, self).__init__(test)
# only p2p mode is supported currently
self.mode = (VppEnum.vl_api_tunnel_mode_t.
TUNNEL_API_MODE_P2P)
def add_vpp_config(self):
r = self.test.vapi.ipsec_itf_create(itf={
'user_instance': 0xffffffff,
'mode': self.mode,
})
self.set_sw_if_index(r.sw_if_index)
self.test.registry.register(self, self.test.logger)
return self
def remove_vpp_config(self):
self.test.vapi.ipsec_itf_delete(sw_if_index=self._sw_if_index)
def query_vpp_config(self):
ts = self.test.vapi.ipsec_itf_dump(sw_if_index=0xffffffff)
for t in ts:
if t.tunnel.sw_if_index == self._sw_if_index:
return True
return False
def __str__(self):
return self.object_id()
def object_id(self):
return "ipsec-%d" % self._sw_if_index