LCOV - code coverage report
Current view: top level - vnet/ip - ip4_source_and_port_range_check.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 12 356 3.4 %
Date: 2023-10-26 01:39:38 Functions: 13 31 41.9 %

          Line data    Source code
       1             : /*
       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:
       6             :  *
       7             :  *         http://www.apache.org/licenses/LICENSE-2.0
       8             :  *
       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.
      14             :  */
      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>
      20             : 
      21             : source_range_check_main_t source_range_check_main;
      22             : 
      23             : /**
      24             :  * @file
      25             :  * @brief IPv4 Source and Port Range Checking.
      26             :  *
      27             :  * This file contains the source code for IPv4 source and port range
      28             :  * checking.
      29             :  */
      30             : 
      31             : 
      32             : /**
      33             :  * @brief The pool of range chack DPOs
      34             :  */
      35             : static protocol_port_range_dpo_t *ppr_dpo_pool;
      36             : 
      37             : /**
      38             :  * @brief Dynamically registered DPO type
      39             :  */
      40             : static dpo_type_t ppr_dpo_type;
      41             : 
      42             : vlib_node_registration_t ip4_source_port_and_range_check_rx;
      43             : vlib_node_registration_t ip4_source_port_and_range_check_tx;
      44             : 
      45             : #define foreach_ip4_source_and_port_range_check_error                   \
      46             :   _(CHECK_FAIL, "ip4 source and port range check bad packets")        \
      47             :   _(CHECK_OK, "ip4 source and port range check good packets")
      48             : 
      49             : typedef enum
      50             : {
      51             : #define _(sym,str) IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_##sym,
      52             :   foreach_ip4_source_and_port_range_check_error
      53             : #undef _
      54             :     IP4_SOURCE_AND_PORT_RANGE_CHECK_N_ERROR,
      55             : } ip4_source_and_port_range_check_error_t;
      56             : 
      57             : static char *ip4_source_and_port_range_check_error_strings[] = {
      58             : #define _(sym,string) string,
      59             :   foreach_ip4_source_and_port_range_check_error
      60             : #undef _
      61             : };
      62             : 
      63             : typedef struct
      64             : {
      65             :   u32 pass;
      66             :   u32 bypass;
      67             :   u32 is_tcp;
      68             :   ip4_address_t src_addr;
      69             :   u16 port;
      70             :   u32 fib_index;
      71             : } ip4_source_and_port_range_check_trace_t;
      72             : 
      73             : static u8 *
      74           0 : format_ip4_source_and_port_range_check_trace (u8 * s, va_list * va)
      75             : {
      76           0 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
      77           0 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
      78           0 :   ip4_source_and_port_range_check_trace_t *t =
      79             :     va_arg (*va, ip4_source_and_port_range_check_trace_t *);
      80             : 
      81           0 :   if (t->bypass)
      82           0 :     s = format (s, "PASS (bypass case)");
      83             :   else
      84           0 :     s = format (s, "fib %d src ip %U %s dst port %d: %s",
      85             :                 t->fib_index, format_ip4_address, &t->src_addr,
      86           0 :                 t->is_tcp ? "TCP" : "UDP", (u32) t->port,
      87           0 :                 (t->pass == 1) ? "PASS" : "FAIL");
      88           0 :   return s;
      89             : }
      90             : 
      91             : typedef enum
      92             : {
      93             :   IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP,
      94             :   IP4_SOURCE_AND_PORT_RANGE_CHECK_N_NEXT,
      95             : } ip4_source_and_port_range_check_next_t;
      96             : 
      97             : 
      98             : static inline u32
      99           0 : check_adj_port_range_x1 (const protocol_port_range_dpo_t * ppr_dpo,
     100             :                          u16 dst_port, u32 next)
     101             : {
     102             : #ifdef CLIB_HAVE_VEC128
     103           0 :   u16x8 key = u16x8_splat (dst_port);
     104             : #endif
     105             :   int i;
     106             : 
     107           0 :   if (NULL == ppr_dpo || dst_port == 0)
     108           0 :     return IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP;
     109             : 
     110             : 
     111           0 :   for (i = 0; i < ppr_dpo->n_used_blocks; i++)
     112             : #ifdef CLIB_HAVE_VEC128
     113           0 :     if (!u16x8_is_all_zero ((ppr_dpo->blocks[i].low.as_u16x8 <= key) &
     114           0 :                             (ppr_dpo->blocks[i].hi.as_u16x8 >= key)))
     115           0 :       return next;
     116             : #else
     117             :     {
     118             :       for (int j = 0; j < 8; j++)
     119             :         {
     120             :           if ((ppr_dpo->blocks[i].low.as_u16[j] <= dst_port) &&
     121             :               (ppr_dpo->blocks[i].hi.as_u16[j] >= dst_port))
     122             :             return next;
     123             :         }
     124             :     };
     125             : #endif
     126             : 
     127           0 :   return IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP;
     128             : }
     129             : 
     130             : always_inline protocol_port_range_dpo_t *
     131           0 : protocol_port_range_dpo_get (index_t index)
     132             : {
     133           0 :   return (pool_elt_at_index (ppr_dpo_pool, index));
     134             : }
     135             : 
     136             : always_inline uword
     137           0 : ip4_source_and_port_range_check_inline (vlib_main_t * vm,
     138             :                                         vlib_node_runtime_t * node,
     139             :                                         vlib_frame_t * frame, int is_tx)
     140             : {
     141           0 :   ip4_main_t *im = &ip4_main;
     142             :   u32 n_left_from, *from, *to_next;
     143             :   u32 next_index;
     144           0 :   vlib_node_runtime_t *error_node = node;
     145           0 :   u32 good_packets = 0;
     146             :   int i;
     147             : 
     148           0 :   from = vlib_frame_vector_args (frame);
     149           0 :   n_left_from = frame->n_vectors;
     150           0 :   next_index = node->cached_next_index;
     151             : 
     152           0 :   while (n_left_from > 0)
     153             :     {
     154             :       u32 n_left_to_next;
     155             : 
     156           0 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     157             : 
     158             : 
     159             :       /*     while (n_left_from >= 4 && n_left_to_next >= 2) */
     160             :       /*       { */
     161             :       /*         vlib_buffer_t *b0, *b1; */
     162             :       /*         ip4_header_t *ip0, *ip1; */
     163             :       /*         ip4_fib_mtrie_t *mtrie0, *mtrie1; */
     164             :       /*         ip4_fib_mtrie_leaf_t leaf0, leaf1; */
     165             :       /*         ip_source_and_port_range_check_config_t *c0, *c1; */
     166             :       /*         ip_adjacency_t *adj0 = 0, *adj1 = 0; */
     167             :       /*         u32 bi0, next0, adj_index0, pass0, save_next0, fib_index0; */
     168             :       /*         u32 bi1, next1, adj_index1, pass1, save_next1, fib_index1; */
     169             :       /*         udp_header_t *udp0, *udp1; */
     170             : 
     171             :       /*         /\* Prefetch next iteration. *\/ */
     172             :       /*         { */
     173             :       /*           vlib_buffer_t *p2, *p3; */
     174             : 
     175             :       /*           p2 = vlib_get_buffer (vm, from[2]); */
     176             :       /*           p3 = vlib_get_buffer (vm, from[3]); */
     177             : 
     178             :       /*           vlib_prefetch_buffer_header (p2, LOAD); */
     179             :       /*           vlib_prefetch_buffer_header (p3, LOAD); */
     180             : 
     181             :       /*           CLIB_PREFETCH (p2->data, sizeof (ip0[0]), LOAD); */
     182             :       /*           CLIB_PREFETCH (p3->data, sizeof (ip1[0]), LOAD); */
     183             :       /*         } */
     184             : 
     185             :       /*         bi0 = to_next[0] = from[0]; */
     186             :       /*         bi1 = to_next[1] = from[1]; */
     187             :       /*         from += 2; */
     188             :       /*         to_next += 2; */
     189             :       /*         n_left_from -= 2; */
     190             :       /*         n_left_to_next -= 2; */
     191             : 
     192             :       /*         b0 = vlib_get_buffer (vm, bi0); */
     193             :       /*         b1 = vlib_get_buffer (vm, bi1); */
     194             : 
     195             :       /*         fib_index0 = */
     196             :       /*           vec_elt (im->fib_index_by_sw_if_index, */
     197             :       /*                 vnet_buffer (b0)->sw_if_index[VLIB_RX]); */
     198             :       /*         fib_index1 = */
     199             :       /*           vec_elt (im->fib_index_by_sw_if_index, */
     200             :       /*                 vnet_buffer (b1)->sw_if_index[VLIB_RX]); */
     201             : 
     202             :       /*         ip0 = vlib_buffer_get_current (b0); */
     203             :       /*         ip1 = vlib_buffer_get_current (b1); */
     204             : 
     205             :       /*         if (is_tx) */
     206             :       /*           { */
     207             :       /*             c0 = vnet_get_config_data (&tx_cm->config_main, */
     208             :       /*                                     &b0->current_config_index, */
     209             :       /*                                     &next0, sizeof (c0[0])); */
     210             :       /*             c1 = vnet_get_config_data (&tx_cm->config_main, */
     211             :       /*                                     &b1->current_config_index, */
     212             :       /*                                     &next1, sizeof (c1[0])); */
     213             :       /*           } */
     214             :       /*         else */
     215             :       /*           { */
     216             :       /*             c0 = vnet_get_config_data (&rx_cm->config_main, */
     217             :       /*                                     &b0->current_config_index, */
     218             :       /*                                     &next0, sizeof (c0[0])); */
     219             :       /*             c1 = vnet_get_config_data (&rx_cm->config_main, */
     220             :       /*                                     &b1->current_config_index, */
     221             :       /*                                     &next1, sizeof (c1[0])); */
     222             :       /*           } */
     223             : 
     224             :       /*         /\* we can't use the default VRF here... *\/ */
     225             :       /*         for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++) */
     226             :       /*           { */
     227             :       /*             ASSERT (c0->fib_index[i] && c1->fib_index[i]); */
     228             :       /*           } */
     229             : 
     230             : 
     231             :       /*         if (is_tx) */
     232             :       /*           { */
     233             :       /*             if (ip0->protocol == IP_PROTOCOL_UDP) */
     234             :       /*            fib_index0 = */
     235             :       /*              c0->fib_index */
     236             :       /*              [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN]; */
     237             :       /*             if (ip0->protocol == IP_PROTOCOL_TCP) */
     238             :       /*            fib_index0 = */
     239             :       /*              c0->fib_index */
     240             :       /*              [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN]; */
     241             :       /*           } */
     242             :       /*         else */
     243             :       /*           { */
     244             :       /*             if (ip0->protocol == IP_PROTOCOL_UDP) */
     245             :       /*            fib_index0 = */
     246             :       /*              c0->fib_index */
     247             :       /*              [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT]; */
     248             :       /*             if (ip0->protocol == IP_PROTOCOL_TCP) */
     249             :       /*            fib_index0 = */
     250             :       /*              c0->fib_index */
     251             :       /*              [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT]; */
     252             :       /*           } */
     253             : 
     254             :       /*         if (PREDICT_TRUE (fib_index0 != ~0)) */
     255             :       /*           { */
     256             : 
     257             :       /*             mtrie0 = &vec_elt_at_index (im->fibs, fib_index0)->mtrie; */
     258             : 
     259             :       /*             leaf0 = IP4_FIB_MTRIE_LEAF_ROOT; */
     260             : 
     261             :       /*             leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, */
     262             :       /*                                             &ip0->src_address, 0); */
     263             : 
     264             :       /*             leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, */
     265             :       /*                                             &ip0->src_address, 1); */
     266             : 
     267             :       /*             leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, */
     268             :       /*                                             &ip0->src_address, 2); */
     269             : 
     270             :       /*             leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, */
     271             :       /*                                             &ip0->src_address, 3); */
     272             : 
     273             :       /*             adj_index0 = ip4_fib_mtrie_leaf_get_adj_index (leaf0); */
     274             : 
     275             :       /*             ASSERT (adj_index0 == ip4_fib_lookup_with_table (im, fib_index0, */
     276             :       /*                                                           &ip0->src_address, */
     277             :       /*                                                           0 */
     278             :       /*                                                           /\* use dflt rt *\/ */
     279             :       /*                  )); */
     280             :       /*             adj0 = ip_get_adjacency (lm, adj_index0); */
     281             :       /*           } */
     282             : 
     283             :       /*         if (is_tx) */
     284             :       /*           { */
     285             :       /*             if (ip1->protocol == IP_PROTOCOL_UDP) */
     286             :       /*            fib_index1 = */
     287             :       /*              c1->fib_index */
     288             :       /*              [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN]; */
     289             :       /*             if (ip1->protocol == IP_PROTOCOL_TCP) */
     290             :       /*            fib_index1 = */
     291             :       /*              c1->fib_index */
     292             :       /*              [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN]; */
     293             :       /*           } */
     294             :       /*         else */
     295             :       /*           { */
     296             :       /*             if (ip1->protocol == IP_PROTOCOL_UDP) */
     297             :       /*            fib_index1 = */
     298             :       /*              c1->fib_index */
     299             :       /*              [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT]; */
     300             :       /*             if (ip1->protocol == IP_PROTOCOL_TCP) */
     301             :       /*            fib_index1 = */
     302             :       /*              c1->fib_index */
     303             :       /*              [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT]; */
     304             :       /*           } */
     305             : 
     306             :       /*         if (PREDICT_TRUE (fib_index1 != ~0)) */
     307             :       /*           { */
     308             : 
     309             :       /*             mtrie1 = &vec_elt_at_index (im->fibs, fib_index1)->mtrie; */
     310             : 
     311             :       /*             leaf1 = IP4_FIB_MTRIE_LEAF_ROOT; */
     312             : 
     313             :       /*             leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, */
     314             :       /*                                             &ip1->src_address, 0); */
     315             : 
     316             :       /*             leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, */
     317             :       /*                                             &ip1->src_address, 1); */
     318             : 
     319             :       /*             leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, */
     320             :       /*                                             &ip1->src_address, 2); */
     321             : 
     322             :       /*             leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, */
     323             :       /*                                             &ip1->src_address, 3); */
     324             : 
     325             :       /*             adj_index1 = ip4_fib_mtrie_leaf_get_adj_index (leaf1); */
     326             : 
     327             :       /*             ASSERT (adj_index1 == ip4_fib_lookup_with_table (im, fib_index1, */
     328             :       /*                                                           &ip1->src_address, */
     329             :       /*                                                           0)); */
     330             :       /*             adj1 = ip_get_adjacency (lm, adj_index1); */
     331             :       /*           } */
     332             : 
     333             :       /*         pass0 = 0; */
     334             :       /*         pass0 |= adj0 == 0; */
     335             :       /*         pass0 |= ip4_address_is_multicast (&ip0->src_address); */
     336             :       /*         pass0 |= */
     337             :       /*           ip0->src_address.as_u32 == clib_host_to_net_u32 (0xFFFFFFFF); */
     338             :       /*         pass0 |= (ip0->protocol != IP_PROTOCOL_UDP) */
     339             :       /*           && (ip0->protocol != IP_PROTOCOL_TCP); */
     340             : 
     341             :       /*         pass1 = 0; */
     342             :       /*         pass1 |= adj1 == 0; */
     343             :       /*         pass1 |= ip4_address_is_multicast (&ip1->src_address); */
     344             :       /*         pass1 |= */
     345             :       /*           ip1->src_address.as_u32 == clib_host_to_net_u32 (0xFFFFFFFF); */
     346             :       /*         pass1 |= (ip1->protocol != IP_PROTOCOL_UDP) */
     347             :       /*           && (ip1->protocol != IP_PROTOCOL_TCP); */
     348             : 
     349             :       /*         save_next0 = next0; */
     350             :       /*         udp0 = ip4_next_header (ip0); */
     351             :       /*         save_next1 = next1; */
     352             :       /*         udp1 = ip4_next_header (ip1); */
     353             : 
     354             :       /*         if (PREDICT_TRUE (pass0 == 0)) */
     355             :       /*           { */
     356             :       /*             good_packets++; */
     357             :       /*             next0 = check_adj_port_range_x1 */
     358             :       /*            (adj0, clib_net_to_host_u16 (udp0->dst_port), next0); */
     359             :       /*             good_packets -= (save_next0 != next0); */
     360             :       /*             b0->error = error_node->errors */
     361             :       /*            [IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_FAIL]; */
     362             :       /*           } */
     363             : 
     364             :       /*         if (PREDICT_TRUE (pass1 == 0)) */
     365             :       /*           { */
     366             :       /*             good_packets++; */
     367             :       /*             next1 = check_adj_port_range_x1 */
     368             :       /*            (adj1, clib_net_to_host_u16 (udp1->dst_port), next1); */
     369             :       /*             good_packets -= (save_next1 != next1); */
     370             :       /*             b1->error = error_node->errors */
     371             :       /*            [IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_FAIL]; */
     372             :       /*           } */
     373             : 
     374             :       /*         if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) */
     375             :       /*                         && (b0->flags & VLIB_BUFFER_IS_TRACED))) */
     376             :       /*           { */
     377             :       /*             ip4_source_and_port_range_check_trace_t *t = */
     378             :       /*            vlib_add_trace (vm, node, b0, sizeof (*t)); */
     379             :       /*             t->pass = next0 == save_next0; */
     380             :       /*             t->bypass = pass0; */
     381             :       /*             t->fib_index = fib_index0; */
     382             :       /*             t->src_addr.as_u32 = ip0->src_address.as_u32; */
     383             :       /*             t->port = (pass0 == 0) ? */
     384             :       /*            clib_net_to_host_u16 (udp0->dst_port) : 0; */
     385             :       /*             t->is_tcp = ip0->protocol == IP_PROTOCOL_TCP; */
     386             :       /*           } */
     387             : 
     388             :       /*         if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) */
     389             :       /*                         && (b1->flags & VLIB_BUFFER_IS_TRACED))) */
     390             :       /*           { */
     391             :       /*             ip4_source_and_port_range_check_trace_t *t = */
     392             :       /*            vlib_add_trace (vm, node, b1, sizeof (*t)); */
     393             :       /*             t->pass = next1 == save_next1; */
     394             :       /*             t->bypass = pass1; */
     395             :       /*             t->fib_index = fib_index1; */
     396             :       /*             t->src_addr.as_u32 = ip1->src_address.as_u32; */
     397             :       /*             t->port = (pass1 == 0) ? */
     398             :       /*            clib_net_to_host_u16 (udp1->dst_port) : 0; */
     399             :       /*             t->is_tcp = ip1->protocol == IP_PROTOCOL_TCP; */
     400             :       /*           } */
     401             : 
     402             :       /*         vlib_validate_buffer_enqueue_x2 (vm, node, next_index, */
     403             :       /*                                       to_next, n_left_to_next, */
     404             :       /*                                       bi0, bi1, next0, next1); */
     405             :       /*       } */
     406             : 
     407           0 :       while (n_left_from > 0 && n_left_to_next > 0)
     408             :         {
     409             :           vlib_buffer_t *b0;
     410             :           ip4_header_t *ip0;
     411             :           ip_source_and_port_range_check_config_t *c0;
     412             :           u32 bi0, next0, lb_index0, pass0, save_next0, fib_index0;
     413             :           udp_header_t *udp0;
     414           0 :           const protocol_port_range_dpo_t *ppr_dpo0 = NULL;
     415             :           const dpo_id_t *dpo;
     416             :           u32 sw_if_index0;
     417             : 
     418           0 :           bi0 = from[0];
     419           0 :           to_next[0] = bi0;
     420           0 :           from += 1;
     421           0 :           to_next += 1;
     422           0 :           n_left_from -= 1;
     423           0 :           n_left_to_next -= 1;
     424             : 
     425           0 :           b0 = vlib_get_buffer (vm, bi0);
     426           0 :           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
     427             : 
     428           0 :           fib_index0 = vec_elt (im->fib_index_by_sw_if_index, sw_if_index0);
     429             : 
     430           0 :           if (is_tx)
     431           0 :             vlib_buffer_advance (b0, sizeof (ethernet_header_t));
     432             : 
     433           0 :           ip0 = vlib_buffer_get_current (b0);
     434             : 
     435           0 :           c0 = vnet_feature_next_with_data (&next0, b0, sizeof (c0[0]));
     436             : 
     437             :           /* we can't use the default VRF here... */
     438           0 :           for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++)
     439             :             {
     440           0 :               ASSERT (c0->fib_index[i]);
     441             :             }
     442             : 
     443             : 
     444           0 :           if (is_tx)
     445             :             {
     446           0 :               if (ip0->protocol == IP_PROTOCOL_UDP)
     447           0 :                 fib_index0 =
     448             :                   c0->fib_index
     449             :                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN];
     450           0 :               if (ip0->protocol == IP_PROTOCOL_TCP)
     451           0 :                 fib_index0 =
     452             :                   c0->fib_index
     453             :                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN];
     454             :             }
     455             :           else
     456             :             {
     457           0 :               if (ip0->protocol == IP_PROTOCOL_UDP)
     458           0 :                 fib_index0 =
     459             :                   c0->fib_index
     460             :                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT];
     461           0 :               if (ip0->protocol == IP_PROTOCOL_TCP)
     462           0 :                 fib_index0 =
     463             :                   c0->fib_index
     464             :                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT];
     465             :             }
     466             : 
     467           0 :           if (fib_index0 != ~0)
     468             :             {
     469           0 :               lb_index0 = ip4_fib_forwarding_lookup (fib_index0,
     470           0 :                                                      &ip0->src_address);
     471             : 
     472             :               dpo =
     473           0 :                 load_balance_get_bucket_i (load_balance_get (lb_index0), 0);
     474             : 
     475           0 :               if (ppr_dpo_type == dpo->dpoi_type)
     476             :                 {
     477           0 :                   ppr_dpo0 = protocol_port_range_dpo_get (dpo->dpoi_index);
     478             :                 }
     479             :               /*
     480             :                * else the lookup hit an enty that was no inserted
     481             :                * by this range checker, which is the default route
     482             :                */
     483             :             }
     484             :           /*
     485             :            * $$$ which (src,dst) categories should we always pass?
     486             :            */
     487           0 :           pass0 = 0;
     488           0 :           pass0 |= ip4_address_is_multicast (&ip0->src_address);
     489           0 :           pass0 |=
     490           0 :             ip0->src_address.as_u32 == clib_host_to_net_u32 (0xFFFFFFFF);
     491           0 :           pass0 |= (ip0->protocol != IP_PROTOCOL_UDP)
     492           0 :             && (ip0->protocol != IP_PROTOCOL_TCP);
     493             : 
     494           0 :           save_next0 = next0;
     495           0 :           udp0 = ip4_next_header (ip0);
     496             : 
     497           0 :           if (PREDICT_TRUE (pass0 == 0))
     498             :             {
     499           0 :               good_packets++;
     500           0 :               next0 = check_adj_port_range_x1
     501           0 :                 (ppr_dpo0, clib_net_to_host_u16 (udp0->dst_port), next0);
     502           0 :               good_packets -= (save_next0 != next0);
     503           0 :               b0->error = error_node->errors
     504           0 :                 [IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_FAIL];
     505             :             }
     506             : 
     507           0 :           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
     508             :                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
     509             :             {
     510             :               ip4_source_and_port_range_check_trace_t *t =
     511           0 :                 vlib_add_trace (vm, node, b0, sizeof (*t));
     512           0 :               t->pass = next0 == save_next0;
     513           0 :               t->bypass = pass0;
     514           0 :               t->fib_index = fib_index0;
     515           0 :               t->src_addr.as_u32 = ip0->src_address.as_u32;
     516           0 :               t->port = (pass0 == 0) ?
     517           0 :                 clib_net_to_host_u16 (udp0->dst_port) : 0;
     518           0 :               t->is_tcp = ip0->protocol == IP_PROTOCOL_TCP;
     519             :             }
     520             : 
     521           0 :           if (is_tx)
     522           0 :             vlib_buffer_advance (b0, -sizeof (ethernet_header_t));
     523             : 
     524           0 :           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
     525             :                                            to_next, n_left_to_next,
     526             :                                            bi0, next0);
     527             :         }
     528             : 
     529           0 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     530             :     }
     531             : 
     532           0 :   if (is_tx)
     533           0 :     vlib_node_increment_counter (vm, ip4_source_port_and_range_check_tx.index,
     534             :                                  IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_OK,
     535             :                                  good_packets);
     536             :   else
     537           0 :     vlib_node_increment_counter (vm, ip4_source_port_and_range_check_rx.index,
     538             :                                  IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_OK,
     539             :                                  good_packets);
     540           0 :   return frame->n_vectors;
     541             : }
     542             : 
     543             : static uword
     544           0 : ip4_source_and_port_range_check_rx (vlib_main_t * vm,
     545             :                                     vlib_node_runtime_t * node,
     546             :                                     vlib_frame_t * frame)
     547             : {
     548           0 :   return ip4_source_and_port_range_check_inline (vm, node, frame,
     549             :                                                  0 /* !is_tx */ );
     550             : }
     551             : 
     552             : static uword
     553           0 : ip4_source_and_port_range_check_tx (vlib_main_t * vm,
     554             :                                     vlib_node_runtime_t * node,
     555             :                                     vlib_frame_t * frame)
     556             : {
     557           0 :   return ip4_source_and_port_range_check_inline (vm, node, frame,
     558             :                                                  1 /* is_tx */ );
     559             : }
     560             : 
     561             : /* Note: Calling same function for both RX and TX nodes
     562             :    as always checking dst_port, although
     563             :    if this changes can easily make new function
     564             : */
     565             : 
     566             : /* *INDENT-OFF* */
     567      183788 : VLIB_REGISTER_NODE (ip4_source_port_and_range_check_rx) = {
     568             :   .function = ip4_source_and_port_range_check_rx,
     569             :   .name = "ip4-source-and-port-range-check-rx",
     570             :   .vector_size = sizeof (u32),
     571             : 
     572             :   .n_errors = ARRAY_LEN(ip4_source_and_port_range_check_error_strings),
     573             :   .error_strings = ip4_source_and_port_range_check_error_strings,
     574             : 
     575             :   .n_next_nodes = IP4_SOURCE_AND_PORT_RANGE_CHECK_N_NEXT,
     576             :   .next_nodes = {
     577             :     [IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP] = "ip4-drop",
     578             :   },
     579             : 
     580             :   .format_buffer = format_ip4_header,
     581             :   .format_trace = format_ip4_source_and_port_range_check_trace,
     582             : };
     583             : /* *INDENT-ON* */
     584             : 
     585             : /* *INDENT-OFF* */
     586      183788 : VLIB_REGISTER_NODE (ip4_source_port_and_range_check_tx) = {
     587             :   .function = ip4_source_and_port_range_check_tx,
     588             :   .name = "ip4-source-and-port-range-check-tx",
     589             :   .vector_size = sizeof (u32),
     590             : 
     591             :   .n_errors = ARRAY_LEN(ip4_source_and_port_range_check_error_strings),
     592             :   .error_strings = ip4_source_and_port_range_check_error_strings,
     593             : 
     594             :   .n_next_nodes = IP4_SOURCE_AND_PORT_RANGE_CHECK_N_NEXT,
     595             :   .next_nodes = {
     596             :     [IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP] = "ip4-drop",
     597             :   },
     598             : 
     599             :   .format_buffer = format_ip4_header,
     600             :   .format_trace = format_ip4_source_and_port_range_check_trace,
     601             : };
     602             : /* *INDENT-ON* */
     603             : 
     604             : int
     605           0 : set_ip_source_and_port_range_check (vlib_main_t * vm,
     606             :                                     u32 * fib_index,
     607             :                                     u32 sw_if_index, u32 is_add)
     608             : {
     609             :   ip_source_and_port_range_check_config_t config;
     610           0 :   int rv = 0;
     611             :   int i;
     612             : 
     613           0 :   for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++)
     614             :     {
     615           0 :       config.fib_index[i] = fib_index[i];
     616             :     }
     617             : 
     618             :   /* For OUT we are in the RX path */
     619           0 :   if ((fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT] != ~0) ||
     620           0 :       (fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT] != ~0))
     621             :     {
     622           0 :       vnet_feature_enable_disable ("ip4-unicast",
     623             :                                    "ip4-source-and-port-range-check-rx",
     624             :                                    sw_if_index, is_add, &config,
     625             :                                    sizeof (config));
     626             :     }
     627             : 
     628             :   /* For IN we are in the TX path */
     629           0 :   if ((fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN] != ~0) ||
     630           0 :       (fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN] != ~0))
     631             :     {
     632           0 :       vnet_feature_enable_disable ("ip4-output",
     633             :                                    "ip4-source-and-port-range-check-tx",
     634             :                                    sw_if_index, is_add, &config,
     635             :                                    sizeof (config));
     636             :     }
     637           0 :   return rv;
     638             : }
     639             : 
     640             : static clib_error_t *
     641           0 : set_ip_source_and_port_range_check_fn (vlib_main_t * vm,
     642             :                                        unformat_input_t * input,
     643             :                                        vlib_cli_command_t * cmd)
     644             : {
     645           0 :   vnet_main_t *vnm = vnet_get_main ();
     646           0 :   ip4_main_t *im = &ip4_main;
     647           0 :   clib_error_t *error = 0;
     648           0 :   u8 is_add = 1;
     649           0 :   u32 sw_if_index = ~0;
     650             :   u32 vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS];
     651             :   u32 fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS];
     652           0 :   int vrf_set = 0;
     653             :   uword *p;
     654           0 :   int rv = 0;
     655             :   int i;
     656             : 
     657           0 :   sw_if_index = ~0;
     658           0 :   for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++)
     659             :     {
     660           0 :       fib_index[i] = ~0;
     661           0 :       vrf_id[i] = ~0;
     662             :     }
     663             : 
     664           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     665             :     {
     666           0 :       if (unformat (input, "%U", unformat_vnet_sw_interface, vnm,
     667             :                     &sw_if_index))
     668             :         ;
     669             :       else
     670           0 :         if (unformat
     671             :             (input, "tcp-out-vrf %d",
     672             :              &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT]))
     673           0 :         vrf_set = 1;
     674             :       else
     675           0 :         if (unformat
     676             :             (input, "udp-out-vrf %d",
     677             :              &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT]))
     678           0 :         vrf_set = 1;
     679             :       else
     680           0 :         if (unformat
     681             :             (input, "tcp-in-vrf %d",
     682             :              &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN]))
     683           0 :         vrf_set = 1;
     684             :       else
     685           0 :         if (unformat
     686             :             (input, "udp-in-vrf %d",
     687             :              &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN]))
     688           0 :         vrf_set = 1;
     689           0 :       else if (unformat (input, "del"))
     690           0 :         is_add = 0;
     691             :       else
     692           0 :         break;
     693             :     }
     694             : 
     695           0 :   if (sw_if_index == ~0)
     696           0 :     return clib_error_return (0, "Interface required but not specified");
     697             : 
     698           0 :   if (!vrf_set)
     699           0 :     return clib_error_return (0,
     700             :                               "TCP or UDP VRF ID required but not specified");
     701             : 
     702           0 :   for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++)
     703             :     {
     704             : 
     705           0 :       if (vrf_id[i] == 0)
     706           0 :         return clib_error_return (0,
     707             :                                   "TCP, UDP VRF ID should not be 0 (default). Should be distinct VRF for this purpose. ");
     708             : 
     709           0 :       if (vrf_id[i] != ~0)
     710             :         {
     711           0 :           p = hash_get (im->fib_index_by_table_id, vrf_id[i]);
     712             : 
     713           0 :           if (p == 0)
     714           0 :             return clib_error_return (0, "Invalid VRF ID %d", vrf_id[i]);
     715             : 
     716           0 :           fib_index[i] = p[0];
     717             :         }
     718             :     }
     719             :   rv =
     720           0 :     set_ip_source_and_port_range_check (vm, fib_index, sw_if_index, is_add);
     721             : 
     722           0 :   switch (rv)
     723             :     {
     724           0 :     case 0:
     725           0 :       break;
     726             : 
     727           0 :     default:
     728           0 :       return clib_error_return
     729             :         (0,
     730             :          "set source and port-range on interface returned an unexpected value: %d",
     731             :          rv);
     732             :     }
     733           0 :   return error;
     734             : }
     735             : 
     736             : /*?
     737             :  * Add the 'ip4-source-and-port-range-check-rx' or
     738             :  * 'ip4-source-and-port-range-check-tx' graph node for a given
     739             :  * interface. 'tcp-out-vrf' and 'udp-out-vrf' will add to
     740             :  * the RX path. 'tcp-in-vrf' and 'udp-in-vrf' will add to
     741             :  * the TX path. A graph node will be inserted into the chain when
     742             :  * the range check is added to the first interface. It will not
     743             :  * be removed from when range check is removed from the last
     744             :  * interface.
     745             :  *
     746             :  * By adding the range check graph node to the interface, incoming
     747             :  * or outgoing TCP/UDP packets will be validated using the
     748             :  * provided IPv4 FIB table (VRF).
     749             :  *
     750             :  * @note 'ip4-source-and-port-range-check-rx' and
     751             :  * 'ip4-source-and-port-range-check-tx' strings are too long, so
     752             :  * they are truncated on the 'show vlib graph' output.
     753             :  *
     754             :  * @todo This content needs to be validated and potentially more detail added.
     755             :  *
     756             :  * @cliexpar
     757             :  * @parblock
     758             :  * Example of graph node before range checking is enabled:
     759             :  * @cliexstart{show vlib graph ip4-source-and-port-range-check-tx}
     760             :  *            Name                      Next                    Previous
     761             :  * ip4-source-and-port-range-      ip4-drop [0]
     762             :  * @cliexend
     763             :  *
     764             :  * Example of how to enable range checking on TX:
     765             :  * @cliexcmd{set interface ip source-and-port-range-check GigabitEthernet2/0/0
     766             :  * udp-in-vrf 7}
     767             :  *
     768             :  * Example of graph node after range checking is enabled:
     769             :  * @cliexstart{show vlib graph ip4-source-and-port-range-check-tx}
     770             :  *            Name                      Next                    Previous
     771             :  * ip4-source-and-port-range-      ip4-drop [0]                ip4-rewrite
     772             :  *                              interface-output [1]
     773             :  * @cliexend
     774             :  *
     775             :  * Example of how to display the features enabled on an interface:
     776             :  * @cliexstart{show ip interface features GigabitEthernet2/0/0}
     777             :  * IP feature paths configured on GigabitEthernet2/0/0...
     778             :  *
     779             :  * ipv4 unicast:
     780             :  *   ip4-source-and-port-range-check-rx
     781             :  *   ip4-lookup
     782             :  *
     783             :  * ipv4 multicast:
     784             :  *   ip4-lookup-multicast
     785             :  *
     786             :  * ipv4 multicast:
     787             :  *   interface-output
     788             :  *
     789             :  * ipv6 unicast:
     790             :  *   ip6-lookup
     791             :  *
     792             :  * ipv6 multicast:
     793             :  *   ip6-lookup
     794             :  *
     795             :  * ipv6 multicast:
     796             :  *   interface-output
     797             :  * @cliexend
     798             :  * @endparblock
     799             : ?*/
     800             : /* *INDENT-OFF* */
     801      285289 : VLIB_CLI_COMMAND (set_interface_ip_source_and_port_range_check_command, static) = {
     802             :   .path = "set interface ip source-and-port-range-check",
     803             :   .function = set_ip_source_and_port_range_check_fn,
     804             :   .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]",
     805             : };
     806             : /* *INDENT-ON* */
     807             : 
     808             : static u8 *
     809           0 : format_ppr_dpo (u8 * s, va_list * args)
     810             : {
     811           0 :   index_t index = va_arg (*args, index_t);
     812           0 :   CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
     813             : 
     814             :   protocol_port_range_dpo_t *ppr_dpo;
     815             :   int i, j;
     816           0 :   int printed = 0;
     817             : 
     818           0 :   ppr_dpo = protocol_port_range_dpo_get (index);
     819             : 
     820           0 :   s = format (s, "allow ");
     821             : 
     822           0 :   for (i = 0; i < ppr_dpo->n_used_blocks; i++)
     823             :     {
     824           0 :       for (j = 0; j < 8; j++)
     825             :         {
     826           0 :           if (ppr_dpo->blocks[i].low.as_u16[j])
     827             :             {
     828           0 :               if (printed)
     829           0 :                 s = format (s, ", ");
     830           0 :               if (ppr_dpo->blocks[i].hi.as_u16[j] >
     831           0 :                   (ppr_dpo->blocks[i].low.as_u16[j] + 1))
     832             :                 s =
     833           0 :                   format (s, "%d-%d", (u32) ppr_dpo->blocks[i].low.as_u16[j],
     834           0 :                           (u32) ppr_dpo->blocks[i].hi.as_u16[j] - 1);
     835             :               else
     836           0 :                 s = format (s, "%d", ppr_dpo->blocks[i].low.as_u16[j]);
     837           0 :               printed = 1;
     838             :             }
     839             :         }
     840             :     }
     841           0 :   return s;
     842             : }
     843             : 
     844             : static void
     845           0 : ppr_dpo_lock (dpo_id_t * dpo)
     846             : {
     847           0 : }
     848             : 
     849             : static void
     850           0 : ppr_dpo_unlock (dpo_id_t * dpo)
     851             : {
     852           0 : }
     853             : 
     854             : const static dpo_vft_t ppr_vft = {
     855             :   .dv_lock = ppr_dpo_lock,
     856             :   .dv_unlock = ppr_dpo_unlock,
     857             :   .dv_format = format_ppr_dpo,
     858             : };
     859             : 
     860             : const static char *const ppr_ip4_nodes[] = {
     861             :   "ip4-source-and-port-range-check-rx",
     862             :   NULL,
     863             : };
     864             : 
     865             : const static char *const *const ppr_nodes[DPO_PROTO_NUM] = {
     866             :   [DPO_PROTO_IP4] = ppr_ip4_nodes,
     867             : };
     868             : 
     869             : clib_error_t *
     870         575 : ip4_source_and_port_range_check_init (vlib_main_t * vm)
     871             : {
     872         575 :   source_range_check_main_t *srm = &source_range_check_main;
     873             : 
     874         575 :   srm->vlib_main = vm;
     875         575 :   srm->vnet_main = vnet_get_main ();
     876             : 
     877         575 :   ppr_dpo_type = dpo_register_new_type (&ppr_vft, ppr_nodes);
     878             : 
     879         575 :   return 0;
     880             : }
     881             : 
     882       38015 : VLIB_INIT_FUNCTION (ip4_source_and_port_range_check_init);
     883             : 
     884             : protocol_port_range_dpo_t *
     885           0 : protocol_port_range_dpo_alloc (void)
     886             : {
     887             :   protocol_port_range_dpo_t *ppr_dpo;
     888             : 
     889           0 :   pool_get_aligned (ppr_dpo_pool, ppr_dpo, CLIB_CACHE_LINE_BYTES);
     890           0 :   clib_memset (ppr_dpo, 0, sizeof (*ppr_dpo));
     891             : 
     892           0 :   ppr_dpo->n_free_ranges = N_PORT_RANGES_PER_DPO;
     893             : 
     894           0 :   return (ppr_dpo);
     895             : }
     896             : 
     897             : 
     898             : static int
     899           0 : add_port_range_adjacency (u32 fib_index,
     900             :                           ip4_address_t * address,
     901             :                           u32 length, u16 * low_ports, u16 * high_ports)
     902             : {
     903             :   protocol_port_range_dpo_t *ppr_dpo;
     904           0 :   dpo_id_t dpop = DPO_INVALID;
     905             :   int i, j, k;
     906             : 
     907             :   fib_node_index_t fei;
     908           0 :   fib_prefix_t pfx = {
     909             :     .fp_proto = FIB_PROTOCOL_IP4,
     910             :     .fp_len = length,
     911             :     .fp_addr = {
     912             :                 .ip4 = *address,
     913             :                 },
     914             :   };
     915             : 
     916             :   /*
     917             :    * check to see if we have already sourced this prefix
     918             :    */
     919           0 :   fei = fib_table_lookup_exact_match (fib_index, &pfx);
     920             : 
     921           0 :   if (FIB_NODE_INDEX_INVALID == fei)
     922             :     {
     923             :       /*
     924             :        * this is a first time add for this prefix.
     925             :        */
     926           0 :       ppr_dpo = protocol_port_range_dpo_alloc ();
     927             :     }
     928             :   else
     929             :     {
     930             :       /*
     931             :        * the prefix is already there.
     932             :        * check it was sourced by us, and if so get the ragne DPO from it.
     933             :        */
     934           0 :       dpo_id_t dpo = DPO_INVALID;
     935             :       const dpo_id_t *bucket;
     936             : 
     937           0 :       if (fib_entry_get_dpo_for_source (fei, FIB_SOURCE_SPECIAL, &dpo))
     938             :         {
     939             :           /*
     940             :            * there is existing state. we'll want to add the new ranges to it
     941             :            */
     942             :           bucket =
     943           0 :             load_balance_get_bucket_i (load_balance_get (dpo.dpoi_index), 0);
     944           0 :           ppr_dpo = protocol_port_range_dpo_get (bucket->dpoi_index);
     945           0 :           dpo_reset (&dpo);
     946             :         }
     947             :       else
     948             :         {
     949             :           /*
     950             :            * there is no PPR state associated with this prefix,
     951             :            * so we'll need a new DPO
     952             :            */
     953           0 :           ppr_dpo = protocol_port_range_dpo_alloc ();
     954             :         }
     955             :     }
     956             : 
     957           0 :   if (vec_len (low_ports) > ppr_dpo->n_free_ranges)
     958           0 :     return VNET_API_ERROR_EXCEEDED_NUMBER_OF_RANGES_CAPACITY;
     959             : 
     960           0 :   j = k = 0;
     961             : 
     962           0 :   for (i = 0; i < vec_len (low_ports); i++)
     963             :     {
     964           0 :       for (; j < N_BLOCKS_PER_DPO; j++)
     965             :         {
     966           0 :           for (; k < 8; k++)
     967             :             {
     968           0 :               if (ppr_dpo->blocks[j].low.as_u16[k] == 0)
     969             :                 {
     970           0 :                   ppr_dpo->blocks[j].low.as_u16[k] = low_ports[i];
     971           0 :                   ppr_dpo->blocks[j].hi.as_u16[k] = high_ports[i];
     972           0 :                   goto doublebreak;
     973             :                 }
     974             :             }
     975             :         }
     976           0 :     doublebreak:;
     977             :     }
     978           0 :   ppr_dpo->n_used_blocks = j + 1;
     979             : 
     980             :   /*
     981             :    * add or update the entry in the FIB
     982             :    */
     983           0 :   dpo_set (&dpop, ppr_dpo_type, DPO_PROTO_IP4, (ppr_dpo - ppr_dpo_pool));
     984             : 
     985           0 :   if (FIB_NODE_INDEX_INVALID == fei)
     986             :     {
     987           0 :       fib_table_entry_special_dpo_add (fib_index,
     988             :                                        &pfx,
     989             :                                        FIB_SOURCE_SPECIAL,
     990             :                                        FIB_ENTRY_FLAG_NONE, &dpop);
     991             :     }
     992             :   else
     993             :     {
     994           0 :       fib_entry_special_update (fei,
     995             :                                 FIB_SOURCE_SPECIAL,
     996             :                                 FIB_ENTRY_FLAG_NONE, &dpop);
     997             :     }
     998             : 
     999           0 :   return 0;
    1000             : }
    1001             : 
    1002             : static int
    1003           0 : remove_port_range_adjacency (u32 fib_index,
    1004             :                              ip4_address_t * address,
    1005             :                              u32 length, u16 * low_ports, u16 * high_ports)
    1006             : {
    1007             :   protocol_port_range_dpo_t *ppr_dpo;
    1008             :   fib_node_index_t fei;
    1009             :   int i, j, k;
    1010             : 
    1011           0 :   fib_prefix_t pfx = {
    1012             :     .fp_proto = FIB_PROTOCOL_IP4,
    1013             :     .fp_len = length,
    1014             :     .fp_addr = {
    1015             :                 .ip4 = *address,
    1016             :                 },
    1017             :   };
    1018             : 
    1019             :   /*
    1020             :    * check to see if we have sourced this prefix
    1021             :    */
    1022           0 :   fei = fib_table_lookup_exact_match (fib_index, &pfx);
    1023             : 
    1024           0 :   if (FIB_NODE_INDEX_INVALID == fei)
    1025             :     {
    1026             :       /*
    1027             :        * not one of ours
    1028             :        */
    1029           0 :       return VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE;
    1030             :     }
    1031             :   else
    1032             :     {
    1033             :       /*
    1034             :        * the prefix is already there.
    1035             :        * check it was sourced by us
    1036             :        */
    1037           0 :       dpo_id_t dpo = DPO_INVALID;
    1038             :       const dpo_id_t *bucket;
    1039             : 
    1040           0 :       if (fib_entry_get_dpo_for_source (fei, FIB_SOURCE_SPECIAL, &dpo))
    1041             :         {
    1042             :           /*
    1043             :            * there is existing state. we'll want to add the new ranges to it
    1044             :            */
    1045             :           bucket =
    1046           0 :             load_balance_get_bucket_i (load_balance_get (dpo.dpoi_index), 0);
    1047           0 :           ppr_dpo = protocol_port_range_dpo_get (bucket->dpoi_index);
    1048           0 :           dpo_reset (&dpo);
    1049             :         }
    1050             :       else
    1051             :         {
    1052             :           /*
    1053             :            * not one of ours
    1054             :            */
    1055           0 :           return VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE;
    1056             :         }
    1057             :     }
    1058             : 
    1059           0 :   for (i = 0; i < vec_len (low_ports); i++)
    1060             :     {
    1061           0 :       for (j = 0; j < N_BLOCKS_PER_DPO; j++)
    1062             :         {
    1063           0 :           for (k = 0; k < 8; k++)
    1064             :             {
    1065           0 :               if (low_ports[i] == ppr_dpo->blocks[j].low.as_u16[k] &&
    1066           0 :                   high_ports[i] == ppr_dpo->blocks[j].hi.as_u16[k])
    1067             :                 {
    1068           0 :                   ppr_dpo->blocks[j].low.as_u16[k] =
    1069           0 :                     ppr_dpo->blocks[j].hi.as_u16[k] = 0;
    1070           0 :                   goto doublebreak;
    1071             :                 }
    1072             :             }
    1073             :         }
    1074           0 :     doublebreak:;
    1075             :     }
    1076             : 
    1077           0 :   ppr_dpo->n_free_ranges = 0;
    1078             : 
    1079             :   /* Have we deleted all ranges yet? */
    1080           0 :   for (i = 0; i < N_BLOCKS_PER_DPO; i++)
    1081             :     {
    1082           0 :       for (j = 0; j < 8; j++)
    1083             :         {
    1084           0 :           if (ppr_dpo->blocks[j].low.as_u16[i] == 0)
    1085           0 :             ppr_dpo->n_free_ranges++;
    1086             :         }
    1087             :     }
    1088             : 
    1089           0 :   if (N_PORT_RANGES_PER_DPO == ppr_dpo->n_free_ranges)
    1090             :     {
    1091             :       /* Yes, lose the adjacency... */
    1092           0 :       fib_table_entry_special_remove (fib_index, &pfx, FIB_SOURCE_SPECIAL);
    1093             :     }
    1094             :   else
    1095             :     {
    1096             :       /*
    1097             :        * compact the ranges down to a contiguous block
    1098             :        */
    1099             :       // FIXME. TODO.
    1100             :     }
    1101             : 
    1102           0 :   return 0;
    1103             : }
    1104             : 
    1105             : // This will be moved to another file and implemented post API freeze.
    1106             : int
    1107           0 : ip6_source_and_port_range_check_add_del (ip6_address_t * address,
    1108             :                                          u32 length,
    1109             :                                          u32 vrf_id,
    1110             :                                          u16 * low_ports,
    1111             :                                          u16 * high_ports, int is_add)
    1112             : {
    1113             :   u32 fib_index;
    1114             : 
    1115           0 :   fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
    1116             : 
    1117           0 :   ASSERT (~0 != fib_index);
    1118             : 
    1119           0 :   fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
    1120             : 
    1121           0 :   return 0;
    1122             : }
    1123             : 
    1124             : int
    1125           0 : ip4_source_and_port_range_check_add_del (ip4_address_t * address,
    1126             :                                          u32 length,
    1127             :                                          u32 vrf_id,
    1128             :                                          u16 * low_ports,
    1129             :                                          u16 * high_ports, int is_add)
    1130             : {
    1131             :   u32 fib_index;
    1132             : 
    1133           0 :   fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id,
    1134             :                                                  FIB_SOURCE_CLASSIFY);
    1135             : 
    1136           0 :   if (is_add == 0)
    1137             :     {
    1138           0 :       remove_port_range_adjacency (fib_index, address, length,
    1139             :                                    low_ports, high_ports);
    1140             :     }
    1141             :   else
    1142             :     {
    1143           0 :       add_port_range_adjacency (fib_index, address, length,
    1144             :                                 low_ports, high_ports);
    1145             :     }
    1146             : 
    1147           0 :   return 0;
    1148             : }
    1149             : 
    1150             : static clib_error_t *
    1151           0 : ip_source_and_port_range_check_command_fn (vlib_main_t * vm,
    1152             :                                            unformat_input_t * input,
    1153             :                                            vlib_cli_command_t * cmd)
    1154             : {
    1155           0 :   u16 *low_ports = 0;
    1156           0 :   u16 *high_ports = 0;
    1157             :   u16 this_low;
    1158             :   u16 this_hi;
    1159             :   ip4_address_t ip4_addr;
    1160             :   ip6_address_t ip6_addr;       //This function will be moved to generic impl when v6 done.
    1161             :   u32 length;
    1162             :   u32 tmp, tmp2;
    1163           0 :   u32 vrf_id = ~0;
    1164           0 :   int is_add = 1, ip_ver = ~0;
    1165             :   int rv;
    1166             : 
    1167             : 
    1168           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    1169             :     {
    1170           0 :       if (unformat (input, "%U/%d", unformat_ip4_address, &ip4_addr, &length))
    1171           0 :         ip_ver = 4;
    1172             :       else
    1173           0 :         if (unformat
    1174             :             (input, "%U/%d", unformat_ip6_address, &ip6_addr, &length))
    1175           0 :         ip_ver = 6;
    1176           0 :       else if (unformat (input, "vrf %d", &vrf_id))
    1177             :         ;
    1178           0 :       else if (unformat (input, "del"))
    1179           0 :         is_add = 0;
    1180           0 :       else if (unformat (input, "port %d", &tmp))
    1181             :         {
    1182           0 :           if (tmp == 0 || tmp > 65535)
    1183           0 :             return clib_error_return (0, "port %d out of range", tmp);
    1184           0 :           this_low = tmp;
    1185           0 :           this_hi = this_low + 1;
    1186           0 :           vec_add1 (low_ports, this_low);
    1187           0 :           vec_add1 (high_ports, this_hi);
    1188             :         }
    1189           0 :       else if (unformat (input, "range %d - %d", &tmp, &tmp2))
    1190             :         {
    1191           0 :           if (tmp > tmp2)
    1192           0 :             return clib_error_return (0, "ports %d and %d out of order",
    1193             :                                       tmp, tmp2);
    1194           0 :           if (tmp == 0 || tmp > 65535)
    1195           0 :             return clib_error_return (0, "low port %d out of range", tmp);
    1196           0 :           if (tmp2 == 0 || tmp2 > 65535)
    1197           0 :             return clib_error_return (0, "high port %d out of range", tmp2);
    1198           0 :           this_low = tmp;
    1199           0 :           this_hi = tmp2 + 1;
    1200           0 :           vec_add1 (low_ports, this_low);
    1201           0 :           vec_add1 (high_ports, this_hi);
    1202             :         }
    1203             :       else
    1204           0 :         break;
    1205             :     }
    1206             : 
    1207           0 :   if (ip_ver == ~0)
    1208           0 :     return clib_error_return (0, " <address>/<mask> not specified");
    1209             : 
    1210           0 :   if (vrf_id == ~0)
    1211           0 :     return clib_error_return (0, " VRF ID required, not specified");
    1212             : 
    1213           0 :   if (vec_len (low_ports) == 0)
    1214           0 :     return clib_error_return (0,
    1215             :                               " Both VRF ID and range/port must be set for a protocol.");
    1216             : 
    1217           0 :   if (vrf_id == 0)
    1218           0 :     return clib_error_return (0, " VRF ID can not be 0 (default).");
    1219             : 
    1220             : 
    1221           0 :   if (ip_ver == 4)
    1222           0 :     rv = ip4_source_and_port_range_check_add_del
    1223             :       (&ip4_addr, length, vrf_id, low_ports, high_ports, is_add);
    1224             :   else
    1225           0 :     return clib_error_return (0, " IPv6 in subsequent patch");
    1226             : 
    1227           0 :   switch (rv)
    1228             :     {
    1229           0 :     case 0:
    1230           0 :       break;
    1231             : 
    1232           0 :     case VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE:
    1233           0 :       return clib_error_return
    1234             :         (0, " Incorrect adjacency for add/del operation");
    1235             : 
    1236           0 :     case VNET_API_ERROR_EXCEEDED_NUMBER_OF_PORTS_CAPACITY:
    1237           0 :       return clib_error_return (0, " Too many ports in add/del operation");
    1238             : 
    1239           0 :     case VNET_API_ERROR_EXCEEDED_NUMBER_OF_RANGES_CAPACITY:
    1240           0 :       return clib_error_return
    1241             :         (0, " Too many ranges requested for add operation");
    1242             : 
    1243           0 :     default:
    1244           0 :       return clib_error_return (0, " returned an unexpected value: %d", rv);
    1245             :     }
    1246             : 
    1247           0 :   return 0;
    1248             : }
    1249             : 
    1250             : /*?
    1251             :  * This command adds an IP Subnet and range of ports to be validated
    1252             :  * by an IP FIB table (VRF).
    1253             :  *
    1254             :  * @todo This is incomplete. This needs a detailed description and a
    1255             :  * practical example.
    1256             :  *
    1257             :  * @cliexpar
    1258             :  * Example of how to add an IPv4 subnet and single port to an IPv4 FIB table:
    1259             :  * @cliexcmd{set ip source-and-port-range-check vrf 7 172.16.1.0/24 port 23}
    1260             :  * Example of how to add an IPv4 subnet and range of ports to an IPv4 FIB table:
    1261             :  * @cliexcmd{set ip source-and-port-range-check vrf 7 172.16.1.0/24 range 23 - 100}
    1262             :  * Example of how to delete an IPv4 subnet and single port from an IPv4 FIB table:
    1263             :  * @cliexcmd{set ip source-and-port-range-check vrf 7 172.16.1.0/24 port 23 del}
    1264             :  * Example of how to delete an IPv4 subnet and range of ports from an IPv4 FIB table:
    1265             :  * @cliexcmd{set ip source-and-port-range-check vrf 7 172.16.1.0/24 range 23 - 100 del}
    1266             : ?*/
    1267             : /* *INDENT-OFF* */
    1268      285289 : VLIB_CLI_COMMAND (ip_source_and_port_range_check_command, static) = {
    1269             :   .path = "set ip source-and-port-range-check",
    1270             :   .function = ip_source_and_port_range_check_command_fn,
    1271             :   .short_help =
    1272             :   "set ip source-and-port-range-check vrf <table-id> <ip-addr>/<mask> {port nn | range <nn> - <nn>} [del]",
    1273             : };
    1274             : /* *INDENT-ON* */
    1275             : 
    1276             : 
    1277             : static clib_error_t *
    1278           0 : show_source_and_port_range_check_fn (vlib_main_t * vm,
    1279             :                                      unformat_input_t * input,
    1280             :                                      vlib_cli_command_t * cmd)
    1281             : {
    1282             :   protocol_port_range_dpo_t *ppr_dpo;
    1283             :   u32 fib_index;
    1284           0 :   u8 addr_set = 0;
    1285           0 :   u32 vrf_id = ~0;
    1286             :   int rv, i, j;
    1287           0 :   u32 port = 0;
    1288           0 :   fib_prefix_t pfx = {
    1289             :     .fp_proto = FIB_PROTOCOL_IP4,
    1290             :     .fp_len = 32,
    1291             :   };
    1292             : 
    1293           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    1294             :     {
    1295           0 :       if (unformat (input, "%U", unformat_ip4_address, &pfx.fp_addr.ip4))
    1296           0 :         addr_set = 1;
    1297           0 :       else if (unformat (input, "vrf %d", &vrf_id))
    1298             :         ;
    1299           0 :       else if (unformat (input, "port %d", &port))
    1300             :         ;
    1301             :       else
    1302           0 :         break;
    1303             :     }
    1304             : 
    1305           0 :   if (addr_set == 0)
    1306           0 :     return clib_error_return (0, "<address> not specified");
    1307             : 
    1308           0 :   if (vrf_id == ~0)
    1309           0 :     return clib_error_return (0, "VRF ID required, not specified");
    1310             : 
    1311           0 :   fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
    1312           0 :   if (~0 == fib_index)
    1313           0 :     return clib_error_return (0, "VRF %d not found", vrf_id);
    1314             : 
    1315             :   /*
    1316             :    * find the longest prefix match on the address requested,
    1317             :    * check it was sourced by us
    1318             :    */
    1319           0 :   dpo_id_t dpo = DPO_INVALID;
    1320             :   const dpo_id_t *bucket;
    1321             : 
    1322           0 :   if (!fib_entry_get_dpo_for_source (fib_table_lookup (fib_index, &pfx),
    1323             :                                      FIB_SOURCE_SPECIAL, &dpo))
    1324             :     {
    1325             :       /*
    1326             :        * not one of ours
    1327             :        */
    1328           0 :       vlib_cli_output (vm, "%U: src address drop", format_ip4_address,
    1329             :                        &pfx.fp_addr.ip4);
    1330           0 :       return 0;
    1331             :     }
    1332             : 
    1333           0 :   bucket = load_balance_get_bucket_i (load_balance_get (dpo.dpoi_index), 0);
    1334           0 :   ppr_dpo = protocol_port_range_dpo_get (bucket->dpoi_index);
    1335           0 :   dpo_reset (&dpo);
    1336             : 
    1337           0 :   if (port)
    1338             :     {
    1339           0 :       rv = check_adj_port_range_x1 (ppr_dpo, (u16) port, 1234);
    1340           0 :       if (rv == 1234)
    1341           0 :         vlib_cli_output (vm, "%U port %d PASS", format_ip4_address,
    1342             :                          &pfx.fp_addr.ip4, port);
    1343             :       else
    1344           0 :         vlib_cli_output (vm, "%U port %d FAIL", format_ip4_address,
    1345             :                          &pfx.fp_addr.ip4, port);
    1346           0 :       return 0;
    1347             :     }
    1348             :   else
    1349             :     {
    1350             :       u8 *s;
    1351             : 
    1352           0 :       s = format (0, "%U: ", format_ip4_address, &pfx.fp_addr.ip4);
    1353             : 
    1354           0 :       for (i = 0; i < N_BLOCKS_PER_DPO; i++)
    1355             :         {
    1356           0 :           for (j = 0; j < 8; j++)
    1357             :             {
    1358           0 :               if (ppr_dpo->blocks[i].low.as_u16[j])
    1359           0 :                 s = format (s, "%d - %d ",
    1360           0 :                             (u32) ppr_dpo->blocks[i].low.as_u16[j],
    1361           0 :                             (u32) ppr_dpo->blocks[i].hi.as_u16[j]);
    1362             :             }
    1363             :         }
    1364           0 :       vlib_cli_output (vm, "%s", s);
    1365           0 :       vec_free (s);
    1366             :     }
    1367             : 
    1368           0 :   return 0;
    1369             : }
    1370             : 
    1371             : /*?
    1372             :  * Display the range of ports being validated by an IPv4 FIB for a given
    1373             :  * IP or subnet, or test if a given IP and port are being validated.
    1374             :  *
    1375             :  * @todo This is incomplete. This needs a detailed description and a
    1376             :  * practical example.
    1377             :  *
    1378             :  * @cliexpar
    1379             :  * Example of how to display the set of ports being validated for a given
    1380             :  * IPv4 subnet:
    1381             :  * @cliexstart{show ip source-and-port-range-check vrf 7 172.16.2.0}
    1382             :  * 172.16.2.0: 23 - 101
    1383             :  * @cliexend
    1384             :  * Example of how to test to determine of a given iPv4 address and port
    1385             :  * are being validated:
    1386             :  * @cliexstart{show ip source-and-port-range-check vrf 7 172.16.2.2 port 23}
    1387             :  * 172.16.2.2 port 23 PASS
    1388             :  * @cliexend
    1389             :  * @cliexstart{show ip source-and-port-range-check vrf 7 172.16.2.2 port 250}
    1390             :  * 172.16.2.2 port 250 FAIL
    1391             :  * @cliexend
    1392             :  ?*/
    1393             : /* *INDENT-OFF* */
    1394      285289 : VLIB_CLI_COMMAND (show_source_and_port_range_check, static) = {
    1395             :   .path = "show ip source-and-port-range-check",
    1396             :   .function = show_source_and_port_range_check_fn,
    1397             :   .short_help =
    1398             :   "show ip source-and-port-range-check vrf <table-id> <ip-addr> [port <n>]",
    1399             : };
    1400             : /* *INDENT-ON* */
    1401             : 
    1402             : /*
    1403             :  * fd.io coding-style-patch-verification: ON
    1404             :  *
    1405             :  * Local Variables:
    1406             :  * eval: (c-set-style "gnu")
    1407             :  * End:
    1408             :  */

Generated by: LCOV version 1.14