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