FD.io VPP  v16.12-rc0-308-g931be3a
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_IP4_TX_FEATURE_INIT (flow_perpacket, static) = {
63  .node_name = "flowperpkt",
64  .runs_before = (char *[]){"interface-output", 0},
65  .feature_index = &flowperpkt_main.ip4_tx_feature_index,
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  * egressInterface, TLV type 14, u32
147  * ipClassOfService, TLV type 5, u8
148  * flowStartNanoseconds, TLV type 156, dateTimeNanoseconds (f64)
149  * Implementation: f64 nanoseconds since VPP started
150  * warning: wireshark doesn't really understand this TLV
151  * dataLinkFrameSize, TLV type 312, u16
152  * warning: wireshark doesn't understand this TLV at all
153  */
154 
155  /* Currently 4 fields */
156  field_count += 4;
157 
158  /* allocate rewrite space */
159  vec_validate_aligned (rewrite,
161  + field_count * sizeof (ipfix_field_specifier_t) - 1,
163 
164  tp = (ip4_ipfix_template_packet_t *) rewrite;
165  ip = (ip4_header_t *) & tp->ip4;
166  udp = (udp_header_t *) (ip + 1);
167  h = (ipfix_message_header_t *) (udp + 1);
168  s = (ipfix_set_header_t *) (h + 1);
169  t = (ipfix_template_header_t *) (s + 1);
170  first_field = f = (ipfix_field_specifier_t *) (t + 1);
171 
172  ip->ip_version_and_header_length = 0x45;
173  ip->ttl = 254;
174  ip->protocol = IP_PROTOCOL_UDP;
175  ip->src_address.as_u32 = src_address->as_u32;
176  ip->dst_address.as_u32 = collector_address->as_u32;
177  udp->src_port = clib_host_to_net_u16 (stream->src_port);
178  udp->dst_port = clib_host_to_net_u16 (collector_port);
179  udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip));
180 
181  /* FIXUP: message header export_time */
182  /* FIXUP: message header sequence_number */
183  h->domain_id = clib_host_to_net_u32 (stream->domain_id);
184 
185  /* Add TLVs to the template */
186  f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , egressInterface,
187  4);
188  f++;
189  f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , ipClassOfService,
190  1);
191  f++;
192  f->e_id_length =
193  ipfix_e_id_length (0 /* enterprise */ , flowStartNanoseconds,
194  8);
195  f++;
196  f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , dataLinkFrameSize,
197  2);
198  f++;
199  /* Extend in the obvious way, right here... */
200 
201  /* Back to the template packet... */
202  ip = (ip4_header_t *) & tp->ip4;
203  udp = (udp_header_t *) (ip + 1);
204 
205  ASSERT (f - first_field);
206  /* Field count in this template */
207  t->id_count = ipfix_id_count (fr->template_id, f - first_field);
208 
209  /* set length in octets */
210  s->set_id_length =
211  ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
212 
213  /* message length in octets */
214  h->version_length = version_length ((u8 *) f - (u8 *) h);
215 
216  ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip);
217  ip->checksum = ip4_header_checksum (ip);
218 
219  return rewrite;
220 }
221 
222 /**
223  * @brief Flush accumulated data
224  * @param frm flow_report_main_t *
225  * @param fr flow_report_t *
226  * @param f vlib_frame_t *
227  *
228  * <em>Notes:</em>
229  * This function must simply return the incoming frame, or no template packets
230  * will be sent.
231  */
232 vlib_frame_t *
234  flow_report_t * fr,
235  vlib_frame_t * f, u32 * to_next, u32 node_index)
236 {
238  return f;
239 }
240 
241 /**
242  * @brief configure / deconfigure the IPFIX flow-per-packet
243  * @param fm flowperpkt_main_t * fm
244  * @param sw_if_index u32 the desired interface
245  * @param is_add int 1 to enable the feature, 0 to disable it
246  * @returns 0 if successful, non-zero otherwise
247  */
248 
250  (flowperpkt_main_t * fm, u32 sw_if_index, int is_add)
251 {
252  u32 ci;
253  ip4_main_t *im = &ip4_main;
254  ip_lookup_main_t *lm = &im->lookup_main;
256  u32 feature_index;
259  int rv;
260 
261  if (!fm->report_created)
262  {
263  memset (a, 0, sizeof (*a));
266  a->is_add = 1;
267  a->domain_id = 1; /*$$$$ config parameter */
268  a->src_port = 4739; /*$$$$ config parameter */
269  fm->report_created = 1;
270 
271  rv = vnet_flow_report_add_del (frm, a);
272  if (rv)
273  {
274  clib_warning ("vnet_flow_report_add_del returned %d", rv);
275  return -1;
276  }
277  }
278 
279  feature_index = fm->ip4_tx_feature_index;
280 
281  ci = cm->config_index_by_sw_if_index[sw_if_index];
282  ci = (is_add
285  (fm->vlib_main, &cm->config_main,
286  ci, feature_index, 0 /* config struct */ ,
287  0 /* sizeof config struct */ );
288  cm->config_index_by_sw_if_index[sw_if_index] = ci;
289 
290  vnet_config_update_tx_feature_count (lm, cm, sw_if_index, is_add);
291  return 0;
292 }
293 
294 /**
295  * @brief API message handler
296  * @param mp vl_api_flowperpkt_tx_interface_add_del_t * mp the api message
297  */
300 {
303  u32 sw_if_index = ntohl (mp->sw_if_index);
304  int rv = 0;
305 
307 
308  rv = flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, mp->is_add);
309 
311 
312  REPLY_MACRO (VL_API_FLOWPERPKT_TX_INTERFACE_ADD_DEL_REPLY);
313 }
314 
315 /**
316  * @brief API message custom-dump function
317  * @param mp vl_api_flowperpkt_tx_interface_add_del_t * mp the api message
318  * @param handle void * print function handle
319  * @returns u8 * output string
320  */
323 {
324  u8 *s;
325 
326  s = format (0, "SCRIPT: flowperpkt_tx_interface_add_del ");
327  s = format (s, "sw_if_index %d is_add %d is_ipv6 %d ",
328  clib_host_to_net_u32 (mp->sw_if_index),
329  (int) mp->is_add, (int) mp->is_ipv6);
330  FINISH;
331 }
332 
333 /* List of message types that this plugin understands */
334 #define foreach_flowperpkt_plugin_api_msg \
335 _(FLOWPERPKT_TX_INTERFACE_ADD_DEL, flowperpkt_tx_interface_add_del)
336 
337 /**
338  * @brief plugin-api required function
339  * @param vm vlib_main_t * vlib main data structure pointer
340  * @param h vlib_plugin_handoff_t * handoff structure
341  * @param from_early_init int notused
342  *
343  * <em>Notes:</em>
344  * This routine exists to convince the vlib plugin framework that
345  * we haven't accidentally copied a random .dll into the plugin directory.
346  *
347  * Also collects global variable pointers passed from the vpp engine
348  */
349 clib_error_t *
351  int from_early_init)
352 {
354  clib_error_t *error = 0;
355 
356  fm->vlib_main = vm;
357  fm->vnet_main = h->vnet_main;
358 
359  return error;
360 }
361 
362 static clib_error_t *
364  unformat_input_t * input,
365  vlib_cli_command_t * cmd)
366 {
368  u32 sw_if_index = ~0;
369  int is_add = 1;
370 
371  int rv;
372 
374  {
375  if (unformat (input, "disable"))
376  is_add = 0;
377  else if (unformat (input, "%U", unformat_vnet_sw_interface,
378  fm->vnet_main, &sw_if_index))
379  ;
380  else
381  break;
382  }
383 
384  if (sw_if_index == ~0)
385  return clib_error_return (0, "Please specify an interface...");
386 
387  rv = flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, is_add);
388  switch (rv)
389  {
390  case 0:
391  break;
392 
393  case VNET_API_ERROR_INVALID_SW_IF_INDEX:
394  return clib_error_return
395  (0, "Invalid interface, only works on physical ports");
396  break;
397 
398  case VNET_API_ERROR_UNIMPLEMENTED:
399  return clib_error_return (0, "ip6 not supported");
400  break;
401 
402  default:
403  return clib_error_return (0, "flowperpkt_enable_disable returned %d",
404  rv);
405  }
406  return 0;
407 }
408 
409 /*?
410  * '<em>flowperpkt feature add-del</em>' commands to enable/disable
411  * per-packet IPFIX flow record generation on an interface
412  *
413  * @cliexpar
414  * @parblock
415  * To enable per-packet IPFIX flow-record generation on an interface:
416  * @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0}
417  *
418  * To disable per-packet IPFIX flow-record generation on an interface:
419  * @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0 disable}
420  * @cliexend
421  * @endparblock
422 ?*/
423 /* *INDENT-OFF* */
424 VLIB_CLI_COMMAND (flowperpkt_enable_disable_command, static) = {
425  .path = "flowperpkt feature add-del",
426  .short_help =
427  "flowperpkt feature add-del <interface-name> [disable]",
429 };
430 /* *INDENT-ON* */
431 
432 /**
433  * @brief Set up the API message handling tables
434  * @param vm vlib_main_t * vlib main data structure pointer
435  * @returns 0 to indicate all is well
436  */
437 static clib_error_t *
439 {
441 #define _(N,n) \
442  vl_msg_api_set_handlers((VL_API_##N + fm->msg_id_base), \
443  #n, \
444  vl_api_##n##_t_handler, \
445  vl_noop_handler, \
446  vl_api_##n##_t_endian, \
447  vl_api_##n##_t_print, \
448  sizeof(vl_api_##n##_t), 1);
450 #undef _
451 
452  return 0;
453 }
454 
455 /**
456  * @brief Set up the API message handling tables
457  * @param vm vlib_main_t * vlib main data structure pointer
458  * @returns 0 to indicate all is well, or a clib_error_t
459  */
460 static clib_error_t *
462 {
465  clib_error_t *error = 0;
466  u32 num_threads;
467  u8 *name;
468 
469  /* Construct the API name */
470  name = format (0, "flowperpkt_%08x%c", api_version, 0);
471 
472  /* Ask for a correctly-sized block of API message decode slots */
474  ((char *) name, VL_MSG_FIRST_AVAILABLE);
475 
476  /* Hook up message handlers */
477  error = flowperpkt_plugin_api_hookup (vm);
478 
479  vec_free (name);
480 
481  /* Decide how many worker threads we have */
482  num_threads = 1 /* main thread */ + tm->n_eal_threads;
483 
484  /* Allocate per worker thread vectors */
485  vec_validate (fm->buffers_per_worker, num_threads - 1);
486  vec_validate (fm->frames_per_worker, num_threads - 1);
487  vec_validate (fm->next_record_offset_per_worker, num_threads - 1);
488 
489  /* Set up time reference pair */
490  fm->vlib_time_0 = vlib_time_now (vm);
492 
493  return error;
494 }
495 
497 
498 /*
499  * fd.io coding-style-patch-verification: ON
500  *
501  * Local Variables:
502  * eval: (c-set-style "gnu")
503  * End:
504  */
vnet_config_main_t config_main
Definition: feature.h:55
#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:363
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:138
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:350
u64 nanosecond_time_0
Time reference pair.
Definition: flowperpkt.h:52
#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
u32 vnet_config_del_feature(vlib_main_t *vm, vnet_config_main_t *cm, u32 config_string_heap_index, u32 feature_index, void *feature_config, u32 n_feature_config_bytes)
Definition: config.c:300
void vnet_config_update_tx_feature_count(ip_lookup_main_t *lm, vnet_feature_config_main_t *tx_cm, u32 sw_if_index, int is_add)
Definition: ip_init.c:148
ip_lookup_main_t lookup_main
Definition: ip4.h:96
vlib_main_t * vlib_main
convenience vlib_main_t pointer
Definition: flowperpkt.h:56
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:299
static clib_error_t * flowperpkt_plugin_api_hookup(vlib_main_t *vm)
Set up the API message handling tables.
Definition: flowperpkt.c:438
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
flow_report_stream_t * streams
Definition: flow_report.h:91
flow-per-packet plugin header file
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:111
int report_created
Has the report been created?
Definition: flowperpkt.h:42
Enable / disable per-packet IPFIX recording on an interface.
Definition: flowperpkt.api:15
ip4_address_t dst_address
Definition: ip4_packet.h:138
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:233
#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:45
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:58
static u32 version_length(u16 length)
Definition: ipfix_packet.h:31
vlib_thread_main_t vlib_thread_main
Definition: threads.c:54
u32 ip4_tx_feature_index
ip4 feature index
Definition: flowperpkt.h:39
VNET_IP4_TX_FEATURE_INIT(flow_perpacket, static)
vnet_feature_config_main_t feature_config_mains[VNET_N_IP_FEAT]
rx unicast, multicast, tx interface/feature configuration.
Definition: lookup.h:360
#define foreach_flowperpkt_plugin_api_msg
Definition: flowperpkt.c:334
#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:250
unsigned int u32
Definition: types.h:88
#define REPLY_MACRO(t)
Definition: flowperpkt.c:76
flowperpkt_main_t flowperpkt_main
Definition: flowperpkt.c:53
IPv4 main type.
Definition: ip4.h:95
u16 * next_record_offset_per_worker
next record offset, per worker thread
Definition: flowperpkt.h:49
u16 template_id
Definition: flow_report.h:71
u32 vnet_config_add_feature(vlib_main_t *vm, vnet_config_main_t *cm, u32 config_string_heap_index, u32 feature_index, void *feature_config, u32 n_feature_config_bytes)
Definition: config.c:239
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:268
#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:461
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:322
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
ip4_main_t ip4_main
Global ip4 main structure.
Definition: ip4_forward.c:1060
u16 vl_msg_api_get_msg_ids(char *name, int n)
Definition: api_shared.c:1269
#define clib_error_return(e, args...)
Definition: error.h:111
u8 ip_version_and_header_length
Definition: ip4_packet.h:108
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:194
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:47