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