FD.io VPP  v17.01.1-3-gc6833f8
Vector Packet Processing
flow_report.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16  * flow_report.c
17  */
18 #include <vnet/flow/flow_report.h>
19 #include <vnet/api_errno.h>
20 
22 
24 {
26  return index < vec_len(frm->streams) &&
27  frm->streams[index].domain_id != ~0;
28 }
29 
31 {
33  u32 i;
34  for (i = 0; i < vec_len(frm->streams); i++)
35  if (!stream_index_valid(i))
36  return &frm->streams[i];
37  u32 index = vec_len(frm->streams);
38  vec_validate(frm->streams, index);
39  return &frm->streams[index];
40 }
41 
43 {
45  ASSERT (index < vec_len(frm->streams));
46  ASSERT (frm->streams[index].domain_id != ~0);
47  frm->streams[index].domain_id = ~0;
48 }
49 
50 static i32 find_stream (u32 domain_id, u16 src_port)
51 {
53  flow_report_stream_t * stream;
54  u32 i;
55  for (i = 0; i < vec_len(frm->streams); i++)
56  if (stream_index_valid(i)) {
57  stream = &frm->streams[i];
58  if (domain_id == stream->domain_id) {
59  if (src_port != stream->src_port)
60  return -2;
61  return i;
62  } else if (src_port == stream->src_port) {
63  return -2;
64  }
65  }
66  return -1;
67 }
68 
70  flow_report_t *fr,
71  u32 * buffer_indexp)
72 {
73  u32 bi0;
74  vlib_buffer_t * b0;
77  ip4_header_t * ip;
78  udp_header_t * udp;
79  vlib_main_t * vm = frm->vlib_main;
80  flow_report_stream_t * stream;
82 
83  ASSERT (buffer_indexp);
84 
85  if (fr->update_rewrite || fr->rewrite == 0)
86  {
87  if (frm->ipfix_collector.as_u32 == 0
88  || frm->src_address.as_u32 == 0)
89  {
90  clib_warning ("no collector: disabling flow collector process");
92  VLIB_NODE_STATE_DISABLED);
93  return -1;
94  }
95  vec_free (fr->rewrite);
96  fr->update_rewrite = 1;
97  }
98 
99  if (fr->update_rewrite)
100  {
101  fr->rewrite = fr->rewrite_callback (frm, fr,
102  &frm->ipfix_collector,
103  &frm->src_address,
104  frm->collector_port);
105  fr->update_rewrite = 0;
106  }
107 
108  if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
109  return -1;
110 
111  b0 = vlib_get_buffer (vm, bi0);
112 
113  /* Initialize the buffer */
117 
119 
120  clib_memcpy (b0->data, fr->rewrite, vec_len (fr->rewrite));
121  b0->current_data = 0;
122  b0->current_length = vec_len (fr->rewrite);
124  vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
125  vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index;
126 
127  tp = vlib_buffer_get_current (b0);
128  ip = (ip4_header_t *) &tp->ip4;
129  udp = (udp_header_t *) (ip+1);
130  h = (ipfix_message_header_t *)(udp+1);
131 
132  /* FIXUP: message header export_time */
133  h->export_time = (u32)
134  (((f64)frm->unix_time_0) +
135  (vlib_time_now(frm->vlib_main) - frm->vlib_time_0));
136  h->export_time = clib_host_to_net_u32(h->export_time);
137 
138  stream = &frm->streams[fr->stream_index];
139 
140  /* FIXUP: message header sequence_number. Templates do not increase it */
141  h->sequence_number = clib_host_to_net_u32(stream->sequence_number);
142 
143  /* FIXUP: udp length */
144  udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
145 
146  if (frm->udp_checksum)
147  {
148  /* RFC 7011 section 10.3.2. */
149  udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
150  if (udp->checksum == 0)
151  udp->checksum = 0xffff;
152  }
153 
154  *buffer_indexp = bi0;
155 
157 
158  return 0;
159 }
160 
161 static uword
163  vlib_node_runtime_t * rt,
164  vlib_frame_t * f)
165 {
167  flow_report_t * fr;
168  u32 ip4_lookup_node_index;
170  vlib_frame_t * nf = 0;
171  u32 template_bi;
172  u32 * to_next;
173  int send_template;
174  f64 now;
175  int rv;
176  uword event_type;
177  uword *event_data = 0;
178 
179  /* Wait for Godot... */
181  event_type = vlib_process_get_events (vm, &event_data);
182  if (event_type != 1)
183  clib_warning ("bogus kickoff event received, %d", event_type);
184  vec_reset_length (event_data);
185 
186  /* Enqueue pkts to ip4-lookup */
187  ip4_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip4-lookup");
188  ip4_lookup_node_index = ip4_lookup_node->index;
189 
190  while (1)
191  {
192  vlib_process_suspend (vm, 5.0);
193 
194  vec_foreach (fr, frm->reports)
195  {
196  now = vlib_time_now (vm);
197 
198  /* Need to send a template packet? */
199  send_template =
200  now > (fr->last_template_sent + frm->template_interval);
201  send_template += fr->last_template_sent == 0;
202  template_bi = ~0;
203  rv = 0;
204 
205  if (send_template)
206  rv = send_template_packet (frm, fr, &template_bi);
207 
208  if (rv < 0)
209  continue;
210 
211  nf = vlib_get_frame_to_node (vm, ip4_lookup_node_index);
212  nf->n_vectors = 0;
213  to_next = vlib_frame_vector_args (nf);
214 
215  if (template_bi != ~0)
216  {
217  to_next[0] = template_bi;
218  to_next++;
219  nf->n_vectors++;
220  }
221 
222  nf = fr->flow_data_callback (frm, fr,
223  nf, to_next, ip4_lookup_node_index);
224  if (nf)
225  vlib_put_frame_to_node (vm, ip4_lookup_node_index, nf);
226  }
227  }
228 
229  return 0; /* not so much */
230 }
231 
233  .function = flow_report_process,
234  .type = VLIB_NODE_TYPE_PROCESS,
235  .name = "flow-report-process",
236 };
237 
240 {
241  int i;
242  int found_index = ~0;
243  flow_report_t *fr;
244  flow_report_stream_t * stream;
245  u32 si;
246 
247  si = find_stream(a->domain_id, a->src_port);
248  if (si == -2)
249  return VNET_API_ERROR_INVALID_VALUE;
250  if (si == -1 && a->is_add == 0)
251  return VNET_API_ERROR_NO_SUCH_ENTRY;
252 
253  for (i = 0; i < vec_len(frm->reports); i++)
254  {
255  fr = vec_elt_at_index (frm->reports, i);
256  if (fr->opaque.as_uword == a->opaque.as_uword
259  {
260  found_index = i;
261  break;
262  }
263  }
264 
265  if (a->is_add == 0)
266  {
267  if (found_index != ~0)
268  {
269  vec_delete (frm->reports, 1, found_index);
270  stream = &frm->streams[si];
271  stream->n_reports--;
272  if (stream->n_reports == 0)
273  delete_stream(si);
274  return 0;
275  }
276  return VNET_API_ERROR_NO_SUCH_ENTRY;
277  }
278 
279  if (found_index != ~0)
280  return VNET_API_ERROR_VALUE_EXIST;
281 
282  if (si == -1)
283  {
284  stream = add_stream();
285  stream->domain_id = a->domain_id;
286  stream->src_port = a->src_port;
287  stream->sequence_number = 0;
288  stream->n_reports = 0;
289  si = stream - frm->streams;
290  }
291  else
292  stream = &frm->streams[si];
293 
294  stream->n_reports++;
295 
296  vec_add2 (frm->reports, fr, 1);
297 
298  fr->stream_index = si;
299  fr->template_id = 256 + stream->next_template_no;
300  stream->next_template_no = (stream->next_template_no + 1) % (65536 - 256);
301  fr->update_rewrite = 1;
302  fr->opaque = a->opaque;
305 
306  return 0;
307 }
308 
310 {
311  switch (error)
312  {
313  case 0:
314  return 0;
315  case VNET_API_ERROR_NO_SUCH_ENTRY:
316  return clib_error_return (0, "Flow report not found");
317  case VNET_API_ERROR_VALUE_EXIST:
318  return clib_error_return (0, "Flow report already exists");
319  case VNET_API_ERROR_INVALID_VALUE:
320  return clib_error_return (0, "Expecting either still unused values "
321  "for both domain_id and src_port "
322  "or already used values for both fields");
323  default:
324  return clib_error_return (0, "vnet_flow_report_add_del returned %d",
325  error);
326  }
327 }
328 
330 {
331  flow_report_t *fr;
332  u32 i;
333 
334  for (i = 0; i < vec_len(frm->streams); i++)
335  if (stream_index_valid(i))
336  frm->streams[i].sequence_number = 0;
337 
338  vec_foreach (fr, frm->reports)
339  {
340  fr->update_rewrite = 1;
341  fr->last_template_sent = 0;
342  }
343 }
344 
345 void vnet_stream_reset (flow_report_main_t * frm, u32 stream_index)
346 {
347  flow_report_t *fr;
348 
349  frm->streams[stream_index].sequence_number = 0;
350 
351  vec_foreach (fr, frm->reports)
352  if (frm->reports->stream_index == stream_index) {
353  fr->update_rewrite = 1;
354  fr->last_template_sent = 0;
355  }
356 }
357 
359  u32 old_domain_id, u16 old_src_port,
360  u32 new_domain_id, u16 new_src_port)
361 {
362  i32 stream_index = find_stream (old_domain_id, old_src_port);
363  if (stream_index < 0)
364  return 1;
365  flow_report_stream_t * stream = &frm->streams[stream_index];
366  stream->domain_id = new_domain_id;
367  stream->src_port = new_src_port;
368  if (old_domain_id != new_domain_id || old_src_port != new_src_port)
369  vnet_stream_reset (frm, stream_index);
370  return 0;
371 }
372 
373 static clib_error_t *
375  unformat_input_t * input,
376  vlib_cli_command_t * cmd)
377 {
379  ip4_address_t collector, src;
380  u16 collector_port = UDP_DST_PORT_ipfix;
381  u32 fib_id;
382  u32 fib_index = ~0;
383 
384  collector.as_u32 = 0;
385  src.as_u32 = 0;
386  u32 path_mtu = 512; // RFC 7011 section 10.3.3.
387  u32 template_interval = 20;
388  u8 udp_checksum = 0;
389 
390  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
391  if (unformat (input, "collector %U", unformat_ip4_address, &collector))
392  ;
393  else if (unformat (input, "port %u", &collector_port))
394  ;
395  else if (unformat (input, "src %U", unformat_ip4_address, &src))
396  ;
397  else if (unformat (input, "fib-id %u", &fib_id))
398  {
399  ip4_main_t * im = &ip4_main;
400  uword * p = hash_get (im->fib_index_by_table_id, fib_id);
401  if (! p)
402  return clib_error_return (0, "fib ID %d doesn't exist\n",
403  fib_id);
404  fib_index = p[0];
405  }
406  else if (unformat (input, "path-mtu %u", &path_mtu))
407  ;
408  else if (unformat (input, "template-interval %u", &template_interval))
409  ;
410  else if (unformat (input, "udp-checksum"))
411  udp_checksum = 1;
412  else
413  break;
414  }
415 
416  if (collector.as_u32 == 0)
417  return clib_error_return (0, "collector address required");
418 
419  if (src.as_u32 == 0)
420  return clib_error_return (0, "src address required");
421 
422  if (path_mtu > 1450 /* vpp does not support fragmentation */)
423  return clib_error_return (0, "too big path-mtu value, maximum is 1450");
424 
425  if (path_mtu < 68)
426  return clib_error_return (0, "too small path-mtu value, minimum is 68");
427 
428  /* Reset report streams if we are reconfiguring IP addresses */
429  if (frm->ipfix_collector.as_u32 != collector.as_u32 ||
430  frm->src_address.as_u32 != src.as_u32 ||
431  frm->collector_port != collector_port)
433 
434  frm->ipfix_collector.as_u32 = collector.as_u32;
435  frm->collector_port = collector_port;
436  frm->src_address.as_u32 = src.as_u32;
437  frm->fib_index = fib_index;
438  frm->path_mtu = path_mtu;
439  frm->template_interval = template_interval;
440  frm->udp_checksum = udp_checksum;
441 
442  vlib_cli_output (vm, "Collector %U, src address %U, "
443  "fib index %d, path MTU %u, "
444  "template resend interval %us, "
445  "udp checksum %s",
448  fib_index, path_mtu, template_interval,
449  udp_checksum ? "enabled" : "disabled");
450 
451  /* Turn on the flow reporting process */
453  1, 0);
454  return 0;
455 }
456 
457 VLIB_CLI_COMMAND (set_ipfix_exporter_command, static) = {
458  .path = "set ipfix exporter",
459  .short_help = "set ipfix exporter "
460  "collector <ip4-address> [port <port>] "
461  "src <ip4-address> [fib-id <fib-id>] "
462  "[path-mtu <path-mtu>] "
463  "[template-interval <template-interval>]",
464  "[udp-checksum]",
465  .function = set_ipfix_exporter_command_fn,
466 };
467 
468 static clib_error_t *
470 {
472 
473  frm->vlib_main = vm;
474  frm->vnet_main = vnet_get_main();
475  frm->unix_time_0 = time(0);
476  frm->vlib_time_0 = vlib_time_now(frm->vlib_main);
477  frm->fib_index = ~0;
478 
479  return 0;
480 }
481 
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:396
clib_error_t * flow_report_add_del_error_to_clib_error(int error)
Definition: flow_report.c:309
sll srl srl sll sra u16x4 i
Definition: vector_sse2.h:343
uword unformat(unformat_input_t *i, char *fmt,...)
Definition: unformat.c:966
static f64 vlib_process_wait_for_event_or_clock(vlib_main_t *vm, f64 dt)
Suspend a cooperative multi-tasking thread Waits for an event, or for the indicated number of seconds...
Definition: node_funcs.h:684
vlib_node_registration_t flow_report_process_node
(constructor) VLIB_REGISTER_NODE (flow_report_process_node)
Definition: flow_report.c:232
a
Definition: bitmap.h:516
u16 udp_checksum(udp_header_t *uh, u32 udp_len, void *ih, u8 version)
Definition: packets.c:114
uword as_uword
Definition: flow_report.h:57
#define VLIB_BUFFER_TRACE_TRAJECTORY_INIT(b)
Definition: buffer.h:406
#define UNFORMAT_END_OF_INPUT
Definition: format.h:143
u32 index
Definition: node.h:237
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:182
u32 stream_index
Definition: flow_report.h:72
opaque_t opaque
Definition: flow_report.h:80
#define vec_add2(V, P, N)
Add N elements to end of vector V, return pointer to new elements in P.
Definition: vec.h:521
ip4_address_t src_address
Definition: flow_report.h:96
format_function_t format_ip4_address
Definition: format.h:79
#define vec_reset_length(v)
Reset vector length to zero NULL-pointer tolerant.
ip4_address_t ipfix_collector
Definition: flow_report.h:94
vlib_node_registration_t ip4_lookup_node
(constructor) VLIB_REGISTER_NODE (ip4_lookup_node)
Definition: ip4_forward.c:511
void vnet_stream_reset(flow_report_main_t *frm, u32 stream_index)
Definition: flow_report.c:345
flow_report_stream_t * streams
Definition: flow_report.h:91
static uword vlib_process_suspend(vlib_main_t *vm, f64 dt)
Suspend a vlib cooperative multi-tasking thread for a period of time.
Definition: node_funcs.h:432
static void vlib_buffer_init_for_free_list(vlib_buffer_t *_dst, vlib_buffer_free_list_t *fl)
Definition: buffer_funcs.h:613
vnet_main_t * vnet_get_main(void)
Definition: misc.c:46
i16 current_data
signed offset in data[], pre_data[] that we are currently processing.
Definition: buffer.h:78
#define static_always_inline
Definition: clib.h:85
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:111
static uword vlib_process_get_events(vlib_main_t *vm, uword **data_vector)
Return the first event type which has occurred and a vector of per-event data of that type...
Definition: node_funcs.h:527
static void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:194
vnet_flow_rewrite_callback_t * rewrite_callback
Definition: flow_report.h:125
int i32
Definition: types.h:81
#define vec_elt_at_index(v, i)
Get vector value at index i checking that i is in bounds.
#define clib_warning(format, args...)
Definition: error.h:59
static uword flow_report_process(vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f)
Definition: flow_report.c:162
flow_report_t * reports
Definition: flow_report.h:90
unformat_function_t unformat_ip4_address
Definition: format.h:76
flow_report_main_t flow_report_main
Definition: flow_report.c:21
#define hash_get(h, key)
Definition: hash.h:248
uword * fib_index_by_table_id
Hash table mapping table id to fib index.
Definition: ip4.h:112
#define VLIB_BUFFER_DEFAULT_FREE_LIST_BYTES
Definition: buffer.h:311
u16 current_length
Nbytes between current data and the end of this buffer.
Definition: buffer.h:82
static void vlib_process_signal_event(vlib_main_t *vm, uword node_index, uword type_opaque, uword data)
Definition: node_funcs.h:931
u8 * rewrite
Definition: flow_report.h:70
void vlib_put_frame_to_node(vlib_main_t *vm, u32 to_node_index, vlib_frame_t *f)
Definition: main.c:196
vnet_main_t * vnet_main
Definition: flow_report.h:114
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:576
int send_template_packet(flow_report_main_t *frm, flow_report_t *fr, u32 *buffer_indexp)
Definition: flow_report.c:69
static_always_inline flow_report_stream_t * add_stream(void)
Definition: flow_report.c:30
u16 n_vectors
Definition: node.h:344
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:300
#define clib_memcpy(a, b, c)
Definition: string.h:69
#define VLIB_BUFFER_TOTAL_LENGTH_VALID
Definition: buffer.h:99
static clib_error_t * set_ipfix_exporter_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: flow_report.c:374
#define VLIB_BUFFER_FLOW_REPORT
Definition: buffer.h:102
static_always_inline u8 stream_index_valid(u32 index)
Definition: flow_report.c:23
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:154
#define VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX
Definition: buffer.h:310
#define ASSERT(truth)
unsigned int u32
Definition: types.h:88
vlib_main_t * vlib_main
Definition: flow_report.h:113
#define vnet_buffer(b)
Definition: buffer.h:361
u16 ip4_tcp_udp_compute_checksum(vlib_main_t *vm, vlib_buffer_t *p0, ip4_header_t *ip0)
Definition: ip4_forward.c:1368
#define vec_delete(V, N, M)
Delete N elements starting at element M.
Definition: vec.h:745
IPv4 main type.
Definition: ip4.h:95
static_always_inline void delete_stream(u32 index)
Definition: flow_report.c:42
void vnet_flow_reports_reset(flow_report_main_t *frm)
Definition: flow_report.c:329
u16 template_id
Definition: flow_report.h:71
static void vlib_node_set_state(vlib_main_t *vm, u32 node_index, vlib_node_state_t new_state)
Set node dispatch state.
Definition: node_funcs.h:146
u32 vlib_buffer_alloc(vlib_main_t *vm, u32 *buffers, u32 n_buffers)
Allocate buffers into supplied array.
vnet_flow_data_callback_t * flow_data_callback
Definition: flow_report.h:86
u64 uword
Definition: types.h:112
vlib_node_t * vlib_get_node_by_name(vlib_main_t *vm, u8 *name)
Definition: node.c:45
Definition: defs.h:47
unsigned short u16
Definition: types.h:57
int update_rewrite
Definition: flow_report.h:74
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
double f64
Definition: types.h:142
unsigned char u8
Definition: types.h:56
f64 last_template_sent
Definition: flow_report.h:73
int vnet_stream_change(flow_report_main_t *frm, u32 old_domain_id, u16 old_src_port, u32 new_domain_id, u16 new_src_port)
Definition: flow_report.c:358
static void * vlib_frame_vector_args(vlib_frame_t *f)
Get pointer to frame vector data.
Definition: node_funcs.h:253
vnet_flow_data_callback_t * flow_data_callback
Definition: flow_report.h:124
static i32 find_stream(u32 domain_id, u16 src_port)
Definition: flow_report.c:50
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:169
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:143
ip4_main_t ip4_main
Global ip4 main structure.
Definition: ip4_forward.c:1099
vnet_flow_rewrite_callback_t * rewrite_callback
Definition: flow_report.h:83
u8 data[0]
Packet data.
Definition: buffer.h:158
#define vec_foreach(var, vec)
Vector iterator.
#define clib_error_return(e, args...)
Definition: error.h:111
struct _unformat_input_t unformat_input_t
static vlib_buffer_free_list_t * vlib_buffer_get_free_list(vlib_main_t *vm, u32 free_list_index)
Definition: buffer_funcs.h:317
vlib_frame_t * vlib_get_frame_to_node(vlib_main_t *vm, u32 to_node_index)
Definition: main.c:187
u32 flags
buffer flags: VLIB_BUFFER_IS_TRACED: trace this buffer.
Definition: buffer.h:85
static vlib_buffer_t * vlib_get_buffer(vlib_main_t *vm, u32 buffer_index)
Translate buffer index into buffer pointer.
Definition: buffer_funcs.h:57
static clib_error_t * flow_report_init(vlib_main_t *vm)
Definition: flow_report.c:469
Definition: defs.h:46
int vnet_flow_report_add_del(flow_report_main_t *frm, vnet_flow_report_add_del_args_t *a)
Definition: flow_report.c:238