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