#include <vnet/vnet.h>
#include <vnet/plugin/plugin.h>
#include <acl/acl.h>
-#include <acl/l2sess.h>
#include <vnet/l2/l2_classify.h>
#include <vnet/classify/input_acl.h>
+#include <vpp/app/version.h>
#include <vlibapi/api.h>
#include <vlibmemory/api.h>
#include <acl/acl_all_api_h.h>
#undef vl_api_version
-#include "node_in.h"
-#include "node_out.h"
+#include "fa_node.h"
acl_main_t acl_main;
-/*
- * A handy macro to set up a message reply.
- * Assumes that the following variables are available:
- * mp - pointer to request message
- * rmp - pointer to reply message type
- * rv - return value
- */
-
-#define REPLY_MACRO(t) \
-do { \
- unix_shared_memory_queue_t * q = \
- vl_api_client_index_to_input_queue (mp->client_index); \
- if (!q) \
- return; \
- \
- rmp = vl_msg_api_alloc (sizeof (*rmp)); \
- rmp->_vl_msg_id = ntohs((t)+sm->msg_id_base); \
- rmp->context = mp->context; \
- rmp->retval = ntohl(rv); \
- \
- vl_msg_api_send_shmem (q, (u8 *)&rmp); \
-} while(0);
-
-#define REPLY_MACRO2(t, body) \
-do { \
- unix_shared_memory_queue_t * q; \
- rv = vl_msg_api_pd_handler (mp, rv); \
- q = vl_api_client_index_to_input_queue (mp->client_index); \
- if (!q) \
- return; \
- \
- rmp = vl_msg_api_alloc (sizeof (*rmp)); \
- rmp->_vl_msg_id = ntohs((t)+am->msg_id_base); \
- rmp->context = mp->context; \
- rmp->retval = ntohl(rv); \
- do {body;} while (0); \
- vl_msg_api_send_shmem (q, (u8 *)&rmp); \
-} while(0);
-
-#define REPLY_MACRO3(t, n, body) \
-do { \
- unix_shared_memory_queue_t * q; \
- rv = vl_msg_api_pd_handler (mp, rv); \
- q = vl_api_client_index_to_input_queue (mp->client_index); \
- if (!q) \
- return; \
- \
- rmp = vl_msg_api_alloc (sizeof (*rmp) + n); \
- rmp->_vl_msg_id = ntohs((t)+am->msg_id_base); \
- rmp->context = mp->context; \
- rmp->retval = ntohl(rv); \
- do {body;} while (0); \
- vl_msg_api_send_shmem (q, (u8 *)&rmp); \
-} while(0);
-
+#define REPLY_MSG_ID_BASE am->msg_id_base
+#include <vlibapi/api_helper_macros.h>
/* List of message types that this plugin understands */
#define foreach_acl_plugin_api_msg \
_(ACL_PLUGIN_GET_VERSION, acl_plugin_get_version) \
+_(ACL_PLUGIN_CONTROL_PING, acl_plugin_control_ping) \
_(ACL_ADD_REPLACE, acl_add_replace) \
_(ACL_DEL, acl_del) \
_(ACL_INTERFACE_ADD_DEL, acl_interface_add_del) \
_(MACIP_ACL_DUMP, macip_acl_dump) \
_(MACIP_ACL_INTERFACE_GET, macip_acl_interface_get)
-/*
- * This routine exists to convince the vlib plugin framework that
- * we haven't accidentally copied a random .dll into the plugin directory.
- *
- * Also collects global variable pointers passed from the vpp engine
- */
-
-clib_error_t *
-vlib_plugin_register (vlib_main_t * vm, vnet_plugin_handoff_t * h,
- int from_early_init)
-{
- acl_main_t *am = &acl_main;
- clib_error_t *error = 0;
-
- am->vlib_main = vm;
- am->vnet_main = h->vnet_main;
- am->ethernet_main = h->ethernet_main;
-
- l2sess_vlib_plugin_register(vm, h, from_early_init);
-
- return error;
-}
-
+/* *INDENT-OFF* */
+VLIB_PLUGIN_REGISTER () = {
+ .version = VPP_BUILD_VER,
+ .description = "Access Control Lists",
+};
+/* *INDENT-ON* */
static void
vl_api_acl_plugin_get_version_t_handler (vl_api_acl_plugin_get_version_t * mp)
vl_msg_api_send_shmem (q, (u8 *) & rmp);
}
+static void
+vl_api_acl_plugin_control_ping_t_handler (vl_api_acl_plugin_control_ping_t * mp)
+{
+ vl_api_acl_plugin_control_ping_reply_t *rmp;
+ acl_main_t *am = &acl_main;
+ int rv = 0;
+
+ /* *INDENT-OFF* */
+ REPLY_MACRO2 (VL_API_ACL_PLUGIN_CONTROL_PING_REPLY,
+ ({
+ rmp->vpe_pid = ntohl (getpid ());
+ }));
+ /* *INDENT-ON* */
+}
static int
acl_add_list (u32 count, vl_api_acl_rule_t rules[],
r->src_prefixlen = rules[i].src_ip_prefix_len;
r->dst_prefixlen = rules[i].dst_ip_prefix_len;
r->proto = rules[i].proto;
- r->src_port_or_type_first = rules[i].srcport_or_icmptype_first;
- r->src_port_or_type_last = rules[i].srcport_or_icmptype_last;
- r->dst_port_or_code_first = rules[i].dstport_or_icmpcode_first;
- r->dst_port_or_code_last = rules[i].dstport_or_icmpcode_last;
+ r->src_port_or_type_first = ntohs ( rules[i].srcport_or_icmptype_first );
+ r->src_port_or_type_last = ntohs ( rules[i].srcport_or_icmptype_last );
+ r->dst_port_or_code_first = ntohs ( rules[i].dstport_or_icmpcode_first );
+ r->dst_port_or_code_last = ntohs ( rules[i].dstport_or_icmpcode_last );
r->tcp_flags_value = rules[i].tcp_flags_value;
r->tcp_flags_mask = rules[i].tcp_flags_mask;
}
am->acl_ip4_input_classify_table_by_sw_if_index[sw_if_index] = ~0;
acl_classify_add_del_table_big (cm, ip4_5tuple_mask,
sizeof (ip4_5tuple_mask) - 1, ~0,
- am->l2_input_classify_next_acl,
+ am->l2_input_classify_next_acl_ip4,
&ip4_table_index, 0);
}
if (am->acl_ip6_input_classify_table_by_sw_if_index[sw_if_index] != ~0)
am->acl_ip6_input_classify_table_by_sw_if_index[sw_if_index] = ~0;
acl_classify_add_del_table_big (cm, ip6_5tuple_mask,
sizeof (ip6_5tuple_mask) - 1, ~0,
- am->l2_input_classify_next_acl,
+ am->l2_input_classify_next_acl_ip6,
&ip6_table_index, 0);
}
am->acl_ip4_output_classify_table_by_sw_if_index[sw_if_index] = ~0;
acl_classify_add_del_table_big (cm, ip4_5tuple_mask,
sizeof (ip4_5tuple_mask) - 1, ~0,
- am->l2_output_classify_next_acl,
+ am->l2_output_classify_next_acl_ip4,
&ip4_table_index, 0);
}
if (am->acl_ip6_output_classify_table_by_sw_if_index[sw_if_index] != ~0)
am->acl_ip6_output_classify_table_by_sw_if_index[sw_if_index] = ~0;
acl_classify_add_del_table_big (cm, ip6_5tuple_mask,
sizeof (ip6_5tuple_mask) - 1, ~0,
- am->l2_output_classify_next_acl,
+ am->l2_output_classify_next_acl_ip6,
&ip6_table_index, 0);
}
rv =
acl_classify_add_del_table_big (cm, ip4_5tuple_mask,
sizeof (ip4_5tuple_mask) - 1, ~0,
- am->l2_input_classify_next_acl,
+ am->l2_input_classify_next_acl_ip4,
&ip4_table_index, 1);
if (rv)
return rv;
rv =
acl_classify_add_del_table_big (cm, ip6_5tuple_mask,
sizeof (ip6_5tuple_mask) - 1, ~0,
- am->l2_input_classify_next_acl,
+ am->l2_input_classify_next_acl_ip6,
&ip6_table_index, 1);
if (rv)
{
acl_classify_add_del_table_big (cm, ip4_5tuple_mask,
sizeof (ip4_5tuple_mask) - 1, ~0,
- am->l2_input_classify_next_acl,
+ am->l2_input_classify_next_acl_ip4,
&ip4_table_index, 0);
return rv;
}
{
acl_classify_add_del_table_big (cm, ip6_5tuple_mask,
sizeof (ip6_5tuple_mask) - 1, ~0,
- am->l2_input_classify_next_acl,
+ am->l2_input_classify_next_acl_ip6,
&ip6_table_index, 0);
acl_classify_add_del_table_big (cm, ip4_5tuple_mask,
sizeof (ip4_5tuple_mask) - 1, ~0,
- am->l2_input_classify_next_acl,
+ am->l2_input_classify_next_acl_ip4,
&ip4_table_index, 0);
return rv;
}
rv =
acl_classify_add_del_table_big (cm, ip4_5tuple_mask,
sizeof (ip4_5tuple_mask) - 1, ~0,
- am->l2_output_classify_next_acl,
+ am->l2_output_classify_next_acl_ip4,
&ip4_table_index, 1);
if (rv)
return rv;
rv =
acl_classify_add_del_table_big (cm, ip6_5tuple_mask,
sizeof (ip6_5tuple_mask) - 1, ~0,
- am->l2_output_classify_next_acl,
+ am->l2_output_classify_next_acl_ip6,
&ip6_table_index, 1);
if (rv)
{
acl_classify_add_del_table_big (cm, ip4_5tuple_mask,
sizeof (ip4_5tuple_mask) - 1, ~0,
- am->l2_output_classify_next_acl,
+ am->l2_output_classify_next_acl_ip4,
&ip4_table_index, 0);
return rv;
}
{
acl_classify_add_del_table_big (cm, ip6_5tuple_mask,
sizeof (ip6_5tuple_mask) - 1, ~0,
- am->l2_output_classify_next_acl,
+ am->l2_output_classify_next_acl_ip6,
&ip6_table_index, 0);
acl_classify_add_del_table_big (cm, ip4_5tuple_mask,
sizeof (ip4_5tuple_mask) - 1, ~0,
- am->l2_output_classify_next_acl,
+ am->l2_output_classify_next_acl_ip4,
&ip4_table_index, 0);
return rv;
}
}
+
int
acl_interface_in_enable_disable (acl_main_t * am, u32 sw_if_index,
int enable_disable)
sw_if_index))
return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+ acl_fa_enable_disable(sw_if_index, 1, enable_disable);
+
if (enable_disable)
{
rv = acl_hook_l2_input_classify (am, sw_if_index);
sw_if_index))
return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+ acl_fa_enable_disable(sw_if_index, 0, enable_disable);
+
if (enable_disable)
{
rv = acl_hook_l2_output_classify (am, sw_if_index);
}
-static void *
-get_ptr_to_offset (vlib_buffer_t * b0, int offset)
-{
- u8 *p = vlib_buffer_get_current (b0) + offset;
- return p;
-}
-
-static u8
-acl_get_l4_proto (vlib_buffer_t * b0, int node_is_ip6)
-{
- u8 proto;
- int proto_offset;
- if (node_is_ip6)
- {
- proto_offset = 20;
- }
- else
- {
- proto_offset = 23;
- }
- proto = *((u8 *) vlib_buffer_get_current (b0) + proto_offset);
- return proto;
-}
-
-static int
-acl_match_addr (ip46_address_t * addr1, ip46_address_t * addr2, int prefixlen,
- int is_ip6)
-{
- if (prefixlen == 0)
- {
- /* match any always succeeds */
- return 1;
- }
- if (is_ip6)
- {
- if (memcmp (addr1, addr2, prefixlen / 8))
- {
- /* If the starting full bytes do not match, no point in bittwidling the thumbs further */
- return 0;
- }
- if (prefixlen % 8)
- {
- u8 b1 = *((u8 *) addr1 + 1 + prefixlen / 8);
- u8 b2 = *((u8 *) addr2 + 1 + prefixlen / 8);
- u8 mask0 = (0xff - ((1 << (8 - (prefixlen % 8))) - 1));
- return (b1 & mask0) == b2;
- }
- else
- {
- /* The prefix fits into integer number of bytes, so nothing left to do */
- return 1;
- }
- }
- else
- {
- uint32_t a1 = ntohl (addr1->ip4.as_u32);
- uint32_t a2 = ntohl (addr2->ip4.as_u32);
- uint32_t mask0 = 0xffffffff - ((1 << (32 - prefixlen)) - 1);
- return (a1 & mask0) == a2;
- }
-}
-
-static int
-acl_match_port (u16 port, u16 port_first, u16 port_last, int is_ip6)
-{
- return ((port >= port_first) && (port <= port_last));
-}
-
-static int
-acl_packet_match (acl_main_t * am, u32 acl_index, vlib_buffer_t * b0,
- u8 * r_action, int *r_is_ip6, u32 * r_acl_match_p,
- u32 * r_rule_match_p, u32 * trace_bitmap)
-{
- ethernet_header_t *h0;
- u16 type0;
-
- ip46_address_t src, dst;
- int is_ip6;
- int is_ip4;
- u8 proto;
- u16 src_port;
- u16 dst_port;
- u8 tcp_flags = 0;
- int i;
- acl_list_t *a;
- acl_rule_t *r;
-
- h0 = vlib_buffer_get_current (b0);
- type0 = clib_net_to_host_u16 (h0->type);
- is_ip4 = (type0 == ETHERNET_TYPE_IP4);
- is_ip6 = (type0 == ETHERNET_TYPE_IP6);
-
- if (!(is_ip4 || is_ip6))
- {
- return 0;
- }
- /* The bunch of hardcoded offsets here is intentional to get rid of them
- ASAP, when getting to a faster matching code */
- if (is_ip4)
- {
- clib_memcpy (&src.ip4, get_ptr_to_offset (b0, 26), 4);
- clib_memcpy (&dst.ip4, get_ptr_to_offset (b0, 30), 4);
- proto = acl_get_l4_proto (b0, 0);
- if (1 == proto)
- {
- *trace_bitmap |= 0x00000001;
- /* type */
- src_port = *(u8 *) get_ptr_to_offset (b0, 34);
- /* code */
- dst_port = *(u8 *) get_ptr_to_offset (b0, 35);
- }
- else
- {
- /* assume TCP/UDP */
- src_port = (*(u16 *) get_ptr_to_offset (b0, 34));
- dst_port = (*(u16 *) get_ptr_to_offset (b0, 36));
- /* UDP gets ability to check on an oddball data byte as a bonus */
- tcp_flags = *(u8 *) get_ptr_to_offset (b0, 14 + 20 + 13);
- }
- }
- else /* is_ipv6 implicitly */
- {
- clib_memcpy (&src, get_ptr_to_offset (b0, 22), 16);
- clib_memcpy (&dst, get_ptr_to_offset (b0, 38), 16);
- proto = acl_get_l4_proto (b0, 1);
- if (58 == proto)
- {
- *trace_bitmap |= 0x00000002;
- /* type */
- src_port = *(u8 *) get_ptr_to_offset (b0, 54);
- /* code */
- dst_port = *(u8 *) get_ptr_to_offset (b0, 55);
- }
- else
- {
- /* assume TCP/UDP */
- src_port = (*(u16 *) get_ptr_to_offset (b0, 54));
- dst_port = (*(u16 *) get_ptr_to_offset (b0, 56));
- tcp_flags = *(u8 *) get_ptr_to_offset (b0, 14 + 40 + 13);
- }
- }
- if (pool_is_free_index (am->acls, acl_index))
- {
- if (r_acl_match_p)
- *r_acl_match_p = acl_index;
- if (r_rule_match_p)
- *r_rule_match_p = -1;
- /* the ACL does not exist but is used for policy. Block traffic. */
- return 0;
- }
- a = am->acls + acl_index;
- for (i = 0; i < a->count; i++)
- {
- r = a->rules + i;
- if (is_ip6 != r->is_ipv6)
- {
- continue;
- }
- if (!acl_match_addr (&dst, &r->dst, r->dst_prefixlen, is_ip6))
- continue;
- if (!acl_match_addr (&src, &r->src, r->src_prefixlen, is_ip6))
- continue;
- if (r->proto)
- {
- if (proto != r->proto)
- continue;
- if (!acl_match_port
- (src_port, r->src_port_or_type_first, r->src_port_or_type_last,
- is_ip6))
- continue;
- if (!acl_match_port
- (dst_port, r->dst_port_or_code_first, r->dst_port_or_code_last,
- is_ip6))
- continue;
- /* No need for check of proto == TCP, since in other rules both fields should be zero, so this match will succeed */
- if ((tcp_flags & r->tcp_flags_mask) != r->tcp_flags_value)
- continue;
- }
- /* everything matches! */
- *r_action = r->is_permit;
- *r_is_ip6 = is_ip6;
- if (r_acl_match_p)
- *r_acl_match_p = acl_index;
- if (r_rule_match_p)
- *r_rule_match_p = i;
- return 1;
- }
- return 0;
-}
-
-void
-input_acl_packet_match (u32 sw_if_index, vlib_buffer_t * b0, u32 * nextp,
- u32 * acl_match_p, u32 * rule_match_p,
- u32 * trace_bitmap)
-{
- acl_main_t *am = &acl_main;
- uint8_t action = 0;
- int is_ip6 = 0;
- int i;
- vec_validate (am->input_acl_vec_by_sw_if_index, sw_if_index);
- for (i = 0; i < vec_len (am->input_acl_vec_by_sw_if_index[sw_if_index]);
- i++)
- {
- if (acl_packet_match
- (am, am->input_acl_vec_by_sw_if_index[sw_if_index][i], b0, &action,
- &is_ip6, acl_match_p, rule_match_p, trace_bitmap))
- {
- if (is_ip6)
- {
- *nextp = am->acl_in_ip6_match_next[action];
- }
- else
- {
- *nextp = am->acl_in_ip4_match_next[action];
- }
- return;
- }
- }
- if (vec_len (am->input_acl_vec_by_sw_if_index[sw_if_index]) > 0)
- {
- /* If there are ACLs and none matched, deny by default */
- *nextp = 0;
- }
-
-}
-
-void
-output_acl_packet_match (u32 sw_if_index, vlib_buffer_t * b0, u32 * nextp,
- u32 * acl_match_p, u32 * rule_match_p,
- u32 * trace_bitmap)
-{
- acl_main_t *am = &acl_main;
- uint8_t action = 0;
- int is_ip6 = 0;
- int i;
- vec_validate (am->output_acl_vec_by_sw_if_index, sw_if_index);
- for (i = 0; i < vec_len (am->output_acl_vec_by_sw_if_index[sw_if_index]);
- i++)
- {
- if (acl_packet_match
- (am, am->output_acl_vec_by_sw_if_index[sw_if_index][i], b0, &action,
- &is_ip6, acl_match_p, rule_match_p, trace_bitmap))
- {
- if (is_ip6)
- {
- *nextp = am->acl_out_ip6_match_next[action];
- }
- else
- {
- *nextp = am->acl_out_ip4_match_next[action];
- }
- return;
- }
- }
- if (vec_len (am->output_acl_vec_by_sw_if_index[sw_if_index]) > 0)
- {
- /* If there are ACLs and none matched, deny by default */
- *nextp = 0;
- }
-}
-
typedef struct
{
u8 is_ipv6;
u8 prefix_len;
u32 count;
u32 table_index;
+ u32 arp_table_index;
} macip_match_type_t;
static u32
vec_sort_with_function (mvec, match_type_compare);
/* Create the classifier tables */
last_table = ~0;
+ /* First add ARP tables */
+ vec_foreach (mt, mvec)
+ {
+ int mask_len;
+ int is6 = mt->is_ipv6;
+
+ mt->arp_table_index = ~0;
+ if (!is6)
+ {
+ memset (mask, 0, sizeof (mask));
+ memcpy (&mask[6], mt->mac_mask, 6);
+ memset (&mask[12], 0xff, 2); /* ethernet protocol */
+ memcpy (&mask[14 + 8], mt->mac_mask, 6);
+
+ for (i = 0; i < (mt->prefix_len / 8); i++)
+ mask[14 + 14 + i] = 0xff;
+ if (mt->prefix_len % 8)
+ mask[14 + 14 + (mt->prefix_len / 8)] = 0xff - ((1 << (8 - mt->prefix_len % 8)) - 1);
+
+ mask_len = ((14 + 14 + ((mt->prefix_len+7) / 8) +
+ (sizeof (u32x4)-1))/sizeof(u32x4)) * sizeof (u32x4);
+ acl_classify_add_del_table_small (cm, mask, mask_len, last_table,
+ (~0 == last_table) ? 0 : ~0, &mt->arp_table_index,
+ 1);
+ last_table = mt->arp_table_index;
+ }
+ }
+ /* Now add IP[46] tables */
vec_foreach (mt, mvec)
{
int mask_len;
int l3_src_offs = get_l3_src_offset(is6);
memset (mask, 0, sizeof (mask));
memcpy (&mask[6], a->rules[i].src_mac, 6);
+ memset (&mask[12], 0xff, 2); /* ethernet protocol */
if (is6)
{
memcpy (&mask[l3_src_offs], &a->rules[i].src_ip_addr.ip6, 16);
+ mask[12] = 0x86;
+ mask[13] = 0xdd;
}
else
{
memcpy (&mask[l3_src_offs], &a->rules[i].src_ip_addr.ip4, 4);
+ mask[12] = 0x08;
+ mask[13] = 0x00;
}
match_type_index =
macip_find_match_type (mvec, a->rules[i].src_mac_mask,
vnet_classify_add_del_session (cm, mvec[match_type_index].table_index,
mask, a->rules[i].is_permit ? ~0 : 0, i,
0, action, metadata, 1);
+ /* add ARP table entry too */
+ if (!is6 && (mvec[match_type_index].arp_table_index != ~0))
+ {
+ memset (mask, 0, sizeof (mask));
+ memcpy (&mask[6], a->rules[i].src_mac, 6);
+ mask[12] = 0x08;
+ mask[13] = 0x06;
+ memcpy (&mask[14 + 8], a->rules[i].src_mac, 6);
+ memcpy (&mask[14 + 14], &a->rules[i].src_ip_addr.ip4, 4);
+ vnet_classify_add_del_session (cm, mvec[match_type_index].arp_table_index,
+ mask, a->rules[i].is_permit ? ~0 : 0, i,
+ 0, action, metadata, 1);
+ }
}
return 0;
}
return rv;
}
+/*
+ * If the client does not allocate enough memory for a variable-length
+ * message, and then proceed to use it as if the full memory allocated,
+ * absent the check we happily consume that on the VPP side, and go
+ * along as if nothing happened. However, the resulting
+ * effects range from just garbage in the API decode
+ * (because the decoder snoops too far), to potential memory
+ * corruptions.
+ *
+ * This verifies that the actual length of the message is
+ * at least expected_len, and complains loudly if it is not.
+ *
+ * A failing check here is 100% a software bug on the API user side,
+ * so we might as well yell.
+ *
+ */
+static int verify_message_len(void *mp, u32 expected_len, char *where)
+{
+ u32 supplied_len = vl_msg_api_get_msg_length (mp);
+ if (supplied_len < expected_len) {
+ clib_warning("%s: Supplied message length %d is less than expected %d",
+ where, supplied_len, expected_len);
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
/* API message handler */
static void
vl_api_acl_add_replace_t_handler (vl_api_acl_add_replace_t * mp)
acl_main_t *am = &acl_main;
int rv;
u32 acl_list_index = ntohl (mp->acl_index);
+ u32 acl_count = ntohl (mp->count);
+ u32 expected_len = sizeof(*mp) + acl_count*sizeof(mp->r[0]);
- rv = acl_add_list (ntohl (mp->count), mp->r, &acl_list_index, mp->tag);
+ if (verify_message_len(mp, expected_len, "acl_add_replace")) {
+ rv = acl_add_list (acl_count, mp->r, &acl_list_index, mp->tag);
+ } else {
+ rv = VNET_API_ERROR_INVALID_VALUE;
+ }
/* *INDENT-OFF* */
REPLY_MACRO2(VL_API_ACL_ADD_REPLACE_REPLY,
static void
vl_api_acl_del_t_handler (vl_api_acl_del_t * mp)
{
- acl_main_t *sm = &acl_main;
+ acl_main_t *am = &acl_main;
vl_api_acl_del_reply_t *rmp;
int rv;
static void
vl_api_acl_interface_add_del_t_handler (vl_api_acl_interface_add_del_t * mp)
{
- acl_main_t *sm = &acl_main;
- vnet_interface_main_t *im = &sm->vnet_main->interface_main;
+ acl_main_t *am = &acl_main;
+ vnet_interface_main_t *im = &am->vnet_main->interface_main;
u32 sw_if_index = ntohl (mp->sw_if_index);
vl_api_acl_interface_add_del_reply_t *rmp;
int rv = -1;
vl_api_acl_interface_set_acl_list_t_handler
(vl_api_acl_interface_set_acl_list_t * mp)
{
- acl_main_t *sm = &acl_main;
+ acl_main_t *am = &acl_main;
vl_api_acl_interface_set_acl_list_reply_t *rmp;
int rv = 0;
int i;
- vnet_interface_main_t *im = &sm->vnet_main->interface_main;
+ vnet_interface_main_t *im = &am->vnet_main->interface_main;
u32 sw_if_index = ntohl (mp->sw_if_index);
if (pool_is_free_index(im->sw_interfaces, sw_if_index))
api_rule->src_ip_prefix_len = r->src_prefixlen;
api_rule->dst_ip_prefix_len = r->dst_prefixlen;
api_rule->proto = r->proto;
- api_rule->srcport_or_icmptype_first = r->src_port_or_type_first;
- api_rule->srcport_or_icmptype_last = r->src_port_or_type_last;
- api_rule->dstport_or_icmpcode_first = r->dst_port_or_code_first;
- api_rule->dstport_or_icmpcode_last = r->dst_port_or_code_last;
+ api_rule->srcport_or_icmptype_first = htons (r->src_port_or_type_first);
+ api_rule->srcport_or_icmptype_last = htons (r->src_port_or_type_last);
+ api_rule->dstport_or_icmpcode_first = htons (r->dst_port_or_code_first);
+ api_rule->dstport_or_icmpcode_last = htons (r->dst_port_or_code_last);
api_rule->tcp_flags_mask = r->tcp_flags_mask;
api_rule->tcp_flags_value = r->tcp_flags_value;
}
acl_main_t *am = &acl_main;
int rv;
u32 acl_list_index = ~0;
+ u32 acl_count = ntohl (mp->count);
+ u32 expected_len = sizeof(*mp) + acl_count*sizeof(mp->r[0]);
- rv =
- macip_acl_add_list (ntohl (mp->count), mp->r, &acl_list_index, mp->tag);
+ if (verify_message_len(mp, expected_len, "macip_acl_add")) {
+ rv = macip_acl_add_list (acl_count, mp->r, &acl_list_index, mp->tag);
+ } else {
+ rv = VNET_API_ERROR_INVALID_VALUE;
+ }
/* *INDENT-OFF* */
REPLY_MACRO2(VL_API_MACIP_ACL_ADD_REPLY,
static void
vl_api_macip_acl_del_t_handler (vl_api_macip_acl_del_t * mp)
{
- acl_main_t *sm = &acl_main;
+ acl_main_t *am = &acl_main;
vl_api_macip_acl_del_reply_t *rmp;
int rv;
vl_api_macip_acl_interface_add_del_t_handler
(vl_api_macip_acl_interface_add_del_t * mp)
{
- acl_main_t *sm = &acl_main;
+ acl_main_t *am = &acl_main;
vl_api_macip_acl_interface_add_del_reply_t *rmp;
int rv = -1;
- vnet_interface_main_t *im = &sm->vnet_main->interface_main;
+ vnet_interface_main_t *im = &am->vnet_main->interface_main;
u32 sw_if_index = ntohl (mp->sw_if_index);
if (pool_is_free_index(im->sw_interfaces, sw_if_index))
vl_msg_api_send_shmem (q, (u8 *) & rmp);
}
-
-
/* Set up the API message handling tables */
static clib_error_t *
acl_plugin_api_hookup (vlib_main_t * vm)
{
- acl_main_t *sm = &acl_main;
+ acl_main_t *am = &acl_main;
#define _(N,n) \
- vl_msg_api_set_handlers((VL_API_##N + sm->msg_id_base), \
+ vl_msg_api_set_handlers((VL_API_##N + am->msg_id_base), \
#n, \
vl_api_##n##_t_handler, \
vl_noop_handler, \
#undef vl_msg_name_crc_list
static void
-setup_message_id_table (acl_main_t * sm, api_main_t * am)
+setup_message_id_table (acl_main_t * am, api_main_t * apim)
{
#define _(id,n,crc) \
- vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + sm->msg_id_base);
+ vl_msg_api_add_msg_name_crc (apim, #n "_" #crc, id + am->msg_id_base);
foreach_vl_msg_name_crc_acl;
#undef _
}
-u32
-register_match_action_nexts (u32 next_in_ip4, u32 next_in_ip6,
- u32 next_out_ip4, u32 next_out_ip6)
+static void
+acl_setup_fa_nodes (void)
+{
+ vlib_main_t *vm = vlib_get_main ();
+ acl_main_t *am = &acl_main;
+ vlib_node_t *n, *n4, *n6;
+
+ n = vlib_get_node_by_name (vm, (u8 *) "l2-input-classify");
+ n4 = vlib_get_node_by_name (vm, (u8 *) "acl-plugin-in-ip4-l2");
+ n6 = vlib_get_node_by_name (vm, (u8 *) "acl-plugin-in-ip6-l2");
+
+
+ am->l2_input_classify_next_acl_ip4 =
+ vlib_node_add_next_with_slot (vm, n->index, n4->index, ~0);
+ am->l2_input_classify_next_acl_ip6 =
+ vlib_node_add_next_with_slot (vm, n->index, n6->index, ~0);
+
+ feat_bitmap_init_next_nodes (vm, n4->index, L2INPUT_N_FEAT,
+ l2input_get_feat_names (),
+ am->fa_acl_in_ip4_l2_node_feat_next_node_index);
+
+ feat_bitmap_init_next_nodes (vm, n6->index, L2INPUT_N_FEAT,
+ l2input_get_feat_names (),
+ am->fa_acl_in_ip6_l2_node_feat_next_node_index);
+
+
+ n = vlib_get_node_by_name (vm, (u8 *) "l2-output-classify");
+ n4 = vlib_get_node_by_name (vm, (u8 *) "acl-plugin-out-ip4-l2");
+ n6 = vlib_get_node_by_name (vm, (u8 *) "acl-plugin-out-ip6-l2");
+
+ am->l2_output_classify_next_acl_ip4 =
+ vlib_node_add_next_with_slot (vm, n->index, n4->index, ~0);
+ am->l2_output_classify_next_acl_ip6 =
+ vlib_node_add_next_with_slot (vm, n->index, n6->index, ~0);
+
+ feat_bitmap_init_next_nodes (vm, n4->index, L2OUTPUT_N_FEAT,
+ l2output_get_feat_names (),
+ am->fa_acl_out_ip4_l2_node_feat_next_node_index);
+
+ feat_bitmap_init_next_nodes (vm, n6->index, L2OUTPUT_N_FEAT,
+ l2output_get_feat_names (),
+ am->fa_acl_out_ip6_l2_node_feat_next_node_index);
+}
+
+static void
+acl_set_timeout_sec(int timeout_type, u32 value)
{
acl_main_t *am = &acl_main;
- u32 act = am->n_match_actions;
- if (am->n_match_actions == 255)
+ clib_time_t *ct = &am->vlib_main->clib_time;
+
+ if (timeout_type < ACL_N_TIMEOUTS) {
+ am->session_timeout_sec[timeout_type] = value;
+ } else {
+ clib_warning("Unknown timeout type %d", timeout_type);
+ return;
+ }
+ am->session_timeout[timeout_type] = (u64)(((f64)value)/ct->seconds_per_clock);
+}
+
+static void
+acl_set_session_max_entries(u32 value)
+{
+ acl_main_t *am = &acl_main;
+ am->fa_conn_table_max_entries = value;
+}
+
+static int
+acl_set_skip_ipv6_eh(u32 eh, u32 value)
+{
+ acl_main_t *am = &acl_main;
+ if ((eh < 256) && (value < 2))
{
- return ~0;
+ am->fa_ipv6_known_eh_bitmap = clib_bitmap_set(am->fa_ipv6_known_eh_bitmap, eh, value);
+ return 1;
}
- am->n_match_actions++;
- am->acl_in_ip4_match_next[act] = next_in_ip4;
- am->acl_in_ip6_match_next[act] = next_in_ip6;
- am->acl_out_ip4_match_next[act] = next_out_ip4;
- am->acl_out_ip6_match_next[act] = next_out_ip6;
- return act;
+ else
+ return 0;
}
-void
-acl_setup_nodes (void)
+
+static clib_error_t *
+acl_sw_interface_add_del (vnet_main_t * vnm, u32 sw_if_index, u32 is_add)
{
- vlib_main_t *vm = vlib_get_main ();
acl_main_t *am = &acl_main;
- vlib_node_t *n;
+ if (0 == is_add) {
+ vlib_process_signal_event (am->vlib_main, am->fa_cleaner_node_index,
+ ACL_FA_CLEANER_DELETE_BY_SW_IF_INDEX, sw_if_index);
+ /* also unapply any ACLs in case the users did not do so. */
+ macip_acl_interface_del_acl(am, sw_if_index);
+ acl_interface_reset_inout_acls (sw_if_index, 0);
+ acl_interface_reset_inout_acls (sw_if_index, 1);
+ }
+ return 0;
+}
- n = vlib_get_node_by_name (vm, (u8 *) "l2-input-classify");
- am->l2_input_classify_next_acl =
- vlib_node_add_next_with_slot (vm, n->index, acl_in_node.index, ~0);
- n = vlib_get_node_by_name (vm, (u8 *) "l2-output-classify");
- am->l2_output_classify_next_acl =
- vlib_node_add_next_with_slot (vm, n->index, acl_out_node.index, ~0);
-
- feat_bitmap_init_next_nodes (vm, acl_in_node.index, L2INPUT_N_FEAT,
- l2input_get_feat_names (),
- am->acl_in_node_input_next_node_index);
-
- memset (&am->acl_in_ip4_match_next[0], 0,
- sizeof (am->acl_in_ip4_match_next));
- memset (&am->acl_in_ip6_match_next[0], 0,
- sizeof (am->acl_in_ip6_match_next));
- memset (&am->acl_out_ip4_match_next[0], 0,
- sizeof (am->acl_out_ip4_match_next));
- memset (&am->acl_out_ip6_match_next[0], 0,
- sizeof (am->acl_out_ip6_match_next));
- am->n_match_actions = 0;
-
- register_match_action_nexts (0, 0, 0, 0); /* drop */
- register_match_action_nexts (~0, ~0, ~0, ~0); /* permit */
- register_match_action_nexts (ACL_IN_L2S_INPUT_IP4_ADD, ACL_IN_L2S_INPUT_IP6_ADD, ACL_OUT_L2S_OUTPUT_IP4_ADD, ACL_OUT_L2S_OUTPUT_IP6_ADD); /* permit + create session */
+VNET_SW_INTERFACE_ADD_DEL_FUNCTION (acl_sw_interface_add_del);
+
+static clib_error_t *
+acl_set_aclplugin_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ clib_error_t *error = 0;
+ u32 timeout = 0;
+ u32 val = 0;
+ u32 eh_val = 0;
+ uword memory_size = 0;
+ acl_main_t *am = &acl_main;
+
+ if (unformat (input, "skip-ipv6-extension-header %u %u", &eh_val, &val)) {
+ if(!acl_set_skip_ipv6_eh(eh_val, val)) {
+ error = clib_error_return(0, "expecting eh=0..255, value=0..1");
+ }
+ goto done;
+ }
+ if (unformat (input, "l4-match-nonfirst-fragment %u", &val))
+ {
+ am->l4_match_nonfirst_fragment = (val != 0);
+ goto done;
+ }
+ if (unformat (input, "session")) {
+ if (unformat (input, "clear")) {
+ acl_main_t *am = &acl_main;
+ vlib_process_signal_event (am->vlib_main, am->fa_cleaner_node_index,
+ ACL_FA_CLEANER_DELETE_BY_SW_IF_INDEX, ~0);
+ goto done;
+ }
+ if (unformat (input, "table")) {
+ /* The commands here are for tuning/testing. No user-serviceable parts inside */
+ if (unformat (input, "max-entries")) {
+ if (!unformat(input, "%u", &val)) {
+ error = clib_error_return(0,
+ "expecting maximum number of entries, got `%U`",
+ format_unformat_error, input);
+ goto done;
+ } else {
+ acl_set_session_max_entries(val);
+ goto done;
+ }
+ }
+ if (unformat (input, "hash-table-buckets")) {
+ if (!unformat(input, "%u", &val)) {
+ error = clib_error_return(0,
+ "expecting maximum number of hash table buckets, got `%U`",
+ format_unformat_error, input);
+ goto done;
+ } else {
+ am->fa_conn_table_hash_num_buckets = val;
+ goto done;
+ }
+ }
+ if (unformat (input, "hash-table-memory")) {
+ if (!unformat(input, "%U", unformat_memory_size, &memory_size)) {
+ error = clib_error_return(0,
+ "expecting maximum amount of hash table memory, got `%U`",
+ format_unformat_error, input);
+ goto done;
+ } else {
+ am->fa_conn_table_hash_memory_size = memory_size;
+ goto done;
+ }
+ }
+ goto done;
+ }
+ if (unformat (input, "timeout")) {
+ if (unformat(input, "udp")) {
+ if(unformat(input, "idle")) {
+ if (!unformat(input, "%u", &timeout)) {
+ error = clib_error_return(0,
+ "expecting timeout value in seconds, got `%U`",
+ format_unformat_error, input);
+ goto done;
+ } else {
+ acl_set_timeout_sec(ACL_TIMEOUT_UDP_IDLE, timeout);
+ goto done;
+ }
+ }
+ }
+ if (unformat(input, "tcp")) {
+ if(unformat(input, "idle")) {
+ if (!unformat(input, "%u", &timeout)) {
+ error = clib_error_return(0,
+ "expecting timeout value in seconds, got `%U`",
+ format_unformat_error, input);
+ goto done;
+ } else {
+ acl_set_timeout_sec(ACL_TIMEOUT_TCP_IDLE, timeout);
+ goto done;
+ }
+ }
+ if(unformat(input, "transient")) {
+ if (!unformat(input, "%u", &timeout)) {
+ error = clib_error_return(0,
+ "expecting timeout value in seconds, got `%U`",
+ format_unformat_error, input);
+ goto done;
+ } else {
+ acl_set_timeout_sec(ACL_TIMEOUT_TCP_TRANSIENT, timeout);
+ goto done;
+ }
+ }
+ }
+ goto done;
+ }
+ }
+done:
+ return error;
}
+static clib_error_t *
+acl_show_aclplugin_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ clib_error_t *error = 0;
+ acl_main_t *am = &acl_main;
+ vnet_interface_main_t *im = &am->vnet_main->interface_main;
+
+ vnet_sw_interface_t *swif;
+
+ if (unformat (input, "sessions"))
+ {
+ u8 * out0 = 0;
+ u16 wk;
+ pool_foreach (swif, im->sw_interfaces,
+ ({
+ u32 sw_if_index = swif->sw_if_index;
+ u64 n_adds = sw_if_index < vec_len(am->fa_session_adds_by_sw_if_index) ? am->fa_session_adds_by_sw_if_index[sw_if_index] : 0;
+ u64 n_dels = sw_if_index < vec_len(am->fa_session_dels_by_sw_if_index) ? am->fa_session_dels_by_sw_if_index[sw_if_index] : 0;
+ out0 = format(out0, "sw_if_index %d: add %lu - del %lu = %lu\n", sw_if_index, n_adds, n_dels, n_adds - n_dels);
+ }));
+ out0 = format(out0, "\n\nPer-worker data:\n");
+ for (wk = 0; wk < vec_len (am->per_worker_data); wk++) {
+ acl_fa_per_worker_data_t *pw = &am->per_worker_data[wk];
+ out0 = format(out0, "Worker #%d:\n", wk);
+ out0 = format(out0, " Next expiry time: %lu\n", pw->next_expiry_time);
+ out0 = format(out0, " Requeue until time: %lu\n", pw->requeue_until_time);
+ out0 = format(out0, " Current time wait interval: %lu\n", pw->current_time_wait_interval);
+ out0 = format(out0, " Count of deleted sessions: %lu\n", pw->cnt_deleted_sessions);
+ out0 = format(out0, " Delete already deleted: %lu\n", pw->cnt_already_deleted_sessions);
+ out0 = format(out0, " Session timers restarted: %lu\n", pw->cnt_session_timer_restarted);
+ out0 = format(out0, " Swipe until this time: %lu\n", pw->swipe_end_time);
+ out0 = format(out0, " sw_if_index serviced bitmap: %U\n", format_bitmap_hex, pw->serviced_sw_if_index_bitmap);
+ out0 = format(out0, " pending clear intfc bitmap : %U\n", format_bitmap_hex, pw->pending_clear_sw_if_index_bitmap);
+ out0 = format(out0, " clear in progress: %u\n", pw->clear_in_process);
+ out0 = format(out0, " interrupt is pending: %d\n", pw->interrupt_is_pending);
+ out0 = format(out0, " interrupt is needed: %d\n", pw->interrupt_is_needed);
+ out0 = format(out0, " interrupt is unwanted: %d\n", pw->interrupt_is_unwanted);
+ }
+ out0 = format(out0, "\n\nConn cleaner thread counters:\n");
+#define _(cnt, desc) out0 = format(out0, " %20lu: %s\n", am->cnt, desc);
+ foreach_fa_cleaner_counter;
+#undef _
+ vlib_cli_output(vm, "\n\n%s\n\n", out0);
+ vlib_cli_output(vm, "Sessions per interval: min %lu max %lu increment: %f ms current: %f ms",
+ am->fa_min_deleted_sessions_per_interval, am->fa_max_deleted_sessions_per_interval,
+ am->fa_cleaner_wait_time_increment * 1000.0, ((f64)am->fa_current_cleaner_timer_wait_interval) * 1000.0/(f64)vm->clib_time.clocks_per_second);
+
+ vec_free(out0);
+ }
+ return error;
+}
+
+
+ /* *INDENT-OFF* */
+VLIB_CLI_COMMAND (aclplugin_set_command, static) = {
+ .path = "set acl-plugin",
+ .short_help = "set acl-plugin session timeout {{udp idle}|tcp {idle|transient}} <seconds>",
+ .function = acl_set_aclplugin_fn,
+};
+
+VLIB_CLI_COMMAND (aclplugin_show_command, static) = {
+ .path = "show acl-plugin",
+ .short_help = "show acl-plugin sessions",
+ .function = acl_show_aclplugin_fn,
+};
+/* *INDENT-ON* */
+
+
static clib_error_t *
acl_init (vlib_main_t * vm)
VL_MSG_FIRST_AVAILABLE);
error = acl_plugin_api_hookup (vm);
- acl_setup_nodes ();
/* Add our API messages to the global name_crc hash table */
setup_message_id_table (am, &api_main);
vec_free (name);
+ acl_setup_fa_nodes();
+ am->session_timeout_sec[ACL_TIMEOUT_TCP_TRANSIENT] = TCP_SESSION_TRANSIENT_TIMEOUT_SEC;
+ am->session_timeout_sec[ACL_TIMEOUT_TCP_IDLE] = TCP_SESSION_IDLE_TIMEOUT_SEC;
+ am->session_timeout_sec[ACL_TIMEOUT_UDP_IDLE] = UDP_SESSION_IDLE_TIMEOUT_SEC;
+
+ am->fa_conn_table_hash_num_buckets = ACL_FA_CONN_TABLE_DEFAULT_HASH_NUM_BUCKETS;
+ am->fa_conn_table_hash_memory_size = ACL_FA_CONN_TABLE_DEFAULT_HASH_MEMORY_SIZE;
+ am->fa_conn_table_max_entries = ACL_FA_CONN_TABLE_DEFAULT_MAX_ENTRIES;
+ vlib_thread_main_t *tm = vlib_get_thread_main ();
+ // vec_validate(am->per_worker_data, os_get_nthreads()-1);
+ vec_validate(am->per_worker_data, tm->n_vlib_mains-1);
+ clib_warning("ACL_FA_INIT: per-worker len: %d", vec_len(am->per_worker_data));
+ {
+ u16 wk;
+ u8 tt;
+ for (wk = 0; wk < vec_len (am->per_worker_data); wk++) {
+ acl_fa_per_worker_data_t *pw = &am->per_worker_data[wk];
+ vec_validate(pw->fa_conn_list_head, ACL_N_TIMEOUTS-1);
+ vec_validate(pw->fa_conn_list_tail, ACL_N_TIMEOUTS-1);
+ for(tt = 0; tt < ACL_N_TIMEOUTS; tt++) {
+ pw->fa_conn_list_head[tt] = ~0;
+ pw->fa_conn_list_tail[tt] = ~0;
+ }
+ }
+ }
+ clib_warning("ACL_FA_INIT-DONE: per-worker len: %d", vec_len(am->per_worker_data));
+
+ am->fa_min_deleted_sessions_per_interval = ACL_FA_DEFAULT_MIN_DELETED_SESSIONS_PER_INTERVAL;
+ am->fa_max_deleted_sessions_per_interval = ACL_FA_DEFAULT_MAX_DELETED_SESSIONS_PER_INTERVAL;
+ am->fa_cleaner_wait_time_increment = ACL_FA_DEFAULT_CLEANER_WAIT_TIME_INCREMENT;
+
+ am->fa_cleaner_cnt_delete_by_sw_index = 0;
+ am->fa_cleaner_cnt_delete_by_sw_index_ok = 0;
+ am->fa_cleaner_cnt_unknown_event = 0;
+ am->fa_cleaner_cnt_timer_restarted = 0;
+ am->fa_cleaner_cnt_wait_with_timeout = 0;
+
+
+#define _(N, v, s) am->fa_ipv6_known_eh_bitmap = clib_bitmap_set(am->fa_ipv6_known_eh_bitmap, v, 1);
+ foreach_acl_eh
+#undef _
+
+ am->l4_match_nonfirst_fragment = 1;
+
return error;
}