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