diff options
author | 2016-04-30 10:25:32 -0400 | |
---|---|---|
committer | 2016-08-01 18:16:24 -0700 | |
commit | 6f9bca21945b171035a2a00663f1cd2185027f8b (patch) | |
tree | 7ed04ce7ed88233f1900941b1b41cd78c890d3ee | |
parent | f7643fd9e1915e703d9a696c97be685328e9c388 (diff) |
VPP-226 IPv4 src-address + port range checker
Change-Id: Ia251e9d7d53e894a5666109f69e9626d27ea74cb
Signed-off-by: Dave Barach <dave@barachs.net>
Signed-off-by: Keith Burns (alagalah) <alagalah@gmail.com>
-rw-r--r-- | vnet/Makefile.am | 2 | ||||
-rw-r--r-- | vnet/vnet/api_errno.h | 5 | ||||
-rw-r--r-- | vnet/vnet/ip/ip4.h | 51 | ||||
-rw-r--r-- | vnet/vnet/ip/ip4_error.h | 4 | ||||
-rw-r--r-- | vnet/vnet/ip/ip4_forward.c | 23 | ||||
-rw-r--r-- | vnet/vnet/ip/ip4_input.c | 4 | ||||
-rw-r--r-- | vnet/vnet/ip/ip4_source_and_port_range_check.c | 956 | ||||
-rw-r--r-- | vnet/vnet/ip/ip_source_and_port_range_check.h | 31 | ||||
-rw-r--r-- | vnet/vnet/ip/lookup.c | 112 | ||||
-rw-r--r-- | vnet/vnet/ip/lookup.h | 34 | ||||
-rw-r--r-- | vpp-api-test/vat/api_format.c | 190 | ||||
-rw-r--r-- | vpp/vpp-api/api.c | 126 | ||||
-rw-r--r-- | vpp/vpp-api/custom_dump.c | 50 | ||||
-rw-r--r-- | vpp/vpp-api/vpe.api | 57 |
14 files changed, 1561 insertions, 84 deletions
diff --git a/vnet/Makefile.am b/vnet/Makefile.am index e9a73474..738f33f1 100644 --- a/vnet/Makefile.am +++ b/vnet/Makefile.am @@ -258,6 +258,7 @@ libvnet_la_SOURCES += \ vnet/ip/ip4_mtrie.c \ vnet/ip/ip4_pg.c \ vnet/ip/ip4_source_check.c \ + vnet/ip/ip4_source_and_port_range_check.c \ vnet/ip/ip6_format.c \ vnet/ip/ip6_forward.c \ vnet/ip/ip6_hop_by_hop.c \ @@ -284,6 +285,7 @@ nobase_include_HEADERS += \ vnet/ip/igmp_packet.h \ vnet/ip/ip.h \ vnet/ip/ip_feature_registration.h \ + vnet/ip/ip_source_and_port_range_check.h \ vnet/ip/ip4.h \ vnet/ip/ip4_mtrie.h \ vnet/ip/ip4_error.h \ diff --git a/vnet/vnet/api_errno.h b/vnet/vnet/api_errno.h index 2c247d45..2b18ef56 100644 --- a/vnet/vnet/api_errno.h +++ b/vnet/vnet/api_errno.h @@ -83,7 +83,10 @@ _(INVALID_WORKER, -89, "Invalid worker thread") \ _(LISP_DISABLED, -90, "LISP is disabled") \ _(CLASSIFY_TABLE_NOT_FOUND, -91, "Classify table not found") \ _(INVALID_EID_TYPE, -92, "Unsupported LSIP EID type") \ -_(CANNOT_CREATE_PCAP_FILE, -93, "Cannot create pcap file") +_(CANNOT_CREATE_PCAP_FILE, -93, "Cannot create pcap file") \ +_(INCORRECT_ADJACENCY_TYPE, -94, "Invalid adjacency type for this operation") \ +_(EXCEEDED_NUMBER_OF_RANGES_CAPACITY, -95, "Operation would exceed configured capacity of ranges") \ +_(EXCEEDED_NUMBER_OF_PORTS_CAPACITY, -96, "Operation would exceed capacity of number of ports") typedef enum { #define _(a,b,c) VNET_API_ERROR_##a = (b), diff --git a/vnet/vnet/ip/ip4.h b/vnet/vnet/ip/ip4.h index fb04893a..92dff075 100644 --- a/vnet/vnet/ip/ip4.h +++ b/vnet/vnet/ip/ip4.h @@ -102,68 +102,87 @@ typedef struct { uword function_opaque; } ip4_add_del_interface_address_callback_t; +/** + * @brief IPv4 main type. + * + * State of IPv4 VPP processing including: + * - FIBs + * - Feature indices used in feature topological sort + * - Feature node run time references + */ + typedef struct ip4_main_t { ip_lookup_main_t lookup_main; - /* Vector of FIBs. */ + /** Vector of FIBs. */ ip4_fib_t * fibs; u32 fib_masks[33]; - /* Table index indexed by software interface. */ + /** Table index indexed by software interface. */ u32 * fib_index_by_sw_if_index; - /* Hash table mapping table id to fib index. + /** Hash table mapping table id to fib index. ID space is not necessarily dense; index space is dense. */ uword * fib_index_by_table_id; - /* Vector of functions to call when routes are added/deleted. */ + /** Vector of functions to call when routes are added/deleted. */ ip4_add_del_route_callback_t * add_del_route_callbacks; - /* Hash table mapping interface route rewrite adjacency index by sw if index. */ + /** Hash table mapping interface route rewrite adjacency index by sw if index. */ uword * interface_route_adj_index_by_sw_if_index; - /* Functions to call when interface address changes. */ + /** Functions to call when interface address changes. */ ip4_add_del_interface_address_callback_t * add_del_interface_address_callbacks; - /* Template used to generate IP4 ARP packets. */ + /** Template used to generate IP4 ARP packets. */ vlib_packet_template_t ip4_arp_request_packet_template; - /* feature path configuration lists */ + /** Feature path configuration lists */ vnet_ip_feature_registration_t * next_uc_feature; vnet_ip_feature_registration_t * next_mc_feature; - /* Built-in unicast feature path indices, see ip_feature_init_cast(...) */ + /** Built-in unicast feature path indice, see @ref ip_feature_init_cast() */ u32 ip4_unicast_rx_feature_check_access; + /** Built-in unicast feature path indice, see @ref ip_feature_init_cast() */ u32 ip4_unicast_rx_feature_source_reachable_via_rx; + /** Built-in unicast feature path indice, see @ref ip_feature_init_cast() */ u32 ip4_unicast_rx_feature_source_reachable_via_any; + /** Built-in unicast feature path indice, see @ref ip_feature_init_cast() */ u32 ip4_unicast_rx_feature_policer_classify; + /** Built-in unicast feature path indice, see @ref ip_feature_init_cast() */ u32 ip4_unicast_rx_feature_ipsec; + /** Built-in unicast feature path indice, see @ref ip_feature_init_cast() */ u32 ip4_unicast_rx_feature_vpath; + /** Built-in unicast feature path indice, see @ref ip_feature_init_cast() */ u32 ip4_unicast_rx_feature_lookup; + /** Built-in unicast feature path indice, see @ref ip_feature_init_cast() */ + u32 ip4_unicast_rx_feature_source_and_port_range_check; - /* Built-in multicast feature path indices */ + /** Built-in multicast feature path indices */ u32 ip4_multicast_rx_feature_vpath; + /** Built-in multicast feature path indices */ u32 ip4_multicast_rx_feature_lookup; - /* Save results for show command */ + /** Save results for show command */ char ** feature_nodes[VNET_N_CAST]; - - /* Seed for Jenkins hash used to compute ip4 flow hash. */ + + /** Seed for Jenkins hash used to compute ip4 flow hash. */ u32 flow_hash_seed; + /** @brief Template information for VPP generated packets */ struct { - /* TTL to use for host generated packets. */ + /** TTL to use for host generated packets. */ u8 ttl; - /* TOS byte to use for host generated packets. */ + /** TOS byte to use for host generated packets. */ u8 tos; u8 pad[2]; } host_config; } ip4_main_t; -/* Global ip4 main structure. */ +/** Global ip4 main structure. */ extern ip4_main_t ip4_main; #define VNET_IP4_UNICAST_FEATURE_INIT(x,...) \ diff --git a/vnet/vnet/ip/ip4_error.h b/vnet/vnet/ip/ip4_error.h index b84b082b..8d7fc53d 100644 --- a/vnet/vnet/ip/ip4_error.h +++ b/vnet/vnet/ip/ip4_error.h @@ -72,10 +72,10 @@ /* Spoofed packets in ip4-rewrite-local */ \ _(SPOOFED_LOCAL_PACKETS, "ip4 spoofed local-address packet drops") \ \ - /* Erros singalled by ip4-inacl */ \ + /* Errors singalled by ip4-inacl */ \ _ (INACL_TABLE_MISS, "input ACL table-miss drops") \ _ (INACL_SESSION_DENY, "input ACL session deny drops") - + typedef enum { #define _(sym,str) IP4_ERROR_##sym, foreach_ip4_error diff --git a/vnet/vnet/ip/ip4_forward.c b/vnet/vnet/ip/ip4_forward.c index 8b71d2db..1862d89d 100644 --- a/vnet/vnet/ip/ip4_forward.c +++ b/vnet/vnet/ip/ip4_forward.c @@ -39,14 +39,18 @@ #include <vnet/vnet.h> #include <vnet/ip/ip.h> -#include <vnet/ethernet/ethernet.h> /* for ethernet_header_t */ -#include <vnet/ethernet/arp_packet.h> /* for ethernet_arp_header_t */ +/** for ethernet_header_t */ +#include <vnet/ethernet/ethernet.h> +/** for ethernet_arp_header_t */ +#include <vnet/ethernet/arp_packet.h> #include <vnet/ppp/ppp.h> -#include <vnet/srp/srp.h> /* for srp_hw_interface_class */ -#include <vnet/api_errno.h> /* for API error numbers */ +/** for srp_hw_interface_class */ +#include <vnet/srp/srp.h> +/** for API error numbers */ +#include <vnet/api_errno.h> -/** \file - vnet ip4 forwarding +/** @file + vnet ip4 forwarding */ /* This is really, really simple but stupid fib. */ @@ -1382,6 +1386,13 @@ VNET_IP4_UNICAST_FEATURE_INIT (ip4_source_check_2, static) = { &ip4_main.ip4_unicast_rx_feature_source_reachable_via_any, }; +VNET_IP4_UNICAST_FEATURE_INIT (ip4_source_and_port_range_check, static) = { + .node_name = "ip4-source-and-port-range-check", + .runs_before = {"ip4-policer-classify", 0}, + .feature_index = + &ip4_main.ip4_unicast_rx_feature_source_and_port_range_check, +}; + VNET_IP4_UNICAST_FEATURE_INIT (ip4_policer_classify, static) = { .node_name = "ip4-policer-classify", .runs_before = {"ipsec-input-ip4", 0}, diff --git a/vnet/vnet/ip/ip4_input.c b/vnet/vnet/ip/ip4_input.c index 1c7d327d..96a68661 100644 --- a/vnet/vnet/ip/ip4_input.c +++ b/vnet/vnet/ip/ip4_input.c @@ -453,6 +453,10 @@ static clib_error_t * ip4_init (vlib_main_t * vm) if ((error = vlib_call_init_function (vm, ip4_source_check_init))) return error; + if ((error = vlib_call_init_function + (vm, ip4_source_and_port_range_check_init))) + return error; + /* Set flow hash to something non-zero. */ ip4_main.flow_hash_seed = 0xdeadbeef; diff --git a/vnet/vnet/ip/ip4_source_and_port_range_check.c b/vnet/vnet/ip/ip4_source_and_port_range_check.c new file mode 100644 index 00000000..9dfb5228 --- /dev/null +++ b/vnet/vnet/ip/ip4_source_and_port_range_check.c @@ -0,0 +1,956 @@ +/* + * 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/ip/ip.h> + +typedef struct { + u32 ranges_per_adjacency; + u32 special_adjacency_format_function_index; + + /* convenience */ + vlib_main_t *vlib_main; + vnet_main_t *vnet_main; +} source_range_check_main_t; + +source_range_check_main_t source_range_check_main; + +vlib_node_registration_t ip4_source_port_and_range_check; + +typedef struct { + union { + u16x8 as_u16x8; + u16 as_u16[8]; + }; +} u16x8vec_t; + +typedef struct { + u16x8vec_t low; + u16x8vec_t hi; +} port_range_t; + +#define foreach_ip4_source_and_port_range_check_error \ +_(CHECK_FAIL, "ip4 source and port range check bad packets") \ +_(CHECK_OK, "ip4 source and port range check good packets") + +typedef enum { +#define _(sym,str) IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_##sym, + foreach_ip4_source_and_port_range_check_error +#undef _ + IP4_SOURCE_AND_PORT_RANGE_CHECK_N_ERROR, +} ip4_source_and_port_range_check_error_t; + +static char * ip4_source_and_port_range_check_error_strings[] = { +#define _(sym,string) string, + foreach_ip4_source_and_port_range_check_error +#undef _ +}; + +typedef struct { + u32 pass; + u32 bypass; + u32 is_tcp; + ip4_address_t src_addr; + u16 dst_port; +} ip4_source_and_port_range_check_trace_t; + +static u8 * format_ip4_source_and_port_range_check_trace (u8 * s, va_list * va) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *); + ip4_source_and_port_range_check_trace_t * t = + va_arg (*va, ip4_source_and_port_range_check_trace_t *); + + if (t->bypass) + s = format (s, "PASS (bypass case)"); + else + s = format (s, "src ip %U %s dst port %d: %s", + format_ip4_address, &t->src_addr, t->is_tcp ? "TCP" : "UDP", + (u32) t->dst_port, + (t->pass == 1) ? "PASS" : "FAIL"); + return s; +} + +typedef enum { + IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP, + IP4_SOURCE_AND_PORT_RANGE_CHECK_N_NEXT, +} ip4_source_and_port_range_check_next_t; + +typedef union { + u32 fib_index; +} ip4_source_and_port_range_check_config_t; + +static inline u32 check_adj_port_range_x1 (ip_adjacency_t * adj, + u16 dst_port, + u32 next) +{ + port_range_t *range; + u16x8vec_t key; + u16x8vec_t diff1; + u16x8vec_t diff2; + u16x8vec_t sum, sum_equal_diff2; + u16 sum_nonzero, sum_equal, winner_mask; + int i; + u8 * rwh; + + if (adj->lookup_next_index != IP_LOOKUP_NEXT_ICMP_ERROR || dst_port == 0) + return IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP; + + rwh = (u8 *)(&adj->rewrite_header); + range = (port_range_t *)rwh; + + /* Make the obvious screw-case work. A variant also works w/ no MMX */ + if (PREDICT_FALSE(dst_port == 65535)) + { + int j; + + for (i = 0; i < VLIB_BUFFER_PRE_DATA_SIZE / sizeof(port_range_t); i++) + { + for (j = 0; j < 8; j++) + if (range->low.as_u16x8[j] == 65535) + return next; + range++; + } + return IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP; + } + + key.as_u16x8 = u16x8_splat (dst_port); + + for (i = 0; i < VLIB_BUFFER_PRE_DATA_SIZE / sizeof(port_range_t); i++) + { + diff1.as_u16x8 = u16x8_sub_saturate (range->low.as_u16x8, key.as_u16x8); + diff2.as_u16x8 = u16x8_sub_saturate (range->hi.as_u16x8, key.as_u16x8); + sum.as_u16x8 = u16x8_add (diff1.as_u16x8, diff2.as_u16x8); + sum_equal_diff2.as_u16x8 = u16x8_is_equal (sum.as_u16x8, diff2.as_u16x8); + sum_nonzero = ~u16x8_zero_byte_mask (sum.as_u16x8); + sum_equal = ~u16x8_zero_byte_mask (sum_equal_diff2.as_u16x8); + winner_mask = sum_nonzero & sum_equal; + if (winner_mask) + return next; + range++; + } + return IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP; +} + +always_inline uword +ip4_source_and_port_range_check_inline +(vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + ip4_main_t * im = &ip4_main; + ip_lookup_main_t * lm = &im->lookup_main; + ip_config_main_t * cm = &lm->rx_config_mains[VNET_UNICAST]; + u32 n_left_from, * from, * to_next; + u32 next_index; + vlib_node_runtime_t * error_node = node; + u32 good_packets = 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 >= 4 && n_left_to_next >= 2) + { + vlib_buffer_t * b0, * b1; + ip4_header_t * ip0, * ip1; + ip4_fib_mtrie_t * mtrie0, * mtrie1; + ip4_fib_mtrie_leaf_t leaf0, leaf1; + ip4_source_and_port_range_check_config_t * c0, * c1; + ip_adjacency_t * adj0, * adj1; + u32 bi0, next0, adj_index0, pass0, save_next0; + u32 bi1, next1, adj_index1, pass1, save_next1; + udp_header_t * udp0, * udp1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t * p2, * p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, sizeof (ip0[0]), LOAD); + CLIB_PREFETCH (p3->data, sizeof (ip1[0]), LOAD); + } + + bi0 = to_next[0] = from[0]; + bi1 = to_next[1] = from[1]; + from += 2; + to_next += 2; + n_left_from -= 2; + n_left_to_next -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + ip0 = vlib_buffer_get_current (b0); + ip1 = vlib_buffer_get_current (b1); + + c0 = vnet_get_config_data (&cm->config_main, + &b0->current_config_index, + &next0, + sizeof (c0[0])); + c1 = vnet_get_config_data (&cm->config_main, + &b1->current_config_index, + &next1, + sizeof (c1[0])); + + /* we can't use the default VRF here... */ + ASSERT (c0->fib_index && c1->fib_index); + + mtrie0 = &vec_elt_at_index (im->fibs, c0->fib_index)->mtrie; + mtrie1 = &vec_elt_at_index (im->fibs, c1->fib_index)->mtrie; + + leaf0 = leaf1 = IP4_FIB_MTRIE_LEAF_ROOT; + + leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, + &ip0->src_address, 0); + leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, + &ip1->src_address, 0); + + leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, + &ip0->src_address, 1); + leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, + &ip1->src_address, 1); + + leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, + &ip0->src_address, 2); + leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, + &ip1->src_address, 2); + + leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, + &ip0->src_address, 3); + leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, + &ip1->src_address, 3); + + adj_index0 = ip4_fib_mtrie_leaf_get_adj_index (leaf0); + adj_index1 = ip4_fib_mtrie_leaf_get_adj_index (leaf1); + + ASSERT (adj_index0 == ip4_fib_lookup_with_table (im, c0->fib_index, + &ip0->src_address, + 0 /* use dflt rt */)); + + ASSERT (adj_index1 == ip4_fib_lookup_with_table (im, c1->fib_index, + &ip1->src_address, + 0)); + adj0 = ip_get_adjacency (lm, adj_index0); + adj1 = ip_get_adjacency (lm, adj_index1); + + pass0 = 0; + pass0 |= ip4_address_is_multicast (&ip0->src_address); + pass0 |= ip0->src_address.as_u32 == clib_host_to_net_u32(0xFFFFFFFF); + pass0 |= (ip0->protocol != IP_PROTOCOL_UDP) && + (ip0->protocol != IP_PROTOCOL_TCP); + + pass1 = 0; + pass1 |= ip4_address_is_multicast (&ip1->src_address); + pass1 |= ip1->src_address.as_u32 == clib_host_to_net_u32(0xFFFFFFFF); + pass1 |= (ip1->protocol != IP_PROTOCOL_UDP) && + (ip1->protocol != IP_PROTOCOL_TCP); + + save_next0 = next0; + udp0 = ip4_next_header (ip0); + save_next1 = next1; + udp1 = ip4_next_header (ip1); + + if (PREDICT_TRUE(pass0 == 0)) + { + good_packets ++; + next0 = check_adj_port_range_x1 + (adj0, clib_net_to_host_u16(udp0->dst_port), next0); + good_packets -= (save_next0 != next0); + b0->error = error_node->errors + [IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_FAIL]; + } + + if (PREDICT_TRUE(pass1 == 0)) + { + good_packets ++; + next1 = check_adj_port_range_x1 + (adj1, clib_net_to_host_u16(udp1->dst_port), next1); + good_packets -= (save_next1 != next1); + b1->error = error_node->errors + [IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_FAIL]; + } + + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) { + ip4_source_and_port_range_check_trace_t * t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->pass = next0 == save_next0; + t->bypass = pass0; + t->src_addr.as_u32 = ip0->src_address.as_u32; + t->dst_port = (pass0 == 0) ? + clib_net_to_host_u16(udp0->dst_port) : 0; + t->is_tcp = ip0->protocol == IP_PROTOCOL_TCP; + } + + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b1->flags & VLIB_BUFFER_IS_TRACED))) { + ip4_source_and_port_range_check_trace_t * t = + vlib_add_trace (vm, node, b1, sizeof (*t)); + t->pass = next1 == save_next1; + t->bypass = pass1; + t->src_addr.as_u32 = ip1->src_address.as_u32; + t->dst_port = (pass1 == 0) ? + clib_net_to_host_u16(udp1->dst_port) : 0; + t->is_tcp = ip1->protocol == IP_PROTOCOL_TCP; + } + + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + vlib_buffer_t * b0; + ip4_header_t * ip0; + ip4_fib_mtrie_t * mtrie0; + ip4_fib_mtrie_leaf_t leaf0; + ip4_source_and_port_range_check_config_t * c0; + ip_adjacency_t * adj0; + u32 bi0, next0, adj_index0, pass0, save_next0; + udp_header_t * udp0; + + 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); + ip0 = vlib_buffer_get_current (b0); + + c0 = vnet_get_config_data + (&cm->config_main, &b0->current_config_index, + &next0, + sizeof (c0[0])); + + /* we can't use the default VRF here... */ + ASSERT(c0->fib_index); + + mtrie0 = &vec_elt_at_index (im->fibs, c0->fib_index)->mtrie; + + leaf0 = IP4_FIB_MTRIE_LEAF_ROOT; + + leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, + &ip0->src_address, 0); + + leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, + &ip0->src_address, 1); + + leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, + &ip0->src_address, 2); + + leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, + &ip0->src_address, 3); + + adj_index0 = ip4_fib_mtrie_leaf_get_adj_index (leaf0); + + ASSERT (adj_index0 == ip4_fib_lookup_with_table + (im, c0->fib_index, + &ip0->src_address, + 0 /* use default route */)); + adj0 = ip_get_adjacency (lm, adj_index0); + + /* + * $$$ which (src,dst) categories should we always pass? + */ + pass0 = 0; + pass0 |= ip4_address_is_multicast (&ip0->src_address); + pass0 |= ip0->src_address.as_u32 == clib_host_to_net_u32(0xFFFFFFFF); + pass0 |= (ip0->protocol != IP_PROTOCOL_UDP) && + (ip0->protocol != IP_PROTOCOL_TCP); + + save_next0 = next0; + udp0 = ip4_next_header (ip0); + + if (PREDICT_TRUE(pass0 == 0)) + { + good_packets ++; + next0 = check_adj_port_range_x1 + (adj0, clib_net_to_host_u16(udp0->dst_port), next0); + good_packets -= (save_next0 != next0); + b0->error = error_node->errors + [IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_FAIL]; + } + + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) { + ip4_source_and_port_range_check_trace_t * t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->pass = next0 == save_next0; + t->bypass = pass0; + t->src_addr.as_u32 = ip0->src_address.as_u32; + t->dst_port = (pass0 == 0) ? + clib_net_to_host_u16(udp0->dst_port) : 0; + t->is_tcp = ip0->protocol == IP_PROTOCOL_TCP; + } + + 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, ip4_source_port_and_range_check.index, + IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_OK, + good_packets); + return frame->n_vectors; +} + +static uword +ip4_source_and_port_range_check (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return ip4_source_and_port_range_check_inline (vm, node, frame); +} + +VLIB_REGISTER_NODE (ip4_source_port_and_range_check) = { + .function = ip4_source_and_port_range_check, + .name = "ip4-source-and-port-range-check", + .vector_size = sizeof (u32), + + .n_errors = ARRAY_LEN(ip4_source_and_port_range_check_error_strings), + .error_strings = ip4_source_and_port_range_check_error_strings, + + .n_next_nodes = IP4_SOURCE_AND_PORT_RANGE_CHECK_N_NEXT, + .next_nodes = { + [IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP] = "error-drop", + }, + + .format_buffer = format_ip4_header, + .format_trace = format_ip4_source_and_port_range_check_trace, +}; + +int set_ip_source_and_port_range_check (vlib_main_t * vm, + u32 fib_index, + u32 sw_if_index, + u32 is_add) +{ + ip4_main_t * im = &ip4_main; + ip_lookup_main_t * lm = &im->lookup_main; + ip_config_main_t * rx_cm = &lm->rx_config_mains[VNET_UNICAST]; + u32 ci; + ip4_source_and_port_range_check_config_t config; + u32 feature_index; + int rv = 0; + u8 is_del = !is_add; + + config.fib_index = fib_index; + feature_index = im->ip4_unicast_rx_feature_source_and_port_range_check; + + vec_validate (rx_cm->config_index_by_sw_if_index, sw_if_index); + + ci = rx_cm->config_index_by_sw_if_index[sw_if_index]; + ci = (is_del + ? vnet_config_del_feature + : vnet_config_add_feature) + (vm, &rx_cm->config_main, + ci, + feature_index, + &config, + sizeof (config)); + rx_cm->config_index_by_sw_if_index[sw_if_index] = ci; + + return rv; +} + +static clib_error_t * +set_ip_source_and_port_range_check_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + vnet_main_t * vnm = vnet_get_main(); + ip4_main_t * im = &ip4_main; + clib_error_t * error = 0; + u32 is_add = 1; + u32 sw_if_index = ~0; + u32 vrf_id = ~0; + u32 fib_index; + uword * p; + int rv = 0; + + sw_if_index = ~0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%U", unformat_vnet_sw_interface, vnm, + &sw_if_index)) + ; + else if (unformat (input, "vrf %d", &vrf_id)) + ; + else if (unformat (input, "del")) + is_add = 0; + else + break; + } + + if (sw_if_index == ~0) + return clib_error_return (0, "Interface required but not specified"); + + if (vrf_id == ~0) + return clib_error_return (0, "VRF ID required but not specified"); + + if (vrf_id == 0) + return clib_error_return (0, "VRF ID should not be default. Should be distinct VRF for this purpose. "); + + p = hash_get (im->fib_index_by_table_id, vrf_id); + + if (p == 0) + return clib_error_return (0, "Invalid VRF ID %d", vrf_id); + + fib_index = p[0]; + rv = set_ip_source_and_port_range_check (vm, fib_index, sw_if_index, is_add); + + switch(rv) + { + case 0: + break; + + default: + return clib_error_return + (0, "set source and port-range on interface returned an unexpected value: %d", rv); + } + return error; +} + +VLIB_CLI_COMMAND (set_interface_ip_source_and_port_range_check_command, + static) = { + .path = "set interface ip source-and-port-range-check", + .function = set_ip_source_and_port_range_check_fn, + .short_help = "set int ip source-and-port-range-check <intfc> vrf <n> [del]", +}; + +static u8 * format_source_and_port_rc_adjacency (u8 * s, va_list * args) +{ + CLIB_UNUSED (vnet_main_t * vnm) = va_arg (*args, vnet_main_t *); + ip_lookup_main_t * lm = va_arg (*args, ip_lookup_main_t *); + u32 adj_index = va_arg (*args, u32); + ip_adjacency_t * adj = ip_get_adjacency (lm, adj_index); + source_range_check_main_t * srm = &source_range_check_main; + u8 * rwh = (u8 *) (&adj->rewrite_header); + port_range_t * range; + int i, j; + int printed = 0; + + range = (port_range_t *) rwh; + + s = format (s, "allow "); + + for (i = 0; i < srm->ranges_per_adjacency; i++) + { + for (j = 0; j < 8; j++) + { + if (range->low.as_u16[j]) + { + if (printed) + s = format (s, ", "); + if (range->hi.as_u16[j] > (range->low.as_u16[j] + 1)) + s = format (s, "%d-%d", (u32) range->low.as_u16[j], + (u32) range->hi.as_u16[j] - 1); + else + s = format (s, "%d", range->low.as_u16[j]); + printed = 1; + } + } + range++; + } + return s; +} + +clib_error_t * ip4_source_and_port_range_check_init (vlib_main_t * vm) +{ + source_range_check_main_t * srm = &source_range_check_main; + ip4_main_t * im = &ip4_main; + ip_lookup_main_t * lm = &im->lookup_main; + + srm->vlib_main = vm; + srm->vnet_main = vnet_get_main(); + + srm->ranges_per_adjacency = VLIB_BUFFER_PRE_DATA_SIZE / (2*sizeof(u16x8)); + srm->special_adjacency_format_function_index = + vnet_register_special_adjacency_format_function + (lm, format_source_and_port_rc_adjacency); + ASSERT (srm->special_adjacency_format_function_index); + + return 0; +} + +VLIB_INIT_FUNCTION (ip4_source_and_port_range_check_init); + + +int ip4_source_and_port_range_check_add_del +(ip4_address_t * address, u32 length, u32 vrf_id, u16 * low_ports, + u16 * hi_ports, int is_add) +{ + source_range_check_main_t * srm = &source_range_check_main; + ip4_main_t * im = &ip4_main; + ip_lookup_main_t * lm = &im->lookup_main; + uword * p; + u32 fib_index; + u32 adj_index; + ip_adjacency_t * adj; + int i, j, k; + port_range_t * range; + u8 *rwh; + + p = hash_get (im->fib_index_by_table_id, vrf_id); + if (!p) + { + ip4_fib_t * f; + f = find_ip4_fib_by_table_index_or_id (im, vrf_id, 0 /* flags */); + fib_index = f->index; + } + else + fib_index = p[0]; + + adj_index = ip4_fib_lookup_with_table + (im, fib_index, address, 0 /* disable_default_route */); + + if (is_add == 0) + { + adj = ip_get_adjacency (lm, adj_index); + if (adj->lookup_next_index != IP_LOOKUP_NEXT_ICMP_ERROR) + return VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE; + + rwh = (u8 *)(&adj->rewrite_header); + + for (i = 0; i < vec_len (low_ports); i++) + { + range = (port_range_t *) rwh; + for (j = 0; j < srm->ranges_per_adjacency; j++) + { + for (k = 0; k < 8; k++) + { + if (low_ports[i] == range->low.as_u16[k] && + hi_ports[i] == range->hi.as_u16[k]) + { + range->low.as_u16[k] = range->hi.as_u16[k] = 0; + goto doublebreak; + } + } + range++; + } + doublebreak: ; + } + + range = (port_range_t *) rwh; + /* Have we deleted all ranges yet? */ + for (i = 0; i < srm->ranges_per_adjacency; i++) + { + for (j = 0; j < 8; j++) + { + if (range->low.as_u16[i] != 0) + goto still_occupied; + } + range++; + } + /* Yes, lose the adjacency... */ + { + ip4_add_del_route_args_t a; + + memset (&a, 0, sizeof(a)); + a.flags = IP4_ROUTE_FLAG_FIB_INDEX | IP4_ROUTE_FLAG_DEL; + a.table_index_or_table_id = fib_index; + a.dst_address = address[0]; + a.dst_address_length = length; + a.adj_index = adj_index; + ip4_add_del_route (im, &a); + } + + still_occupied: + ; + } + else + { + adj = ip_get_adjacency (lm, adj_index); + /* $$$$ fixme: add ports if address + mask match */ + if (adj->lookup_next_index == IP_LOOKUP_NEXT_ICMP_ERROR) + return VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE; + + { + ip_adjacency_t template_adj; + ip4_add_del_route_args_t a; + + memset (&template_adj, 0, sizeof (template_adj)); + + template_adj.lookup_next_index = IP_LOOKUP_NEXT_ICMP_ERROR; + template_adj.if_address_index = ~0; + template_adj.special_adjacency_format_function_index = + srm->special_adjacency_format_function_index; + + rwh = (u8 *) (&template_adj.rewrite_header); + + range = (port_range_t *) rwh; + + if (vec_len (low_ports) > 8 * srm->ranges_per_adjacency) + return VNET_API_ERROR_EXCEEDED_NUMBER_OF_RANGES_CAPACITY; + + j = k = 0; + + for (i = 0; i < vec_len (low_ports); i++) + { + for (; j < srm->ranges_per_adjacency; j++) + { + for (; k < 8; k++) + { + if (range->low.as_u16[k] == 0) + { + range->low.as_u16[k] = low_ports[i]; + range->hi.as_u16[k] = hi_ports[i]; + k++; + if (k == 7) + { + k = 0; + j++; + } + goto doublebreak2; + } + } + k = 0; + range++; + } + j = 0; + /* Too many ports specified... */ + return VNET_API_ERROR_EXCEEDED_NUMBER_OF_PORTS_CAPACITY; + + doublebreak2: ; + } + + memset (&a, 0, sizeof(a)); + a.flags = IP4_ROUTE_FLAG_FIB_INDEX; + a.table_index_or_table_id = fib_index; + a.dst_address = address[0]; + a.dst_address_length = length; + a.add_adj = &template_adj; + a.n_add_adj = 1; + + ip4_add_del_route (im, &a); + } + } + + return 0; +} + +static clib_error_t * +ip_source_and_port_range_check_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + u16 * low_ports = 0; + u16 * high_ports = 0; + u16 this_low; + u16 this_hi; + ip4_address_t addr; + u32 length; + u32 tmp, tmp2; + u8 prefix_set = 0; + u32 vrf_id = ~0; + int is_add = 1; + int rv; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%U/%d", unformat_ip4_address, &addr, &length)) + prefix_set = 1; + else if (unformat (input, "vrf %d", &vrf_id)) + ; + else if (unformat (input, "del")) + is_add = 0; + else if (unformat (input, "port %d", &tmp)) + { + if (tmp == 0 || tmp > 65535) + return clib_error_return (0, "port %d out of range", tmp); + this_low = tmp; + this_hi = this_low + 1; + vec_add1 (low_ports, this_low); + vec_add1 (high_ports, this_hi); + } + else if (unformat (input, "range %d - %d", &tmp, &tmp2)) + { + if (tmp > tmp2) + return clib_error_return (0, "ports %d and %d out of order", + tmp, tmp2); + if (tmp == 0 || tmp > 65535) + return clib_error_return (0, "low port %d out of range", tmp); + if (tmp2 == 0 || tmp2 > 65535) + return clib_error_return (0, "hi port %d out of range", tmp2); + this_low = tmp; + this_hi = tmp2+1; + vec_add1 (low_ports, this_low); + vec_add1 (high_ports, this_hi); + } + else + break; + } + + if (prefix_set == 0) + return clib_error_return (0, "<address>/<mask> not specified"); + + if (vrf_id == ~0) + return clib_error_return (0, "VRF ID required, not specified"); + + if (vrf_id == 0) + return clib_error_return (0, "VRF ID should not be default. Should be distinct VRF for this purpose. "); + + if (vec_len(low_ports) == 0) + return clib_error_return (0, "At least one port or port range required"); + + rv = ip4_source_and_port_range_check_add_del + (&addr, length, vrf_id, low_ports, high_ports, is_add); + + switch(rv) + { + case 0: + break; + + case VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE: + return clib_error_return + (0, "Incorrect adjacency for add/del operation in ip4 source and port-range check."); + + case VNET_API_ERROR_EXCEEDED_NUMBER_OF_PORTS_CAPACITY: + return clib_error_return + (0, "Too many ports in add/del operation in ip4 source and port-range check."); + + case VNET_API_ERROR_EXCEEDED_NUMBER_OF_RANGES_CAPACITY: + return clib_error_return + (0, "Too many ranges requested for add operation in ip4 source and port-range check."); + + default: + return clib_error_return + (0, "ip4_source_and_port_range_check_add returned an unexpected value: %d", rv); + } + + return 0; +} + +VLIB_CLI_COMMAND (ip_source_and_port_range_check_command, static) = { + .path = "set ip source-and-port-range-check", + .function = ip_source_and_port_range_check_command_fn, + .short_help = + "set ip source-and-port-range-check <ip-addr>/<mask> range <nn>-<nn> vrf <id>", +}; + + +static clib_error_t * +show_source_and_port_range_check_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + source_range_check_main_t * srm = & source_range_check_main; + ip4_main_t * im = &ip4_main; + ip_lookup_main_t * lm = &im->lookup_main; + port_range_t * range; + u32 fib_index; + ip4_address_t addr; + u8 addr_set = 0; + u32 vrf_id = ~0; + int rv, i, j; + u32 adj_index; + ip_adjacency_t *adj; + u32 port = 0; + u8 * rwh; + uword * p; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%U", unformat_ip4_address, &addr)) + addr_set = 1; + else if (unformat (input, "vrf %d", &vrf_id)) + ; + else if (unformat (input, "port %d", &port)) + ; + else + break; + } + + if (addr_set == 0) + return clib_error_return (0, "<address> not specified"); + + if (vrf_id == ~0) + return clib_error_return (0, "VRF ID required, not specified"); + + p = hash_get (im->fib_index_by_table_id, vrf_id); + if (p == 0) + return clib_error_return (0, "VRF %d not found", vrf_id); + fib_index = p[0]; + + adj_index = ip4_fib_lookup_with_table + (im, fib_index, &addr, 0 /* disable_default_route */); + + adj = ip_get_adjacency (lm, adj_index); + + if (adj->lookup_next_index != IP_LOOKUP_NEXT_ICMP_ERROR) + { + vlib_cli_output (vm, "%U: src address drop", format_ip4_address, &addr); + return 0; + } + + if (port) + { + rv = check_adj_port_range_x1 (adj, (u16) port, 1234); + if (rv == 1234) + vlib_cli_output (vm, "%U port %d PASS", format_ip4_address, + &addr, port); + else + vlib_cli_output (vm, "%U port %d FAIL", format_ip4_address, + &addr, port); + return 0; + } + else + { + u8 * s; + rwh = (u8 *) (&adj->rewrite_header); + + s = format (0, "%U: ", format_ip4_address, &addr); + + range = (port_range_t *) rwh; + + for (i = 0; i < srm->ranges_per_adjacency; i++) + { + for (j = 0; j < 8; j++) + { + if (range->low.as_u16[j]) + s = format (s, "%d - %d ", (u32) range->low.as_u16[j], + (u32) range->hi.as_u16[j]); + } + range++; + } + vlib_cli_output (vm, "%s", s); + vec_free(s); + } + + return 0; +} + +VLIB_CLI_COMMAND (show_source_and_port_range_check, static) = { + .path = "show ip source-and-port-range-check", + .function = show_source_and_port_range_check_fn, + .short_help = + "show ip source-and-port-range-check vrf <nn> <ip-addr> <port>", +}; diff --git a/vnet/vnet/ip/ip_source_and_port_range_check.h b/vnet/vnet/ip/ip_source_and_port_range_check.h new file mode 100644 index 00000000..7fbb2b0f --- /dev/null +++ b/vnet/vnet/ip/ip_source_and_port_range_check.h @@ -0,0 +1,31 @@ +/* + * 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. + */ + +#ifndef included_ip_ip_source_and_port_range_check_h +#define included_ip_ip_source_and_port_range_check_h + +int ip4_source_and_port_range_check_add_del (ip4_address_t * address, + u32 length, + u32 vrf_id, + u16 * low_ports, + u16 * hi_ports, + int is_add); + +int set_ip_source_and_port_range_check (vlib_main_t * vm, + u32 fib_index, + u32 sw_if_index, + u32 is_add); + +#endif /* included ip_source_and_port_range_check_h */ diff --git a/vnet/vnet/ip/lookup.c b/vnet/vnet/ip/lookup.c index 126783a2..8531f645 100644 --- a/vnet/vnet/ip/lookup.c +++ b/vnet/vnet/ip/lookup.c @@ -237,9 +237,10 @@ ip_add_adjacency (ip_lookup_main_t * lm, adj[i].mcast_group_index = ~0; adj[i].classify.table_index = ~0; adj[i].saved_lookup_next_index = 0; + adj[i].special_adjacency_format_function_index = 0; if (copy_adj) - adj[i] = copy_adj[i]; + adj[i] = copy_adj[i]; adj[i].heap_handle = handle; adj[i].n_adj = n_adj; @@ -950,7 +951,7 @@ void ip_lookup_init (ip_lookup_main_t * lm, u32 is_ip6) u8 * format_ip_flow_hash_config (u8 * s, va_list * args) { u32 flow_hash_config = va_arg (*args, u32); - + #define _(n,v) if (flow_hash_config & v) s = format (s, "%s ", #n); foreach_flow_hash_bit; #undef _ @@ -973,8 +974,6 @@ u8 * format_ip_lookup_next (u8 * s, va_list * args) reg = vec_elt_at_index(lm->registered_adjacencies, n); if (reg->node_name) { s = format (s, "%s:", reg->node_name); - } else { - s = format (s, "unknown %d", n); } return s; @@ -1010,6 +1009,33 @@ static u8 * format_ip_interface_address (u8 * s, va_list * args) return format (s, "%U", format_ip4_address_and_length, a, ia->address_length); } +u32 vnet_register_special_adjacency_format_function +(ip_lookup_main_t * lm, format_function_t * fp) +{ + u32 rv; + /* + * Initialize the format function registration vector + * Index 0 must be invalid, to avoid finding and fixing trivial bugs + * all over the place + */ + if (vec_len (lm->special_adjacency_format_functions) == 0) + { + vec_add1 (lm->special_adjacency_format_functions, + (format_function_t *) 0); + } + + rv = vec_len (lm->special_adjacency_format_functions); + vec_add1 (lm->special_adjacency_format_functions, fp); + return rv; +} + +/** @brief Pretty print helper function for formatting specific adjacencies. + @param s - input string to format + @param args - other args passed to format function such as: + - vnet_main_t + - ip_lookup_main_t + - adj_index +*/ u8 * format_ip_adjacency (u8 * s, va_list * args) { vnet_main_t * vnm = va_arg (*args, vnet_main_t *); @@ -1018,54 +1044,54 @@ u8 * format_ip_adjacency (u8 * s, va_list * args) ip_adjacency_t * adj = ip_get_adjacency (lm, adj_index); ip_adj_register_t *reg; + if (adj->lookup_next_index < vec_len (lm->registered_adjacencies)) + { + reg = vec_elt_at_index(lm->registered_adjacencies, + adj->lookup_next_index); + if (reg->fn) + { + s = format(s, " %U", reg->fn, lm, adj); + goto format_done; + } + } + switch (adj->lookup_next_index) { case IP_LOOKUP_NEXT_REWRITE: s = format (s, "%U", format_vnet_rewrite, - vnm->vlib_main, &adj->rewrite_header, sizeof (adj->rewrite_data)); + vnm->vlib_main, &adj->rewrite_header, + sizeof (adj->rewrite_data)); break; - + + case IP_LOOKUP_NEXT_ARP: + if (adj->if_address_index != ~0) + s = format (s, " %U", format_ip_interface_address, lm, + adj->if_address_index); + if (adj->arp.next_hop.ip6.as_u64[0] || adj->arp.next_hop.ip6.as_u64[1]) + s = format (s, " via %U", format_ip46_address, + &adj->arp.next_hop, IP46_TYPE_ANY); + break; + case IP_LOOKUP_NEXT_LOCAL: + if (adj->if_address_index != ~0) + s = format (s, " %U", format_ip_interface_address, lm, + adj->if_address_index); + break; + + case IP_LOOKUP_NEXT_CLASSIFY: + s = format (s, " table %d", adj->classify.table_index); + break; + case IP_LOOKUP_NEXT_INDIRECT: + s = format (s, " via %U", format_ip46_address, + &adj->indirect.next_hop, IP46_TYPE_ANY); + break; + default: - s = format (s, "%U", format_ip_lookup_next, lm, adj->lookup_next_index); - if (adj->lookup_next_index == IP_LOOKUP_NEXT_ARP) - s = format (s, " %U", - format_vnet_sw_interface_name, - vnm, - vnet_get_sw_interface (vnm, adj->rewrite_header.sw_if_index)); - switch (adj->lookup_next_index) - { - case IP_LOOKUP_NEXT_ARP: - if (adj->if_address_index != ~0) - s = format (s, " %U", format_ip_interface_address, lm, adj->if_address_index); - if (adj->arp.next_hop.ip6.as_u64[0] || adj->arp.next_hop.ip6.as_u64[1]) - s = format (s, " via %U", format_ip46_address, - &adj->arp.next_hop, IP46_TYPE_ANY); - break; - case IP_LOOKUP_NEXT_LOCAL: - if (adj->if_address_index != ~0) - s = format (s, " %U", format_ip_interface_address, lm, adj->if_address_index); - break; - - case IP_LOOKUP_NEXT_CLASSIFY: - s = format (s, " table %d", adj->classify.table_index); - break; - case IP_LOOKUP_NEXT_INDIRECT: - s = format (s, " via %U", format_ip46_address, - &adj->indirect.next_hop, IP46_TYPE_ANY); - break; - default: - //Fallback to registered format functions - vec_validate(lm->registered_adjacencies, adj->lookup_next_index); - reg = vec_elt_at_index(lm->registered_adjacencies, adj->lookup_next_index); - if (reg->fn) { - s = format(s, " "); - s = reg->fn(s, lm, adj); - } - break; - } + s = format (s, " unknown %d", adj->lookup_next_index); break; } + + format_done: if (adj->explicit_fib_index != ~0 && adj->explicit_fib_index != 0) s = format (s, " lookup fib index %d", adj->explicit_fib_index); if (adj->share_count > 0) diff --git a/vnet/vnet/ip/lookup.h b/vnet/vnet/ip/lookup.h index a66b9ed1..7a6e8565 100644 --- a/vnet/vnet/ip/lookup.h +++ b/vnet/vnet/ip/lookup.h @@ -45,40 +45,42 @@ #include <vnet/ip/ip4_packet.h> #include <vnet/ip/ip6_packet.h> -/* Common (IP4/IP6) next index stored in adjacency. */ +/** @brief Common (IP4/IP6) next index stored in adjacency. */ typedef enum { - /* Packet does not match any route in table. */ + /** Packet does not match any route in table. */ IP_LOOKUP_NEXT_MISS, - /* Adjacency says to drop or punt this packet. */ + /** Adjacency to drop this packet. */ IP_LOOKUP_NEXT_DROP, + /** Adjacency to punt this packet. */ IP_LOOKUP_NEXT_PUNT, - /* This packet is for one of our own IP addresses. */ + /** This packet is for one of our own IP addresses. */ IP_LOOKUP_NEXT_LOCAL, - /* This packet matches an "interface route" and packets + /** This packet matches an "interface route" and packets need to be passed to ARP to find rewrite string for this destination. */ IP_LOOKUP_NEXT_ARP, - /* This packet is to be rewritten and forwarded to the next + /** This packet is to be rewritten and forwarded to the next processing node. This is typically the output interface but might be another node for further output processing. */ IP_LOOKUP_NEXT_REWRITE, - /* This packet needs to be classified */ + /** This packet needs to be classified */ IP_LOOKUP_NEXT_CLASSIFY, - /* This packet needs to go to MAP - RFC7596, RFC7597 */ + /** This packet needs to go to MAP - RFC7596, RFC7597 */ IP_LOOKUP_NEXT_MAP, - /* This packet needs to go to MAP with Translation - RFC7599 */ + /** This packet needs to go to MAP with Translation - RFC7599 */ IP_LOOKUP_NEXT_MAP_T, - /* This packets needs to go to indirect next hop */ + /** This packets needs to go to indirect next hop */ IP_LOOKUP_NEXT_INDIRECT, + /** This packets needs to go to ICMP error */ IP_LOOKUP_NEXT_ICMP_ERROR, IP_LOOKUP_N_NEXT, @@ -191,6 +193,13 @@ typedef struct { u8 opaque[IP_ADJACENCY_OPAQUE_SZ]; }; + /* + * Special format function for this adjacency. + * Specifically good for cases which use the entire rewrite + * for their own purposes. Can easily reduce to a u16 or a u8 if/when + * the first cache line reads "full" on the free space gas gauge. + */ + u32 special_adjacency_format_function_index; /* 0 is invalid */ STRUCT_MARK(signature_end); /* Number of FIB entries sharing this adjacency */ @@ -429,6 +438,9 @@ typedef struct ip_lookup_main_t { /* Either format_ip4_address_and_length or format_ip6_address_and_length. */ format_function_t * format_address_and_length; + /* Special adjacency format functions */ + format_function_t ** special_adjacency_format_functions; + /* Table mapping ip protocol to ip[46]-local node next index. */ u8 local_next_by_ip_protocol[256]; @@ -610,5 +622,7 @@ do { \ } while (0) void ip_lookup_init (ip_lookup_main_t * lm, u32 ip_lookup_node_index); +u32 vnet_register_special_adjacency_format_function +(ip_lookup_main_t * lm, format_function_t * fp); #endif /* included_ip_lookup_h */ diff --git a/vpp-api-test/vat/api_format.c b/vpp-api-test/vat/api_format.c index 90c69a2c..a79a5cce 100644 --- a/vpp-api-test/vat/api_format.c +++ b/vpp-api-test/vat/api_format.c @@ -45,6 +45,7 @@ #include <vnet/map/map.h> #include <vnet/cop/cop.h> #include <vnet/ip/ip6_hop_by_hop.h> +#include <vnet/ip/ip_source_and_port_range_check.h> #include <vnet/policer/xlate.h> #include <vnet/policer/policer.h> #include <vnet/policer/police.h> @@ -3059,14 +3060,16 @@ _(lisp_enable_disable_reply) \ _(lisp_pitr_set_locator_set_reply) \ _(lisp_add_del_map_request_itr_rlocs_reply) \ _(lisp_eid_table_add_del_map_reply) \ -_(vxlan_gpe_add_del_tunnel_reply) \ +_(vxlan_gpe_add_del_tunnel_reply) \ _(af_packet_delete_reply) \ _(policer_classify_set_interface_reply) \ _(netmap_create_reply) \ _(netmap_delete_reply) \ _(ipfix_enable_reply) \ _(pg_capture_reply) \ -_(pg_enable_disable_reply) +_(pg_enable_disable_reply) \ +_(ip_source_and_port_range_check_add_del_reply) \ +_(ip_source_and_port_range_check_interface_add_del_reply) #define _(n) \ static void vl_api_##n##_t_handler \ @@ -3278,7 +3281,11 @@ _(IPFIX_DETAILS, ipfix_details) \ _(GET_NEXT_INDEX_REPLY, get_next_index_reply) \ _(PG_CREATE_INTERFACE_REPLY, pg_create_interface_reply) \ _(PG_CAPTURE_REPLY, pg_capture_reply) \ -_(PG_ENABLE_DISABLE_REPLY, pg_enable_disable_reply) +_(PG_ENABLE_DISABLE_REPLY, pg_enable_disable_reply) \ +_(IP_SOURCE_AND_PORT_RANGE_CHECK_ADD_DEL_REPLY, \ + ip_source_and_port_range_check_add_del_reply) \ +_(IP_SOURCE_AND_PORT_RANGE_CHECK_INTERFACE_ADD_DEL_REPLY, \ + ip_source_and_port_range_check_interface_add_del_reply) /* M: construct, but don't yet send a message */ @@ -13467,6 +13474,177 @@ int api_pg_enable_disable (vat_main_t *vam) return 0; } +int api_ip_source_and_port_range_check_add_del (vat_main_t *vam) +{ + unformat_input_t * input = vam->input; + vl_api_ip_source_and_port_range_check_add_del_t *mp; + f64 timeout; + + u16 * low_ports = 0; + u16 * high_ports = 0; + u16 this_low; + u16 this_hi; + ip4_address_t ip4_addr; + ip6_address_t ip6_addr; + u32 length; + u32 tmp, tmp2; + u8 prefix_set = 0; + u32 vrf_id =~0; + u8 is_add = 1; + u8 is_ipv6 = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%U/%d", unformat_ip4_address, &ip4_addr, &length)) + { + prefix_set = 1; + } + else if (unformat (input, "%U/%d", unformat_ip6_address, &ip6_addr, &length)) + { + prefix_set = 1; + is_ipv6 = 1; + } + else if (unformat (input, "vrf %d", &vrf_id)) + ; + else if (unformat (input, "del")) + is_add = 0; + else if (unformat (input, "port %d", &tmp)) + { + if (tmp == 0 || tmp > 65535) { + errmsg ("port %d out of range", tmp); + return -99; + } + this_low = tmp; + this_hi = this_low + 1; + vec_add1 (low_ports, this_low); + vec_add1 (high_ports, this_hi); + } + else if (unformat (input, "range %d - %d", &tmp, &tmp2)) + { + if ((tmp > tmp2) || + (tmp == 0) || + (tmp2 > 65535)) { + errmsg ("incorrect range parameters\n"); + return -99; + } + this_low = tmp; + /* Note: in debug CLI +1 is added to high before + passing to real fn that does "the work" + (ip_source_and_port_range_check_add_del). + This fn is a wrapper around the binary API fn a + control plane will call, which expects this increment + to have occurred. Hence letting the binary API control + plane fn do the increment for consistency between VAT + and other control planes. + */ + this_hi = tmp2; + vec_add1 (low_ports, this_low); + vec_add1 (high_ports, this_hi); + } + else + break; + } + + if (prefix_set == 0) { + errmsg ("<address>/<mask> not specified\n"); + return -99; + } + + if (vrf_id == ~0) { + errmsg ("VRF ID required, not specified\n"); + return -99; + } + + if (vrf_id == 0) { + errmsg ("VRF ID should not be default. Should be distinct VRF for this purpose.\n"); + return -99; + } + + if (vec_len(low_ports) == 0) { + errmsg ("At least one port or port range required\n"); + return -99; + } + + M(IP_SOURCE_AND_PORT_RANGE_CHECK_ADD_DEL, ip_source_and_port_range_check_add_del); + + mp->is_add = is_add; + + if (is_ipv6) { + mp->is_ipv6 = 1; + clib_memcpy (mp->address, &ip6_addr, sizeof (ip6_addr)); + } else { + mp->is_ipv6 = 0; + clib_memcpy (mp->address, &ip4_addr, sizeof (ip4_addr)); + } + + mp->mask_length = length; + mp->number_of_ranges = vec_len (low_ports); + + clib_memcpy (mp->low_ports, low_ports, vec_len(low_ports)); + vec_free(low_ports); + + clib_memcpy (mp->high_ports, high_ports, vec_len(high_ports)); + vec_free (high_ports); + + mp->vrf_id = ntohl(vrf_id); + + S; W; + /* NOTREACHED */ + return 0; +} + +int api_ip_source_and_port_range_check_interface_add_del (vat_main_t * vam) +{ + unformat_input_t * input = vam->input; + vl_api_ip_source_and_port_range_check_interface_add_del_t *mp; + f64 timeout; + u32 sw_if_index = ~0; + u32 vrf_id = ~0; + u8 is_add = 1; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%U", unformat_sw_if_index, vam, &sw_if_index)) + ; + else if (unformat (input, "sw_if_index %d", &sw_if_index)) + ; + else if (unformat (input, "vrf %d", &vrf_id)) + ; + else if (unformat (input, "del")) + is_add = 0; + else + break; + } + + if (sw_if_index == ~0) { + errmsg ("Interface required but not specified\n"); + return -99; + } + + if (vrf_id == ~0) { + errmsg ("VRF ID required but not specified\n"); + return -99; + } + + if (vrf_id == 0) { + errmsg ("VRF ID should not be default. Should be distinct VRF for this purpose.\n"); + return -99; + } + + /* Construct the API message */ + M(IP_SOURCE_AND_PORT_RANGE_CHECK_INTERFACE_ADD_DEL, ip_source_and_port_range_check_interface_add_del); + + mp->sw_if_index = ntohl (sw_if_index); + mp->is_add = is_add; + mp->vrf_id = ntohl (vrf_id); + + /* send it... */ + S; + + /* Wait for a reply... */ + W; +} + static int q_or_quit (vat_main_t * vam) { longjmp (vam->jump_buf, 1); @@ -13995,7 +14173,11 @@ _(ipfix_dump, "") \ _(get_next_index, "node-name <node-name> next-node-name <node-name>") \ _(pg_create_interface, "if_id <nn>") \ _(pg_capture, "if_id <nnn> pcap <file_name> count <nnn> [disable]") \ -_(pg_enable_disable, "[stream <id>] disable") +_(pg_enable_disable, "[stream <id>] disable") \ +_(ip_source_and_port_range_check_add_del, \ + "<ip-addr>/<mask> range <nn>-<nn> vrf <id>") \ +_(ip_source_and_port_range_check_interface_add_del, \ + "<intf> | sw_if_index <nn> vrf <id>") /* List of command functions, CLI names map directly to functions */ #define foreach_cli_function \ diff --git a/vpp/vpp-api/api.c b/vpp/vpp-api/api.c index dcaedac3..3f48a7ef 100644 --- a/vpp/vpp-api/api.c +++ b/vpp/vpp-api/api.c @@ -77,6 +77,7 @@ #include <vnet/map/map.h> #include <vnet/cop/cop.h> #include <vnet/ip/ip6_hop_by_hop.h> +#include <vnet/ip/ip_source_and_port_range_check.h> #include <vnet/devices/af_packet/af_packet.h> #include <vnet/policer/policer.h> #include <vnet/devices/netmap/netmap.h> @@ -377,7 +378,11 @@ _(IPFIX_DUMP,ipfix_dump) \ _(GET_NEXT_INDEX, get_next_index) \ _(PG_CREATE_INTERFACE, pg_create_interface) \ _(PG_CAPTURE, pg_capture) \ -_(PG_ENABLE_DISABLE, pg_enable_disable) +_(PG_ENABLE_DISABLE, pg_enable_disable) \ +_(IP_SOURCE_AND_PORT_RANGE_CHECK_ADD_DEL, \ + ip_source_and_port_range_check_add_del) \ +_(IP_SOURCE_AND_PORT_RANGE_CHECK_INTERFACE_ADD_DEL, \ + ip_source_and_port_range_check_interface_add_del) #define QUOTE_(x) #x #define QUOTE(x) QUOTE_(x) @@ -7455,6 +7460,125 @@ static void vl_api_pg_enable_disable_t_handler (vl_api_pg_enable_disable_t *mp) REPLY_MACRO(VL_API_PG_ENABLE_DISABLE_REPLY); } +static void vl_api_ip_source_and_port_range_check_add_del_t_handler ( + vl_api_ip_source_and_port_range_check_add_del_t *mp) +{ + vl_api_ip_source_and_port_range_check_add_del_reply_t *rmp; + int rv = 0; + + u8 is_ipv6 = mp->is_ipv6; + u8 is_add = mp->is_add; + u8 mask_length = mp->mask_length; + ip4_address_t ip4_addr; + //ip6_address_t ip6_addr; + u16 * low_ports = 0 ; + u16 * high_ports = 0 ; + u16 tmp_low, tmp_high; + u8 num_ranges ; + int i; + u32 vrf_id; + + // Validate port range + num_ranges = mp->number_of_ranges; + if (num_ranges > 32) { // This is size of array in VPE.API + rv = VNET_API_ERROR_EXCEEDED_NUMBER_OF_RANGES_CAPACITY; + goto reply; + } + + vec_reset_length (low_ports); + vec_reset_length (high_ports); + + for (i = 0; i < num_ranges; i++) { + tmp_low = mp->low_ports[i]; + tmp_high = mp->high_ports[i]; + // If tmp_low <= tmp_high then only need to check tmp_low = 0 + // If tmp_low <= tmp_high then only need to check tmp_high > 65535 + if (tmp_low > tmp_high || tmp_low == 0 || tmp_high > 65535) { + rv = VNET_API_ERROR_INVALID_VALUE; + goto reply; + } + vec_add1 (low_ports, tmp_low ); + vec_add1 (high_ports, tmp_high+1 ); + } + + // Validate mask_length + if (mask_length < 0 || + ( is_ipv6 && mask_length > 128) || + ( !is_ipv6 && mask_length > 32)) { + rv = VNET_API_ERROR_ADDRESS_LENGTH_MISMATCH; + goto reply; + } + + vrf_id = ntohl (mp->vrf_id); + + if ( vrf_id < 1 ) { + rv = VNET_API_ERROR_INVALID_VALUE; + goto reply; + } + //ip6 + if (is_ipv6) { + /* clib_memcpy (ip6_addr.as_u8, mp->address, */ + /* sizeof (ip6_addr.as_u8)); */ + /* rv = ip6_source_and_port_range_check_add_del (ip6_addr, */ + /* mask_length, */ + /* vrf_id, */ + /* low_ports, */ + /* high_ports, */ + /* is_add); */ + + //ip4 + } else { + clib_memcpy (ip4_addr.data, mp->address, + sizeof (ip4_addr)); + rv = ip4_source_and_port_range_check_add_del (&ip4_addr, + mask_length, + vrf_id, + low_ports, + high_ports, + is_add); + } + + reply: + vec_free (low_ports); + vec_free (high_ports); + + REPLY_MACRO(VL_API_IP_SOURCE_AND_PORT_RANGE_CHECK_ADD_DEL_REPLY); +} + +static void +vl_api_ip_source_and_port_range_check_interface_add_del_t_handler +(vl_api_ip_source_and_port_range_check_interface_add_del_t * mp) +{ + vlib_main_t *vm = vlib_get_main(); + vl_api_ip_source_and_port_range_check_interface_add_del_reply_t * rmp; + ip4_main_t * im = &ip4_main; + int rv; + u32 sw_if_index, fib_index, vrf_id; + uword * p = 0; + + vrf_id = ntohl(mp->vrf_id); + + p = hash_get (im->fib_index_by_table_id, vrf_id); + + if (p == 0) { + rv = VNET_API_ERROR_INVALID_VALUE; + goto reply; + } + + fib_index = p[0]; + + sw_if_index = ntohl(mp->sw_if_index); + + VALIDATE_SW_IF_INDEX(mp); + + rv = set_ip_source_and_port_range_check (vm, fib_index, sw_if_index, mp->is_add); + + BAD_SW_IF_INDEX_LABEL; + reply: + + REPLY_MACRO(VL_API_IP_SOURCE_AND_PORT_RANGE_CHECK_INTERFACE_ADD_DEL_REPLY); +} + #define BOUNCE_HANDLER(nn) \ static void vl_api_##nn##_t_handler ( \ vl_api_##nn##_t *mp) \ diff --git a/vpp/vpp-api/custom_dump.c b/vpp/vpp-api/custom_dump.c index a6898eb8..51bf81d9 100644 --- a/vpp/vpp-api/custom_dump.c +++ b/vpp/vpp-api/custom_dump.c @@ -2084,6 +2084,49 @@ static void *vl_api_pg_enable_disable_t_print FINISH; } +static void *vl_api_ip_source_and_port_range_check_add_del_t_print +(vl_api_ip_source_and_port_range_check_add_del_t * mp, void *handle) +{ + u8 * s; + int i; + + s = format (0, "SCRIPT: ip_source_and_port_range_check_add_del "); + if (mp->is_ipv6) + s = format (s, "%U/%d ", format_ip6_address, mp->address, + mp->mask_length); + else + s = format (s, "%U/%d ", format_ip4_address, mp->address, + mp->mask_length); + + for (i = 0; i < mp->number_of_ranges; i++) { + s = format (s, "range %d - %d", mp->low_ports[i], mp->high_ports[i]); + } + + s = format (s, "vrf %d ", ntohl(mp->vrf_id)); + + if (mp->is_add == 0) + s = format (s, "del "); + + FINISH; +} + +static void *vl_api_ip_source_and_port_range_check_interface_add_del_t_print +(vl_api_ip_source_and_port_range_check_interface_add_del_t * mp, void *handle) +{ + u8 * s; + + s = format (0, "SCRIPT: ip_source_and_port_range_check_interface_add_del "); + + s = format (s, "%d ", ntohl(mp->sw_if_index)); + + s = format (s, "vrf %d ", ntohl(mp->vrf_id)); + + if (mp->is_add == 0) + s = format (s, "del "); + + FINISH; +} + #define foreach_custom_print_function \ _(CREATE_LOOPBACK, create_loopback) \ _(SW_INTERFACE_SET_FLAGS, sw_interface_set_flags) \ @@ -2189,7 +2232,12 @@ _(PG_ENABLE_DISABLE, pg_enable_disable) \ _(POLICER_ADD_DEL, policer_add_del) \ _(POLICER_DUMP, policer_dump) \ _(POLICER_CLASSIFY_SET_INTERFACE, policer_classify_set_interface) \ -_(POLICER_CLASSIFY_DUMP, policer_classify_dump) +_(POLICER_CLASSIFY_DUMP, policer_classify_dump) \ +_(IP_SOURCE_AND_PORT_RANGE_CHECK_ADD_DEL, \ + ip_source_and_port_range_check_add_del) \ +_(IP_SOURCE_AND_PORT_RANGE_CHECK_INTERFACE_ADD_DEL, \ + ip_source_and_port_range_check_interface_add_del) + void vl_msg_api_custom_dump_configure (api_main_t *am) { diff --git a/vpp/vpp-api/vpe.api b/vpp/vpp-api/vpe.api index ebadda85..ba7572bc 100644 --- a/vpp/vpp-api/vpe.api +++ b/vpp/vpp-api/vpe.api @@ -4343,3 +4343,60 @@ define pg_enable_disable_reply { u32 context; i32 retval; }; + +/** \brief Configure IP source and L4 port-range check + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_ip6 - 1 if source address type is IPv6 + @param is_add - 1 if add, 0 if delete + @param mask_length - mask length for address entry + @param address - array of address bytes + @param low_ports[32] - up to 32 low end of port range entries (must have corresponding high_ports entry) + @param high_ports[32] - up to 32 high end of port range entries (must have corresponding low_ports entry) + @param vrf_id - fib table/vrf id to associate the source and port-range check with + @note To specify a single port set low_port and high_port entry the same +*/ +define ip_source_and_port_range_check_add_del { + u32 client_index; + u32 context; + u8 is_ipv6; + u8 is_add; + u8 mask_length; + u8 address[16]; + u8 number_of_ranges; + u16 low_ports[32]; + u16 high_ports[32]; + u32 vrf_id; +}; + +/** \brief Configure IP source and L4 port-range check reply + @param context - returned sender context, to match reply w/ request + @param retval - return code +*/ +define ip_source_and_port_range_check_add_del_reply { + u32 context; + i32 retval; +}; + +/** \brief Set interface source and L4 port-range request + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param interface_id - interface index + @param vrf_id - VRF associated with source and L4 port-range check +*/ +define ip_source_and_port_range_check_interface_add_del { + u32 client_index; + u32 context; + u8 is_add; + u32 sw_if_index; + u32 vrf_id; +}; + +/** \brief Set interface source and L4 port-range response + @param context - sender context, to match reply w/ request + @param retval - return value for request +*/ +define ip_source_and_port_range_check_interface_add_del_reply { + u32 context; + i32 retval; +}; |