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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
15 #include <vnet/flow/flow_report.h>
16 #include <vnet/flow/flow_report_sample.h>
17 #include <vnet/api_errno.h>
20 u32 classify_table_index;
21 } flow_report_sample_main_t;
23 flow_report_sample_main_t flow_report_sample_main;
25 static u8 * template_rewrite (flow_report_main_t * frm,
27 ip4_address_t * collector_address,
28 ip4_address_t * src_address,
31 vnet_classify_table_t * tblp;
32 vnet_classify_main_t * vcm = &vnet_classify_main;
33 flow_report_sample_main_t *fsm =
34 (flow_report_sample_main_t *) fr->opaque;
37 ipfix_message_header_t * h;
38 ipfix_set_header_t * s;
39 ipfix_template_header_t * t;
40 ipfix_field_specifier_t * f;
41 ipfix_field_specifier_t * first_field;
43 ip4_ipfix_template_packet_t * tp;
44 i32 l3_offset = -2; /* sizeof (ethernet_header_t) - sizeof (u32x4) */
48 tblp = pool_elt_at_index (vcm->tables, fsm->classify_table_index);
51 * Mumble, assumes that we're not classifying on L2 or first 2 octets
55 ip = (ip4_header_t *)(((u8 *)(tblp->mask)) + l3_offset);
56 udp = (udp_header_t *)(ip+1);
58 /* Determine field count */
59 #define _(field,mask,item,length) \
60 if ((field) == (mask)) \
64 fr->fields_to_send = clib_bitmap_set (fr->fields_to_send, \
71 /* Add packetTotalCount manually */
74 /* $$$ enterprise fields, at some later date */
76 /* allocate rewrite space */
77 vec_validate_aligned (rewrite,
78 sizeof (ip4_ipfix_template_packet_t)
79 + field_count * sizeof (ipfix_field_specifier_t) - 1,
80 CLIB_CACHE_LINE_BYTES);
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);
90 ip->ip_version_and_header_length = 0x45;
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 (4739 /* $$FIXME */);
96 udp->dst_port = clib_host_to_net_u16 (collector_port);
97 udp->length = clib_host_to_net_u16 (vec_len(rewrite) - sizeof (*ip));
99 /* FIXUP: message header export_time */
100 /* FIXUP: message header sequence_number */
101 h->domain_id = clib_host_to_net_u32 (fr->domain_id);
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)) \
109 f->e_id_length = ipfix_e_id_length (0 /* enterprise */, \
116 /* Add packetTotalCount manually */
117 f->e_id_length = ipfix_e_id_length (0 /* enterprise */, packetTotalCount, 8);
120 /* Back to the template packet... */
121 ip = (ip4_header_t *) &tp->ip4;
122 udp = (udp_header_t *) (ip+1);
124 ASSERT (f - first_field);
125 /* Field count in this template */
126 t->id_count = ipfix_id_count (256 /* template_id */, f - first_field);
128 /* set length in octets*/
129 s->set_id_length = ipfix_set_id_length (2 /* set_id */, (u8 *) f - (u8 *)s);
131 /* message length in octets */
132 h->version_length = version_length ((u8 *)f - (u8 *)h);
134 ip->length = clib_host_to_net_u16 ((u8 *)f - (u8 *)ip);
135 ip->checksum = ip4_header_checksum (ip);
140 static vlib_frame_t * send_flows (flow_report_main_t * frm,
142 vlib_frame_t * f, u32 * to_next,
145 vnet_classify_main_t * vcm = &vnet_classify_main;
146 flow_report_sample_main_t * fsm =
147 (flow_report_sample_main_t *) fr->opaque;
148 vnet_classify_table_t * t =
149 pool_elt_at_index (vcm->tables, fsm->classify_table_index);
150 vnet_classify_bucket_t * b;
151 vnet_classify_entry_t * v, * save_v;
152 vlib_buffer_t *b0 = 0;
154 u32 record_offset = 0;
157 ip4_ipfix_template_packet_t * tp;
158 ipfix_message_header_t * h = 0;
159 ipfix_set_header_t * s = 0;
163 ip4_header_t * match;
164 u32 records_this_buffer;
167 vlib_main_t * vm = frm->vlib_main;
169 while (__sync_lock_test_and_set (t->writer_lock, 1))
172 for (i = 0; i < t->nbuckets; i++)
178 save_v = vnet_classify_get_entry (t, b->offset);
179 for (j = 0; j < (1<<b->log2_pages); j++)
181 for (k = 0; k < t->entries_per_page; k++)
183 v = vnet_classify_entry_at_index
184 (t, save_v, j*t->entries_per_page + k);
186 if (vnet_classify_entry_is_free (v))
189 /* OK, we have something to send... */
190 if (PREDICT_FALSE (b0 == 0))
192 if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
194 b0 = vlib_get_buffer (vm, bi0);
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;
202 b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
203 vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
204 vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index;
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);
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);
218 /* FIXUP: message header sequence_number */
219 h->sequence_number = fr->sequence_number;
220 h->sequence_number = clib_host_to_net_u32 (h->sequence_number);
222 next_offset = (u32) (((u8 *)(s+1)) - (u8 *)tp);
223 record_offset = next_offset;
224 records_this_buffer = 0;
228 match = (ip4_header_t *) (((u8 *)v->key) - 2);
230 udp = (udp_header_t * )(ip+1);
232 #define _(field,mask,item,length) \
233 if (clib_bitmap_get (fr->fields_to_send, field_index)) \
235 clib_memcpy (b0->data + next_offset, &field, \
237 next_offset += length; \
243 /* Add packetTotalCount manually */
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);
249 records_this_buffer++;
250 fr->sequence_number++;
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;
256 if (next_offset + next_record_size > frm->path_mtu)
258 s->set_id_length = ipfix_set_id_length (256 /* template ID*/,
260 (sizeof (*ip) + sizeof (*udp) +
262 h->version_length = version_length (next_offset -
263 (sizeof (*ip) + sizeof (*udp)));
264 b0->current_length = next_offset;
265 b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
267 tp = vlib_buffer_get_current (b0);
268 ip = (ip4_header_t *) &tp->ip4;
269 udp = (udp_header_t *) (ip+1);
274 clib_host_to_net_u16 ((u16)next_offset);
276 sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
277 length /* changed member */);
279 ip->checksum = ip_csum_fold (sum0);
282 clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
284 ASSERT (ip->checksum == ip4_header_checksum (ip));
290 if (f->n_vectors == VLIB_FRAME_SIZE)
292 vlib_put_frame_to_node (vm, node_index, f);
293 f = vlib_get_frame_to_node (vm, node_index);
295 to_next = vlib_frame_vector_args (f);
307 s->set_id_length = ipfix_set_id_length (256 /* template ID*/,
309 (sizeof (*ip) + sizeof (*udp) +
311 h->version_length = version_length (next_offset -
312 (sizeof (*ip) + sizeof (*udp)));
313 b0->current_length = next_offset;
314 b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
316 tp = vlib_buffer_get_current (b0);
317 ip = (ip4_header_t *) &tp->ip4;
318 udp = (udp_header_t *) (ip+1);
322 new_l0 = clib_host_to_net_u16 ((u16)next_offset);
324 sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
325 length /* changed member */);
327 ip->checksum = ip_csum_fold (sum0);
329 udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
331 ASSERT (ip->checksum == ip4_header_checksum (ip));
340 *(t->writer_lock) = 0;
345 static clib_error_t *
346 flow_sample_command_fn (vlib_main_t * vm,
347 unformat_input_t * input,
348 vlib_cli_command_t * cmd)
350 flow_report_sample_main_t *fsm = &flow_report_sample_main;
351 flow_report_main_t *frm = &flow_report_main;
352 vnet_flow_report_add_del_args_t args;
358 fsm->classify_table_index = ~0;
359 memset (&args, 0, sizeof (args));
361 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
362 if (unformat (input, "table %d", &fsm->classify_table_index))
364 else if (unformat (input, "domain %d", &domain_id))
366 else if (unformat (input, "del"))
369 return clib_error_return (0, "unknown input `%U'",
370 format_unformat_error, input);
373 if (fsm->classify_table_index == ~0)
374 return clib_error_return (0, "classifier table not specified");
376 args.opaque = (void *) fsm;
377 args.rewrite_callback = template_rewrite;
378 args.flow_data_callback = send_flows;
379 args.is_add = is_add;
380 args.domain_id = domain_id;
382 rv = vnet_flow_report_add_del (frm, &args);
388 case VNET_API_ERROR_NO_SUCH_ENTRY:
389 return clib_error_return (0, "registration not found...");
391 return clib_error_return (0, "vnet_flow_report_add_del returned %d", rv);
397 VLIB_CLI_COMMAND (flow_sample_command, static) = {
398 .path = "flow sample",
399 .short_help = "flow sample",
400 .function = flow_sample_command_fn,
403 static clib_error_t *
404 flow_report_sample_init (vlib_main_t *vm)
406 clib_error_t * error;
408 if ((error = vlib_call_init_function (vm, flow_report_init)))
414 VLIB_INIT_FUNCTION (flow_report_sample_init);