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