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