Line data Source code
1 : /*
2 : * flowprobe.c - ipfix probe plugin
3 : *
4 : * Copyright (c) 2016 Cisco and/or its affiliates.
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at:
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : /**
19 : * @file
20 : * @brief Per-packet IPFIX flow record generator plugin
21 : *
22 : * This file implements vpp plugin registration mechanics,
23 : * debug CLI, and binary API handling.
24 : */
25 :
26 : #include <vnet/vnet.h>
27 : #include <vpp/app/version.h>
28 : #include <vnet/plugin/plugin.h>
29 : #include <vnet/udp/udp_local.h>
30 : #include <flowprobe/flowprobe.h>
31 :
32 : #include <vlibapi/api.h>
33 : #include <vlibmemory/api.h>
34 :
35 : /* define message IDs */
36 : #include <flowprobe/flowprobe.api_enum.h>
37 : #include <flowprobe/flowprobe.api_types.h>
38 :
39 : flowprobe_main_t flowprobe_main;
40 : static vlib_node_registration_t flowprobe_timer_node;
41 : uword flowprobe_walker_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
42 : vlib_frame_t * f);
43 :
44 : #define REPLY_MSG_ID_BASE fm->msg_id_base
45 : #include <vlibapi/api_helper_macros.h>
46 :
47 : /* Define the per-interface configurable features */
48 : /* *INDENT-OFF* */
49 57051 : VNET_FEATURE_INIT (flowprobe_input_ip4_unicast, static) = {
50 : .arc_name = "ip4-unicast",
51 : .node_name = "flowprobe-input-ip4",
52 : .runs_before = VNET_FEATURES ("ip4-lookup"),
53 : };
54 57051 : VNET_FEATURE_INIT (flowprobe_input_ip4_multicast, static) = {
55 : .arc_name = "ip4-multicast",
56 : .node_name = "flowprobe-input-ip4",
57 : .runs_before = VNET_FEATURES ("ip4-mfib-forward-lookup"),
58 : };
59 57051 : VNET_FEATURE_INIT (flowprobe_input_ip6_unicast, static) = {
60 : .arc_name = "ip6-unicast",
61 : .node_name = "flowprobe-input-ip6",
62 : .runs_before = VNET_FEATURES ("ip6-lookup"),
63 : };
64 57051 : VNET_FEATURE_INIT (flowprobe_input_ip6_multicast, static) = {
65 : .arc_name = "ip6-multicast",
66 : .node_name = "flowprobe-input-ip6",
67 : .runs_before = VNET_FEATURES ("ip6-mfib-forward-lookup"),
68 : };
69 57051 : VNET_FEATURE_INIT (flowprobe_input_l2, static) = {
70 : .arc_name = "device-input",
71 : .node_name = "flowprobe-input-l2",
72 : .runs_before = VNET_FEATURES ("ethernet-input"),
73 : };
74 57051 : VNET_FEATURE_INIT (flowprobe_output_ip4, static) = {
75 : .arc_name = "ip4-output",
76 : .node_name = "flowprobe-output-ip4",
77 : .runs_before = VNET_FEATURES ("interface-output"),
78 : };
79 :
80 57051 : VNET_FEATURE_INIT (flowprobe_output_ip6, static) = {
81 : .arc_name = "ip6-output",
82 : .node_name = "flowprobe-output-ip6",
83 : .runs_before = VNET_FEATURES ("interface-output"),
84 : };
85 :
86 57051 : VNET_FEATURE_INIT (flowprobe_output_l2, static) = {
87 : .arc_name = "interface-output",
88 : .node_name = "flowprobe-output-l2",
89 : .runs_before = VNET_FEATURES ("interface-output-arc-end"),
90 : };
91 : /* *INDENT-ON* */
92 :
93 : #define FINISH \
94 : vec_add1 (s, 0); \
95 : vlib_cli_output (handle, (char *) s); \
96 : vec_free (s); \
97 : return handle;
98 :
99 : static inline ipfix_field_specifier_t *
100 17 : flowprobe_template_ip4_fields (ipfix_field_specifier_t * f)
101 : {
102 : #define flowprobe_template_ip4_field_count() 4
103 : /* sourceIpv4Address, TLV type 8, u32 */
104 17 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
105 : sourceIPv4Address, 4);
106 17 : f++;
107 : /* destinationIPv4Address, TLV type 12, u32 */
108 17 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
109 : destinationIPv4Address, 4);
110 17 : f++;
111 : /* protocolIdentifier, TLV type 4, u8 */
112 17 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
113 : protocolIdentifier, 1);
114 17 : f++;
115 : /* octetDeltaCount, TLV type 1, u64 */
116 17 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
117 : octetDeltaCount, 8);
118 17 : f++;
119 17 : return f;
120 : }
121 :
122 : static inline ipfix_field_specifier_t *
123 16 : flowprobe_template_ip6_fields (ipfix_field_specifier_t * f)
124 : {
125 : #define flowprobe_template_ip6_field_count() 4
126 : /* sourceIpv6Address, TLV type 27, 16 octets */
127 16 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
128 : sourceIPv6Address, 16);
129 16 : f++;
130 : /* destinationIPv6Address, TLV type 28, 16 octets */
131 16 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
132 : destinationIPv6Address, 16);
133 16 : f++;
134 : /* protocolIdentifier, TLV type 4, u8 */
135 16 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
136 : protocolIdentifier, 1);
137 16 : f++;
138 : /* octetDeltaCount, TLV type 1, u64 */
139 16 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
140 : octetDeltaCount, 8);
141 16 : f++;
142 16 : return f;
143 : }
144 :
145 : static inline ipfix_field_specifier_t *
146 43 : flowprobe_template_l2_fields (ipfix_field_specifier_t * f)
147 : {
148 : #define flowprobe_template_l2_field_count() 3
149 : /* sourceMacAddress, TLV type 56, u8[6] we hope */
150 43 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
151 : sourceMacAddress, 6);
152 43 : f++;
153 : /* destinationMacAddress, TLV type 80, u8[6] we hope */
154 43 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
155 : destinationMacAddress, 6);
156 43 : f++;
157 : /* ethernetType, TLV type 256, u16 */
158 43 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
159 : ethernetType, 2);
160 43 : f++;
161 43 : return f;
162 : }
163 :
164 : static inline ipfix_field_specifier_t *
165 59 : flowprobe_template_common_fields (ipfix_field_specifier_t * f)
166 : {
167 : #define flowprobe_template_common_field_count() 6
168 : /* ingressInterface, TLV type 10, u32 */
169 59 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
170 : ingressInterface, 4);
171 59 : f++;
172 :
173 : /* egressInterface, TLV type 14, u32 */
174 59 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
175 : egressInterface, 4);
176 59 : f++;
177 :
178 : /* flowDirection, TLV type 61, u8 */
179 59 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */, flowDirection, 1);
180 59 : f++;
181 :
182 : /* packetDeltaCount, TLV type 2, u64 */
183 59 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
184 : packetDeltaCount, 8);
185 59 : f++;
186 :
187 : /* flowStartNanoseconds, TLV type 156, u64 */
188 59 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
189 : flowStartNanoseconds, 8);
190 59 : f++;
191 :
192 : /* flowEndNanoseconds, TLV type 157, u64 */
193 59 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
194 : flowEndNanoseconds, 8);
195 59 : f++;
196 :
197 59 : return f;
198 : }
199 :
200 : static inline ipfix_field_specifier_t *
201 33 : flowprobe_template_l4_fields (ipfix_field_specifier_t * f)
202 : {
203 : #define flowprobe_template_l4_field_count() 3
204 : /* sourceTransportPort, TLV type 7, u16 */
205 33 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
206 : sourceTransportPort, 2);
207 33 : f++;
208 : /* destinationTransportPort, TLV type 11, u16 */
209 33 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
210 : destinationTransportPort, 2);
211 33 : f++;
212 : /* tcpControlBits, TLV type 6, u16 */
213 33 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
214 : tcpControlBits, 2);
215 33 : f++;
216 :
217 33 : return f;
218 : }
219 :
220 : /**
221 : * @brief Create an IPFIX template packet rewrite string
222 : * @param frm flow_report_main_t *
223 : * @param fr flow_report_t *
224 : * @param collector_address ip4_address_t * the IPFIX collector address
225 : * @param src_address ip4_address_t * the source address we should use
226 : * @param collector_port u16 the collector port we should use, host byte order
227 : * @returns u8 * vector containing the indicated IPFIX template packet
228 : */
229 : static inline u8 *
230 59 : flowprobe_template_rewrite_inline (ipfix_exporter_t *exp, flow_report_t *fr,
231 : u16 collector_port,
232 : flowprobe_variant_t which)
233 : {
234 : ip4_header_t *ip;
235 : udp_header_t *udp;
236 : ipfix_message_header_t *h;
237 : ipfix_set_header_t *s;
238 : ipfix_template_header_t *t;
239 : ipfix_field_specifier_t *f;
240 : ipfix_field_specifier_t *first_field;
241 59 : u8 *rewrite = 0;
242 : ip4_ipfix_template_packet_t *tp;
243 59 : u32 field_count = 0;
244 : flow_report_stream_t *stream;
245 59 : flowprobe_main_t *fm = &flowprobe_main;
246 59 : flowprobe_record_t flags = fr->opaque.as_uword;
247 59 : bool collect_ip4 = false, collect_ip6 = false;
248 59 : bool collect_l4 = false;
249 :
250 59 : stream = &exp->streams[fr->stream_index];
251 :
252 59 : if (flags & FLOW_RECORD_L3)
253 : {
254 43 : collect_ip4 = which == FLOW_VARIANT_L2_IP4 || which == FLOW_VARIANT_IP4;
255 43 : collect_ip6 = which == FLOW_VARIANT_L2_IP6 || which == FLOW_VARIANT_IP6;
256 43 : if (which == FLOW_VARIANT_L2_IP4)
257 12 : flags |= FLOW_RECORD_L2_IP4;
258 43 : if (which == FLOW_VARIANT_L2_IP6)
259 12 : flags |= FLOW_RECORD_L2_IP6;
260 : }
261 59 : if (flags & FLOW_RECORD_L4)
262 : {
263 43 : collect_l4 = (which != FLOW_VARIANT_L2);
264 : }
265 :
266 59 : field_count += flowprobe_template_common_field_count ();
267 59 : if (flags & FLOW_RECORD_L2)
268 43 : field_count += flowprobe_template_l2_field_count ();
269 59 : if (collect_ip4)
270 17 : field_count += flowprobe_template_ip4_field_count ();
271 59 : if (collect_ip6)
272 16 : field_count += flowprobe_template_ip6_field_count ();
273 59 : if (collect_l4)
274 33 : field_count += flowprobe_template_l4_field_count ();
275 :
276 : /* allocate rewrite space */
277 59 : vec_validate_aligned
278 : (rewrite, sizeof (ip4_ipfix_template_packet_t)
279 : + field_count * sizeof (ipfix_field_specifier_t) - 1,
280 : CLIB_CACHE_LINE_BYTES);
281 :
282 59 : tp = (ip4_ipfix_template_packet_t *) rewrite;
283 59 : ip = (ip4_header_t *) & tp->ip4;
284 59 : udp = (udp_header_t *) (ip + 1);
285 59 : h = (ipfix_message_header_t *) (udp + 1);
286 59 : s = (ipfix_set_header_t *) (h + 1);
287 59 : t = (ipfix_template_header_t *) (s + 1);
288 59 : first_field = f = (ipfix_field_specifier_t *) (t + 1);
289 :
290 59 : ip->ip_version_and_header_length = 0x45;
291 59 : ip->ttl = 254;
292 59 : ip->protocol = IP_PROTOCOL_UDP;
293 59 : ip->src_address.as_u32 = exp->src_address.ip.ip4.as_u32;
294 59 : ip->dst_address.as_u32 = exp->ipfix_collector.ip.ip4.as_u32;
295 59 : udp->src_port = clib_host_to_net_u16 (stream->src_port);
296 59 : udp->dst_port = clib_host_to_net_u16 (collector_port);
297 59 : udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip));
298 :
299 : /* FIXUP: message header export_time */
300 : /* FIXUP: message header sequence_number */
301 59 : h->domain_id = clib_host_to_net_u32 (stream->domain_id);
302 :
303 : /* Add TLVs to the template */
304 59 : f = flowprobe_template_common_fields (f);
305 :
306 59 : if (flags & FLOW_RECORD_L2)
307 43 : f = flowprobe_template_l2_fields (f);
308 59 : if (collect_ip4)
309 17 : f = flowprobe_template_ip4_fields (f);
310 59 : if (collect_ip6)
311 16 : f = flowprobe_template_ip6_fields (f);
312 59 : if (collect_l4)
313 33 : f = flowprobe_template_l4_fields (f);
314 :
315 : /* Back to the template packet... */
316 59 : ip = (ip4_header_t *) & tp->ip4;
317 59 : udp = (udp_header_t *) (ip + 1);
318 :
319 59 : ASSERT (f - first_field);
320 : /* Field count in this template */
321 59 : t->id_count = ipfix_id_count (fr->template_id, f - first_field);
322 :
323 59 : fm->template_size[flags] = (u8 *) f - (u8 *) s;
324 :
325 : /* set length in octets */
326 59 : s->set_id_length =
327 59 : ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
328 :
329 : /* message length in octets */
330 59 : h->version_length = version_length ((u8 *) f - (u8 *) h);
331 :
332 59 : ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip);
333 59 : ip->checksum = ip4_header_checksum (ip);
334 :
335 59 : return rewrite;
336 : }
337 :
338 : static u8 *
339 8 : flowprobe_template_rewrite_ip6 (ipfix_exporter_t *exp, flow_report_t *fr,
340 : u16 collector_port,
341 : ipfix_report_element_t *elts, u32 n_elts,
342 : u32 *stream_index)
343 : {
344 8 : return flowprobe_template_rewrite_inline (exp, fr, collector_port,
345 : FLOW_VARIANT_IP6);
346 : }
347 :
348 : static u8 *
349 9 : flowprobe_template_rewrite_ip4 (ipfix_exporter_t *exp, flow_report_t *fr,
350 : u16 collector_port,
351 : ipfix_report_element_t *elts, u32 n_elts,
352 : u32 *stream_index)
353 : {
354 9 : return flowprobe_template_rewrite_inline (exp, fr, collector_port,
355 : FLOW_VARIANT_IP4);
356 : }
357 :
358 : static u8 *
359 14 : flowprobe_template_rewrite_l2 (ipfix_exporter_t *exp, flow_report_t *fr,
360 : u16 collector_port,
361 : ipfix_report_element_t *elts, u32 n_elts,
362 : u32 *stream_index)
363 : {
364 14 : return flowprobe_template_rewrite_inline (exp, fr, collector_port,
365 : FLOW_VARIANT_L2);
366 : }
367 :
368 : static u8 *
369 14 : flowprobe_template_rewrite_l2_ip4 (ipfix_exporter_t *exp, flow_report_t *fr,
370 : u16 collector_port,
371 : ipfix_report_element_t *elts, u32 n_elts,
372 : u32 *stream_index)
373 : {
374 14 : return flowprobe_template_rewrite_inline (exp, fr, collector_port,
375 : FLOW_VARIANT_L2_IP4);
376 : }
377 :
378 : static u8 *
379 14 : flowprobe_template_rewrite_l2_ip6 (ipfix_exporter_t *exp, flow_report_t *fr,
380 : u16 collector_port,
381 : ipfix_report_element_t *elts, u32 n_elts,
382 : u32 *stream_index)
383 : {
384 14 : return flowprobe_template_rewrite_inline (exp, fr, collector_port,
385 : FLOW_VARIANT_L2_IP6);
386 : }
387 :
388 : /**
389 : * @brief Flush accumulated data
390 : * @param frm flow_report_main_t *
391 : * @param fr flow_report_t *
392 : * @param f vlib_frame_t *
393 : *
394 : * <em>Notes:</em>
395 : * This function must simply return the incoming frame, or no template packets
396 : * will be sent.
397 : */
398 : vlib_frame_t *
399 18 : flowprobe_data_callback_ip4 (flow_report_main_t *frm, ipfix_exporter_t *exp,
400 : flow_report_t *fr, vlib_frame_t *f, u32 *to_next,
401 : u32 node_index)
402 : {
403 18 : flowprobe_flush_callback_ip4 ();
404 18 : return f;
405 : }
406 :
407 : vlib_frame_t *
408 14 : flowprobe_data_callback_ip6 (flow_report_main_t *frm, ipfix_exporter_t *exp,
409 : flow_report_t *fr, vlib_frame_t *f, u32 *to_next,
410 : u32 node_index)
411 : {
412 14 : flowprobe_flush_callback_ip6 ();
413 14 : return f;
414 : }
415 :
416 : vlib_frame_t *
417 105 : flowprobe_data_callback_l2 (flow_report_main_t *frm, ipfix_exporter_t *exp,
418 : flow_report_t *fr, vlib_frame_t *f, u32 *to_next,
419 : u32 node_index)
420 : {
421 105 : flowprobe_flush_callback_l2 ();
422 105 : return f;
423 : }
424 :
425 : static int
426 122 : flowprobe_template_add_del (u32 domain_id, u16 src_port,
427 : flowprobe_record_t flags,
428 : vnet_flow_data_callback_t * flow_data_callback,
429 : vnet_flow_rewrite_callback_t * rewrite_callback,
430 : bool is_add, u16 * template_id)
431 : {
432 122 : ipfix_exporter_t *exp = &flow_report_main.exporters[0];
433 122 : vnet_flow_report_add_del_args_t a = {
434 : .rewrite_callback = rewrite_callback,
435 : .flow_data_callback = flow_data_callback,
436 : .is_add = is_add,
437 : .domain_id = domain_id,
438 : .src_port = src_port,
439 : .opaque.as_uword = flags,
440 : };
441 122 : return vnet_flow_report_add_del (exp, &a, template_id);
442 : }
443 :
444 : static void
445 3 : flowprobe_expired_timer_callback (u32 * expired_timers)
446 : {
447 3 : vlib_main_t *vm = vlib_get_main ();
448 3 : flowprobe_main_t *fm = &flowprobe_main;
449 3 : u32 my_cpu_number = vm->thread_index;
450 : int i;
451 : u32 poolindex;
452 :
453 6 : for (i = 0; i < vec_len (expired_timers); i++)
454 : {
455 3 : poolindex = expired_timers[i] & 0x7FFFFFFF;
456 3 : vec_add1 (fm->expired_passive_per_worker[my_cpu_number], poolindex);
457 : }
458 3 : }
459 :
460 : static clib_error_t *
461 3 : flowprobe_create_state_tables (u32 active_timer)
462 : {
463 3 : flowprobe_main_t *fm = &flowprobe_main;
464 3 : vlib_thread_main_t *tm = &vlib_thread_main;
465 3 : vlib_main_t *vm = vlib_get_main ();
466 3 : clib_error_t *error = 0;
467 : u32 num_threads;
468 : int i;
469 :
470 : /* Decide how many worker threads we have */
471 3 : num_threads = 1 /* main thread */ + tm->n_threads;
472 :
473 : /* Hash table per worker */
474 3 : fm->ht_log2len = FLOWPROBE_LOG2_HASHSIZE;
475 :
476 : /* Init per worker flow state and timer wheels */
477 3 : if (active_timer)
478 : {
479 1 : vec_validate (fm->timers_per_worker, num_threads - 1);
480 1 : vec_validate (fm->expired_passive_per_worker, num_threads - 1);
481 1 : vec_validate (fm->hash_per_worker, num_threads - 1);
482 1 : vec_validate (fm->pool_per_worker, num_threads - 1);
483 :
484 2 : for (i = 0; i < num_threads; i++)
485 : {
486 : int j;
487 1 : pool_alloc (fm->pool_per_worker[i], 1 << fm->ht_log2len);
488 1 : vec_resize (fm->hash_per_worker[i], 1 << fm->ht_log2len);
489 262145 : for (j = 0; j < (1 << fm->ht_log2len); j++)
490 262144 : fm->hash_per_worker[i][j] = ~0;
491 2 : fm->timers_per_worker[i] =
492 1 : clib_mem_alloc (sizeof (TWT (tw_timer_wheel)));
493 1 : tw_timer_wheel_init_2t_1w_2048sl (fm->timers_per_worker[i],
494 : flowprobe_expired_timer_callback,
495 : 1.0, 1024);
496 : }
497 1 : fm->disabled = true;
498 : }
499 : else
500 : {
501 2 : f64 now = vlib_time_now (vm);
502 2 : vec_validate (fm->stateless_entry, num_threads - 1);
503 4 : for (i = 0; i < num_threads; i++)
504 2 : fm->stateless_entry[i].last_exported = now;
505 2 : fm->disabled = false;
506 : }
507 3 : fm->initialized = true;
508 3 : return error;
509 : }
510 :
511 : static clib_error_t *
512 37 : flowprobe_clear_state_if_index (u32 sw_if_index)
513 : {
514 37 : flowprobe_main_t *fm = &flowprobe_main;
515 37 : clib_error_t *error = 0;
516 : u32 worker_i;
517 : u32 entry_i;
518 :
519 37 : if (fm->active_timer > 0)
520 : {
521 8 : vec_foreach_index (worker_i, fm->pool_per_worker)
522 : {
523 4 : pool_foreach_index (entry_i, fm->pool_per_worker[worker_i])
524 : {
525 0 : flowprobe_entry_t *e =
526 0 : pool_elt_at_index (fm->pool_per_worker[worker_i], entry_i);
527 0 : if (e->key.rx_sw_if_index == sw_if_index ||
528 0 : e->key.tx_sw_if_index == sw_if_index)
529 : {
530 0 : if (fm->passive_timer > 0)
531 : {
532 0 : tw_timer_stop_2t_1w_2048sl (
533 0 : fm->timers_per_worker[worker_i],
534 : e->passive_timer_handle);
535 : }
536 0 : flowprobe_delete_by_index (worker_i, entry_i);
537 : }
538 : }
539 : }
540 : }
541 :
542 37 : return error;
543 : }
544 :
545 : static int
546 74 : validate_feature_on_interface (flowprobe_main_t * fm, u32 sw_if_index,
547 : u8 which)
548 : {
549 99 : vec_validate_init_empty (fm->flow_per_interface, sw_if_index, ~0);
550 99 : vec_validate_init_empty (fm->direction_per_interface, sw_if_index, ~0);
551 :
552 74 : if (fm->flow_per_interface[sw_if_index] == (u8) ~ 0)
553 37 : return -1;
554 37 : else if (fm->flow_per_interface[sw_if_index] != which)
555 0 : return 0;
556 : else
557 37 : return 1;
558 : }
559 :
560 : /**
561 : * @brief configure / deconfigure the IPFIX flow-per-packet
562 : * @param fm flowprobe_main_t * fm
563 : * @param sw_if_index u32 the desired interface
564 : * @param which u8 the desired datapath
565 : * @param direction u8 the desired direction
566 : * @param is_add int 1 to enable the feature, 0 to disable it
567 : * @returns 0 if successful, non-zero otherwise
568 : */
569 :
570 : static int
571 74 : flowprobe_interface_add_del_feature (flowprobe_main_t *fm, u32 sw_if_index,
572 : u8 which, u8 direction, int is_add)
573 : {
574 74 : vlib_main_t *vm = vlib_get_main ();
575 74 : int rv = 0;
576 74 : u16 template_id = 0;
577 74 : flowprobe_record_t flags = fm->record;
578 :
579 74 : fm->flow_per_interface[sw_if_index] = (is_add) ? which : (u8) ~ 0;
580 74 : fm->direction_per_interface[sw_if_index] = (is_add) ? direction : (u8) ~0;
581 74 : fm->template_per_flow[which] += (is_add) ? 1 : -1;
582 74 : if (is_add && fm->template_per_flow[which] > 1)
583 0 : template_id = fm->template_reports[flags];
584 :
585 74 : if ((is_add && fm->template_per_flow[which] == 1) ||
586 37 : (!is_add && fm->template_per_flow[which] == 0))
587 : {
588 74 : if (which == FLOW_VARIANT_L2)
589 : {
590 36 : if (!is_add)
591 : {
592 18 : flowprobe_flush_callback_l2 ();
593 : }
594 36 : if (fm->record & FLOW_RECORD_L2)
595 : {
596 28 : rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags,
597 : flowprobe_data_callback_l2,
598 : flowprobe_template_rewrite_l2,
599 : is_add, &template_id);
600 28 : fm->template_reports[flags] = (is_add) ? template_id : 0;
601 : }
602 36 : if (fm->record & FLOW_RECORD_L3 || fm->record & FLOW_RECORD_L4)
603 : {
604 28 : rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags,
605 : flowprobe_data_callback_l2,
606 : flowprobe_template_rewrite_l2_ip4,
607 : is_add, &template_id);
608 28 : fm->template_reports[flags | FLOW_RECORD_L2_IP4] =
609 : (is_add) ? template_id : 0;
610 : rv =
611 28 : flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags,
612 : flowprobe_data_callback_l2,
613 : flowprobe_template_rewrite_l2_ip6,
614 : is_add, &template_id);
615 28 : fm->template_reports[flags | FLOW_RECORD_L2_IP6] =
616 : (is_add) ? template_id : 0;
617 :
618 : /* Special case L2 */
619 28 : fm->context[FLOW_VARIANT_L2_IP4].flags =
620 28 : flags | FLOW_RECORD_L2_IP4;
621 28 : fm->context[FLOW_VARIANT_L2_IP6].flags =
622 28 : flags | FLOW_RECORD_L2_IP6;
623 : }
624 : }
625 38 : else if (which == FLOW_VARIANT_IP4)
626 : {
627 20 : if (!is_add)
628 : {
629 10 : flowprobe_flush_callback_ip4 ();
630 : }
631 20 : rv = flowprobe_template_add_del (
632 : 1, UDP_DST_PORT_ipfix, flags, flowprobe_data_callback_ip4,
633 : flowprobe_template_rewrite_ip4, is_add, &template_id);
634 20 : fm->template_reports[flags] = (is_add) ? template_id : 0;
635 : }
636 18 : else if (which == FLOW_VARIANT_IP6)
637 : {
638 18 : if (!is_add)
639 : {
640 9 : flowprobe_flush_callback_ip6 ();
641 : }
642 18 : rv = flowprobe_template_add_del (
643 : 1, UDP_DST_PORT_ipfix, flags, flowprobe_data_callback_ip6,
644 : flowprobe_template_rewrite_ip6, is_add, &template_id);
645 18 : fm->template_reports[flags] = (is_add) ? template_id : 0;
646 : }
647 : }
648 74 : if (rv && rv != VNET_API_ERROR_VALUE_EXIST)
649 : {
650 0 : clib_warning ("vnet_flow_report_add_del returned %d", rv);
651 0 : return -1;
652 : }
653 :
654 74 : if (which != (u8) ~ 0)
655 : {
656 74 : fm->context[which].flags = fm->record;
657 : }
658 :
659 74 : if (direction == FLOW_DIRECTION_RX || direction == FLOW_DIRECTION_BOTH)
660 : {
661 34 : if (which == FLOW_VARIANT_IP4)
662 : {
663 8 : vnet_feature_enable_disable ("ip4-unicast", "flowprobe-input-ip4",
664 : sw_if_index, is_add, 0, 0);
665 8 : vnet_feature_enable_disable ("ip4-multicast", "flowprobe-input-ip4",
666 : sw_if_index, is_add, 0, 0);
667 : }
668 26 : else if (which == FLOW_VARIANT_IP6)
669 : {
670 10 : vnet_feature_enable_disable ("ip6-unicast", "flowprobe-input-ip6",
671 : sw_if_index, is_add, 0, 0);
672 10 : vnet_feature_enable_disable ("ip6-multicast", "flowprobe-input-ip6",
673 : sw_if_index, is_add, 0, 0);
674 : }
675 16 : else if (which == FLOW_VARIANT_L2)
676 16 : vnet_feature_enable_disable ("device-input", "flowprobe-input-l2",
677 : sw_if_index, is_add, 0, 0);
678 : }
679 :
680 74 : if (direction == FLOW_DIRECTION_TX || direction == FLOW_DIRECTION_BOTH)
681 : {
682 42 : if (which == FLOW_VARIANT_IP4)
683 12 : vnet_feature_enable_disable ("ip4-output", "flowprobe-output-ip4",
684 : sw_if_index, is_add, 0, 0);
685 30 : else if (which == FLOW_VARIANT_IP6)
686 10 : vnet_feature_enable_disable ("ip6-output", "flowprobe-output-ip6",
687 : sw_if_index, is_add, 0, 0);
688 20 : else if (which == FLOW_VARIANT_L2)
689 20 : vnet_feature_enable_disable ("interface-output", "flowprobe-output-l2",
690 : sw_if_index, is_add, 0, 0);
691 : }
692 :
693 : /* Stateful flow collection */
694 74 : if (is_add && !fm->initialized)
695 : {
696 3 : flowprobe_create_state_tables (fm->active_timer);
697 3 : if (fm->active_timer)
698 1 : vlib_process_signal_event (vm, flowprobe_timer_node.index, 1, 0);
699 : }
700 :
701 74 : if (!is_add && fm->initialized)
702 : {
703 37 : flowprobe_clear_state_if_index (sw_if_index);
704 : }
705 :
706 74 : return 0;
707 : }
708 :
709 : /**
710 : * @brief API message handler
711 : * @param mp vl_api_flowprobe_tx_interface_add_del_t * mp the api message
712 : */
713 0 : void vl_api_flowprobe_tx_interface_add_del_t_handler
714 : (vl_api_flowprobe_tx_interface_add_del_t * mp)
715 : {
716 0 : flowprobe_main_t *fm = &flowprobe_main;
717 : vl_api_flowprobe_tx_interface_add_del_reply_t *rmp;
718 0 : u32 sw_if_index = ntohl (mp->sw_if_index);
719 0 : int rv = 0;
720 :
721 0 : VALIDATE_SW_IF_INDEX (mp);
722 :
723 0 : if (fm->record == 0)
724 : {
725 0 : clib_warning ("Please specify flowprobe params record first...");
726 0 : rv = VNET_API_ERROR_CANNOT_ENABLE_DISABLE_FEATURE;
727 0 : goto out;
728 : }
729 :
730 0 : rv = validate_feature_on_interface (fm, sw_if_index, mp->which);
731 0 : if ((rv == 1 && mp->is_add == 1) || rv == 0)
732 : {
733 0 : rv = VNET_API_ERROR_CANNOT_ENABLE_DISABLE_FEATURE;
734 0 : goto out;
735 : }
736 :
737 0 : rv = flowprobe_interface_add_del_feature (fm, sw_if_index, mp->which,
738 0 : FLOW_DIRECTION_TX, mp->is_add);
739 :
740 0 : out:
741 0 : BAD_SW_IF_INDEX_LABEL;
742 :
743 0 : REPLY_MACRO (VL_API_FLOWPROBE_TX_INTERFACE_ADD_DEL_REPLY);
744 : }
745 :
746 : void
747 74 : vl_api_flowprobe_interface_add_del_t_handler (
748 : vl_api_flowprobe_interface_add_del_t *mp)
749 : {
750 74 : flowprobe_main_t *fm = &flowprobe_main;
751 : vl_api_flowprobe_interface_add_del_reply_t *rmp;
752 : u32 sw_if_index;
753 : u8 which;
754 : u8 direction;
755 : bool is_add;
756 74 : int rv = 0;
757 :
758 74 : VALIDATE_SW_IF_INDEX (mp);
759 :
760 74 : sw_if_index = ntohl (mp->sw_if_index);
761 74 : is_add = mp->is_add;
762 :
763 74 : if (mp->which == FLOWPROBE_WHICH_IP4)
764 20 : which = FLOW_VARIANT_IP4;
765 54 : else if (mp->which == FLOWPROBE_WHICH_IP6)
766 18 : which = FLOW_VARIANT_IP6;
767 36 : else if (mp->which == FLOWPROBE_WHICH_L2)
768 36 : which = FLOW_VARIANT_L2;
769 : else
770 : {
771 0 : clib_warning ("Invalid value of which");
772 0 : rv = VNET_API_ERROR_INVALID_VALUE;
773 0 : goto out;
774 : }
775 :
776 74 : if (mp->direction == FLOWPROBE_DIRECTION_RX)
777 32 : direction = FLOW_DIRECTION_RX;
778 42 : else if (mp->direction == FLOWPROBE_DIRECTION_TX)
779 40 : direction = FLOW_DIRECTION_TX;
780 2 : else if (mp->direction == FLOWPROBE_DIRECTION_BOTH)
781 2 : direction = FLOW_DIRECTION_BOTH;
782 : else
783 : {
784 0 : clib_warning ("Invalid value of direction");
785 0 : rv = VNET_API_ERROR_INVALID_VALUE;
786 0 : goto out;
787 : }
788 :
789 74 : if (fm->record == 0)
790 : {
791 0 : clib_warning ("Please specify flowprobe params record first");
792 0 : rv = VNET_API_ERROR_CANNOT_ENABLE_DISABLE_FEATURE;
793 0 : goto out;
794 : }
795 :
796 74 : rv = validate_feature_on_interface (fm, sw_if_index, which);
797 74 : if (rv == 1)
798 : {
799 37 : if (is_add)
800 : {
801 0 : clib_warning ("Variant is already enabled for given interface");
802 0 : rv = VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
803 0 : goto out;
804 : }
805 : }
806 37 : else if (rv == 0)
807 : {
808 0 : clib_warning ("Interface has different variant enabled");
809 0 : rv = VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
810 0 : goto out;
811 : }
812 37 : else if (rv == -1)
813 : {
814 37 : if (!is_add)
815 : {
816 0 : clib_warning ("Interface has no variant enabled");
817 0 : rv = VNET_API_ERROR_NO_SUCH_ENTRY;
818 0 : goto out;
819 : }
820 : }
821 :
822 74 : rv = flowprobe_interface_add_del_feature (fm, sw_if_index, which, direction,
823 : is_add);
824 :
825 74 : out:
826 74 : BAD_SW_IF_INDEX_LABEL;
827 :
828 74 : REPLY_MACRO (VL_API_FLOWPROBE_INTERFACE_ADD_DEL_REPLY);
829 : }
830 :
831 : static void
832 4 : send_flowprobe_interface_details (u32 sw_if_index, u8 which, u8 direction,
833 : vl_api_registration_t *reg, u32 context)
834 : {
835 4 : flowprobe_main_t *fm = &flowprobe_main;
836 4 : vl_api_flowprobe_interface_details_t *rmp = 0;
837 :
838 4 : rmp = vl_msg_api_alloc (sizeof (*rmp));
839 4 : if (!rmp)
840 0 : return;
841 4 : clib_memset (rmp, 0, sizeof (*rmp));
842 4 : rmp->_vl_msg_id =
843 4 : ntohs (VL_API_FLOWPROBE_INTERFACE_DETAILS + REPLY_MSG_ID_BASE);
844 4 : rmp->context = context;
845 :
846 4 : rmp->sw_if_index = htonl (sw_if_index);
847 :
848 4 : if (which == FLOW_VARIANT_IP4)
849 2 : rmp->which = FLOWPROBE_WHICH_IP4;
850 2 : else if (which == FLOW_VARIANT_IP6)
851 1 : rmp->which = FLOWPROBE_WHICH_IP6;
852 1 : else if (which == FLOW_VARIANT_L2)
853 1 : rmp->which = FLOWPROBE_WHICH_L2;
854 : else
855 0 : ASSERT (0);
856 :
857 4 : if (direction == FLOW_DIRECTION_RX)
858 1 : rmp->direction = FLOWPROBE_DIRECTION_RX;
859 3 : else if (direction == FLOW_DIRECTION_TX)
860 2 : rmp->direction = FLOWPROBE_DIRECTION_TX;
861 1 : else if (direction == FLOW_DIRECTION_BOTH)
862 1 : rmp->direction = FLOWPROBE_DIRECTION_BOTH;
863 : else
864 0 : ASSERT (0);
865 :
866 4 : vl_api_send_msg (reg, (u8 *) rmp);
867 : }
868 :
869 : static void
870 3 : vl_api_flowprobe_interface_dump_t_handler (
871 : vl_api_flowprobe_interface_dump_t *mp)
872 : {
873 3 : flowprobe_main_t *fm = &flowprobe_main;
874 : vl_api_registration_t *reg;
875 : u32 sw_if_index;
876 :
877 3 : reg = vl_api_client_index_to_registration (mp->client_index);
878 3 : if (!reg)
879 0 : return;
880 :
881 3 : sw_if_index = ntohl (mp->sw_if_index);
882 :
883 3 : if (sw_if_index == ~0)
884 : {
885 : u8 *which;
886 :
887 11 : vec_foreach (which, fm->flow_per_interface)
888 : {
889 10 : if (*which == (u8) ~0)
890 7 : continue;
891 :
892 3 : sw_if_index = which - fm->flow_per_interface;
893 3 : send_flowprobe_interface_details (
894 3 : sw_if_index, *which, fm->direction_per_interface[sw_if_index], reg,
895 : mp->context);
896 : }
897 : }
898 2 : else if (vec_len (fm->flow_per_interface) > sw_if_index &&
899 1 : fm->flow_per_interface[sw_if_index] != (u8) ~0)
900 : {
901 1 : send_flowprobe_interface_details (
902 1 : sw_if_index, fm->flow_per_interface[sw_if_index],
903 1 : fm->direction_per_interface[sw_if_index], reg, mp->context);
904 : }
905 : }
906 :
907 : #define vec_neg_search(v,E) \
908 : ({ \
909 : word _v(i) = 0; \
910 : while (_v(i) < vec_len(v) && v[_v(i)] == E) \
911 : { \
912 : _v(i)++; \
913 : } \
914 : if (_v(i) == vec_len(v)) \
915 : _v(i) = ~0; \
916 : _v(i); \
917 : })
918 :
919 : static int
920 35 : flowprobe_params (flowprobe_main_t * fm, u8 record_l2,
921 : u8 record_l3, u8 record_l4,
922 : u32 active_timer, u32 passive_timer)
923 : {
924 35 : flowprobe_record_t flags = 0;
925 :
926 245 : if (vec_neg_search (fm->flow_per_interface, (u8) ~ 0) != ~0)
927 0 : return VNET_API_ERROR_UNSUPPORTED;
928 :
929 35 : if (record_l2)
930 23 : flags |= FLOW_RECORD_L2;
931 35 : if (record_l3)
932 21 : flags |= FLOW_RECORD_L3;
933 35 : if (record_l4)
934 21 : flags |= FLOW_RECORD_L4;
935 :
936 35 : fm->record = flags;
937 :
938 : /*
939 : * Timers: ~0 is default, 0 is off
940 : */
941 35 : fm->active_timer =
942 35 : (active_timer == (u32) ~ 0 ? FLOWPROBE_TIMER_ACTIVE : active_timer);
943 35 : fm->passive_timer =
944 35 : (passive_timer == (u32) ~ 0 ? FLOWPROBE_TIMER_PASSIVE : passive_timer);
945 :
946 35 : return 0;
947 : }
948 :
949 : void
950 0 : vl_api_flowprobe_params_t_handler (vl_api_flowprobe_params_t * mp)
951 : {
952 0 : flowprobe_main_t *fm = &flowprobe_main;
953 : vl_api_flowprobe_params_reply_t *rmp;
954 0 : int rv = 0;
955 :
956 0 : rv = flowprobe_params
957 : (fm,
958 0 : mp->record_flags & FLOWPROBE_RECORD_FLAG_L2,
959 0 : mp->record_flags & FLOWPROBE_RECORD_FLAG_L3,
960 0 : mp->record_flags & FLOWPROBE_RECORD_FLAG_L4,
961 : clib_net_to_host_u32 (mp->active_timer),
962 : clib_net_to_host_u32 (mp->passive_timer));
963 :
964 0 : REPLY_MACRO (VL_API_FLOWPROBE_PARAMS_REPLY);
965 : }
966 :
967 : void
968 35 : vl_api_flowprobe_set_params_t_handler (vl_api_flowprobe_set_params_t *mp)
969 : {
970 35 : flowprobe_main_t *fm = &flowprobe_main;
971 : vl_api_flowprobe_set_params_reply_t *rmp;
972 : bool record_l2, record_l3, record_l4;
973 : u32 active_timer;
974 : u32 passive_timer;
975 35 : int rv = 0;
976 :
977 35 : record_l2 = (mp->record_flags & FLOWPROBE_RECORD_FLAG_L2);
978 35 : record_l3 = (mp->record_flags & FLOWPROBE_RECORD_FLAG_L3);
979 35 : record_l4 = (mp->record_flags & FLOWPROBE_RECORD_FLAG_L4);
980 :
981 35 : active_timer = clib_net_to_host_u32 (mp->active_timer);
982 35 : passive_timer = clib_net_to_host_u32 (mp->passive_timer);
983 :
984 35 : if (passive_timer > 0 && active_timer > passive_timer)
985 : {
986 0 : clib_warning ("Passive timer must be greater than active timer");
987 0 : rv = VNET_API_ERROR_INVALID_VALUE;
988 0 : goto out;
989 : }
990 :
991 35 : rv = flowprobe_params (fm, record_l2, record_l3, record_l4, active_timer,
992 : passive_timer);
993 35 : if (rv == VNET_API_ERROR_UNSUPPORTED)
994 0 : clib_warning (
995 : "Cannot change params when feature is enabled on some interfaces");
996 :
997 35 : out:
998 35 : REPLY_MACRO (VL_API_FLOWPROBE_SET_PARAMS_REPLY);
999 : }
1000 :
1001 : void
1002 1 : vl_api_flowprobe_get_params_t_handler (vl_api_flowprobe_get_params_t *mp)
1003 : {
1004 1 : flowprobe_main_t *fm = &flowprobe_main;
1005 : vl_api_flowprobe_get_params_reply_t *rmp;
1006 1 : u8 record_flags = 0;
1007 1 : int rv = 0;
1008 :
1009 1 : if (fm->record & FLOW_RECORD_L2)
1010 1 : record_flags |= FLOWPROBE_RECORD_FLAG_L2;
1011 1 : if (fm->record & FLOW_RECORD_L3)
1012 1 : record_flags |= FLOWPROBE_RECORD_FLAG_L3;
1013 1 : if (fm->record & FLOW_RECORD_L4)
1014 1 : record_flags |= FLOWPROBE_RECORD_FLAG_L4;
1015 :
1016 : // clang-format off
1017 1 : REPLY_MACRO2 (VL_API_FLOWPROBE_GET_PARAMS_REPLY,
1018 : ({
1019 : rmp->record_flags = record_flags;
1020 : rmp->active_timer = htonl (fm->active_timer);
1021 : rmp->passive_timer = htonl (fm->passive_timer);
1022 : }));
1023 : // clang-format on
1024 : }
1025 :
1026 : /* *INDENT-OFF* */
1027 : VLIB_PLUGIN_REGISTER () = {
1028 : .version = VPP_BUILD_VER,
1029 : .description = "Flow per Packet",
1030 : };
1031 : /* *INDENT-ON* */
1032 :
1033 : u8 *
1034 0 : format_flowprobe_direction (u8 *s, va_list *args)
1035 : {
1036 0 : u8 *direction = va_arg (*args, u8 *);
1037 0 : if (*direction == FLOW_DIRECTION_RX)
1038 0 : s = format (s, "rx");
1039 0 : else if (*direction == FLOW_DIRECTION_TX)
1040 0 : s = format (s, "tx");
1041 0 : else if (*direction == FLOW_DIRECTION_BOTH)
1042 0 : s = format (s, "rx tx");
1043 :
1044 0 : return s;
1045 : }
1046 :
1047 : u8 *
1048 0 : format_flowprobe_entry (u8 * s, va_list * args)
1049 : {
1050 0 : flowprobe_entry_t *e = va_arg (*args, flowprobe_entry_t *);
1051 0 : s = format (s, " %U", format_flowprobe_direction, &e->key.direction);
1052 0 : s = format (s, " %d/%d", e->key.rx_sw_if_index, e->key.tx_sw_if_index);
1053 :
1054 0 : s = format (s, " %U %U", format_ethernet_address, &e->key.src_mac,
1055 : format_ethernet_address, &e->key.dst_mac);
1056 0 : s = format (s, " %U -> %U",
1057 : format_ip46_address, &e->key.src_address, IP46_TYPE_ANY,
1058 : format_ip46_address, &e->key.dst_address, IP46_TYPE_ANY);
1059 0 : s = format (s, " %d", e->key.protocol);
1060 0 : s = format (s, " %d %d\n", clib_net_to_host_u16 (e->key.src_port),
1061 0 : clib_net_to_host_u16 (e->key.dst_port));
1062 :
1063 0 : return s;
1064 : }
1065 :
1066 : u8 *
1067 0 : format_flowprobe_feature (u8 * s, va_list * args)
1068 : {
1069 0 : u8 *which = va_arg (*args, u8 *);
1070 0 : if (*which == FLOW_VARIANT_IP4)
1071 0 : s = format (s, "ip4");
1072 0 : else if (*which == FLOW_VARIANT_IP6)
1073 0 : s = format (s, "ip6");
1074 0 : else if (*which == FLOW_VARIANT_L2)
1075 0 : s = format (s, "l2");
1076 :
1077 0 : return s;
1078 : }
1079 :
1080 : u8 *
1081 0 : format_flowprobe_params (u8 * s, va_list * args)
1082 : {
1083 0 : flowprobe_record_t flags = va_arg (*args, flowprobe_record_t);
1084 0 : u32 active_timer = va_arg (*args, u32);
1085 0 : u32 passive_timer = va_arg (*args, u32);
1086 :
1087 0 : if (flags & FLOW_RECORD_L2)
1088 0 : s = format (s, " l2");
1089 0 : if (flags & FLOW_RECORD_L3)
1090 0 : s = format (s, " l3");
1091 0 : if (flags & FLOW_RECORD_L4)
1092 0 : s = format (s, " l4");
1093 :
1094 0 : if (active_timer != (u32) ~ 0)
1095 0 : s = format (s, " active: %d", active_timer);
1096 :
1097 0 : if (passive_timer != (u32) ~ 0)
1098 0 : s = format (s, " passive: %d", passive_timer);
1099 :
1100 0 : return s;
1101 : }
1102 :
1103 : static clib_error_t *
1104 0 : flowprobe_show_table_fn (vlib_main_t * vm,
1105 : unformat_input_t * input, vlib_cli_command_t * cm)
1106 : {
1107 0 : flowprobe_main_t *fm = &flowprobe_main;
1108 : int i;
1109 : flowprobe_entry_t *e;
1110 :
1111 0 : vlib_cli_output (vm, "Dumping IPFIX table");
1112 :
1113 0 : for (i = 0; i < vec_len (fm->pool_per_worker); i++)
1114 : {
1115 : /* *INDENT-OFF* */
1116 0 : pool_foreach (e, fm->pool_per_worker[i])
1117 : {
1118 0 : vlib_cli_output (vm, "%U",
1119 : format_flowprobe_entry,
1120 : e);
1121 : }
1122 : /* *INDENT-ON* */
1123 :
1124 : }
1125 0 : return 0;
1126 : }
1127 :
1128 : static clib_error_t *
1129 0 : flowprobe_show_stats_fn (vlib_main_t * vm,
1130 : unformat_input_t * input, vlib_cli_command_t * cm)
1131 : {
1132 0 : flowprobe_main_t *fm = &flowprobe_main;
1133 : int i;
1134 :
1135 0 : vlib_cli_output (vm, "IPFIX table statistics");
1136 0 : vlib_cli_output (vm, "Flow entry size: %d\n", sizeof (flowprobe_entry_t));
1137 0 : vlib_cli_output (vm, "Flow pool size per thread: %d\n",
1138 : 0x1 << FLOWPROBE_LOG2_HASHSIZE);
1139 :
1140 0 : for (i = 0; i < vec_len (fm->pool_per_worker); i++)
1141 0 : vlib_cli_output (vm, "Pool utilisation thread %d is %d%%\n", i,
1142 0 : (100 * pool_elts (fm->pool_per_worker[i])) /
1143 : (0x1 << FLOWPROBE_LOG2_HASHSIZE));
1144 0 : return 0;
1145 : }
1146 :
1147 : static clib_error_t *
1148 0 : flowprobe_interface_add_del_feature_command_fn (vlib_main_t *vm,
1149 : unformat_input_t *input,
1150 : vlib_cli_command_t *cmd)
1151 : {
1152 0 : flowprobe_main_t *fm = &flowprobe_main;
1153 0 : u32 sw_if_index = ~0;
1154 0 : int is_add = 1;
1155 0 : u8 which = FLOW_VARIANT_IP4;
1156 0 : flowprobe_direction_t direction = FLOW_DIRECTION_TX;
1157 : int rv;
1158 :
1159 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1160 : {
1161 0 : if (unformat (input, "disable"))
1162 0 : is_add = 0;
1163 0 : else if (unformat (input, "%U", unformat_vnet_sw_interface,
1164 : fm->vnet_main, &sw_if_index));
1165 0 : else if (unformat (input, "ip4"))
1166 0 : which = FLOW_VARIANT_IP4;
1167 0 : else if (unformat (input, "ip6"))
1168 0 : which = FLOW_VARIANT_IP6;
1169 0 : else if (unformat (input, "l2"))
1170 0 : which = FLOW_VARIANT_L2;
1171 0 : else if (unformat (input, "rx"))
1172 0 : direction = FLOW_DIRECTION_RX;
1173 0 : else if (unformat (input, "tx"))
1174 0 : direction = FLOW_DIRECTION_TX;
1175 0 : else if (unformat (input, "both"))
1176 0 : direction = FLOW_DIRECTION_BOTH;
1177 : else
1178 0 : break;
1179 : }
1180 :
1181 0 : if (fm->record == 0)
1182 0 : return clib_error_return (0,
1183 : "Please specify flowprobe params record first...");
1184 :
1185 0 : if (sw_if_index == ~0)
1186 0 : return clib_error_return (0, "Please specify an interface...");
1187 :
1188 0 : rv = validate_feature_on_interface (fm, sw_if_index, which);
1189 0 : if (rv == 1)
1190 : {
1191 0 : if (is_add)
1192 0 : return clib_error_return (0,
1193 : "Datapath is already enabled for given interface...");
1194 : }
1195 0 : else if (rv == 0)
1196 0 : return clib_error_return (0,
1197 : "Interface has enable different datapath ...");
1198 0 : else if (rv == -1)
1199 : {
1200 0 : if (!is_add)
1201 : {
1202 0 : return clib_error_return (0, "Interface has no datapath enabled");
1203 : }
1204 : }
1205 :
1206 0 : rv = flowprobe_interface_add_del_feature (fm, sw_if_index, which, direction,
1207 : is_add);
1208 0 : switch (rv)
1209 : {
1210 0 : case 0:
1211 0 : break;
1212 :
1213 0 : case VNET_API_ERROR_INVALID_SW_IF_INDEX:
1214 0 : return clib_error_return
1215 : (0, "Invalid interface, only works on physical ports");
1216 : break;
1217 :
1218 0 : case VNET_API_ERROR_UNIMPLEMENTED:
1219 0 : return clib_error_return (0, "ip6 not supported");
1220 : break;
1221 :
1222 0 : default:
1223 0 : return clib_error_return (0, "flowprobe_enable_disable returned %d",
1224 : rv);
1225 : }
1226 0 : return 0;
1227 : }
1228 :
1229 : static clib_error_t *
1230 0 : flowprobe_show_feature_command_fn (vlib_main_t * vm,
1231 : unformat_input_t * input,
1232 : vlib_cli_command_t * cmd)
1233 : {
1234 0 : flowprobe_main_t *fm = &flowprobe_main;
1235 : u8 *which;
1236 : u32 sw_if_index;
1237 :
1238 0 : vec_foreach (which, fm->flow_per_interface)
1239 : {
1240 0 : if (*which == (u8) ~ 0)
1241 0 : continue;
1242 :
1243 0 : sw_if_index = which - fm->flow_per_interface;
1244 0 : vlib_cli_output (vm, " %U %U %U", format_vnet_sw_if_index_name,
1245 : vnet_get_main (), sw_if_index, format_flowprobe_feature,
1246 : which, format_flowprobe_direction,
1247 0 : &fm->direction_per_interface[sw_if_index]);
1248 : }
1249 0 : return 0;
1250 : }
1251 :
1252 : static clib_error_t *
1253 0 : flowprobe_params_command_fn (vlib_main_t * vm,
1254 : unformat_input_t * input,
1255 : vlib_cli_command_t * cmd)
1256 : {
1257 0 : flowprobe_main_t *fm = &flowprobe_main;
1258 0 : bool record_l2 = false, record_l3 = false, record_l4 = false;
1259 0 : u32 active_timer = ~0;
1260 0 : u32 passive_timer = ~0;
1261 :
1262 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1263 : {
1264 0 : if (unformat (input, "active %d", &active_timer))
1265 : ;
1266 0 : else if (unformat (input, "passive %d", &passive_timer))
1267 : ;
1268 0 : else if (unformat (input, "record"))
1269 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1270 : {
1271 0 : if (unformat (input, "l2"))
1272 0 : record_l2 = true;
1273 0 : else if (unformat (input, "l3"))
1274 0 : record_l3 = true;
1275 0 : else if (unformat (input, "l4"))
1276 0 : record_l4 = true;
1277 : else
1278 0 : break;
1279 : }
1280 : else
1281 0 : break;
1282 : }
1283 :
1284 0 : if (passive_timer > 0 && active_timer > passive_timer)
1285 0 : return clib_error_return (0,
1286 : "Passive timer has to be greater than active one...");
1287 :
1288 0 : if (flowprobe_params (fm, record_l2, record_l3, record_l4,
1289 : active_timer, passive_timer))
1290 0 : return clib_error_return (0,
1291 : "Couldn't change flowperpacket params when feature is enabled on some interface ...");
1292 0 : return 0;
1293 : }
1294 :
1295 : static clib_error_t *
1296 0 : flowprobe_show_params_command_fn (vlib_main_t * vm,
1297 : unformat_input_t * input,
1298 : vlib_cli_command_t * cmd)
1299 : {
1300 0 : flowprobe_main_t *fm = &flowprobe_main;
1301 0 : flowprobe_record_t flags = fm->record;
1302 0 : u32 active_timer = fm->active_timer;
1303 0 : u32 passive_timer = fm->passive_timer;
1304 :
1305 0 : vlib_cli_output (vm, "%U", format_flowprobe_params, flags, active_timer,
1306 : passive_timer);
1307 0 : return 0;
1308 : }
1309 :
1310 : /*?
1311 : * '<em>flowprobe feature add-del</em>' commands to enable/disable
1312 : * per-packet IPFIX flow record generation on an interface
1313 : *
1314 : * @cliexpar
1315 : * @parblock
1316 : * To enable per-packet IPFIX flow-record generation on an interface:
1317 : * @cliexcmd{flowprobe feature add-del GigabitEthernet2/0/0}
1318 : *
1319 : * To disable per-packet IPFIX flow-record generation on an interface:
1320 : * @cliexcmd{flowprobe feature add-del GigabitEthernet2/0/0 disable}
1321 : * @cliexend
1322 : * @endparblock
1323 : ?*/
1324 : /* *INDENT-OFF* */
1325 224233 : VLIB_CLI_COMMAND (flowprobe_enable_disable_command, static) = {
1326 : .path = "flowprobe feature add-del",
1327 : .short_help = "flowprobe feature add-del <interface-name> [(l2|ip4|ip6)] "
1328 : "[(rx|tx|both)] [disable]",
1329 : .function = flowprobe_interface_add_del_feature_command_fn,
1330 : };
1331 224233 : VLIB_CLI_COMMAND (flowprobe_params_command, static) = {
1332 : .path = "flowprobe params",
1333 : .short_help = "flowprobe params record [l2] [l3] [l4] [active <timer>] "
1334 : "[passive <timer>]",
1335 : .function = flowprobe_params_command_fn,
1336 : };
1337 :
1338 224233 : VLIB_CLI_COMMAND (flowprobe_show_feature_command, static) = {
1339 : .path = "show flowprobe feature",
1340 : .short_help =
1341 : "show flowprobe feature",
1342 : .function = flowprobe_show_feature_command_fn,
1343 : };
1344 224233 : VLIB_CLI_COMMAND (flowprobe_show_params_command, static) = {
1345 : .path = "show flowprobe params",
1346 : .short_help =
1347 : "show flowprobe params",
1348 : .function = flowprobe_show_params_command_fn,
1349 : };
1350 224233 : VLIB_CLI_COMMAND (flowprobe_show_table_command, static) = {
1351 : .path = "show flowprobe table",
1352 : .short_help = "show flowprobe table",
1353 : .function = flowprobe_show_table_fn,
1354 : };
1355 224233 : VLIB_CLI_COMMAND (flowprobe_show_stats_command, static) = {
1356 : .path = "show flowprobe statistics",
1357 : .short_help = "show flowprobe statistics",
1358 : .function = flowprobe_show_stats_fn,
1359 : };
1360 : /* *INDENT-ON* */
1361 :
1362 : /*
1363 : * Main-core process, sending an interrupt to the per worker input
1364 : * process that spins the per worker timer wheel.
1365 : */
1366 : static uword
1367 575 : timer_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
1368 : {
1369 575 : uword *event_data = 0;
1370 575 : vlib_main_t **worker_vms = 0, *worker_vm;
1371 575 : flowprobe_main_t *fm = &flowprobe_main;
1372 :
1373 : /* Wait for Godot... */
1374 575 : vlib_process_wait_for_event_or_clock (vm, 1e9);
1375 1 : uword event_type = vlib_process_get_events (vm, &event_data);
1376 1 : if (event_type != 1)
1377 0 : clib_warning ("bogus kickoff event received, %d", event_type);
1378 1 : vec_reset_length (event_data);
1379 :
1380 : int i;
1381 1 : if (vlib_get_n_threads () == 0)
1382 0 : vec_add1 (worker_vms, vm);
1383 : else
1384 : {
1385 2 : for (i = 0; i < vlib_get_n_threads (); i++)
1386 : {
1387 1 : worker_vm = vlib_get_main_by_index (i);
1388 1 : if (worker_vm)
1389 1 : vec_add1 (worker_vms, worker_vm);
1390 : }
1391 : }
1392 1 : f64 sleep_duration = 0.1;
1393 :
1394 : while (1)
1395 : {
1396 : /* Send an interrupt to each timer input node */
1397 146453 : sleep_duration = 0.1;
1398 292906 : for (i = 0; i < vec_len (worker_vms); i++)
1399 : {
1400 146453 : worker_vm = worker_vms[i];
1401 146453 : if (worker_vm)
1402 : {
1403 146453 : vlib_node_set_interrupt_pending (worker_vm,
1404 : flowprobe_walker_node.index);
1405 146453 : sleep_duration =
1406 146453 : (fm->expired_passive_per_worker[i] > 0) ? 1e-4 : 0.1;
1407 : }
1408 : }
1409 146453 : vlib_process_suspend (vm, sleep_duration);
1410 : }
1411 : return 0; /* or not */
1412 : }
1413 :
1414 : /* *INDENT-OFF* */
1415 144044 : VLIB_REGISTER_NODE (flowprobe_timer_node,static) = {
1416 : .function = timer_process,
1417 : .name = "flowprobe-timer-process",
1418 : .type = VLIB_NODE_TYPE_PROCESS,
1419 : };
1420 : /* *INDENT-ON* */
1421 :
1422 : #include <flowprobe/flowprobe.api.c>
1423 :
1424 : /**
1425 : * @brief Set up the API message handling tables
1426 : * @param vm vlib_main_t * vlib main data structure pointer
1427 : * @returns 0 to indicate all is well, or a clib_error_t
1428 : */
1429 : static clib_error_t *
1430 575 : flowprobe_init (vlib_main_t * vm)
1431 : {
1432 575 : flowprobe_main_t *fm = &flowprobe_main;
1433 575 : vlib_thread_main_t *tm = &vlib_thread_main;
1434 575 : clib_error_t *error = 0;
1435 : u32 num_threads;
1436 : int i;
1437 :
1438 575 : fm->vnet_main = vnet_get_main ();
1439 :
1440 : /* Ask for a correctly-sized block of API message decode slots */
1441 575 : fm->msg_id_base = setup_message_id_table ();
1442 :
1443 : /* Set up time reference pair */
1444 575 : fm->vlib_time_0 = vlib_time_now (vm);
1445 575 : fm->nanosecond_time_0 = unix_time_now_nsec ();
1446 :
1447 575 : clib_memset (fm->template_reports, 0, sizeof (fm->template_reports));
1448 575 : clib_memset (fm->template_size, 0, sizeof (fm->template_size));
1449 575 : clib_memset (fm->template_per_flow, 0, sizeof (fm->template_per_flow));
1450 :
1451 : /* Decide how many worker threads we have */
1452 575 : num_threads = 1 /* main thread */ + tm->n_threads;
1453 :
1454 : /* Allocate per worker thread vectors per flavour */
1455 3450 : for (i = 0; i < FLOW_N_VARIANTS; i++)
1456 : {
1457 2875 : vec_validate (fm->context[i].buffers_per_worker, num_threads - 1);
1458 2875 : vec_validate (fm->context[i].frames_per_worker, num_threads - 1);
1459 2875 : vec_validate (fm->context[i].next_record_offset_per_worker,
1460 : num_threads - 1);
1461 : }
1462 :
1463 575 : fm->active_timer = FLOWPROBE_TIMER_ACTIVE;
1464 575 : fm->passive_timer = FLOWPROBE_TIMER_PASSIVE;
1465 :
1466 575 : return error;
1467 : }
1468 :
1469 1151 : VLIB_INIT_FUNCTION (flowprobe_init);
1470 :
1471 : /*
1472 : * fd.io coding-style-patch-verification: ON
1473 : *
1474 : * Local Variables:
1475 : * eval: (c-set-style "gnu")
1476 : * End:
1477 : */
|