diff options
author | Andrew Yourtchenko <ayourtch@gmail.com> | 2016-12-05 16:25:28 +0000 |
---|---|---|
committer | Damjan Marion <dmarion.lists@gmail.com> | 2016-12-06 08:18:53 +0000 |
commit | b09167f33d3c79e7ccc27e0fc484cc5fbcdb9943 (patch) | |
tree | 9824d4efba7af3e6eb40507f83d4a1e76ae392ef | |
parent | 65209ed18c0bdc4e8d3d4a2ebbcd6cf34b68bb43 (diff) |
acl: The ACL plugin.
This is the commit from the accumulated work in the github ACL branch,
to move it to gerrit.
Change-Id: I85a6b0df0d3dd3c3c7588e92a1e22c553e4b6ef7
Signed-off-by: Andrew Yourtchenko <ayourtch@gmail.com>
-rw-r--r-- | plugins/Makefile.am | 4 | ||||
-rw-r--r-- | plugins/acl-plugin/Makefile.am | 59 | ||||
-rw-r--r-- | plugins/acl-plugin/acl/acl.api | 444 | ||||
-rw-r--r-- | plugins/acl-plugin/acl/acl.c | 1881 | ||||
-rw-r--r-- | plugins/acl-plugin/acl/acl.h | 148 | ||||
-rw-r--r-- | plugins/acl-plugin/acl/acl_all_api_h.h | 321 | ||||
-rw-r--r-- | plugins/acl-plugin/acl/acl_msg_enum.h | 28 | ||||
-rw-r--r-- | plugins/acl-plugin/acl/acl_test.c | 1024 | ||||
-rw-r--r-- | plugins/acl-plugin/acl/l2sess.c | 243 | ||||
-rw-r--r-- | plugins/acl-plugin/acl/l2sess.h | 150 | ||||
-rw-r--r-- | plugins/acl-plugin/acl/l2sess_node.c | 813 | ||||
-rw-r--r-- | plugins/acl-plugin/acl/node_in.c | 168 | ||||
-rw-r--r-- | plugins/acl-plugin/acl/node_in.h | 12 | ||||
-rw-r--r-- | plugins/acl-plugin/acl/node_out.c | 175 | ||||
-rw-r--r-- | plugins/acl-plugin/acl/node_out.h | 12 | ||||
-rw-r--r-- | plugins/acl-plugin/configure.ac | 9 | ||||
-rwxr-xr-x | plugins/acl-plugin/test/run-python | 28 | ||||
-rwxr-xr-x | plugins/acl-plugin/test/run-scapy | 26 | ||||
-rw-r--r-- | plugins/acl-plugin/test/test_acl_plugin.py | 118 | ||||
-rw-r--r-- | plugins/configure.ac | 1 |
20 files changed, 5664 insertions, 0 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 117f11dd..100f089e 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -55,3 +55,7 @@ endif if ENABLE_flowperpkt_PLUGIN SUBDIRS += flowperpkt-plugin endif + +if ENABLE_acl_PLUGIN +SUBDIRS += acl-plugin +endif diff --git a/plugins/acl-plugin/Makefile.am b/plugins/acl-plugin/Makefile.am new file mode 100644 index 00000000..623c192c --- /dev/null +++ b/plugins/acl-plugin/Makefile.am @@ -0,0 +1,59 @@ +# 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. + +AUTOMAKE_OPTIONS = foreign subdir-objects + +AM_CFLAGS = -Wall +AM_LDFLAGS = -module -shared -avoid-version + +vppapitestpluginsdir = ${libdir}/vpp_api_test_plugins +vpppluginsdir = ${libdir}/vpp_plugins + +vppapitestplugins_LTLIBRARIES = acl_test_plugin.la +vppplugins_LTLIBRARIES = acl_plugin.la + +acl_plugin_la_SOURCES = \ + acl/acl.c \ + acl/node_in.c \ + acl/node_out.c \ + acl/l2sess.c \ + acl/l2sess_node.c \ + acl/l2sess.h \ + acl/acl_plugin.api.h + +BUILT_SOURCES = acl/acl.api.h + +SUFFIXES = .api.h .api + +# +# ACL API +# +%.api.h: %.api + mkdir -p $(dir $@) ; \ + $(CC) $(CPPFLAGS) -E -P -C -x c $^ \ + | vppapigen --input - --output $@ --show-name $@ + +apidir = $(prefix)/acl +api_DATA = acl/acl.api + +noinst_HEADERS = \ + acl/acl_all_api_h.h \ + acl/acl_msg_enum.h \ + acl/acl.api.h + +acl_test_plugin_la_SOURCES = acl/acl_test.c acl/acl_plugin.api.h + +# Remove *.la files +install-data-hook: + @(cd $(vpppluginsdir) && $(RM) $(vppplugins_LTLIBRARIES)) + @(cd $(vppapitestpluginsdir) && $(RM) $(vppapitestplugins_LTLIBRARIES)) diff --git a/plugins/acl-plugin/acl/acl.api b/plugins/acl-plugin/acl/acl.api new file mode 100644 index 00000000..58a5a171 --- /dev/null +++ b/plugins/acl-plugin/acl/acl.api @@ -0,0 +1,444 @@ +/* Hey Emacs use -*- mode: C -*- */ +/* + * 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. + */ + +/** \file + This file defines the vpp control-plane API messages + used to control the ACL plugin +*/ + + +/** \brief Get the plugin version + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ + +define acl_plugin_get_version +{ + u32 client_index; + u32 context; +}; + +/** \brief Reply to get the plugin version + @param context - returned sender context, to match reply w/ request + @param major - Incremented every time a known breaking behavior change is introduced + @param minor - Incremented with small changes, may be used to avoid buggy versions +*/ + +define acl_plugin_get_version_reply +{ + u32 context; + u32 major; + u32 minor; +}; + +/** \brief Access List Rule entry + @param is_permit - deny (0), permit (1), or permit+reflect(2) action on this rule. + @param is_ipv6 - IP addresses in this rule are IPv6 (1) or IPv4 (0) + @param src_ip_addr - Source prefix value + @param src_ip_prefix_len - Source prefix length + @param dst_ip_addr - Destination prefix value + @param dst_ip_prefix_len - Destination prefix length + @param proto - L4 protocol (http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml) + @param srcport_or_icmptype_first - beginning of source port or ICMP4/6 type range + @param srcport_or_icmptype_last - end of source port or ICMP4/6 type range + @param dstport_or_icmpcode_first - beginning of destination port or ICMP4/6 code range + @param dstport_or_icmpcode_last - end of destination port or ICMP4/6 code range + @param tcp_flags_mask - if proto==6, match masked TCP flags with this value + @param tcp_flags_value - if proto==6, mask to AND the TCP flags in the packet with +*/ + +typeonly manual_print manual_endian define acl_rule +{ + u8 is_permit; + u8 is_ipv6; + u8 src_ip_addr[16]; + u8 src_ip_prefix_len; + u8 dst_ip_addr[16]; + u8 dst_ip_prefix_len; +/* + * L4 protocol. IANA number. 1 = ICMP, 58 = ICMPv6, 6 = TCP, 17 = UDP. + * 0 => ignore L4 and ignore the ports/tcpflags when matching. + */ + u8 proto; +/* + * If the L4 protocol is TCP or UDP, the below + * hold ranges of ports, else if the L4 is ICMP/ICMPv6 + * they hold ranges of ICMP(v6) types/codes. + * + * Ranges are inclusive, i.e. to match "any" TCP/UDP port, + * use first=0,last=65535. For ICMP(v6), + * use first=0,last=255. + */ + u16 srcport_or_icmptype_first; + u16 srcport_or_icmptype_last; + u16 dstport_or_icmpcode_first; + u16 dstport_or_icmpcode_last; +/* + * for proto = 6, this matches if the + * TCP flags in the packet, ANDed with tcp_flags_mask, + * is equal to tcp_flags_value. + */ + u8 tcp_flags_mask; + u8 tcp_flags_value; +}; + +/** \brief MACIP Access List Rule entry + @param is_permit - deny (0), permit (1) action on this rule. + @param is_ipv6 - IP addresses in this rule are IPv6 (1) or IPv4 (0) + @param src_mac - match masked source MAC address against this value + @param src_mac_mask - AND source MAC address with this value before matching + @param src_ip_addr - Source prefix value + @param src_ip_prefix_len - Source prefix length +*/ + +typeonly manual_print manual_endian define macip_acl_rule +{ + u8 is_permit; + u8 is_ipv6; +/* + * The source mac of the packet ANDed with src_mac_mask. + * The source ip[46] address in the packet is matched + * against src_ip_addr, with src_ip_prefix_len set to 0. + * + * For better performance, minimize the number of + * (src_mac_mask, src_ip_prefix_len) combinations + * in a MACIP ACL. + */ + u8 src_mac[6]; + u8 src_mac_mask[6]; + u8 src_ip_addr[16]; + u8 src_ip_prefix_len; +}; + +/** \brief Replace an existing ACL in-place or create a new ACL + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param acl_index - an existing ACL entry (0..0xfffffffe) to replace, or 0xffffffff to make new ACL + @param tag - a string value stored along with the ACL, for descriptive purposes + @param count - number of ACL rules + @r - Rules for this access-list +*/ + +manual_print manual_endian define acl_add_replace +{ + u32 client_index; + u32 context; + u32 acl_index; /* ~0 to add, existing ACL# to replace */ + u8 tag[64]; /* What gets in here gets out in the corresponding tag field when dumping the ACLs. */ + u32 count; + vl_api_acl_rule_t r[count]; +}; + +/** \brief Reply to add/replace ACL + @param context - returned sender context, to match reply w/ request + @param acl_index - index of the updated or newly created ACL + @param retval 0 - no error +*/ + +define acl_add_replace_reply +{ + u32 context; + u32 acl_index; + i32 retval; +}; + +/** \brief Delete an ACL + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param acl_index - ACL index to delete +*/ + +define acl_del +{ + u32 client_index; + u32 context; + u32 acl_index; +}; + +/** \brief Reply to delete the ACL + @param context - returned sender context, to match reply w/ request + @param retval 0 - no error +*/ + +define acl_del_reply +{ + u32 context; + i32 retval; +}; + +/* acl_interface_add_del(_reply) to be deprecated in lieu of acl_interface_set_acl_list */ +/** \brief Use acl_interface_set_acl_list instead + Append/remove an ACL index to/from the list of ACLs checked for an interface + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - add or delete the ACL index from the list + @param is_input - check the ACL on input (1) or output (0) + @param sw_if_index - the interface to alter the list of ACLs on + @param acl_index - index of ACL for the operation +*/ + +define acl_interface_add_del +{ + u32 client_index; + u32 context; + u8 is_add; +/* + * is_input = 0 => ACL applied on interface egress + * is_input = 1 => ACL applied on interface ingress + */ + u8 is_input; + u32 sw_if_index; + u32 acl_index; +}; + +/** \brief Reply to alter the ACL list + @param context - returned sender context, to match reply w/ request + @param retval 0 - no error +*/ + +define acl_interface_add_del_reply +{ + u32 context; + i32 retval; +}; + +/** \brief Set the vector of input/output ACLs checked for an interface + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param sw_if_index - the interface to alter the list of ACLs on + @param count - total number of ACL indices in the vector + @param n_input - this many first elements correspond to input ACLs, the rest - output + @param acls - vector of ACL indices +*/ + +manual_endian define acl_interface_set_acl_list +{ + u32 client_index; + u32 context; + u32 sw_if_index; + u8 count; + u8 n_input; /* First n_input ACLs are set as a list of input ACLs, the rest are applied as output */ + u32 acls[count]; +}; + +/** \brief Reply to set the ACL list on an interface + @param context - returned sender context, to match reply w/ request + @param retval 0 - no error +*/ + +define acl_interface_set_acl_list_reply +{ + u32 context; + i32 retval; +}; + +/** \brief Dump the specific ACL contents or all of the ACLs' contents + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param acl_index - ACL index to dump, ~0 to dump all ACLs +*/ + +define acl_dump +{ + u32 client_index; + u32 context; + u32 acl_index; /* ~0 for all ACLs */ +}; + +/** \brief Details about a single ACL contents + @param context - returned sender context, to match reply w/ request + @param acl_index - ACL index whose contents are being sent in this message + @param tag - Descriptive tag value which was supplied at ACL creation + @param count - Number of rules in this ACL + @param r - Array of rules within this ACL +*/ + +manual_print manual_endian define acl_details +{ + u32 context; + u32 acl_index; + u8 tag[64]; /* Same blob that was supplied to us when creating the ACL, one hopes. */ + u32 count; + vl_api_acl_rule_t r[count]; +}; + +/** \brief Dump the list(s) of ACL applied to specific or all interfaces + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param sw_if_index - interface to dump the ACL list for +*/ + +define acl_interface_list_dump +{ + u32 client_index; + u32 context; + u32 sw_if_index; /* ~0 for all interfaces */ +}; + +/** \brief Details about a single ACL contents + @param context - returned sender context, to match reply w/ request + @param sw_if_index - interface for which the list of ACLs is applied + @param count - total length of acl indices vector + @param n_input - this many of indices in the beginning are input ACLs, the rest - output + @param acls - the vector of ACL indices +*/ + +manual_endian define acl_interface_list_details +{ + u32 context; + u32 sw_if_index; + u8 count; + u8 n_input; + u32 acls[count]; +}; + +/** \brief Add a MACIP ACL + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param tag - descriptive value for this MACIP ACL + @param count - number of rules in this ACL + @param r - vector of MACIP ACL rules +*/ + +manual_print manual_endian define macip_acl_add +{ + u32 client_index; + u32 context; + u8 tag[64]; + u32 count; + vl_api_macip_acl_rule_t r[count]; +}; + +/** \brief Reply to add MACIP ACL + @param context - returned sender context, to match reply w/ request + @param acl_index - index of the newly created ACL + @param retval 0 - no error +*/ + +define macip_acl_add_reply +{ + u32 context; + u32 acl_index; + i32 retval; +}; + +/** \brief Delete a MACIP ACL + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param acl_index - MACIP ACL index to delete +*/ + +define macip_acl_del +{ + u32 client_index; + u32 context; + u32 acl_index; +}; + +/** \brief Reply to delete the MACIP ACL + @param context - returned sender context, to match reply w/ request + @param retval 0 - no error +*/ + +define macip_acl_del_reply +{ + u32 context; + i32 retval; +}; + +/** \brief Add or delete a MACIP ACL to/from interface + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - add (1) or delete (0) ACL from being used on an interface + @param sw_if_index - interface to apply the action to + @param acl_index - MACIP ACL index +*/ + +define macip_acl_interface_add_del +{ + u32 client_index; + u32 context; + u8 is_add; + /* macip ACLs are always input */ + u32 sw_if_index; + u32 acl_index; +}; + +/** \brief Reply to apply/unapply the MACIP ACL + @param context - returned sender context, to match reply w/ request + @param retval 0 - no error +*/ + +define macip_acl_interface_add_del_reply +{ + u32 context; + i32 retval; +}; + +/** \brief Dump one or all defined MACIP ACLs + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param acl_index - MACIP ACL index or ~0 to dump all ACLs +*/ + +define macip_acl_dump +{ + u32 client_index; + u32 context; + u32 acl_index; /* ~0 for all ACLs */ +}; + +/** \brief Details about one MACIP ACL + @param context - returned sender context, to match reply w/ request + @param acl_index - index of this MACIP ACL + @param tag - descriptive tag which was supplied during the creation + @param count - length of the vector of MACIP ACL rules + @param r - rules comprising this ACL +*/ + +manual_print manual_endian define macip_acl_details +{ + u32 context; + u32 acl_index; + u8 tag[64]; + u32 count; + vl_api_macip_acl_rule_t r[count]; +}; + +/** \brief Get the vector of MACIP ACL IDs applied to the interfaces + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ + +define macip_acl_interface_get +{ + u32 client_index; + u32 context; +}; + +/** \brief Reply with the vector of MACIP ACLs by sw_if_index + @param context - returned sender context, to match reply w/ request + @param count - total number of elements in the vector + @param acls - the vector of active MACACL indices per sw_if_index +*/ + +define macip_acl_interface_get_reply +{ + u32 context; + u32 count; + u32 acls[count]; +}; + diff --git a/plugins/acl-plugin/acl/acl.c b/plugins/acl-plugin/acl/acl.c new file mode 100644 index 00000000..149f006d --- /dev/null +++ b/plugins/acl-plugin/acl/acl.c @@ -0,0 +1,1881 @@ +/* + * 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/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 <vlibapi/api.h> +#include <vlibmemory/api.h> +#include <vlibsocket/api.h> + +/* define message IDs */ +#include <acl/acl_msg_enum.h> + +/* define message structures */ +#define vl_typedefs +#include <acl/acl_all_api_h.h> +#undef vl_typedefs + +/* define generated endian-swappers */ +#define vl_endianfun +#include <acl/acl_all_api_h.h> +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) +#define vl_printfun +#include <acl/acl_all_api_h.h> +#undef vl_printfun + +/* Get the API version number */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include <acl/acl_all_api_h.h> +#undef vl_api_version + +#include "node_in.h" +#include "node_out.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 VALIDATE_SW_IF_INDEX(mp) \ + do { u32 __sw_if_index = ntohl(mp->sw_if_index); \ + vnet_main_t *__vnm = vnet_get_main(); \ + if (pool_is_free_index(__vnm->interface_main.sw_interfaces, \ + __sw_if_index)) { \ + rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; \ + goto bad_sw_if_index; \ + } \ +} while(0); + +#define BAD_SW_IF_INDEX_LABEL \ +do { \ +bad_sw_if_index: \ + ; \ +} while (0); + + + +/* List of message types that this plugin understands */ + +#define foreach_acl_plugin_api_msg \ +_(ACL_PLUGIN_GET_VERSION, acl_plugin_get_version) \ +_(ACL_ADD_REPLACE, acl_add_replace) \ +_(ACL_DEL, acl_del) \ +_(ACL_INTERFACE_ADD_DEL, acl_interface_add_del) \ +_(ACL_INTERFACE_SET_ACL_LIST, acl_interface_set_acl_list) \ +_(ACL_DUMP, acl_dump) \ +_(ACL_INTERFACE_LIST_DUMP, acl_interface_list_dump) \ +_(MACIP_ACL_ADD, macip_acl_add) \ +_(MACIP_ACL_DEL, macip_acl_del) \ +_(MACIP_ACL_INTERFACE_ADD_DEL, macip_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; +} + + +static void +vl_api_acl_plugin_get_version_t_handler (vl_api_acl_plugin_get_version_t * mp) +{ + acl_main_t *am = &acl_main; + vl_api_acl_plugin_get_version_reply_t *rmp; + int msg_size = sizeof (*rmp); + unix_shared_memory_queue_t *q; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + { + return; + } + + rmp = vl_msg_api_alloc (msg_size); + memset (rmp, 0, msg_size); + rmp->_vl_msg_id = + ntohs (VL_API_ACL_PLUGIN_GET_VERSION_REPLY + am->msg_id_base); + rmp->context = mp->context; + rmp->major = htonl (ACL_PLUGIN_VERSION_MAJOR); + rmp->minor = htonl (ACL_PLUGIN_VERSION_MINOR); + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + + +static int +acl_add_list (u32 count, vl_api_acl_rule_t rules[], + u32 * acl_list_index, u8 * tag) +{ + acl_main_t *am = &acl_main; + acl_list_t *a; + acl_rule_t *r; + acl_rule_t *acl_new_rules; + int i; + + if (*acl_list_index != ~0) + { + /* They supplied some number, let's see if this ACL exists */ + if (pool_is_free_index (am->acls, *acl_list_index)) + { + /* tried to replace a non-existent ACL, no point doing anything */ + return -1; + } + } + + /* Create and populate the rules */ + acl_new_rules = clib_mem_alloc_aligned (sizeof (acl_rule_t) * count, + CLIB_CACHE_LINE_BYTES); + if (!acl_new_rules) + { + /* Could not allocate rules. New or existing ACL - bail out regardless */ + return -1; + } + + for (i = 0; i < count; i++) + { + r = &acl_new_rules[i]; + r->is_permit = rules[i].is_permit; + r->is_ipv6 = rules[i].is_ipv6; + if (r->is_ipv6) + { + memcpy (&r->src, rules[i].src_ip_addr, sizeof (r->src)); + memcpy (&r->dst, rules[i].dst_ip_addr, sizeof (r->dst)); + } + else + { + memcpy (&r->src.ip4, rules[i].src_ip_addr, sizeof (r->src.ip4)); + memcpy (&r->dst.ip4, rules[i].dst_ip_addr, sizeof (r->dst.ip4)); + } + 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->tcp_flags_value = rules[i].tcp_flags_value; + r->tcp_flags_mask = rules[i].tcp_flags_mask; + } + + if (~0 == *acl_list_index) + { + /* Get ACL index */ + pool_get_aligned (am->acls, a, CLIB_CACHE_LINE_BYTES); + memset (a, 0, sizeof (*a)); + /* Will return the newly allocated ACL index */ + *acl_list_index = a - am->acls; + } + else + { + a = am->acls + *acl_list_index; + /* Get rid of the old rules */ + clib_mem_free (a->rules); + } + a->rules = acl_new_rules; + a->count = count; + memcpy (a->tag, tag, sizeof (a->tag)); + + return 0; +} + +static int +acl_del_list (u32 acl_list_index) +{ + acl_main_t *am = &acl_main; + acl_list_t *a; + int i, ii; + if (pool_is_free_index (am->acls, acl_list_index)) + { + return -1; + } + + /* delete any references to the ACL */ + for (i = 0; i < vec_len (am->output_acl_vec_by_sw_if_index); i++) + { + for (ii = 0; ii < vec_len (am->output_acl_vec_by_sw_if_index[i]); + /* see body */ ) + { + if (acl_list_index == am->output_acl_vec_by_sw_if_index[i][ii]) + { + vec_del1 (am->output_acl_vec_by_sw_if_index[i], ii); + } + else + { + ii++; + } + } + } + for (i = 0; i < vec_len (am->input_acl_vec_by_sw_if_index); i++) + { + for (ii = 0; ii < vec_len (am->input_acl_vec_by_sw_if_index[i]); + /* see body */ ) + { + if (acl_list_index == am->input_acl_vec_by_sw_if_index[i][ii]) + { + vec_del1 (am->input_acl_vec_by_sw_if_index[i], ii); + } + else + { + ii++; + } + } + } + + /* now we can delete the ACL itself */ + a = &am->acls[acl_list_index]; + if (a->rules) + { + clib_mem_free (a->rules); + } + pool_put (am->acls, a); + return 0; +} + +/* Some aids in ASCII graphing the content */ +#define XX "\377" +#define __ "\000" +#define _(x) +#define v + +u8 ip4_5tuple_mask[] = +_(" dmac smac etype ") +_(ether) __ __ __ __ __ __ v __ __ __ __ __ __ v __ __ v + _(" v ihl totlen ") + _(0x0000) + __ __ __ __ + _(" ident fl+fo ") + _(0x0004) + __ __ __ __ + _(" ttl pr checksum ") + _(0x0008) + __ XX __ __ + _(" src address ") + _(0x000C) + XX XX XX XX + _(" dst address ") + _(0x0010) + XX XX XX XX + _("L4 T/U sport dport ") + _(tcpudp) + XX XX XX XX + _(padpad) + __ __ __ __ + _(padpad) + __ __ __ __ + _(padeth) + __ __; + + u8 ip6_5tuple_mask[] = + _(" dmac smac etype ") + _(ether) __ __ __ __ __ __ v __ __ __ __ __ __ v __ __ v + _(" v tc + flow ") + _(0x0000) __ __ __ __ + _(" plen nh hl ") + _(0x0004) __ __ XX __ + _(" src address ") + _(0x0008) XX XX XX XX + _(0x000C) XX XX XX XX + _(0x0010) XX XX XX XX + _(0x0014) XX XX XX XX + _(" dst address ") + _(0x0018) XX XX XX XX + _(0x001C) XX XX XX XX + _(0x0020) XX XX XX XX + _(0x0024) XX XX XX XX + _("L4T/U sport dport ") + _(tcpudp) XX XX XX XX _(padpad) __ __ __ __ _(padeth) __ __; + +#undef XX +#undef __ +#undef _ +#undef v + + static int count_skip (u8 * p, u32 size) +{ + u64 *p64 = (u64 *) p; + /* Be tolerant to null pointer */ + if (0 == p) + return 0; + + while ((0ULL == *p64) && ((u8 *) p64 - p) < size) + { + p64++; + } + return (p64 - (u64 *) p) / 2; +} + +static int +acl_classify_add_del_table_big (vnet_classify_main_t * cm, u8 * mask, + u32 mask_len, u32 next_table_index, + u32 miss_next_index, u32 * table_index, + int is_add) +{ + u32 nbuckets = 65536; + u32 memory_size = 2 << 30; + u32 skip = count_skip (mask, mask_len); + u32 match = (mask_len / 16) - skip; + u8 *skip_mask_ptr = mask + 16 * skip; + u32 current_data_flag = 0; + int current_data_offset = 0; + + if (0 == match) + match = 1; + + return vnet_classify_add_del_table (cm, skip_mask_ptr, nbuckets, + memory_size, skip, match, + next_table_index, miss_next_index, + table_index, current_data_flag, + current_data_offset, is_add); +} + +static int +acl_classify_add_del_table_small (vnet_classify_main_t * cm, u8 * mask, + u32 mask_len, u32 next_table_index, + u32 miss_next_index, u32 * table_index, + int is_add) +{ + u32 nbuckets = 32; + u32 memory_size = 2 << 20; + u32 skip = count_skip (mask, mask_len); + u32 match = (mask_len / 16) - skip; + u8 *skip_mask_ptr = mask + 16 * skip; + u32 current_data_flag = 0; + int current_data_offset = 0; + + if (0 == match) + match = 1; + + return vnet_classify_add_del_table (cm, skip_mask_ptr, nbuckets, + memory_size, skip, match, + next_table_index, miss_next_index, + table_index, current_data_flag, + current_data_offset, is_add); +} + + +static int +acl_unhook_l2_input_classify (acl_main_t * am, u32 sw_if_index) +{ + vnet_classify_main_t *cm = &vnet_classify_main; + u32 ip4_table_index = ~0; + u32 ip6_table_index = ~0; + + vec_validate_init_empty (am->acl_ip4_input_classify_table_by_sw_if_index, + sw_if_index, ~0); + vec_validate_init_empty (am->acl_ip6_input_classify_table_by_sw_if_index, + sw_if_index, ~0); + + vnet_l2_input_classify_enable_disable (sw_if_index, 0); + + if (am->acl_ip4_input_classify_table_by_sw_if_index[sw_if_index] != ~0) + { + ip4_table_index = + am->acl_ip4_input_classify_table_by_sw_if_index[sw_if_index]; + 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, + &ip4_table_index, 0); + } + if (am->acl_ip6_input_classify_table_by_sw_if_index[sw_if_index] != ~0) + { + ip6_table_index = + am->acl_ip6_input_classify_table_by_sw_if_index[sw_if_index]; + 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, + &ip6_table_index, 0); + } + + return 0; +} + +static int +acl_unhook_l2_output_classify (acl_main_t * am, u32 sw_if_index) +{ + vnet_classify_main_t *cm = &vnet_classify_main; + u32 ip4_table_index = ~0; + u32 ip6_table_index = ~0; + + vec_validate_init_empty (am->acl_ip4_output_classify_table_by_sw_if_index, + sw_if_index, ~0); + vec_validate_init_empty (am->acl_ip6_output_classify_table_by_sw_if_index, + sw_if_index, ~0); + + vnet_l2_output_classify_enable_disable (sw_if_index, 0); + + if (am->acl_ip4_output_classify_table_by_sw_if_index[sw_if_index] != ~0) + { + ip4_table_index = + am->acl_ip4_output_classify_table_by_sw_if_index[sw_if_index]; + 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, + &ip4_table_index, 0); + } + if (am->acl_ip6_output_classify_table_by_sw_if_index[sw_if_index] != ~0) + { + ip6_table_index = + am->acl_ip6_output_classify_table_by_sw_if_index[sw_if_index]; + 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, + &ip6_table_index, 0); + } + + return 0; +} + +static int +acl_hook_l2_input_classify (acl_main_t * am, u32 sw_if_index) +{ + vnet_classify_main_t *cm = &vnet_classify_main; + u32 ip4_table_index = ~0; + u32 ip6_table_index = ~0; + int rv; + + /* in case there were previous tables attached */ + acl_unhook_l2_input_classify (am, sw_if_index); + rv = + acl_classify_add_del_table_big (cm, ip4_5tuple_mask, + sizeof (ip4_5tuple_mask) - 1, ~0, + am->l2_input_classify_next_acl, + &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, + &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, + &ip4_table_index, 0); + return rv; + } + rv = + vnet_l2_input_classify_set_tables (sw_if_index, ip4_table_index, + ip6_table_index, ~0); + clib_warning + ("ACL enabling on interface sw_if_index %d, setting tables to the following: ip4: %d ip6: %d\n", + sw_if_index, ip4_table_index, ip6_table_index); + if (rv) + { + acl_classify_add_del_table_big (cm, ip6_5tuple_mask, + sizeof (ip6_5tuple_mask) - 1, ~0, + am->l2_input_classify_next_acl, + &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, + &ip4_table_index, 0); + return rv; + } + + am->acl_ip4_input_classify_table_by_sw_if_index[sw_if_index] = + ip4_table_index; + am->acl_ip6_input_classify_table_by_sw_if_index[sw_if_index] = + ip6_table_index; + + vnet_l2_input_classify_enable_disable (sw_if_index, 1); + return rv; +} + +static int +acl_hook_l2_output_classify (acl_main_t * am, u32 sw_if_index) +{ + vnet_classify_main_t *cm = &vnet_classify_main; + u32 ip4_table_index = ~0; + u32 ip6_table_index = ~0; + int rv; + + /* in case there were previous tables attached */ + acl_unhook_l2_output_classify (am, sw_if_index); + rv = + acl_classify_add_del_table_big (cm, ip4_5tuple_mask, + sizeof (ip4_5tuple_mask) - 1, ~0, + am->l2_output_classify_next_acl, + &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, + &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, + &ip4_table_index, 0); + return rv; + } + rv = + vnet_l2_output_classify_set_tables (sw_if_index, ip4_table_index, + ip6_table_index, ~0); + clib_warning + ("ACL enabling on interface sw_if_index %d, setting tables to the following: ip4: %d ip6: %d\n", + sw_if_index, ip4_table_index, ip6_table_index); + if (rv) + { + acl_classify_add_del_table_big (cm, ip6_5tuple_mask, + sizeof (ip6_5tuple_mask) - 1, ~0, + am->l2_output_classify_next_acl, + &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, + &ip4_table_index, 0); + return rv; + } + + am->acl_ip4_output_classify_table_by_sw_if_index[sw_if_index] = + ip4_table_index; + am->acl_ip6_output_classify_table_by_sw_if_index[sw_if_index] = + ip6_table_index; + + vnet_l2_output_classify_enable_disable (sw_if_index, 1); + return rv; +} + + +int +acl_interface_in_enable_disable (acl_main_t * am, u32 sw_if_index, + int enable_disable) +{ + int rv; + + /* Utterly wrong? */ + if (pool_is_free_index (am->vnet_main->interface_main.sw_interfaces, + sw_if_index)) + return VNET_API_ERROR_INVALID_SW_IF_INDEX; + + if (enable_disable) + { + rv = acl_hook_l2_input_classify (am, sw_if_index); + } + else + { + rv = acl_unhook_l2_input_classify (am, sw_if_index); + } + + return rv; +} + +int +acl_interface_out_enable_disable (acl_main_t * am, u32 sw_if_index, + int enable_disable) +{ + int rv; + + /* Utterly wrong? */ + if (pool_is_free_index (am->vnet_main->interface_main.sw_interfaces, + sw_if_index)) + return VNET_API_ERROR_INVALID_SW_IF_INDEX; + + if (enable_disable) + { + rv = acl_hook_l2_output_classify (am, sw_if_index); + } + else + { + rv = acl_unhook_l2_output_classify (am, sw_if_index); + } + + return rv; +} + + +static int +acl_interface_add_inout_acl (u32 sw_if_index, u8 is_input, u32 acl_list_index) +{ + acl_main_t *am = &acl_main; + if (is_input) + { + vec_validate (am->input_acl_vec_by_sw_if_index, sw_if_index); + vec_add (am->input_acl_vec_by_sw_if_index[sw_if_index], &acl_list_index, + 1); + acl_interface_in_enable_disable (am, sw_if_index, 1); + } + else + { + vec_validate (am->output_acl_vec_by_sw_if_index, sw_if_index); + vec_add (am->output_acl_vec_by_sw_if_index[sw_if_index], + &acl_list_index, 1); + acl_interface_out_enable_disable (am, sw_if_index, 1); + } + return 0; +} + +static int +acl_interface_del_inout_acl (u32 sw_if_index, u8 is_input, u32 acl_list_index) +{ + acl_main_t *am = &acl_main; + int i; + int rv = -1; + if (is_input) + { + 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_list_index == + am->input_acl_vec_by_sw_if_index[sw_if_index][i]) + { + vec_del1 (am->input_acl_vec_by_sw_if_index[sw_if_index], i); + rv = 0; + break; + } + } + if (0 == vec_len (am->input_acl_vec_by_sw_if_index[sw_if_index])) + { + acl_interface_in_enable_disable (am, sw_if_index, 0); + } + } + else + { + 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_list_index == + am->output_acl_vec_by_sw_if_index[sw_if_index][i]) + { + vec_del1 (am->output_acl_vec_by_sw_if_index[sw_if_index], i); + rv = 0; + break; + } + } + if (0 == vec_len (am->output_acl_vec_by_sw_if_index[sw_if_index])) + { + acl_interface_out_enable_disable (am, sw_if_index, 0); + } + } + return rv; +} + +static void +acl_interface_reset_inout_acls (u32 sw_if_index, u8 is_input) +{ + acl_main_t *am = &acl_main; + if (is_input) + { + acl_interface_in_enable_disable (am, sw_if_index, 0); + vec_validate (am->input_acl_vec_by_sw_if_index, sw_if_index); + vec_reset_length (am->input_acl_vec_by_sw_if_index[sw_if_index]); + } + else + { + acl_interface_out_enable_disable (am, sw_if_index, 0); + vec_validate (am->output_acl_vec_by_sw_if_index, sw_if_index); + vec_reset_length (am->output_acl_vec_by_sw_if_index[sw_if_index]); + } +} + +static int +acl_interface_add_del_inout_acl (u32 sw_if_index, u8 is_add, u8 is_input, + u32 acl_list_index) +{ + int rv = -1; + if (is_add) + { + rv = + acl_interface_add_inout_acl (sw_if_index, is_input, acl_list_index); + } + else + { + rv = + acl_interface_del_inout_acl (sw_if_index, is_input, acl_list_index); + } + return rv; +} + + +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 mac_mask[6]; + u8 prefix_len; + u32 count; + u32 table_index; +} macip_match_type_t; + +static u32 +macip_find_match_type (macip_match_type_t * mv, u8 * mac_mask, u8 prefix_len, + u8 is_ipv6) +{ + u32 i; + if (mv) + { + for (i = 0; i < vec_len (mv); i++) + { + if ((mv[i].prefix_len == prefix_len) && (mv[i].is_ipv6 == is_ipv6) + && (0 == memcmp (mv[i].mac_mask, mac_mask, 6))) + { + return i; + } + } + } + return ~0; +} + + +/* Get metric used to sort match types. + The more specific and the more often seen - the bigger the metric */ +static int +match_type_metric (macip_match_type_t * m) +{ + /* FIXME: count the ones in the MAC mask as well, check how well this heuristic works in real life */ + return m->prefix_len + m->is_ipv6 + 10 * m->count; +} + +static int +match_type_compare (macip_match_type_t * m1, macip_match_type_t * m2) +{ + /* Ascending sort based on the metric values */ + return match_type_metric (m1) - match_type_metric (m2); +} + +static int +macip_create_classify_tables (acl_main_t * am, u32 macip_acl_index) +{ + macip_match_type_t *mvec = NULL; + macip_match_type_t *mt; + macip_acl_list_t *a = &am->macip_acls[macip_acl_index]; + int i; + u32 match_type_index; + u32 last_table; + u8 mask[5 * 16]; + vnet_classify_main_t *cm = &vnet_classify_main; + + /* Count the number of different types of rules */ + for (i = 0; i < a->count; i++) + { + if (~0 == + (match_type_index = + macip_find_match_type (mvec, a->rules[i].src_mac_mask, + a->rules[i].src_prefixlen, + a->rules[i].is_ipv6))) + { + match_type_index = vec_len (mvec); + vec_validate (mvec, match_type_index); + memcpy (mvec[match_type_index].mac_mask, + a->rules[match_type_index].src_mac_mask, 6); + mvec[match_type_index].prefix_len = a->rules[i].src_prefixlen; + mvec[match_type_index].is_ipv6 = a->rules[i].is_ipv6; + mvec[match_type_index].table_index = ~0; + } + mvec[match_type_index].count++; + } + /* Put the most frequently used tables last in the list so we can create classifier tables in reverse order */ + vec_sort_with_function (mvec, match_type_compare); + /* Create the classifier tables */ + last_table = ~0; + vec_foreach (mt, mvec) + { + int mask_len; + int is6 = a->rules[i].is_ipv6; + int l3_src_offs = is6 ? 22 : 26; /* See the ascii art packet format above to verify these */ + memset (mask, 0, sizeof (mask)); + memcpy (&mask[6], mt->mac_mask, 6); + for (i = 0; i < (mt->prefix_len / 8); i++) + { + mask[l3_src_offs + i] = 0xff; + } + if (mt->prefix_len % 8) + { + mask[l3_src_offs + (mt->prefix_len / 8)] = + 0xff - ((1 << (8 - mt->prefix_len % 8)) - 1); + } + mask_len = ((l3_src_offs + (mt->prefix_len / 8)) / 16 + 1) * 16; + acl_classify_add_del_table_small (cm, mask, mask_len, last_table, + (~0 == last_table) ? 0 : ~0, &mt->table_index, + 1); + last_table = mt->table_index; + } + a->ip4_table_index = ~0; + a->ip6_table_index = ~0; + a->l2_table_index = last_table; + + /* Populate the classifier tables with rules from the MACIP ACL */ + for (i = 0; i < a->count; i++) + { + u32 action = 0; + u32 metadata = 0; + int is6 = a->rules[i].is_ipv6; + int l3_src_offs = is6 ? 22 : 26; /* See the ascii art packet format above to verify these */ + memset (mask, 0, sizeof (mask)); + memcpy (&mask[6], a->rules[i].src_mac, 6); + if (is6) + { + memcpy (&mask[l3_src_offs], &a->rules[i].src_ip_addr.ip6, 16); + } + else + { + memcpy (&mask[l3_src_offs], &a->rules[i].src_ip_addr.ip4, 4); + } + match_type_index = + macip_find_match_type (mvec, a->rules[i].src_mac_mask, + a->rules[i].src_prefixlen, + a->rules[i].is_ipv6); + /* add session to table mvec[match_type_index].table_index; */ + 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); + } + return 0; +} + +static void +macip_destroy_classify_tables (acl_main_t * am, u32 macip_acl_index) +{ + vnet_classify_main_t *cm = &vnet_classify_main; + macip_acl_list_t *a = &am->macip_acls[macip_acl_index]; + + if (a->ip4_table_index != ~0) + { + acl_classify_add_del_table_small (cm, 0, ~0, ~0, ~0, &a->ip4_table_index, 0); + a->ip4_table_index = ~0; + } + if (a->ip6_table_index != ~0) + { + acl_classify_add_del_table_small (cm, 0, ~0, ~0, ~0, &a->ip6_table_index, 0); + a->ip6_table_index = ~0; + } + if (a->l2_table_index != ~0) + { + acl_classify_add_del_table_small (cm, 0, ~0, ~0, ~0, &a->l2_table_index, 0); + a->l2_table_index = ~0; + } +} + +static int +macip_acl_add_list (u32 count, vl_api_macip_acl_rule_t rules[], + u32 * acl_list_index, u8 * tag) +{ + acl_main_t *am = &acl_main; + macip_acl_list_t *a; + macip_acl_rule_t *r; + macip_acl_rule_t *acl_new_rules; + int i; + + /* Create and populate the rules */ + acl_new_rules = clib_mem_alloc_aligned (sizeof (macip_acl_rule_t) * count, + CLIB_CACHE_LINE_BYTES); + if (!acl_new_rules) + { + /* Could not allocate rules. New or existing ACL - bail out regardless */ + return -1; + } + + for (i = 0; i < count; i++) + { + r = &acl_new_rules[i]; + r->is_permit = rules[i].is_permit; + r->is_ipv6 = rules[i].is_ipv6; + memcpy (&r->src_mac, rules[i].src_mac, 6); + memcpy (&r->src_mac_mask, rules[i].src_mac_mask, 6); + + memcpy (&r->src_ip_addr, rules[i].src_ip_addr, sizeof (r->src_ip_addr)); + r->src_prefixlen = rules[i].src_ip_prefix_len; + } + + /* Get ACL index */ + pool_get_aligned (am->macip_acls, a, CLIB_CACHE_LINE_BYTES); + memset (a, 0, sizeof (*a)); + /* Will return the newly allocated ACL index */ + *acl_list_index = a - am->macip_acls; + + a->rules = acl_new_rules; + a->count = count; + memcpy (a->tag, tag, sizeof (a->tag)); + + /* Create and populate the classifer tables */ + macip_create_classify_tables (am, *acl_list_index); + + return 0; +} + + +/* No check for validity of sw_if_index - the callers were supposed to validate */ + +static int +macip_acl_interface_add_acl (acl_main_t * am, u32 sw_if_index, + u32 macip_acl_index) +{ + macip_acl_list_t *a; + int rv; + if (pool_is_free_index (am->macip_acls, macip_acl_index)) + { + return -1; + } + a = &am->macip_acls[macip_acl_index]; + vec_validate_init_empty (am->macip_acl_by_sw_if_index, sw_if_index, ~0); + am->macip_acl_by_sw_if_index[sw_if_index] = macip_acl_index; + /* Apply the classifier tables for L2 ACLs */ + rv = + vnet_set_input_acl_intfc (am->vlib_main, sw_if_index, a->ip4_table_index, + a->ip6_table_index, a->l2_table_index, 1); + return rv; +} + +static int +macip_acl_interface_del_acl (acl_main_t * am, u32 sw_if_index) +{ + int rv; + vec_validate_init_empty (am->macip_acl_by_sw_if_index, sw_if_index, ~0); + am->macip_acl_by_sw_if_index[sw_if_index] = ~0; + /* remove the classifier tables off the interface L2 ACL */ + rv = vnet_set_input_acl_intfc (am->vlib_main, sw_if_index, ~0, ~0, ~0, 0); + return rv; +} + +static int +macip_acl_del_list (u32 acl_list_index) +{ + acl_main_t *am = &acl_main; + macip_acl_list_t *a; + int i; + if (pool_is_free_index (am->macip_acls, acl_list_index)) + { + return -1; + } + + /* delete any references to the ACL */ + for (i = 0; i < vec_len (am->macip_acl_by_sw_if_index); i++) + { + if (am->macip_acl_by_sw_if_index[i] == acl_list_index) + { + macip_acl_interface_del_acl (am, i); + } + } + + /* Now that classifier tables are detached, clean them up */ + macip_destroy_classify_tables (am, acl_list_index); + + /* now we can delete the ACL itself */ + a = &am->macip_acls[acl_list_index]; + if (a->rules) + { + clib_mem_free (a->rules); + } + pool_put (am->macip_acls, a); + return 0; +} + + +static int +macip_acl_interface_add_del_acl (u32 sw_if_index, u8 is_add, + u32 acl_list_index) +{ + acl_main_t *am = &acl_main; + int rv = -1; + if (is_add) + { + rv = macip_acl_interface_add_acl (am, sw_if_index, acl_list_index); + } + else + { + rv = macip_acl_interface_del_acl (am, sw_if_index); + } + return rv; +} + +/* API message handler */ +static void +vl_api_acl_add_replace_t_handler (vl_api_acl_add_replace_t * mp) +{ + vl_api_acl_add_replace_reply_t *rmp; + acl_main_t *am = &acl_main; + int rv; + u32 acl_list_index = ntohl (mp->acl_index); + + rv = acl_add_list (ntohl (mp->count), mp->r, &acl_list_index, mp->tag); + + /* *INDENT-OFF* */ + REPLY_MACRO2(VL_API_ACL_ADD_REPLACE_REPLY, + ({ + rmp->acl_index = htonl(acl_list_index); + })); + /* *INDENT-ON* */ +} + +static void +vl_api_acl_del_t_handler (vl_api_acl_del_t * mp) +{ + acl_main_t *sm = &acl_main; + vl_api_acl_del_reply_t *rmp; + int rv; + + rv = acl_del_list (ntohl (mp->acl_index)); + + REPLY_MACRO (VL_API_ACL_DEL_REPLY); +} + +static void +vl_api_acl_interface_add_del_t_handler (vl_api_acl_interface_add_del_t * mp) +{ + acl_main_t *sm = &acl_main; + vl_api_acl_interface_add_del_reply_t *rmp; + int rv = -1; + VALIDATE_SW_IF_INDEX (mp); + + rv = + acl_interface_add_del_inout_acl (ntohl (mp->sw_if_index), mp->is_add, + mp->is_input, ntohl (mp->acl_index)); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_ACL_INTERFACE_ADD_DEL_REPLY); +} + +static void +vl_api_acl_interface_set_acl_list_t_handler + (vl_api_acl_interface_set_acl_list_t * mp) +{ + acl_main_t *sm = &acl_main; + vl_api_acl_interface_set_acl_list_reply_t *rmp; + int rv = 0; + int i; + VALIDATE_SW_IF_INDEX (mp); + u32 sw_if_index = ntohl (mp->sw_if_index); + + acl_interface_reset_inout_acls (sw_if_index, 0); + acl_interface_reset_inout_acls (sw_if_index, 1); + + for (i = 0; i < mp->count; i++) + { + acl_interface_add_del_inout_acl (sw_if_index, 1, (i < mp->n_input), + ntohl (mp->acls[i])); + } + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_ACL_INTERFACE_SET_ACL_LIST_REPLY); +} + +static void +copy_acl_rule_to_api_rule (vl_api_acl_rule_t * api_rule, acl_rule_t * r) +{ + api_rule->is_permit = r->is_permit; + api_rule->is_ipv6 = r->is_ipv6; + if(r->is_ipv6) + { + memcpy (api_rule->src_ip_addr, &r->src, sizeof (r->src)); + memcpy (api_rule->dst_ip_addr, &r->dst, sizeof (r->dst)); + } + else + { + memcpy (api_rule->src_ip_addr, &r->src.ip4, sizeof (r->src.ip4)); + memcpy (api_rule->dst_ip_addr, &r->dst.ip4, sizeof (r->dst.ip4)); + } + 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->tcp_flags_mask = r->tcp_flags_mask; + api_rule->tcp_flags_value = r->tcp_flags_value; +} + +static void +send_acl_details (acl_main_t * am, unix_shared_memory_queue_t * q, + acl_list_t * acl, u32 context) +{ + vl_api_acl_details_t *mp; + vl_api_acl_rule_t *rules; + int i; + int msg_size = sizeof (*mp) + sizeof (mp->r[0]) * acl->count; + + mp = vl_msg_api_alloc (msg_size); + memset (mp, 0, msg_size); + mp->_vl_msg_id = ntohs (VL_API_ACL_DETAILS + am->msg_id_base); + + /* fill in the message */ + mp->context = context; + mp->count = htonl (acl->count); + mp->acl_index = htonl (acl - am->acls); + memcpy (mp->tag, acl->tag, sizeof (mp->tag)); + // clib_memcpy (mp->r, acl->rules, acl->count * sizeof(acl->rules[0])); + rules = mp->r; + for (i = 0; i < acl->count; i++) + { + copy_acl_rule_to_api_rule (&rules[i], &acl->rules[i]); + } + + clib_warning("Sending acl details for ACL index %d", ntohl(mp->acl_index)); + vl_msg_api_send_shmem (q, (u8 *) & mp); +} + + +static void +vl_api_acl_dump_t_handler (vl_api_acl_dump_t * mp) +{ + acl_main_t *am = &acl_main; + u32 acl_index; + acl_list_t *acl; + + int rv = -1; + unix_shared_memory_queue_t *q; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + { + return; + } + + if (mp->acl_index == ~0) + { + /* *INDENT-OFF* */ + /* Just dump all ACLs */ + pool_foreach (acl, am->acls, + ({ + send_acl_details(am, q, acl, mp->context); + })); + /* *INDENT-ON* */ + } + else + { + acl_index = ntohl (mp->acl_index); + if (!pool_is_free_index (am->acls, acl_index)) + { + acl = &am->acls[acl_index]; + send_acl_details (am, q, acl, mp->context); + } + } + + if (rv == -1) + { + /* FIXME API: should we signal an error here at all ? */ + return; + } +} + +static void +send_acl_interface_list_details (acl_main_t * am, + unix_shared_memory_queue_t * q, + u32 sw_if_index, u32 context) +{ + vl_api_acl_interface_list_details_t *mp; + int msg_size; + int n_input; + int n_output; + int count; + int i = 0; + + vec_validate (am->input_acl_vec_by_sw_if_index, sw_if_index); + vec_validate (am->output_acl_vec_by_sw_if_index, sw_if_index); + + n_input = vec_len (am->input_acl_vec_by_sw_if_index[sw_if_index]); + n_output = vec_len (am->output_acl_vec_by_sw_if_index[sw_if_index]); + count = n_input + n_output; + + msg_size = sizeof (*mp); + msg_size += sizeof (mp->acls[0]) * count; + + mp = vl_msg_api_alloc (msg_size); + memset (mp, 0, msg_size); + mp->_vl_msg_id = + ntohs (VL_API_ACL_INTERFACE_LIST_DETAILS + am->msg_id_base); + + /* fill in the message */ + mp->context = context; + mp->sw_if_index = htonl (sw_if_index); + mp->count = count; + mp->n_input = n_input; + for (i = 0; i < n_input; i++) + { + mp->acls[i] = htonl (am->input_acl_vec_by_sw_if_index[sw_if_index][i]); + } + for (i = 0; i < n_output; i++) + { + mp->acls[n_input + i] = + htonl (am->output_acl_vec_by_sw_if_index[sw_if_index][i]); + } + + vl_msg_api_send_shmem (q, (u8 *) & mp); +} + +static void +vl_api_acl_interface_list_dump_t_handler (vl_api_acl_interface_list_dump_t * + mp) +{ + acl_main_t *am = &acl_main; + vnet_sw_interface_t *swif; + vnet_interface_main_t *im = &am->vnet_main->interface_main; + + int rv = -1; + u32 sw_if_index; + unix_shared_memory_queue_t *q; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + { + return; + } + + if (mp->sw_if_index == ~0) + { + /* *INDENT-OFF* */ + pool_foreach (swif, im->sw_interfaces, + ({ + send_acl_interface_list_details(am, q, swif->sw_if_index, mp->context); + })); + /* *INDENT-ON* */ + } + else + { + VALIDATE_SW_IF_INDEX (mp); + sw_if_index = ntohl (mp->sw_if_index); + send_acl_interface_list_details (am, q, sw_if_index, mp->context); + } + return; + + BAD_SW_IF_INDEX_LABEL; + if (rv == -1) + { + /* FIXME API: should we signal an error here at all ? */ + return; + } +} + +/* MACIP ACL API handlers */ + +static void +vl_api_macip_acl_add_t_handler (vl_api_macip_acl_add_t * mp) +{ + vl_api_macip_acl_add_reply_t *rmp; + acl_main_t *am = &acl_main; + int rv; + u32 acl_list_index = ~0; + + rv = + macip_acl_add_list (ntohl (mp->count), mp->r, &acl_list_index, mp->tag); + + /* *INDENT-OFF* */ + REPLY_MACRO2(VL_API_MACIP_ACL_ADD_REPLY, + ({ + rmp->acl_index = htonl(acl_list_index); + })); + /* *INDENT-ON* */ +} + +static void +vl_api_macip_acl_del_t_handler (vl_api_macip_acl_del_t * mp) +{ + acl_main_t *sm = &acl_main; + vl_api_macip_acl_del_reply_t *rmp; + int rv; + + rv = macip_acl_del_list (ntohl (mp->acl_index)); + + REPLY_MACRO (VL_API_MACIP_ACL_DEL_REPLY); +} + +static void +vl_api_macip_acl_interface_add_del_t_handler + (vl_api_macip_acl_interface_add_del_t * mp) +{ + acl_main_t *sm = &acl_main; + vl_api_macip_acl_interface_add_del_reply_t *rmp; + int rv = -1; + VALIDATE_SW_IF_INDEX (mp); + + rv = + macip_acl_interface_add_del_acl (ntohl (mp->sw_if_index), mp->is_add, + ntohl (mp->acl_index)); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_MACIP_ACL_INTERFACE_ADD_DEL_REPLY); +} + +static void +send_macip_acl_details (acl_main_t * am, unix_shared_memory_queue_t * q, + macip_acl_list_t * acl, u32 context) +{ + vl_api_macip_acl_details_t *mp; + vl_api_macip_acl_rule_t *rules; + macip_acl_rule_t *r; + int i; + int msg_size = sizeof (*mp) + (acl ? sizeof (mp->r[0]) * acl->count : 0); + + mp = vl_msg_api_alloc (msg_size); + memset (mp, 0, msg_size); + mp->_vl_msg_id = ntohs (VL_API_MACIP_ACL_DETAILS + am->msg_id_base); + + /* fill in the message */ + mp->context = context; + if (acl) + { + memcpy (mp->tag, acl->tag, sizeof (mp->tag)); + mp->count = htonl (acl->count); + mp->acl_index = htonl (acl - am->macip_acls); + rules = mp->r; + for (i = 0; i < acl->count; i++) + { + r = &acl->rules[i]; + rules[i].is_permit = r->is_permit; + rules[i].is_ipv6 = r->is_ipv6; + memcpy (rules[i].src_mac, &r->src_mac, sizeof (r->src_mac)); + memcpy (rules[i].src_mac_mask, &r->src_mac_mask, + sizeof (r->src_mac_mask)); + + memcpy (rules[i].src_ip_addr, &r->src_ip_addr, + sizeof (r->src_ip_addr)); + rules[i].src_ip_prefix_len = r->src_prefixlen; + } + } + else + { + /* No martini, no party - no ACL applied to this interface. */ + mp->acl_index = ~0; + mp->count = 0; + } + + vl_msg_api_send_shmem (q, (u8 *) & mp); +} + + +static void +vl_api_macip_acl_dump_t_handler (vl_api_macip_acl_dump_t * mp) +{ + acl_main_t *am = &acl_main; + macip_acl_list_t *acl; + + unix_shared_memory_queue_t *q; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + { + return; + } + + if (mp->acl_index == ~0) + { + /* Just dump all ACLs for now, with sw_if_index = ~0 */ + pool_foreach (acl, am->macip_acls, ( + { + send_macip_acl_details (am, q, acl, + mp-> + context);} + )); + /* *INDENT-ON* */ + } + else + { + u32 acl_index = ntohl (mp->acl_index); + if (!pool_is_free_index (am->macip_acls, acl_index)) + { + acl = &am->macip_acls[acl_index]; + send_macip_acl_details (am, q, acl, mp->context); + } + } +} + +static void +vl_api_macip_acl_interface_get_t_handler (vl_api_macip_acl_interface_get_t * + mp) +{ + acl_main_t *am = &acl_main; + vl_api_macip_acl_interface_get_reply_t *rmp; + u32 count = vec_len (am->macip_acl_by_sw_if_index); + int msg_size = sizeof (*rmp) + sizeof (rmp->acls[0]) * count; + unix_shared_memory_queue_t *q; + int i; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + { + return; + } + + rmp = vl_msg_api_alloc (msg_size); + memset (rmp, 0, msg_size); + rmp->_vl_msg_id = + ntohs (VL_API_MACIP_ACL_INTERFACE_GET_REPLY + am->msg_id_base); + rmp->context = mp->context; + rmp->count = htonl (count); + for (i = 0; i < count; i++) + { + rmp->acls[i] = htonl (am->macip_acl_by_sw_if_index[i]); + } + + 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; +#define _(N,n) \ + vl_msg_api_set_handlers((VL_API_##N + sm->msg_id_base), \ + #n, \ + vl_api_##n##_t_handler, \ + vl_noop_handler, \ + vl_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_acl_plugin_api_msg; +#undef _ + + return 0; +} + +#define vl_msg_name_crc_list +#include <acl/acl_all_api_h.h> +#undef vl_msg_name_crc_list + +static void +setup_message_id_table (acl_main_t * sm, api_main_t * am) +{ +#define _(id,n,crc) \ + vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + sm->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) +{ + acl_main_t *am = &acl_main; + u32 act = am->n_match_actions; + if (am->n_match_actions == 255) + { + return ~0; + } + 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; +} + +void +acl_setup_nodes (void) +{ + vlib_main_t *vm = vlib_get_main (); + acl_main_t *am = &acl_main; + vlib_node_t *n; + + 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 */ +} + + + +static clib_error_t * +acl_init (vlib_main_t * vm) +{ + acl_main_t *am = &acl_main; + clib_error_t *error = 0; + memset (am, 0, sizeof (*am)); + am->vlib_main = vm; + am->vnet_main = vnet_get_main (); + + u8 *name = format (0, "acl_%08x%c", api_version, 0); + + /* Ask for a correctly-sized block of API message decode slots */ + am->msg_id_base = vl_msg_api_get_msg_ids ((char *) name, + 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); + + return error; +} + +VLIB_INIT_FUNCTION (acl_init); diff --git a/plugins/acl-plugin/acl/acl.h b/plugins/acl-plugin/acl/acl.h new file mode 100644 index 00000000..afc9b289 --- /dev/null +++ b/plugins/acl-plugin/acl/acl.h @@ -0,0 +1,148 @@ +/* + * 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. + */ +#ifndef included_acl_h +#define included_acl_h + +#include <vnet/vnet.h> +#include <vnet/ip/ip.h> +#include <vnet/ethernet/ethernet.h> +#include <vnet/l2/l2_output.h> + + +#include <vppinfra/hash.h> +#include <vppinfra/error.h> +#include <vppinfra/elog.h> + +#define ACL_PLUGIN_VERSION_MAJOR 1 +#define ACL_PLUGIN_VERSION_MINOR 1 + +extern vlib_node_registration_t acl_in_node; +extern vlib_node_registration_t acl_out_node; + +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); +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); + +enum address_e { IP4, IP6 }; +typedef struct +{ + enum address_e type; + union { + ip6_address_t ip6; + ip4_address_t ip4; + } addr; +} address_t; + +/* + * ACL rules + */ +typedef struct +{ + u8 is_permit; + u8 is_ipv6; + ip46_address_t src; + u8 src_prefixlen; + ip46_address_t dst; + u8 dst_prefixlen; + u8 proto; + u16 src_port_or_type_first; + u16 src_port_or_type_last; + u16 dst_port_or_code_first; + u16 dst_port_or_code_last; + u8 tcp_flags_value; + u8 tcp_flags_mask; +} acl_rule_t; + +typedef struct +{ + u8 is_permit; + u8 is_ipv6; + u8 src_mac[6]; + u8 src_mac_mask[6]; + ip46_address_t src_ip_addr; + u8 src_prefixlen; +} macip_acl_rule_t; + +/* + * ACL + */ +typedef struct +{ + u8 tag[64]; + u32 count; + acl_rule_t *rules; +} acl_list_t; + +typedef struct +{ + u8 tag[64]; + u32 count; + macip_acl_rule_t *rules; + /* References to the classifier tables that will enforce the rules */ + u32 ip4_table_index; + u32 ip6_table_index; + u32 l2_table_index; +} macip_acl_list_t; + +typedef struct { + /* API message ID base */ + u16 msg_id_base; + + acl_list_t *acls; /* Pool of ACLs */ + macip_acl_list_t *macip_acls; /* Pool of MAC-IP ACLs */ + + /* ACLs associated with interfaces */ + u32 **input_acl_vec_by_sw_if_index; + u32 **output_acl_vec_by_sw_if_index; + + /* + * Classify tables used to grab the packets for the ACL check, + * and serving as the 5-tuple session tables at the same time + */ + u32 *acl_ip4_input_classify_table_by_sw_if_index; + u32 *acl_ip6_input_classify_table_by_sw_if_index; + u32 *acl_ip4_output_classify_table_by_sw_if_index; + u32 *acl_ip6_output_classify_table_by_sw_if_index; + + /* MACIP (input) ACLs associated with the interfaces */ + u32 *macip_acl_by_sw_if_index; + + /* next indices for our nodes in the l2-classify tables */ + u32 l2_input_classify_next_acl; + u32 l2_output_classify_next_acl; + + /* next node indices for feature bitmap */ + u32 acl_in_node_input_next_node_index[32]; + /* the respective thing for the output feature */ + l2_output_next_nodes_st acl_out_output_next_nodes; + + /* ACL match actions (must be coherent across in/out ACLs to next indices (can differ) */ + + u32 acl_in_ip4_match_next[256]; + u32 acl_in_ip6_match_next[256]; + u32 acl_out_ip4_match_next[256]; + u32 acl_out_ip6_match_next[256]; + u32 n_match_actions; + + + /* convenience */ + vlib_main_t * vlib_main; + vnet_main_t * vnet_main; + ethernet_main_t * ethernet_main; +} acl_main_t; + +extern acl_main_t acl_main; + + +#endif diff --git a/plugins/acl-plugin/acl/acl_all_api_h.h b/plugins/acl-plugin/acl/acl_all_api_h.h new file mode 100644 index 00000000..96eca56d --- /dev/null +++ b/plugins/acl-plugin/acl/acl_all_api_h.h @@ -0,0 +1,321 @@ +/* + * 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 the generated file, see BUILT_SOURCES in Makefile.am */ +#include <acl/acl.api.h> + +#ifdef vl_printfun + +#ifdef LP64 +#define _uword_fmt "%lld" +#define _uword_cast (long long) +#else +#define _uword_fmt "%ld" +#define _uword_cast long +#endif + +static inline void * +vl_api_acl_rule_t_print (vl_api_acl_rule_t * a, void *handle) +{ + vl_print (handle, "vl_api_acl_rule_t:\n"); + vl_print (handle, "is_permit: %u\n", (unsigned) a->is_permit); + vl_print (handle, "is_ipv6: %u\n", (unsigned) a->is_ipv6); + { + int _i; + for (_i = 0; _i < 16; _i++) + { + vl_print (handle, "src_ip_addr[%d]: %u\n", _i, a->src_ip_addr[_i]); + } + } + vl_print (handle, "src_ip_prefix_len: %u\n", + (unsigned) a->src_ip_prefix_len); + { + int _i; + for (_i = 0; _i < 16; _i++) + { + vl_print (handle, "dst_ip_addr[%d]: %u\n", _i, a->dst_ip_addr[_i]); + } + } + vl_print (handle, "dst_ip_prefix_len: %u\n", + (unsigned) a->dst_ip_prefix_len); + vl_print (handle, "proto: %u\n", (unsigned) a->proto); + vl_print (handle, "srcport_or_icmptype_first: %u\n", + (unsigned) a->srcport_or_icmptype_first); + vl_print (handle, "srcport_or_icmptype_last: %u\n", + (unsigned) a->srcport_or_icmptype_last); + vl_print (handle, "dstport_or_icmpcode_first: %u\n", + (unsigned) a->dstport_or_icmpcode_first); + vl_print (handle, "dstport_or_icmpcode_last: %u\n", + (unsigned) a->dstport_or_icmpcode_last); + vl_print (handle, "tcp_flags_mask: %u\n", (unsigned) a->tcp_flags_mask); + vl_print (handle, "tcp_flags_value: %u\n", (unsigned) a->tcp_flags_value); + return handle; +} + +static inline void * +vl_api_acl_add_replace_t_print (vl_api_acl_add_replace_t * a, void *handle) +{ + int i; + vl_print (handle, "vl_api_acl_add_replace_t:\n"); + vl_print (handle, "_vl_msg_id: %u\n", (unsigned) a->_vl_msg_id); + vl_print (handle, "client_index: %u\n", (unsigned) a->client_index); + vl_print (handle, "context: %u\n", (unsigned) a->context); + vl_print (handle, "acl_index: %u\n", (unsigned) a->acl_index); + vl_print (handle, "count: %u\n", (unsigned) a->count); + vl_print (handle, "r ----- \n"); + for (i = 0; i < a->count; i++) + { + vl_print (handle, " r[%d]:\n", i); + vl_api_acl_rule_t_print (&a->r[i], handle); + } + vl_print (handle, "r ----- END \n"); + return handle; +} + + +static inline void *vl_api_acl_details_t_print (vl_api_acl_details_t *a,void *handle) +{ + vl_print(handle, "vl_api_acl_details_t:\n"); + vl_print(handle, "_vl_msg_id: %u\n", (unsigned) a->_vl_msg_id); + vl_print(handle, "context: %u\n", (unsigned) a->context); + vl_print(handle, "acl_index: %u\n", (unsigned) a->acl_index); + { + int _i; + for (_i = 0; _i < 64; _i++) { + vl_print(handle, "tag[%d]: %u\n", _i, a->tag[_i]); + } + } + vl_print(handle, "count: %u\n", (unsigned) a->count); + vl_print(handle, "r ----- \n"); + // FIXME vl_api_acl_rule_t_print(&a->r, handle); + vl_print(handle, "r ----- END \n"); + return handle; +} + +static inline void * +vl_api_macip_acl_rule_t_print (vl_api_macip_acl_rule_t * a, void *handle) +{ + vl_print (handle, "vl_api_macip_acl_rule_t:\n"); + vl_print (handle, "is_permit: %u\n", (unsigned) a->is_permit); + vl_print (handle, "is_ipv6: %u\n", (unsigned) a->is_ipv6); + { + int _i; + for (_i = 0; _i < 6; _i++) + { + vl_print (handle, "src_mac[%d]: %u\n", _i, a->src_mac[_i]); + } + } + { + int _i; + for (_i = 0; _i < 6; _i++) + { + vl_print (handle, "src_mac_mask[%d]: %u\n", _i, a->src_mac_mask[_i]); + } + } + { + int _i; + for (_i = 0; _i < 16; _i++) + { + vl_print (handle, "src_ip_addr[%d]: %u\n", _i, a->src_ip_addr[_i]); + } + } + vl_print (handle, "src_ip_prefix_len: %u\n", + (unsigned) a->src_ip_prefix_len); + return handle; +} + +static inline void * +vl_api_macip_acl_add_t_print (vl_api_macip_acl_add_t * a, void *handle) +{ + int i; + vl_print (handle, "vl_api_macip_acl_add_t:\n"); + vl_print (handle, "_vl_msg_id: %u\n", (unsigned) a->_vl_msg_id); + vl_print (handle, "client_index: %u\n", (unsigned) a->client_index); + vl_print (handle, "context: %u\n", (unsigned) a->context); + vl_print (handle, "count: %u\n", (unsigned) a->count); + vl_print (handle, "r ----- \n"); + for (i = 0; i < a->count; i++) + { + vl_print (handle, " r[%d]:\n", i); + vl_api_macip_acl_rule_t_print (&a->r[i], handle); + } + vl_print (handle, "r ----- END \n"); + return handle; +} + +static inline void *vl_api_macip_acl_details_t_print (vl_api_macip_acl_details_t *a,void *handle) +{ + int i; + vl_print(handle, "vl_api_macip_acl_details_t:\n"); + vl_print(handle, "_vl_msg_id: %u\n", (unsigned) a->_vl_msg_id); + vl_print(handle, "context: %u\n", (unsigned) a->context); + vl_print(handle, "acl_index: %u\n", (unsigned) a->acl_index); + { + int _i; + for (_i = 0; _i < 64; _i++) { + vl_print(handle, "tag[%d]: %u\n", _i, a->tag[_i]); + } + } + vl_print(handle, "count: %u\n", (unsigned) a->count); + vl_print(handle, "r ----- \n"); + for (i = 0; i < a->count; i++) + { + vl_print (handle, " r[%d]:\n", i); + vl_api_macip_acl_rule_t_print (&a->r[i], handle); + } + vl_print(handle, "r ----- END \n"); + return handle; +} + +#endif /* vl_printfun */ + + +#ifdef vl_endianfun + +#undef clib_net_to_host_uword +#ifdef LP64 +#define clib_net_to_host_uword clib_net_to_host_u64 +#else +#define clib_net_to_host_uword clib_net_to_host_u32 +#endif + +/* + * Manual endian/print functions created by copypasting the automatically + * generated ones with small required adjustments. Appears the codegen + * can't make code to print the contents of custom-type array. + */ + +static inline void +vl_api_acl_rule_t_endian (vl_api_acl_rule_t * a) +{ + /* a->is_permit = a->is_permit (no-op) */ + /* a->is_ipv6 = a->is_ipv6 (no-op) */ + /* a->src_ip_addr[0..15] = a->src_ip_addr[0..15] (no-op) */ + /* a->src_ip_prefix_len = a->src_ip_prefix_len (no-op) */ + /* a->dst_ip_addr[0..15] = a->dst_ip_addr[0..15] (no-op) */ + /* a->dst_ip_prefix_len = a->dst_ip_prefix_len (no-op) */ + /* a->proto = a->proto (no-op) */ + a->srcport_or_icmptype_first = + clib_net_to_host_u16 (a->srcport_or_icmptype_first); + a->srcport_or_icmptype_last = + clib_net_to_host_u16 (a->srcport_or_icmptype_last); + a->dstport_or_icmpcode_first = + clib_net_to_host_u16 (a->dstport_or_icmpcode_first); + a->dstport_or_icmpcode_last = + clib_net_to_host_u16 (a->dstport_or_icmpcode_last); + /* a->tcp_flags_mask = a->tcp_flags_mask (no-op) */ + /* a->tcp_flags_value = a->tcp_flags_value (no-op) */ +} + +static inline void +vl_api_acl_add_replace_t_endian (vl_api_acl_add_replace_t * a) +{ + int i; + a->_vl_msg_id = clib_net_to_host_u16 (a->_vl_msg_id); + a->client_index = clib_net_to_host_u32 (a->client_index); + a->context = clib_net_to_host_u32 (a->context); + a->acl_index = clib_net_to_host_u32 (a->acl_index); + a->count = clib_net_to_host_u32 (a->count); + for (i = 0; i < a->count; i++) + { + vl_api_acl_rule_t_endian (&a->r[i]); + } +} + +static inline void vl_api_acl_details_t_endian (vl_api_acl_details_t *a) +{ + int i; + a->_vl_msg_id = clib_net_to_host_u16(a->_vl_msg_id); + a->context = clib_net_to_host_u32(a->context); + a->acl_index = clib_net_to_host_u32(a->acl_index); + /* a->tag[0..63] = a->tag[0..63] (no-op) */ + a->count = clib_net_to_host_u32(a->count); + for (i = 0; i < a->count; i++) + { + vl_api_acl_rule_t_endian (&a->r[i]); + } +} + +static inline void vl_api_acl_interface_list_details_t_endian (vl_api_acl_interface_list_details_t *a) +{ + int i; + a->_vl_msg_id = clib_net_to_host_u16(a->_vl_msg_id); + a->context = clib_net_to_host_u32(a->context); + a->sw_if_index = clib_net_to_host_u32(a->sw_if_index); + /* a->count = a->count (no-op) */ + /* a->n_input = a->n_input (no-op) */ + for(i=0; i<a->count; i++) { + a->acls[i] = clib_net_to_host_u32(a->acls[i]); + } +} + +static inline void vl_api_acl_interface_set_acl_list_t_endian (vl_api_acl_interface_set_acl_list_t *a) +{ + int i; + a->_vl_msg_id = clib_net_to_host_u16(a->_vl_msg_id); + a->client_index = clib_net_to_host_u32(a->client_index); + a->context = clib_net_to_host_u32(a->context); + a->sw_if_index = clib_net_to_host_u32(a->sw_if_index); + /* a->count = a->count (no-op) */ + /* a->n_input = a->n_input (no-op) */ + for(i=0; i<a->count; i++) { + a->acls[i] = clib_net_to_host_u32(a->acls[i]); + } +} + +static inline void +vl_api_macip_acl_rule_t_endian (vl_api_macip_acl_rule_t * a) +{ + /* a->is_permit = a->is_permit (no-op) */ + /* a->is_ipv6 = a->is_ipv6 (no-op) */ + /* a->src_mac[0..5] = a->src_mac[0..5] (no-op) */ + /* a->src_mac_mask[0..5] = a->src_mac_mask[0..5] (no-op) */ + /* a->src_ip_addr[0..15] = a->src_ip_addr[0..15] (no-op) */ + /* a->src_ip_prefix_len = a->src_ip_prefix_len (no-op) */ +} + +static inline void +vl_api_macip_acl_add_t_endian (vl_api_macip_acl_add_t * a) +{ + int i; + a->_vl_msg_id = clib_net_to_host_u16 (a->_vl_msg_id); + a->client_index = clib_net_to_host_u32 (a->client_index); + a->context = clib_net_to_host_u32 (a->context); + a->count = clib_net_to_host_u32 (a->count); + for (i = 0; i < a->count; i++) + { + vl_api_macip_acl_rule_t_endian (&a->r[i]); + } +} + +static inline void vl_api_macip_acl_details_t_endian (vl_api_macip_acl_details_t *a) +{ + int i; + a->_vl_msg_id = clib_net_to_host_u16(a->_vl_msg_id); + a->context = clib_net_to_host_u32(a->context); + a->acl_index = clib_net_to_host_u32(a->acl_index); + /* a->tag[0..63] = a->tag[0..63] (no-op) */ + a->count = clib_net_to_host_u32(a->count); + for (i = 0; i < a->count; i++) + { + vl_api_macip_acl_rule_t_endian (&a->r[i]); + } +} + + + + +#endif /* vl_printfun */ + + diff --git a/plugins/acl-plugin/acl/acl_msg_enum.h b/plugins/acl-plugin/acl/acl_msg_enum.h new file mode 100644 index 00000000..14d8b48c --- /dev/null +++ b/plugins/acl-plugin/acl/acl_msg_enum.h @@ -0,0 +1,28 @@ +/* + * 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. + */ +#ifndef included_acl_msg_enum_h +#define included_acl_msg_enum_h + +#include <vppinfra/byte_order.h> + +#define vl_msg_id(n,h) n, +typedef enum { +#include <acl/acl_all_api_h.h> + /* We'll want to know how many messages IDs we need... */ + VL_MSG_FIRST_AVAILABLE, +} vl_msg_id_t; +#undef vl_msg_id + +#endif diff --git a/plugins/acl-plugin/acl/acl_test.c b/plugins/acl-plugin/acl/acl_test.c new file mode 100644 index 00000000..a0e413e1 --- /dev/null +++ b/plugins/acl-plugin/acl/acl_test.c @@ -0,0 +1,1024 @@ +/* + * Copyright (c) 2015 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. + */ +/* + *------------------------------------------------------------------ + * acl_test.c - test harness plugin + *------------------------------------------------------------------ + */ + +#include <vat/vat.h> +#include <vlibapi/api.h> +#include <vlibmemory/api.h> +#include <vlibsocket/api.h> +#include <vppinfra/error.h> +#include <vnet/ip/ip.h> +#include <arpa/inet.h> + +uword unformat_sw_if_index (unformat_input_t * input, va_list * args); + +/* Declare message IDs */ +#include <acl/acl_msg_enum.h> + +/* define message structures */ +#define vl_typedefs +#include <acl/acl_all_api_h.h> +#undef vl_typedefs + +/* define message structures */ +#define vl_endianfun +#include <acl/acl_all_api_h.h> +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) +#define vl_printfun +#include <acl/acl_all_api_h.h> +#undef vl_printfun + +/* Get the API version number. */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include <acl/acl_all_api_h.h> +#undef vl_api_version + +typedef struct { + /* API message ID base */ + u16 msg_id_base; + vat_main_t *vat_main; +} acl_test_main_t; + +acl_test_main_t acl_test_main; + +#define foreach_standard_reply_retval_handler \ +_(acl_del_reply) \ +_(acl_interface_add_del_reply) \ +_(macip_acl_interface_add_del_reply) \ +_(acl_interface_set_acl_list_reply) \ +_(macip_acl_del_reply) + +#define foreach_reply_retval_aclindex_handler \ +_(acl_add_replace_reply) \ +_(macip_acl_add_reply) + +#define _(n) \ + static void vl_api_##n##_t_handler \ + (vl_api_##n##_t * mp) \ + { \ + vat_main_t * vam = acl_test_main.vat_main; \ + i32 retval = ntohl(mp->retval); \ + if (vam->async_mode) { \ + vam->async_errors += (retval < 0); \ + } else { \ + vam->retval = retval; \ + vam->result_ready = 1; \ + } \ + } +foreach_standard_reply_retval_handler; +#undef _ + +#define _(n) \ + static void vl_api_##n##_t_handler \ + (vl_api_##n##_t * mp) \ + { \ + vat_main_t * vam = acl_test_main.vat_main; \ + i32 retval = ntohl(mp->retval); \ + if (vam->async_mode) { \ + vam->async_errors += (retval < 0); \ + } else { \ + clib_warning("ACL index: %d", ntohl(mp->acl_index)); \ + vam->retval = retval; \ + vam->result_ready = 1; \ + } \ + } +foreach_reply_retval_aclindex_handler; +#undef _ + +/* These two ought to be in a library somewhere but they aren't */ +static uword +my_unformat_mac_address (unformat_input_t * input, va_list * args) +{ + u8 *a = va_arg (*args, u8 *); + return unformat (input, "%x:%x:%x:%x:%x:%x", &a[0], &a[1], &a[2], &a[3], + &a[4], &a[5]); +} + +static u8 * +my_format_mac_address (u8 * s, va_list * args) +{ + u8 *a = va_arg (*args, u8 *); + return format (s, "%02x:%02x:%02x:%02x:%02x:%02x", + a[0], a[1], a[2], a[3], a[4], a[5]); +} + + + +static void vl_api_acl_plugin_get_version_reply_t_handler + (vl_api_acl_plugin_get_version_reply_t * mp) + { + vat_main_t * vam = acl_test_main.vat_main; + clib_warning("ACL plugin version: %d.%d", ntohl(mp->major), ntohl(mp->minor)); + vam->result_ready = 1; + } + +static void vl_api_acl_interface_list_details_t_handler + (vl_api_acl_interface_list_details_t * mp) + { + int i; + vat_main_t * vam = acl_test_main.vat_main; + u8 *out = 0; + vl_api_acl_interface_list_details_t_endian(mp); + out = format(out, "sw_if_index: %d, count: %d, n_input: %d\n", mp->sw_if_index, mp->count, mp->n_input); + out = format(out, " input "); + for(i=0; i<mp->count; i++) { + out = format(out, "%d ", mp->acls[i]); + if (i == mp->n_input-1) + out = format(out, "\n output "); + } + out = format(out, "\n"); + clib_warning("%s", out); + vec_free(out); + vam->result_ready = 1; + } + + +static inline u8 * +vl_api_acl_rule_t_pretty_format (u8 *out, vl_api_acl_rule_t * a) +{ + int af = a->is_ipv6 ? AF_INET6 : AF_INET; + u8 src[INET6_ADDRSTRLEN]; + u8 dst[INET6_ADDRSTRLEN]; + inet_ntop(af, a->src_ip_addr, (void *)src, sizeof(src)); + inet_ntop(af, a->dst_ip_addr, (void *)dst, sizeof(dst)); + + out = format(out, "%s action %d src %s/%d dst %s/%d proto %d sport %d-%d dport %d-%d tcpflags %d %d", + a->is_ipv6 ? "ipv6" : "ipv4", a->is_permit, + src, a->src_ip_prefix_len, + dst, a->dst_ip_prefix_len, + a->proto, + a->srcport_or_icmptype_first, a->srcport_or_icmptype_last, + a->dstport_or_icmpcode_first, a->dstport_or_icmpcode_last, + a->tcp_flags_mask, a->tcp_flags_value); + return(out); +} + + + +static void vl_api_acl_details_t_handler + (vl_api_acl_details_t * mp) + { + int i; + vat_main_t * vam = acl_test_main.vat_main; + vl_api_acl_details_t_endian(mp); + u8 *out = 0; + out = format(0, "acl_index: %d, count: %d\n tag {%s}\n", mp->acl_index, mp->count, mp->tag); + for(i=0; i<mp->count; i++) { + out = format(out, " "); + out = vl_api_acl_rule_t_pretty_format(out, &mp->r[i]); + out = format(out, "%s\n", i<mp->count-1 ? "," : ""); + } + clib_warning("%s", out); + vec_free(out); + vam->result_ready = 1; + } + +static inline u8 * +vl_api_macip_acl_rule_t_pretty_format (u8 *out, vl_api_macip_acl_rule_t * a) +{ + int af = a->is_ipv6 ? AF_INET6 : AF_INET; + u8 src[INET6_ADDRSTRLEN]; + inet_ntop(af, a->src_ip_addr, (void *)src, sizeof(src)); + + out = format(out, "%s action %d ip %s/%d mac %U mask %U", + a->is_ipv6 ? "ipv6" : "ipv4", a->is_permit, + src, a->src_ip_prefix_len, + my_format_mac_address, a->src_mac, + my_format_mac_address, a->src_mac_mask); + return(out); +} + + +static void vl_api_macip_acl_details_t_handler + (vl_api_macip_acl_details_t * mp) + { + int i; + vat_main_t * vam = acl_test_main.vat_main; + vl_api_macip_acl_details_t_endian(mp); + u8 *out = format(0,"MACIP acl_index: %d, count: %d\n tag {%s}\n", mp->acl_index, mp->count, mp->tag); + for(i=0; i<mp->count; i++) { + out = format(out, " "); + out = vl_api_macip_acl_rule_t_pretty_format(out, &mp->r[i]); + out = format(out, "%s\n", i<mp->count-1 ? "," : ""); + } + clib_warning("%s", out); + vec_free(out); + vam->result_ready = 1; + } + +static void vl_api_macip_acl_interface_get_reply_t_handler + (vl_api_macip_acl_interface_get_reply_t * mp) + { + int i; + vat_main_t * vam = acl_test_main.vat_main; + u8 *out = format(0, "sw_if_index with MACIP ACL count: %d\n", ntohl(mp->count)); + for(i=0; i<ntohl(mp->count); i++) { + out = format(out, " macip_acl_interface_add_del sw_if_index %d add acl %d\n", i, ntohl(mp->acls[i])); + } + out = format(out, "\n"); + clib_warning("%s", out); + vec_free(out); + vam->result_ready = 1; + } + + +/* + * Table of message reply handlers, must include boilerplate handlers + * we just generated + */ +#define foreach_vpe_api_reply_msg \ +_(ACL_ADD_REPLACE_REPLY, acl_add_replace_reply) \ +_(ACL_DEL_REPLY, acl_del_reply) \ +_(ACL_INTERFACE_ADD_DEL_REPLY, acl_interface_add_del_reply) \ +_(ACL_INTERFACE_SET_ACL_LIST_REPLY, acl_interface_set_acl_list_reply) \ +_(ACL_INTERFACE_LIST_DETAILS, acl_interface_list_details) \ +_(ACL_DETAILS, acl_details) \ +_(MACIP_ACL_ADD_REPLY, macip_acl_add_reply) \ +_(MACIP_ACL_DEL_REPLY, macip_acl_del_reply) \ +_(MACIP_ACL_DETAILS, macip_acl_details) \ +_(MACIP_ACL_INTERFACE_ADD_DEL_REPLY, macip_acl_interface_add_del_reply) \ +_(MACIP_ACL_INTERFACE_GET_REPLY, macip_acl_interface_get_reply) \ +_(ACL_PLUGIN_GET_VERSION_REPLY, acl_plugin_get_version_reply) + +/* M: construct, but don't yet send a message */ + +#define M(T,t) \ +do { \ + vam->result_ready = 0; \ + mp = vl_msg_api_alloc(sizeof(*mp)); \ + memset (mp, 0, sizeof (*mp)); \ + mp->_vl_msg_id = ntohs (VL_API_##T + sm->msg_id_base); \ + mp->client_index = vam->my_client_index; \ +} while(0); + +#define M2(T,t,n) \ +do { \ + vam->result_ready = 0; \ + mp = vl_msg_api_alloc(sizeof(*mp)+(n)); \ + memset (mp, 0, sizeof (*mp)); \ + mp->_vl_msg_id = ntohs (VL_API_##T + sm->msg_id_base); \ + mp->client_index = vam->my_client_index; \ +} while(0); + +/* S: send a message */ +#define S (vl_msg_api_send_shmem (vam->vl_input_queue, (u8 *)&mp)) + +/* W: wait for results, with timeout */ +#define W \ +do { \ + timeout = vat_time_now (vam) + 1.0; \ + \ + while (vat_time_now (vam) < timeout) { \ + if (vam->result_ready == 1) { \ + return (vam->retval); \ + } \ + } \ + return -99; \ +} while(0); + +static int api_acl_plugin_get_version (vat_main_t * vam) +{ + acl_test_main_t * sm = &acl_test_main; + vl_api_acl_plugin_get_version_t * mp; + u32 msg_size = sizeof(*mp); + f64 timeout; + + vam->result_ready = 0; + mp = vl_msg_api_alloc_as_if_client(msg_size); + memset (mp, 0, msg_size); + mp->_vl_msg_id = ntohs (VL_API_ACL_PLUGIN_GET_VERSION + sm->msg_id_base); + mp->client_index = vam->my_client_index; + + /* send it... */ + S; + + /* Wait for a reply... */ + W; + + return 0; +} + +static int api_macip_acl_interface_get (vat_main_t * vam) +{ + acl_test_main_t * sm = &acl_test_main; + vl_api_acl_plugin_get_version_t * mp; + u32 msg_size = sizeof(*mp); + f64 timeout; + + vam->result_ready = 0; + mp = vl_msg_api_alloc_as_if_client(msg_size); + memset (mp, 0, msg_size); + mp->_vl_msg_id = ntohs (VL_API_MACIP_ACL_INTERFACE_GET + sm->msg_id_base); + mp->client_index = vam->my_client_index; + + /* send it... */ + S; + + /* Wait for a reply... */ + W; + + return 0; +} + +#define vec_validate_acl_rules(v, idx) \ + do { \ + if (vec_len(v) < idx+1) { \ + vec_validate(v, idx); \ + v[idx].is_permit = 0x1; \ + v[idx].srcport_or_icmptype_last = 0xffff; \ + v[idx].dstport_or_icmpcode_last = 0xffff; \ + } \ + } while (0) + + +static int api_acl_add_replace (vat_main_t * vam) +{ + acl_test_main_t * sm = &acl_test_main; + unformat_input_t * i = vam->input; + f64 timeout; + vl_api_acl_add_replace_t * mp; + u32 acl_index = ~0; + u32 msg_size = sizeof (*mp); /* without the rules */ + + vl_api_acl_rule_t *rules = 0; + int rule_idx = 0; + int n_rules = 0; + u32 proto = 0; + u32 port1 = 0; + u32 port2 = 0; + u32 action = 0; + u32 tcpflags, tcpmask; + u32 src_prefix_length = 0, dst_prefix_length = 0; + ip4_address_t src_v4address, dst_v4address; + ip6_address_t src_v6address, dst_v6address; + u8 *tag = 0; + + if (!unformat (i, "%d", &acl_index)) { + /* Just assume -1 */ + } + + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "ipv6")) + { + vec_validate_acl_rules(rules, rule_idx); + rules[rule_idx].is_ipv6 = 1; + } + else if (unformat (i, "ipv4")) + { + vec_validate_acl_rules(rules, rule_idx); + rules[rule_idx].is_ipv6 = 0; + } + else if (unformat (i, "permit+reflect")) + { + vec_validate_acl_rules(rules, rule_idx); + rules[rule_idx].is_permit = 2; + } + else if (unformat (i, "permit")) + { + vec_validate_acl_rules(rules, rule_idx); + rules[rule_idx].is_permit = 1; + } + else if (unformat (i, "action %d", &action)) + { + vec_validate_acl_rules(rules, rule_idx); + rules[rule_idx].is_permit = action; + } + else if (unformat (i, "src %U/%d", + unformat_ip4_address, &src_v4address, &src_prefix_length)) + { + vec_validate_acl_rules(rules, rule_idx); + memcpy (rules[rule_idx].src_ip_addr, &src_v4address, 4); + rules[rule_idx].src_ip_prefix_len = src_prefix_length; + rules[rule_idx].is_ipv6 = 0; + } + else if (unformat (i, "src %U/%d", + unformat_ip6_address, &src_v6address, &src_prefix_length)) + { + vec_validate_acl_rules(rules, rule_idx); + memcpy (rules[rule_idx].src_ip_addr, &src_v6address, 16); + rules[rule_idx].src_ip_prefix_len = src_prefix_length; + rules[rule_idx].is_ipv6 = 1; + } + else if (unformat (i, "dst %U/%d", + unformat_ip4_address, &dst_v4address, &dst_prefix_length)) + { + vec_validate_acl_rules(rules, rule_idx); + memcpy (rules[rule_idx].dst_ip_addr, &dst_v4address, 4); + rules[rule_idx].dst_ip_prefix_len = dst_prefix_length; + rules[rule_idx].is_ipv6 = 0; + } + else if (unformat (i, "dst %U/%d", + unformat_ip6_address, &dst_v6address, &dst_prefix_length)) + { + vec_validate_acl_rules(rules, rule_idx); + memcpy (rules[rule_idx].dst_ip_addr, &dst_v6address, 16); + rules[rule_idx].dst_ip_prefix_len = dst_prefix_length; + rules[rule_idx].is_ipv6 = 1; + } + else if (unformat (i, "sport %d-%d", &port1, &port2)) + { + vec_validate_acl_rules(rules, rule_idx); + rules[rule_idx].srcport_or_icmptype_first = htons(port1); + rules[rule_idx].srcport_or_icmptype_last = htons(port2); + } + else if (unformat (i, "sport %d", &port1)) + { + vec_validate_acl_rules(rules, rule_idx); + rules[rule_idx].srcport_or_icmptype_first = htons(port1); + rules[rule_idx].srcport_or_icmptype_last = htons(port1); + } + else if (unformat (i, "dport %d-%d", &port1, &port2)) + { + vec_validate_acl_rules(rules, rule_idx); + rules[rule_idx].dstport_or_icmpcode_first = htons(port1); + rules[rule_idx].dstport_or_icmpcode_last = htons(port2); + } + else if (unformat (i, "dport %d", &port1)) + { + vec_validate_acl_rules(rules, rule_idx); + rules[rule_idx].dstport_or_icmpcode_first = htons(port1); + rules[rule_idx].dstport_or_icmpcode_last = htons(port1); + } + else if (unformat (i, "tcpflags %d %d", &tcpflags, &tcpmask)) + { + vec_validate_acl_rules(rules, rule_idx); + rules[rule_idx].tcp_flags_value = tcpflags; + rules[rule_idx].tcp_flags_mask = tcpmask; + } + else if (unformat (i, "proto %d", &proto)) + { + vec_validate_acl_rules(rules, rule_idx); + rules[rule_idx].proto = proto; + } + else if (unformat (i, "tag %s", &tag)) + { + } + else if (unformat (i, ",")) + { + rule_idx++; + vec_validate_acl_rules(rules, rule_idx); + } + else + break; + } + + /* Construct the API message */ + vam->result_ready = 0; + + if(rules) + n_rules = vec_len(rules); + else + n_rules = 0; + + msg_size += n_rules*sizeof(rules[0]); + + mp = vl_msg_api_alloc_as_if_client(msg_size); + memset (mp, 0, msg_size); + mp->_vl_msg_id = ntohs (VL_API_ACL_ADD_REPLACE + sm->msg_id_base); + mp->client_index = vam->my_client_index; + if (n_rules > 0) + clib_memcpy(mp->r, rules, n_rules*sizeof (vl_api_acl_rule_t)); + if (tag) + { + if (vec_len(tag) >= sizeof(mp->tag)) + { + tag[sizeof(mp->tag)-1] = 0; + _vec_len(tag) = sizeof(mp->tag); + } + clib_memcpy(mp->tag, tag, vec_len(tag)); + vec_free(tag); + } + mp->acl_index = ntohl(acl_index); + mp->count = htonl(n_rules); + + /* send it... */ + S; + + /* Wait for a reply... */ + W; +} + +static int api_acl_del (vat_main_t * vam) +{ + acl_test_main_t * sm = &acl_test_main; + unformat_input_t * i = vam->input; + f64 timeout; + vl_api_acl_del_t * mp; + u32 acl_index = ~0; + + if (!unformat (i, "%d", &acl_index)) { + errmsg ("missing acl index\n"); + return -99; + } + + /* Construct the API message */ + M(ACL_DEL, acl_del); + mp->acl_index = ntohl(acl_index); + + /* send it... */ + S; + + /* Wait for a reply... */ + W; +} + +static int api_macip_acl_del (vat_main_t * vam) +{ + acl_test_main_t * sm = &acl_test_main; + unformat_input_t * i = vam->input; + f64 timeout; + vl_api_acl_del_t * mp; + u32 acl_index = ~0; + + if (!unformat (i, "%d", &acl_index)) { + errmsg ("missing acl index\n"); + return -99; + } + + /* Construct the API message */ + M(MACIP_ACL_DEL, acl_del); + mp->acl_index = ntohl(acl_index); + + /* send it... */ + S; + + /* Wait for a reply... */ + W; +} + +static int api_acl_interface_add_del (vat_main_t * vam) +{ + acl_test_main_t * sm = &acl_test_main; + unformat_input_t * i = vam->input; + f64 timeout; + vl_api_acl_interface_add_del_t * mp; + u32 sw_if_index = ~0; + u32 acl_index = ~0; + u8 is_input = 0; + u8 is_add = 0; + +// acl_interface_add_del <intfc> | sw_if_index <if-idx> acl_index <acl-idx> [out] [del] + + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "%d", &acl_index)) + ; + else + break; + } + + + /* Parse args required to build the message */ + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { + if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index)) + ; + else if (unformat (i, "sw_if_index %d", &sw_if_index)) + ; + else if (unformat (i, "add")) + is_add = 1; + else if (unformat (i, "del")) + is_add = 0; + else if (unformat (i, "acl %d", &acl_index)) + ; + else if (unformat (i, "input")) + is_input = 1; + else if (unformat (i, "output")) + is_input = 0; + else + break; + } + + if (sw_if_index == ~0) { + errmsg ("missing interface name / explicit sw_if_index number \n"); + return -99; + } + + if (acl_index == ~0) { + errmsg ("missing ACL index\n"); + return -99; + } + + + + /* Construct the API message */ + M(ACL_INTERFACE_ADD_DEL, acl_interface_add_del); + mp->acl_index = ntohl(acl_index); + mp->sw_if_index = ntohl(sw_if_index); + mp->is_add = is_add; + mp->is_input = is_input; + + /* send it... */ + S; + + /* Wait for a reply... */ + W; +} + +static int api_macip_acl_interface_add_del (vat_main_t * vam) +{ + acl_test_main_t * sm = &acl_test_main; + unformat_input_t * i = vam->input; + f64 timeout; + vl_api_macip_acl_interface_add_del_t * mp; + u32 sw_if_index = ~0; + u32 acl_index = ~0; + u8 is_add = 0; + + /* Parse args required to build the message */ + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { + if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index)) + ; + else if (unformat (i, "sw_if_index %d", &sw_if_index)) + ; + else if (unformat (i, "add")) + is_add = 1; + else if (unformat (i, "del")) + is_add = 0; + else if (unformat (i, "acl %d", &acl_index)) + ; + else + break; + } + + if (sw_if_index == ~0) { + errmsg ("missing interface name / explicit sw_if_index number \n"); + return -99; + } + + if (acl_index == ~0) { + errmsg ("missing ACL index\n"); + return -99; + } + + + + /* Construct the API message */ + M(MACIP_ACL_INTERFACE_ADD_DEL, macip_acl_interface_add_del); + mp->acl_index = ntohl(acl_index); + mp->sw_if_index = ntohl(sw_if_index); + mp->is_add = is_add; + + /* send it... */ + S; + + /* Wait for a reply... */ + W; +} + +static int api_acl_interface_set_acl_list (vat_main_t * vam) +{ + acl_test_main_t * sm = &acl_test_main; + unformat_input_t * i = vam->input; + f64 timeout; + vl_api_acl_interface_set_acl_list_t * mp; + u32 sw_if_index = ~0; + u32 acl_index = ~0; + u32 *inacls = 0; + u32 *outacls = 0; + u8 is_input = 0; + +// acl_interface_set_acl_list <intfc> | sw_if_index <if-idx> input [acl-idx list] output [acl-idx list] + + /* Parse args required to build the message */ + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { + if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index)) + ; + else if (unformat (i, "sw_if_index %d", &sw_if_index)) + ; + else if (unformat (i, "%d", &acl_index)) + { + if(is_input) + vec_add1(inacls, htonl(acl_index)); + else + vec_add1(outacls, htonl(acl_index)); + } + else if (unformat (i, "acl %d", &acl_index)) + ; + else if (unformat (i, "input")) + is_input = 1; + else if (unformat (i, "output")) + is_input = 0; + else + break; + } + + if (sw_if_index == ~0) { + errmsg ("missing interface name / explicit sw_if_index number \n"); + return -99; + } + + /* Construct the API message */ + M2(ACL_INTERFACE_SET_ACL_LIST, acl_interface_set_acl_list, sizeof(u32) * (vec_len(inacls) + vec_len(outacls))); + mp->sw_if_index = ntohl(sw_if_index); + mp->n_input = vec_len(inacls); + mp->count = vec_len(inacls) + vec_len(outacls); + vec_append(inacls, outacls); + if (vec_len(inacls) > 0) + clib_memcpy(mp->acls, inacls, vec_len(inacls)*sizeof(u32)); + + /* send it... */ + S; + + /* Wait for a reply... */ + W; +} + + +static int api_acl_interface_list_dump (vat_main_t * vam) +{ + acl_test_main_t * sm = &acl_test_main; + unformat_input_t * i = vam->input; + f64 timeout; + u32 sw_if_index = ~0; + vl_api_acl_interface_list_dump_t * mp; + + /* Parse args required to build the message */ + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { + if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index)) + ; + else if (unformat (i, "sw_if_index %d", &sw_if_index)) + ; + else + break; + } + + /* Construct the API message */ + M(ACL_INTERFACE_LIST_DUMP, acl_interface_list_dump); + mp->sw_if_index = ntohl (sw_if_index); + + /* send it... */ + S; + + /* Wait for a reply... */ + W; +} + +static int api_acl_dump (vat_main_t * vam) +{ + acl_test_main_t * sm = &acl_test_main; + unformat_input_t * i = vam->input; + f64 timeout; + u32 acl_index = ~0; + vl_api_acl_dump_t * mp; + + /* Parse args required to build the message */ + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { + if (unformat (i, "%d", &acl_index)) + ; + else + break; + } + + /* Construct the API message */ + M(ACL_DUMP, acl_dump); + mp->acl_index = ntohl (acl_index); + + /* send it... */ + S; + + /* Wait for a reply... */ + W; +} + +static int api_macip_acl_dump (vat_main_t * vam) +{ + acl_test_main_t * sm = &acl_test_main; + unformat_input_t * i = vam->input; + f64 timeout; + u32 acl_index = ~0; + vl_api_acl_dump_t * mp; + + /* Parse args required to build the message */ + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { + if (unformat (i, "%d", &acl_index)) + ; + else + break; + } + + /* Construct the API message */ + M(MACIP_ACL_DUMP, macip_acl_dump); + mp->acl_index = ntohl (acl_index); + + /* send it... */ + S; + + /* Wait for a reply... */ + W; +} + +#define vec_validate_macip_acl_rules(v, idx) \ + do { \ + if (vec_len(v) < idx+1) { \ + vec_validate(v, idx); \ + v[idx].is_permit = 0x1; \ + } \ + } while (0) + + +static int api_macip_acl_add (vat_main_t * vam) +{ + acl_test_main_t * sm = &acl_test_main; + unformat_input_t * i = vam->input; + f64 timeout; + vl_api_macip_acl_add_t * mp; + u32 msg_size = sizeof (*mp); /* without the rules */ + + vl_api_macip_acl_rule_t *rules = 0; + int rule_idx = 0; + int n_rules = 0; + u32 src_prefix_length = 0; + u32 action = 0; + ip4_address_t src_v4address; + ip6_address_t src_v6address; + u8 src_mac[6]; + u8 *tag = 0; + u8 mac_mask_all_1[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "ipv6")) + { + vec_validate_macip_acl_rules(rules, rule_idx); + rules[rule_idx].is_ipv6 = 1; + } + else if (unformat (i, "ipv4")) + { + vec_validate_macip_acl_rules(rules, rule_idx); + rules[rule_idx].is_ipv6 = 1; + } + else if (unformat (i, "permit")) + { + vec_validate_macip_acl_rules(rules, rule_idx); + rules[rule_idx].is_permit = 1; + } + else if (unformat (i, "deny")) + { + vec_validate_macip_acl_rules(rules, rule_idx); + rules[rule_idx].is_permit = 0; + } + else if (unformat (i, "action %d", &action)) + { + vec_validate_macip_acl_rules(rules, rule_idx); + rules[rule_idx].is_permit = action; + } + else if (unformat (i, "ip %U/%d", + unformat_ip4_address, &src_v4address, &src_prefix_length)) + { + vec_validate_macip_acl_rules(rules, rule_idx); + memcpy (rules[rule_idx].src_ip_addr, &src_v4address, 4); + rules[rule_idx].src_ip_prefix_len = src_prefix_length; + rules[rule_idx].is_ipv6 = 0; + } + else if (unformat (i, "ip %U/%d", + unformat_ip6_address, &src_v6address, &src_prefix_length)) + { + vec_validate_macip_acl_rules(rules, rule_idx); + memcpy (rules[rule_idx].src_ip_addr, &src_v6address, 16); + rules[rule_idx].src_ip_prefix_len = src_prefix_length; + rules[rule_idx].is_ipv6 = 1; + } + else if (unformat (i, "mac %U", + my_unformat_mac_address, &src_mac)) + { + vec_validate_macip_acl_rules(rules, rule_idx); + memcpy (rules[rule_idx].src_mac, &src_mac, 6); + memcpy (rules[rule_idx].src_mac_mask, &mac_mask_all_1, 6); + } + else if (unformat (i, "mask %U", + my_unformat_mac_address, &src_mac)) + { + vec_validate_macip_acl_rules(rules, rule_idx); + memcpy (rules[rule_idx].src_mac_mask, &src_mac, 6); + } + else if (unformat (i, "tag %s", &tag)) + { + } + else if (unformat (i, ",")) + { + rule_idx++; + vec_validate_macip_acl_rules(rules, rule_idx); + } + else + break; + } + + /* Construct the API message */ + vam->result_ready = 0; + + if(rules) + n_rules = vec_len(rules); + else + n_rules = 0; + + msg_size += n_rules*sizeof(rules[0]); + + mp = vl_msg_api_alloc_as_if_client(msg_size); + memset (mp, 0, msg_size); + mp->_vl_msg_id = ntohs (VL_API_MACIP_ACL_ADD + sm->msg_id_base); + mp->client_index = vam->my_client_index; + if (n_rules > 0) + clib_memcpy(mp->r, rules, n_rules*sizeof (mp->r[0])); + if (tag) + { + if (vec_len(tag) >= sizeof(mp->tag)) + { + tag[sizeof(mp->tag)-1] = 0; + _vec_len(tag) = sizeof(mp->tag); + } + clib_memcpy(mp->tag, tag, vec_len(tag)); + vec_free(tag); + } + + mp->count = htonl(n_rules); + + /* send it... */ + S; + + /* Wait for a reply... */ + W; +} + +/* + * List of messages that the api test plugin sends, + * and that the data plane plugin processes + */ +#define foreach_vpe_api_msg \ +_(acl_plugin_get_version, "") \ +_(acl_add_replace, "<acl-idx> [<ipv4|ipv6> <permit|permit+reflect|deny|action N> [src IP/plen] [dst IP/plen] [sport X-Y] [dport X-Y] [proto P] [tcpflags FL MASK], ... , ...") \ +_(acl_del, "<acl-idx>") \ +_(acl_dump, "[<acl-idx>]") \ +_(acl_interface_add_del, "<intfc> | sw_if_index <if-idx> [add|del] [input|output] acl <acl-idx>") \ +_(acl_interface_set_acl_list, "<intfc> | sw_if_index <if-idx> input [acl-idx list] output [acl-idx list]") \ +_(acl_interface_list_dump, "[<intfc> | sw_if_index <if-idx>]") \ +_(macip_acl_add, "...") \ +_(macip_acl_del, "<acl-idx>")\ +_(macip_acl_dump, "[<acl-idx>]") \ +_(macip_acl_interface_add_del, "<intfc> | sw_if_index <if-idx> [add|del] acl <acl-idx>") \ +_(macip_acl_interface_get, "") + + + +void vat_api_hookup (vat_main_t *vam) +{ + acl_test_main_t * sm = &acl_test_main; + /* Hook up handlers for replies from the data plane plug-in */ +#define _(N,n) \ + vl_msg_api_set_handlers((VL_API_##N + sm->msg_id_base), \ + #n, \ + vl_api_##n##_t_handler, \ + vl_noop_handler, \ + vl_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_vpe_api_reply_msg; +#undef _ + + /* API messages we can send */ +#define _(n,h) hash_set_mem (vam->function_by_name, #n, api_##n); + foreach_vpe_api_msg; +#undef _ + + /* Help strings */ +#define _(n,h) hash_set_mem (vam->help_by_name, #n, h); + foreach_vpe_api_msg; +#undef _ +} + +clib_error_t * vat_plugin_register (vat_main_t *vam) +{ + acl_test_main_t * sm = &acl_test_main; + u8 * name; + + sm->vat_main = vam; + + name = format (0, "acl_%08x%c", api_version, 0); + sm->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name); + + if (sm->msg_id_base != (u16) ~0) + vat_api_hookup (vam); + + vec_free(name); + + return 0; +} diff --git a/plugins/acl-plugin/acl/l2sess.c b/plugins/acl-plugin/acl/l2sess.c new file mode 100644 index 00000000..cc9bde44 --- /dev/null +++ b/plugins/acl-plugin/acl/l2sess.c @@ -0,0 +1,243 @@ +/* + * 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. + */ +/* + *------------------------------------------------------------------ + * l2sess.c - simple MAC-swap API / debug CLI handling + *------------------------------------------------------------------ + */ + +#include <vnet/vnet.h> +#include <vnet/plugin/plugin.h> +#include <acl/l2sess.h> + +#include <vlibapi/api.h> +#include <vlibmemory/api.h> +#include <vlibsocket/api.h> +#include <vppinfra/timing_wheel.h> + +#include <vnet/l2/l2_output.h> +#include <vnet/l2/l2_input.h> + +void +l2sess_vlib_plugin_register (vlib_main_t * vm, void* hh, + int from_early_init) +{ + l2sess_main_t *sm = &l2sess_main; + vnet_plugin_handoff_t * h = hh; + memset (sm, 0, sizeof (*sm)); + + sm->vlib_main = vm; + sm->vnet_main = h->vnet_main; + sm->ethernet_main = h->ethernet_main; +} + +void +l2sess_init_next_features_input (vlib_main_t * vm, l2sess_main_t * sm) +{ +#define _(node_name, node_var, is_out, is_ip6, is_track) \ + if (!is_out) feat_bitmap_init_next_nodes(vm, node_var.index, L2INPUT_N_FEAT, l2input_get_feat_names (), sm->node_var ## _input_next_node_index); + foreach_l2sess_node +#undef _ +} + +void +l2sess_add_our_next_nodes (vlib_main_t * vm, l2sess_main_t * sm, + u8 * prev_node_name, int add_output_nodes) +{ + vlib_node_t *n; + n = vlib_get_node_by_name (vm, prev_node_name); +#define _(node_name, node_var, is_out, is_ip6, is_track) \ + if (is_out == add_output_nodes) { \ + u32 idx = vlib_node_add_next_with_slot(vm, n->index, node_var.index, ~0); \ + if (is_track) { \ + sm->next_slot_track_node_by_is_ip6_is_out[is_ip6][is_out] = idx; \ + } \ + } + foreach_l2sess_node +#undef _ +} + +void +l2sess_setup_nodes (void) +{ + vlib_main_t *vm = vlib_get_main (); + l2sess_main_t *sm = &l2sess_main; + + l2sess_init_next_features_input (vm, sm); + + l2sess_add_our_next_nodes (vm, sm, (u8 *) "l2-input-classify", 0); + l2sess_add_our_next_nodes (vm, sm, (u8 *) "l2-output-classify", 1); + +} + +static char * +get_l4_proto_str (int is_ip6, uint8_t l4_proto) +{ + switch (l4_proto) + { + case 6: + return "tcp"; + case 17: + return "udp"; + case 1: + return "icmp"; + case 58: + return "icmp6"; + default: + return "<?l4-unknown?>"; + } +} + +static clib_error_t * +l2sess_show_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + l2sess_main_t *sm = &l2sess_main; + clib_time_t *ct = &vm->clib_time; + l2s_session_t *s; + u64 now = clib_cpu_time_now (); + + vlib_cli_output (vm, "Timing wheel info: \n%U", format_timing_wheel, + &sm->timing_wheel, 255); + + pool_foreach (s, sm->sessions, ( + { + f64 ctime = + (now - + s->create_time) * ct->seconds_per_clock; + f64 atime0 = + (now - + s->side[0].active_time) * + ct->seconds_per_clock; + f64 atime1 = + (now - + s->side[1].active_time) * + ct->seconds_per_clock; +/* + f64 ctime = (s->create_time - vm->cpu_time_main_loop_start) * ct->seconds_per_clock; + f64 atime0 = (s->side[0].active_time - vm->cpu_time_main_loop_start) * ct->seconds_per_clock; + f64 atime1 = (s->side[1].active_time - vm->cpu_time_main_loop_start) * ct->seconds_per_clock; +*/ + u8 * out0 = + format (0, + "%5d: create time: %U pkts/bytes/active time: [ %ld %ld %U : %ld %ld %U ]\n", + (s - sm->sessions), + format_time_interval, "h:m:s:u", + ctime, s->side[0].n_packets, + s->side[0].n_bytes, + format_time_interval, "h:m:s:u", + atime0, s->side[1].n_packets, + s->side[1].n_bytes, + format_time_interval, "h:m:s:u", + atime1); u8 * out1 = 0; + if (s->is_ip6) + { + out1 = + format (0, "%s %U :%u <-> %U :%u", + get_l4_proto_str (s->is_ip6, + s->l4_proto), + format_ip6_address, + &s->side[0].addr.ip6, + s->side[0].port, + format_ip6_address, + &s->side[1].addr.ip6, + s->side[1].port);} + else + { + out1 = + format (0, "%s %U :%u <-> %U :%u", + get_l4_proto_str (s->is_ip6, + s->l4_proto), + format_ip4_address, + &s->side[0].addr.ip4, + s->side[0].port, + format_ip4_address, + &s->side[1].addr.ip4, + s->side[1].port);} + vlib_cli_output (vm, "%s %s", out0, + out1); vec_free (out0); + vec_free (out1);} + )); + return 0; +} + +static clib_error_t * +l2sess_show_count_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + l2sess_main_t *sm = &l2sess_main; + + vlib_cli_output (vm, "Timing wheel info: \n%U", format_timing_wheel, + &sm->timing_wheel, 255); + vlib_cli_output (vm, "session pool len: %d, pool elts: %d", + pool_len (sm->sessions), pool_elts (sm->sessions)); + vlib_cli_output (vm, + "attempted to delete sessions which were already free: %d", + sm->counter_attempted_delete_free_session); + return 0; +} + + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (l2sess_show_command, static) = { + .path = "show l2sess", + .short_help = "show l2sess", + .function = l2sess_show_command_fn, +}; + +VLIB_CLI_COMMAND (l2sess_show_count_command, static) = { + .path = "show l2sess count", + .short_help = "show l2sess count", + .function = l2sess_show_count_command_fn, +}; +/* *INDENT-OFF* */ + +static inline u64 +time_sec_to_clock( clib_time_t *ct, f64 sec) +{ + return (u64)(((f64)sec)/ct->seconds_per_clock); +} + +static clib_error_t * l2sess_init (vlib_main_t * vm) +{ + l2sess_main_t * sm = &l2sess_main; + clib_error_t * error = 0; + u64 cpu_time_now = clib_cpu_time_now(); + + + clib_time_t *ct = &vm->clib_time; + sm->udp_session_idle_timeout = time_sec_to_clock(ct, UDP_SESSION_IDLE_TIMEOUT_SEC); + sm->tcp_session_idle_timeout = time_sec_to_clock(ct, TCP_SESSION_IDLE_TIMEOUT_SEC); + sm->tcp_session_transient_timeout = time_sec_to_clock(ct, TCP_SESSION_TRANSIENT_TIMEOUT_SEC); + + /* The min sched time of 10e-1 causes erroneous behavior... */ + sm->timing_wheel.min_sched_time = 10e-2; + sm->timing_wheel.max_sched_time = 3600.0*48.0; + timing_wheel_init (&sm->timing_wheel, cpu_time_now, vm->clib_time.clocks_per_second); + sm->timer_wheel_next_expiring_time = 0; + sm->timer_wheel_tick = time_sec_to_clock(ct, sm->timing_wheel.min_sched_time); + /* Pre-allocate expired nodes. */ + vec_alloc (sm->data_from_advancing_timing_wheel, 32); + + l2sess_setup_nodes(); + l2output_init_output_node_vec (&sm->output_next_nodes.output_node_index_vec); + + return error; +} + +VLIB_INIT_FUNCTION (l2sess_init); + + diff --git a/plugins/acl-plugin/acl/l2sess.h b/plugins/acl-plugin/acl/l2sess.h new file mode 100644 index 00000000..db899917 --- /dev/null +++ b/plugins/acl-plugin/acl/l2sess.h @@ -0,0 +1,150 @@ +/* + * 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. + */ +#ifndef __included_l2sess_h__ +#define __included_l2sess_h__ + +#include <vnet/vnet.h> +#include <vnet/ip/ip.h> +#include <vnet/ethernet/ethernet.h> + +#include <vppinfra/hash.h> +#include <vppinfra/error.h> +#include <vppinfra/elog.h> +#include <vppinfra/timing_wheel.h> + +#include <vnet/l2/l2_output.h> +#include <vnet/l2/l2_input.h> + +#define _(node_name, node_var, is_out, is_ip6, is_track) +#undef _ +#define foreach_l2sess_node \ + _("aclp-l2s-input-ip4-add", l2sess_in_ip4_add, 0, 0, 0) \ + _("aclp-l2s-input-ip6-add", l2sess_in_ip6_add, 0, 1, 0) \ + _("aclp-l2s-output-ip4-add", l2sess_out_ip4_add, 1, 0, 0) \ + _("aclp-l2s-output-ip6-add", l2sess_out_ip6_add, 1, 1, 0) \ + _("aclp-l2s-input-ip4-track", l2sess_in_ip4_track, 0, 0, 1) \ + _("aclp-l2s-input-ip6-track", l2sess_in_ip6_track, 0, 1, 1) \ + _("aclp-l2s-output-ip4-track",l2sess_out_ip4_track, 1, 0, 1) \ + _("aclp-l2s-output-ip6-track", l2sess_out_ip6_track, 1, 1, 1) + +#define _(node_name, node_var, is_out, is_ip6, is_track) \ + extern vlib_node_registration_t node_var; +foreach_l2sess_node +#undef _ + +#define TCP_FLAG_FIN 0x01 +#define TCP_FLAG_SYN 0x02 +#define TCP_FLAG_RST 0x04 +#define TCP_FLAG_PUSH 0x08 +#define TCP_FLAG_ACK 0x10 +#define TCP_FLAG_URG 0x20 +#define TCP_FLAG_ECE 0x40 +#define TCP_FLAG_CWR 0x80 +#define TCP_FLAGS_RSTFINACKSYN (TCP_FLAG_RST + TCP_FLAG_FIN + TCP_FLAG_SYN + TCP_FLAG_ACK) +#define TCP_FLAGS_ACKSYN (TCP_FLAG_SYN + TCP_FLAG_ACK) + +typedef struct { + ip46_address_t addr; + u64 active_time; + u64 n_packets; + u64 n_bytes; + u16 port; +} l2s_session_side_t; + +enum { + L2S_SESSION_SIDE_IN = 0, + L2S_SESSION_SIDE_OUT, + L2S_N_SESSION_SIDES +}; + +typedef struct { + u64 create_time; + l2s_session_side_t side[L2S_N_SESSION_SIDES]; + u8 l4_proto; + u8 is_ip6; + u16 tcp_flags_seen; /* u16 because of two sides */ +} l2s_session_t; + +#define PROD +#ifdef PROD +#define UDP_SESSION_IDLE_TIMEOUT_SEC 600 +#define TCP_SESSION_IDLE_TIMEOUT_SEC (3600*24) +#define TCP_SESSION_TRANSIENT_TIMEOUT_SEC 120 +#else +#define UDP_SESSION_IDLE_TIMEOUT_SEC 15 +#define TCP_SESSION_IDLE_TIMEOUT_SEC 15 +#define TCP_SESSION_TRANSIENT_TIMEOUT_SEC 5 +#endif + +typedef struct { + /* + * the next two fields are present for all nodes, but + * only one of them is used per node - depending + * on whether the node is an input or output one. + */ +#define _(node_name, node_var, is_out, is_ip6, is_track) \ + u32 node_var ## _input_next_node_index[32]; \ + l2_output_next_nodes_st node_var ## _next_nodes; +foreach_l2sess_node +#undef _ + l2_output_next_nodes_st output_next_nodes; + + /* Next indices of the tracker nodes */ + u32 next_slot_track_node_by_is_ip6_is_out[2][2]; + + /* + * Pairing of "forward" and "reverse" tables by table index. + * Each relationship has two entries - for one and the other table, + * so it is bidirectional. + */ + + u32 *fwd_to_rev_by_table_index; + + /* + * The vector of per-interface session pools + */ + + l2s_session_t *sessions; + + /* The session timeouts */ + u64 tcp_session_transient_timeout; + u64 tcp_session_idle_timeout; + u64 udp_session_idle_timeout; + + /* Timing wheel to time out the idle sessions */ + timing_wheel_t timing_wheel; + u32 *data_from_advancing_timing_wheel; + u64 timer_wheel_next_expiring_time; + u64 timer_wheel_tick; + + /* convenience */ + vlib_main_t * vlib_main; + vnet_main_t * vnet_main; + ethernet_main_t * ethernet_main; + + /* Counter(s) */ + u64 counter_attempted_delete_free_session; +} l2sess_main_t; + +l2sess_main_t l2sess_main; + +/* Just exposed for acl.c */ + +void +l2sess_vlib_plugin_register (vlib_main_t * vm, void * hh, + int from_early_init); + + +#endif /* __included_l2sess_h__ */ diff --git a/plugins/acl-plugin/acl/l2sess_node.c b/plugins/acl-plugin/acl/l2sess_node.c new file mode 100644 index 00000000..3e735f47 --- /dev/null +++ b/plugins/acl-plugin/acl/l2sess_node.c @@ -0,0 +1,813 @@ +/* + * 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 <netinet/in.h> +#include <vlib/vlib.h> +#include <vnet/vnet.h> +#include <vnet/pg/pg.h> +#include <vppinfra/error.h> +#include <acl/l2sess.h> +#include <vnet/l2/l2_classify.h> + + +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u32 trace_flags; + u32 session_tables[2]; + u32 session_nexts[2]; + u8 l4_proto; +} l2sess_trace_t; + +/* packet trace format function */ + +#define _(node_name, node_var, is_out, is_ip6, is_track) \ +static u8 * format_## node_var ##_trace (u8 * s, va_list * args) \ +{ \ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); \ + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); \ + l2sess_trace_t * t = va_arg (*args, l2sess_trace_t *); \ + \ + s = format (s, node_name ": sw_if_index %d, next index %d trace_flags %08x L4 proto %d\n" \ + " tables [ %d, %d ] nexts [ %d, %d ]", \ + t->sw_if_index, t->next_index, t->trace_flags, t->l4_proto, \ + t->session_tables[0], t->session_tables[1], \ + t->session_nexts[0], t->session_nexts[1]); \ + return s; \ +} +foreach_l2sess_node +#undef _ +#define foreach_l2sess_error \ +_(SWAPPED, "Mac swap packets processed") + typedef enum +{ +#define _(sym,str) L2SESS_ERROR_##sym, + foreach_l2sess_error +#undef _ + L2SESS_N_ERROR, +} l2sess_error_t; + +static char *l2sess_error_strings[] = { +#define _(sym,string) string, + foreach_l2sess_error +#undef _ +}; + +typedef enum +{ + L2SESS_NEXT_DROP, + L2SESS_N_NEXT, +} l2sess_next_t; + +u8 +l2sess_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; +} + + +u8 +l2sess_get_tcp_flags (vlib_buffer_t * b0, int node_is_ip6) +{ + u8 flags; + int flags_offset; + if (node_is_ip6) + { + flags_offset = 14 + 40 + 13; /* FIXME: no extension headers assumed */ + } + else + { + flags_offset = 14 + 20 + 13; + } + flags = *((u8 *) vlib_buffer_get_current (b0) + flags_offset); + return flags; +} + +static inline int +l4_tcp_or_udp (u8 proto) +{ + return ((proto == 6) || (proto == 17)); +} + +void +l2sess_get_session_tables (l2sess_main_t * sm, u32 sw_if_index, + int node_is_out, int node_is_ip6, u8 l4_proto, + u32 * session_tables) +{ +/* + * Based on the direction, l3 and l4 protocol, fill a u32[2] array: + * [0] is index for the "direct match" path, [1] is for "mirrored match". + * Store the indices of the tables to add the session to in session_tables[] + */ + l2_output_classify_main_t *l2om = &l2_output_classify_main; + l2_input_classify_main_t *l2im = &l2_input_classify_main; + + u32 output_table_index; + u32 input_table_index; + + if (!l4_tcp_or_udp (l4_proto)) + { + return; + } + + if (node_is_ip6) + { + vec_validate_init_empty (l2im-> + classify_table_index_by_sw_if_index + [L2_INPUT_CLASSIFY_TABLE_IP6], sw_if_index, + ~0); + input_table_index = + l2im-> + classify_table_index_by_sw_if_index[L2_INPUT_CLASSIFY_TABLE_IP6] + [sw_if_index]; + vec_validate_init_empty (l2om-> + classify_table_index_by_sw_if_index + [L2_OUTPUT_CLASSIFY_TABLE_IP6], sw_if_index, + ~0); + output_table_index = + l2om-> + classify_table_index_by_sw_if_index[L2_OUTPUT_CLASSIFY_TABLE_IP6] + [sw_if_index]; + } + else + { + vec_validate_init_empty (l2im-> + classify_table_index_by_sw_if_index + [L2_INPUT_CLASSIFY_TABLE_IP4], sw_if_index, + ~0); + input_table_index = + l2im-> + classify_table_index_by_sw_if_index[L2_INPUT_CLASSIFY_TABLE_IP4] + [sw_if_index]; + vec_validate_init_empty (l2om-> + classify_table_index_by_sw_if_index + [L2_OUTPUT_CLASSIFY_TABLE_IP4], sw_if_index, + ~0); + output_table_index = + l2om-> + classify_table_index_by_sw_if_index[L2_OUTPUT_CLASSIFY_TABLE_IP4] + [sw_if_index]; + } + + if (node_is_out) + { + session_tables[0] = output_table_index; + session_tables[1] = input_table_index; + } + else + { + session_tables[0] = input_table_index; + session_tables[1] = output_table_index; + } +} + +void +l2sess_get_session_nexts (l2sess_main_t * sm, u32 sw_if_index, + int node_is_out, int node_is_ip6, u8 l4_proto, + u32 * session_nexts) +{ +/* + * Based on the direction, l3 and l4 protocol, fill a u32[2] array: + * [0] is the index for the "direct match" path, [1] is for "mirrored match". + * Store the match_next_index in session_nexts[] for a new session entry which is being added to session tables. + */ + u32 input_node_index; + u32 output_node_index; + + if (!l4_tcp_or_udp (l4_proto)) + { + return; + } + + input_node_index = + sm->next_slot_track_node_by_is_ip6_is_out[node_is_ip6][0]; + output_node_index = + sm->next_slot_track_node_by_is_ip6_is_out[node_is_ip6][1]; + + if (node_is_out) + { + session_nexts[0] = output_node_index; + session_nexts[1] = input_node_index; + } + else + { + session_nexts[0] = input_node_index; + session_nexts[1] = output_node_index; + } +} + + +static inline void +swap_bytes (vlib_buffer_t * b0, int off_a, int off_b, int nbytes) +{ + u8 tmp; + u8 *pa = vlib_buffer_get_current (b0) + off_a; + u8 *pb = vlib_buffer_get_current (b0) + off_b; + while (nbytes--) + { + tmp = *pa; + *pa++ = *pb; + *pb++ = tmp; + } +} + +/* + * This quite pro[bv]ably is a terrible idea performance wise. Moreso doing it twice. + * Would having a long (ish) chunk of memory work better for this ? + * We will see when we get to the performance of this. + */ +void +l2sess_flip_l3l4_fields (vlib_buffer_t * b0, int node_is_ip6, u8 l4_proto) +{ + if (!l4_tcp_or_udp (l4_proto)) + { + return; + } + if (node_is_ip6) + { + swap_bytes (b0, 22, 38, 16); /* L3 */ + swap_bytes (b0, 54, 56, 2); /* L4 (when no EH!) */ + } + else + { + swap_bytes (b0, 26, 30, 4); /* L3 */ + swap_bytes (b0, 34, 36, 2); /* L4 */ + } +} + +void +l2sess_add_session (vlib_buffer_t * b0, int node_is_out, int node_is_ip6, + u32 session_table, u32 session_match_next, + u32 opaque_index) +{ + vnet_classify_main_t *cm = &vnet_classify_main; + u32 action = 0; + u32 metadata = 0; + +#ifdef DEBUG_SESSIONS + printf ("Adding session to table %d with next %d\n", session_table, + session_match_next); +#endif + vnet_classify_add_del_session (cm, session_table, + vlib_buffer_get_current (b0), + session_match_next, opaque_index, 0, action, + metadata, 1); +} + + + +static void * +get_ptr_to_offset (vlib_buffer_t * b0, int offset) +{ + u8 *p = vlib_buffer_get_current (b0) + offset; + return p; +} + + +/* + * FIXME: Hardcoded offsets are ugly, although if casting to structs one + * would need to take care about alignment.. So let's for now be naive and simple. + */ + +void +session_store_ip4_l3l4_info (vlib_buffer_t * b0, l2s_session_t * sess, + int node_is_out) +{ + clib_memcpy (&sess->side[1 - node_is_out].addr.ip4, + get_ptr_to_offset (b0, 26), 4); + clib_memcpy (&sess->side[node_is_out].addr.ip4, get_ptr_to_offset (b0, 30), + 4); + sess->side[1 - node_is_out].port = + ntohs (*(u16 *) get_ptr_to_offset (b0, 34)); + sess->side[node_is_out].port = ntohs (*(u16 *) get_ptr_to_offset (b0, 36)); +} + +void +session_store_ip6_l3l4_info (vlib_buffer_t * b0, l2s_session_t * sess, + int node_is_out) +{ + clib_memcpy (&sess->side[1 - node_is_out].addr.ip6, + get_ptr_to_offset (b0, 22), 16); + clib_memcpy (&sess->side[node_is_out].addr.ip4, get_ptr_to_offset (b0, 38), + 16); + sess->side[1 - node_is_out].port = + ntohs (*(u16 *) get_ptr_to_offset (b0, 54)); + sess->side[node_is_out].port = ntohs (*(u16 *) get_ptr_to_offset (b0, 56)); +} + +static void +build_match_from_session (l2sess_main_t * sm, u8 * match, + l2s_session_t * sess, int is_out) +{ + if (sess->is_ip6) + { + match[20] = sess->l4_proto; + clib_memcpy (&match[22], &sess->side[1 - is_out].addr.ip6, 16); + clib_memcpy (&match[38], &sess->side[is_out].addr.ip4, 16); + *(u16 *) & match[54] = htons (sess->side[1 - is_out].port); + *(u16 *) & match[56] = htons (sess->side[is_out].port); + } + else + { + match[23] = sess->l4_proto; + clib_memcpy (&match[26], &sess->side[1 - is_out].addr.ip6, 4); + clib_memcpy (&match[30], &sess->side[is_out].addr.ip4, 4); + *(u16 *) & match[34] = htons (sess->side[1 - is_out].port); + *(u16 *) & match[36] = htons (sess->side[is_out].port); + } +} + +static void +delete_session (l2sess_main_t * sm, u32 sw_if_index, u32 session_index) +{ + vnet_classify_main_t *cm = &vnet_classify_main; + u8 match[5 * 16]; /* For building the mock of the packet to delete the classifier session */ + u32 session_tables[2] = { ~0, ~0 }; + l2s_session_t *sess = sm->sessions + session_index; + if (pool_is_free (sm->sessions, sess)) + { + sm->counter_attempted_delete_free_session++; + return; + } + l2sess_get_session_tables (sm, sw_if_index, 0, sess->is_ip6, sess->l4_proto, + session_tables); + if (session_tables[1] != ~0) + { + build_match_from_session (sm, match, sess, 1); + vnet_classify_add_del_session (cm, session_tables[1], match, 0, 0, 0, 0, + 0, 0); + } + if (session_tables[1] != ~0) + { + build_match_from_session (sm, match, sess, 1); + vnet_classify_add_del_session (cm, session_tables[1], match, 0, 0, 0, 0, + 0, 0); + } + pool_put (sm->sessions, sess); +} + +static void +udp_session_account_buffer (vlib_buffer_t * b0, l2s_session_t * s, + int which_side, u64 now) +{ + l2s_session_side_t *ss = &s->side[which_side]; + ss->active_time = now; + ss->n_packets++; + ss->n_bytes += b0->current_data + b0->current_length; +} + +static inline u64 +udp_session_get_timeout (l2sess_main_t * sm, l2s_session_t * sess, u64 now) +{ + return (sm->udp_session_idle_timeout); +} + +static void +tcp_session_account_buffer (vlib_buffer_t * b0, l2s_session_t * s, + int which_side, u64 now) +{ + l2s_session_side_t *ss = &s->side[which_side]; + ss->active_time = now; + ss->n_packets++; + ss->n_bytes += b0->current_data + b0->current_length; + /* Very very lightweight TCP state tracking: just record which flags were seen */ + s->tcp_flags_seen |= + l2sess_get_tcp_flags (b0, s->is_ip6) << (8 * which_side); +} + +/* + * Since we are tracking for the purposes of timing the sessions out, + * we mostly care about two states: established (maximize the idle timeouts) + * and transient (halfopen/halfclosed/reset) - we need to have a reasonably short timeout to + * quickly get rid of sessions but not short enough to violate the TCP specs. + */ + +static inline u64 +tcp_session_get_timeout (l2sess_main_t * sm, l2s_session_t * sess, u64 now) +{ + /* seen both SYNs and ACKs but not FINs means we are in establshed state */ + u16 masked_flags = + sess->tcp_flags_seen & ((TCP_FLAGS_RSTFINACKSYN << 8) + + TCP_FLAGS_RSTFINACKSYN); + if (((TCP_FLAGS_ACKSYN << 8) + TCP_FLAGS_ACKSYN) == masked_flags) + { + return (sm->tcp_session_idle_timeout); + } + else + { + return (sm->tcp_session_transient_timeout); + } +} + +static inline u64 +session_get_timeout (l2sess_main_t * sm, l2s_session_t * sess, u64 now) +{ + u64 timeout; + + switch (sess->l4_proto) + { + case 6: + timeout = tcp_session_get_timeout (sm, sess, now); + break; + case 17: + timeout = udp_session_get_timeout (sm, sess, now); + break; + default: + timeout = 0; + } + + return timeout; +} + +static inline u64 +get_session_last_active_time(l2s_session_t * sess) +{ + u64 last_active = + sess->side[0].active_time > + sess->side[1].active_time ? sess->side[0].active_time : sess->side[1]. + active_time; + return last_active; +} + +static int +session_is_alive (l2sess_main_t * sm, l2s_session_t * sess, u64 now, u64 *last_active_cache) +{ + u64 last_active = get_session_last_active_time(sess); + u64 timeout = session_get_timeout (sm, sess, now); + int is_alive = ((now - last_active) < timeout); + if (last_active_cache) + *last_active_cache = last_active; + return is_alive; +} + +void +check_idle_sessions (l2sess_main_t * sm, u32 sw_if_index, u64 now) +{ + sm->timer_wheel_next_expiring_time = 0; + sm->data_from_advancing_timing_wheel + = + timing_wheel_advance (&sm->timing_wheel, now, + sm->data_from_advancing_timing_wheel, + &sm->timer_wheel_next_expiring_time); +#ifdef DEBUG_SESSIONS_VERBOSE + { + clib_time_t *ct = &sm->vlib_main->clib_time; + f64 ctime; + ctime = now * ct->seconds_per_clock; + clib_warning ("Now : %U", format_time_interval, "h:m:s:u", ctime); + ctime = sm->timer_wheel_next_expiring_time * ct->seconds_per_clock; + clib_warning ("Next expire: %U", format_time_interval, "h:m:s:u", ctime); + clib_warning ("Expired items: %d", + (int) vec_len (sm->data_from_advancing_timing_wheel)); + } +#endif + + sm->timer_wheel_next_expiring_time = now + sm->timer_wheel_tick; + + if (PREDICT_FALSE (_vec_len (sm->data_from_advancing_timing_wheel) > 0)) + { + uword i; + for (i = 0; i < vec_len (sm->data_from_advancing_timing_wheel); i++) + { + u32 session_index = sm->data_from_advancing_timing_wheel[i]; + if (!pool_is_free_index (sm->sessions, session_index)) + { + l2s_session_t *sess = sm->sessions + session_index; + u64 last_active; + if (session_is_alive (sm, sess, now, &last_active)) + { +#ifdef DEBUG_SESSIONS + clib_warning ("Restarting timer for session %d", (int) session_index); +#endif + /* Pretend we did this in the past, at last_active moment */ + timing_wheel_insert (&sm->timing_wheel, + last_active + session_get_timeout (sm, sess, + last_active), + session_index); + } + else + { +#ifdef DEBUG_SESSIONS + clib_warning ("Deleting session %d", (int) session_index); +#endif + delete_session (sm, sw_if_index, session_index); + } + } + } + _vec_len (sm->data_from_advancing_timing_wheel) = 0; + } +} + +static uword +l2sess_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + l2sess_next_t next_index; + u32 pkts_swapped = 0; + u32 cached_sw_if_index = (u32) ~ 0; + u32 cached_next_index = (u32) ~ 0; + u32 feature_bitmap0; + u32 trace_flags0; + + l2sess_main_t *sm = &l2sess_main; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* Only a single loop for now for simplicity */ + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0 = L2SESS_NEXT_DROP; + u32 sw_if_index0; + //ethernet_header_t *en0; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + //en0 = vlib_buffer_get_current (b0); + +/* + * The non-boilerplate is in the block below. + * Note first a magic macro block that sets up the behavior qualifiers: + * node_is_out : 1 = is output, 0 = is input + * node_is_ip6 : 1 = is ip6, 0 = is ip4 + * node_is_track : 1 = is a state tracking node, 0 - is a session addition node + * + * Subsequently the code adjusts its behavior depending on these variables. + * It's most probably not great performance wise but much easier to work with. + * + */ + { + int node_is_out = -1; + CLIB_UNUSED (int node_is_ip6) = -1; + CLIB_UNUSED (int node_is_track) = -1; + u32 node_index = 0; + u32 session_tables[2] = { ~0, ~0 }; + u32 session_nexts[2] = { ~0, ~0 }; + l2_output_next_nodes_st *next_nodes = 0; + u32 *input_feat_next_node_index; + u8 l4_proto; + u64 now = clib_cpu_time_now (); + +/* + * Set the variables according to which of the 8 nodes we are. + * Hopefully the compiler is smart enough to eliminate the extraneous. + */ +#define _(node_name, node_var, is_out, is_ip6, is_track) \ +if(node_var.index == node->node_index) \ + { \ + node_is_out = is_out; \ + node_is_ip6 = is_ip6; \ + node_is_track = is_track; \ + node_index = node_var.index; \ + next_nodes = &sm->node_var ## _next_nodes; \ + input_feat_next_node_index = sm->node_var ## _input_next_node_index; \ + } + foreach_l2sess_node +#undef _ + trace_flags0 = 0; + if (node_is_out) + { + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX]; + } + else + { + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + } + /* potentially also remove the nodes here */ + feature_bitmap0 = vnet_buffer (b0)->l2.feature_bitmap; + + if (node_is_track) + { + u32 sess_index = vnet_buffer (b0)->l2_classify.opaque_index; + l2s_session_t *sess = sm->sessions + sess_index; + l4_proto = sess->l4_proto; + + if (session_is_alive (sm, sess, now, 0)) + { + if (6 == l4_proto) + { + tcp_session_account_buffer (b0, sess, node_is_out, + now); + } + else + { + udp_session_account_buffer (b0, sess, node_is_out, + now); + } + } + else + { + timing_wheel_delete (&sm->timing_wheel, sess_index); + delete_session (sm, sw_if_index0, sess_index); + /* FIXME: drop the packet that hit the obsolete node, for now. We really ought to recycle it. */ + next0 = 0; + } + } + else + { + /* + * "-add" node: take l2opaque which arrived to us, and deduce + * the tables out of that. ~0 means the topmost classifier table + * applied for this AF on the RX(for input)/TX(for output)) sw_if_index. + * Also add the mirrored session to the paired table. + */ + l2s_session_t *sess; + u32 sess_index; + + l4_proto = l2sess_get_l4_proto (b0, node_is_ip6); + + pool_get (sm->sessions, sess); + sess_index = sess - sm->sessions; + sess->create_time = now; + sess->side[node_is_out].active_time = now; + sess->side[1 - node_is_out].active_time = now; + sess->l4_proto = l4_proto; + sess->is_ip6 = node_is_ip6; + if (node_is_ip6) + { + session_store_ip6_l3l4_info (b0, sess, node_is_out); + } + else + { + session_store_ip4_l3l4_info (b0, sess, node_is_out); + } + + l2sess_get_session_tables (sm, sw_if_index0, node_is_out, + node_is_ip6, l4_proto, + session_tables); + l2sess_get_session_nexts (sm, sw_if_index0, node_is_out, + node_is_ip6, l4_proto, + session_nexts); + l2sess_flip_l3l4_fields (b0, node_is_ip6, l4_proto); + if (session_tables[1] != ~0) + { + l2sess_add_session (b0, node_is_out, node_is_ip6, + session_tables[1], session_nexts[1], + sess_index); + } + l2sess_flip_l3l4_fields (b0, node_is_ip6, l4_proto); + if (session_tables[0] != ~0) + { + l2sess_add_session (b0, node_is_out, node_is_ip6, + session_tables[0], session_nexts[0], + sess_index); + } + if (6 == sess->l4_proto) + { + tcp_session_account_buffer (b0, sess, node_is_out, now); + } + else + { + udp_session_account_buffer (b0, sess, node_is_out, now); + } + timing_wheel_insert (&sm->timing_wheel, + now + session_get_timeout (sm, sess, + now), + sess_index); + } + + if (now >= sm->timer_wheel_next_expiring_time) + { + check_idle_sessions (sm, sw_if_index0, now); + } + + if (node_is_out) + { + if (feature_bitmap0) + { + trace_flags0 |= 0x10; + } + if (sw_if_index0 == cached_sw_if_index) + { + trace_flags0 |= 0x20; + } + l2_output_dispatch (sm->vlib_main, + sm->vnet_main, + node, + node_index, + &cached_sw_if_index, + &cached_next_index, + next_nodes, + b0, sw_if_index0, feature_bitmap0, + &next0); + trace_flags0 |= 2; + + } + else + { + next0 = + feat_bitmap_get_next_node_index (input_feat_next_node_index, + feature_bitmap0); + trace_flags0 |= 4; + + } + + + + if (next0 >= node->n_next_nodes) + { + trace_flags0 |= 1; + } + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + l2sess_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next0; + t->trace_flags = trace_flags0; + t->l4_proto = l4_proto; + t->session_tables[0] = session_tables[0]; + t->session_tables[1] = session_tables[1]; + t->session_nexts[0] = session_nexts[0]; + t->session_nexts[1] = session_nexts[1]; + } + + } + pkts_swapped += 1; + if (next0 >= node->n_next_nodes) + { + next0 = 0; + } + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + vlib_node_increment_counter (vm, node->node_index, + L2SESS_ERROR_SWAPPED, pkts_swapped); + return frame->n_vectors; +} + + +#define _(node_name, node_var, is_out, is_ip6, is_track) \ +static uword \ +node_var ## node_fn (vlib_main_t * vm, \ + vlib_node_runtime_t * node, \ + vlib_frame_t * frame) \ +{ \ + return l2sess_node_fn(vm, node, frame); \ +} \ +VLIB_REGISTER_NODE (node_var) = { \ + .function = node_var ## node_fn, \ + .name = node_name, \ + .vector_size = sizeof (u32), \ + .format_trace = format_ ## node_var ## _trace, \ + .type = VLIB_NODE_TYPE_INTERNAL, \ + \ + .n_errors = ARRAY_LEN(l2sess_error_strings), \ + .error_strings = l2sess_error_strings, \ + \ + .n_next_nodes = L2SESS_N_NEXT, \ + .next_nodes = { \ + [L2SESS_NEXT_DROP] = "error-drop", \ + }, \ +}; +foreach_l2sess_node +#undef _ diff --git a/plugins/acl-plugin/acl/node_in.c b/plugins/acl-plugin/acl/node_in.c new file mode 100644 index 00000000..2a5199a9 --- /dev/null +++ b/plugins/acl-plugin/acl/node_in.c @@ -0,0 +1,168 @@ +/* + * 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 <vlib/vlib.h> +#include <vnet/vnet.h> +#include <vnet/pg/pg.h> +#include <vppinfra/error.h> +#include <acl/acl.h> +#include "node_in.h" + +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u32 match_acl_index; + u32 match_rule_index; + u32 trace_bitmap; +} acl_in_trace_t; + +/* packet trace format function */ +static u8 * +format_acl_in_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + acl_in_trace_t *t = va_arg (*args, acl_in_trace_t *); + + s = + format (s, + "ACL_IN: sw_if_index %d, next index %d, match: inacl %d rule %d trace_bits %08x", + t->sw_if_index, t->next_index, t->match_acl_index, + t->match_rule_index, t->trace_bitmap); + return s; +} + +vlib_node_registration_t acl_in_node; + +#define foreach_acl_in_error \ +_(ACL_CHECK, "InACL check packets processed") + +typedef enum +{ +#define _(sym,str) ACL_IN_ERROR_##sym, + foreach_acl_in_error +#undef _ + ACL_IN_N_ERROR, +} acl_in_error_t; + +static char *acl_in_error_strings[] = { +#define _(sym,string) string, + foreach_acl_in_error +#undef _ +}; + +static uword +acl_in_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + acl_in_next_t next_index; + u32 pkts_acl_checked = 0; + u32 feature_bitmap0; + u32 trace_bitmap = 0; + u32 *input_feat_next_node_index = + acl_main.acl_in_node_input_next_node_index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0 = ~0; + u32 sw_if_index0; + u32 next = ~0; + u32 match_acl_index = ~0; + u32 match_rule_index = ~0; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + feature_bitmap0 = vnet_buffer (b0)->l2.feature_bitmap; + + input_acl_packet_match (sw_if_index0, b0, &next, &match_acl_index, + &match_rule_index, &trace_bitmap); + if (next != ~0) + { + next0 = next; + } + if (next0 == ~0) + { + next0 = + feat_bitmap_get_next_node_index (input_feat_next_node_index, + feature_bitmap0); + } + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + acl_in_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next0; + t->match_acl_index = match_acl_index; + t->match_rule_index = match_rule_index; + t->trace_bitmap = trace_bitmap; + } + + next0 = next0 < node->n_next_nodes ? next0 : 0; + + pkts_acl_checked += 1; + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, acl_in_node.index, + ACL_IN_ERROR_ACL_CHECK, pkts_acl_checked); + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (acl_in_node) = +{ + .function = acl_in_node_fn,.name = "acl-plugin-in",.vector_size = + sizeof (u32),.format_trace = format_acl_in_trace,.type = + VLIB_NODE_TYPE_INTERNAL,.n_errors = + ARRAY_LEN (acl_in_error_strings),.error_strings = + acl_in_error_strings,.n_next_nodes = ACL_IN_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = + { + [ACL_IN_ERROR_DROP] = "error-drop", + [ACL_IN_ETHERNET_INPUT] = "ethernet-input", + [ACL_IN_L2S_INPUT_IP4_ADD] = "aclp-l2s-input-ip4-add", + [ACL_IN_L2S_INPUT_IP6_ADD] = "aclp-l2s-input-ip6-add",} +,}; diff --git a/plugins/acl-plugin/acl/node_in.h b/plugins/acl-plugin/acl/node_in.h new file mode 100644 index 00000000..502bbf8d --- /dev/null +++ b/plugins/acl-plugin/acl/node_in.h @@ -0,0 +1,12 @@ +#ifndef _NODE_IN_H_ +#define _NODE_IN_H_ + +typedef enum { + ACL_IN_ERROR_DROP, + ACL_IN_ETHERNET_INPUT, + ACL_IN_L2S_INPUT_IP4_ADD, + ACL_IN_L2S_INPUT_IP6_ADD, + ACL_IN_N_NEXT, +} acl_in_next_t; + +#endif diff --git a/plugins/acl-plugin/acl/node_out.c b/plugins/acl-plugin/acl/node_out.c new file mode 100644 index 00000000..50af3679 --- /dev/null +++ b/plugins/acl-plugin/acl/node_out.c @@ -0,0 +1,175 @@ +/* + * 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 <vlib/vlib.h> +#include <vnet/vnet.h> +#include <vnet/pg/pg.h> +#include <vppinfra/error.h> +#include <acl/acl.h> + +#include "node_out.h" + +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u32 match_acl_index; + u32 match_rule_index; + u32 trace_bitmap; +} acl_out_trace_t; + +/* packet trace format function */ +static u8 * +format_acl_out_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + acl_out_trace_t *t = va_arg (*args, acl_out_trace_t *); + s = + format (s, + "ACL_OUT: sw_if_index %d, next index %d, match: outacl %d rule %d trace_bits %08x", + t->sw_if_index, t->next_index, t->match_acl_index, + t->match_rule_index, t->trace_bitmap); + return s; +} + +vlib_node_registration_t acl_out_node; + +#define foreach_acl_out_error \ +_(ACL_CHECK, "OutACL check packets processed") + +typedef enum +{ +#define _(sym,str) ACL_OUT_ERROR_##sym, + foreach_acl_out_error +#undef _ + ACL_OUT_N_ERROR, +} acl_out_error_t; + +static char *acl_out_error_strings[] = { +#define _(sym,string) string, + foreach_acl_out_error +#undef _ +}; + +static uword +acl_out_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + acl_main_t *am = &acl_main; + l2_output_next_nodes_st *next_nodes = &am->acl_out_output_next_nodes; + u32 n_left_from, *from, *to_next; + acl_out_next_t next_index; + u32 pkts_acl_checked = 0; + u32 feature_bitmap0; + u32 cached_sw_if_index = (u32) ~ 0; + u32 cached_next_index = (u32) ~ 0; + u32 match_acl_index = ~0; + u32 match_rule_index = ~0; + u32 trace_bitmap = 0; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0 = ~0; + u32 next = 0; + u32 sw_if_index0; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX]; + feature_bitmap0 = vnet_buffer (b0)->l2.feature_bitmap; + + output_acl_packet_match (sw_if_index0, b0, &next, &match_acl_index, + &match_rule_index, &trace_bitmap); + if (next != ~0) + { + next0 = next; + } + if (next0 == ~0) + { + l2_output_dispatch (vm, + am->vnet_main, + node, + acl_out_node.index, + &cached_sw_if_index, + &cached_next_index, + next_nodes, + b0, sw_if_index0, feature_bitmap0, &next0); + } + + + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + acl_out_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next0; + t->match_acl_index = match_acl_index; + t->match_rule_index = match_rule_index; + t->trace_bitmap = trace_bitmap; + } + + pkts_acl_checked += 1; + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, acl_out_node.index, + ACL_OUT_ERROR_ACL_CHECK, pkts_acl_checked); + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (acl_out_node) = +{ + .function = acl_out_node_fn,.name = "acl-plugin-out",.vector_size = + sizeof (u32),.format_trace = format_acl_out_trace,.type = + VLIB_NODE_TYPE_INTERNAL,.n_errors = + ARRAY_LEN (acl_out_error_strings),.error_strings = + acl_out_error_strings,.n_next_nodes = ACL_OUT_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = + { + [ACL_OUT_ERROR_DROP] = "error-drop", + [ACL_OUT_INTERFACE_OUTPUT] = "interface-output", + [ACL_OUT_L2S_OUTPUT_IP4_ADD] = "aclp-l2s-output-ip4-add", + [ACL_OUT_L2S_OUTPUT_IP6_ADD] = "aclp-l2s-output-ip6-add",} +,}; diff --git a/plugins/acl-plugin/acl/node_out.h b/plugins/acl-plugin/acl/node_out.h new file mode 100644 index 00000000..c919f3b7 --- /dev/null +++ b/plugins/acl-plugin/acl/node_out.h @@ -0,0 +1,12 @@ +#ifndef _NODE_OUT_H_ +#define _NODE_OUT_H_ + +typedef enum { + ACL_OUT_ERROR_DROP, + ACL_OUT_INTERFACE_OUTPUT, + ACL_OUT_L2S_OUTPUT_IP4_ADD, + ACL_OUT_L2S_OUTPUT_IP6_ADD, + ACL_OUT_N_NEXT, +} acl_out_next_t; + +#endif diff --git a/plugins/acl-plugin/configure.ac b/plugins/acl-plugin/configure.ac new file mode 100644 index 00000000..a4622c2d --- /dev/null +++ b/plugins/acl-plugin/configure.ac @@ -0,0 +1,9 @@ +AC_INIT(acl_plugin, 1.0) +LT_INIT +AM_INIT_AUTOMAKE +AM_SILENT_RULES([yes]) +AC_PREFIX_DEFAULT([/usr]) + +AC_PROG_CC + +AC_OUTPUT([Makefile]) diff --git a/plugins/acl-plugin/test/run-python b/plugins/acl-plugin/test/run-python new file mode 100755 index 00000000..215eb17a --- /dev/null +++ b/plugins/acl-plugin/test/run-python @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Do all the legwork to run a scapy shell with APIs available for load +# +CURR_DIR=`pwd` +ROOT_DIR=`git rev-parse --show-toplevel` +cd $ROOT_DIR +sudo apt-get install -y python-virtualenv +# uncomment the line below to enable build of plugins and api each time +# make plugins && make build-vpp-api || exit +virtualenv virtualenv +virtualenv/bin/pip install ipaddress +virtualenv/bin/pip install scapy +# install the python API into the virtualenv +cd $ROOT_DIR/vpp-api/python/ +$ROOT_DIR/virtualenv/bin/python setup.py install +# install the python ACL plugin API into the virtualenv +ACL_PLUGIN_SETUP_DIR=`find $ROOT_DIR/build-root -name acl-plugin` +cd $ACL_PLUGIN_SETUP_DIR; +$ROOT_DIR/virtualenv/bin/python setup.py install +cd $ROOT_DIR +# figure out the shared library path and start scapy +export LD_LIBRARY_PATH=`pwd`/`find . -name "libpneum.so" -exec dirname {} \; | grep lib64 | head -n 1` +cd $CURR_DIR +sudo LD_LIBRARY_PATH=$LD_LIBRARY_PATH $ROOT_DIR/virtualenv/bin/python $1 $2 $3 $4 $5 $6 $7 $8 $9 + + + diff --git a/plugins/acl-plugin/test/run-scapy b/plugins/acl-plugin/test/run-scapy new file mode 100755 index 00000000..266f07d1 --- /dev/null +++ b/plugins/acl-plugin/test/run-scapy @@ -0,0 +1,26 @@ +#!/bin/sh +# +# Do all the legwork to run a scapy shell with APIs available for load +# +ROOT_DIR=`git rev-parse --show-toplevel` +cd $ROOT_DIR +sudo apt-get install -y python-virtualenv +# uncomment the line below to enable the build of plugins and API each time.. +# make plugins && make build-vpp-api || exit +virtualenv virtualenv +virtualenv/bin/pip install ipaddress +virtualenv/bin/pip install scapy +# install the python API into the virtualenv +cd $ROOT_DIR/vpp-api/python/ +$ROOT_DIR/virtualenv/bin/python setup.py install +# install the python ACL plugin API into the virtualenv +ACL_PLUGIN_SETUP_DIR=`find $ROOT_DIR/build-root -name acl-plugin` +cd $ACL_PLUGIN_SETUP_DIR; +$ROOT_DIR/virtualenv/bin/python setup.py install +cd $ROOT_DIR +# figure out the shared library path and start scapy +export LD_LIBRARY_PATH=`pwd`/`find . -name "libpneum.so" -exec dirname {} \; | grep lib64 | head -n 1` +sudo LD_LIBRARY_PATH=$LD_LIBRARY_PATH virtualenv/bin/scapy + + + diff --git a/plugins/acl-plugin/test/test_acl_plugin.py b/plugins/acl-plugin/test/test_acl_plugin.py new file mode 100644 index 00000000..7fc72d67 --- /dev/null +++ b/plugins/acl-plugin/test/test_acl_plugin.py @@ -0,0 +1,118 @@ +from __future__ import print_function +import unittest, sys, time, threading, struct, logging, os +import vpp_papi +# import vpp_papi_plugins.acl +from ipaddress import * +papi_event = threading.Event() +print(vpp_papi.vpe.VL_API_SW_INTERFACE_SET_FLAGS) +def papi_event_handler(result): + if result.vl_msg_id == vpp_papi.vpe.VL_API_SW_INTERFACE_SET_FLAGS: + return + if result.vl_msg_id == vpp_papi.vpe.VL_API_VNET_INTERFACE_COUNTERS: + print('Interface counters', result) + return + if result.vl_msg_id == vpp_papi.vpe.VL_API_VNET_IP6_FIB_COUNTERS: + print('IPv6 FIB counters', result) + papi_event.set() + return + + print('Unknown message id:', result.vl_msg_id) + +import glob, subprocess +class TestAclPlugin(unittest.TestCase): + @classmethod + def setUpClass(cls): + print("Setup") + @classmethod + def tearDownClass(cls): + print("Teardown") + + def setUp(self): + print("Connecting API") + r = vpp_papi.connect("test_papi") + self.assertEqual(r, 0) + + def tearDown(self): + r = vpp_papi.disconnect() + self.assertEqual(r, 0) + + # + # The tests themselves + # + + # + # Basic request / reply + # + def test_show_version(self): + t = vpp_papi.show_version() + print('T', t); + program = t.program.decode().rstrip('\x00') + self.assertEqual('vpe', program) + + def x_test_acl_add(self): + print("Test ACL add") + self.assertEqual(1, 1) + + # + # Details / Dump + # + def x_test_details_dump(self): + t = vpp_papi.sw_interface_dump(0, b'') + print('Dump/details T', t) + + # + # Arrays + # + def x_test_arrays(self): + t = vpp_papi.vnet_get_summary_stats() + print('Summary stats', t) + print('Packets:', t.total_pkts[0]) + print('Packets:', t.total_pkts[1]) + # + # Variable sized arrays and counters + # + #@unittest.skip("stats") + def x_test_want_stats(self): + pid = 123 + vpp_papi.register_event_callback(papi_event_handler) + papi_event.clear() + + # Need to configure IPv6 to get som IPv6 FIB stats + t = vpp_papi.create_loopback('') + print(t) + self.assertEqual(t.retval, 0) + + ifindex = t.sw_if_index + addr = str(IPv6Address(u'1::1').packed) + t = vpp_papi.sw_interface_add_del_address(ifindex, 1, 1, 0, 16, addr) + print(t) + self.assertEqual(t.retval, 0) + + # Check if interface is up + # XXX: Add new API to query interface state based on ifindex, instead of dump all. + t = vpp_papi.sw_interface_set_flags(ifindex, 1, 1, 0) + self.assertEqual(t.retval, 0) + + t = vpp_papi.want_stats(True, pid) + + print (t) + + # + # Wait for some stats + # + self.assertEqual(papi_event.wait(15), True) + t = vpp_papi.want_stats(False, pid) + print (t) + + + # + # Plugins? + # + +if __name__ == '__main__' or __name__ == '__builtin__': + print("This is main") + suite = unittest.TestLoader().loadTestsFromTestCase(TestAclPlugin) + unittest.TextTestRunner(verbosity=2).run(suite) + #logging.basicConfig(level=logging.DEBUG) + # unittest.main() + diff --git a/plugins/configure.ac b/plugins/configure.ac index cf58bf75..6e7d5b8a 100644 --- a/plugins/configure.ac +++ b/plugins/configure.ac @@ -59,6 +59,7 @@ PLUGIN_ENABLED(snat) PLUGIN_ENABLED(ila) PLUGIN_ENABLED(lb) PLUGIN_ENABLED(flowperpkt) +PLUGIN_ENABLED(acl) # Disabled plugins, require --enable-XXX-plugin PLUGIN_DISABLED(vcgn) |