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