FD.io VPP  v16.09
Vector Packet Processing
flow_report_classify.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 #include <vnet/flow/flow_report.h>
17 #include <vnet/api_errno.h>
18 
19 typedef struct {
22 
24 
26  flow_report_t * fr,
27  ip4_address_t * collector_address,
28  ip4_address_t * src_address,
29  u16 collector_port)
30 {
31  vnet_classify_table_t * tblp;
35  ip4_header_t * ip;
36  udp_header_t * udp;
41  ipfix_field_specifier_t * first_field;
42  u8 * rewrite = 0;
44  i32 l3_offset = -2; /* sizeof (ethernet_header_t) - sizeof (u32x4) */
45  u32 field_count = 0;
46  u32 field_index = 0;
47 
48  tblp = pool_elt_at_index (vcm->tables, fcm->classify_table_index);
49 
50  /*
51  * Mumble, assumes that we're not classifying on L2 or first 2 octets
52  * of L3..
53  */
54 
55  ip = (ip4_header_t *)(((u8 *)(tblp->mask)) + l3_offset);
56  udp = (udp_header_t *)(ip+1);
57 
58  /* Determine field count */
59 #define _(field,mask,item,length) \
60  if ((field) == (mask)) \
61  { \
62  field_count++; \
63  \
64  fr->fields_to_send = clib_bitmap_set (fr->fields_to_send, \
65  field_index, 1); \
66  } \
67  field_index++;
68 
70 #undef _
71  /* Add packetTotalCount manually */
72  field_count += 1;
73 
74  /* $$$ enterprise fields, at some later date */
75 
76  /* allocate rewrite space */
77  vec_validate_aligned (rewrite,
79  + field_count * sizeof (ipfix_field_specifier_t) - 1,
81 
82  tp = (ip4_ipfix_template_packet_t *) rewrite;
83  ip = (ip4_header_t *) &tp->ip4;
84  udp = (udp_header_t *) (ip+1);
85  h = (ipfix_message_header_t *)(udp+1);
86  s = (ipfix_set_header_t *)(h+1);
87  t = (ipfix_template_header_t *)(s+1);
88  first_field = f = (ipfix_field_specifier_t *)(t+1);
89 
90  ip->ip_version_and_header_length = 0x45;
91  ip->ttl = 254;
92  ip->protocol = IP_PROTOCOL_UDP;
93  ip->src_address.as_u32 = src_address->as_u32;
94  ip->dst_address.as_u32 = collector_address->as_u32;
95  udp->src_port = clib_host_to_net_u16 (fr->src_port);
96  udp->dst_port = clib_host_to_net_u16 (collector_port);
97  udp->length = clib_host_to_net_u16 (vec_len(rewrite) - sizeof (*ip));
98 
99  /* FIXUP: message header export_time */
100  /* FIXUP: message header sequence_number */
101  h->domain_id = clib_host_to_net_u32 (fr->domain_id);
102 
103  /* Take another trip through the mask and build the template */
104  ip = (ip4_header_t *)(((u8 *)(tblp->mask)) + l3_offset);
105  udp = (udp_header_t *)(ip+1);
106 #define _(field,mask,item,length) \
107  if ((field) == (mask)) \
108  { \
109  f->e_id_length = ipfix_e_id_length (0 /* enterprise */, \
110  item, length); \
111  f++; \
112  }
114 #undef _
115 
116  /* Add packetTotalCount manually */
117  f->e_id_length = ipfix_e_id_length (0 /* enterprise */, packetTotalCount, 8);
118  f++;
119 
120  /* Back to the template packet... */
121  ip = (ip4_header_t *) &tp->ip4;
122  udp = (udp_header_t *) (ip+1);
123 
124  ASSERT (f - first_field);
125  /* Field count in this template */
126  t->id_count = ipfix_id_count (256 /* template_id */, f - first_field);
127 
128  /* set length in octets*/
129  s->set_id_length = ipfix_set_id_length (2 /* set_id */, (u8 *) f - (u8 *)s);
130 
131  /* message length in octets */
132  h->version_length = version_length ((u8 *)f - (u8 *)h);
133 
134  ip->length = clib_host_to_net_u16 ((u8 *)f - (u8 *)ip);
135  ip->checksum = ip4_header_checksum (ip);
136 
137  return rewrite;
138 }
139 
141  flow_report_t * fr,
142  vlib_frame_t * f, u32 * to_next,
143  u32 node_index)
144 {
148  vnet_classify_table_t * t =
149  pool_elt_at_index (vcm->tables, fcm->classify_table_index);
151  vnet_classify_entry_t * v, * save_v;
152  vlib_buffer_t *b0 = 0;
153  u32 next_offset = 0;
154  u32 record_offset = 0;
155  u32 bi0 = ~0;
156  int i, j, k;
158  ipfix_message_header_t * h = 0;
159  ipfix_set_header_t * s = 0;
160  ip4_header_t * ip;
161  udp_header_t * udp;
162  int field_index;
163  ip4_header_t * match;
164  u32 records_this_buffer;
165  u16 new_l0, old_l0;
166  ip_csum_t sum0;
167  vlib_main_t * vm = frm->vlib_main;
168 
169  while (__sync_lock_test_and_set (t->writer_lock, 1))
170  ;
171 
172  for (i = 0; i < t->nbuckets; i++)
173  {
174  b = &t->buckets [i];
175  if (b->offset == 0)
176  continue;
177 
178  save_v = vnet_classify_get_entry (t, b->offset);
179  for (j = 0; j < (1<<b->log2_pages); j++)
180  {
181  for (k = 0; k < t->entries_per_page; k++)
182  {
184  (t, save_v, j*t->entries_per_page + k);
185 
187  continue;
188 
189  /* OK, we have something to send... */
190  if (PREDICT_FALSE (b0 == 0))
191  {
192  if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
193  goto flush;
194  b0 = vlib_get_buffer (vm, bi0);
195 
196  u32 copy_len = sizeof(ip4_header_t) +
197  sizeof(udp_header_t) +
198  sizeof(ipfix_message_header_t);
199  clib_memcpy (b0->data, fr->rewrite, copy_len);
200  b0->current_data = 0;
201  b0->current_length = copy_len;
203  vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
204  vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index;
205 
206  tp = vlib_buffer_get_current (b0);
207  ip = (ip4_header_t *) &tp->ip4;
208  udp = (udp_header_t *) (ip+1);
209  h = (ipfix_message_header_t *)(udp+1);
210  s = (ipfix_set_header_t *)(h+1);
211 
212  /* FIXUP: message header export_time */
213  h->export_time = (u32)
214  (((f64)frm->unix_time_0) +
215  (vlib_time_now(frm->vlib_main) - frm->vlib_time_0));
216  h->export_time = clib_host_to_net_u32(h->export_time);
217 
218  /* FIXUP: message header sequence_number */
220  h->sequence_number = clib_host_to_net_u32 (h->sequence_number);
221 
222  next_offset = (u32) (((u8 *)(s+1)) - (u8 *)tp);
223  record_offset = next_offset;
224  records_this_buffer = 0;
225  }
226 
227  field_index = 0;
228  match = (ip4_header_t *) (((u8 *)v->key) - 2);
229  ip = match;
230  udp = (udp_header_t * )(ip+1);
231 
232 #define _(field,mask,item,length) \
233  if (clib_bitmap_get (fr->fields_to_send, field_index)) \
234  { \
235  clib_memcpy (b0->data + next_offset, &field, \
236  length); \
237  next_offset += length; \
238  } \
239  field_index++;
241 #undef _
242 
243  /* Add packetTotalCount manually */
244  {
245  u64 packets = clib_host_to_net_u64 (v->hits);
246  clib_memcpy (b0->data + next_offset, &packets, sizeof (packets));
247  next_offset += sizeof (packets);
248  }
249  records_this_buffer++;
250  fr->sequence_number++;
251 
252  /* Next record will have the same size as this record */
253  u32 next_record_size = next_offset - record_offset;
254  record_offset = next_offset;
255 
256  if (next_offset + next_record_size > frm->path_mtu)
257  {
258  s->set_id_length = ipfix_set_id_length (256 /* template ID*/,
259  next_offset -
260  (sizeof (*ip) + sizeof (*udp) +
261  sizeof (*h)));
262  h->version_length = version_length (next_offset -
263  (sizeof (*ip) + sizeof (*udp)));
264  b0->current_length = next_offset;
266 
267  tp = vlib_buffer_get_current (b0);
268  ip = (ip4_header_t *) &tp->ip4;
269  udp = (udp_header_t *) (ip+1);
270 
271  sum0 = ip->checksum;
272  old_l0 = ip->length;
273  new_l0 =
274  clib_host_to_net_u16 ((u16)next_offset);
275 
276  sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
277  length /* changed member */);
278 
279  ip->checksum = ip_csum_fold (sum0);
280  ip->length = new_l0;
281  udp->length =
282  clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
283 
284  ASSERT (ip->checksum == ip4_header_checksum (ip));
285 
286  to_next[0] = bi0;
287  f->n_vectors++;
288  to_next++;
289 
290  if (f->n_vectors == VLIB_FRAME_SIZE)
291  {
292  vlib_put_frame_to_node (vm, node_index, f);
293  f = vlib_get_frame_to_node (vm, node_index);
294  f->n_vectors = 0;
295  to_next = vlib_frame_vector_args (f);
296  }
297  b0 = 0;
298  bi0 = ~0;
299  }
300  }
301  }
302  }
303 
304  flush:
305  if (b0)
306  {
307  s->set_id_length = ipfix_set_id_length (256 /* template ID*/,
308  next_offset -
309  (sizeof (*ip) + sizeof (*udp) +
310  sizeof (*h)));
311  h->version_length = version_length (next_offset -
312  (sizeof (*ip) + sizeof (*udp)));
313  b0->current_length = next_offset;
315 
316  tp = vlib_buffer_get_current (b0);
317  ip = (ip4_header_t *) &tp->ip4;
318  udp = (udp_header_t *) (ip+1);
319 
320  sum0 = ip->checksum;
321  old_l0 = ip->length;
322  new_l0 = clib_host_to_net_u16 ((u16)next_offset);
323 
324  sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
325  length /* changed member */);
326 
327  ip->checksum = ip_csum_fold (sum0);
328  ip->length = new_l0;
329  udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
330 
331  ASSERT (ip->checksum == ip4_header_checksum (ip));
332 
333  to_next[0] = bi0;
334  f->n_vectors++;
335 
336  b0 = 0;
337  bi0 = ~0;
338  }
339 
340  *(t->writer_lock) = 0;
341  return f;
342 }
343 
344 
345 static clib_error_t *
347  unformat_input_t * input,
348  vlib_cli_command_t * cmd)
349 {
353  int rv;
354  int is_add = 1;
355  u32 domain_id = 0;
356  u32 src_port = UDP_DST_PORT_ipfix;
357 
358  domain_id = 0;
359  fcm->classify_table_index = ~0;
360  memset (&args, 0, sizeof (args));
361 
362  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
363  if (unformat (input, "table %d", &fcm->classify_table_index))
364  ;
365  else if (unformat (input, "domain %d", &domain_id))
366  ;
367  else if (unformat (input, "src-port %d", &src_port))
368  ;
369  else if (unformat (input, "del"))
370  is_add = 0;
371  else
372  return clib_error_return (0, "unknown input `%U'",
373  format_unformat_error, input);
374  }
375 
376  if (fcm->classify_table_index == ~0)
377  return clib_error_return (0, "classifier table not specified");
378 
379  args.opaque = (void *) fcm;
382  args.is_add = is_add;
383  args.domain_id = domain_id;
384  args.src_port = (u16)src_port;
385 
386  rv = vnet_flow_report_add_del (frm, &args);
387 
388  switch (rv)
389  {
390  case 0:
391  break;
392  case VNET_API_ERROR_NO_SUCH_ENTRY:
393  return clib_error_return (0, "registration not found...");
394  default:
395  return clib_error_return (0, "vnet_flow_report_add_del returned %d", rv);
396  }
397 
398  return 0;
399 }
400 
401 VLIB_CLI_COMMAND (flow_classify_command, static) = {
402  .path = "flow classify",
403  .short_help = "flow classify",
404  .function = flow_classify_command_fn,
405 };
406 
407 static clib_error_t *
409 {
410  clib_error_t * error;
411 
412  if ((error = vlib_call_init_function (vm, flow_report_init)))
413  return error;
414 
415  return 0;
416 }
417 
sll srl srl sll sra u16x4 i
Definition: vector_sse2.h:343
uword unformat(unformat_input_t *i, char *fmt,...)
Definition: unformat.c:966
#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
void * opaque
Definition: flow_report.h:67
static u8 * template_rewrite(flow_report_main_t *frm, flow_report_t *fr, ip4_address_t *collector_address, ip4_address_t *src_address, u16 collector_port)
uword ip_csum_t
Definition: ip_packet.h:86
#define vec_validate_aligned(V, I, A)
Make sure vector is long enough for given index (no header, specified alignment)
Definition: vec.h:407
i16 current_data
signed offset in data[], pre_data[] that we are currently processing.
Definition: buffer.h:78
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:111
static void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:187
vnet_flow_rewrite_callback_t * rewrite_callback
Definition: flow_report.h:108
int i32
Definition: types.h:81
unsigned long u64
Definition: types.h:89
#define vlib_call_init_function(vm, x)
Definition: init.h:161
static int vnet_classify_entry_is_free(vnet_classify_entry_t *e)
Definition: vnet_classify.h:89
flow_report_main_t flow_report_main
Definition: flow_report.c:21
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:369
flow_report_classify_main_t flow_report_classify_main
u16 current_length
Nbytes between current data and the end of this buffer.
Definition: buffer.h:82
u8 * rewrite
Definition: flow_report.h:56
#define PREDICT_FALSE(x)
Definition: clib.h:97
#define VLIB_FRAME_SIZE
Definition: node.h:328
void vlib_put_frame_to_node(vlib_main_t *vm, u32 to_node_index, vlib_frame_t *f)
Definition: main.c:194
static vlib_frame_t * send_flows(flow_report_main_t *frm, flow_report_t *fr, vlib_frame_t *f, u32 *to_next, u32 node_index)
static u32 version_length(u16 length)
Definition: ipfix_packet.h:31
static vnet_classify_entry_t * vnet_classify_entry_at_index(vnet_classify_table_t *t, vnet_classify_entry_t *e, u32 index)
u32 sequence_number
Definition: flow_report.h:57
u16 n_vectors
Definition: node.h:344
static u32 ipfix_id_count(u16 id, u16 count)
Definition: ipfix_packet.h:175
#define clib_memcpy(a, b, c)
Definition: string.h:63
#define VLIB_BUFFER_TOTAL_LENGTH_VALID
Definition: buffer.h:97
struct _vnet_classify_main vnet_classify_main_t
Definition: vnet_classify.h:50
#define ASSERT(truth)
unsigned int u32
Definition: types.h:88
vlib_main_t * vlib_main
Definition: flow_report.h:96
u8 * format_unformat_error(u8 *s, va_list *va)
Definition: unformat.c:91
#define vnet_buffer(b)
Definition: buffer.h:335
vnet_classify_main_t vnet_classify_main
Definition: vnet_classify.c:21
u32 vlib_buffer_alloc(vlib_main_t *vm, u32 *buffers, u32 n_buffers)
Allocate buffers into supplied array.
Definition: dpdk_buffer.c:643
#define foreach_ipfix_field
static u32 ipfix_set_id_length(u16 set_id, u16 length)
Definition: ipfix_packet.h:114
Definition: defs.h:47
unsigned short u16
Definition: types.h:57
VLIB_CLI_COMMAND(set_interface_ip_source_and_port_range_check_command, static)
#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
static void * vlib_frame_vector_args(vlib_frame_t *f)
Get pointer to frame vector data.
Definition: node_funcs.h:251
#define ip_csum_update(sum, old, new, type, field)
Definition: ip_packet.h:133
vnet_flow_data_callback_t * flow_data_callback
Definition: flow_report.h:107
static clib_error_t * flow_classify_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:169
u8 data[0]
Packet data.
Definition: buffer.h:151
#define clib_error_return(e, args...)
Definition: error.h:111
static clib_error_t * flow_report_classify_init(vlib_main_t *vm)
struct _unformat_input_t unformat_input_t
vlib_frame_t * vlib_get_frame_to_node(vlib_main_t *vm, u32 to_node_index)
Definition: main.c:185
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:67
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:69
static u16 ip4_header_checksum(ip4_header_t *i)
Definition: ip4_packet.h:194
static clib_error_t * flow_report_init(vlib_main_t *vm)
Definition: flow_report.c:317
static u16 ip_csum_fold(ip_csum_t c)
Definition: ip_packet.h:138
Definition: defs.h:46
static vnet_classify_entry_t * vnet_classify_get_entry(vnet_classify_table_t *t, uword offset)
int vnet_flow_report_add_del(flow_report_main_t *frm, vnet_flow_report_add_del_args_t *a)
Definition: flow_report.c:175