2 * Copyright (c) 2016 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
15 #include <vnet/ip/ip.h>
16 #include <vnet/ip/ip_source_and_port_range_check.h>
17 #include <vnet/dpo/load_balance.h>
18 #include <vnet/fib/fib_table.h>
19 #include <vnet/fib/ip4_fib.h>
23 * @brief IPv4 Source and Port Range Checking.
25 * This file contains the source code for IPv4 source and port range
31 * @brief The pool of range chack DPOs
33 static protocol_port_range_dpo_t *ppr_dpo_pool;
36 * @brief Dynamically registered DPO type
38 static dpo_type_t ppr_dpo_type;
40 vlib_node_registration_t ip4_source_port_and_range_check_rx;
41 vlib_node_registration_t ip4_source_port_and_range_check_tx;
43 #define foreach_ip4_source_and_port_range_check_error \
44 _(CHECK_FAIL, "ip4 source and port range check bad packets") \
45 _(CHECK_OK, "ip4 source and port range check good packets")
49 #define _(sym,str) IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_##sym,
50 foreach_ip4_source_and_port_range_check_error
52 IP4_SOURCE_AND_PORT_RANGE_CHECK_N_ERROR,
53 } ip4_source_and_port_range_check_error_t;
55 static char *ip4_source_and_port_range_check_error_strings[] = {
56 #define _(sym,string) string,
57 foreach_ip4_source_and_port_range_check_error
66 ip4_address_t src_addr;
69 } ip4_source_and_port_range_check_trace_t;
72 format_ip4_source_and_port_range_check_trace (u8 * s, va_list * va)
74 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
75 CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
76 ip4_source_and_port_range_check_trace_t *t =
77 va_arg (*va, ip4_source_and_port_range_check_trace_t *);
80 s = format (s, "PASS (bypass case)");
82 s = format (s, "fib %d src ip %U %s dst port %d: %s",
83 t->fib_index, format_ip4_address, &t->src_addr,
84 t->is_tcp ? "TCP" : "UDP", (u32) t->port,
85 (t->pass == 1) ? "PASS" : "FAIL");
91 IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP,
92 IP4_SOURCE_AND_PORT_RANGE_CHECK_N_NEXT,
93 } ip4_source_and_port_range_check_next_t;
97 check_adj_port_range_x1 (const protocol_port_range_dpo_t * ppr_dpo,
98 u16 dst_port, u32 next)
103 u16x8vec_t sum, sum_equal_diff2;
104 u16 sum_nonzero, sum_equal, winner_mask;
107 if (NULL == ppr_dpo || dst_port == 0)
108 return IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP;
110 /* Make the obvious screw-case work. A variant also works w/ no MMX */
111 if (PREDICT_FALSE (dst_port == 65535))
116 i < VLIB_BUFFER_PRE_DATA_SIZE / sizeof (protocol_port_range_t);
119 for (j = 0; j < 8; j++)
120 if (ppr_dpo->blocks[i].low.as_u16[j] == 65535)
123 return IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP;
126 key.as_u16x8 = u16x8_splat (dst_port);
128 for (i = 0; i < ppr_dpo->n_used_blocks; i++)
131 u16x8_sub_saturate (ppr_dpo->blocks[i].low.as_u16x8, key.as_u16x8);
133 u16x8_sub_saturate (ppr_dpo->blocks[i].hi.as_u16x8, key.as_u16x8);
134 sum.as_u16x8 = u16x8_add (diff1.as_u16x8, diff2.as_u16x8);
135 sum_equal_diff2.as_u16x8 =
136 u16x8_is_equal (sum.as_u16x8, diff2.as_u16x8);
137 sum_nonzero = ~u16x8_zero_byte_mask (sum.as_u16x8);
138 sum_equal = ~u16x8_zero_byte_mask (sum_equal_diff2.as_u16x8);
139 winner_mask = sum_nonzero & sum_equal;
143 return IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP;
146 always_inline protocol_port_range_dpo_t *
147 protocol_port_range_dpo_get (index_t index)
149 return (pool_elt_at_index (ppr_dpo_pool, index));
153 ip4_source_and_port_range_check_inline (vlib_main_t * vm,
154 vlib_node_runtime_t * node,
155 vlib_frame_t * frame, int is_tx)
157 ip4_main_t *im = &ip4_main;
158 ip_lookup_main_t *lm = &im->lookup_main;
159 vnet_feature_config_main_t *rx_cm =
160 &lm->feature_config_mains[VNET_IP_RX_UNICAST_FEAT];
161 vnet_feature_config_main_t *tx_cm =
162 &lm->feature_config_mains[VNET_IP_TX_FEAT];
163 u32 n_left_from, *from, *to_next;
165 vlib_node_runtime_t *error_node = node;
166 u32 good_packets = 0;
169 from = vlib_frame_vector_args (frame);
170 n_left_from = frame->n_vectors;
171 next_index = node->cached_next_index;
173 while (n_left_from > 0)
177 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
180 /* while (n_left_from >= 4 && n_left_to_next >= 2) */
182 /* vlib_buffer_t *b0, *b1; */
183 /* ip4_header_t *ip0, *ip1; */
184 /* ip4_fib_mtrie_t *mtrie0, *mtrie1; */
185 /* ip4_fib_mtrie_leaf_t leaf0, leaf1; */
186 /* ip_source_and_port_range_check_config_t *c0, *c1; */
187 /* ip_adjacency_t *adj0 = 0, *adj1 = 0; */
188 /* u32 bi0, next0, adj_index0, pass0, save_next0, fib_index0; */
189 /* u32 bi1, next1, adj_index1, pass1, save_next1, fib_index1; */
190 /* udp_header_t *udp0, *udp1; */
192 /* /\* Prefetch next iteration. *\/ */
194 /* vlib_buffer_t *p2, *p3; */
196 /* p2 = vlib_get_buffer (vm, from[2]); */
197 /* p3 = vlib_get_buffer (vm, from[3]); */
199 /* vlib_prefetch_buffer_header (p2, LOAD); */
200 /* vlib_prefetch_buffer_header (p3, LOAD); */
202 /* CLIB_PREFETCH (p2->data, sizeof (ip0[0]), LOAD); */
203 /* CLIB_PREFETCH (p3->data, sizeof (ip1[0]), LOAD); */
206 /* bi0 = to_next[0] = from[0]; */
207 /* bi1 = to_next[1] = from[1]; */
210 /* n_left_from -= 2; */
211 /* n_left_to_next -= 2; */
213 /* b0 = vlib_get_buffer (vm, bi0); */
214 /* b1 = vlib_get_buffer (vm, bi1); */
217 /* vec_elt (im->fib_index_by_sw_if_index, */
218 /* vnet_buffer (b0)->sw_if_index[VLIB_RX]); */
220 /* vec_elt (im->fib_index_by_sw_if_index, */
221 /* vnet_buffer (b1)->sw_if_index[VLIB_RX]); */
223 /* ip0 = vlib_buffer_get_current (b0); */
224 /* ip1 = vlib_buffer_get_current (b1); */
228 /* c0 = vnet_get_config_data (&tx_cm->config_main, */
229 /* &b0->current_config_index, */
230 /* &next0, sizeof (c0[0])); */
231 /* c1 = vnet_get_config_data (&tx_cm->config_main, */
232 /* &b1->current_config_index, */
233 /* &next1, sizeof (c1[0])); */
237 /* c0 = vnet_get_config_data (&rx_cm->config_main, */
238 /* &b0->current_config_index, */
239 /* &next0, sizeof (c0[0])); */
240 /* c1 = vnet_get_config_data (&rx_cm->config_main, */
241 /* &b1->current_config_index, */
242 /* &next1, sizeof (c1[0])); */
245 /* /\* we can't use the default VRF here... *\/ */
246 /* for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++) */
248 /* ASSERT (c0->fib_index[i] && c1->fib_index[i]); */
254 /* if (ip0->protocol == IP_PROTOCOL_UDP) */
257 /* [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN]; */
258 /* if (ip0->protocol == IP_PROTOCOL_TCP) */
261 /* [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN]; */
265 /* if (ip0->protocol == IP_PROTOCOL_UDP) */
268 /* [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT]; */
269 /* if (ip0->protocol == IP_PROTOCOL_TCP) */
272 /* [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT]; */
275 /* if (PREDICT_TRUE (fib_index0 != ~0)) */
278 /* mtrie0 = &vec_elt_at_index (im->fibs, fib_index0)->mtrie; */
280 /* leaf0 = IP4_FIB_MTRIE_LEAF_ROOT; */
282 /* leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, */
283 /* &ip0->src_address, 0); */
285 /* leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, */
286 /* &ip0->src_address, 1); */
288 /* leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, */
289 /* &ip0->src_address, 2); */
291 /* leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, */
292 /* &ip0->src_address, 3); */
294 /* adj_index0 = ip4_fib_mtrie_leaf_get_adj_index (leaf0); */
296 /* ASSERT (adj_index0 == ip4_fib_lookup_with_table (im, fib_index0, */
297 /* &ip0->src_address, */
299 /* /\* use dflt rt *\/ */
301 /* adj0 = ip_get_adjacency (lm, adj_index0); */
306 /* if (ip1->protocol == IP_PROTOCOL_UDP) */
309 /* [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN]; */
310 /* if (ip1->protocol == IP_PROTOCOL_TCP) */
313 /* [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN]; */
317 /* if (ip1->protocol == IP_PROTOCOL_UDP) */
320 /* [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT]; */
321 /* if (ip1->protocol == IP_PROTOCOL_TCP) */
324 /* [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT]; */
327 /* if (PREDICT_TRUE (fib_index1 != ~0)) */
330 /* mtrie1 = &vec_elt_at_index (im->fibs, fib_index1)->mtrie; */
332 /* leaf1 = IP4_FIB_MTRIE_LEAF_ROOT; */
334 /* leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, */
335 /* &ip1->src_address, 0); */
337 /* leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, */
338 /* &ip1->src_address, 1); */
340 /* leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, */
341 /* &ip1->src_address, 2); */
343 /* leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, */
344 /* &ip1->src_address, 3); */
346 /* adj_index1 = ip4_fib_mtrie_leaf_get_adj_index (leaf1); */
348 /* ASSERT (adj_index1 == ip4_fib_lookup_with_table (im, fib_index1, */
349 /* &ip1->src_address, */
351 /* adj1 = ip_get_adjacency (lm, adj_index1); */
355 /* pass0 |= adj0 == 0; */
356 /* pass0 |= ip4_address_is_multicast (&ip0->src_address); */
358 /* ip0->src_address.as_u32 == clib_host_to_net_u32 (0xFFFFFFFF); */
359 /* pass0 |= (ip0->protocol != IP_PROTOCOL_UDP) */
360 /* && (ip0->protocol != IP_PROTOCOL_TCP); */
363 /* pass1 |= adj1 == 0; */
364 /* pass1 |= ip4_address_is_multicast (&ip1->src_address); */
366 /* ip1->src_address.as_u32 == clib_host_to_net_u32 (0xFFFFFFFF); */
367 /* pass1 |= (ip1->protocol != IP_PROTOCOL_UDP) */
368 /* && (ip1->protocol != IP_PROTOCOL_TCP); */
370 /* save_next0 = next0; */
371 /* udp0 = ip4_next_header (ip0); */
372 /* save_next1 = next1; */
373 /* udp1 = ip4_next_header (ip1); */
375 /* if (PREDICT_TRUE (pass0 == 0)) */
377 /* good_packets++; */
378 /* next0 = check_adj_port_range_x1 */
379 /* (adj0, clib_net_to_host_u16 (udp0->dst_port), next0); */
380 /* good_packets -= (save_next0 != next0); */
381 /* b0->error = error_node->errors */
382 /* [IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_FAIL]; */
385 /* if (PREDICT_TRUE (pass1 == 0)) */
387 /* good_packets++; */
388 /* next1 = check_adj_port_range_x1 */
389 /* (adj1, clib_net_to_host_u16 (udp1->dst_port), next1); */
390 /* good_packets -= (save_next1 != next1); */
391 /* b1->error = error_node->errors */
392 /* [IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_FAIL]; */
395 /* if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) */
396 /* && (b0->flags & VLIB_BUFFER_IS_TRACED))) */
398 /* ip4_source_and_port_range_check_trace_t *t = */
399 /* vlib_add_trace (vm, node, b0, sizeof (*t)); */
400 /* t->pass = next0 == save_next0; */
401 /* t->bypass = pass0; */
402 /* t->fib_index = fib_index0; */
403 /* t->src_addr.as_u32 = ip0->src_address.as_u32; */
404 /* t->port = (pass0 == 0) ? */
405 /* clib_net_to_host_u16 (udp0->dst_port) : 0; */
406 /* t->is_tcp = ip0->protocol == IP_PROTOCOL_TCP; */
409 /* if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) */
410 /* && (b1->flags & VLIB_BUFFER_IS_TRACED))) */
412 /* ip4_source_and_port_range_check_trace_t *t = */
413 /* vlib_add_trace (vm, node, b1, sizeof (*t)); */
414 /* t->pass = next1 == save_next1; */
415 /* t->bypass = pass1; */
416 /* t->fib_index = fib_index1; */
417 /* t->src_addr.as_u32 = ip1->src_address.as_u32; */
418 /* t->port = (pass1 == 0) ? */
419 /* clib_net_to_host_u16 (udp1->dst_port) : 0; */
420 /* t->is_tcp = ip1->protocol == IP_PROTOCOL_TCP; */
423 /* vlib_validate_buffer_enqueue_x2 (vm, node, next_index, */
424 /* to_next, n_left_to_next, */
425 /* bi0, bi1, next0, next1); */
428 while (n_left_from > 0 && n_left_to_next > 0)
432 ip_source_and_port_range_check_config_t *c0;
433 u32 bi0, next0, lb_index0, pass0, save_next0, fib_index0;
435 const protocol_port_range_dpo_t *ppr_dpo0 = NULL;
445 b0 = vlib_get_buffer (vm, bi0);
448 vec_elt (im->fib_index_by_sw_if_index,
449 vnet_buffer (b0)->sw_if_index[VLIB_RX]);
452 vlib_buffer_advance (b0, sizeof (ethernet_header_t));
454 ip0 = vlib_buffer_get_current (b0);
458 c0 = vnet_get_config_data
459 (&tx_cm->config_main, &b0->current_config_index,
460 &next0, sizeof (c0[0]));
464 c0 = vnet_get_config_data
465 (&rx_cm->config_main, &b0->current_config_index,
466 &next0, sizeof (c0[0]));
469 /* we can't use the default VRF here... */
470 for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++)
472 ASSERT (c0->fib_index[i]);
478 if (ip0->protocol == IP_PROTOCOL_UDP)
481 [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN];
482 if (ip0->protocol == IP_PROTOCOL_TCP)
485 [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN];
489 if (ip0->protocol == IP_PROTOCOL_UDP)
492 [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT];
493 if (ip0->protocol == IP_PROTOCOL_TCP)
496 [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT];
499 if (fib_index0 != ~0)
501 lb_index0 = ip4_fib_forwarding_lookup (fib_index0,
505 load_balance_get_bucket_i (load_balance_get (lb_index0), 0);
507 if (ppr_dpo_type == dpo->dpoi_type)
509 ppr_dpo0 = protocol_port_range_dpo_get (dpo->dpoi_index);
512 * else the lookup hit an enty that was no inserted
513 * by this range checker, which is the default route
517 * $$$ which (src,dst) categories should we always pass?
520 pass0 |= ip4_address_is_multicast (&ip0->src_address);
522 ip0->src_address.as_u32 == clib_host_to_net_u32 (0xFFFFFFFF);
523 pass0 |= (ip0->protocol != IP_PROTOCOL_UDP)
524 && (ip0->protocol != IP_PROTOCOL_TCP);
527 udp0 = ip4_next_header (ip0);
529 if (PREDICT_TRUE (pass0 == 0))
532 next0 = check_adj_port_range_x1
533 (ppr_dpo0, clib_net_to_host_u16 (udp0->dst_port), next0);
534 good_packets -= (save_next0 != next0);
535 b0->error = error_node->errors
536 [IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_FAIL];
539 if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
540 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
542 ip4_source_and_port_range_check_trace_t *t =
543 vlib_add_trace (vm, node, b0, sizeof (*t));
544 t->pass = next0 == save_next0;
546 t->fib_index = fib_index0;
547 t->src_addr.as_u32 = ip0->src_address.as_u32;
548 t->port = (pass0 == 0) ?
549 clib_net_to_host_u16 (udp0->dst_port) : 0;
550 t->is_tcp = ip0->protocol == IP_PROTOCOL_TCP;
554 vlib_buffer_advance (b0, -sizeof (ethernet_header_t));
556 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
557 to_next, n_left_to_next,
561 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
565 vlib_node_increment_counter (vm, ip4_source_port_and_range_check_tx.index,
566 IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_OK,
569 vlib_node_increment_counter (vm, ip4_source_port_and_range_check_rx.index,
570 IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_OK,
572 return frame->n_vectors;
577 ip4_source_and_port_range_check_rx (vlib_main_t * vm,
578 vlib_node_runtime_t * node,
579 vlib_frame_t * frame)
581 return ip4_source_and_port_range_check_inline (vm, node, frame,
586 ip4_source_and_port_range_check_tx (vlib_main_t * vm,
587 vlib_node_runtime_t * node,
588 vlib_frame_t * frame)
590 return ip4_source_and_port_range_check_inline (vm, node, frame,
594 /* Note: Calling same function for both RX and TX nodes
595 as always checking dst_port, although
596 if this changes can easily make new function
600 VLIB_REGISTER_NODE (ip4_source_port_and_range_check_rx) = {
601 .function = ip4_source_and_port_range_check_rx,
602 .name = "ip4-source-and-port-range-check-rx",
603 .vector_size = sizeof (u32),
605 .n_errors = ARRAY_LEN(ip4_source_and_port_range_check_error_strings),
606 .error_strings = ip4_source_and_port_range_check_error_strings,
608 .n_next_nodes = IP4_SOURCE_AND_PORT_RANGE_CHECK_N_NEXT,
610 [IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP] = "error-drop",
613 .format_buffer = format_ip4_header,
614 .format_trace = format_ip4_source_and_port_range_check_trace,
619 VLIB_REGISTER_NODE (ip4_source_port_and_range_check_tx) = {
620 .function = ip4_source_and_port_range_check_tx,
621 .name = "ip4-source-and-port-range-check-tx",
622 .vector_size = sizeof (u32),
624 .n_errors = ARRAY_LEN(ip4_source_and_port_range_check_error_strings),
625 .error_strings = ip4_source_and_port_range_check_error_strings,
627 .n_next_nodes = IP4_SOURCE_AND_PORT_RANGE_CHECK_N_NEXT,
629 [IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP] = "error-drop",
632 .format_buffer = format_ip4_header,
633 .format_trace = format_ip4_source_and_port_range_check_trace,
638 set_ip_source_and_port_range_check (vlib_main_t * vm,
640 u32 sw_if_index, u32 is_add)
642 ip4_main_t *im = &ip4_main;
643 ip_lookup_main_t *lm = &im->lookup_main;
644 vnet_feature_config_main_t *rx_cm =
645 &lm->feature_config_mains[VNET_IP_RX_UNICAST_FEAT];
646 vnet_feature_config_main_t *tx_cm =
647 &lm->feature_config_mains[VNET_IP_TX_FEAT];
649 ip_source_and_port_range_check_config_t config;
654 for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++)
656 config.fib_index[i] = fib_index[i];
659 /* For OUT we are in the RX path */
660 if ((fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT] != ~0) ||
661 (fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT] != ~0))
663 feature_index = im->ip4_unicast_rx_feature_source_and_port_range_check;
665 vec_validate (rx_cm->config_index_by_sw_if_index, sw_if_index);
667 ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
669 ? vnet_config_add_feature
670 : vnet_config_del_feature)
671 (vm, &rx_cm->config_main, ci, feature_index, &config,
673 rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
676 /* For IN we are in the TX path */
677 if ((fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN] != ~0) ||
678 (fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN] != ~0))
680 feature_index = im->ip4_unicast_tx_feature_source_and_port_range_check;
682 vec_validate (tx_cm->config_index_by_sw_if_index, sw_if_index);
684 ci = tx_cm->config_index_by_sw_if_index[sw_if_index];
686 ? vnet_config_add_feature
687 : vnet_config_del_feature)
688 (vm, &tx_cm->config_main, ci, feature_index, &config,
690 tx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
692 vnet_config_update_tx_feature_count (lm, tx_cm, sw_if_index, is_add);
697 static clib_error_t *
698 set_ip_source_and_port_range_check_fn (vlib_main_t * vm,
699 unformat_input_t * input,
700 vlib_cli_command_t * cmd)
702 vnet_main_t *vnm = vnet_get_main ();
703 ip4_main_t *im = &ip4_main;
704 clib_error_t *error = 0;
706 u32 sw_if_index = ~0;
707 u32 vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS];
708 u32 fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS];
715 for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++)
721 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
723 if (unformat (input, "%U", unformat_vnet_sw_interface, vnm,
728 (input, "tcp-out-vrf %d",
729 &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT]))
733 (input, "udp-out-vrf %d",
734 &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT]))
738 (input, "tcp-in-vrf %d",
739 &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN]))
743 (input, "udp-in-vrf %d",
744 &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN]))
746 else if (unformat (input, "del"))
752 if (sw_if_index == ~0)
753 return clib_error_return (0, "Interface required but not specified");
756 return clib_error_return (0,
757 "TCP or UDP VRF ID required but not specified");
759 for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++)
763 return clib_error_return (0,
764 "TCP, UDP VRF ID should not be 0 (default). Should be distinct VRF for this purpose. ");
768 p = hash_get (im->fib_index_by_table_id, vrf_id[i]);
771 return clib_error_return (0, "Invalid VRF ID %d", vrf_id[i]);
777 set_ip_source_and_port_range_check (vm, fib_index, sw_if_index, is_add);
785 return clib_error_return
787 "set source and port-range on interface returned an unexpected value: %d",
794 * Add the 'ip4-source-and-port-range-check-rx' or
795 * 'ip4-source-and-port-range-check-tx' graph node for a given
796 * interface. 'tcp-out-vrf' and 'udp-out-vrf' will add to
797 * the RX path. 'tcp-in-vrf' and 'udp-in-vrf' will add to
798 * the TX path. A graph node will be inserted into the chain when
799 * the range check is added to the first interface. It will not
800 * be removed from when range check is removed from the last
803 * By adding the range check graph node to the interface, incoming
804 * or outgoing TCP/UDP packets will be validated using the
805 * provided IPv4 FIB table (VRF).
807 * @note 'ip4-source-and-port-range-check-rx' and
808 * 'ip4-source-and-port-range-check-tx' strings are too long, so
809 * they are truncated on the 'show vlib graph' output.
811 * @todo This content needs to be validated and potentially more detail added.
815 * Example of graph node before range checking is enabled:
816 * @cliexstart{show vlib graph ip4-source-and-port-range-check-tx}
818 * ip4-source-and-port-range- error-drop [0]
821 * Example of how to enable range checking on TX:
822 * @cliexcmd{set interface ip source-and-port-range-check GigabitEthernet2/0/0 udp-in-vrf 7}
824 * Example of graph node after range checking is enabled:
825 * @cliexstart{show vlib graph ip4-source-and-port-range-check-tx}
827 * ip4-source-and-port-range- error-drop [0] ip4-rewrite-local
828 * interface-output [1] ip4-rewrite-transit
831 * Example of how to display the features enabed on an interface:
832 * @cliexstart{show ip interface features GigabitEthernet2/0/0}
833 * IP feature paths configured on GigabitEthernet2/0/0...
836 * ip4-source-and-port-range-check-rx
840 * ip4-lookup-multicast
857 VLIB_CLI_COMMAND (set_interface_ip_source_and_port_range_check_command, static) = {
858 .path = "set interface ip source-and-port-range-check",
859 .function = set_ip_source_and_port_range_check_fn,
860 .short_help = "set interface ip source-and-port-range-check <interface> [tcp-out-vrf <table-id>] [udp-out-vrf <table-id>] [tcp-in-vrf <table-id>] [udp-in-vrf <table-id>] [del]",
865 format_ppr_dpo (u8 * s, va_list * args)
867 index_t index = va_arg (*args, index_t);
868 CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
870 protocol_port_range_dpo_t *ppr_dpo;
874 ppr_dpo = protocol_port_range_dpo_get (index);
876 s = format (s, "allow ");
878 for (i = 0; i < ppr_dpo->n_used_blocks; i++)
880 for (j = 0; j < 8; j++)
882 if (ppr_dpo->blocks[i].low.as_u16[j])
885 s = format (s, ", ");
886 if (ppr_dpo->blocks[i].hi.as_u16[j] >
887 (ppr_dpo->blocks[i].low.as_u16[j] + 1))
889 format (s, "%d-%d", (u32) ppr_dpo->blocks[i].low.as_u16[j],
890 (u32) ppr_dpo->blocks[i].hi.as_u16[j] - 1);
892 s = format (s, "%d", ppr_dpo->blocks[i].low.as_u16[j]);
901 ppr_dpo_lock (dpo_id_t * dpo)
906 ppr_dpo_unlock (dpo_id_t * dpo)
910 const static dpo_vft_t ppr_vft = {
911 .dv_lock = ppr_dpo_lock,
912 .dv_unlock = ppr_dpo_unlock,
913 .dv_format = format_ppr_dpo,
916 const static char *const ppr_ip4_nodes[] = {
917 "ip4-source-and-port-range-check-rx",
921 const static char *const *const ppr_nodes[DPO_PROTO_NUM] = {
922 [DPO_PROTO_IP4] = ppr_ip4_nodes,
926 ip4_source_and_port_range_check_init (vlib_main_t * vm)
928 source_range_check_main_t *srm = &source_range_check_main;
931 srm->vnet_main = vnet_get_main ();
933 ppr_dpo_type = dpo_register_new_type (&ppr_vft, ppr_nodes);
938 VLIB_INIT_FUNCTION (ip4_source_and_port_range_check_init);
940 protocol_port_range_dpo_t *
941 protocol_port_range_dpo_alloc (void)
943 protocol_port_range_dpo_t *ppr_dpo;
945 pool_get_aligned (ppr_dpo_pool, ppr_dpo, CLIB_CACHE_LINE_BYTES);
946 memset (ppr_dpo, 0, sizeof (*ppr_dpo));
948 ppr_dpo->n_free_ranges = N_PORT_RANGES_PER_DPO;
955 add_port_range_adjacency (u32 fib_index,
956 ip4_address_t * address,
957 u32 length, u16 * low_ports, u16 * high_ports)
959 protocol_port_range_dpo_t *ppr_dpo;
960 dpo_id_t dpop = DPO_INVALID;
963 fib_node_index_t fei;
965 .fp_proto = FIB_PROTOCOL_IP4,
973 * check to see if we have already sourced this prefix
975 fei = fib_table_lookup_exact_match (fib_index, &pfx);
977 if (FIB_NODE_INDEX_INVALID == fei)
980 * this is a first time add for this prefix.
982 ppr_dpo = protocol_port_range_dpo_alloc ();
987 * the prefix is already there.
988 * check it was sourced by us, and if so get the ragne DPO from it.
990 dpo_id_t dpo = DPO_INVALID;
991 const dpo_id_t *bucket;
993 if (fib_entry_get_dpo_for_source (fei, FIB_SOURCE_SPECIAL, &dpo))
996 * there is existing state. we'll want to add the new ranges to it
999 load_balance_get_bucket_i (load_balance_get (dpo.dpoi_index), 0);
1000 ppr_dpo = protocol_port_range_dpo_get (bucket->dpoi_index);
1006 * there is no PPR state associated with this prefix,
1007 * so we'll need a new DPO
1009 ppr_dpo = protocol_port_range_dpo_alloc ();
1013 if (vec_len (low_ports) > ppr_dpo->n_free_ranges)
1014 return VNET_API_ERROR_EXCEEDED_NUMBER_OF_RANGES_CAPACITY;
1018 for (i = 0; i < vec_len (low_ports); i++)
1020 for (; j < N_BLOCKS_PER_DPO; j++)
1024 if (ppr_dpo->blocks[j].low.as_u16[k] == 0)
1026 ppr_dpo->blocks[j].low.as_u16[k] = low_ports[i];
1027 ppr_dpo->blocks[j].hi.as_u16[k] = high_ports[i];
1034 ppr_dpo->n_used_blocks = j + 1;
1037 * add or update the entry in the FIB
1039 dpo_set (&dpop, ppr_dpo_type, DPO_PROTO_IP4, (ppr_dpo - ppr_dpo_pool));
1041 if (FIB_NODE_INDEX_INVALID == fei)
1043 fib_table_entry_special_dpo_add (fib_index,
1046 FIB_ENTRY_FLAG_NONE, &dpop);
1050 fib_entry_special_update (fei,
1052 FIB_ENTRY_FLAG_NONE, &dpop);
1059 remove_port_range_adjacency (u32 fib_index,
1060 ip4_address_t * address,
1061 u32 length, u16 * low_ports, u16 * high_ports)
1063 protocol_port_range_dpo_t *ppr_dpo;
1064 fib_node_index_t fei;
1067 fib_prefix_t pfx = {
1068 .fp_proto = FIB_PROTOCOL_IP4,
1076 * check to see if we have sourced this prefix
1078 fei = fib_table_lookup_exact_match (fib_index, &pfx);
1080 if (FIB_NODE_INDEX_INVALID == fei)
1085 return VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE;
1090 * the prefix is already there.
1091 * check it was sourced by us
1093 dpo_id_t dpo = DPO_INVALID;
1094 const dpo_id_t *bucket;
1096 if (fib_entry_get_dpo_for_source (fei, FIB_SOURCE_SPECIAL, &dpo))
1099 * there is existing state. we'll want to add the new ranges to it
1102 load_balance_get_bucket_i (load_balance_get (dpo.dpoi_index), 0);
1103 ppr_dpo = protocol_port_range_dpo_get (bucket->dpoi_index);
1111 return VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE;
1115 for (i = 0; i < vec_len (low_ports); i++)
1117 for (j = 0; j < N_BLOCKS_PER_DPO; j++)
1119 for (k = 0; k < 8; k++)
1121 if (low_ports[i] == ppr_dpo->blocks[j].low.as_u16[k] &&
1122 high_ports[i] == ppr_dpo->blocks[j].hi.as_u16[k])
1124 ppr_dpo->blocks[j].low.as_u16[k] =
1125 ppr_dpo->blocks[j].hi.as_u16[k] = 0;
1133 ppr_dpo->n_free_ranges = 0;
1135 /* Have we deleted all ranges yet? */
1136 for (i = 0; i < N_BLOCKS_PER_DPO; i++)
1138 for (j = 0; j < 8; j++)
1140 if (ppr_dpo->blocks[j].low.as_u16[i] == 0)
1141 ppr_dpo->n_free_ranges++;
1145 if (N_PORT_RANGES_PER_DPO == ppr_dpo->n_free_ranges)
1147 /* Yes, lose the adjacency... */
1148 fib_table_entry_special_remove (fib_index, &pfx, FIB_SOURCE_SPECIAL);
1153 * compact the ranges down to a contiguous block
1161 // This will be moved to another file and implemented post API freeze.
1163 ip6_source_and_port_range_check_add_del (ip6_address_t * address,
1167 u16 * high_ports, int is_add)
1173 ip4_source_and_port_range_check_add_del (ip4_address_t * address,
1177 u16 * high_ports, int is_add)
1181 fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id);
1185 remove_port_range_adjacency (fib_index, address, length,
1186 low_ports, high_ports);
1190 add_port_range_adjacency (fib_index, address, length,
1191 low_ports, high_ports);
1197 static clib_error_t *
1198 ip_source_and_port_range_check_command_fn (vlib_main_t * vm,
1199 unformat_input_t * input,
1200 vlib_cli_command_t * cmd)
1203 u16 *high_ports = 0;
1206 ip4_address_t ip4_addr;
1207 ip6_address_t ip6_addr; //This function will be moved to generic impl when v6 done.
1211 int is_add = 1, ip_ver = ~0;
1215 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1217 if (unformat (input, "%U/%d", unformat_ip4_address, &ip4_addr, &length))
1221 (input, "%U/%d", unformat_ip6_address, &ip6_addr, &length))
1223 else if (unformat (input, "vrf %d", &vrf_id))
1225 else if (unformat (input, "del"))
1227 else if (unformat (input, "port %d", &tmp))
1229 if (tmp == 0 || tmp > 65535)
1230 return clib_error_return (0, "port %d out of range", tmp);
1232 this_hi = this_low + 1;
1233 vec_add1 (low_ports, this_low);
1234 vec_add1 (high_ports, this_hi);
1236 else if (unformat (input, "range %d - %d", &tmp, &tmp2))
1239 return clib_error_return (0, "ports %d and %d out of order",
1241 if (tmp == 0 || tmp > 65535)
1242 return clib_error_return (0, "low port %d out of range", tmp);
1243 if (tmp2 == 0 || tmp2 > 65535)
1244 return clib_error_return (0, "high port %d out of range", tmp2);
1247 vec_add1 (low_ports, this_low);
1248 vec_add1 (high_ports, this_hi);
1255 return clib_error_return (0, " <address>/<mask> not specified");
1258 return clib_error_return (0, " VRF ID required, not specified");
1260 if (vec_len (low_ports) == 0)
1261 return clib_error_return (0,
1262 " Both VRF ID and range/port must be set for a protocol.");
1265 return clib_error_return (0, " VRF ID can not be 0 (default).");
1269 rv = ip4_source_and_port_range_check_add_del
1270 (&ip4_addr, length, vrf_id, low_ports, high_ports, is_add);
1272 return clib_error_return (0, " IPv6 in subsequent patch");
1279 case VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE:
1280 return clib_error_return
1281 (0, " Incorrect adjacency for add/del operation");
1283 case VNET_API_ERROR_EXCEEDED_NUMBER_OF_PORTS_CAPACITY:
1284 return clib_error_return (0, " Too many ports in add/del operation");
1286 case VNET_API_ERROR_EXCEEDED_NUMBER_OF_RANGES_CAPACITY:
1287 return clib_error_return
1288 (0, " Too many ranges requested for add operation");
1291 return clib_error_return (0, " returned an unexpected value: %d", rv);
1298 * This command adds an IP Subnet and range of ports to be validated
1299 * by an IP FIB table (VRF).
1301 * @todo This is incomplete. This needs a detailed description and a
1302 * practical example.
1305 * Example of how to add an IPv4 subnet and single port to an IPv4 FIB table:
1306 * @cliexcmd{set ip source-and-port-range-check vrf 7 172.16.1.0/24 port 23}
1307 * Example of how to add an IPv4 subnet and range of ports to an IPv4 FIB table:
1308 * @cliexcmd{set ip source-and-port-range-check vrf 7 172.16.1.0/24 range 23 - 100}
1309 * Example of how to delete an IPv4 subnet and single port from an IPv4 FIB table:
1310 * @cliexcmd{set ip source-and-port-range-check vrf 7 172.16.1.0/24 port 23 del}
1311 * Example of how to delete an IPv4 subnet and range of ports from an IPv4 FIB table:
1312 * @cliexcmd{set ip source-and-port-range-check vrf 7 172.16.1.0/24 range 23 - 100 del}
1315 VLIB_CLI_COMMAND (ip_source_and_port_range_check_command, static) = {
1316 .path = "set ip source-and-port-range-check",
1317 .function = ip_source_and_port_range_check_command_fn,
1319 "set ip source-and-port-range-check vrf <table-id> <ip-addr>/<mask> {port nn | range <nn> - <nn>} [del]",
1324 static clib_error_t *
1325 show_source_and_port_range_check_fn (vlib_main_t * vm,
1326 unformat_input_t * input,
1327 vlib_cli_command_t * cmd)
1329 protocol_port_range_dpo_t *ppr_dpo;
1335 fib_prefix_t pfx = {
1336 .fp_proto = FIB_PROTOCOL_IP4,
1340 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1342 if (unformat (input, "%U", unformat_ip4_address, &pfx.fp_addr.ip4))
1344 else if (unformat (input, "vrf %d", &vrf_id))
1346 else if (unformat (input, "port %d", &port))
1353 return clib_error_return (0, "<address> not specified");
1356 return clib_error_return (0, "VRF ID required, not specified");
1358 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
1359 if (~0 == fib_index)
1360 return clib_error_return (0, "VRF %d not found", vrf_id);
1363 * find the longest prefix match on the address requested,
1364 * check it was sourced by us
1366 dpo_id_t dpo = DPO_INVALID;
1367 const dpo_id_t *bucket;
1369 if (!fib_entry_get_dpo_for_source (fib_table_lookup (fib_index, &pfx),
1370 FIB_SOURCE_SPECIAL, &dpo))
1375 vlib_cli_output (vm, "%U: src address drop", format_ip4_address,
1380 bucket = load_balance_get_bucket_i (load_balance_get (dpo.dpoi_index), 0);
1381 ppr_dpo = protocol_port_range_dpo_get (bucket->dpoi_index);
1386 rv = check_adj_port_range_x1 (ppr_dpo, (u16) port, 1234);
1388 vlib_cli_output (vm, "%U port %d PASS", format_ip4_address,
1389 &pfx.fp_addr.ip4, port);
1391 vlib_cli_output (vm, "%U port %d FAIL", format_ip4_address,
1392 &pfx.fp_addr.ip4, port);
1399 s = format (0, "%U: ", format_ip4_address, &pfx.fp_addr.ip4);
1401 for (i = 0; i < N_BLOCKS_PER_DPO; i++)
1403 for (j = 0; j < 8; j++)
1405 if (ppr_dpo->blocks[i].low.as_u16[j])
1406 s = format (s, "%d - %d ",
1407 (u32) ppr_dpo->blocks[i].low.as_u16[j],
1408 (u32) ppr_dpo->blocks[i].hi.as_u16[j]);
1411 vlib_cli_output (vm, "%s", s);
1419 * Display the range of ports being validated by an IPv4 FIB for a given
1420 * IP or subnet, or test if a given IP and port are being validated.
1422 * @todo This is incomplete. This needs a detailed description and a
1423 * practical example.
1426 * Example of how to display the set of ports being validated for a given
1428 * @cliexstart{show ip source-and-port-range-check vrf 7 172.16.2.0}
1429 * 172.16.2.0: 23 - 101
1431 * Example of how to test to determine of a given Pv4 address and port
1432 * are being validated:
1433 * @cliexstart{show ip source-and-port-range-check vrf 7 172.16.2.2 port 23}
1434 * 172.16.2.2 port 23 PASS
1436 * @cliexstart{show ip source-and-port-range-check vrf 7 172.16.2.2 port 250}
1437 * 172.16.2.2 port 250 FAIL
1441 VLIB_CLI_COMMAND (show_source_and_port_range_check, static) = {
1442 .path = "show ip source-and-port-range-check",
1443 .function = show_source_and_port_range_check_fn,
1445 "show ip source-and-port-range-check vrf <table-id> <ip-addr> [port <n>]",
1450 * fd.io coding-style-patch-verification: ON
1453 * eval: (c-set-style "gnu")