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