ebfa767d8f048bc858371e47fa4845d715971941
[vpp.git] / vnet / vnet / ip / ip4_source_and_port_range_check.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *         http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <vnet/ip/ip.h>
16 #include <vnet/ip/ip_source_and_port_range_check.h>
17
18
19 vlib_node_registration_t ip4_source_port_and_range_check_rx;
20 vlib_node_registration_t ip4_source_port_and_range_check_tx;
21
22 #define foreach_ip4_source_and_port_range_check_error                   \
23   _(CHECK_FAIL, "ip4 source and port range check bad packets")  \
24   _(CHECK_OK, "ip4 source and port range check good packets")
25
26 typedef enum
27 {
28 #define _(sym,str) IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_##sym,
29   foreach_ip4_source_and_port_range_check_error
30 #undef _
31     IP4_SOURCE_AND_PORT_RANGE_CHECK_N_ERROR,
32 } ip4_source_and_port_range_check_error_t;
33
34 static char *ip4_source_and_port_range_check_error_strings[] = {
35 #define _(sym,string) string,
36   foreach_ip4_source_and_port_range_check_error
37 #undef _
38 };
39
40 typedef struct
41 {
42   u32 pass;
43   u32 bypass;
44   u32 is_tcp;
45   ip4_address_t src_addr;
46   u16 port;
47   u32 fib_index;
48 } ip4_source_and_port_range_check_trace_t;
49
50 static u8 *
51 format_ip4_source_and_port_range_check_trace (u8 * s, va_list * va)
52 {
53   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
54   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
55   ip4_source_and_port_range_check_trace_t *t =
56     va_arg (*va, ip4_source_and_port_range_check_trace_t *);
57
58   if (t->bypass)
59     s = format (s, "PASS (bypass case)");
60   else
61     s = format (s, "fib %d src ip %U %s dst port %d: %s",
62                 t->fib_index, format_ip4_address, &t->src_addr,
63                 t->is_tcp ? "TCP" : "UDP", (u32) t->port,
64                 (t->pass == 1) ? "PASS" : "FAIL");
65   return s;
66 }
67
68 typedef enum
69 {
70   IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP,
71   IP4_SOURCE_AND_PORT_RANGE_CHECK_N_NEXT,
72 } ip4_source_and_port_range_check_next_t;
73
74
75 static inline u32
76 check_adj_port_range_x1 (ip_adjacency_t * adj, u16 dst_port, u32 next)
77 {
78   protocol_port_range_t *range;
79   u16x8vec_t key;
80   u16x8vec_t diff1;
81   u16x8vec_t diff2;
82   u16x8vec_t sum, sum_equal_diff2;
83   u16 sum_nonzero, sum_equal, winner_mask;
84   int i;
85   u8 *rwh;
86
87   if (adj->lookup_next_index != IP_LOOKUP_NEXT_ICMP_ERROR || dst_port == 0)
88     return IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP;
89
90   rwh = (u8 *) (&adj->rewrite_header);
91   range = (protocol_port_range_t *) rwh;
92
93   /* Make the obvious screw-case work. A variant also works w/ no MMX */
94   if (PREDICT_FALSE (dst_port == 65535))
95     {
96       int j;
97
98       for (i = 0;
99            i < VLIB_BUFFER_PRE_DATA_SIZE / sizeof (protocol_port_range_t);
100            i++)
101         {
102           for (j = 0; j < 8; j++)
103             if (range->low.as_u16[j] == 65535)
104               return next;
105           range++;
106         }
107       return IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP;
108     }
109
110   key.as_u16x8 = u16x8_splat (dst_port);
111
112   for (i = 0; i < VLIB_BUFFER_PRE_DATA_SIZE / sizeof (protocol_port_range_t);
113        i++)
114     {
115       diff1.as_u16x8 = u16x8_sub_saturate (range->low.as_u16x8, key.as_u16x8);
116       diff2.as_u16x8 = u16x8_sub_saturate (range->hi.as_u16x8, key.as_u16x8);
117       sum.as_u16x8 = u16x8_add (diff1.as_u16x8, diff2.as_u16x8);
118       sum_equal_diff2.as_u16x8 =
119         u16x8_is_equal (sum.as_u16x8, diff2.as_u16x8);
120       sum_nonzero = ~u16x8_zero_byte_mask (sum.as_u16x8);
121       sum_equal = ~u16x8_zero_byte_mask (sum_equal_diff2.as_u16x8);
122       winner_mask = sum_nonzero & sum_equal;
123       if (winner_mask)
124         return next;
125       range++;
126     }
127   return IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP;
128 }
129
130 always_inline uword
131 ip4_source_and_port_range_check_inline (vlib_main_t * vm,
132                                         vlib_node_runtime_t * node,
133                                         vlib_frame_t * frame, int is_tx)
134 {
135   ip4_main_t *im = &ip4_main;
136   ip_lookup_main_t *lm = &im->lookup_main;
137   ip_config_main_t *rx_cm =
138     &lm->feature_config_mains[VNET_IP_RX_UNICAST_FEAT];
139   ip_config_main_t *tx_cm = &lm->feature_config_mains[VNET_IP_TX_FEAT];
140   u32 n_left_from, *from, *to_next;
141   u32 next_index;
142   vlib_node_runtime_t *error_node = node;
143   u32 good_packets = 0;
144   int i;
145
146   from = vlib_frame_vector_args (frame);
147   n_left_from = frame->n_vectors;
148   next_index = node->cached_next_index;
149
150   while (n_left_from > 0)
151     {
152       u32 n_left_to_next;
153
154       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
155
156
157       while (n_left_from >= 4 && n_left_to_next >= 2)
158         {
159           vlib_buffer_t *b0, *b1;
160           ip4_header_t *ip0, *ip1;
161           ip4_fib_mtrie_t *mtrie0, *mtrie1;
162           ip4_fib_mtrie_leaf_t leaf0, leaf1;
163           ip_source_and_port_range_check_config_t *c0, *c1;
164           ip_adjacency_t *adj0 = 0, *adj1 = 0;
165           u32 bi0, next0, adj_index0, pass0, save_next0, fib_index0;
166           u32 bi1, next1, adj_index1, pass1, save_next1, fib_index1;
167           udp_header_t *udp0, *udp1;
168
169           /* Prefetch next iteration. */
170           {
171             vlib_buffer_t *p2, *p3;
172
173             p2 = vlib_get_buffer (vm, from[2]);
174             p3 = vlib_get_buffer (vm, from[3]);
175
176             vlib_prefetch_buffer_header (p2, LOAD);
177             vlib_prefetch_buffer_header (p3, LOAD);
178
179             CLIB_PREFETCH (p2->data, sizeof (ip0[0]), LOAD);
180             CLIB_PREFETCH (p3->data, sizeof (ip1[0]), LOAD);
181           }
182
183           bi0 = to_next[0] = from[0];
184           bi1 = to_next[1] = from[1];
185           from += 2;
186           to_next += 2;
187           n_left_from -= 2;
188           n_left_to_next -= 2;
189
190           b0 = vlib_get_buffer (vm, bi0);
191           b1 = vlib_get_buffer (vm, bi1);
192
193           fib_index0 =
194             vec_elt (im->fib_index_by_sw_if_index,
195                      vnet_buffer (b0)->sw_if_index[VLIB_RX]);
196           fib_index1 =
197             vec_elt (im->fib_index_by_sw_if_index,
198                      vnet_buffer (b1)->sw_if_index[VLIB_RX]);
199
200           ip0 = vlib_buffer_get_current (b0);
201           ip1 = vlib_buffer_get_current (b1);
202
203           if (is_tx)
204             {
205               c0 = vnet_get_config_data (&tx_cm->config_main,
206                                          &b0->current_config_index,
207                                          &next0, sizeof (c0[0]));
208               c1 = vnet_get_config_data (&tx_cm->config_main,
209                                          &b1->current_config_index,
210                                          &next1, sizeof (c1[0]));
211             }
212           else
213             {
214               c0 = vnet_get_config_data (&rx_cm->config_main,
215                                          &b0->current_config_index,
216                                          &next0, sizeof (c0[0]));
217               c1 = vnet_get_config_data (&rx_cm->config_main,
218                                          &b1->current_config_index,
219                                          &next1, sizeof (c1[0]));
220             }
221
222           /* we can't use the default VRF here... */
223           for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++)
224             {
225               ASSERT (c0->fib_index[i] && c1->fib_index[i]);
226             }
227
228
229           if (is_tx)
230             {
231               if (ip0->protocol == IP_PROTOCOL_UDP)
232                 fib_index0 =
233                   c0->fib_index
234                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN];
235               if (ip0->protocol == IP_PROTOCOL_TCP)
236                 fib_index0 =
237                   c0->fib_index
238                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN];
239             }
240           else
241             {
242               if (ip0->protocol == IP_PROTOCOL_UDP)
243                 fib_index0 =
244                   c0->fib_index
245                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT];
246               if (ip0->protocol == IP_PROTOCOL_TCP)
247                 fib_index0 =
248                   c0->fib_index
249                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT];
250             }
251
252           if (PREDICT_TRUE (fib_index0 != ~0))
253             {
254
255               mtrie0 = &vec_elt_at_index (im->fibs, fib_index0)->mtrie;
256
257               leaf0 = IP4_FIB_MTRIE_LEAF_ROOT;
258
259               leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0,
260                                                  &ip0->src_address, 0);
261
262               leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0,
263                                                  &ip0->src_address, 1);
264
265               leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0,
266                                                  &ip0->src_address, 2);
267
268               leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0,
269                                                  &ip0->src_address, 3);
270
271               adj_index0 = ip4_fib_mtrie_leaf_get_adj_index (leaf0);
272
273               ASSERT (adj_index0 == ip4_fib_lookup_with_table (im, fib_index0,
274                                                                &ip0->src_address,
275                                                                0
276                                                                /* use dflt rt */
277                       ));
278               adj0 = ip_get_adjacency (lm, adj_index0);
279             }
280
281           if (is_tx)
282             {
283               if (ip1->protocol == IP_PROTOCOL_UDP)
284                 fib_index1 =
285                   c1->fib_index
286                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN];
287               if (ip1->protocol == IP_PROTOCOL_TCP)
288                 fib_index1 =
289                   c1->fib_index
290                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN];
291             }
292           else
293             {
294               if (ip1->protocol == IP_PROTOCOL_UDP)
295                 fib_index1 =
296                   c1->fib_index
297                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT];
298               if (ip1->protocol == IP_PROTOCOL_TCP)
299                 fib_index1 =
300                   c1->fib_index
301                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT];
302             }
303
304           if (PREDICT_TRUE (fib_index1 != ~0))
305             {
306
307               mtrie1 = &vec_elt_at_index (im->fibs, fib_index1)->mtrie;
308
309               leaf1 = IP4_FIB_MTRIE_LEAF_ROOT;
310
311               leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1,
312                                                  &ip1->src_address, 0);
313
314               leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1,
315                                                  &ip1->src_address, 1);
316
317               leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1,
318                                                  &ip1->src_address, 2);
319
320               leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1,
321                                                  &ip1->src_address, 3);
322
323               adj_index1 = ip4_fib_mtrie_leaf_get_adj_index (leaf1);
324
325               ASSERT (adj_index1 == ip4_fib_lookup_with_table (im, fib_index1,
326                                                                &ip1->src_address,
327                                                                0));
328               adj1 = ip_get_adjacency (lm, adj_index1);
329             }
330
331           pass0 = 0;
332           pass0 |= adj0 == 0;
333           pass0 |= ip4_address_is_multicast (&ip0->src_address);
334           pass0 |=
335             ip0->src_address.as_u32 == clib_host_to_net_u32 (0xFFFFFFFF);
336           pass0 |= (ip0->protocol != IP_PROTOCOL_UDP)
337             && (ip0->protocol != IP_PROTOCOL_TCP);
338
339           pass1 = 0;
340           pass1 |= adj1 == 0;
341           pass1 |= ip4_address_is_multicast (&ip1->src_address);
342           pass1 |=
343             ip1->src_address.as_u32 == clib_host_to_net_u32 (0xFFFFFFFF);
344           pass1 |= (ip1->protocol != IP_PROTOCOL_UDP)
345             && (ip1->protocol != IP_PROTOCOL_TCP);
346
347           save_next0 = next0;
348           udp0 = ip4_next_header (ip0);
349           save_next1 = next1;
350           udp1 = ip4_next_header (ip1);
351
352           if (PREDICT_TRUE (pass0 == 0))
353             {
354               good_packets++;
355               next0 = check_adj_port_range_x1
356                 (adj0, clib_net_to_host_u16 (udp0->dst_port), next0);
357               good_packets -= (save_next0 != next0);
358               b0->error = error_node->errors
359                 [IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_FAIL];
360             }
361
362           if (PREDICT_TRUE (pass1 == 0))
363             {
364               good_packets++;
365               next1 = check_adj_port_range_x1
366                 (adj1, clib_net_to_host_u16 (udp1->dst_port), next1);
367               good_packets -= (save_next1 != next1);
368               b1->error = error_node->errors
369                 [IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_FAIL];
370             }
371
372           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
373                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
374             {
375               ip4_source_and_port_range_check_trace_t *t =
376                 vlib_add_trace (vm, node, b0, sizeof (*t));
377               t->pass = next0 == save_next0;
378               t->bypass = pass0;
379               t->fib_index = fib_index0;
380               t->src_addr.as_u32 = ip0->src_address.as_u32;
381               t->port = (pass0 == 0) ?
382                 clib_net_to_host_u16 (udp0->dst_port) : 0;
383               t->is_tcp = ip0->protocol == IP_PROTOCOL_TCP;
384             }
385
386           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
387                              && (b1->flags & VLIB_BUFFER_IS_TRACED)))
388             {
389               ip4_source_and_port_range_check_trace_t *t =
390                 vlib_add_trace (vm, node, b1, sizeof (*t));
391               t->pass = next1 == save_next1;
392               t->bypass = pass1;
393               t->fib_index = fib_index1;
394               t->src_addr.as_u32 = ip1->src_address.as_u32;
395               t->port = (pass1 == 0) ?
396                 clib_net_to_host_u16 (udp1->dst_port) : 0;
397               t->is_tcp = ip1->protocol == IP_PROTOCOL_TCP;
398             }
399
400           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
401                                            to_next, n_left_to_next,
402                                            bi0, bi1, next0, next1);
403         }
404
405       while (n_left_from > 0 && n_left_to_next > 0)
406         {
407           vlib_buffer_t *b0;
408           ip4_header_t *ip0;
409           ip4_fib_mtrie_t *mtrie0;
410           ip4_fib_mtrie_leaf_t leaf0;
411           ip_source_and_port_range_check_config_t *c0;
412           ip_adjacency_t *adj0 = 0;
413           u32 bi0, next0, adj_index0, pass0, save_next0, fib_index0;
414           udp_header_t *udp0;
415
416           bi0 = from[0];
417           to_next[0] = bi0;
418           from += 1;
419           to_next += 1;
420           n_left_from -= 1;
421           n_left_to_next -= 1;
422
423           b0 = vlib_get_buffer (vm, bi0);
424
425           fib_index0 =
426             vec_elt (im->fib_index_by_sw_if_index,
427                      vnet_buffer (b0)->sw_if_index[VLIB_RX]);
428
429           if (is_tx)
430             vlib_buffer_advance (b0, sizeof (ethernet_header_t));
431
432           ip0 = vlib_buffer_get_current (b0);
433
434           if (is_tx)
435             {
436               c0 = vnet_get_config_data
437                 (&tx_cm->config_main, &b0->current_config_index,
438                  &next0, sizeof (c0[0]));
439             }
440           else
441             {
442               c0 = vnet_get_config_data
443                 (&rx_cm->config_main, &b0->current_config_index,
444                  &next0, sizeof (c0[0]));
445             }
446
447           /* we can't use the default VRF here... */
448           for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++)
449             {
450               ASSERT (c0->fib_index[i]);
451             }
452
453
454           if (is_tx)
455             {
456               if (ip0->protocol == IP_PROTOCOL_UDP)
457                 fib_index0 =
458                   c0->fib_index
459                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN];
460               if (ip0->protocol == IP_PROTOCOL_TCP)
461                 fib_index0 =
462                   c0->fib_index
463                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN];
464             }
465           else
466             {
467               if (ip0->protocol == IP_PROTOCOL_UDP)
468                 fib_index0 =
469                   c0->fib_index
470                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT];
471               if (ip0->protocol == IP_PROTOCOL_TCP)
472                 fib_index0 =
473                   c0->fib_index
474                   [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT];
475             }
476
477           if (fib_index0 != ~0)
478             {
479
480               mtrie0 = &vec_elt_at_index (im->fibs, fib_index0)->mtrie;
481
482               leaf0 = IP4_FIB_MTRIE_LEAF_ROOT;
483
484               leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0,
485                                                  &ip0->src_address, 0);
486
487               leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0,
488                                                  &ip0->src_address, 1);
489
490               leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0,
491                                                  &ip0->src_address, 2);
492
493               leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0,
494                                                  &ip0->src_address, 3);
495
496               adj_index0 = ip4_fib_mtrie_leaf_get_adj_index (leaf0);
497
498               ASSERT (adj_index0 == ip4_fib_lookup_with_table
499                       (im, fib_index0,
500                        &ip0->src_address, 0 /* use default route */ ));
501               adj0 = ip_get_adjacency (lm, adj_index0);
502             }
503           /*
504            * $$$ which (src,dst) categories should we always pass?
505            */
506           pass0 = 0;
507           pass0 |= adj0 == 0;
508           pass0 |= ip4_address_is_multicast (&ip0->src_address);
509           pass0 |=
510             ip0->src_address.as_u32 == clib_host_to_net_u32 (0xFFFFFFFF);
511           pass0 |= (ip0->protocol != IP_PROTOCOL_UDP)
512             && (ip0->protocol != IP_PROTOCOL_TCP);
513
514           save_next0 = next0;
515           udp0 = ip4_next_header (ip0);
516
517           if (PREDICT_TRUE (pass0 == 0))
518             {
519               good_packets++;
520               next0 = check_adj_port_range_x1
521                 (adj0, clib_net_to_host_u16 (udp0->dst_port), next0);
522               good_packets -= (save_next0 != next0);
523               b0->error = error_node->errors
524                 [IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_FAIL];
525             }
526
527           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
528                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
529             {
530               ip4_source_and_port_range_check_trace_t *t =
531                 vlib_add_trace (vm, node, b0, sizeof (*t));
532               t->pass = next0 == save_next0;
533               t->bypass = pass0;
534               t->fib_index = fib_index0;
535               t->src_addr.as_u32 = ip0->src_address.as_u32;
536               t->port = (pass0 == 0) ?
537                 clib_net_to_host_u16 (udp0->dst_port) : 0;
538               t->is_tcp = ip0->protocol == IP_PROTOCOL_TCP;
539             }
540
541           if (is_tx)
542             vlib_buffer_advance (b0, -sizeof (ethernet_header_t));
543
544           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
545                                            to_next, n_left_to_next,
546                                            bi0, next0);
547         }
548
549       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
550     }
551
552   if (is_tx)
553     vlib_node_increment_counter (vm, ip4_source_port_and_range_check_tx.index,
554                                  IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_OK,
555                                  good_packets);
556   else
557     vlib_node_increment_counter (vm, ip4_source_port_and_range_check_rx.index,
558                                  IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_OK,
559                                  good_packets);
560   return frame->n_vectors;
561 }
562
563 static uword
564 ip4_source_and_port_range_check_rx (vlib_main_t * vm,
565                                     vlib_node_runtime_t * node,
566                                     vlib_frame_t * frame)
567 {
568   return ip4_source_and_port_range_check_inline (vm, node, frame,
569                                                  0 /* !is_tx */ );
570 }
571
572 static uword
573 ip4_source_and_port_range_check_tx (vlib_main_t * vm,
574                                     vlib_node_runtime_t * node,
575                                     vlib_frame_t * frame)
576 {
577   return ip4_source_and_port_range_check_inline (vm, node, frame,
578                                                  1 /* is_tx */ );
579 }
580
581 /* Note: Calling same function for both RX and TX nodes
582    as always checking dst_port, although
583    if this changes can easily make new function
584 */
585
586 /* *INDENT-OFF* */
587 VLIB_REGISTER_NODE (ip4_source_port_and_range_check_rx) = {
588   .function = ip4_source_and_port_range_check_rx,
589   .name = "ip4-source-and-port-range-check-rx",
590   .vector_size = sizeof (u32),
591
592   .n_errors = ARRAY_LEN(ip4_source_and_port_range_check_error_strings),
593   .error_strings = ip4_source_and_port_range_check_error_strings,
594
595   .n_next_nodes = IP4_SOURCE_AND_PORT_RANGE_CHECK_N_NEXT,
596   .next_nodes = {
597     [IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP] = "error-drop",
598   },
599
600   .format_buffer = format_ip4_header,
601   .format_trace = format_ip4_source_and_port_range_check_trace,
602 };
603 /* *INDENT-ON* */
604
605 /* *INDENT-OFF* */
606 VLIB_REGISTER_NODE (ip4_source_port_and_range_check_tx) = {
607   .function = ip4_source_and_port_range_check_tx,
608   .name = "ip4-source-and-port-range-check-tx",
609   .vector_size = sizeof (u32),
610
611   .n_errors = ARRAY_LEN(ip4_source_and_port_range_check_error_strings),
612   .error_strings = ip4_source_and_port_range_check_error_strings,
613
614   .n_next_nodes = IP4_SOURCE_AND_PORT_RANGE_CHECK_N_NEXT,
615   .next_nodes = {
616     [IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP] = "error-drop",
617   },
618
619   .format_buffer = format_ip4_header,
620   .format_trace = format_ip4_source_and_port_range_check_trace,
621 };
622 /* *INDENT-ON* */
623
624 int
625 set_ip_source_and_port_range_check (vlib_main_t * vm,
626                                     u32 * fib_index,
627                                     u32 sw_if_index, u32 is_add)
628 {
629   ip4_main_t *im = &ip4_main;
630   ip_lookup_main_t *lm = &im->lookup_main;
631   ip_config_main_t *rx_cm =
632     &lm->feature_config_mains[VNET_IP_RX_UNICAST_FEAT];
633   ip_config_main_t *tx_cm = &lm->feature_config_mains[VNET_IP_TX_FEAT];
634   u32 ci;
635   ip_source_and_port_range_check_config_t config;
636   u32 feature_index;
637   int rv = 0;
638   int i;
639
640   for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++)
641     {
642       config.fib_index[i] = fib_index[i];
643     }
644
645   /* For OUT we are in the RX path */
646   if ((fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT] != ~0) ||
647       (fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT] != ~0))
648     {
649       feature_index = im->ip4_unicast_rx_feature_source_and_port_range_check;
650
651       vec_validate (rx_cm->config_index_by_sw_if_index, sw_if_index);
652
653       ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
654       ci = (is_add
655             ? vnet_config_add_feature
656             : vnet_config_del_feature)
657         (vm, &rx_cm->config_main, ci, feature_index, &config,
658          sizeof (config));
659       rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
660     }
661
662   /* For IN we are in the TX path */
663   if ((fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN] != ~0) ||
664       (fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN] != ~0))
665     {
666       feature_index = im->ip4_unicast_tx_feature_source_and_port_range_check;
667
668       vec_validate (tx_cm->config_index_by_sw_if_index, sw_if_index);
669
670       ci = tx_cm->config_index_by_sw_if_index[sw_if_index];
671       ci = (is_add
672             ? vnet_config_add_feature
673             : vnet_config_del_feature)
674         (vm, &tx_cm->config_main, ci, feature_index, &config,
675          sizeof (config));
676       tx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
677
678       vnet_config_update_tx_feature_count (lm, tx_cm, sw_if_index, is_add);
679     }
680   return rv;
681 }
682
683 static clib_error_t *
684 set_ip_source_and_port_range_check_fn (vlib_main_t * vm,
685                                        unformat_input_t * input,
686                                        vlib_cli_command_t * cmd)
687 {
688   vnet_main_t *vnm = vnet_get_main ();
689   ip4_main_t *im = &ip4_main;
690   clib_error_t *error = 0;
691   u8 is_add = 1;
692   u32 sw_if_index = ~0;
693   u32 vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS];
694   u32 fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS];
695   int vrf_set = 0;
696   uword *p;
697   int rv = 0;
698   int i;
699
700   sw_if_index = ~0;
701   for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++)
702     {
703       fib_index[i] = ~0;
704       vrf_id[i] = ~0;
705     }
706
707   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
708     {
709       if (unformat (input, "%U", unformat_vnet_sw_interface, vnm,
710                     &sw_if_index))
711         ;
712       else
713         if (unformat
714             (input, "tcp-out-vrf %d",
715              &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT]))
716         vrf_set = 1;
717       else
718         if (unformat
719             (input, "udp-out-vrf %d",
720              &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT]))
721         vrf_set = 1;
722       else
723         if (unformat
724             (input, "tcp-in-vrf %d",
725              &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN]))
726         vrf_set = 1;
727       else
728         if (unformat
729             (input, "udp-in-vrf %d",
730              &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN]))
731         vrf_set = 1;
732       else if (unformat (input, "del"))
733         is_add = 0;
734       else
735         break;
736     }
737
738   if (sw_if_index == ~0)
739     return clib_error_return (0, "Interface required but not specified");
740
741   if (!vrf_set)
742     return clib_error_return (0,
743                               "TCP or UDP VRF ID required but not specified");
744
745   for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++)
746     {
747
748       if (vrf_id[i] == 0)
749         return clib_error_return (0,
750                                   "TCP, UDP VRF ID should not be 0 (default). Should be distinct VRF for this purpose. ");
751
752       if (vrf_id[i] != ~0)
753         {
754           p = hash_get (im->fib_index_by_table_id, vrf_id[i]);
755
756           if (p == 0)
757             return clib_error_return (0, "Invalid VRF ID %d", vrf_id[i]);
758
759           fib_index[i] = p[0];
760         }
761     }
762   rv =
763     set_ip_source_and_port_range_check (vm, fib_index, sw_if_index, is_add);
764
765   switch (rv)
766     {
767     case 0:
768       break;
769
770     default:
771       return clib_error_return
772         (0,
773          "set source and port-range on interface returned an unexpected value: %d",
774          rv);
775     }
776   return error;
777 }
778
779 /* *INDENT-OFF* */
780 VLIB_CLI_COMMAND (set_interface_ip_source_and_port_range_check_command,
781                   static) = {
782   .path = "set interface ip source-and-port-range-check",
783   .function = set_ip_source_and_port_range_check_fn,
784   .short_help = "set int ip source-and-port-range-check <intfc> [tcp-out-vrf <n>] [udp-out-vrf <n>] [tcp-in-vrf <n>] [udp-in-vrf <n>] [del]",
785 };
786 /* *INDENT-ON* */
787
788 static u8 *
789 format_source_and_port_rc_adjacency (u8 * s, va_list * args)
790 {
791   CLIB_UNUSED (vnet_main_t * vnm) = va_arg (*args, vnet_main_t *);
792   ip_lookup_main_t *lm = va_arg (*args, ip_lookup_main_t *);
793   u32 adj_index = va_arg (*args, u32);
794   ip_adjacency_t *adj = ip_get_adjacency (lm, adj_index);
795   source_range_check_main_t *srm = &source_range_check_main;
796   u8 *rwh = (u8 *) (&adj->rewrite_header);
797   protocol_port_range_t *range;
798   int i, j;
799   int printed = 0;
800
801   range = (protocol_port_range_t *) rwh;
802
803   s = format (s, "allow ");
804
805   for (i = 0; i < srm->ranges_per_adjacency; i++)
806     {
807       for (j = 0; j < 8; j++)
808         {
809           if (range->low.as_u16[j])
810             {
811               if (printed)
812                 s = format (s, ", ");
813               if (range->hi.as_u16[j] > (range->low.as_u16[j] + 1))
814                 s = format (s, "%d-%d", (u32) range->low.as_u16[j],
815                             (u32) range->hi.as_u16[j] - 1);
816               else
817                 s = format (s, "%d", range->low.as_u16[j]);
818               printed = 1;
819             }
820         }
821       range++;
822     }
823   return s;
824 }
825
826 clib_error_t *
827 ip4_source_and_port_range_check_init (vlib_main_t * vm)
828 {
829   source_range_check_main_t *srm = &source_range_check_main;
830   ip4_main_t *im = &ip4_main;
831   ip_lookup_main_t *lm = &im->lookup_main;
832
833   srm->vlib_main = vm;
834   srm->vnet_main = vnet_get_main ();
835
836   srm->ranges_per_adjacency =
837     VLIB_BUFFER_PRE_DATA_SIZE / (2 * sizeof (u16x8));
838   srm->special_adjacency_format_function_index =
839     vnet_register_special_adjacency_format_function (lm,
840                                                      format_source_and_port_rc_adjacency);
841   ASSERT (srm->special_adjacency_format_function_index);
842
843   return 0;
844 }
845
846 VLIB_INIT_FUNCTION (ip4_source_and_port_range_check_init);
847
848 int
849 add_port_range_adjacency (ip4_address_t * address,
850                           u32 length,
851                           u32 adj_index,
852                           u16 * low_ports, u16 * high_ports, u32 fib_index)
853 {
854   ip_adjacency_t *adj;
855   int i, j, k;
856   source_range_check_main_t *srm = &source_range_check_main;
857   ip4_main_t *im = &ip4_main;
858   ip_lookup_main_t *lm = &im->lookup_main;
859   protocol_port_range_t *range;
860   u8 *rwh;
861
862   adj = ip_get_adjacency (lm, adj_index);
863   /* $$$$ fixme: add ports if address + mask match */
864   if (adj->lookup_next_index == IP_LOOKUP_NEXT_ICMP_ERROR)
865     return VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE;
866
867   ip_adjacency_t template_adj;
868   ip4_add_del_route_args_t a;
869
870   memset (&template_adj, 0, sizeof (template_adj));
871
872   template_adj.lookup_next_index = IP_LOOKUP_NEXT_ICMP_ERROR;
873   template_adj.if_address_index = ~0;
874   template_adj.special_adjacency_format_function_index =
875     srm->special_adjacency_format_function_index;
876
877   rwh = (u8 *) (&template_adj.rewrite_header);
878
879   range = (protocol_port_range_t *) rwh;
880
881   if (vec_len (low_ports) > 8 * srm->ranges_per_adjacency)
882     return VNET_API_ERROR_EXCEEDED_NUMBER_OF_RANGES_CAPACITY;
883
884   j = k = 0;
885
886   for (i = 0; i < vec_len (low_ports); i++)
887     {
888       for (; j < srm->ranges_per_adjacency; j++)
889         {
890           for (; k < 8; k++)
891             {
892               if (range->low.as_u16[k] == 0)
893                 {
894                   range->low.as_u16[k] = low_ports[i];
895                   range->hi.as_u16[k] = high_ports[i];
896                   k++;
897                   if (k == 7)
898                     {
899                       k = 0;
900                       j++;
901                     }
902                   goto doublebreak2;
903                 }
904             }
905           k = 0;
906           range++;
907         }
908       j = 0;
909       /* Too many ports specified... */
910       return VNET_API_ERROR_EXCEEDED_NUMBER_OF_PORTS_CAPACITY;
911
912     doublebreak2:;
913     }
914
915   memset (&a, 0, sizeof (a));
916   a.flags = IP4_ROUTE_FLAG_FIB_INDEX;
917   a.table_index_or_table_id = fib_index;
918   a.dst_address = address[0];
919   a.dst_address_length = length;
920   a.add_adj = &template_adj;
921   a.n_add_adj = 1;
922
923   ip4_add_del_route (im, &a);
924   return 0;
925 }
926
927 int
928 remove_port_range_adjacency (ip4_address_t * address,
929                              u32 length,
930                              u32 adj_index,
931                              u16 * low_ports, u16 * high_ports, u32 fib_index)
932 {
933   ip_adjacency_t *adj;
934   int i, j, k;
935   source_range_check_main_t *srm = &source_range_check_main;
936   ip4_main_t *im = &ip4_main;
937   ip_lookup_main_t *lm = &im->lookup_main;
938   protocol_port_range_t *range;
939   u8 *rwh;
940
941   adj = ip_get_adjacency (lm, adj_index);
942   if (adj->lookup_next_index != IP_LOOKUP_NEXT_ICMP_ERROR)      /* _ICMP_ERROR is a dummy placeholder */
943     return VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE;
944
945   rwh = (u8 *) (&adj->rewrite_header);
946
947   for (i = 0; i < vec_len (low_ports); i++)
948     {
949       range = (protocol_port_range_t *) rwh;
950       for (j = 0; j < srm->ranges_per_adjacency; j++)
951         {
952           for (k = 0; k < 8; k++)
953             {
954               if (low_ports[i] == range->low.as_u16[k] &&
955                   high_ports[i] == range->hi.as_u16[k])
956                 {
957                   range->low.as_u16[k] = range->hi.as_u16[k] = 0;
958                   goto doublebreak;
959                 }
960             }
961           range++;
962         }
963     doublebreak:;
964     }
965
966   range = (protocol_port_range_t *) rwh;
967   /* Have we deleted all ranges yet? */
968   for (i = 0; i < srm->ranges_per_adjacency; i++)
969     {
970       for (j = 0; j < 8; j++)
971         {
972           if (range->low.as_u16[i] != 0)
973             goto still_occupied;
974         }
975       range++;
976     }
977   /* Yes, lose the adjacency... */
978   {
979     ip4_add_del_route_args_t a;
980
981     memset (&a, 0, sizeof (a));
982     a.flags = IP4_ROUTE_FLAG_FIB_INDEX | IP4_ROUTE_FLAG_DEL;
983     a.table_index_or_table_id = fib_index;
984     a.dst_address = address[0];
985     a.dst_address_length = length;
986     a.adj_index = adj_index;
987     ip4_add_del_route (im, &a);
988   }
989
990 still_occupied:
991   ;
992   return 0;
993 }
994
995 // This will be moved to another file and implemented post API freeze.
996 int
997 ip6_source_and_port_range_check_add_del (ip6_address_t * address,
998                                          u32 length,
999                                          u32 vrf_id,
1000                                          u16 * low_ports,
1001                                          u16 * high_ports, int is_add)
1002 {
1003   return 0;
1004 }
1005
1006 int
1007 ip4_source_and_port_range_check_add_del (ip4_address_t * address,
1008                                          u32 length,
1009                                          u32 vrf_id,
1010                                          u16 * low_ports,
1011                                          u16 * high_ports, int is_add)
1012 {
1013
1014   ip4_main_t *im = &ip4_main;
1015   //  ip_lookup_main_t * lm = &im->lookup_main;
1016   uword *p;
1017   u32 fib_index;
1018   u32 adj_index;
1019
1020   p = hash_get (im->fib_index_by_table_id, vrf_id);
1021   if (!p)
1022     {
1023       ip4_fib_t *f;
1024       f = find_ip4_fib_by_table_index_or_id (im, vrf_id, 0 /* flags */ );
1025       fib_index = f->index;
1026     }
1027   else
1028     fib_index = p[0];
1029
1030   adj_index = ip4_fib_lookup_with_table
1031     (im, fib_index, address, 0 /* disable_default_route */ );
1032
1033   if (is_add == 0)
1034     {
1035       remove_port_range_adjacency (address, length, adj_index, low_ports,
1036                                    high_ports, fib_index);
1037     }
1038   else
1039     {
1040       add_port_range_adjacency (address, length, adj_index, low_ports,
1041                                 high_ports, fib_index);
1042     }
1043
1044   return 0;
1045 }
1046
1047 static clib_error_t *
1048 ip_source_and_port_range_check_command_fn (vlib_main_t * vm,
1049                                            unformat_input_t * input,
1050                                            vlib_cli_command_t * cmd)
1051 {
1052   u16 *low_ports = 0;
1053   u16 *high_ports = 0;
1054   u16 this_low;
1055   u16 this_hi;
1056   ip4_address_t ip4_addr;
1057   ip6_address_t ip6_addr;       //This function will be moved to generic impl when v6 done.
1058   u32 length;
1059   u32 tmp, tmp2;
1060   u32 vrf_id = ~0;
1061   int is_add = 1, ip_ver = ~0;
1062   int rv;
1063
1064
1065   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1066     {
1067       if (unformat (input, "%U/%d", unformat_ip4_address, &ip4_addr, &length))
1068         ip_ver = 4;
1069       else
1070         if (unformat
1071             (input, "%U/%d", unformat_ip6_address, &ip6_addr, &length))
1072         ip_ver = 6;
1073       else if (unformat (input, "vrf %d", &vrf_id))
1074         ;
1075       else if (unformat (input, "del"))
1076         is_add = 0;
1077       else if (unformat (input, "port %d", &tmp))
1078         {
1079           if (tmp == 0 || tmp > 65535)
1080             return clib_error_return (0, "port %d out of range", tmp);
1081           this_low = tmp;
1082           this_hi = this_low + 1;
1083           vec_add1 (low_ports, this_low);
1084           vec_add1 (high_ports, this_hi);
1085         }
1086       else if (unformat (input, "range %d - %d", &tmp, &tmp2))
1087         {
1088           if (tmp > tmp2)
1089             return clib_error_return (0, "ports %d and %d out of order",
1090                                       tmp, tmp2);
1091           if (tmp == 0 || tmp > 65535)
1092             return clib_error_return (0, "low port %d out of range", tmp);
1093           if (tmp2 == 0 || tmp2 > 65535)
1094             return clib_error_return (0, "high port %d out of range", tmp2);
1095           this_low = tmp;
1096           this_hi = tmp2 + 1;
1097           vec_add1 (low_ports, this_low);
1098           vec_add1 (high_ports, this_hi);
1099         }
1100       else
1101         break;
1102     }
1103
1104   if (ip_ver == ~0)
1105     return clib_error_return (0, " <address>/<mask> not specified");
1106
1107   if (vrf_id == ~0)
1108     return clib_error_return (0, " VRF ID required, not specified");
1109
1110   if (vec_len (low_ports) == 0)
1111     return clib_error_return (0,
1112                               " Both VRF ID and range/port must be set for a protocol.");
1113
1114   if (vrf_id == 0)
1115     return clib_error_return (0, " VRF ID can not be 0 (default).");
1116
1117
1118   if (ip_ver == 4)
1119     rv = ip4_source_and_port_range_check_add_del
1120       (&ip4_addr, length, vrf_id, low_ports, high_ports, is_add);
1121   else
1122     return clib_error_return (0, " IPv6 in subsequent patch");
1123
1124   switch (rv)
1125     {
1126     case 0:
1127       break;
1128
1129     case VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE:
1130       return clib_error_return
1131         (0, " Incorrect adjacency for add/del operation");
1132
1133     case VNET_API_ERROR_EXCEEDED_NUMBER_OF_PORTS_CAPACITY:
1134       return clib_error_return (0, " Too many ports in add/del operation");
1135
1136     case VNET_API_ERROR_EXCEEDED_NUMBER_OF_RANGES_CAPACITY:
1137       return clib_error_return
1138         (0, " Too many ranges requested for add operation");
1139
1140     default:
1141       return clib_error_return (0, " returned an unexpected value: %d", rv);
1142     }
1143
1144   return 0;
1145 }
1146
1147 /* *INDENT-OFF* */
1148 VLIB_CLI_COMMAND (ip_source_and_port_range_check_command, static) = {
1149   .path = "set ip source-and-port-range-check",
1150   .function = ip_source_and_port_range_check_command_fn,
1151   .short_help =
1152   "set ip source-and-port-range-check <ip-addr>/<mask> [range <nn> - <nn>] [vrf <id>] [del]",
1153 };
1154 /* *INDENT-ON* */
1155
1156
1157 static clib_error_t *
1158 show_source_and_port_range_check_fn (vlib_main_t * vm,
1159                                      unformat_input_t * input,
1160                                      vlib_cli_command_t * cmd)
1161 {
1162   source_range_check_main_t *srm = &source_range_check_main;
1163   ip4_main_t *im = &ip4_main;
1164   ip_lookup_main_t *lm = &im->lookup_main;
1165   protocol_port_range_t *range;
1166   u32 fib_index;
1167   ip4_address_t addr;
1168   u8 addr_set = 0;
1169   u32 vrf_id = ~0;
1170   int rv, i, j;
1171   u32 adj_index;
1172   ip_adjacency_t *adj;
1173   u32 port = 0;
1174   u8 *rwh;
1175   uword *p;
1176
1177   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1178     {
1179       if (unformat (input, "%U", unformat_ip4_address, &addr))
1180         addr_set = 1;
1181       else if (unformat (input, "vrf %d", &vrf_id))
1182         ;
1183       else if (unformat (input, "port %d", &port))
1184         ;
1185       else
1186         break;
1187     }
1188
1189   if (addr_set == 0)
1190     return clib_error_return (0, "<address> not specified");
1191
1192   if (vrf_id == ~0)
1193     return clib_error_return (0, "VRF ID required, not specified");
1194
1195   p = hash_get (im->fib_index_by_table_id, vrf_id);
1196   if (p == 0)
1197     return clib_error_return (0, "VRF %d not found", vrf_id);
1198   fib_index = p[0];
1199
1200   adj_index = ip4_fib_lookup_with_table
1201     (im, fib_index, &addr, 0 /* disable_default_route */ );
1202
1203   adj = ip_get_adjacency (lm, adj_index);
1204
1205   if (adj->lookup_next_index != IP_LOOKUP_NEXT_ICMP_ERROR)
1206     {
1207       vlib_cli_output (vm, "%U: src address drop", format_ip4_address, &addr);
1208       return 0;
1209     }
1210
1211   if (port)
1212     {
1213       rv = check_adj_port_range_x1 (adj, (u16) port, 1234);
1214       if (rv == 1234)
1215         vlib_cli_output (vm, "%U port %d PASS", format_ip4_address,
1216                          &addr, port);
1217       else
1218         vlib_cli_output (vm, "%U port %d FAIL", format_ip4_address,
1219                          &addr, port);
1220       return 0;
1221     }
1222   else
1223     {
1224       u8 *s;
1225       rwh = (u8 *) (&adj->rewrite_header);
1226
1227       s = format (0, "%U: ", format_ip4_address, &addr);
1228
1229       range = (protocol_port_range_t *) rwh;
1230
1231       for (i = 0; i < srm->ranges_per_adjacency; i++)
1232         {
1233           for (j = 0; j < 8; j++)
1234             {
1235               if (range->low.as_u16[j])
1236                 s = format (s, "%d - %d ", (u32) range->low.as_u16[j],
1237                             (u32) range->hi.as_u16[j]);
1238             }
1239           range++;
1240         }
1241       vlib_cli_output (vm, "%s", s);
1242       vec_free (s);
1243     }
1244
1245   return 0;
1246 }
1247
1248 /* *INDENT-OFF* */
1249 VLIB_CLI_COMMAND (show_source_and_port_range_check, static) = {
1250   .path = "show ip source-and-port-range-check",
1251   .function = show_source_and_port_range_check_fn,
1252   .short_help =
1253   "show ip source-and-port-range-check vrf <nn> <ip-addr> <port>",
1254 };
1255 /* *INDENT-ON* */
1256
1257 /*
1258  * fd.io coding-style-patch-verification: ON
1259  *
1260  * Local Variables:
1261  * eval: (c-set-style "gnu")
1262  * End:
1263  */