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