FD.io VPP  v17.01.1-3-gc6833f8
Vector Packet Processing
flowperpkt.c
Go to the documentation of this file.
1 /*
2  * flowperpkt.c - per-packet data capture flow report plugin
3  *
4  * Copyright (c) <current-year> <your-organization>
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 <vnet/plugin/plugin.h>
28 #include <flowperpkt/flowperpkt.h>
29 
30 #include <vlibapi/api.h>
31 #include <vlibmemory/api.h>
32 #include <vlibsocket/api.h>
33 
34 /* define message IDs */
36 
37 /* define message structures */
38 #define vl_typedefs
40 #undef vl_typedefs
41 
42 /* define generated endian-swappers */
43 #define vl_endianfun
45 #undef vl_endianfun
46 
47 /* instantiate all the print functions we know about */
48 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
49 #define vl_printfun
51 #undef vl_printfun
52 
54 
55 /* Get the API version number */
56 #define vl_api_version(n,v) static u32 api_version=(v);
58 #undef vl_api_version
59 
60 /* Define the per-interface configurable feature */
61 /* *INDENT-OFF* */
62 VNET_FEATURE_INIT (flow_perpacket, static) = {
63  .arc_name = "ip4-output",
64  .node_name = "flowperpkt",
65  .runs_before = VNET_FEATURES ("interface-output"),
66 };
67 /* *INDENT-ON* */
68 
69 /*
70  * A handy macro to set up a message reply.
71  * Assumes that the following variables are available:
72  * mp - pointer to request message
73  * rmp - pointer to reply message type
74  * rv - return value
75  */
76 #define REPLY_MACRO(t) \
77 do { \
78  unix_shared_memory_queue_t * q = \
79  vl_api_client_index_to_input_queue (mp->client_index); \
80  if (!q) \
81  return; \
82  \
83  rmp = vl_msg_api_alloc (sizeof (*rmp)); \
84  rmp->_vl_msg_id = ntohs((t)+fm->msg_id_base); \
85  rmp->context = mp->context; \
86  rmp->retval = ntohl(rv); \
87  \
88  vl_msg_api_send_shmem (q, (u8 *)&rmp); \
89 } while(0);
90 
91 /* Macro to finish up custom dump fns */
92 #define FINISH \
93  vec_add1 (s, 0); \
94  vl_print (handle, (char *)s); \
95  vec_free (s); \
96  return handle;
97 
98 #define VALIDATE_SW_IF_INDEX(mp) \
99  do { u32 __sw_if_index = ntohl(mp->sw_if_index); \
100  vnet_main_t *__vnm = vnet_get_main(); \
101  if (pool_is_free_index(__vnm->interface_main.sw_interfaces, \
102  __sw_if_index)) { \
103  rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; \
104  goto bad_sw_if_index; \
105  } \
106 } while(0);
107 
108 #define BAD_SW_IF_INDEX_LABEL \
109 do { \
110 bad_sw_if_index: \
111  ; \
112 } while (0);
113 
114 /**
115  * @brief Create an IPFIX template packet rewrite string
116  * @param frm flow_report_main_t *
117  * @param fr flow_report_t *
118  * @param collector_address ip4_address_t * the IPFIX collector address
119  * @param src_address ip4_address_t * the source address we should use
120  * @param collector_port u16 the collector port we should use, host byte order
121  * @returns u8 * vector containing the indicated IPFIX template packet
122  */
123 u8 *
125  flow_report_t * fr,
126  ip4_address_t * collector_address,
127  ip4_address_t * src_address, u16 collector_port)
128 {
129  ip4_header_t *ip;
130  udp_header_t *udp;
135  ipfix_field_specifier_t *first_field;
136  u8 *rewrite = 0;
138  u32 field_count = 0;
139  flow_report_stream_t *stream;
140 
141  stream = &frm->streams[fr->stream_index];
142 
143  /*
144  * Supported Fields:
145  *
146  * ingressInterface, TLV type 10, u32
147  * egressInterface, TLV type 14, u32
148  * sourceIpv4Address, TLV type 8, u32
149  * destinationIPv4Address, TLV type 12, u32
150  * ipClassOfService, TLV type 5, u8
151  * flowStartNanoseconds, TLV type 156, dateTimeNanoseconds (f64)
152  * Implementation: f64 nanoseconds since VPP started
153  * warning: wireshark doesn't really understand this TLV
154  * dataLinkFrameSize, TLV type 312, u16
155  * warning: wireshark doesn't understand this TLV at all
156  */
157 
158  /* Currently 7 fields */
159  field_count += 7;
160 
161  /* allocate rewrite space */
162  vec_validate_aligned (rewrite,
164  + field_count * sizeof (ipfix_field_specifier_t) - 1,
166 
167  tp = (ip4_ipfix_template_packet_t *) rewrite;
168  ip = (ip4_header_t *) & tp->ip4;
169  udp = (udp_header_t *) (ip + 1);
170  h = (ipfix_message_header_t *) (udp + 1);
171  s = (ipfix_set_header_t *) (h + 1);
172  t = (ipfix_template_header_t *) (s + 1);
173  first_field = f = (ipfix_field_specifier_t *) (t + 1);
174 
175  ip->ip_version_and_header_length = 0x45;
176  ip->ttl = 254;
177  ip->protocol = IP_PROTOCOL_UDP;
178  ip->src_address.as_u32 = src_address->as_u32;
179  ip->dst_address.as_u32 = collector_address->as_u32;
180  udp->src_port = clib_host_to_net_u16 (stream->src_port);
181  udp->dst_port = clib_host_to_net_u16 (collector_port);
182  udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip));
183 
184  /* FIXUP: message header export_time */
185  /* FIXUP: message header sequence_number */
186  h->domain_id = clib_host_to_net_u32 (stream->domain_id);
187 
188  /* Add TLVs to the template */
189  f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , ingressInterface,
190  4);
191  f++;
192  f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , egressInterface,
193  4);
194  f++;
195  f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , sourceIPv4Address,
196  4);
197  f++;
198  f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
199  destinationIPv4Address, 4);
200  f++;
201  f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , ipClassOfService,
202  1);
203  f++;
204  f->e_id_length =
205  ipfix_e_id_length (0 /* enterprise */ , flowStartNanoseconds,
206  8);
207  f++;
208  f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , dataLinkFrameSize,
209  2);
210  f++;
211  /* Extend in the obvious way, right here... */
212 
213  /* Back to the template packet... */
214  ip = (ip4_header_t *) & tp->ip4;
215  udp = (udp_header_t *) (ip + 1);
216 
217  ASSERT (f - first_field);
218  /* Field count in this template */
219  t->id_count = ipfix_id_count (fr->template_id, f - first_field);
220 
221  /* set length in octets */
222  s->set_id_length =
223  ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
224 
225  /* message length in octets */
226  h->version_length = version_length ((u8 *) f - (u8 *) h);
227 
228  ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip);
229  ip->checksum = ip4_header_checksum (ip);
230 
231  return rewrite;
232 }
233 
234 /**
235  * @brief Flush accumulated data
236  * @param frm flow_report_main_t *
237  * @param fr flow_report_t *
238  * @param f vlib_frame_t *
239  *
240  * <em>Notes:</em>
241  * This function must simply return the incoming frame, or no template packets
242  * will be sent.
243  */
244 vlib_frame_t *
246  flow_report_t * fr,
247  vlib_frame_t * f, u32 * to_next, u32 node_index)
248 {
250  return f;
251 }
252 
253 /**
254  * @brief configure / deconfigure the IPFIX flow-per-packet
255  * @param fm flowperpkt_main_t * fm
256  * @param sw_if_index u32 the desired interface
257  * @param is_add int 1 to enable the feature, 0 to disable it
258  * @returns 0 if successful, non-zero otherwise
259  */
260 
262  (flowperpkt_main_t * fm, u32 sw_if_index, int is_add)
263 {
266  int rv;
267 
268  if (!fm->report_created)
269  {
270  memset (a, 0, sizeof (*a));
273  a->is_add = 1;
274  a->domain_id = 1; /*$$$$ config parameter */
275  a->src_port = 4739; /*$$$$ config parameter */
276  fm->report_created = 1;
277 
278  rv = vnet_flow_report_add_del (frm, a);
279  if (rv)
280  {
281  clib_warning ("vnet_flow_report_add_del returned %d", rv);
282  return -1;
283  }
284  }
285 
286  vnet_feature_enable_disable ("ip4-output", "flowperpkt", sw_if_index,
287  is_add, 0, 0);
288 
289  return 0;
290 }
291 
292 /**
293  * @brief API message handler
294  * @param mp vl_api_flowperpkt_tx_interface_add_del_t * mp the api message
295  */
298 {
301  u32 sw_if_index = ntohl (mp->sw_if_index);
302  int rv = 0;
303 
305 
306  rv = flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, mp->is_add);
307 
309 
310  REPLY_MACRO (VL_API_FLOWPERPKT_TX_INTERFACE_ADD_DEL_REPLY);
311 }
312 
313 /**
314  * @brief API message custom-dump function
315  * @param mp vl_api_flowperpkt_tx_interface_add_del_t * mp the api message
316  * @param handle void * print function handle
317  * @returns u8 * output string
318  */
321 {
322  u8 *s;
323 
324  s = format (0, "SCRIPT: flowperpkt_tx_interface_add_del ");
325  s = format (s, "sw_if_index %d is_add %d is_ipv6 %d ",
326  clib_host_to_net_u32 (mp->sw_if_index),
327  (int) mp->is_add, (int) mp->is_ipv6);
328  FINISH;
329 }
330 
331 /* List of message types that this plugin understands */
332 #define foreach_flowperpkt_plugin_api_msg \
333 _(FLOWPERPKT_TX_INTERFACE_ADD_DEL, flowperpkt_tx_interface_add_del)
334 
335 /**
336  * @brief plugin-api required function
337  * @param vm vlib_main_t * vlib main data structure pointer
338  * @param h vlib_plugin_handoff_t * handoff structure
339  * @param from_early_init int notused
340  *
341  * <em>Notes:</em>
342  * This routine exists to convince the vlib plugin framework that
343  * we haven't accidentally copied a random .dll into the plugin directory.
344  *
345  * Also collects global variable pointers passed from the vpp engine
346  */
347 clib_error_t *
349  int from_early_init)
350 {
352  clib_error_t *error = 0;
353 
354  fm->vlib_main = vm;
355  fm->vnet_main = h->vnet_main;
356 
357  return error;
358 }
359 
360 static clib_error_t *
362  unformat_input_t * input,
363  vlib_cli_command_t * cmd)
364 {
366  u32 sw_if_index = ~0;
367  int is_add = 1;
368 
369  int rv;
370 
372  {
373  if (unformat (input, "disable"))
374  is_add = 0;
375  else if (unformat (input, "%U", unformat_vnet_sw_interface,
376  fm->vnet_main, &sw_if_index))
377  ;
378  else
379  break;
380  }
381 
382  if (sw_if_index == ~0)
383  return clib_error_return (0, "Please specify an interface...");
384 
385  rv = flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, is_add);
386  switch (rv)
387  {
388  case 0:
389  break;
390 
391  case VNET_API_ERROR_INVALID_SW_IF_INDEX:
392  return clib_error_return
393  (0, "Invalid interface, only works on physical ports");
394  break;
395 
396  case VNET_API_ERROR_UNIMPLEMENTED:
397  return clib_error_return (0, "ip6 not supported");
398  break;
399 
400  default:
401  return clib_error_return (0, "flowperpkt_enable_disable returned %d",
402  rv);
403  }
404  return 0;
405 }
406 
407 /*?
408  * '<em>flowperpkt feature add-del</em>' commands to enable/disable
409  * per-packet IPFIX flow record generation on an interface
410  *
411  * @cliexpar
412  * @parblock
413  * To enable per-packet IPFIX flow-record generation on an interface:
414  * @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0}
415  *
416  * To disable per-packet IPFIX flow-record generation on an interface:
417  * @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0 disable}
418  * @cliexend
419  * @endparblock
420 ?*/
421 /* *INDENT-OFF* */
422 VLIB_CLI_COMMAND (flowperpkt_enable_disable_command, static) = {
423  .path = "flowperpkt feature add-del",
424  .short_help =
425  "flowperpkt feature add-del <interface-name> [disable]",
427 };
428 /* *INDENT-ON* */
429 
430 /**
431  * @brief Set up the API message handling tables
432  * @param vm vlib_main_t * vlib main data structure pointer
433  * @returns 0 to indicate all is well
434  */
435 static clib_error_t *
437 {
439 #define _(N,n) \
440  vl_msg_api_set_handlers((VL_API_##N + fm->msg_id_base), \
441  #n, \
442  vl_api_##n##_t_handler, \
443  vl_noop_handler, \
444  vl_api_##n##_t_endian, \
445  vl_api_##n##_t_print, \
446  sizeof(vl_api_##n##_t), 1);
448 #undef _
449 
450  return 0;
451 }
452 
453 #define vl_msg_name_crc_list
455 #undef vl_msg_name_crc_list
456 
457 static void
459 {
460 #define _(id,n,crc) \
461  vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + fm->msg_id_base);
462  foreach_vl_msg_name_crc_flowperpkt;
463 #undef _
464 }
465 
466 /**
467  * @brief Set up the API message handling tables
468  * @param vm vlib_main_t * vlib main data structure pointer
469  * @returns 0 to indicate all is well, or a clib_error_t
470  */
471 static clib_error_t *
473 {
476  clib_error_t *error = 0;
477  u32 num_threads;
478  u8 *name;
479 
480  /* Construct the API name */
481  name = format (0, "flowperpkt_%08x%c", api_version, 0);
482 
483  /* Ask for a correctly-sized block of API message decode slots */
485  ((char *) name, VL_MSG_FIRST_AVAILABLE);
486 
487  /* Hook up message handlers */
488  error = flowperpkt_plugin_api_hookup (vm);
489 
490  /* Add our API messages to the global name_crc hash table */
492 
493  vec_free (name);
494 
495  /* Decide how many worker threads we have */
496  num_threads = 1 /* main thread */ + tm->n_eal_threads;
497 
498  /* Allocate per worker thread vectors */
499  vec_validate (fm->buffers_per_worker, num_threads - 1);
500  vec_validate (fm->frames_per_worker, num_threads - 1);
501  vec_validate (fm->next_record_offset_per_worker, num_threads - 1);
502 
503  /* Set up time reference pair */
504  fm->vlib_time_0 = vlib_time_now (vm);
506 
507  return error;
508 }
509 
511 
512 /*
513  * fd.io coding-style-patch-verification: ON
514  *
515  * Local Variables:
516  * eval: (c-set-style "gnu")
517  * End:
518  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:396
static clib_error_t * flowperpkt_tx_interface_add_del_feature_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: flowperpkt.c:361
uword unformat(unformat_input_t *i, char *fmt,...)
Definition: unformat.c:966
a
Definition: bitmap.h:516
ip4_address_t src_address
Definition: ip4_packet.h:163
clib_error_t * vlib_plugin_register(vlib_main_t *vm, vnet_plugin_handoff_t *h, int from_early_init)
plugin-api required function
Definition: flowperpkt.c:348
u64 nanosecond_time_0
Time reference pair.
Definition: flowperpkt.h:49
#define UNFORMAT_END_OF_INPUT
Definition: format.h:143
static u32 ipfix_e_id_length(int e, u16 id, u16 length)
Definition: ipfix_packet.h:72
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:182
u32 stream_index
Definition: flow_report.h:72
vlib_main_t * vlib_main
convenience vlib_main_t pointer
Definition: flowperpkt.h:53
unformat_function_t unformat_vnet_sw_interface
u16 msg_id_base
API message ID base.
Definition: flowperpkt.h:36
#define vec_validate_aligned(V, I, A)
Make sure vector is long enough for given index (no header, specified alignment)
Definition: vec.h:407
void vl_api_flowperpkt_tx_interface_add_del_t_handler(vl_api_flowperpkt_tx_interface_add_del_t *mp)
API message handler.
Definition: flowperpkt.c:297
static clib_error_t * flowperpkt_plugin_api_hookup(vlib_main_t *vm)
Set up the API message handling tables.
Definition: flowperpkt.c:436
u8 * flowperpkt_template_rewrite(flow_report_main_t *frm, flow_report_t *fr, ip4_address_t *collector_address, ip4_address_t *src_address, u16 collector_port)
Create an IPFIX template packet rewrite string.
Definition: flowperpkt.c:124
api_main_t api_main
Definition: api_shared.c:39
flow_report_stream_t * streams
Definition: flow_report.h:91
flow-per-packet plugin header file
VNET_FEATURE_INIT(flow_perpacket, static)
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:111
int report_created
Has the report been created?
Definition: flowperpkt.h:39
Enable / disable per-packet IPFIX recording on an interface.
Definition: flowperpkt.api:15
ip4_address_t dst_address
Definition: ip4_packet.h:163
vnet_flow_rewrite_callback_t * rewrite_callback
Definition: flow_report.h:125
#define BAD_SW_IF_INDEX_LABEL
Definition: flowperpkt.c:108
vlib_frame_t * flowperpkt_data_callback(flow_report_main_t *frm, flow_report_t *fr, vlib_frame_t *f, u32 *to_next, u32 node_index)
Flush accumulated data.
Definition: flowperpkt.c:245
#define clib_warning(format, args...)
Definition: error.h:59
vlib_buffer_t ** buffers_per_worker
ipfix buffers under construction, per-worker thread
Definition: flowperpkt.h:42
flow_report_main_t flow_report_main
Definition: flow_report.c:21
vnet_main_t * vnet_main
convenience vnet_main_t pointer
Definition: flowperpkt.h:55
static u32 version_length(u16 length)
Definition: ipfix_packet.h:31
vlib_thread_main_t vlib_thread_main
Definition: threads.c:55
#define foreach_flowperpkt_plugin_api_msg
Definition: flowperpkt.c:332
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:300
static u32 ipfix_id_count(u16 id, u16 count)
Definition: ipfix_packet.h:175
static u64 unix_time_now_nsec(void)
Definition: time.h:238
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:154
#define VALIDATE_SW_IF_INDEX(mp)
Definition: flowperpkt.c:98
#define ASSERT(truth)
static int flowperpkt_tx_interface_add_del_feature(flowperpkt_main_t *fm, u32 sw_if_index, int is_add)
configure / deconfigure the IPFIX flow-per-packet
Definition: flowperpkt.c:262
unsigned int u32
Definition: types.h:88
#define REPLY_MACRO(t)
Definition: flowperpkt.c:76
flowperpkt_main_t flowperpkt_main
Definition: flowperpkt.c:53
u16 * next_record_offset_per_worker
next record offset, per worker thread
Definition: flowperpkt.h:46
u16 template_id
Definition: flow_report.h:71
#define VNET_FEATURES(...)
Definition: feature.h:363
static u32 ipfix_set_id_length(u16 set_id, u16 length)
Definition: ipfix_packet.h:114
unsigned short u16
Definition: types.h:57
void flowperpkt_flush_callback(void)
Definition: node.c:295
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
unsigned char u8
Definition: types.h:56
static clib_error_t * flowperpkt_init(vlib_main_t *vm)
Set up the API message handling tables.
Definition: flowperpkt.c:472
static void * vl_api_flowperpkt_tx_interface_add_del_t_print(vl_api_flowperpkt_tx_interface_add_del_t *mp, void *handle)
API message custom-dump function.
Definition: flowperpkt.c:320
vnet_flow_data_callback_t * flow_data_callback
Definition: flow_report.h:124
vnet_main_t * vnet_main
Definition: plugin.h:26
#define FINISH
Definition: flowperpkt.c:92
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:169
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:418
static void setup_message_id_table(flowperpkt_main_t *fm, api_main_t *am)
Definition: flowperpkt.c:458
u16 vl_msg_api_get_msg_ids(char *name, int n)
Definition: api_shared.c:1309
#define clib_error_return(e, args...)
Definition: error.h:111
u8 ip_version_and_header_length
Definition: ip4_packet.h:131
struct _unformat_input_t unformat_input_t
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:67
Reply to enable/disable per-packet IPFIX recording messages.
Definition: flowperpkt.api:35
static u16 ip4_header_checksum(ip4_header_t *i)
Definition: ip4_packet.h:238
int vnet_feature_enable_disable(const char *arc_name, const char *node_name, u32 sw_if_index, int enable_disable, void *feature_config, u32 n_feature_config_bytes)
Definition: feature.c:238
int vnet_flow_report_add_del(flow_report_main_t *frm, vnet_flow_report_add_del_args_t *a)
Definition: flow_report.c:238
vlib_frame_t ** frames_per_worker
frames containing ipfix buffers, per-worker thread
Definition: flowperpkt.h:44