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