/* * 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 #include vlib_node_registration_t ip4_source_port_and_range_check; #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; u32 fib_index; } 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, "fib %d src ip %U %s dst port %d: %s", t->fib_index, 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; static inline u32 check_adj_port_range_x1 (ip_adjacency_t * adj, u16 dst_port, u32 next) { protocol_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 = (protocol_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(protocol_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(protocol_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; int i; 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; ip_source_and_port_range_check_config_t * c0, * c1; ip_adjacency_t * adj0 = 0, * adj1 = 0; u32 bi0, next0, adj_index0, pass0, save_next0, fib_index0; u32 bi1, next1, adj_index1, pass1, save_next1, fib_index1; 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); fib_index0 = vec_elt (im->fib_index_by_sw_if_index, vnet_buffer (b0)->sw_if_index[VLIB_RX]); fib_index1 = vec_elt (im->fib_index_by_sw_if_index, vnet_buffer (b1)->sw_if_index[VLIB_RX]); 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... */ for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++) { ASSERT(c0->fib_index[i] && c1->fib_index[i]); } if (ip0->protocol == IP_PROTOCOL_UDP) fib_index0 = c0->fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT]; if (ip0->protocol == IP_PROTOCOL_TCP) fib_index0 = c0->fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT]; if (PREDICT_TRUE(fib_index0 != ~0)) { mtrie0 = &vec_elt_at_index (im->fibs, fib_index0)->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, fib_index0, &ip0->src_address, 0 /* use dflt rt */)); adj0 = ip_get_adjacency (lm, adj_index0); } if (ip1->protocol == IP_PROTOCOL_UDP) fib_index1 = c1->fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT]; if (ip1->protocol == IP_PROTOCOL_TCP) fib_index1 = c1->fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT]; if (PREDICT_TRUE(fib_index1 != ~0)) { mtrie1 = &vec_elt_at_index (im->fibs, fib_index1)->mtrie; leaf1 = IP4_FIB_MTRIE_LEAF_ROOT; leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, &ip1->src_address, 0); leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, &ip1->src_address, 1); leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, &ip1->src_address, 2); leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, &ip1->src_address, 3); adj_index1 = ip4_fib_mtrie_leaf_get_adj_index (leaf1); ASSERT (adj_index1 == ip4_fib_lookup_with_table (im, fib_index1, &ip1->src_address, 0)); adj1 = ip_get_adjacency (lm, adj_index1); } pass0 = 0; pass0 |= adj0 == 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 |= adj1 == 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->fib_index = fib_index0; 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->fib_index = fib_index1; 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; ip_source_and_port_range_check_config_t * c0; ip_adjacency_t * adj0 = 0; u32 bi0, next0, adj_index0, pass0, save_next0, fib_index0; 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); fib_index0 = vec_elt (im->fib_index_by_sw_if_index, vnet_buffer (b0)->sw_if_index[VLIB_RX]); 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... */ for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++) { ASSERT(c0->fib_index[i]); } if (ip0->protocol == IP_PROTOCOL_UDP) fib_index0 = c0->fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT]; if (ip0->protocol == IP_PROTOCOL_TCP) fib_index0 = c0->fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT]; if (fib_index0 != ~0) { mtrie0 = &vec_elt_at_index (im->fibs, fib_index0)->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, fib_index0, &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 |= adj0 == 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->fib_index = fib_index0; 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; ip_source_and_port_range_check_config_t config; u32 feature_index; int rv = 0; int i; for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++) { config.fib_index[i] = fib_index[i]; } 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_add ? vnet_config_add_feature : vnet_config_del_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; u8 is_add = 1; u32 sw_if_index = ~0; u32 vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS]; u32 fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS]; int vrf_set = 0; uword * p; int rv = 0; int i; sw_if_index = ~0; for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++) { fib_index[i] = ~0; vrf_id[i] = ~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, "tcp-out-vrf %d", &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT])) vrf_set = 1; else if (unformat (input, "udp-out-vrf %d", &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT])) vrf_set = 1; else if (unformat (input, "tcp-in-vrf %d", &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN])) vrf_set = 1; else if (unformat (input, "udp-in-vrf %d", &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN])) vrf_set = 1; 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_set) return clib_error_return (0, "TCP or UDP VRF ID required but not specified"); for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++) { if (vrf_id[i] == 0) return clib_error_return (0, "TCP, UDP VRF ID should not be 0 (default). Should be distinct VRF for this purpose. "); if (vrf_id[i] != ~0) { p = hash_get (im->fib_index_by_table_id, vrf_id[i]); if (p == 0) return clib_error_return (0, "Invalid VRF ID %d", vrf_id[i]); fib_index[i] = 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 [tcp-out-vrf ] [udp-out-vrf ] [tcp-in-vrf ] [udp-in-vrf ] [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); protocol_port_range_t * range; int i, j; int printed = 0; range = (protocol_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 add_port_range_adjacency(ip4_address_t * address, u32 length, u32 adj_index, u16 * low_ports, u16 * high_ports, u32 fib_index) { ip_adjacency_t * adj; int i, j, k; source_range_check_main_t * srm = &source_range_check_main; ip4_main_t * im = &ip4_main; ip_lookup_main_t * lm = &im->lookup_main; protocol_port_range_t * range; u8 *rwh; 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 = (protocol_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] = high_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; } int remove_port_range_adjacency(ip4_address_t * address, u32 length, u32 adj_index, u16 * low_ports, u16 * high_ports, u32 fib_index) { ip_adjacency_t * adj; int i, j, k; source_range_check_main_t * srm = &source_range_check_main; ip4_main_t * im = &ip4_main; ip_lookup_main_t * lm = &im->lookup_main; protocol_port_range_t * range; u8 *rwh; adj = ip_get_adjacency (lm, adj_index); if (adj->lookup_next_index != IP_LOOKUP_NEXT_ICMP_ERROR) /* _ICMP_ERROR is a dummy placeholder */ return VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE; rwh = (u8 *)(&adj->rewrite_header); for (i = 0; i < vec_len (low_ports); i++) { range = (protocol_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] && high_ports[i] == range->hi.as_u16[k]) { range->low.as_u16[k] = range->hi.as_u16[k] = 0; goto doublebreak; } } range++; } doublebreak: ; } range = (protocol_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: ; return 0; } // This will be moved to another file and implemented post API freeze. int ip6_source_and_port_range_check_add_del (ip6_address_t * address, u32 length, u32 vrf_id, u16 * low_ports, u16 * high_ports, int is_add) { return 0; } int ip4_source_and_port_range_check_add_del (ip4_address_t * address, u32 length, u32 vrf_id, u16 * low_ports, u16 * high_ports, int is_add) { ip4_main_t * im = &ip4_main; // ip_lookup_main_t * lm = &im->lookup_main; uword * p; u32 fib_index; u32 adj_index; 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) { remove_port_range_adjacency(address, length, adj_index, low_ports, high_ports, fib_index); } else { add_port_range_adjacency(address, length, adj_index, low_ports, high_ports, fib_index); } 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 ip4_addr; ip6_address_t ip6_addr; //This function will be moved to generic impl when v6 done. u32 length; u32 tmp, tmp2; u32 vrf_id = ~0; int is_add = 1, ip_ver = ~0; int rv; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "%U/%d", unformat_ip4_address, &ip4_addr, &length)) ip_ver = 4; else if (unformat (input, "%U/%d", unformat_ip6_address, &ip6_addr, &length)) ip_ver = 6; 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, "high 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 (ip_ver == ~0) return clib_error_return (0, "
/ not specified"); if (vrf_id == ~0) return clib_error_return (0, " VRF ID required, not specified"); if ( vec_len (low_ports) == 0) return clib_error_return (0, " Both VRF ID and range/port must be set for a protocol."); if (vrf_id == 0) return clib_error_return (0, " VRF ID can not be 0 (default)."); if (ip_ver == 4) rv = ip4_source_and_port_range_check_add_del (&ip4_addr, length, vrf_id, low_ports, high_ports, is_add); else return clib_error_return (0, " IPv6 in subsequent patch"); switch(rv) { case 0: break; case VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE: return clib_error_return (0, " Incorrect adjacency for add/del operation"); case VNET_API_ERROR_EXCEEDED_NUMBER_OF_PORTS_CAPACITY: return clib_error_return (0, " Too many ports in add/del operation"); case VNET_API_ERROR_EXCEEDED_NUMBER_OF_RANGES_CAPACITY: return clib_error_return (0, " Too many ranges requested for add operation"); default: return clib_error_return (0, " 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 / [range - tcp-vrf ] [vrf ] [del]", }; 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; protocol_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, "
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 = (protocol_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 ", };