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