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:

committed by
Neale Ranns

parent
7b3a3df263
commit
abb2a42239
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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",
|
||||
|
@ -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
@ -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:
|
||||
|
@ -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)
|
||||
|
Reference in New Issue
Block a user