Line data Source code
1 : /*
2 : * Copyright (c) 2017 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 : #include <vlib/vlib.h>
17 : #include <vnet/ip/ip6_packet.h>
18 : #include <vnet/udp/udp_local.h>
19 : #include <ioam/analyse/ioam_summary_export.h>
20 : #include <ioam/analyse/ip6/ip6_ioam_analyse.h>
21 :
22 : u8 *
23 0 : ioam_template_rewrite (ipfix_exporter_t *exp, flow_report_t *fr,
24 : u16 collector_port, ipfix_report_element_t *elts,
25 : u32 n_elts, u32 *stream_index)
26 : {
27 : ip4_header_t *ip;
28 : udp_header_t *udp;
29 : ipfix_message_header_t *h;
30 : ipfix_set_header_t *s;
31 : ipfix_template_header_t *t;
32 : ipfix_field_specifier_t *f;
33 : ipfix_field_specifier_t *first_field;
34 0 : u8 *rewrite = 0;
35 : ip4_ipfix_template_packet_t *tp;
36 0 : u32 field_count = 0;
37 0 : u32 field_index = 0;
38 : flow_report_stream_t *stream;
39 :
40 0 : stream = &exp->streams[fr->stream_index];
41 :
42 : /* Determine field count */
43 : #define _(field,mask,item,length) \
44 : { \
45 : field_count++; \
46 : fr->fields_to_send = clib_bitmap_set (fr->fields_to_send, \
47 : field_index, 1); \
48 : } \
49 : field_index++;
50 :
51 0 : foreach_ioam_ipfix_field;
52 : #undef _
53 :
54 : /* Add Src address, dest address, src port, dest port
55 : * path map, number of paths manually */
56 0 : field_count += 6;
57 :
58 : /* allocate rewrite space */
59 0 : vec_validate_aligned (rewrite,
60 : sizeof (ip4_ipfix_template_packet_t)
61 : + field_count * sizeof (ipfix_field_specifier_t) - 1,
62 : CLIB_CACHE_LINE_BYTES);
63 :
64 0 : tp = (ip4_ipfix_template_packet_t *) rewrite;
65 0 : ip = (ip4_header_t *) & tp->ip4;
66 0 : udp = (udp_header_t *) (ip + 1);
67 0 : h = (ipfix_message_header_t *) (udp + 1);
68 0 : s = (ipfix_set_header_t *) (h + 1);
69 0 : t = (ipfix_template_header_t *) (s + 1);
70 0 : first_field = f = (ipfix_field_specifier_t *) (t + 1);
71 :
72 0 : ip->ip_version_and_header_length = 0x45;
73 0 : ip->ttl = 254;
74 0 : ip->protocol = IP_PROTOCOL_UDP;
75 0 : ip->src_address.as_u32 = exp->src_address.ip.ip4.as_u32;
76 0 : ip->dst_address.as_u32 = exp->ipfix_collector.ip.ip4.as_u32;
77 0 : udp->src_port = clib_host_to_net_u16 (collector_port);
78 0 : udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix);
79 0 : udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip));
80 :
81 0 : h->domain_id = clib_host_to_net_u32 (stream->domain_id); //fr->domain_id);
82 :
83 : /* Add Src address, dest address, src port, dest port
84 : * path map, number of paths manually */
85 0 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
86 : sourceIPv6Address,
87 : sizeof (ip6_address_t));
88 0 : f++;
89 :
90 0 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
91 : destinationIPv6Address,
92 : sizeof (ip6_address_t));
93 0 : f++;
94 :
95 0 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
96 : sourceTransportPort, 2);
97 0 : f++;
98 :
99 0 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
100 : destinationTransportPort, 2);
101 0 : f++;
102 :
103 : #define _(field,mask,item,length) \
104 : { \
105 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */, \
106 : item, length); \
107 : f++; \
108 : }
109 0 : foreach_ioam_ipfix_field;
110 : #undef _
111 :
112 0 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
113 : ioamNumberOfPaths, 2);
114 0 : f++;
115 :
116 : /* Add ioamPathMap manually */
117 0 : f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
118 : ioamPathMap,
119 : (sizeof (ioam_path) +
120 : (sizeof (ioam_path_map_t) *
121 : IOAM_TRACE_MAX_NODES)));
122 0 : f++;
123 :
124 : /* Back to the template packet... */
125 0 : ip = (ip4_header_t *) & tp->ip4;
126 0 : udp = (udp_header_t *) (ip + 1);
127 :
128 0 : ASSERT (f - first_field);
129 : /* Field count in this template */
130 0 : t->id_count = ipfix_id_count (IOAM_FLOW_TEMPLATE_ID, f - first_field);
131 :
132 : /* set length in octets */
133 0 : s->set_id_length =
134 0 : ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
135 :
136 : /* message length in octets */
137 0 : h->version_length = version_length ((u8 *) f - (u8 *) h);
138 :
139 0 : ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip);
140 0 : ip->checksum = ip4_header_checksum (ip);
141 :
142 0 : return rewrite;
143 : }
144 :
145 : u16
146 0 : ioam_analyse_add_ipfix_record (flow_report_t * fr,
147 : ioam_analyser_data_t * record,
148 : vlib_buffer_t * b0, u16 offset,
149 : ip6_address_t * src, ip6_address_t * dst,
150 : u16 src_port, u16 dst_port)
151 : {
152 0 : clib_spinlock_lock (&record->writer_lock);
153 :
154 0 : int field_index = 0;
155 : u16 tmp;
156 : int i, j;
157 0 : u16 num_paths = 0;
158 : u16 num_paths_offset;
159 :
160 :
161 : /* Add IPv6 source address manually */
162 0 : memcpy (b0->data + offset, &src->as_u64[0], sizeof (u64));
163 0 : offset += sizeof (u64);
164 0 : memcpy (b0->data + offset, &src->as_u64[1], sizeof (u64));
165 0 : offset += sizeof (u64);
166 :
167 : /* Add IPv6 destination address manually */
168 0 : memcpy (b0->data + offset, &dst->as_u64[0], sizeof (u64));
169 0 : offset += sizeof (u64);
170 0 : memcpy (b0->data + offset, &dst->as_u64[1], sizeof (u64));
171 0 : offset += sizeof (u64);
172 :
173 : /* Add source port manually */
174 0 : tmp = clib_host_to_net_u16 (src_port);
175 0 : memcpy (b0->data + offset, &tmp, sizeof (u16));
176 0 : offset += sizeof (u16);
177 :
178 : /* Add dest port manually */
179 0 : tmp = clib_host_to_net_u16 (dst_port);
180 0 : memcpy (b0->data + offset, &tmp, sizeof (u16));
181 0 : offset += sizeof (u16);
182 :
183 : #define _(field,mask,item,length) \
184 : if (clib_bitmap_get (fr->fields_to_send, field_index)) \
185 : { \
186 : /* Expect only 4 bytes */ \
187 : u32 tmp; \
188 : tmp = clib_host_to_net_u32((u32)record->field - (u32)record->chached_data_list->field);\
189 : memcpy (b0->data + offset, &tmp, length); \
190 : offset += length; \
191 : }
192 0 : field_index++;
193 0 : foreach_ioam_ipfix_field;
194 : #undef _
195 :
196 : /* Store num_paths_offset here and update later */
197 0 : num_paths_offset = offset;
198 0 : offset += sizeof (u16);
199 :
200 : /* Add ioamPathMap manually */
201 0 : for (i = 0; i < IOAM_MAX_PATHS_PER_FLOW; i++)
202 : {
203 0 : ioam_analyse_trace_record *trace = record->trace_data.path_data + i;
204 0 : ioam_analyse_trace_record *trace_cached =
205 0 : record->chached_data_list->trace_data.path_data + i;
206 0 : ioam_path *path = (ioam_path *) (b0->data + offset);
207 :
208 0 : if (!trace->is_free)
209 : {
210 0 : num_paths++;
211 :
212 0 : path->num_nodes = trace->num_nodes;
213 :
214 0 : path->trace_type = trace->trace_type;
215 0 : if (0 < (trace->pkt_counter - trace_cached->pkt_counter))
216 : {
217 0 : u64 new_sum = trace->mean_delay * record->seqno_data.rx_packets;
218 0 : u64 old_sum =
219 0 : trace_cached->mean_delay *
220 0 : record->chached_data_list->seqno_data.rx_packets;
221 0 : path->mean_delay =
222 0 : (u32) ((new_sum - old_sum) / (trace->pkt_counter -
223 0 : trace_cached->pkt_counter));
224 0 : path->mean_delay = clib_host_to_net_u32 (path->mean_delay);
225 : }
226 : else
227 0 : path->mean_delay = 0;
228 :
229 0 : path->bytes_counter =
230 0 : trace->bytes_counter - trace_cached->bytes_counter;
231 0 : path->bytes_counter = clib_host_to_net_u32 (path->bytes_counter);
232 :
233 0 : path->pkt_counter = trace->pkt_counter - trace_cached->pkt_counter;
234 0 : path->pkt_counter = clib_host_to_net_u32 (path->pkt_counter);
235 0 : offset += sizeof (ioam_path);
236 :
237 0 : for (j = 0; j < trace->num_nodes; j++)
238 : {
239 0 : path->path[j].node_id =
240 0 : clib_host_to_net_u32 (trace->path[j].node_id);
241 0 : path->path[j].ingress_if =
242 0 : clib_host_to_net_u16 (trace->path[j].ingress_if);
243 0 : path->path[j].egress_if =
244 0 : clib_host_to_net_u16 (trace->path[j].egress_if);
245 0 : path->path[j].state_up = trace->path[j].state_up;
246 : }
247 :
248 : //offset += (sizeof(ioam_path_map_t) * trace->num_nodes);
249 0 : offset += (sizeof (ioam_path_map_t) * IOAM_TRACE_MAX_NODES); //FIXME
250 : }
251 : }
252 :
253 0 : num_paths = clib_host_to_net_u16 (num_paths);
254 0 : memcpy (b0->data + num_paths_offset, &num_paths, sizeof (u16));
255 :
256 : /* Update cache */
257 0 : *(record->chached_data_list) = *record;
258 0 : record->chached_data_list->chached_data_list = NULL;
259 :
260 0 : clib_spinlock_unlock (&record->writer_lock);
261 0 : return offset;
262 : }
263 :
264 : vlib_frame_t *
265 0 : ioam_send_flows (flow_report_main_t *frm, ipfix_exporter_t *exp,
266 : flow_report_t *fr, vlib_frame_t *f, u32 *to_next,
267 : u32 node_index)
268 : {
269 0 : vlib_buffer_t *b0 = NULL;
270 0 : u32 next_offset = 0;
271 0 : u32 bi0 = ~0;
272 : int i;
273 : ip4_ipfix_template_packet_t *tp;
274 : ipfix_message_header_t *h;
275 0 : ipfix_set_header_t *s = NULL;
276 : ip4_header_t *ip;
277 : udp_header_t *udp;
278 : u16 new_l0, old_l0;
279 : ip_csum_t sum0;
280 0 : vlib_main_t *vm = vlib_get_main ();
281 : ip6_address_t temp;
282 0 : ioam_analyser_data_t *record = NULL;
283 : flow_report_stream_t *stream;
284 : ioam_analyser_data_t *aggregated_data;
285 : u16 data_len;
286 :
287 0 : stream = &exp->streams[fr->stream_index];
288 :
289 0 : clib_memset (&temp, 0, sizeof (ip6_address_t));
290 :
291 0 : aggregated_data = ioam_analyser_main.aggregated_data;
292 0 : data_len = vec_len (aggregated_data);
293 :
294 0 : vec_foreach_index (i, aggregated_data)
295 : {
296 0 : u8 flush = 0;
297 0 : record = aggregated_data + i;
298 :
299 : /* Flush if last entry */
300 0 : if (i == (data_len - 1))
301 0 : flush = 1;
302 :
303 0 : if (!record->is_free)
304 : {
305 :
306 0 : if (PREDICT_FALSE (b0 == NULL))
307 : {
308 0 : if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
309 0 : break;
310 :
311 0 : b0 = vlib_get_buffer (vm, bi0);
312 0 : memcpy (b0->data, fr->rewrite, vec_len (fr->rewrite));
313 0 : b0->current_data = 0;
314 0 : b0->current_length = vec_len (fr->rewrite);
315 0 : b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
316 0 : vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
317 0 : vnet_buffer (b0)->sw_if_index[VLIB_TX] = ~0;
318 :
319 0 : tp = vlib_buffer_get_current (b0);
320 0 : ip = &tp->ip4;
321 0 : h = &tp->ipfix.h;
322 0 : s = &tp->ipfix.s;
323 :
324 : /* FIXUP: message header export_time */
325 0 : h->export_time = clib_host_to_net_u32 (((u32) time (NULL)));
326 :
327 : /* FIXUP: message header sequence_number */
328 0 : h->sequence_number = stream->sequence_number++;
329 0 : h->sequence_number = clib_host_to_net_u32 (h->sequence_number);
330 0 : next_offset = (u32) (((u8 *) (s + 1)) - (u8 *) tp);
331 : }
332 :
333 0 : next_offset = ioam_analyse_add_ipfix_record (fr, record,
334 : b0, next_offset,
335 : &temp, &temp, 0, 0);
336 :
337 : /* Flush data if packet len is about to reach path mtu */
338 0 : if (next_offset > (exp->path_mtu - 250))
339 0 : flush = 1;
340 : }
341 :
342 0 : if (PREDICT_FALSE (flush && b0))
343 : {
344 0 : s->set_id_length = ipfix_set_id_length (IOAM_FLOW_TEMPLATE_ID,
345 0 : next_offset - (sizeof (*ip) +
346 : sizeof (*udp) +
347 : sizeof (*h)));
348 0 : b0->current_length = next_offset;
349 0 : b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
350 0 : tp = vlib_buffer_get_current (b0);
351 0 : ip = (ip4_header_t *) & tp->ip4;
352 0 : udp = (udp_header_t *) (ip + 1);
353 :
354 0 : sum0 = ip->checksum;
355 0 : old_l0 = ip->length;
356 0 : new_l0 = clib_host_to_net_u16 ((u16) next_offset);
357 0 : sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
358 : length /* changed member */ );
359 :
360 0 : ip->checksum = ip_csum_fold (sum0);
361 0 : ip->length = new_l0;
362 0 : udp->length =
363 0 : clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
364 :
365 0 : if (exp->udp_checksum)
366 : {
367 : /* RFC 7011 section 10.3.2. */
368 0 : udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
369 0 : if (udp->checksum == 0)
370 0 : udp->checksum = 0xffff;
371 : }
372 :
373 0 : to_next[0] = bi0;
374 0 : f->n_vectors++;
375 0 : to_next++;
376 :
377 0 : if (f->n_vectors == VLIB_FRAME_SIZE)
378 : {
379 0 : vlib_put_frame_to_node (vm, node_index, f);
380 0 : f = vlib_get_frame_to_node (vm, node_index);
381 0 : f->n_vectors = 0;
382 0 : to_next = vlib_frame_vector_args (f);
383 : }
384 0 : b0 = 0;
385 0 : bi0 = ~0;
386 : }
387 : }
388 :
389 0 : return f;
390 : }
391 :
392 : clib_error_t *
393 0 : ioam_flow_create (u8 del)
394 : {
395 : vnet_flow_report_add_del_args_t args;
396 : int rv;
397 0 : u32 domain_id = 0;
398 0 : ipfix_exporter_t *exp = &flow_report_main.exporters[0];
399 : u16 template_id;
400 :
401 0 : clib_memset (&args, 0, sizeof (args));
402 0 : args.rewrite_callback = ioam_template_rewrite;
403 0 : args.flow_data_callback = ioam_send_flows;
404 0 : del ? (args.is_add = 0) : (args.is_add = 1);
405 0 : args.domain_id = domain_id;
406 :
407 0 : rv = vnet_flow_report_add_del (exp, &args, &template_id);
408 :
409 0 : switch (rv)
410 : {
411 0 : case 0:
412 0 : break;
413 0 : case VNET_API_ERROR_NO_SUCH_ENTRY:
414 0 : return clib_error_return (0, "registration not found...");
415 0 : default:
416 0 : return clib_error_return (0, "vnet_flow_report_add_del returned %d",
417 : rv);
418 : }
419 :
420 0 : return 0;
421 : }
422 :
423 : clib_error_t *
424 559 : ioam_flow_report_init (vlib_main_t * vm)
425 : {
426 559 : return 0;
427 : }
428 :
429 : /* *INDENT-OFF* */
430 6719 : VLIB_INIT_FUNCTION (ioam_flow_report_init) =
431 : {
432 : .runs_after = VLIB_INITS("flow_report_init"),
433 : };
434 : /* *INDENT-ON* */
435 :
436 : /*
437 : * fd.io coding-style-patch-verification: ON
438 : *
439 : * Local Variables:
440 : * eval: (c-set-style "gnu")
441 : * End:
442 : */
|