import unittest import socket import struct from scapy.layers.inet import IP, ICMP, TCP, UDP from scapy.layers.ipsec import SecurityAssociation, ESP from scapy.layers.l2 import Ether from scapy.packet import raw, Raw from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, IPv6ExtHdrHopByHop, \ IPv6ExtHdrFragment, IPv6ExtHdrDestOpt from framework import VppTestCase, VppTestRunner from util import ppp, reassemble4, fragment_rfc791, fragment_rfc8200 from vpp_papi import VppEnum class IPsecIPv4Params: addr_type = socket.AF_INET addr_any = "0.0.0.0" addr_bcast = "255.255.255.255" addr_len = 32 is_ipv6 = 0 def __init__(self): self.remote_tun_if_host = '1.1.1.1' self.remote_tun_if_host6 = '1111::1' self.scapy_tun_sa_id = 100 self.scapy_tun_spi = 1000 self.vpp_tun_sa_id = 200 self.vpp_tun_spi = 2000 self.scapy_tra_sa_id = 300 self.scapy_tra_spi = 3000 self.vpp_tra_sa_id = 400 self.vpp_tra_spi = 4000 self.outer_hop_limit = 64 self.inner_hop_limit = 255 self.outer_flow_label = 0 self.inner_flow_label = 0x12345 self.auth_algo_vpp_id = (VppEnum.vl_api_ipsec_integ_alg_t. IPSEC_API_INTEG_ALG_SHA1_96) self.auth_algo = 'HMAC-SHA1-96' # scapy name self.auth_key = b'C91KUR9GYMm5GfkEvNjX' self.crypt_algo_vpp_id = (VppEnum.vl_api_ipsec_crypto_alg_t. IPSEC_API_CRYPTO_ALG_AES_CBC_128) self.crypt_algo = 'AES-CBC' # scapy name self.crypt_key = b'JPjyOWBeVEQiMe7h' self.salt = 0 self.flags = 0 self.nat_header = None self.tun_flags = (VppEnum.vl_api_tunnel_encap_decap_flags_t. TUNNEL_API_ENCAP_DECAP_FLAG_NONE) self.dscp = 0 self.async_mode = False class IPsecIPv6Params: addr_type = socket.AF_INET6 addr_any = "0::0" addr_bcast = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" addr_len = 128 is_ipv6 = 1 def __init__(self): self.remote_tun_if_host = '1111:1111:1111:1111:1111:1111:1111:1111' self.remote_tun_if_host4 = '1.1.1.1' self.scapy_tun_sa_id = 500 self.scapy_tun_spi = 3001 self.vpp_tun_sa_id = 600 self.vpp_tun_spi = 3000 self.scapy_tra_sa_id = 700 self.scapy_tra_spi = 4001 self.vpp_tra_sa_id = 800 self.vpp_tra_spi = 4000 self.outer_hop_limit = 64 self.inner_hop_limit = 255 self.outer_flow_label = 0 self.inner_flow_label = 0x12345 self.auth_algo_vpp_id = (VppEnum.vl_api_ipsec_integ_alg_t. IPSEC_API_INTEG_ALG_SHA1_96) self.auth_algo = 'HMAC-SHA1-96' # scapy name self.auth_key = b'C91KUR9GYMm5GfkEvNjX' self.crypt_algo_vpp_id = (VppEnum.vl_api_ipsec_crypto_alg_t. IPSEC_API_CRYPTO_ALG_AES_CBC_128) self.crypt_algo = 'AES-CBC' # scapy name self.crypt_key = b'JPjyOWBeVEQiMe7h' self.salt = 0 self.flags = 0 self.nat_header = None self.tun_flags = (VppEnum.vl_api_tunnel_encap_decap_flags_t. TUNNEL_API_ENCAP_DECAP_FLAG_NONE) self.dscp = 0 self.async_mode = False def mk_scapy_crypt_key(p): if p.crypt_algo in ("AES-GCM", "AES-CTR"): return p.crypt_key + struct.pack("!I", p.salt) else: return p.crypt_key def config_tun_params(p, encryption_type, tun_if): ip_class_by_addr_type = {socket.AF_INET: IP, socket.AF_INET6: IPv6} esn_en = bool(p.flags & (VppEnum.vl_api_ipsec_sad_flags_t. IPSEC_API_SAD_FLAG_USE_ESN)) p.tun_dst = tun_if.remote_addr[p.addr_type] p.tun_src = tun_if.local_addr[p.addr_type] crypt_key = mk_scapy_crypt_key(p) p.scapy_tun_sa = SecurityAssociation( encryption_type, spi=p.vpp_tun_spi, crypt_algo=p.crypt_algo, crypt_key=crypt_key, auth_algo=p.auth_algo, auth_key=p.auth_key, tunnel_header=ip_class_by_addr_type[p.addr_type]( src=p.tun_dst, dst=p.tun_src), nat_t_header=p.nat_header, esn_en=esn_en) p.vpp_tun_sa = SecurityAssociation( encryption_type, spi=p.scapy_tun_spi, crypt_algo=p.crypt_algo, crypt_key=crypt_key, auth_algo=p.auth_algo, auth_key=p.auth_key, tunnel_header=ip_class_by_addr_type[p.addr_type]( dst=p.tun_dst, src=p.tun_src), nat_t_header=p.nat_header, esn_en=esn_en) def config_tra_params(p, encryption_type): esn_en = bool(p.flags & (VppEnum.vl_api_ipsec_sad_flags_t. IPSEC_API_SAD_FLAG_USE_ESN)) crypt_key = mk_scapy_crypt_key(p) p.scapy_tra_sa = SecurityAssociation( encryption_type, spi=p.vpp_tra_spi, crypt_algo=p.crypt_algo, crypt_key=crypt_key, auth_algo=p.auth_algo, auth_key=p.auth_key, nat_t_header=p.nat_header, esn_en=esn_en) p.vpp_tra_sa = SecurityAssociation( encryption_type, spi=p.scapy_tra_spi, crypt_algo=p.crypt_algo, crypt_key=crypt_key, auth_algo=p.auth_algo, auth_key=p.auth_key, nat_t_header=p.nat_header, esn_en=esn_en) class TemplateIpsec(VppTestCase): """ TRANSPORT MODE: ------ encrypt --- |tra_if| <-------> |VPP| ------ decrypt --- TUNNEL MODE: ------ encrypt --- plain --- |tun_if| <------- |VPP| <------ |pg1| ------ --- --- ------ decrypt --- plain --- |tun_if| -------> |VPP| ------> |pg1| ------ --- --- """ tun_spd_id = 1 tra_spd_id = 2 def ipsec_select_backend(self): """ empty method to be overloaded when necessary """ pass @classmethod def setUpClass(cls): super(TemplateIpsec, cls).setUpClass() @classmethod def tearDownClass(cls): super(TemplateIpsec, cls).tearDownClass() def setup_params(self): if not hasattr(self, 'ipv4_params'): self.ipv4_params = IPsecIPv4Params() if not hasattr(self, 'ipv6_params'): self.ipv6_params = IPsecIPv6Params() self.params = {self.ipv4_params.addr_type: self.ipv4_params, self.ipv6_params.addr_type: self.ipv6_params} def config_interfaces(self): self.create_pg_interfaces(range(3)) self.interfaces = list(self.pg_interfaces) for i in self.interfaces: i.admin_up() i.config_ip4() i.resolve_arp() i.config_ip6() i.resolve_ndp() def setUp(self): super(TemplateIpsec, self).setUp() self.setup_params() self.vpp_esp_protocol = (VppEnum.vl_api_ipsec_proto_t. IPSEC_API_PROTO_ESP) self.vpp_ah_protocol = (VppEnum.vl_api_ipsec_proto_t. IPSEC_API_PROTO_AH) self.config_interfaces() self.ipsec_select_backend() def unconfig_interfaces(self): for i in self.interfaces: i.admin_down() i.unconfig_ip4() i.unconfig_ip6() def tearDown(self): super(TemplateIpsec, self).tearDown() self.unconfig_interfaces() def show_commands_at_teardown(self): self.logger.info(self.vapi.cli("show hardware")) def gen_encrypt_pkts(self, p, sa, sw_intf, src, dst, count=1, payload_size=54): return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / sa.encrypt(IP(src=src, dst=dst) / ICMP() / Raw(b'X' * payload_size)) for i in range(count)] def gen_encrypt_pkts6(self, p, sa, sw_intf, src, dst, count=1, payload_size=54): return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / sa.encrypt(IPv6(src=src, dst=dst, hlim=p.inner_hop_limit, fl=p.inner_flow_label) / ICMPv6EchoRequest(id=0, seq=1, data='X' * payload_size)) for i in range(count)] def gen_pkts(self, sw_intf, src, dst, count=1, payload_size=54): return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / IP(src=src, dst=dst) / ICMP() / Raw(b'X' * payload_size) for i in range(count)] def gen_pkts6(self, p, sw_intf, src, dst, count=1, payload_size=54): return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / IPv6(src=src, dst=dst, hlim=p.inner_hop_limit, fl=p.inner_flow_label) / ICMPv6EchoRequest(id=0, seq=1, data='X' * payload_size) for i in range(count)] class IpsecTcp(object): def verify_tcp_checksum(self): self.vapi.cli("test http server") p = self.params[socket.AF_INET] send = (Ether(src=self.tun_if.remote_mac, dst=self.tun_if.local_mac) / p.scapy_tun_sa.encrypt(IP(src=p.remote_tun_if_host, dst=self.tun_if.local_ip4) / TCP(flags='S', dport=80))) self.logger.debug(ppp("Sending packet:", send)) recv = self.send_and_expect(self.tun_if, [send], self.tun_if) recv = recv[0] decrypted = p.vpp_tun_sa.decrypt(recv[IP]) self.assert_packet_checksums_valid(decrypted) class IpsecTcpTests(IpsecTcp): def test_tcp_checksum(self): """ verify checksum correctness for vpp generated packets """ self.verify_tcp_checksum() class IpsecTra4(object): """ verify methods for Transport v4 """ def get_replay_counts(self, p): replay_node_name = ('/err/%s/SA replayed packet' % self.tra4_decrypt_node_name[0]) count = self.statistics.get_err_counter(replay_node_name) if p.async_mode: replay_post_node_name = ('/err/%s/SA replayed packet' % self.tra4_decrypt_node_name[p.async_mode]) count += self.statistics.get_err_counter(replay_post_node_name) return count def get_hash_failed_counts(self, p): if ESP == self.encryption_type and p.crypt_algo == "AES-GCM": hash_failed_node_name = ('/err/%s/ESP decryption failed' % self.tra4_decrypt_node_name[p.async_mode]) else: hash_failed_node_name = ('/err/%s/Integrity check failed' % self.tra4_decrypt_node_name[p.async_mode]) count = self.statistics.get_err_counter(hash_failed_node_name) if p.async_mode: count += self.statistics.get_err_counter( '/err/crypto-dispatch/bad-hmac') return count def verify_tra_anti_replay(self): p = self.params[socket.AF_INET] esn_en = p.vpp_tra_sa.esn_en seq_cycle_node_name = \ ('/err/%s/sequence number cycled (packet dropped)' % self.tra4_encrypt_node_name) replay_count = self.get_replay_counts(p) hash_failed_count = self.get_hash_failed_counts(p) seq_cycle_count = self.statistics.get_err_counter(seq_cycle_node_name) if ESP == self.encryption_type: undersize_node_name = ('/err/%s/undersized packet' % self.tra4_decrypt_node_name[0]) undersize_count = self.statistics.get_err_counter( undersize_node_name) # # send packets with seq numbers 1->34 # this means the window size is still in Case B (see RFC4303 # Appendix A) # # for reasons i haven't investigated Scapy won't create a packet with # seq_num=0 # pkts = [(Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac) / p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(), seq_num=seq)) for seq in range(1, 34)] recv_pkts = self.send_and_expect(self.tra_if, pkts, self.tra_if) # replayed packets are dropped self.send_and_assert_no_replies(self.tra_if, pkts) replay_count += len(pkts) self.assertEqual(self.get_replay_counts(p), replay_count) # # now send a batch of packets all with the same sequence number # the first packet in the batch is legitimate, the rest bogus # self.vapi.cli("clear error") self.vapi.cli("clear node counters")
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <vnet/classify/policer_classify.h>
#include <vnet/l2/l2_input.h>
policer_classify_main_t policer_classify_main;
static void
vnet_policer_classify_feature_enable (vlib_main_t * vnm,
policer_classify_main_t * pcm,
u32 sw_if_index,
policer_classify_table_id_t tid,
int feature_enable)
{
if (tid == POLICER_CLASSIFY_TABLE_L2)
{
l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_POLICER_CLAS,
feature_enable);
}
else
{
vnet_feature_config_main_t *fcm;
u8 arc;
if (tid == POLICER_CLASSIFY_TABLE_IP4)
{
vnet_feature_enable_disable ("ip4-unicast", "ip4-policer-classify",
sw_if_index, feature_enable, 0, 0);
arc = vnet_get_feature_arc_index ("ip4-unicast");
}
else
{
vnet_feature_enable_disable ("ip6-unicast", "ip6-policer-classify",
sw_if_index, feature_enable, 0, 0);
arc = vnet_get_feature_arc_index ("ip6-unicast");
}
fcm = vnet_get_feature_arc_config_main (arc);
pcm->vnet_config_main[tid] = &fcm->config_main;
}
}
int
vnet_set_policer_classify_intfc (vlib_main_t * vm, u32 sw_if_index,
u32 ip4_table_index, u32 ip6_table_index,
u32 l2_table_index, u32 is_add)
{
policer_classify_main_t *pcm = &policer_classify_main;
vnet_classify_main_t *vcm = pcm->vnet_classify_main;
u32 pct[POLICER_CLASSIFY_N_TABLES] = { ip4_table_index, ip6_table_index,
l2_table_index
};
u32 ti;
/* Assume that we've validated sw_if_index in the API layer */
for (ti = 0; ti < POLICER_CLASSIFY_N_TABLES; ti++)
{
if (pct[ti] == ~0)
continue;
if (pool_is_free_index (vcm->tables, pct[ti]))
return VNET_API_ERROR_NO_SUCH_TABLE;
vec_validate_init_empty
(pcm->classify_table_index_by_sw_if_index[ti], sw_if_index, ~0);
/* Reject any DEL operation with wrong sw_if_index */
if (!is_add &&
(pct[ti] !=
pcm->classify_table_index_by_sw_if_index[ti][sw_if_index]))
{
clib_warning
("Non-existent intf_idx=%d with table_index=%d for delete",
sw_if_index, pct[ti]);
return VNET_API_ERROR_NO_SUCH_TABLE;
}
/* Return ok on ADD operaton if feature is already enabled */
if (is_add &&
pcm->classify_table_index_by_sw_if_index[ti][sw_if_index] != ~0)
return 0;
vnet_policer_classify_feature_enable (vm, pcm, sw_if_index, ti, is_add);
if (is_add)
pcm->classify_table_index_by_sw_if_index[ti][sw_if_index] = pct[ti];
else
pcm->classify_table_index_by_sw_if_index[ti][sw_if_index] = ~0;
}
return 0;
}
static clib_error_t *
set_policer_classify_command_fn (vlib_main_t * vm,
unformat_input_t * input,
vlib_cli_command_t * cmd)
{
vnet_main_t *vnm = vnet_get_main ();
u32 sw_if_index = ~0;
u32 ip4_table_index = ~0;
u32 ip6_table_index = ~0;
u32 l2_table_index = ~0;
u32 is_add = 1;
u32 idx_cnt = 0;
int rv;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (input, "interface %U", unformat_vnet_sw_interface,
vnm, &sw_if_index))
;
else if (unformat (input, "ip4-table %d", &ip4_table_index))
idx_cnt++;
else if (unformat (input, "ip6-table %d", &ip6_table_index))
idx_cnt++;
else if (unformat (input, "l2-table %d", &l2_table_index))
idx_cnt++;
else if (unformat (input, "del"))
is_add = 0;
else
break;
}
if (sw_if_index == ~0)
return clib_error_return (0, "Interface must be specified.");
if (!idx_cnt)
return clib_error_return (0, "Table index should be specified.");
if (idx_cnt > 1)
return clib_error_return (0, "Only one table index per API is allowed.");
rv = vnet_set_policer_classify_intfc (vm, sw_if_index, ip4_table_index,
ip6_table_index, l2_table_index,
is_add);
switch (rv)
{
case 0:
break;
case VNET_API_ERROR_NO_MATCHING_INTERFACE:
return clib_error_return (0, "No such interface");
case VNET_API_ERROR_NO_SUCH_ENTRY:
return clib_error_return (0, "No such classifier table");
}
return 0;
}
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (set_policer_classify_command, static) = {
.path = "set policer classify",
.short_help =
"set policer classify interface <int> [ip4-table <index>]\n"
" [ip6-table <index>] [l2-table <index>] [del]",
.function = set_policer_classify_command_fn,
};
/* *INDENT-ON* */
static uword
unformat_table_type (unformat_input_t * input, va_list * va)
{
u32 *r = va_arg (*va, u32 *);
u32 tid;
if (unformat (input, "ip4"))
tid = POLICER_CLASSIFY_TABLE_IP4;
else if (unformat (input, "ip6"))
tid = POLICER_CLASSIFY_TABLE_IP6;
else if (unformat (input, "l2"))
tid = POLICER_CLASSIFY_TABLE_L2;
else
return 0;
*r = tid;
return 1;
}
static clib_error_t *
show_policer_classify_command_fn (vlib_main_t * vm,
unformat_input_t * input,
vlib_cli_command_t * cmd)
{
policer_classify_main_t *pcm = &policer_classify_main;
u32 type = POLICER_CLASSIFY_N_TABLES;
u32 *vec_tbl;
int i;
if (unformat (input, "type %U", unformat_table_type, &type))
;
else
return clib_error_return (0, "Type must be specified.");;
if (type == POLICER_CLASSIFY_N_TABLES)
return clib_error_return (0, "Invalid table type.");
vec_tbl = pcm->classify_table_index_by_sw_if_index[type];
if (vec_len (vec_tbl))
vlib_cli_output (vm, "%10s%20s\t\t%s", "Intfc idx", "Classify table",
"Interface name");
else
vlib_cli_output (vm, "No tables configured.");
for (i = 0; i < vec_len (vec_tbl); i++)
{
if (vec_elt (vec_tbl, i) == ~0)
continue;
vlib_cli_output (vm, "%10d%20d\t\t%U", i, vec_elt (vec_tbl, i),
format_vnet_sw_if_index_name, pcm->vnet_main, i);
}
return 0;
}
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (show_policer_classify_command, static) = {
.path = "show classify policer",
.short_help = "show classify policer type [ip4|ip6|l2]",
.function = show_policer_classify_command_fn,
};
/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/