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