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)
30 vnet_classify_table_t * tblp;
31 vnet_classify_main_t * vcm = &vnet_classify_main;
32 flow_report_sample_main_t *fsm =
33 (flow_report_sample_main_t *) fr->opaque;
36 ipfix_message_header_t * h;
37 ipfix_set_header_t * s;
38 ipfix_template_header_t * t;
39 ipfix_field_specifier_t * f;
40 ipfix_field_specifier_t * first_field;
42 ip4_ipfix_template_packet_t * tp;
43 i32 l3_offset = -2; /* sizeof (ethernet_header_t) - sizeof (u32x4) */
47 tblp = pool_elt_at_index (vcm->tables, fsm->classify_table_index);
50 * Mumble, assumes that we're not classifying on L2 or first 2 octets
54 ip = (ip4_header_t *)(((u8 *)(tblp->mask)) + l3_offset);
55 udp = (udp_header_t *)(ip+1);
57 /* Determine field count */
58 #define _(field,mask,item,length) \
59 if ((field) == (mask)) \
63 fr->fields_to_send = clib_bitmap_set (fr->fields_to_send, \
70 /* Add packetTotalCount manually */
73 /* $$$ enterprise fields, at some later date */
75 /* allocate rewrite space */
76 vec_validate_aligned (rewrite,
77 sizeof (ip4_ipfix_template_packet_t)
78 + field_count * sizeof (ipfix_field_specifier_t) - 1,
79 CLIB_CACHE_LINE_BYTES);
81 tp = (ip4_ipfix_template_packet_t *) rewrite;
82 ip = (ip4_header_t *) &tp->ip4;
83 udp = (udp_header_t *) (ip+1);
84 h = (ipfix_message_header_t *)(udp+1);
85 s = (ipfix_set_header_t *)(h+1);
86 t = (ipfix_template_header_t *)(s+1);
87 first_field = f = (ipfix_field_specifier_t *)(t+1);
89 ip->ip_version_and_header_length = 0x45;
91 ip->protocol = IP_PROTOCOL_UDP;
92 ip->src_address.as_u32 = src_address->as_u32;
93 ip->dst_address.as_u32 = collector_address->as_u32;
94 udp->src_port = clib_host_to_net_u16 (4739 /* $$FIXME */);
95 udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix);
96 udp->length = clib_host_to_net_u16 (vec_len(rewrite) - sizeof (*ip));
98 /* FIXUP: message header export_time */
99 /* FIXUP: message header sequence_number */
100 h->domain_id = clib_host_to_net_u32 (fr->domain_id);
102 /* Take another trip through the mask and build the template */
103 ip = (ip4_header_t *)(((u8 *)(tblp->mask)) + l3_offset);
104 udp = (udp_header_t *)(ip+1);
105 #define _(field,mask,item,length) \
106 if ((field) == (mask)) \
108 f->e_id_length = ipfix_e_id_length (0 /* enterprise */, \
115 /* Add packetTotalCount manually */
116 f->e_id_length = ipfix_e_id_length (0 /* enterprise */, packetTotalCount, 8);
119 /* Back to the template packet... */
120 ip = (ip4_header_t *) &tp->ip4;
121 udp = (udp_header_t *) (ip+1);
123 ASSERT (f - first_field);
124 /* Field count in this template */
125 t->id_count = ipfix_id_count (256 /* template_id */, f - first_field);
127 /* set length in octets*/
128 s->set_id_length = ipfix_set_id_length (2 /* set_id */, (u8 *) f - (u8 *)s);
130 /* message length in octets */
131 h->version_length = version_length ((u8 *)f - (u8 *)h);
133 ip->length = clib_host_to_net_u16 ((u8 *)f - (u8 *)ip);
134 ip->checksum = ip4_header_checksum (ip);
139 static vlib_frame_t * send_flows (flow_report_main_t * frm,
141 vlib_frame_t * f, u32 * to_next,
144 vnet_classify_main_t * vcm = &vnet_classify_main;
145 flow_report_sample_main_t * fsm =
146 (flow_report_sample_main_t *) fr->opaque;
147 vnet_classify_table_t * t =
148 pool_elt_at_index (vcm->tables, fsm->classify_table_index);
149 vnet_classify_bucket_t * b;
150 vnet_classify_entry_t * v, * save_v;
151 vlib_buffer_t *b0 = 0;
155 ip4_ipfix_template_packet_t * tp;
156 ipfix_message_header_t * h = 0;
157 ipfix_set_header_t * s = 0;
161 ip4_header_t * match;
162 u32 records_this_buffer;
165 vlib_main_t * vm = frm->vlib_main;
167 while (__sync_lock_test_and_set (t->writer_lock, 1))
170 for (i = 0; i < t->nbuckets; i++)
176 save_v = vnet_classify_get_entry (t, b->offset);
177 for (j = 0; j < (1<<b->log2_pages); j++)
179 for (k = 0; k < t->entries_per_page; k++)
181 v = vnet_classify_entry_at_index
182 (t, save_v, j*t->entries_per_page + k);
184 if (vnet_classify_entry_is_free (v))
187 /* OK, we have something to send... */
188 if (PREDICT_FALSE (b0 == 0))
190 if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
192 b0 = vlib_get_buffer (vm, bi0);
194 u32 copy_len = sizeof(ip4_header_t) +
195 sizeof(udp_header_t) +
196 sizeof(ipfix_message_header_t);
197 clib_memcpy (b0->data, fr->rewrite, copy_len);
198 b0->current_data = 0;
199 b0->current_length = copy_len;
200 b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
201 vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
202 /* $$$ for now, look up in fib-0. Later: arbitrary TX fib */
203 vnet_buffer (b0)->sw_if_index[VLIB_TX] = ~0;
205 tp = vlib_buffer_get_current (b0);
206 ip = (ip4_header_t *) &tp->ip4;
207 udp = (udp_header_t *) (ip+1);
208 h = (ipfix_message_header_t *)(udp+1);
209 s = (ipfix_set_header_t *)(h+1);
211 /* FIXUP: message header export_time */
212 h->export_time = (u32)
213 (((f64)frm->unix_time_0) +
214 (vlib_time_now(frm->vlib_main) - frm->vlib_time_0));
215 h->export_time = clib_host_to_net_u32(h->export_time);
217 /* FIXUP: message header sequence_number */
218 h->sequence_number = fr->sequence_number;
219 h->sequence_number = clib_host_to_net_u32 (h->sequence_number);
221 next_offset = (u32) (((u8 *)(s+1)) - (u8 *)tp);
222 records_this_buffer = 0;
226 match = (ip4_header_t *) (((u8 *)v->key) - 2);
228 udp = (udp_header_t * )(ip+1);
230 #define _(field,mask,item,length) \
231 if (clib_bitmap_get (fr->fields_to_send, field_index)) \
233 clib_memcpy (b0->data + next_offset, &field, \
235 next_offset += length; \
241 /* Add packetTotalCount manually */
243 u64 packets = clib_host_to_net_u64 (v->hits);
244 clib_memcpy (b0->data + next_offset, &packets, sizeof (packets));
245 next_offset += sizeof (packets);
247 records_this_buffer++;
248 fr->sequence_number++;
250 if (next_offset > 1450)
252 s->set_id_length = ipfix_set_id_length (256 /* template ID*/,
254 (sizeof (*ip) + sizeof (*udp) +
256 h->version_length = version_length (next_offset -
257 (sizeof (*ip) + sizeof (*udp)));
258 b0->current_length = next_offset;
259 b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
261 tp = vlib_buffer_get_current (b0);
262 ip = (ip4_header_t *) &tp->ip4;
263 udp = (udp_header_t *) (ip+1);
268 clib_host_to_net_u16 ((u16)next_offset);
270 sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
271 length /* changed member */);
273 ip->checksum = ip_csum_fold (sum0);
276 clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
278 ASSERT (ip->checksum == ip4_header_checksum (ip));
284 if (f->n_vectors == VLIB_FRAME_SIZE)
286 vlib_put_frame_to_node (vm, node_index, f);
287 f = vlib_get_frame_to_node (vm, node_index);
289 to_next = vlib_frame_vector_args (f);
301 s->set_id_length = ipfix_set_id_length (256 /* template ID*/,
303 (sizeof (*ip) + sizeof (*udp) +
305 h->version_length = version_length (next_offset -
306 (sizeof (*ip) + sizeof (*udp)));
307 b0->current_length = next_offset;
308 b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
310 tp = vlib_buffer_get_current (b0);
311 ip = (ip4_header_t *) &tp->ip4;
312 udp = (udp_header_t *) (ip+1);
316 new_l0 = clib_host_to_net_u16 ((u16)next_offset);
318 sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
319 length /* changed member */);
321 ip->checksum = ip_csum_fold (sum0);
323 udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
325 ASSERT (ip->checksum == ip4_header_checksum (ip));
334 *(t->writer_lock) = 0;
339 static clib_error_t *
340 flow_sample_command_fn (vlib_main_t * vm,
341 unformat_input_t * input,
342 vlib_cli_command_t * cmd)
344 flow_report_sample_main_t *fsm = &flow_report_sample_main;
345 flow_report_main_t *frm = &flow_report_main;
346 vnet_flow_report_add_del_args_t args;
352 fsm->classify_table_index = ~0;
353 memset (&args, 0, sizeof (args));
355 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
356 if (unformat (input, "table %d", &fsm->classify_table_index))
358 else if (unformat (input, "domain %d", &domain_id))
360 else if (unformat (input, "del"))
363 return clib_error_return (0, "unknown input `%U'",
364 format_unformat_error, input);
367 if (fsm->classify_table_index == ~0)
368 return clib_error_return (0, "classifier table not specified");
370 args.opaque = (void *) fsm;
371 args.rewrite_callback = template_rewrite;
372 args.flow_data_callback = send_flows;
373 args.is_add = is_add;
374 args.domain_id = domain_id;
376 rv = vnet_flow_report_add_del (frm, &args);
382 case VNET_API_ERROR_NO_SUCH_ENTRY:
383 return clib_error_return (0, "registration not found...");
385 return clib_error_return (0, "vnet_flow_report_add_del returned %d", rv);
391 VLIB_CLI_COMMAND (flow_sample_command, static) = {
392 .path = "flow sample",
393 .short_help = "flow sample",
394 .function = flow_sample_command_fn,
397 static clib_error_t *
398 flow_report_sample_init (vlib_main_t *vm)
400 clib_error_t * error;
402 if ((error = vlib_call_init_function (vm, flow_report_init)))
408 VLIB_INIT_FUNCTION (flow_report_sample_init);