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