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