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