73835919dd9e45a74bcd4b79e56ee702d5d0cf78
[vpp.git] / src / plugins / ioam / udp-ping / udp_ping_export.c
1 /*
2  * Copyright (c) 2016 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 <vnet/ipfix-export/flow_report.h>
17 #include <ioam/analyse/ioam_summary_export.h>
18 #include <vnet/api_errno.h>
19 #include <ioam/udp-ping/udp_ping.h>
20
21 #define UDP_PING_EXPORT_RECORD_SIZE 400
22
23 static u8 *
24 udp_ping_template_rewrite (flow_report_main_t * frm, flow_report_t * fr,
25                            ip4_address_t * collector_address,
26                            ip4_address_t * src_address, u16 collector_port,
27                            ipfix_report_element_t * elts,
28                            u32 n_elts, u32 * stream_index)
29 {
30   return ioam_template_rewrite (frm, fr, collector_address,
31                                 src_address, collector_port, elts, n_elts,
32                                 stream_index);
33 }
34
35 static vlib_frame_t *
36 udp_ping_send_flows (flow_report_main_t * frm, flow_report_t * fr,
37                      vlib_frame_t * f, u32 * to_next, u32 node_index)
38 {
39   vlib_buffer_t *b0 = NULL;
40   u32 next_offset = 0;
41   u32 bi0 = ~0;
42   int i, j;
43   ip4_ipfix_template_packet_t *tp;
44   ipfix_message_header_t *h;
45   ipfix_set_header_t *s = NULL;
46   ip4_header_t *ip;
47   udp_header_t *udp;
48   u32 records_this_buffer;
49   u16 new_l0, old_l0;
50   ip_csum_t sum0;
51   vlib_main_t *vm = frm->vlib_main;
52   flow_report_stream_t *stream;
53   udp_ping_flow_data *stats;
54   ip46_udp_ping_flow *ip46_flow;
55   u16 src_port, dst_port;
56   u16 data_len;
57
58   stream = &frm->streams[fr->stream_index];
59   data_len = vec_len (udp_ping_main.ip46_flow);
60
61   for (i = 0; i < data_len; i++)
62     {
63       if (pool_is_free_index (udp_ping_main.ip46_flow, i))
64         continue;
65
66       ip46_flow = pool_elt_at_index (udp_ping_main.ip46_flow, i);
67       j = 0;
68       for (src_port = ip46_flow->udp_data.start_src_port;
69            src_port <= ip46_flow->udp_data.end_src_port; src_port++)
70         {
71           for (dst_port = ip46_flow->udp_data.start_dst_port;
72                dst_port <= ip46_flow->udp_data.end_dst_port; dst_port++, j++)
73             {
74               stats = ip46_flow->udp_data.stats + j;
75               if (PREDICT_FALSE (b0 == NULL))
76                 {
77                   if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
78                     break;
79
80
81                   b0 = vlib_get_buffer (vm, bi0);
82                   memcpy (b0->data, fr->rewrite, vec_len (fr->rewrite));
83                   b0->current_data = 0;
84                   b0->current_length = vec_len (fr->rewrite);
85                   b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
86                   vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
87                   vnet_buffer (b0)->sw_if_index[VLIB_TX] = ~0;
88
89                   tp = vlib_buffer_get_current (b0);
90                   ip = &tp->ip4;
91                   h = &tp->ipfix.h;
92                   s = &tp->ipfix.s;
93
94                   /* FIXUP: message header export_time */
95                   h->export_time = clib_host_to_net_u32 (((u32) time (NULL)));
96
97                   /* FIXUP: message header sequence_number */
98                   h->sequence_number = stream->sequence_number++;
99                   h->sequence_number =
100                     clib_host_to_net_u32 (h->sequence_number);
101                   next_offset = (u32) (((u8 *) (s + 1)) - (u8 *) tp);
102                   records_this_buffer = 0;
103                 }
104
105               next_offset = ioam_analyse_add_ipfix_record (fr,
106                                                            &stats->analyse_data,
107                                                            b0, next_offset,
108                                                            &ip46_flow->
109                                                            src.ip6,
110                                                            &ip46_flow->
111                                                            dst.ip6, src_port,
112                                                            dst_port);
113
114               //u32 pak_sent = clib_host_to_net_u32(stats->pak_sent);
115               //memcpy (b0->data + next_offset, &pak_sent, sizeof(u32));
116               //next_offset += sizeof(u32);
117
118               records_this_buffer++;
119
120               /* Flush data if packet len is about to reach path mtu */
121               if (next_offset > (frm->path_mtu - UDP_PING_EXPORT_RECORD_SIZE))
122                 {
123                   b0->current_length = next_offset;
124                   b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
125                   tp = vlib_buffer_get_current (b0);
126                   ip = (ip4_header_t *) & tp->ip4;
127                   udp = (udp_header_t *) (ip + 1);
128                   h = &tp->ipfix.h;
129                   s = &tp->ipfix.s;
130
131                   s->set_id_length =
132                     ipfix_set_id_length (IOAM_FLOW_TEMPLATE_ID,
133                                          next_offset - (sizeof (*ip) +
134                                                         sizeof (*udp) +
135                                                         sizeof (*h)));
136                   h->version_length =
137                     version_length (next_offset -
138                                     (sizeof (*ip) + sizeof (*udp)));
139
140                   sum0 = ip->checksum;
141                   old_l0 = ip->length;
142                   new_l0 = clib_host_to_net_u16 ((u16) next_offset);
143                   sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
144                                          length /* changed member */ );
145
146                   ip->checksum = ip_csum_fold (sum0);
147                   ip->length = new_l0;
148                   udp->length =
149                     clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
150
151                   udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
152                   if (udp->checksum == 0)
153                     udp->checksum = 0xffff;
154
155                   ASSERT (ip->checksum == ip4_header_checksum (ip));
156
157                   to_next[0] = bi0;
158                   f->n_vectors++;
159                   to_next++;
160
161                   if (f->n_vectors == VLIB_FRAME_SIZE)
162                     {
163                       vlib_put_frame_to_node (vm, node_index, f);
164                       f = vlib_get_frame_to_node (vm, node_index);
165                       f->n_vectors = 0;
166                       to_next = vlib_frame_vector_args (f);
167                     }
168                   b0 = 0;
169                   bi0 = ~0;
170                 }
171             }
172         }
173     }
174
175   if (b0)
176     {
177       b0->current_length = next_offset;
178       b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
179       tp = vlib_buffer_get_current (b0);
180       ip = (ip4_header_t *) & tp->ip4;
181       udp = (udp_header_t *) (ip + 1);
182       h = &tp->ipfix.h;
183       s = &tp->ipfix.s;
184
185       s->set_id_length = ipfix_set_id_length (IOAM_FLOW_TEMPLATE_ID,
186                                               next_offset - (sizeof (*ip) +
187                                                              sizeof (*udp) +
188                                                              sizeof (*h)));
189       h->version_length =
190         version_length (next_offset - (sizeof (*ip) + sizeof (*udp)));
191
192       sum0 = ip->checksum;
193       old_l0 = ip->length;
194       new_l0 = clib_host_to_net_u16 ((u16) next_offset);
195       sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
196                              length /* changed member */ );
197
198       ip->checksum = ip_csum_fold (sum0);
199       ip->length = new_l0;
200       udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
201
202       udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
203       if (udp->checksum == 0)
204         udp->checksum = 0xffff;
205
206       ASSERT (ip->checksum == ip4_header_checksum (ip));
207
208       to_next[0] = bi0;
209       f->n_vectors++;
210       to_next++;
211
212       if (f->n_vectors == VLIB_FRAME_SIZE)
213         {
214           vlib_put_frame_to_node (vm, node_index, f);
215           f = vlib_get_frame_to_node (vm, node_index);
216           f->n_vectors = 0;
217           to_next = vlib_frame_vector_args (f);
218         }
219       b0 = 0;
220       bi0 = ~0;
221     }
222   return f;
223 }
224
225 clib_error_t *
226 udp_ping_flow_create (u8 del)
227 {
228   vnet_flow_report_add_del_args_t args;
229   int rv;
230   u32 domain_id = 0;
231   flow_report_main_t *frm = &flow_report_main;
232   u16 template_id;
233
234   memset (&args, 0, sizeof (args));
235   args.rewrite_callback = udp_ping_template_rewrite;
236   args.flow_data_callback = udp_ping_send_flows;
237   del ? (args.is_add = 0) : (args.is_add = 1);
238   args.domain_id = domain_id;
239   args.src_port = UDP_DST_PORT_ipfix;
240
241   rv = vnet_flow_report_add_del (frm, &args, &template_id);
242
243   switch (rv)
244     {
245     case 0:
246       break;
247     case VNET_API_ERROR_NO_SUCH_ENTRY:
248       return clib_error_return (0, "registration not found...");
249     default:
250       return clib_error_return (0, "vnet_flow_report_add_del returned %d",
251                                 rv);
252     }
253
254   return 0;
255 }
256
257 static clib_error_t *
258 set_udp_ping_export_command_fn (vlib_main_t * vm, unformat_input_t * input,
259                                 vlib_cli_command_t * cmd)
260 {
261   //int rv;
262   int is_add = 1;
263
264   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
265     {
266       if (unformat (input, "export-ipfix"))
267         is_add = 1;
268       else if (unformat (input, "disable"))
269         is_add = 0;
270       else
271         break;
272     }
273
274   if (is_add)
275     (void) udp_ping_flow_create (0);
276   else
277     (void) udp_ping_flow_create (1);
278
279   return 0;
280 }
281
282 /* *INDENT-OFF* */
283 VLIB_CLI_COMMAND (set_udp_ping_export_command, static) = {
284     .path = "set udp-ping export-ipfix",
285     .short_help = "set udp-ping export-ipfix [disable]",
286     .function = set_udp_ping_export_command_fn,
287 };
288 /* *INDENT-ON* */
289
290 clib_error_t *
291 udp_ping_flow_report_init (vlib_main_t * vm)
292 {
293   clib_error_t *error;
294
295   if ((error = vlib_call_init_function (vm, flow_report_init)))
296     return error;
297
298   return 0;
299 }
300
301 VLIB_INIT_FUNCTION (udp_ping_flow_report_init);
302
303 /*
304  * fd.io coding-style-patch-verification: ON
305  *
306  * Local Variables:
307  * eval: (c-set-style "gnu")
308  * End:
309  */