ip: add classifier-based ACLs support on ip punt

This feature allows one to add classifier-based ACLs on packets punted
from the ip infra, eg. to only whitelist specific sender(s).

Type: feature

Change-Id: Idab37b188583efbca980038875fc3e540cb2e880
Signed-off-by: Benoît Ganne <bganne@cisco.com>
This commit is contained in:
Benoît Ganne
2021-09-30 13:41:00 +02:00
committed by Neale Ranns
parent 7b3a3df263
commit abb2a42239
7 changed files with 386 additions and 300 deletions

View File

@ -420,6 +420,22 @@ autoreply define input_acl_set_interface
bool is_add;
};
/** \brief Add/del punt ACL
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
@param ip4_table_index - ip4 punt classify table index (~0 for skip)
@param ip6_table_index - ip6 punt classify table index (~0 for skip)
@param is_add - add punt ACL if non-zero, else delete
*/
autoreply define punt_acl_add_del
{
u32 client_index;
u32 context;
u32 ip4_table_index [default=0xffffffff];
u32 ip6_table_index [default=0xffffffff];
bool is_add [default=true];
};
/** \brief Set/unset output ACL interface
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request

View File

@ -896,6 +896,22 @@ static void vl_api_input_acl_set_interface_t_handler
REPLY_MACRO (VL_API_INPUT_ACL_SET_INTERFACE_REPLY);
}
static void
vl_api_punt_acl_add_del_t_handler (vl_api_punt_acl_add_del_t *mp)
{
vlib_main_t *vm = vlib_get_main ();
vl_api_punt_acl_add_del_reply_t *rmp;
int rv;
rv = vnet_set_in_out_acl_intfc (
vm, 0 /* sw_if_index */, ~0 /* ip4_table_index */,
~0 /* ip6_table_index */, ~0 /* l2_table_index */,
ntohl (mp->ip4_table_index), ntohl (mp->ip6_table_index), mp->is_add,
0 /* is_output */);
REPLY_MACRO (VL_API_PUNT_ACL_ADD_DEL_REPLY);
}
static void vl_api_output_acl_set_interface_t_handler
(vl_api_output_acl_set_interface_t * mp)
{

View File

@ -21,63 +21,75 @@
in_out_acl_main_t in_out_acl_main;
static int
vnet_in_out_acl_ip_feature_enable (vlib_main_t * vnm,
in_out_acl_main_t * am,
u32 sw_if_index,
in_out_acl_table_id_t tid,
int feature_enable, int is_output)
vnet_in_out_acl_feature_enable (in_out_acl_main_t *am, u32 sw_if_index,
in_out_acl_table_id_t tid, int feature_enable,
int is_output)
{
const char *arc_name, *feature_name;
vnet_feature_config_main_t *fcm;
u8 arc;
int rv;
if (tid == IN_OUT_ACL_TABLE_L2)
switch (tid)
{
case IN_OUT_ACL_N_TABLES:
return VNET_API_ERROR_NO_SUCH_TABLE;
case IN_OUT_ACL_TABLE_L2:
if (is_output)
l2output_intf_bitmap_enable (sw_if_index, L2OUTPUT_FEAT_ACL,
feature_enable);
else
l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_ACL,
feature_enable);
return 0;
case IN_OUT_ACL_TABLE_IP4:
arc_name = is_output ? "ip4-output" : "ip4-unicast";
feature_name = is_output ? "ip4-outacl" : "ip4-inacl";
break;
case IN_OUT_ACL_TABLE_IP6:
arc_name = is_output ? "ip6-output" : "ip6-unicast";
feature_name = is_output ? "ip6-outacl" : "ip6-inacl";
break;
case IN_OUT_ACL_TABLE_IP4_PUNT:
if (sw_if_index != 0)
return VNET_API_ERROR_INVALID_INTERFACE;
arc_name = "ip4-punt";
feature_name = "ip4-punt-acl";
break;
case IN_OUT_ACL_TABLE_IP6_PUNT:
if (sw_if_index != 0)
return VNET_API_ERROR_INVALID_INTERFACE;
arc_name = "ip6-punt";
feature_name = "ip6-punt-acl";
break;
}
else
{ /* IP[46] */
vnet_feature_config_main_t *fcm;
u8 arc;
if (tid == IN_OUT_ACL_TABLE_IP4)
{
char *arc_name = is_output ? "ip4-output" : "ip4-unicast";
vnet_feature_enable_disable (arc_name,
is_output ? "ip4-outacl" : "ip4-inacl",
sw_if_index, feature_enable, 0, 0);
arc = vnet_get_feature_arc_index (arc_name);
}
else
{
char *arc_name = is_output ? "ip6-output" : "ip6-unicast";
vnet_feature_enable_disable (arc_name,
is_output ? "ip6-outacl" : "ip6-inacl",
sw_if_index, feature_enable, 0, 0);
arc = vnet_get_feature_arc_index (arc_name);
}
rv = vnet_feature_enable_disable (arc_name, feature_name, sw_if_index,
feature_enable, 0, 0);
if (rv)
return rv;
fcm = vnet_get_feature_arc_config_main (arc);
am->vnet_config_main[is_output][tid] = &fcm->config_main;
}
arc = vnet_get_feature_arc_index (arc_name);
fcm = vnet_get_feature_arc_config_main (arc);
am->vnet_config_main[is_output][tid] = &fcm->config_main;
return 0;
}
int
vnet_set_in_out_acl_intfc (vlib_main_t * vm, u32 sw_if_index,
u32 ip4_table_index,
u32 ip6_table_index, u32 l2_table_index,
u32 is_add, u32 is_output)
vnet_set_in_out_acl_intfc (vlib_main_t *vm, u32 sw_if_index,
u32 ip4_table_index, u32 ip6_table_index,
u32 l2_table_index, u32 ip4_punt_table_index,
u32 ip6_punt_table_index, u32 is_add, u32 is_output)
{
in_out_acl_main_t *am = &in_out_acl_main;
vnet_classify_main_t *vcm = am->vnet_classify_main;
u32 acl[IN_OUT_ACL_N_TABLES] = { ip4_table_index, ip6_table_index,
l2_table_index
u32 acl[IN_OUT_ACL_N_TABLES] = {
ip4_table_index, ip6_table_index, l2_table_index,
ip4_punt_table_index, ip6_punt_table_index,
};
u32 ti;
int rv;
/* Assume that we've validated sw_if_index in the API layer */
@ -111,8 +123,10 @@ vnet_set_in_out_acl_intfc (vlib_main_t * vm, u32 sw_if_index,
!= ~0)
return 0;
vnet_in_out_acl_ip_feature_enable (vm, am, sw_if_index, ti, is_add,
is_output);
rv = vnet_in_out_acl_feature_enable (am, sw_if_index, ti, is_add,
is_output);
if (rv)
return rv;
if (is_add)
am->classify_table_index_by_sw_if_index[is_output][ti][sw_if_index] =
@ -130,9 +144,10 @@ vnet_set_input_acl_intfc (vlib_main_t * vm, u32 sw_if_index,
u32 ip4_table_index,
u32 ip6_table_index, u32 l2_table_index, u32 is_add)
{
return vnet_set_in_out_acl_intfc (vm, sw_if_index, ip4_table_index,
ip6_table_index, l2_table_index, is_add,
IN_OUT_ACL_INPUT_TABLE_GROUP);
return vnet_set_in_out_acl_intfc (
vm, sw_if_index, ip4_table_index, ip6_table_index, l2_table_index,
~0 /* ip4_punt_table_index */, ~0 /* ip6_punt_table_index */, is_add,
IN_OUT_ACL_INPUT_TABLE_GROUP);
}
int
@ -141,9 +156,10 @@ vnet_set_output_acl_intfc (vlib_main_t * vm, u32 sw_if_index,
u32 ip6_table_index, u32 l2_table_index,
u32 is_add)
{
return vnet_set_in_out_acl_intfc (vm, sw_if_index, ip4_table_index,
ip6_table_index, l2_table_index, is_add,
IN_OUT_ACL_OUTPUT_TABLE_GROUP);
return vnet_set_in_out_acl_intfc (
vm, sw_if_index, ip4_table_index, ip6_table_index, l2_table_index,
~0 /* ip4_punt_table_index */, ~0 /* ip6_punt_table_index */, is_add,
IN_OUT_ACL_OUTPUT_TABLE_GROUP);
}
static clib_error_t *
@ -155,6 +171,8 @@ set_in_out_acl_command_fn (vlib_main_t * vm,
u32 sw_if_index = ~0;
u32 ip4_table_index = ~0;
u32 ip6_table_index = ~0;
u32 ip4_punt_table_index = ~0;
u32 ip6_punt_table_index = ~0;
u32 l2_table_index = ~0;
u32 is_add = 1;
u32 idx_cnt = 0;
@ -169,6 +187,10 @@ set_in_out_acl_command_fn (vlib_main_t * vm,
idx_cnt++;
else if (unformat (input, "ip6-table %d", &ip6_table_index))
idx_cnt++;
else if (unformat (input, "ip4-punt-table %d", &ip4_punt_table_index))
idx_cnt++;
else if (unformat (input, "ip6-punt-table %d", &ip6_punt_table_index))
idx_cnt++;
else if (unformat (input, "l2-table %d", &l2_table_index))
idx_cnt++;
else if (unformat (input, "del"))
@ -186,9 +208,9 @@ set_in_out_acl_command_fn (vlib_main_t * vm,
if (idx_cnt > 1)
return clib_error_return (0, "Only one table index per API is allowed.");
rv = vnet_set_in_out_acl_intfc (vm, sw_if_index, ip4_table_index,
ip6_table_index, l2_table_index, is_add,
is_output);
rv = vnet_set_in_out_acl_intfc (
vm, sw_if_index, ip4_table_index, ip6_table_index, l2_table_index,
ip4_punt_table_index, ip6_punt_table_index, is_add, is_output);
switch (rv)
{
@ -200,6 +222,9 @@ set_in_out_acl_command_fn (vlib_main_t * vm,
case VNET_API_ERROR_NO_SUCH_ENTRY:
return clib_error_return (0, "No such classifier table");
default:
return clib_error_return (0, "Error: %d", rv);
}
return 0;
}
@ -232,11 +257,12 @@ set_output_acl_command_fn (vlib_main_t * vm,
*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (set_input_acl_command, static) = {
.path = "set interface input acl",
.short_help =
.path = "set interface input acl",
.short_help =
"set interface input acl intfc <int> [ip4-table <index>]\n"
" [ip6-table <index>] [l2-table <index>] [del]",
.function = set_input_acl_command_fn,
" [ip6-table <index>] [l2-table <index>] [ip4-punt-table <index>]\n"
" [ip6-punt-table <index> [del]",
.function = set_input_acl_command_fn,
};
VLIB_CLI_COMMAND (set_output_acl_command, static) = {
.path = "set interface output acl",

View File

@ -31,6 +31,8 @@ typedef enum
IN_OUT_ACL_TABLE_IP4,
IN_OUT_ACL_TABLE_IP6,
IN_OUT_ACL_TABLE_L2,
IN_OUT_ACL_TABLE_IP4_PUNT,
IN_OUT_ACL_TABLE_IP6_PUNT,
IN_OUT_ACL_N_TABLES,
} in_out_acl_table_id_t;
@ -59,14 +61,14 @@ typedef struct
extern in_out_acl_main_t in_out_acl_main;
int vnet_set_in_out_acl_intfc (vlib_main_t * vm, u32 sw_if_index,
u32 ip4_table_index,
u32 ip6_table_index,
u32 l2_table_index, u32 is_add, u32 is_output);
int vnet_set_in_out_acl_intfc (vlib_main_t *vm, u32 sw_if_index,
u32 ip4_table_index, u32 ip6_table_index,
u32 l2_table_index, u32 ip4_punt_table_index,
u32 ip6_punt_table_index, u32 is_add,
u32 is_output);
int vnet_set_input_acl_intfc (vlib_main_t * vm, u32 sw_if_index,
u32 ip4_table_index,
u32 ip6_table_index,
int vnet_set_input_acl_intfc (vlib_main_t *vm, u32 sw_if_index,
u32 ip4_table_index, u32 ip6_table_index,
u32 l2_table_index, u32 is_add);
int vnet_set_output_acl_intfc (vlib_main_t * vm, u32 sw_if_index,

File diff suppressed because it is too large Load Diff

View File

@ -116,11 +116,10 @@ class TestClassifier(VppTestCase):
if self.acl_active_table.endswith('out'):
self.output_acl_set_interface(
self.pg0, self.acl_tbl_idx.get(self.acl_active_table), 0)
self.acl_active_table = ''
elif self.acl_active_table != '':
self.input_acl_set_interface(
self.pg0, self.acl_tbl_idx.get(self.acl_active_table), 0)
self.acl_active_table = ''
self.acl_active_table = ''
for intf in self.interfaces:
if self.af == AF_INET:

View File

@ -13,6 +13,7 @@ from util import ppp
from template_classifier import TestClassifier, VarMask, VarMatch
from vpp_ip_route import VppIpRoute, VppRoutePath
from vpp_ip import INVALID_INDEX
from vpp_papi import VppEnum
# Tests split to different test case classes because of issue reported in
@ -843,5 +844,81 @@ class TestClassifierPBR(TestClassifier):
# and the table should be gone.
self.assertFalse(self.verify_vrf(self.pbr_vrfid))
class TestClassifierPunt(TestClassifier):
""" Classifier punt Test Case """
@classmethod
def setUpClass(cls):
super(TestClassifierPunt, cls).setUpClass()
@classmethod
def tearDownClass(cls):
super(TestClassifierPunt, cls).tearDownClass()
def test_punt_udp(self):
""" IPv4/UDP protocol punt ACL test
Test scenario for basic punt ACL with UDP protocol
- Create IPv4 stream for pg0 -> pg1 interface.
- Create punt ACL with UDP IP protocol.
- Send and verify received packets on pg1 interface.
"""
sport = 6754
dport = 17923
key = 'ip4_udp_punt'
self.create_classify_table(
key,
self.build_ip_mask(
src_ip='ffffffff',
proto='ff',
src_port='ffff'))
table_index = self.acl_tbl_idx.get(key)
self.vapi.punt_acl_add_del(ip4_table_index=table_index)
self.acl_active_table = key
# punt udp packets to dport received on pg0 through pg1
self.vapi.set_punt(
is_add=1,
punt={
'type': VppEnum.vl_api_punt_type_t.PUNT_API_TYPE_L4,
'punt': {
'l4': {
'af': VppEnum.vl_api_address_family_t.ADDRESS_IP4,
'protocol': VppEnum.vl_api_ip_proto_t.IP_API_PROTO_UDP,
'port': dport,
}}})
self.vapi.ip_punt_redirect(punt={
'rx_sw_if_index': self.pg0.sw_if_index,
'tx_sw_if_index': self.pg1.sw_if_index,
'nh': self.pg1.remote_ip4,
})
pkts = [(Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
UDP(sport=sport, dport=dport) /
Raw('\x17' * 100))] * 2
# allow a session but not matching the stream: expect to drop
self.create_classify_session(
table_index,
self.build_ip_match(src_ip=self.pg0.remote_ip4,
proto=socket.IPPROTO_UDP, src_port=sport + 10))
self.send_and_assert_no_replies(self.pg0, pkts)
# allow a session matching the stream: expect to pass
self.create_classify_session(
table_index,
self.build_ip_match(src_ip=self.pg0.remote_ip4,
proto=socket.IPPROTO_UDP, src_port=sport))
self.send_and_expect_only(self.pg0, pkts, self.pg1)
# cleanup
self.acl_active_table = ''
self.vapi.punt_acl_add_del(ip4_table_index=table_index, is_add=0)
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)