c6037c48946bd920400906d21f73589522ebbb5c
[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   const protocol_port_range_t *range;
101   u16x8vec_t key;
102   u16x8vec_t diff1;
103   u16x8vec_t diff2;
104   u16x8vec_t sum, sum_equal_diff2;
105   u16 sum_nonzero, sum_equal, winner_mask;
106   int i;
107
108   if (NULL == ppr_dpo || dst_port == 0)
109     return IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP;
110
111   /* Make the obvious screw-case work. A variant also works w/ no MMX */
112   if (PREDICT_FALSE (dst_port == 65535))
113     {
114       int j;
115
116       for (i = 0;
117            i < VLIB_BUFFER_PRE_DATA_SIZE / sizeof (protocol_port_range_t);
118            i++)
119         {
120           for (j = 0; j < 8; j++)
121             if (ppr_dpo->blocks[i].low.as_u16[j] == 65535)
122               return next;
123         }
124       return IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP;
125     }
126
127   key.as_u16x8 = u16x8_splat (dst_port);
128
129   for (i = 0; i < ppr_dpo->n_used_blocks; i++)
130     {
131       diff1.as_u16x8 =
132         u16x8_sub_saturate (ppr_dpo->blocks[i].low.as_u16x8, key.as_u16x8);
133       diff2.as_u16x8 =
134         u16x8_sub_saturate (ppr_dpo->blocks[i].hi.as_u16x8, key.as_u16x8);
135       sum.as_u16x8 = u16x8_add (diff1.as_u16x8, diff2.as_u16x8);
136       sum_equal_diff2.as_u16x8 =
137         u16x8_is_equal (sum.as_u16x8, diff2.as_u16x8);
138       sum_nonzero = ~u16x8_zero_byte_mask (sum.as_u16x8);
139       sum_equal = ~u16x8_zero_byte_mask (sum_equal_diff2.as_u16x8);
140       winner_mask = sum_nonzero & sum_equal;
141       if (winner_mask)
142         return next;
143       range++;
144     }
145   return IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP;
146 }
147
148 always_inline protocol_port_range_dpo_t *
149 protocol_port_range_dpo_get (index_t index)
150 {
151   return (pool_elt_at_index (ppr_dpo_pool, index));
152 }
153
154 always_inline uword
155 ip4_source_and_port_range_check_inline (vlib_main_t * vm,
156                                         vlib_node_runtime_t * node,
157                                         vlib_frame_t * frame, int is_tx)
158 {
159   ip4_main_t *im = &ip4_main;
160   ip_lookup_main_t *lm = &im->lookup_main;
161   ip_config_main_t *rx_cm =
162     &lm->feature_config_mains[VNET_IP_RX_UNICAST_FEAT];
163   ip_config_main_t *tx_cm = &lm->feature_config_mains[VNET_IP_TX_FEAT];
164   u32 n_left_from, *from, *to_next;
165   u32 next_index;
166   vlib_node_runtime_t *error_node = node;
167   u32 good_packets = 0;
168   int i;
169
170   from = vlib_frame_vector_args (frame);
171   n_left_from = frame->n_vectors;
172   next_index = node->cached_next_index;
173
174   while (n_left_from > 0)
175     {
176       u32 n_left_to_next;
177
178       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
179
180
181       /*     while (n_left_from >= 4 && n_left_to_next >= 2) */
182       /*       { */
183       /*         vlib_buffer_t *b0, *b1; */
184       /*         ip4_header_t *ip0, *ip1; */
185       /*         ip4_fib_mtrie_t *mtrie0, *mtrie1; */
186       /*         ip4_fib_mtrie_leaf_t leaf0, leaf1; */
187       /*         ip_source_and_port_range_check_config_t *c0, *c1; */
188       /*         ip_adjacency_t *adj0 = 0, *adj1 = 0; */
189       /*         u32 bi0, next0, adj_index0, pass0, save_next0, fib_index0; */
190       /*         u32 bi1, next1, adj_index1, pass1, save_next1, fib_index1; */
191       /*         udp_header_t *udp0, *udp1; */
192
193       /*         /\* Prefetch next iteration. *\/ */
194       /*         { */
195       /*           vlib_buffer_t *p2, *p3; */
196
197       /*           p2 = vlib_get_buffer (vm, from[2]); */
198       /*           p3 = vlib_get_buffer (vm, from[3]); */
199
200       /*           vlib_prefetch_buffer_header (p2, LOAD); */
201       /*           vlib_prefetch_buffer_header (p3, LOAD); */
202
203       /*           CLIB_PREFETCH (p2->data, sizeof (ip0[0]), LOAD); */
204       /*           CLIB_PREFETCH (p3->data, sizeof (ip1[0]), LOAD); */
205       /*         } */
206
207       /*         bi0 = to_next[0] = from[0]; */
208       /*         bi1 = to_next[1] = from[1]; */
209       /*         from += 2; */
210       /*         to_next += 2; */
211       /*         n_left_from -= 2; */
212       /*         n_left_to_next -= 2; */
213
214       /*         b0 = vlib_get_buffer (vm, bi0); */
215       /*         b1 = vlib_get_buffer (vm, bi1); */
216
217       /*         fib_index0 = */
218       /*           vec_elt (im->fib_index_by_sw_if_index, */
219       /*                 vnet_buffer (b0)->sw_if_index[VLIB_RX]); */
220       /*         fib_index1 = */
221       /*           vec_elt (im->fib_index_by_sw_if_index, */
222       /*                 vnet_buffer (b1)->sw_if_index[VLIB_RX]); */
223
224       /*         ip0 = vlib_buffer_get_current (b0); */
225       /*         ip1 = vlib_buffer_get_current (b1); */
226
227       /*         if (is_tx) */
228       /*           { */
229       /*             c0 = vnet_get_config_data (&tx_cm->config_main, */
230       /*                                     &b0->current_config_index, */
231       /*                                     &next0, sizeof (c0[0])); */
232       /*             c1 = vnet_get_config_data (&tx_cm->config_main, */
233       /*                                     &b1->current_config_index, */
234       /*                                     &next1, sizeof (c1[0])); */
235       /*           } */
236       /*         else */
237       /*           { */
238       /*             c0 = vnet_get_config_data (&rx_cm->config_main, */
239       /*                                     &b0->current_config_index, */
240       /*                                     &next0, sizeof (c0[0])); */
241       /*             c1 = vnet_get_config_data (&rx_cm->config_main, */
242       /*                                     &b1->current_config_index, */
243       /*                                     &next1, sizeof (c1[0])); */
244       /*           } */
245
246       /*         /\* we can't use the default VRF here... *\/ */
247       /*         for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++) */
248       /*           { */
249       /*             ASSERT (c0->fib_index[i] && c1->fib_index[i]); */
250       /*           } */
251
252
253       /*         if (is_tx) */
254       /*           { */
255       /*             if (ip0->protocol == IP_PROTOCOL_UDP) */
256       /*            fib_index0 = */
257       /*              c0->fib_index */
258       /*              [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN]; */
259       /*             if (ip0->protocol == IP_PROTOCOL_TCP) */
260       /*            fib_index0 = */
261       /*              c0->fib_index */
262       /*              [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN]; */
263       /*           } */
264       /*         else */
265       /*           { */
266       /*             if (ip0->protocol == IP_PROTOCOL_UDP) */
267       /*            fib_index0 = */
268       /*              c0->fib_index */
269       /*              [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT]; */
270       /*             if (ip0->protocol == IP_PROTOCOL_TCP) */
271       /*            fib_index0 = */
272       /*              c0->fib_index */
273       /*              [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT]; */
274       /*           } */
275
276       /*         if (PREDICT_TRUE (fib_index0 != ~0)) */
277       /*           { */
278
279       /*             mtrie0 = &vec_elt_at_index (im->fibs, fib_index0)->mtrie; */
280
281       /*             leaf0 = IP4_FIB_MTRIE_LEAF_ROOT; */
282
283       /*             leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, */
284       /*                                             &ip0->src_address, 0); */
285
286       /*             leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, */
287       /*                                             &ip0->src_address, 1); */
288
289       /*             leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, */
290       /*                                             &ip0->src_address, 2); */
291
292       /*             leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, */
293       /*                                             &ip0->src_address, 3); */
294
295       /*             adj_index0 = ip4_fib_mtrie_leaf_get_adj_index (leaf0); */
296
297       /*             ASSERT (adj_index0 == ip4_fib_lookup_with_table (im, fib_index0, */
298       /*                                                           &ip0->src_address, */
299       /*                                                           0 */
300       /*                                                           /\* use dflt rt *\/ */
301       /*                  )); */
302       /*             adj0 = ip_get_adjacency (lm, adj_index0); */
303       /*           } */
304
305       /*         if (is_tx) */
306       /*           { */
307       /*             if (ip1->protocol == IP_PROTOCOL_UDP) */
308       /*            fib_index1 = */
309       /*              c1->fib_index */
310       /*              [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN]; */
311       /*             if (ip1->protocol == IP_PROTOCOL_TCP) */
312       /*            fib_index1 = */
313       /*              c1->fib_index */
314       /*              [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN]; */
315       /*           } */
316       /*         else */
317       /*           { */
318       /*             if (ip1->protocol == IP_PROTOCOL_UDP) */
319       /*            fib_index1 = */
320       /*              c1->fib_index */
321       /*              [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT]; */
322       /*             if (ip1->protocol == IP_PROTOCOL_TCP) */
323       /*            fib_index1 = */
324       /*              c1->fib_index */
325       /*              [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT]; */
326       /*           } */
327
328       /*         if (PREDICT_TRUE (fib_index1 != ~0)) */
329       /*           { */
330
331       /*             mtrie1 = &vec_elt_at_index (im->fibs, fib_index1)->mtrie; */
332
333       /*             leaf1 = IP4_FIB_MTRIE_LEAF_ROOT; */
334
335       /*             leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, */
336       /*                                             &ip1->src_address, 0); */
337
338       /*             leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, */
339       /*                                             &ip1->src_address, 1); */
340
341       /*             leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, */
342       /*                                             &ip1->src_address, 2); */
343
344       /*             leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, */
345       /*                                             &ip1->src_address, 3); */
346
347       /*             adj_index1 = ip4_fib_mtrie_leaf_get_adj_index (leaf1); */
348
349       /*             ASSERT (adj_index1 == ip4_fib_lookup_with_table (im, fib_index1, */
350       /*                                                           &ip1->src_address, */
351       /*                                                           0)); */
352       /*             adj1 = ip_get_adjacency (lm, adj_index1); */
353       /*           } */
354
355       /*         pass0 = 0; */
356       /*         pass0 |= adj0 == 0; */
357       /*         pass0 |= ip4_address_is_multicast (&ip0->src_address); */
358       /*         pass0 |= */
359       /*           ip0->src_address.as_u32 == clib_host_to_net_u32 (0xFFFFFFFF); */
360       /*         pass0 |= (ip0->protocol != IP_PROTOCOL_UDP) */
361       /*           && (ip0->protocol != IP_PROTOCOL_TCP); */
362
363       /*         pass1 = 0; */
364       /*         pass1 |= adj1 == 0; */
365       /*         pass1 |= ip4_address_is_multicast (&ip1->src_address); */
366       /*         pass1 |= */
367       /*           ip1->src_address.as_u32 == clib_host_to_net_u32 (0xFFFFFFFF); */
368       /*         pass1 |= (ip1->protocol != IP_PROTOCOL_UDP) */
369       /*           && (ip1->protocol != IP_PROTOCOL_TCP); */
370
371       /*         save_next0 = next0; */
372       /*         udp0 = ip4_next_header (ip0); */
373       /*         save_next1 = next1; */
374       /*         udp1 = ip4_next_header (ip1); */
375
376       /*         if (PREDICT_TRUE (pass0 == 0)) */
377       /*           { */
378       /*             good_packets++; */
379       /*             next0 = check_adj_port_range_x1 */
380       /*            (adj0, clib_net_to_host_u16 (udp0->dst_port), next0); */
381       /*             good_packets -= (save_next0 != next0); */
382       /*             b0->error = error_node->errors */
383       /*            [IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_FAIL]; */
384       /*           } */
385
386       /*         if (PREDICT_TRUE (pass1 == 0)) */
387       /*           { */
388       /*             good_packets++; */
389       /*             next1 = check_adj_port_range_x1 */
390       /*            (adj1, clib_net_to_host_u16 (udp1->dst_port), next1); */
391       /*             good_packets -= (save_next1 != next1); */
392       /*             b1->error = error_node->errors */
393       /*            [IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_FAIL]; */
394       /*           } */
395
396       /*         if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) */
397       /*                         && (b0->flags & VLIB_BUFFER_IS_TRACED))) */
398       /*           { */
399       /*             ip4_source_and_port_range_check_trace_t *t = */
400       /*            vlib_add_trace (vm, node, b0, sizeof (*t)); */
401       /*             t->pass = next0 == save_next0; */
402       /*             t->bypass = pass0; */
403       /*             t->fib_index = fib_index0; */
404       /*             t->src_addr.as_u32 = ip0->src_address.as_u32; */
405       /*             t->port = (pass0 == 0) ? */
406       /*            clib_net_to_host_u16 (udp0->dst_port) : 0; */
407       /*             t->is_tcp = ip0->protocol == IP_PROTOCOL_TCP; */
408       /*           } */
409
410       /*         if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) */
411       /*                         && (b1->flags & VLIB_BUFFER_IS_TRACED))) */
412       /*           { */
413       /*             ip4_source_and_port_range_check_trace_t *t = */
414       /*            vlib_add_trace (vm, node, b1, sizeof (*t)); */
415       /*             t->pass = next1 == save_next1; */
416       /*             t->bypass = pass1; */
417       /*             t->fib_index = fib_index1; */
418       /*             t->src_addr.as_u32 = ip1->src_address.as_u32; */
419       /*             t->port = (pass1 == 0) ? */
420       /*            clib_net_to_host_u16 (udp1->dst_port) : 0; */
421       /*             t->is_tcp = ip1->protocol == IP_PROTOCOL_TCP; */
422       /*           } */
423
424       /*         vlib_validate_buffer_enqueue_x2 (vm, node, next_index, */
425       /*                                       to_next, n_left_to_next, */
426       /*                                       bi0, bi1, next0, next1); */
427       /*       } */
428
429       while (n_left_from > 0 && n_left_to_next > 0)
430         {
431           vlib_buffer_t *b0;
432           ip4_header_t *ip0;
433           ip_source_and_port_range_check_config_t *c0;
434           u32 bi0, next0, lb_index0, pass0, save_next0, fib_index0;
435           udp_header_t *udp0;
436           const protocol_port_range_dpo_t *ppr_dpo0 = NULL;
437           const dpo_id_t *dpo;
438
439           bi0 = from[0];
440           to_next[0] = bi0;
441           from += 1;
442           to_next += 1;
443           n_left_from -= 1;
444           n_left_to_next -= 1;
445
446           b0 = vlib_get_buffer (vm, bi0);
447
448           fib_index0 =
449             vec_elt (im->fib_index_by_sw_if_index,
450                      vnet_buffer (b0)->sw_if_index[VLIB_RX]);
451
452           if (is_tx)
453             vlib_buffer_advance (b0, sizeof (ethernet_header_t));
454
455           ip0 = vlib_buffer_get_current (b0);
456
457           if (is_tx)
458             {
459               c0 = vnet_get_config_data
460                 (&tx_cm->config_main, &b0->current_config_index,
461                  &next0, sizeof (c0[0]));
462             }
463           else
464             {
465               c0 = vnet_get_config_data
466                 (&rx_cm->config_main, &b0->current_config_index,
467                  &next0, sizeof (c0[0]));
468             }
469
470           /* we can't use the default VRF here... */
471           for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++)
472             {
473               ASSERT (c0->fib_index[i]);
474             }
475
476
477           if (is_tx)
478             {
479               if (ip0->protocol == IP_PROTOCOL_UDP)
480                 fib_index0 =
481                   c0->fib_index
482                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN];
483               if (ip0->protocol == IP_PROTOCOL_TCP)
484                 fib_index0 =
485                   c0->fib_index
486                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN];
487             }
488           else
489             {
490               if (ip0->protocol == IP_PROTOCOL_UDP)
491                 fib_index0 =
492                   c0->fib_index
493                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT];
494               if (ip0->protocol == IP_PROTOCOL_TCP)
495                 fib_index0 =
496                   c0->fib_index
497                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT];
498             }
499
500           if (fib_index0 != ~0)
501             {
502               lb_index0 = ip4_fib_forwarding_lookup (fib_index0,
503                                                      &ip0->src_address);
504
505               dpo =
506                 load_balance_get_bucket_i (load_balance_get (lb_index0), 0);
507
508               if (ppr_dpo_type == dpo->dpoi_type)
509                 {
510                   ppr_dpo0 = protocol_port_range_dpo_get (dpo->dpoi_index);
511                 }
512               /*
513                * else the lookup hit an enty that was no inserted
514                * by this range checker, which is the default route
515                */
516             }
517           /*
518            * $$$ which (src,dst) categories should we always pass?
519            */
520           pass0 = 0;
521           pass0 |= ip4_address_is_multicast (&ip0->src_address);
522           pass0 |=
523             ip0->src_address.as_u32 == clib_host_to_net_u32 (0xFFFFFFFF);
524           pass0 |= (ip0->protocol != IP_PROTOCOL_UDP)
525             && (ip0->protocol != IP_PROTOCOL_TCP);
526
527           save_next0 = next0;
528           udp0 = ip4_next_header (ip0);
529
530           if (PREDICT_TRUE (pass0 == 0))
531             {
532               good_packets++;
533               next0 = check_adj_port_range_x1
534                 (ppr_dpo0, clib_net_to_host_u16 (udp0->dst_port), next0);
535               good_packets -= (save_next0 != next0);
536               b0->error = error_node->errors
537                 [IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_FAIL];
538             }
539
540           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
541                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
542             {
543               ip4_source_and_port_range_check_trace_t *t =
544                 vlib_add_trace (vm, node, b0, sizeof (*t));
545               t->pass = next0 == save_next0;
546               t->bypass = pass0;
547               t->fib_index = fib_index0;
548               t->src_addr.as_u32 = ip0->src_address.as_u32;
549               t->port = (pass0 == 0) ?
550                 clib_net_to_host_u16 (udp0->dst_port) : 0;
551               t->is_tcp = ip0->protocol == IP_PROTOCOL_TCP;
552             }
553
554           if (is_tx)
555             vlib_buffer_advance (b0, -sizeof (ethernet_header_t));
556
557           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
558                                            to_next, n_left_to_next,
559                                            bi0, next0);
560         }
561
562       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
563     }
564
565   if (is_tx)
566     vlib_node_increment_counter (vm, ip4_source_port_and_range_check_tx.index,
567                                  IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_OK,
568                                  good_packets);
569   else
570     vlib_node_increment_counter (vm, ip4_source_port_and_range_check_rx.index,
571                                  IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_OK,
572                                  good_packets);
573   return frame->n_vectors;
574   return 0;
575 }
576
577 static uword
578 ip4_source_and_port_range_check_rx (vlib_main_t * vm,
579                                     vlib_node_runtime_t * node,
580                                     vlib_frame_t * frame)
581 {
582   return ip4_source_and_port_range_check_inline (vm, node, frame,
583                                                  0 /* !is_tx */ );
584 }
585
586 static uword
587 ip4_source_and_port_range_check_tx (vlib_main_t * vm,
588                                     vlib_node_runtime_t * node,
589                                     vlib_frame_t * frame)
590 {
591   return ip4_source_and_port_range_check_inline (vm, node, frame,
592                                                  1 /* is_tx */ );
593 }
594
595 /* Note: Calling same function for both RX and TX nodes
596    as always checking dst_port, although
597    if this changes can easily make new function
598 */
599
600 /* *INDENT-OFF* */
601 VLIB_REGISTER_NODE (ip4_source_port_and_range_check_rx) = {
602   .function = ip4_source_and_port_range_check_rx,
603   .name = "ip4-source-and-port-range-check-rx",
604   .vector_size = sizeof (u32),
605
606   .n_errors = ARRAY_LEN(ip4_source_and_port_range_check_error_strings),
607   .error_strings = ip4_source_and_port_range_check_error_strings,
608
609   .n_next_nodes = IP4_SOURCE_AND_PORT_RANGE_CHECK_N_NEXT,
610   .next_nodes = {
611     [IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP] = "error-drop",
612   },
613
614   .format_buffer = format_ip4_header,
615   .format_trace = format_ip4_source_and_port_range_check_trace,
616 };
617 /* *INDENT-ON* */
618
619 /* *INDENT-OFF* */
620 VLIB_REGISTER_NODE (ip4_source_port_and_range_check_tx) = {
621   .function = ip4_source_and_port_range_check_tx,
622   .name = "ip4-source-and-port-range-check-tx",
623   .vector_size = sizeof (u32),
624
625   .n_errors = ARRAY_LEN(ip4_source_and_port_range_check_error_strings),
626   .error_strings = ip4_source_and_port_range_check_error_strings,
627
628   .n_next_nodes = IP4_SOURCE_AND_PORT_RANGE_CHECK_N_NEXT,
629   .next_nodes = {
630     [IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP] = "error-drop",
631   },
632
633   .format_buffer = format_ip4_header,
634   .format_trace = format_ip4_source_and_port_range_check_trace,
635 };
636 /* *INDENT-ON* */
637
638 int
639 set_ip_source_and_port_range_check (vlib_main_t * vm,
640                                     u32 * fib_index,
641                                     u32 sw_if_index, u32 is_add)
642 {
643   ip4_main_t *im = &ip4_main;
644   ip_lookup_main_t *lm = &im->lookup_main;
645   ip_config_main_t *rx_cm =
646     &lm->feature_config_mains[VNET_IP_RX_UNICAST_FEAT];
647   ip_config_main_t *tx_cm = &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_NULL;
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_NULL;
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_table_entry_special_dpo_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_NULL;
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_NULL;
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  */