ipsec: support UDP encap/decap for NAT traversal

Change-Id: I65c12617ad49e4d5ef242e53988782f0cefa5684
Signed-off-by: Klement Sekera <ksekera@cisco.com>
This commit is contained in:
Klement Sekera
2018-04-17 18:04:57 +02:00
committed by Damjan Marion
parent 8e43d04ca4
commit 4b089f27b3
13 changed files with 366 additions and 48 deletions

View File

@@ -42,6 +42,14 @@ typedef CLIB_PACKED (struct {
}) ip4_and_esp_header_t;
/* *INDENT-ON* */
/* *INDENT-OFF* */
typedef CLIB_PACKED (struct {
ip4_header_t ip4;
udp_header_t udp;
esp_header_t esp;
}) ip4_and_udp_and_esp_header_t;
/* *INDENT-ON* */
/* *INDENT-OFF* */
typedef CLIB_PACKED (struct {
ip6_header_t ip6;

View File

@@ -18,6 +18,7 @@
#include <vnet/vnet.h>
#include <vnet/api_errno.h>
#include <vnet/ip/ip.h>
#include <vnet/udp/udp.h>
#include <vnet/ipsec/ipsec.h>
#include <vnet/ipsec/esp.h>
@@ -65,6 +66,7 @@ typedef struct
{
u32 spi;
u32 seq;
u8 udp_encap;
ipsec_crypto_alg_t crypto_alg;
ipsec_integ_alg_t integ_alg;
} esp_encrypt_trace_t;
@@ -77,10 +79,11 @@ format_esp_encrypt_trace (u8 * s, va_list * args)
CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
esp_encrypt_trace_t *t = va_arg (*args, esp_encrypt_trace_t *);
s = format (s, "esp: spi %u seq %u crypto %U integrity %U",
s = format (s, "esp: spi %u seq %u crypto %U integrity %U%s",
t->spi, t->seq,
format_ipsec_crypto_alg, t->crypto_alg,
format_ipsec_integ_alg, t->integ_alg);
format_ipsec_integ_alg, t->integ_alg,
t->udp_encap ? " udp-encap-enabled" : "");
return s;
}
@@ -155,13 +158,14 @@ esp_encrypt_node_fn (vlib_main_t * vm,
vlib_buffer_t *i_b0, *o_b0 = 0;
u32 sa_index0;
ipsec_sa_t *sa0;
ip4_and_esp_header_t *ih0, *oh0 = 0;
ip4_and_esp_header_t *oh0 = 0;
ip6_and_esp_header_t *ih6_0, *oh6_0 = 0;
ip4_and_udp_and_esp_header_t *iuh0, *ouh0 = 0;
uword last_empty_buffer;
esp_header_t *o_esp0;
esp_footer_t *f0;
u8 is_ipv6;
u8 ip_hdr_size;
u8 ip_udp_hdr_size;
u8 next_hdr_type;
u32 ip_proto = 0;
u8 transport_mode = 0;
@@ -198,7 +202,7 @@ esp_encrypt_node_fn (vlib_main_t * vm,
o_b0 = vlib_get_buffer (vm, o_bi0);
o_b0->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID;
o_b0->current_data = sizeof (ethernet_header_t);
ih0 = vlib_buffer_get_current (i_b0);
iuh0 = vlib_buffer_get_current (i_b0);
vlib_prefetch_buffer_with_index (vm,
empty_buffers[last_empty_buffer -
1], STORE);
@@ -211,18 +215,18 @@ esp_encrypt_node_fn (vlib_main_t * vm,
/* is ipv6 */
if (PREDICT_FALSE
((ih0->ip4.ip_version_and_header_length & 0xF0) == 0x60))
((iuh0->ip4.ip_version_and_header_length & 0xF0) == 0x60))
{
is_ipv6 = 1;
ih6_0 = vlib_buffer_get_current (i_b0);
ip_hdr_size = sizeof (ip6_header_t);
next_hdr_type = IP_PROTOCOL_IPV6;
oh6_0 = vlib_buffer_get_current (o_b0);
o_esp0 = vlib_buffer_get_current (o_b0) + sizeof (ip6_header_t);
oh6_0->ip6.ip_version_traffic_class_and_flow_label =
ih6_0->ip6.ip_version_traffic_class_and_flow_label;
oh6_0->ip6.protocol = IP_PROTOCOL_IPSEC_ESP;
ip_udp_hdr_size = sizeof (ip6_header_t);
o_esp0 = vlib_buffer_get_current (o_b0) + ip_udp_hdr_size;
oh6_0->ip6.hop_limit = 254;
oh6_0->ip6.src_address.as_u64[0] =
ih6_0->ip6.src_address.as_u64[0];
@@ -232,8 +236,8 @@ esp_encrypt_node_fn (vlib_main_t * vm,
ih6_0->ip6.dst_address.as_u64[0];
oh6_0->ip6.dst_address.as_u64[1] =
ih6_0->ip6.dst_address.as_u64[1];
oh6_0->esp.spi = clib_net_to_host_u32 (sa0->spi);
oh6_0->esp.seq = clib_net_to_host_u32 (sa0->seq);
o_esp0->spi = clib_net_to_host_u32 (sa0->spi);
o_esp0->seq = clib_net_to_host_u32 (sa0->seq);
ip_proto = ih6_0->ip6.protocol;
next0 = ESP_ENCRYPT_NEXT_IP6_LOOKUP;
@@ -241,22 +245,37 @@ esp_encrypt_node_fn (vlib_main_t * vm,
else
{
is_ipv6 = 0;
ip_hdr_size = sizeof (ip4_header_t);
next_hdr_type = IP_PROTOCOL_IP_IN_IP;
oh0 = vlib_buffer_get_current (o_b0);
o_esp0 = vlib_buffer_get_current (o_b0) + sizeof (ip4_header_t);
ouh0 = vlib_buffer_get_current (o_b0);
oh0->ip4.ip_version_and_header_length = 0x45;
oh0->ip4.tos = ih0->ip4.tos;
oh0->ip4.tos = iuh0->ip4.tos;
oh0->ip4.fragment_id = 0;
oh0->ip4.flags_and_fragment_offset = 0;
oh0->ip4.ttl = 254;
oh0->ip4.protocol = IP_PROTOCOL_IPSEC_ESP;
oh0->ip4.src_address.as_u32 = ih0->ip4.src_address.as_u32;
oh0->ip4.dst_address.as_u32 = ih0->ip4.dst_address.as_u32;
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;
if (sa0->udp_encap)
{
ouh0->udp.src_port =
clib_host_to_net_u16 (UDP_DST_PORT_ipsec);
ouh0->udp.dst_port =
clib_host_to_net_u16 (UDP_DST_PORT_ipsec);
ouh0->udp.checksum = 0;
ouh0->ip4.protocol = IP_PROTOCOL_UDP;
ip_udp_hdr_size =
sizeof (udp_header_t) + sizeof (ip4_header_t);
}
else
{
oh0->ip4.protocol = IP_PROTOCOL_IPSEC_ESP;
ip_udp_hdr_size = sizeof (ip4_header_t);
}
o_esp0 = vlib_buffer_get_current (o_b0) + ip_udp_hdr_size;
oh0->ip4.src_address.as_u32 = iuh0->ip4.src_address.as_u32;
oh0->ip4.dst_address.as_u32 = iuh0->ip4.dst_address.as_u32;
o_esp0->spi = clib_net_to_host_u32 (sa0->spi);
o_esp0->seq = clib_net_to_host_u32 (sa0->seq);
ip_proto = iuh0->ip4.protocol;
next0 = ESP_ENCRYPT_NEXT_IP4_LOOKUP;
}
@@ -299,7 +318,7 @@ esp_encrypt_node_fn (vlib_main_t * vm,
vnet_buffer (o_b0)->sw_if_index[VLIB_TX] =
vnet_buffer (i_b0)->sw_if_index[VLIB_TX];
}
vlib_buffer_advance (i_b0, ip_hdr_size);
vlib_buffer_advance (i_b0, ip_udp_hdr_size);
}
ASSERT (sa0->crypto_alg < IPSEC_CRYPTO_N_ALG);
@@ -327,7 +346,7 @@ esp_encrypt_node_fn (vlib_main_t * vm,
f0->pad_length = pad_bytes;
f0->next_header = next_hdr_type;
o_b0->current_length = ip_hdr_size + sizeof (esp_header_t) +
o_b0->current_length = ip_udp_hdr_size + sizeof (esp_header_t) +
BLOCK_SIZE * blocks + IV_SIZE;
vnet_buffer (o_b0)->sw_if_index[VLIB_RX] =
@@ -338,14 +357,14 @@ esp_encrypt_node_fn (vlib_main_t * vm,
RAND_bytes (iv, sizeof (iv));
clib_memcpy ((u8 *) vlib_buffer_get_current (o_b0) +
ip_hdr_size + sizeof (esp_header_t), iv,
ip_udp_hdr_size + sizeof (esp_header_t), iv,
em->ipsec_proto_main_crypto_algs[sa0->
crypto_alg].iv_size);
esp_encrypt_cbc (sa0->crypto_alg,
(u8 *) vlib_buffer_get_current (i_b0),
(u8 *) vlib_buffer_get_current (o_b0) +
ip_hdr_size + sizeof (esp_header_t) +
ip_udp_hdr_size + sizeof (esp_header_t) +
IV_SIZE, BLOCK_SIZE * blocks,
sa0->crypto_key, iv);
}
@@ -354,7 +373,7 @@ esp_encrypt_node_fn (vlib_main_t * vm,
sa0->integ_key_len,
(u8 *) o_esp0,
o_b0->current_length -
ip_hdr_size,
ip_udp_hdr_size,
vlib_buffer_get_current (o_b0) +
o_b0->current_length,
sa0->use_esn, sa0->seq_hi);
@@ -371,6 +390,12 @@ esp_encrypt_node_fn (vlib_main_t * vm,
oh0->ip4.length =
clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, o_b0));
oh0->ip4.checksum = ip4_header_checksum (&oh0->ip4);
if (sa0->udp_encap)
{
ouh0->udp.length =
clib_host_to_net_u16 (oh0->ip4.length -
ip4_header_bytes (&oh0->ip4));
}
}
if (transport_mode)
@@ -387,6 +412,7 @@ esp_encrypt_node_fn (vlib_main_t * vm,
vlib_add_trace (vm, node, o_b0, sizeof (*tr));
tr->spi = sa0->spi;
tr->seq = sa0->seq - 1;
tr->udp_encap = sa0->udp_encap;
tr->crypto_alg = sa0->crypto_alg;
tr->integ_alg = sa0->integ_alg;
}

View File

@@ -130,6 +130,7 @@ autoreply define ipsec_spd_add_del_entry
@param is_tunnel_ipv6 - IPsec tunnel mode is IPv6 if non-zero, else IPv4 tunnel only valid if is_tunnel is non-zero
@param tunnel_src_address - IPsec tunnel source address IPv6 if is_tunnel_ipv6 is non-zero, else IPv4. Only valid if is_tunnel is non-zero
@param tunnel_dst_address - IPsec tunnel destination address IPv6 if is_tunnel_ipv6 is non-zero, else IPv4. Only valid if is_tunnel is non-zero
@param udp_encap - enable UDP encapsulation for NAT traversal
To be added:
Anti-replay
@@ -163,6 +164,7 @@ autoreply define ipsec_sad_add_del_entry
u8 is_tunnel_ipv6;
u8 tunnel_src_address[16];
u8 tunnel_dst_address[16];
u8 udp_encap;
};
/** \brief IPsec: Update Security Association keys
@@ -587,6 +589,7 @@ define ipsec_sa_dump {
@param last_seq_hi - high 32 bits of highest ESN received inbound
@param replay_window - bit map of seq nums received relative to last_seq if using anti-replay
@param total_data_size - total bytes sent or received
@param udp_encap - 1 if UDP encap enabled, 0 otherwise
*/
define ipsec_sa_details {
u32 context;
@@ -618,6 +621,7 @@ define ipsec_sa_details {
u64 replay_window;
u64 total_data_size;
u8 udp_encap;
};
/** \brief Set key on IPsec interface

View File

@@ -19,12 +19,14 @@
#include <vnet/api_errno.h>
#include <vnet/ip/ip.h>
#include <vnet/interface.h>
#include <vnet/udp/udp.h>
#include <vnet/ipsec/ipsec.h>
#include <vnet/ipsec/ikev2.h>
#include <vnet/ipsec/esp.h>
#include <vnet/ipsec/ah.h>
ipsec_main_t ipsec_main;
u32
@@ -411,7 +413,8 @@ ipsec_is_sa_used (u32 sa_index)
}
int
ipsec_add_del_sa (vlib_main_t * vm, ipsec_sa_t * new_sa, int is_add)
ipsec_add_del_sa (vlib_main_t * vm, ipsec_sa_t * new_sa, int is_add,
u8 udp_encap)
{
ipsec_main_t *im = &ipsec_main;
ipsec_sa_t *sa = 0;
@@ -450,6 +453,7 @@ ipsec_add_del_sa (vlib_main_t * vm, ipsec_sa_t * new_sa, int is_add)
pool_get (im->sad, sa);
clib_memcpy (sa, new_sa, sizeof (*sa));
sa_index = sa - im->sad;
sa->udp_encap = udp_encap ? 1 : 0;
hash_set (im->sa_index_by_sa_id, sa->id, sa_index);
if (im->cb.add_del_sa_sess_cb)
{

View File

@@ -127,6 +127,7 @@ typedef struct
u8 is_tunnel;
u8 is_tunnel_ip6;
u8 udp_encap;
ip46_address_t tunnel_src_addr;
ip46_address_t tunnel_dst_addr;
@@ -318,7 +319,8 @@ int ipsec_set_interface_spd (vlib_main_t * vm, u32 sw_if_index, u32 spd_id,
int ipsec_add_del_spd (vlib_main_t * vm, u32 spd_id, int is_add);
int ipsec_add_del_policy (vlib_main_t * vm, ipsec_policy_t * policy,
int is_add);
int ipsec_add_del_sa (vlib_main_t * vm, ipsec_sa_t * new_sa, int is_add);
int ipsec_add_del_sa (vlib_main_t * vm, ipsec_sa_t * new_sa, int is_add,
u8 udp_encap);
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);

View File

@@ -241,7 +241,7 @@ static void vl_api_ipsec_sad_add_del_entry_t_handler
goto out;
}
rv = ipsec_add_del_sa (vm, &sa, mp->is_add);
rv = ipsec_add_del_sa (vm, &sa, mp->is_add, mp->udp_encap);
#else
rv = VNET_API_ERROR_UNIMPLEMENTED;
goto out;
@@ -457,6 +457,7 @@ send_ipsec_sa_details (ipsec_sa_t * sa, vl_api_registration_t * reg,
if (sa->use_anti_replay)
mp->replay_window = clib_host_to_net_u64 (sa->replay_window);
mp->total_data_size = clib_host_to_net_u64 (sa->total_data_size);
mp->udp_encap = sa->udp_encap;
vl_api_send_msg (reg, (u8 *) mp);
}

View File

@@ -176,7 +176,7 @@ ipsec_sa_add_del_command_fn (vlib_main_t * vm,
goto done;
}
ipsec_add_del_sa (vm, &sa, is_add);
ipsec_add_del_sa (vm, &sa, is_add, 0 /* enable nat traversal */ );
done:
unformat_free (line_input);
@@ -451,9 +451,10 @@ show_ipsec_command_fn (vlib_main_t * vm,
/* *INDENT-OFF* */
pool_foreach (sa, im->sad, ({
if (sa->id) {
vlib_cli_output(vm, "sa %u spi %u mode %s protocol %s", sa->id, sa->spi,
vlib_cli_output(vm, "sa %u spi %u mode %s protocol %s%s", sa->id, sa->spi,
sa->is_tunnel ? "tunnel" : "transport",
sa->protocol ? "esp" : "ah");
sa->protocol ? "esp" : "ah",
sa->udp_encap ? " udp-encap-enabled" : "");
if (sa->protocol == IPSEC_PROTOCOL_ESP) {
vlib_cli_output(vm, " crypto alg %U%s%U integrity alg %U%s%U",
format_ipsec_crypto_alg, sa->crypto_alg,

View File

@@ -216,7 +216,9 @@ ipsec_input_ip4_node_fn (vlib_main_t * vm,
ip0 = vlib_buffer_get_current (b0);
if (PREDICT_TRUE (ip0->protocol == IP_PROTOCOL_IPSEC_ESP))
if (PREDICT_TRUE
(ip0->protocol == IP_PROTOCOL_IPSEC_ESP
|| ip0->protocol == IP_PROTOCOL_UDP))
{
#if 0
clib_warning
@@ -228,6 +230,13 @@ ipsec_input_ip4_node_fn (vlib_main_t * vm,
#endif
esp0 = (esp_header_t *) ((u8 *) ip0 + ip4_header_bytes (ip0));
if (PREDICT_FALSE (ip0->protocol == IP_PROTOCOL_UDP))
{
esp0 =
(esp_header_t *) ((u8 *) esp0 + sizeof (udp_header_t));
}
/* FIXME TODO missing check whether there is enough data inside
* IP/UDP to contain ESP header & stuff ? */
p0 = ipsec_input_protect_policy_match (spd0,
clib_net_to_host_u32
(ip0->src_address.
@@ -245,7 +254,7 @@ ipsec_input_ip4_node_fn (vlib_main_t * vm,
vnet_buffer (b0)->ipsec.sad_index = p0->sa_index;
vnet_buffer (b0)->ipsec.flags = 0;
next0 = im->esp_decrypt_next_index;
vlib_buffer_advance (b0, ip4_header_bytes (ip0));
vlib_buffer_advance (b0, ((u8 *) esp0 - (u8 *) ip0));
goto trace0;
}
@@ -255,7 +264,8 @@ ipsec_input_ip4_node_fn (vlib_main_t * vm,
{
ipsec_input_trace_t *tr =
vlib_add_trace (vm, node, b0, sizeof (*tr));
if (ip0->protocol == IP_PROTOCOL_IPSEC_ESP)
if (ip0->protocol == IP_PROTOCOL_IPSEC_ESP ||
ip0->protocol == IP_PROTOCOL_UDP)
{
if (p0)
tr->sa_id = p0->sa_id;

View File

@@ -53,6 +53,7 @@ _ (3784, bfd4) \
_ (3785, bfd_echo4) \
_ (4341, lisp_gpe) \
_ (4342, lisp_cp) \
_ (4500, ipsec) \
_ (4739, ipfix) \
_ (4789, vxlan) \
_ (4789, vxlan6) \

View File

@@ -99,14 +99,14 @@ class TestIpsecAh(VppTestCase):
l_stopaddr,
r_startaddr,
r_stopaddr,
protocol=51)
protocol=socket.IPPROTO_AH)
cls.vapi.ipsec_spd_add_del_entry(
spd_id,
l_startaddr,
l_stopaddr,
r_startaddr,
r_stopaddr,
protocol=51,
protocol=socket.IPPROTO_AH,
is_outbound=0)
l_startaddr = l_stopaddr = socket.inet_pton(
socket.AF_INET, cls.remote_pg0_lb_addr)
@@ -164,14 +164,14 @@ class TestIpsecAh(VppTestCase):
l_stopaddr,
r_startaddr,
r_stopaddr,
protocol=51)
protocol=socket.IPPROTO_AH)
cls.vapi.ipsec_spd_add_del_entry(
spd_id,
l_startaddr,
l_stopaddr,
r_startaddr,
r_stopaddr,
protocol=51,
protocol=socket.IPPROTO_AH,
is_outbound=0)
l_startaddr = l_stopaddr = cls.pg2.local_ip4n
r_startaddr = r_stopaddr = cls.pg2.remote_ip4n

View File

@@ -31,11 +31,11 @@ class TestIpsecEsp(VppTestCase):
TUNNEL MODE:
--- encrypt --- plain ---
|pg0| -------> |VPP| ------> |pg1|
|pg0| <------- |VPP| <------ |pg1|
--- --- ---
--- decrypt --- plain ---
|pg0| <------- |VPP| <------ |pg1|
|pg0| -------> |VPP| ------> |pg1|
--- --- ---
Note : IPv6 is not covered
@@ -103,14 +103,14 @@ class TestIpsecEsp(VppTestCase):
l_stopaddr,
r_startaddr,
r_stopaddr,
protocol=50)
protocol=socket.IPPROTO_ESP)
cls.vapi.ipsec_spd_add_del_entry(
spd_id,
l_startaddr,
l_stopaddr,
r_startaddr,
r_stopaddr,
protocol=50,
protocol=socket.IPPROTO_ESP,
is_outbound=0)
l_startaddr = l_stopaddr = socket.inet_pton(
socket.AF_INET, cls.remote_pg0_lb_addr)
@@ -172,14 +172,14 @@ class TestIpsecEsp(VppTestCase):
l_stopaddr,
r_startaddr,
r_stopaddr,
protocol=50)
protocol=socket.IPPROTO_ESP)
cls.vapi.ipsec_spd_add_del_entry(
spd_id,
l_startaddr,
l_stopaddr,
r_startaddr,
r_stopaddr,
protocol=50,
protocol=socket.IPPROTO_ESP,
is_outbound=0)
l_startaddr = l_stopaddr = cls.pg2.local_ip4n
r_startaddr = r_stopaddr = cls.pg2.remote_ip4n

260
test/test_ipsec_nat.py Normal file
View File

@@ -0,0 +1,260 @@
#!/usr/bin/env python
import socket
from scapy.layers.l2 import Ether
from scapy.layers.inet import ICMP, IP, TCP, UDP
from scapy.layers.ipsec import SecurityAssociation, ESP
from util import ppp, ppc
from framework import VppTestCase
class IPSecNATTestCase(VppTestCase):
""" IPSec/NAT
TRANSPORT MODE:
--- encrypt ---
|pg2| <-------> |VPP|
--- decrypt ---
TUNNEL MODE:
public network | private network
--- encrypt --- plain ---
|pg0| <------- |VPP| <------ |pg1|
--- --- ---
--- decrypt --- plain ---
|pg0| -------> |VPP| ------> |pg1|
--- --- ---
"""
remote_pg0_client_addr = '1.1.1.1'
@classmethod
def setUpClass(cls):
super(IPSecNATTestCase, cls).setUpClass()
cls.create_pg_interfaces(range(2))
for i in cls.pg_interfaces:
i.configure_ipv4_neighbors()
i.admin_up()
i.config_ip4()
i.resolve_arp()
cls.tcp_port_in = 6303
cls.tcp_port_out = 6303
cls.udp_port_in = 6304
cls.udp_port_out = 6304
cls.icmp_id_in = 6305
cls.icmp_id_out = 6305
cls.config_esp_tun()
cls.logger.info(cls.vapi.ppcli("show ipsec"))
def create_stream_plain(self, src_mac, dst_mac, src_ip, dst_ip):
return [
# TCP
Ether(src=src_mac, dst=dst_mac) /
IP(src=src_ip, dst=dst_ip) /
TCP(sport=self.tcp_port_in, dport=20),
# UDP
Ether(src=src_mac, dst=dst_mac) /
IP(src=src_ip, dst=dst_ip) /
UDP(sport=self.udp_port_in, dport=20),
# ICMP
Ether(src=src_mac, dst=dst_mac) /
IP(src=src_ip, dst=dst_ip) /
ICMP(id=self.icmp_id_in, type='echo-request')
]
def create_stream_encrypted(self, src_mac, dst_mac, src_ip, dst_ip, sa):
return [
# TCP
Ether(src=src_mac, dst=dst_mac) /
sa.encrypt(IP(src=src_ip, dst=dst_ip) /
TCP(dport=self.tcp_port_out, sport=20)),
# UDP
Ether(src=src_mac, dst=dst_mac) /
sa.encrypt(IP(src=src_ip, dst=dst_ip) /
UDP(dport=self.udp_port_out, sport=20)),
# ICMP
Ether(src=src_mac, dst=dst_mac) /
sa.encrypt(IP(src=src_ip, dst=dst_ip) /
ICMP(id=self.icmp_id_out, type='echo-request'))
]
def check_checksum(self, pkt, layer):
""" Check checksum of the packet on given layer """
new = pkt.__class__(str(pkt))
del new[layer].chksum
new = new.__class__(str(new))
self.assertEqual(new[layer].chksum, pkt[layer].chksum)
def check_ip_checksum(self, pkt):
return self.check_checksum(pkt, 'IP')
def check_tcp_checksum(self, pkt):
return self.check_checksum(pkt, 'TCP')
def check_udp_checksum(self, pkt):
return self.check_checksum(pkt, 'UDP')
def check_icmp_checksum(self, pkt):
return self.check_checksum(pkt, 'ICMP')
def verify_capture_plain(self, capture):
for packet in capture:
try:
self.check_ip_checksum(packet)
self.assert_equal(packet[IP].src, self.pg0.remote_ip4,
"decrypted packet source address")
self.assert_equal(packet[IP].dst, self.pg1.remote_ip4,
"decrypted packet destination address")
if packet.haslayer(TCP):
self.assertFalse(
packet.haslayer(UDP),
"unexpected UDP header in decrypted packet")
self.assert_equal(packet[TCP].dport, self.tcp_port_in,
"decrypted packet TCP destination port")
self.check_tcp_checksum(packet)
elif packet.haslayer(UDP):
if packet[UDP].payload:
self.assertFalse(
packet[UDP][1].haslayer(UDP),
"unexpected UDP header in decrypted packet")
self.assert_equal(packet[UDP].dport, self.udp_port_in,
"decrypted packet UDP destination port")
else:
self.assertFalse(
packet.haslayer(UDP),
"unexpected UDP header in decrypted packet")
self.assert_equal(packet[ICMP].id, self.icmp_id_in,
"decrypted packet ICMP ID")
self.check_icmp_checksum(packet)
except Exception:
self.logger.error(
ppp("Unexpected or invalid plain packet:", packet))
raise
def verify_capture_encrypted(self, capture, sa):
for packet in capture:
try:
self.assertIn(ESP, packet[IP])
decrypt_pkt = sa.decrypt(packet[IP])
self.assert_equal(decrypt_pkt[IP].src, self.pg1.remote_ip4,
"encrypted packet source address")
self.assert_equal(decrypt_pkt[IP].dst, self.pg0.remote_ip4,
"encrypted packet destination address")
# if decrypt_pkt.haslayer(TCP):
# self.tcp_port_out = decrypt_pkt[TCP].sport
# elif decrypt_pkt.haslayer(UDP):
# self.udp_port_out = decrypt_pkt[UDP].sport
# else:
# self.icmp_id_out = decrypt_pkt[ICMP].id
except Exception:
self.logger.error(
ppp("Unexpected or invalid encrypted packet:", packet))
raise
@classmethod
def config_esp_tun(cls):
spd_id = 1
remote_sa_id = 10
local_sa_id = 20
remote_tun_spi = 1001
local_tun_spi = 1000
client = socket.inet_pton(socket.AF_INET, cls.remote_pg0_client_addr)
cls.vapi.ip_add_del_route(client, 32, cls.pg0.remote_ip4n)
cls.vapi.ipsec_sad_add_del_entry(remote_sa_id, remote_tun_spi,
cls.pg1.remote_ip4n,
cls.pg0.remote_ip4n,
integrity_key_length=20,
crypto_key_length=16,
protocol=1, udp_encap=1)
cls.vapi.ipsec_sad_add_del_entry(local_sa_id, local_tun_spi,
cls.pg0.remote_ip4n,
cls.pg1.remote_ip4n,
integrity_key_length=20,
crypto_key_length=16,
protocol=1, udp_encap=1)
cls.vapi.ipsec_spd_add_del(spd_id)
cls.vapi.ipsec_interface_add_del_spd(spd_id, cls.pg0.sw_if_index)
l_startaddr = r_startaddr = socket.inet_pton(socket.AF_INET,
"0.0.0.0")
l_stopaddr = r_stopaddr = socket.inet_pton(socket.AF_INET,
"255.255.255.255")
cls.vapi.ipsec_spd_add_del_entry(spd_id, l_startaddr, l_stopaddr,
r_startaddr, r_stopaddr,
protocol=socket.IPPROTO_ESP)
cls.vapi.ipsec_spd_add_del_entry(spd_id, l_startaddr, l_stopaddr,
r_startaddr, r_stopaddr,
protocol=socket.IPPROTO_ESP,
is_outbound=0)
cls.vapi.ipsec_spd_add_del_entry(spd_id, l_startaddr, l_stopaddr,
r_startaddr, r_stopaddr,
remote_port_start=4500,
remote_port_stop=4500,
protocol=socket.IPPROTO_UDP)
cls.vapi.ipsec_spd_add_del_entry(spd_id, l_startaddr, l_stopaddr,
r_startaddr, r_stopaddr,
remote_port_start=4500,
remote_port_stop=4500,
protocol=socket.IPPROTO_UDP,
is_outbound=0)
l_startaddr = l_stopaddr = cls.pg0.remote_ip4n
r_startaddr = r_stopaddr = cls.pg1.remote_ip4n
cls.vapi.ipsec_spd_add_del_entry(spd_id, l_startaddr, l_stopaddr,
r_startaddr, r_stopaddr,
priority=10, policy=3,
is_outbound=0, sa_id=local_sa_id)
cls.vapi.ipsec_spd_add_del_entry(spd_id, r_startaddr, r_stopaddr,
l_startaddr, l_stopaddr,
priority=10, policy=3,
sa_id=remote_sa_id)
def test_ipsec_nat_tun(self):
""" IPSec/NAT tunnel test case """
local_tun_sa = SecurityAssociation(ESP, spi=0x000003e9,
crypt_algo='AES-CBC',
crypt_key='JPjyOWBeVEQiMe7h',
auth_algo='HMAC-SHA1-96',
auth_key='C91KUR9GYMm5GfkEvNjX',
tunnel_header=IP(
src=self.pg1.remote_ip4,
dst=self.pg0.remote_ip4),
nat_t_header=UDP(
sport=4500,
dport=4500))
# in2out - from private network to public
pkts = self.create_stream_plain(
self.pg1.remote_mac, self.pg1.local_mac,
self.pg1.remote_ip4, self.pg0.remote_ip4)
self.pg1.add_stream(pkts)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
capture = self.pg0.get_capture(len(pkts))
self.verify_capture_encrypted(capture, local_tun_sa)
remote_tun_sa = SecurityAssociation(ESP, spi=0x000003e8,
crypt_algo='AES-CBC',
crypt_key='JPjyOWBeVEQiMe7h',
auth_algo='HMAC-SHA1-96',
auth_key='C91KUR9GYMm5GfkEvNjX',
tunnel_header=IP(
src=self.pg0.remote_ip4,
dst=self.pg1.remote_ip4),
nat_t_header=UDP(
sport=4500,
dport=4500))
# out2in - from public network to private
pkts = self.create_stream_encrypted(
self.pg0.remote_mac, self.pg0.local_mac,
self.pg0.remote_ip4, self.pg1.remote_ip4, remote_tun_sa)
self.logger.info(ppc("Sending packets:", pkts))
self.pg0.add_stream(pkts)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
capture = self.pg1.get_capture(len(pkts))
self.verify_capture_plain(capture)

View File

@@ -3060,9 +3060,8 @@ class VppPapiProvider(object):
:returns: reply from the API
"""
return self.api(
self.papi.ipsec_interface_add_del_spd, {
'spd_id': spd_id,
'sw_if_index': sw_if_index, 'is_add': is_add})
self.papi.ipsec_interface_add_del_spd,
{'spd_id': spd_id, 'sw_if_index': sw_if_index, 'is_add': is_add})
def ipsec_sad_add_del_entry(self,
sad_id,
@@ -3077,7 +3076,8 @@ class VppPapiProvider(object):
crypto_key_length=0,
crypto_key='JPjyOWBeVEQiMe7h',
is_add=1,
is_tunnel=1):
is_tunnel=1,
udp_encap=0):
""" IPSEC SA add/del
Sample CLI : 'ipsec sa add 10 spi 1001 esp \
crypto-key 4a506a794f574265564551694d653768 \
@@ -3130,7 +3130,8 @@ class VppPapiProvider(object):
'crypto_key_length': crypto_key_length,
'crypto_key': crypto_key,
'is_add': is_add,
'is_tunnel': is_tunnel})
'is_tunnel': is_tunnel,
'udp_encap': udp_encap})
def ipsec_spd_add_del_entry(self,
spd_id,