Line data Source code
1 : /*
2 : * Copyright (c) 2016 Cisco and/or its affiliates.
3 : * Licensed under the Apache License, Version 2.0 (the "License");
4 : * you may not use this file except in compliance with the License.
5 : * You may obtain a copy of the License at:
6 : *
7 : * http://www.apache.org/licenses/LICENSE-2.0
8 : *
9 : * Unless required by applicable law or agreed to in writing, software
10 : * distributed under the License is distributed on an "AS IS" BASIS,
11 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : * See the License for the specific language governing permissions and
13 : * limitations under the License.
14 : */
15 : #include <vnet/ip/ip.h>
16 : #include <vnet/ip/ip_source_and_port_range_check.h>
17 : #include <vnet/dpo/load_balance.h>
18 : #include <vnet/fib/fib_table.h>
19 : #include <vnet/fib/ip4_fib.h>
20 :
21 : source_range_check_main_t source_range_check_main;
22 :
23 : /**
24 : * @file
25 : * @brief IPv4 Source and Port Range Checking.
26 : *
27 : * This file contains the source code for IPv4 source and port range
28 : * checking.
29 : */
30 :
31 :
32 : /**
33 : * @brief The pool of range chack DPOs
34 : */
35 : static protocol_port_range_dpo_t *ppr_dpo_pool;
36 :
37 : /**
38 : * @brief Dynamically registered DPO type
39 : */
40 : static dpo_type_t ppr_dpo_type;
41 :
42 : vlib_node_registration_t ip4_source_port_and_range_check_rx;
43 : vlib_node_registration_t ip4_source_port_and_range_check_tx;
44 :
45 : #define foreach_ip4_source_and_port_range_check_error \
46 : _(CHECK_FAIL, "ip4 source and port range check bad packets") \
47 : _(CHECK_OK, "ip4 source and port range check good packets")
48 :
49 : typedef enum
50 : {
51 : #define _(sym,str) IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_##sym,
52 : foreach_ip4_source_and_port_range_check_error
53 : #undef _
54 : IP4_SOURCE_AND_PORT_RANGE_CHECK_N_ERROR,
55 : } ip4_source_and_port_range_check_error_t;
56 :
57 : static char *ip4_source_and_port_range_check_error_strings[] = {
58 : #define _(sym,string) string,
59 : foreach_ip4_source_and_port_range_check_error
60 : #undef _
61 : };
62 :
63 : typedef struct
64 : {
65 : u32 pass;
66 : u32 bypass;
67 : u32 is_tcp;
68 : ip4_address_t src_addr;
69 : u16 port;
70 : u32 fib_index;
71 : } ip4_source_and_port_range_check_trace_t;
72 :
73 : static u8 *
74 0 : format_ip4_source_and_port_range_check_trace (u8 * s, va_list * va)
75 : {
76 0 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
77 0 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
78 0 : ip4_source_and_port_range_check_trace_t *t =
79 : va_arg (*va, ip4_source_and_port_range_check_trace_t *);
80 :
81 0 : if (t->bypass)
82 0 : s = format (s, "PASS (bypass case)");
83 : else
84 0 : s = format (s, "fib %d src ip %U %s dst port %d: %s",
85 : t->fib_index, format_ip4_address, &t->src_addr,
86 0 : t->is_tcp ? "TCP" : "UDP", (u32) t->port,
87 0 : (t->pass == 1) ? "PASS" : "FAIL");
88 0 : return s;
89 : }
90 :
91 : typedef enum
92 : {
93 : IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP,
94 : IP4_SOURCE_AND_PORT_RANGE_CHECK_N_NEXT,
95 : } ip4_source_and_port_range_check_next_t;
96 :
97 :
98 : static inline u32
99 0 : check_adj_port_range_x1 (const protocol_port_range_dpo_t * ppr_dpo,
100 : u16 dst_port, u32 next)
101 : {
102 : #ifdef CLIB_HAVE_VEC128
103 0 : u16x8 key = u16x8_splat (dst_port);
104 : #endif
105 : int i;
106 :
107 0 : if (NULL == ppr_dpo || dst_port == 0)
108 0 : return IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP;
109 :
110 :
111 0 : for (i = 0; i < ppr_dpo->n_used_blocks; i++)
112 : #ifdef CLIB_HAVE_VEC128
113 0 : if (!u16x8_is_all_zero ((ppr_dpo->blocks[i].low.as_u16x8 <= key) &
114 0 : (ppr_dpo->blocks[i].hi.as_u16x8 >= key)))
115 0 : return next;
116 : #else
117 : {
118 : for (int j = 0; j < 8; j++)
119 : {
120 : if ((ppr_dpo->blocks[i].low.as_u16[j] <= dst_port) &&
121 : (ppr_dpo->blocks[i].hi.as_u16[j] >= dst_port))
122 : return next;
123 : }
124 : };
125 : #endif
126 :
127 0 : return IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP;
128 : }
129 :
130 : always_inline protocol_port_range_dpo_t *
131 0 : protocol_port_range_dpo_get (index_t index)
132 : {
133 0 : return (pool_elt_at_index (ppr_dpo_pool, index));
134 : }
135 :
136 : always_inline uword
137 0 : ip4_source_and_port_range_check_inline (vlib_main_t * vm,
138 : vlib_node_runtime_t * node,
139 : vlib_frame_t * frame, int is_tx)
140 : {
141 0 : ip4_main_t *im = &ip4_main;
142 : u32 n_left_from, *from, *to_next;
143 : u32 next_index;
144 0 : vlib_node_runtime_t *error_node = node;
145 0 : u32 good_packets = 0;
146 : int i;
147 :
148 0 : from = vlib_frame_vector_args (frame);
149 0 : n_left_from = frame->n_vectors;
150 0 : next_index = node->cached_next_index;
151 :
152 0 : while (n_left_from > 0)
153 : {
154 : u32 n_left_to_next;
155 :
156 0 : vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
157 :
158 :
159 : /* while (n_left_from >= 4 && n_left_to_next >= 2) */
160 : /* { */
161 : /* vlib_buffer_t *b0, *b1; */
162 : /* ip4_header_t *ip0, *ip1; */
163 : /* ip4_fib_mtrie_t *mtrie0, *mtrie1; */
164 : /* ip4_fib_mtrie_leaf_t leaf0, leaf1; */
165 : /* ip_source_and_port_range_check_config_t *c0, *c1; */
166 : /* ip_adjacency_t *adj0 = 0, *adj1 = 0; */
167 : /* u32 bi0, next0, adj_index0, pass0, save_next0, fib_index0; */
168 : /* u32 bi1, next1, adj_index1, pass1, save_next1, fib_index1; */
169 : /* udp_header_t *udp0, *udp1; */
170 :
171 : /* /\* Prefetch next iteration. *\/ */
172 : /* { */
173 : /* vlib_buffer_t *p2, *p3; */
174 :
175 : /* p2 = vlib_get_buffer (vm, from[2]); */
176 : /* p3 = vlib_get_buffer (vm, from[3]); */
177 :
178 : /* vlib_prefetch_buffer_header (p2, LOAD); */
179 : /* vlib_prefetch_buffer_header (p3, LOAD); */
180 :
181 : /* CLIB_PREFETCH (p2->data, sizeof (ip0[0]), LOAD); */
182 : /* CLIB_PREFETCH (p3->data, sizeof (ip1[0]), LOAD); */
183 : /* } */
184 :
185 : /* bi0 = to_next[0] = from[0]; */
186 : /* bi1 = to_next[1] = from[1]; */
187 : /* from += 2; */
188 : /* to_next += 2; */
189 : /* n_left_from -= 2; */
190 : /* n_left_to_next -= 2; */
191 :
192 : /* b0 = vlib_get_buffer (vm, bi0); */
193 : /* b1 = vlib_get_buffer (vm, bi1); */
194 :
195 : /* fib_index0 = */
196 : /* vec_elt (im->fib_index_by_sw_if_index, */
197 : /* vnet_buffer (b0)->sw_if_index[VLIB_RX]); */
198 : /* fib_index1 = */
199 : /* vec_elt (im->fib_index_by_sw_if_index, */
200 : /* vnet_buffer (b1)->sw_if_index[VLIB_RX]); */
201 :
202 : /* ip0 = vlib_buffer_get_current (b0); */
203 : /* ip1 = vlib_buffer_get_current (b1); */
204 :
205 : /* if (is_tx) */
206 : /* { */
207 : /* c0 = vnet_get_config_data (&tx_cm->config_main, */
208 : /* &b0->current_config_index, */
209 : /* &next0, sizeof (c0[0])); */
210 : /* c1 = vnet_get_config_data (&tx_cm->config_main, */
211 : /* &b1->current_config_index, */
212 : /* &next1, sizeof (c1[0])); */
213 : /* } */
214 : /* else */
215 : /* { */
216 : /* c0 = vnet_get_config_data (&rx_cm->config_main, */
217 : /* &b0->current_config_index, */
218 : /* &next0, sizeof (c0[0])); */
219 : /* c1 = vnet_get_config_data (&rx_cm->config_main, */
220 : /* &b1->current_config_index, */
221 : /* &next1, sizeof (c1[0])); */
222 : /* } */
223 :
224 : /* /\* we can't use the default VRF here... *\/ */
225 : /* for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++) */
226 : /* { */
227 : /* ASSERT (c0->fib_index[i] && c1->fib_index[i]); */
228 : /* } */
229 :
230 :
231 : /* if (is_tx) */
232 : /* { */
233 : /* if (ip0->protocol == IP_PROTOCOL_UDP) */
234 : /* fib_index0 = */
235 : /* c0->fib_index */
236 : /* [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN]; */
237 : /* if (ip0->protocol == IP_PROTOCOL_TCP) */
238 : /* fib_index0 = */
239 : /* c0->fib_index */
240 : /* [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN]; */
241 : /* } */
242 : /* else */
243 : /* { */
244 : /* if (ip0->protocol == IP_PROTOCOL_UDP) */
245 : /* fib_index0 = */
246 : /* c0->fib_index */
247 : /* [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT]; */
248 : /* if (ip0->protocol == IP_PROTOCOL_TCP) */
249 : /* fib_index0 = */
250 : /* c0->fib_index */
251 : /* [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT]; */
252 : /* } */
253 :
254 : /* if (PREDICT_TRUE (fib_index0 != ~0)) */
255 : /* { */
256 :
257 : /* mtrie0 = &vec_elt_at_index (im->fibs, fib_index0)->mtrie; */
258 :
259 : /* leaf0 = IP4_FIB_MTRIE_LEAF_ROOT; */
260 :
261 : /* leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, */
262 : /* &ip0->src_address, 0); */
263 :
264 : /* leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, */
265 : /* &ip0->src_address, 1); */
266 :
267 : /* leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, */
268 : /* &ip0->src_address, 2); */
269 :
270 : /* leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, */
271 : /* &ip0->src_address, 3); */
272 :
273 : /* adj_index0 = ip4_fib_mtrie_leaf_get_adj_index (leaf0); */
274 :
275 : /* ASSERT (adj_index0 == ip4_fib_lookup_with_table (im, fib_index0, */
276 : /* &ip0->src_address, */
277 : /* 0 */
278 : /* /\* use dflt rt *\/ */
279 : /* )); */
280 : /* adj0 = ip_get_adjacency (lm, adj_index0); */
281 : /* } */
282 :
283 : /* if (is_tx) */
284 : /* { */
285 : /* if (ip1->protocol == IP_PROTOCOL_UDP) */
286 : /* fib_index1 = */
287 : /* c1->fib_index */
288 : /* [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN]; */
289 : /* if (ip1->protocol == IP_PROTOCOL_TCP) */
290 : /* fib_index1 = */
291 : /* c1->fib_index */
292 : /* [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN]; */
293 : /* } */
294 : /* else */
295 : /* { */
296 : /* if (ip1->protocol == IP_PROTOCOL_UDP) */
297 : /* fib_index1 = */
298 : /* c1->fib_index */
299 : /* [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT]; */
300 : /* if (ip1->protocol == IP_PROTOCOL_TCP) */
301 : /* fib_index1 = */
302 : /* c1->fib_index */
303 : /* [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT]; */
304 : /* } */
305 :
306 : /* if (PREDICT_TRUE (fib_index1 != ~0)) */
307 : /* { */
308 :
309 : /* mtrie1 = &vec_elt_at_index (im->fibs, fib_index1)->mtrie; */
310 :
311 : /* leaf1 = IP4_FIB_MTRIE_LEAF_ROOT; */
312 :
313 : /* leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, */
314 : /* &ip1->src_address, 0); */
315 :
316 : /* leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, */
317 : /* &ip1->src_address, 1); */
318 :
319 : /* leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, */
320 : /* &ip1->src_address, 2); */
321 :
322 : /* leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, */
323 : /* &ip1->src_address, 3); */
324 :
325 : /* adj_index1 = ip4_fib_mtrie_leaf_get_adj_index (leaf1); */
326 :
327 : /* ASSERT (adj_index1 == ip4_fib_lookup_with_table (im, fib_index1, */
328 : /* &ip1->src_address, */
329 : /* 0)); */
330 : /* adj1 = ip_get_adjacency (lm, adj_index1); */
331 : /* } */
332 :
333 : /* pass0 = 0; */
334 : /* pass0 |= adj0 == 0; */
335 : /* pass0 |= ip4_address_is_multicast (&ip0->src_address); */
336 : /* pass0 |= */
337 : /* ip0->src_address.as_u32 == clib_host_to_net_u32 (0xFFFFFFFF); */
338 : /* pass0 |= (ip0->protocol != IP_PROTOCOL_UDP) */
339 : /* && (ip0->protocol != IP_PROTOCOL_TCP); */
340 :
341 : /* pass1 = 0; */
342 : /* pass1 |= adj1 == 0; */
343 : /* pass1 |= ip4_address_is_multicast (&ip1->src_address); */
344 : /* pass1 |= */
345 : /* ip1->src_address.as_u32 == clib_host_to_net_u32 (0xFFFFFFFF); */
346 : /* pass1 |= (ip1->protocol != IP_PROTOCOL_UDP) */
347 : /* && (ip1->protocol != IP_PROTOCOL_TCP); */
348 :
349 : /* save_next0 = next0; */
350 : /* udp0 = ip4_next_header (ip0); */
351 : /* save_next1 = next1; */
352 : /* udp1 = ip4_next_header (ip1); */
353 :
354 : /* if (PREDICT_TRUE (pass0 == 0)) */
355 : /* { */
356 : /* good_packets++; */
357 : /* next0 = check_adj_port_range_x1 */
358 : /* (adj0, clib_net_to_host_u16 (udp0->dst_port), next0); */
359 : /* good_packets -= (save_next0 != next0); */
360 : /* b0->error = error_node->errors */
361 : /* [IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_FAIL]; */
362 : /* } */
363 :
364 : /* if (PREDICT_TRUE (pass1 == 0)) */
365 : /* { */
366 : /* good_packets++; */
367 : /* next1 = check_adj_port_range_x1 */
368 : /* (adj1, clib_net_to_host_u16 (udp1->dst_port), next1); */
369 : /* good_packets -= (save_next1 != next1); */
370 : /* b1->error = error_node->errors */
371 : /* [IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_FAIL]; */
372 : /* } */
373 :
374 : /* if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) */
375 : /* && (b0->flags & VLIB_BUFFER_IS_TRACED))) */
376 : /* { */
377 : /* ip4_source_and_port_range_check_trace_t *t = */
378 : /* vlib_add_trace (vm, node, b0, sizeof (*t)); */
379 : /* t->pass = next0 == save_next0; */
380 : /* t->bypass = pass0; */
381 : /* t->fib_index = fib_index0; */
382 : /* t->src_addr.as_u32 = ip0->src_address.as_u32; */
383 : /* t->port = (pass0 == 0) ? */
384 : /* clib_net_to_host_u16 (udp0->dst_port) : 0; */
385 : /* t->is_tcp = ip0->protocol == IP_PROTOCOL_TCP; */
386 : /* } */
387 :
388 : /* if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) */
389 : /* && (b1->flags & VLIB_BUFFER_IS_TRACED))) */
390 : /* { */
391 : /* ip4_source_and_port_range_check_trace_t *t = */
392 : /* vlib_add_trace (vm, node, b1, sizeof (*t)); */
393 : /* t->pass = next1 == save_next1; */
394 : /* t->bypass = pass1; */
395 : /* t->fib_index = fib_index1; */
396 : /* t->src_addr.as_u32 = ip1->src_address.as_u32; */
397 : /* t->port = (pass1 == 0) ? */
398 : /* clib_net_to_host_u16 (udp1->dst_port) : 0; */
399 : /* t->is_tcp = ip1->protocol == IP_PROTOCOL_TCP; */
400 : /* } */
401 :
402 : /* vlib_validate_buffer_enqueue_x2 (vm, node, next_index, */
403 : /* to_next, n_left_to_next, */
404 : /* bi0, bi1, next0, next1); */
405 : /* } */
406 :
407 0 : while (n_left_from > 0 && n_left_to_next > 0)
408 : {
409 : vlib_buffer_t *b0;
410 : ip4_header_t *ip0;
411 : ip_source_and_port_range_check_config_t *c0;
412 : u32 bi0, next0, lb_index0, pass0, save_next0, fib_index0;
413 : udp_header_t *udp0;
414 0 : const protocol_port_range_dpo_t *ppr_dpo0 = NULL;
415 : const dpo_id_t *dpo;
416 : u32 sw_if_index0;
417 :
418 0 : bi0 = from[0];
419 0 : to_next[0] = bi0;
420 0 : from += 1;
421 0 : to_next += 1;
422 0 : n_left_from -= 1;
423 0 : n_left_to_next -= 1;
424 :
425 0 : b0 = vlib_get_buffer (vm, bi0);
426 0 : sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
427 :
428 0 : fib_index0 = vec_elt (im->fib_index_by_sw_if_index, sw_if_index0);
429 :
430 0 : if (is_tx)
431 0 : vlib_buffer_advance (b0, sizeof (ethernet_header_t));
432 :
433 0 : ip0 = vlib_buffer_get_current (b0);
434 :
435 0 : c0 = vnet_feature_next_with_data (&next0, b0, sizeof (c0[0]));
436 :
437 : /* we can't use the default VRF here... */
438 0 : for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++)
439 : {
440 0 : ASSERT (c0->fib_index[i]);
441 : }
442 :
443 :
444 0 : if (is_tx)
445 : {
446 0 : if (ip0->protocol == IP_PROTOCOL_UDP)
447 0 : fib_index0 =
448 : c0->fib_index
449 : [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN];
450 0 : if (ip0->protocol == IP_PROTOCOL_TCP)
451 0 : fib_index0 =
452 : c0->fib_index
453 : [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN];
454 : }
455 : else
456 : {
457 0 : if (ip0->protocol == IP_PROTOCOL_UDP)
458 0 : fib_index0 =
459 : c0->fib_index
460 : [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT];
461 0 : if (ip0->protocol == IP_PROTOCOL_TCP)
462 0 : fib_index0 =
463 : c0->fib_index
464 : [IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT];
465 : }
466 :
467 0 : if (fib_index0 != ~0)
468 : {
469 0 : lb_index0 = ip4_fib_forwarding_lookup (fib_index0,
470 0 : &ip0->src_address);
471 :
472 : dpo =
473 0 : load_balance_get_bucket_i (load_balance_get (lb_index0), 0);
474 :
475 0 : if (ppr_dpo_type == dpo->dpoi_type)
476 : {
477 0 : ppr_dpo0 = protocol_port_range_dpo_get (dpo->dpoi_index);
478 : }
479 : /*
480 : * else the lookup hit an enty that was no inserted
481 : * by this range checker, which is the default route
482 : */
483 : }
484 : /*
485 : * $$$ which (src,dst) categories should we always pass?
486 : */
487 0 : pass0 = 0;
488 0 : pass0 |= ip4_address_is_multicast (&ip0->src_address);
489 0 : pass0 |=
490 0 : ip0->src_address.as_u32 == clib_host_to_net_u32 (0xFFFFFFFF);
491 0 : pass0 |= (ip0->protocol != IP_PROTOCOL_UDP)
492 0 : && (ip0->protocol != IP_PROTOCOL_TCP);
493 :
494 0 : save_next0 = next0;
495 0 : udp0 = ip4_next_header (ip0);
496 :
497 0 : if (PREDICT_TRUE (pass0 == 0))
498 : {
499 0 : good_packets++;
500 0 : next0 = check_adj_port_range_x1
501 0 : (ppr_dpo0, clib_net_to_host_u16 (udp0->dst_port), next0);
502 0 : good_packets -= (save_next0 != next0);
503 0 : b0->error = error_node->errors
504 0 : [IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_FAIL];
505 : }
506 :
507 0 : if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
508 : && (b0->flags & VLIB_BUFFER_IS_TRACED)))
509 : {
510 : ip4_source_and_port_range_check_trace_t *t =
511 0 : vlib_add_trace (vm, node, b0, sizeof (*t));
512 0 : t->pass = next0 == save_next0;
513 0 : t->bypass = pass0;
514 0 : t->fib_index = fib_index0;
515 0 : t->src_addr.as_u32 = ip0->src_address.as_u32;
516 0 : t->port = (pass0 == 0) ?
517 0 : clib_net_to_host_u16 (udp0->dst_port) : 0;
518 0 : t->is_tcp = ip0->protocol == IP_PROTOCOL_TCP;
519 : }
520 :
521 0 : if (is_tx)
522 0 : vlib_buffer_advance (b0, -sizeof (ethernet_header_t));
523 :
524 0 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
525 : to_next, n_left_to_next,
526 : bi0, next0);
527 : }
528 :
529 0 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
530 : }
531 :
532 0 : if (is_tx)
533 0 : vlib_node_increment_counter (vm, ip4_source_port_and_range_check_tx.index,
534 : IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_OK,
535 : good_packets);
536 : else
537 0 : vlib_node_increment_counter (vm, ip4_source_port_and_range_check_rx.index,
538 : IP4_SOURCE_AND_PORT_RANGE_CHECK_ERROR_CHECK_OK,
539 : good_packets);
540 0 : return frame->n_vectors;
541 : }
542 :
543 : static uword
544 0 : ip4_source_and_port_range_check_rx (vlib_main_t * vm,
545 : vlib_node_runtime_t * node,
546 : vlib_frame_t * frame)
547 : {
548 0 : return ip4_source_and_port_range_check_inline (vm, node, frame,
549 : 0 /* !is_tx */ );
550 : }
551 :
552 : static uword
553 0 : ip4_source_and_port_range_check_tx (vlib_main_t * vm,
554 : vlib_node_runtime_t * node,
555 : vlib_frame_t * frame)
556 : {
557 0 : return ip4_source_and_port_range_check_inline (vm, node, frame,
558 : 1 /* is_tx */ );
559 : }
560 :
561 : /* Note: Calling same function for both RX and TX nodes
562 : as always checking dst_port, although
563 : if this changes can easily make new function
564 : */
565 :
566 : /* *INDENT-OFF* */
567 183788 : VLIB_REGISTER_NODE (ip4_source_port_and_range_check_rx) = {
568 : .function = ip4_source_and_port_range_check_rx,
569 : .name = "ip4-source-and-port-range-check-rx",
570 : .vector_size = sizeof (u32),
571 :
572 : .n_errors = ARRAY_LEN(ip4_source_and_port_range_check_error_strings),
573 : .error_strings = ip4_source_and_port_range_check_error_strings,
574 :
575 : .n_next_nodes = IP4_SOURCE_AND_PORT_RANGE_CHECK_N_NEXT,
576 : .next_nodes = {
577 : [IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP] = "ip4-drop",
578 : },
579 :
580 : .format_buffer = format_ip4_header,
581 : .format_trace = format_ip4_source_and_port_range_check_trace,
582 : };
583 : /* *INDENT-ON* */
584 :
585 : /* *INDENT-OFF* */
586 183788 : VLIB_REGISTER_NODE (ip4_source_port_and_range_check_tx) = {
587 : .function = ip4_source_and_port_range_check_tx,
588 : .name = "ip4-source-and-port-range-check-tx",
589 : .vector_size = sizeof (u32),
590 :
591 : .n_errors = ARRAY_LEN(ip4_source_and_port_range_check_error_strings),
592 : .error_strings = ip4_source_and_port_range_check_error_strings,
593 :
594 : .n_next_nodes = IP4_SOURCE_AND_PORT_RANGE_CHECK_N_NEXT,
595 : .next_nodes = {
596 : [IP4_SOURCE_AND_PORT_RANGE_CHECK_NEXT_DROP] = "ip4-drop",
597 : },
598 :
599 : .format_buffer = format_ip4_header,
600 : .format_trace = format_ip4_source_and_port_range_check_trace,
601 : };
602 : /* *INDENT-ON* */
603 :
604 : int
605 0 : set_ip_source_and_port_range_check (vlib_main_t * vm,
606 : u32 * fib_index,
607 : u32 sw_if_index, u32 is_add)
608 : {
609 : ip_source_and_port_range_check_config_t config;
610 0 : int rv = 0;
611 : int i;
612 :
613 0 : for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++)
614 : {
615 0 : config.fib_index[i] = fib_index[i];
616 : }
617 :
618 : /* For OUT we are in the RX path */
619 0 : if ((fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT] != ~0) ||
620 0 : (fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT] != ~0))
621 : {
622 0 : vnet_feature_enable_disable ("ip4-unicast",
623 : "ip4-source-and-port-range-check-rx",
624 : sw_if_index, is_add, &config,
625 : sizeof (config));
626 : }
627 :
628 : /* For IN we are in the TX path */
629 0 : if ((fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN] != ~0) ||
630 0 : (fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN] != ~0))
631 : {
632 0 : vnet_feature_enable_disable ("ip4-output",
633 : "ip4-source-and-port-range-check-tx",
634 : sw_if_index, is_add, &config,
635 : sizeof (config));
636 : }
637 0 : return rv;
638 : }
639 :
640 : static clib_error_t *
641 0 : set_ip_source_and_port_range_check_fn (vlib_main_t * vm,
642 : unformat_input_t * input,
643 : vlib_cli_command_t * cmd)
644 : {
645 0 : vnet_main_t *vnm = vnet_get_main ();
646 0 : ip4_main_t *im = &ip4_main;
647 0 : clib_error_t *error = 0;
648 0 : u8 is_add = 1;
649 0 : u32 sw_if_index = ~0;
650 : u32 vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS];
651 : u32 fib_index[IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS];
652 0 : int vrf_set = 0;
653 : uword *p;
654 0 : int rv = 0;
655 : int i;
656 :
657 0 : sw_if_index = ~0;
658 0 : for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++)
659 : {
660 0 : fib_index[i] = ~0;
661 0 : vrf_id[i] = ~0;
662 : }
663 :
664 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
665 : {
666 0 : if (unformat (input, "%U", unformat_vnet_sw_interface, vnm,
667 : &sw_if_index))
668 : ;
669 : else
670 0 : if (unformat
671 : (input, "tcp-out-vrf %d",
672 : &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_OUT]))
673 0 : vrf_set = 1;
674 : else
675 0 : if (unformat
676 : (input, "udp-out-vrf %d",
677 : &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_OUT]))
678 0 : vrf_set = 1;
679 : else
680 0 : if (unformat
681 : (input, "tcp-in-vrf %d",
682 : &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_TCP_IN]))
683 0 : vrf_set = 1;
684 : else
685 0 : if (unformat
686 : (input, "udp-in-vrf %d",
687 : &vrf_id[IP_SOURCE_AND_PORT_RANGE_CHECK_PROTOCOL_UDP_IN]))
688 0 : vrf_set = 1;
689 0 : else if (unformat (input, "del"))
690 0 : is_add = 0;
691 : else
692 0 : break;
693 : }
694 :
695 0 : if (sw_if_index == ~0)
696 0 : return clib_error_return (0, "Interface required but not specified");
697 :
698 0 : if (!vrf_set)
699 0 : return clib_error_return (0,
700 : "TCP or UDP VRF ID required but not specified");
701 :
702 0 : for (i = 0; i < IP_SOURCE_AND_PORT_RANGE_CHECK_N_PROTOCOLS; i++)
703 : {
704 :
705 0 : if (vrf_id[i] == 0)
706 0 : return clib_error_return (0,
707 : "TCP, UDP VRF ID should not be 0 (default). Should be distinct VRF for this purpose. ");
708 :
709 0 : if (vrf_id[i] != ~0)
710 : {
711 0 : p = hash_get (im->fib_index_by_table_id, vrf_id[i]);
712 :
713 0 : if (p == 0)
714 0 : return clib_error_return (0, "Invalid VRF ID %d", vrf_id[i]);
715 :
716 0 : fib_index[i] = p[0];
717 : }
718 : }
719 : rv =
720 0 : set_ip_source_and_port_range_check (vm, fib_index, sw_if_index, is_add);
721 :
722 0 : switch (rv)
723 : {
724 0 : case 0:
725 0 : break;
726 :
727 0 : default:
728 0 : return clib_error_return
729 : (0,
730 : "set source and port-range on interface returned an unexpected value: %d",
731 : rv);
732 : }
733 0 : return error;
734 : }
735 :
736 : /*?
737 : * Add the 'ip4-source-and-port-range-check-rx' or
738 : * 'ip4-source-and-port-range-check-tx' graph node for a given
739 : * interface. 'tcp-out-vrf' and 'udp-out-vrf' will add to
740 : * the RX path. 'tcp-in-vrf' and 'udp-in-vrf' will add to
741 : * the TX path. A graph node will be inserted into the chain when
742 : * the range check is added to the first interface. It will not
743 : * be removed from when range check is removed from the last
744 : * interface.
745 : *
746 : * By adding the range check graph node to the interface, incoming
747 : * or outgoing TCP/UDP packets will be validated using the
748 : * provided IPv4 FIB table (VRF).
749 : *
750 : * @note 'ip4-source-and-port-range-check-rx' and
751 : * 'ip4-source-and-port-range-check-tx' strings are too long, so
752 : * they are truncated on the 'show vlib graph' output.
753 : *
754 : * @todo This content needs to be validated and potentially more detail added.
755 : *
756 : * @cliexpar
757 : * @parblock
758 : * Example of graph node before range checking is enabled:
759 : * @cliexstart{show vlib graph ip4-source-and-port-range-check-tx}
760 : * Name Next Previous
761 : * ip4-source-and-port-range- ip4-drop [0]
762 : * @cliexend
763 : *
764 : * Example of how to enable range checking on TX:
765 : * @cliexcmd{set interface ip source-and-port-range-check GigabitEthernet2/0/0
766 : * udp-in-vrf 7}
767 : *
768 : * Example of graph node after range checking is enabled:
769 : * @cliexstart{show vlib graph ip4-source-and-port-range-check-tx}
770 : * Name Next Previous
771 : * ip4-source-and-port-range- ip4-drop [0] ip4-rewrite
772 : * interface-output [1]
773 : * @cliexend
774 : *
775 : * Example of how to display the features enabled on an interface:
776 : * @cliexstart{show ip interface features GigabitEthernet2/0/0}
777 : * IP feature paths configured on GigabitEthernet2/0/0...
778 : *
779 : * ipv4 unicast:
780 : * ip4-source-and-port-range-check-rx
781 : * ip4-lookup
782 : *
783 : * ipv4 multicast:
784 : * ip4-lookup-multicast
785 : *
786 : * ipv4 multicast:
787 : * interface-output
788 : *
789 : * ipv6 unicast:
790 : * ip6-lookup
791 : *
792 : * ipv6 multicast:
793 : * ip6-lookup
794 : *
795 : * ipv6 multicast:
796 : * interface-output
797 : * @cliexend
798 : * @endparblock
799 : ?*/
800 : /* *INDENT-OFF* */
801 285289 : VLIB_CLI_COMMAND (set_interface_ip_source_and_port_range_check_command, static) = {
802 : .path = "set interface ip source-and-port-range-check",
803 : .function = set_ip_source_and_port_range_check_fn,
804 : .short_help = "set interface ip source-and-port-range-check <interface> [tcp-out-vrf <table-id>] [udp-out-vrf <table-id>] [tcp-in-vrf <table-id>] [udp-in-vrf <table-id>] [del]",
805 : };
806 : /* *INDENT-ON* */
807 :
808 : static u8 *
809 0 : format_ppr_dpo (u8 * s, va_list * args)
810 : {
811 0 : index_t index = va_arg (*args, index_t);
812 0 : CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
813 :
814 : protocol_port_range_dpo_t *ppr_dpo;
815 : int i, j;
816 0 : int printed = 0;
817 :
818 0 : ppr_dpo = protocol_port_range_dpo_get (index);
819 :
820 0 : s = format (s, "allow ");
821 :
822 0 : for (i = 0; i < ppr_dpo->n_used_blocks; i++)
823 : {
824 0 : for (j = 0; j < 8; j++)
825 : {
826 0 : if (ppr_dpo->blocks[i].low.as_u16[j])
827 : {
828 0 : if (printed)
829 0 : s = format (s, ", ");
830 0 : if (ppr_dpo->blocks[i].hi.as_u16[j] >
831 0 : (ppr_dpo->blocks[i].low.as_u16[j] + 1))
832 : s =
833 0 : format (s, "%d-%d", (u32) ppr_dpo->blocks[i].low.as_u16[j],
834 0 : (u32) ppr_dpo->blocks[i].hi.as_u16[j] - 1);
835 : else
836 0 : s = format (s, "%d", ppr_dpo->blocks[i].low.as_u16[j]);
837 0 : printed = 1;
838 : }
839 : }
840 : }
841 0 : return s;
842 : }
843 :
844 : static void
845 0 : ppr_dpo_lock (dpo_id_t * dpo)
846 : {
847 0 : }
848 :
849 : static void
850 0 : ppr_dpo_unlock (dpo_id_t * dpo)
851 : {
852 0 : }
853 :
854 : const static dpo_vft_t ppr_vft = {
855 : .dv_lock = ppr_dpo_lock,
856 : .dv_unlock = ppr_dpo_unlock,
857 : .dv_format = format_ppr_dpo,
858 : };
859 :
860 : const static char *const ppr_ip4_nodes[] = {
861 : "ip4-source-and-port-range-check-rx",
862 : NULL,
863 : };
864 :
865 : const static char *const *const ppr_nodes[DPO_PROTO_NUM] = {
866 : [DPO_PROTO_IP4] = ppr_ip4_nodes,
867 : };
868 :
869 : clib_error_t *
870 575 : ip4_source_and_port_range_check_init (vlib_main_t * vm)
871 : {
872 575 : source_range_check_main_t *srm = &source_range_check_main;
873 :
874 575 : srm->vlib_main = vm;
875 575 : srm->vnet_main = vnet_get_main ();
876 :
877 575 : ppr_dpo_type = dpo_register_new_type (&ppr_vft, ppr_nodes);
878 :
879 575 : return 0;
880 : }
881 :
882 38015 : VLIB_INIT_FUNCTION (ip4_source_and_port_range_check_init);
883 :
884 : protocol_port_range_dpo_t *
885 0 : protocol_port_range_dpo_alloc (void)
886 : {
887 : protocol_port_range_dpo_t *ppr_dpo;
888 :
889 0 : pool_get_aligned (ppr_dpo_pool, ppr_dpo, CLIB_CACHE_LINE_BYTES);
890 0 : clib_memset (ppr_dpo, 0, sizeof (*ppr_dpo));
891 :
892 0 : ppr_dpo->n_free_ranges = N_PORT_RANGES_PER_DPO;
893 :
894 0 : return (ppr_dpo);
895 : }
896 :
897 :
898 : static int
899 0 : add_port_range_adjacency (u32 fib_index,
900 : ip4_address_t * address,
901 : u32 length, u16 * low_ports, u16 * high_ports)
902 : {
903 : protocol_port_range_dpo_t *ppr_dpo;
904 0 : dpo_id_t dpop = DPO_INVALID;
905 : int i, j, k;
906 :
907 : fib_node_index_t fei;
908 0 : fib_prefix_t pfx = {
909 : .fp_proto = FIB_PROTOCOL_IP4,
910 : .fp_len = length,
911 : .fp_addr = {
912 : .ip4 = *address,
913 : },
914 : };
915 :
916 : /*
917 : * check to see if we have already sourced this prefix
918 : */
919 0 : fei = fib_table_lookup_exact_match (fib_index, &pfx);
920 :
921 0 : if (FIB_NODE_INDEX_INVALID == fei)
922 : {
923 : /*
924 : * this is a first time add for this prefix.
925 : */
926 0 : ppr_dpo = protocol_port_range_dpo_alloc ();
927 : }
928 : else
929 : {
930 : /*
931 : * the prefix is already there.
932 : * check it was sourced by us, and if so get the ragne DPO from it.
933 : */
934 0 : dpo_id_t dpo = DPO_INVALID;
935 : const dpo_id_t *bucket;
936 :
937 0 : if (fib_entry_get_dpo_for_source (fei, FIB_SOURCE_SPECIAL, &dpo))
938 : {
939 : /*
940 : * there is existing state. we'll want to add the new ranges to it
941 : */
942 : bucket =
943 0 : load_balance_get_bucket_i (load_balance_get (dpo.dpoi_index), 0);
944 0 : ppr_dpo = protocol_port_range_dpo_get (bucket->dpoi_index);
945 0 : dpo_reset (&dpo);
946 : }
947 : else
948 : {
949 : /*
950 : * there is no PPR state associated with this prefix,
951 : * so we'll need a new DPO
952 : */
953 0 : ppr_dpo = protocol_port_range_dpo_alloc ();
954 : }
955 : }
956 :
957 0 : if (vec_len (low_ports) > ppr_dpo->n_free_ranges)
958 0 : return VNET_API_ERROR_EXCEEDED_NUMBER_OF_RANGES_CAPACITY;
959 :
960 0 : j = k = 0;
961 :
962 0 : for (i = 0; i < vec_len (low_ports); i++)
963 : {
964 0 : for (; j < N_BLOCKS_PER_DPO; j++)
965 : {
966 0 : for (; k < 8; k++)
967 : {
968 0 : if (ppr_dpo->blocks[j].low.as_u16[k] == 0)
969 : {
970 0 : ppr_dpo->blocks[j].low.as_u16[k] = low_ports[i];
971 0 : ppr_dpo->blocks[j].hi.as_u16[k] = high_ports[i];
972 0 : goto doublebreak;
973 : }
974 : }
975 : }
976 0 : doublebreak:;
977 : }
978 0 : ppr_dpo->n_used_blocks = j + 1;
979 :
980 : /*
981 : * add or update the entry in the FIB
982 : */
983 0 : dpo_set (&dpop, ppr_dpo_type, DPO_PROTO_IP4, (ppr_dpo - ppr_dpo_pool));
984 :
985 0 : if (FIB_NODE_INDEX_INVALID == fei)
986 : {
987 0 : fib_table_entry_special_dpo_add (fib_index,
988 : &pfx,
989 : FIB_SOURCE_SPECIAL,
990 : FIB_ENTRY_FLAG_NONE, &dpop);
991 : }
992 : else
993 : {
994 0 : fib_entry_special_update (fei,
995 : FIB_SOURCE_SPECIAL,
996 : FIB_ENTRY_FLAG_NONE, &dpop);
997 : }
998 :
999 0 : return 0;
1000 : }
1001 :
1002 : static int
1003 0 : remove_port_range_adjacency (u32 fib_index,
1004 : ip4_address_t * address,
1005 : u32 length, u16 * low_ports, u16 * high_ports)
1006 : {
1007 : protocol_port_range_dpo_t *ppr_dpo;
1008 : fib_node_index_t fei;
1009 : int i, j, k;
1010 :
1011 0 : fib_prefix_t pfx = {
1012 : .fp_proto = FIB_PROTOCOL_IP4,
1013 : .fp_len = length,
1014 : .fp_addr = {
1015 : .ip4 = *address,
1016 : },
1017 : };
1018 :
1019 : /*
1020 : * check to see if we have sourced this prefix
1021 : */
1022 0 : fei = fib_table_lookup_exact_match (fib_index, &pfx);
1023 :
1024 0 : if (FIB_NODE_INDEX_INVALID == fei)
1025 : {
1026 : /*
1027 : * not one of ours
1028 : */
1029 0 : return VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE;
1030 : }
1031 : else
1032 : {
1033 : /*
1034 : * the prefix is already there.
1035 : * check it was sourced by us
1036 : */
1037 0 : dpo_id_t dpo = DPO_INVALID;
1038 : const dpo_id_t *bucket;
1039 :
1040 0 : if (fib_entry_get_dpo_for_source (fei, FIB_SOURCE_SPECIAL, &dpo))
1041 : {
1042 : /*
1043 : * there is existing state. we'll want to add the new ranges to it
1044 : */
1045 : bucket =
1046 0 : load_balance_get_bucket_i (load_balance_get (dpo.dpoi_index), 0);
1047 0 : ppr_dpo = protocol_port_range_dpo_get (bucket->dpoi_index);
1048 0 : dpo_reset (&dpo);
1049 : }
1050 : else
1051 : {
1052 : /*
1053 : * not one of ours
1054 : */
1055 0 : return VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE;
1056 : }
1057 : }
1058 :
1059 0 : for (i = 0; i < vec_len (low_ports); i++)
1060 : {
1061 0 : for (j = 0; j < N_BLOCKS_PER_DPO; j++)
1062 : {
1063 0 : for (k = 0; k < 8; k++)
1064 : {
1065 0 : if (low_ports[i] == ppr_dpo->blocks[j].low.as_u16[k] &&
1066 0 : high_ports[i] == ppr_dpo->blocks[j].hi.as_u16[k])
1067 : {
1068 0 : ppr_dpo->blocks[j].low.as_u16[k] =
1069 0 : ppr_dpo->blocks[j].hi.as_u16[k] = 0;
1070 0 : goto doublebreak;
1071 : }
1072 : }
1073 : }
1074 0 : doublebreak:;
1075 : }
1076 :
1077 0 : ppr_dpo->n_free_ranges = 0;
1078 :
1079 : /* Have we deleted all ranges yet? */
1080 0 : for (i = 0; i < N_BLOCKS_PER_DPO; i++)
1081 : {
1082 0 : for (j = 0; j < 8; j++)
1083 : {
1084 0 : if (ppr_dpo->blocks[j].low.as_u16[i] == 0)
1085 0 : ppr_dpo->n_free_ranges++;
1086 : }
1087 : }
1088 :
1089 0 : if (N_PORT_RANGES_PER_DPO == ppr_dpo->n_free_ranges)
1090 : {
1091 : /* Yes, lose the adjacency... */
1092 0 : fib_table_entry_special_remove (fib_index, &pfx, FIB_SOURCE_SPECIAL);
1093 : }
1094 : else
1095 : {
1096 : /*
1097 : * compact the ranges down to a contiguous block
1098 : */
1099 : // FIXME. TODO.
1100 : }
1101 :
1102 0 : return 0;
1103 : }
1104 :
1105 : // This will be moved to another file and implemented post API freeze.
1106 : int
1107 0 : ip6_source_and_port_range_check_add_del (ip6_address_t * address,
1108 : u32 length,
1109 : u32 vrf_id,
1110 : u16 * low_ports,
1111 : u16 * high_ports, int is_add)
1112 : {
1113 : u32 fib_index;
1114 :
1115 0 : fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
1116 :
1117 0 : ASSERT (~0 != fib_index);
1118 :
1119 0 : fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY);
1120 :
1121 0 : return 0;
1122 : }
1123 :
1124 : int
1125 0 : ip4_source_and_port_range_check_add_del (ip4_address_t * address,
1126 : u32 length,
1127 : u32 vrf_id,
1128 : u16 * low_ports,
1129 : u16 * high_ports, int is_add)
1130 : {
1131 : u32 fib_index;
1132 :
1133 0 : fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id,
1134 : FIB_SOURCE_CLASSIFY);
1135 :
1136 0 : if (is_add == 0)
1137 : {
1138 0 : remove_port_range_adjacency (fib_index, address, length,
1139 : low_ports, high_ports);
1140 : }
1141 : else
1142 : {
1143 0 : add_port_range_adjacency (fib_index, address, length,
1144 : low_ports, high_ports);
1145 : }
1146 :
1147 0 : return 0;
1148 : }
1149 :
1150 : static clib_error_t *
1151 0 : ip_source_and_port_range_check_command_fn (vlib_main_t * vm,
1152 : unformat_input_t * input,
1153 : vlib_cli_command_t * cmd)
1154 : {
1155 0 : u16 *low_ports = 0;
1156 0 : u16 *high_ports = 0;
1157 : u16 this_low;
1158 : u16 this_hi;
1159 : ip4_address_t ip4_addr;
1160 : ip6_address_t ip6_addr; //This function will be moved to generic impl when v6 done.
1161 : u32 length;
1162 : u32 tmp, tmp2;
1163 0 : u32 vrf_id = ~0;
1164 0 : int is_add = 1, ip_ver = ~0;
1165 : int rv;
1166 :
1167 :
1168 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1169 : {
1170 0 : if (unformat (input, "%U/%d", unformat_ip4_address, &ip4_addr, &length))
1171 0 : ip_ver = 4;
1172 : else
1173 0 : if (unformat
1174 : (input, "%U/%d", unformat_ip6_address, &ip6_addr, &length))
1175 0 : ip_ver = 6;
1176 0 : else if (unformat (input, "vrf %d", &vrf_id))
1177 : ;
1178 0 : else if (unformat (input, "del"))
1179 0 : is_add = 0;
1180 0 : else if (unformat (input, "port %d", &tmp))
1181 : {
1182 0 : if (tmp == 0 || tmp > 65535)
1183 0 : return clib_error_return (0, "port %d out of range", tmp);
1184 0 : this_low = tmp;
1185 0 : this_hi = this_low + 1;
1186 0 : vec_add1 (low_ports, this_low);
1187 0 : vec_add1 (high_ports, this_hi);
1188 : }
1189 0 : else if (unformat (input, "range %d - %d", &tmp, &tmp2))
1190 : {
1191 0 : if (tmp > tmp2)
1192 0 : return clib_error_return (0, "ports %d and %d out of order",
1193 : tmp, tmp2);
1194 0 : if (tmp == 0 || tmp > 65535)
1195 0 : return clib_error_return (0, "low port %d out of range", tmp);
1196 0 : if (tmp2 == 0 || tmp2 > 65535)
1197 0 : return clib_error_return (0, "high port %d out of range", tmp2);
1198 0 : this_low = tmp;
1199 0 : this_hi = tmp2 + 1;
1200 0 : vec_add1 (low_ports, this_low);
1201 0 : vec_add1 (high_ports, this_hi);
1202 : }
1203 : else
1204 0 : break;
1205 : }
1206 :
1207 0 : if (ip_ver == ~0)
1208 0 : return clib_error_return (0, " <address>/<mask> not specified");
1209 :
1210 0 : if (vrf_id == ~0)
1211 0 : return clib_error_return (0, " VRF ID required, not specified");
1212 :
1213 0 : if (vec_len (low_ports) == 0)
1214 0 : return clib_error_return (0,
1215 : " Both VRF ID and range/port must be set for a protocol.");
1216 :
1217 0 : if (vrf_id == 0)
1218 0 : return clib_error_return (0, " VRF ID can not be 0 (default).");
1219 :
1220 :
1221 0 : if (ip_ver == 4)
1222 0 : rv = ip4_source_and_port_range_check_add_del
1223 : (&ip4_addr, length, vrf_id, low_ports, high_ports, is_add);
1224 : else
1225 0 : return clib_error_return (0, " IPv6 in subsequent patch");
1226 :
1227 0 : switch (rv)
1228 : {
1229 0 : case 0:
1230 0 : break;
1231 :
1232 0 : case VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE:
1233 0 : return clib_error_return
1234 : (0, " Incorrect adjacency for add/del operation");
1235 :
1236 0 : case VNET_API_ERROR_EXCEEDED_NUMBER_OF_PORTS_CAPACITY:
1237 0 : return clib_error_return (0, " Too many ports in add/del operation");
1238 :
1239 0 : case VNET_API_ERROR_EXCEEDED_NUMBER_OF_RANGES_CAPACITY:
1240 0 : return clib_error_return
1241 : (0, " Too many ranges requested for add operation");
1242 :
1243 0 : default:
1244 0 : return clib_error_return (0, " returned an unexpected value: %d", rv);
1245 : }
1246 :
1247 0 : return 0;
1248 : }
1249 :
1250 : /*?
1251 : * This command adds an IP Subnet and range of ports to be validated
1252 : * by an IP FIB table (VRF).
1253 : *
1254 : * @todo This is incomplete. This needs a detailed description and a
1255 : * practical example.
1256 : *
1257 : * @cliexpar
1258 : * Example of how to add an IPv4 subnet and single port to an IPv4 FIB table:
1259 : * @cliexcmd{set ip source-and-port-range-check vrf 7 172.16.1.0/24 port 23}
1260 : * Example of how to add an IPv4 subnet and range of ports to an IPv4 FIB table:
1261 : * @cliexcmd{set ip source-and-port-range-check vrf 7 172.16.1.0/24 range 23 - 100}
1262 : * Example of how to delete an IPv4 subnet and single port from an IPv4 FIB table:
1263 : * @cliexcmd{set ip source-and-port-range-check vrf 7 172.16.1.0/24 port 23 del}
1264 : * Example of how to delete an IPv4 subnet and range of ports from an IPv4 FIB table:
1265 : * @cliexcmd{set ip source-and-port-range-check vrf 7 172.16.1.0/24 range 23 - 100 del}
1266 : ?*/
1267 : /* *INDENT-OFF* */
1268 285289 : VLIB_CLI_COMMAND (ip_source_and_port_range_check_command, static) = {
1269 : .path = "set ip source-and-port-range-check",
1270 : .function = ip_source_and_port_range_check_command_fn,
1271 : .short_help =
1272 : "set ip source-and-port-range-check vrf <table-id> <ip-addr>/<mask> {port nn | range <nn> - <nn>} [del]",
1273 : };
1274 : /* *INDENT-ON* */
1275 :
1276 :
1277 : static clib_error_t *
1278 0 : show_source_and_port_range_check_fn (vlib_main_t * vm,
1279 : unformat_input_t * input,
1280 : vlib_cli_command_t * cmd)
1281 : {
1282 : protocol_port_range_dpo_t *ppr_dpo;
1283 : u32 fib_index;
1284 0 : u8 addr_set = 0;
1285 0 : u32 vrf_id = ~0;
1286 : int rv, i, j;
1287 0 : u32 port = 0;
1288 0 : fib_prefix_t pfx = {
1289 : .fp_proto = FIB_PROTOCOL_IP4,
1290 : .fp_len = 32,
1291 : };
1292 :
1293 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1294 : {
1295 0 : if (unformat (input, "%U", unformat_ip4_address, &pfx.fp_addr.ip4))
1296 0 : addr_set = 1;
1297 0 : else if (unformat (input, "vrf %d", &vrf_id))
1298 : ;
1299 0 : else if (unformat (input, "port %d", &port))
1300 : ;
1301 : else
1302 0 : break;
1303 : }
1304 :
1305 0 : if (addr_set == 0)
1306 0 : return clib_error_return (0, "<address> not specified");
1307 :
1308 0 : if (vrf_id == ~0)
1309 0 : return clib_error_return (0, "VRF ID required, not specified");
1310 :
1311 0 : fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
1312 0 : if (~0 == fib_index)
1313 0 : return clib_error_return (0, "VRF %d not found", vrf_id);
1314 :
1315 : /*
1316 : * find the longest prefix match on the address requested,
1317 : * check it was sourced by us
1318 : */
1319 0 : dpo_id_t dpo = DPO_INVALID;
1320 : const dpo_id_t *bucket;
1321 :
1322 0 : if (!fib_entry_get_dpo_for_source (fib_table_lookup (fib_index, &pfx),
1323 : FIB_SOURCE_SPECIAL, &dpo))
1324 : {
1325 : /*
1326 : * not one of ours
1327 : */
1328 0 : vlib_cli_output (vm, "%U: src address drop", format_ip4_address,
1329 : &pfx.fp_addr.ip4);
1330 0 : return 0;
1331 : }
1332 :
1333 0 : bucket = load_balance_get_bucket_i (load_balance_get (dpo.dpoi_index), 0);
1334 0 : ppr_dpo = protocol_port_range_dpo_get (bucket->dpoi_index);
1335 0 : dpo_reset (&dpo);
1336 :
1337 0 : if (port)
1338 : {
1339 0 : rv = check_adj_port_range_x1 (ppr_dpo, (u16) port, 1234);
1340 0 : if (rv == 1234)
1341 0 : vlib_cli_output (vm, "%U port %d PASS", format_ip4_address,
1342 : &pfx.fp_addr.ip4, port);
1343 : else
1344 0 : vlib_cli_output (vm, "%U port %d FAIL", format_ip4_address,
1345 : &pfx.fp_addr.ip4, port);
1346 0 : return 0;
1347 : }
1348 : else
1349 : {
1350 : u8 *s;
1351 :
1352 0 : s = format (0, "%U: ", format_ip4_address, &pfx.fp_addr.ip4);
1353 :
1354 0 : for (i = 0; i < N_BLOCKS_PER_DPO; i++)
1355 : {
1356 0 : for (j = 0; j < 8; j++)
1357 : {
1358 0 : if (ppr_dpo->blocks[i].low.as_u16[j])
1359 0 : s = format (s, "%d - %d ",
1360 0 : (u32) ppr_dpo->blocks[i].low.as_u16[j],
1361 0 : (u32) ppr_dpo->blocks[i].hi.as_u16[j]);
1362 : }
1363 : }
1364 0 : vlib_cli_output (vm, "%s", s);
1365 0 : vec_free (s);
1366 : }
1367 :
1368 0 : return 0;
1369 : }
1370 :
1371 : /*?
1372 : * Display the range of ports being validated by an IPv4 FIB for a given
1373 : * IP or subnet, or test if a given IP and port are being validated.
1374 : *
1375 : * @todo This is incomplete. This needs a detailed description and a
1376 : * practical example.
1377 : *
1378 : * @cliexpar
1379 : * Example of how to display the set of ports being validated for a given
1380 : * IPv4 subnet:
1381 : * @cliexstart{show ip source-and-port-range-check vrf 7 172.16.2.0}
1382 : * 172.16.2.0: 23 - 101
1383 : * @cliexend
1384 : * Example of how to test to determine of a given iPv4 address and port
1385 : * are being validated:
1386 : * @cliexstart{show ip source-and-port-range-check vrf 7 172.16.2.2 port 23}
1387 : * 172.16.2.2 port 23 PASS
1388 : * @cliexend
1389 : * @cliexstart{show ip source-and-port-range-check vrf 7 172.16.2.2 port 250}
1390 : * 172.16.2.2 port 250 FAIL
1391 : * @cliexend
1392 : ?*/
1393 : /* *INDENT-OFF* */
1394 285289 : VLIB_CLI_COMMAND (show_source_and_port_range_check, static) = {
1395 : .path = "show ip source-and-port-range-check",
1396 : .function = show_source_and_port_range_check_fn,
1397 : .short_help =
1398 : "show ip source-and-port-range-check vrf <table-id> <ip-addr> [port <n>]",
1399 : };
1400 : /* *INDENT-ON* */
1401 :
1402 : /*
1403 : * fd.io coding-style-patch-verification: ON
1404 : *
1405 : * Local Variables:
1406 : * eval: (c-set-style "gnu")
1407 : * End:
1408 : */
|