Line data Source code
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 <stddef.h>
17 :
18 : #include <vlib/vlib.h>
19 : #include <vlib/unix/unix.h>
20 : #include <vnet/fib/ip6_fib.h>
21 : #include <vnet/fib/ip4_fib.h>
22 : #include <vnet/ip/ip_sas.h>
23 : #include <vnet/ip/ip6_link.h>
24 : #include <vnet/ip/ip6_ll_table.h>
25 : #include <vnet/plugin/plugin.h>
26 : #include <vpp/app/version.h>
27 :
28 : #include <vnet/ip/icmp4.h>
29 : #include <ping/ping.h>
30 :
31 : ping_main_t ping_main;
32 :
33 : /**
34 : * @file
35 : * @brief IPv4 and IPv6 ICMP Ping.
36 : *
37 : * This file contains code to support IPv4 or IPv6 ICMP ECHO_REQUEST to
38 : * network hosts.
39 : *
40 : */
41 :
42 : typedef struct
43 : {
44 : u16 id;
45 : u16 seq;
46 : u32 cli_process_node;
47 : u8 is_ip6;
48 : } icmp_echo_trace_t;
49 :
50 :
51 : u8 *
52 2 : format_icmp_echo_trace (u8 * s, va_list * va)
53 : {
54 2 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
55 2 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
56 2 : icmp_echo_trace_t *t = va_arg (*va, icmp_echo_trace_t *);
57 :
58 : s =
59 2 : format (s, "ICMP%s echo id %d seq %d", t->is_ip6 ? "6" : "4", t->id,
60 2 : t->seq);
61 2 : if (t->cli_process_node == PING_CLI_UNKNOWN_NODE)
62 : {
63 2 : s = format (s, " (unknown)");
64 : }
65 : else
66 : {
67 0 : s = format (s, " send to cli node %d", t->cli_process_node);
68 : }
69 :
70 2 : return s;
71 : }
72 :
73 :
74 : static u8 *
75 0 : format_ip46_ping_result (u8 * s, va_list * args)
76 : {
77 0 : send_ip46_ping_result_t res = va_arg (*args, send_ip46_ping_result_t);
78 :
79 0 : switch (res)
80 : {
81 : #define _(v, n) case SEND_PING_##v: s = format(s, "%s", n);break;
82 0 : foreach_ip46_ping_result
83 : #undef _
84 : }
85 :
86 0 : return (s);
87 : }
88 :
89 :
90 : /*
91 : * Poor man's get-set-clear functions
92 : * for manipulation of icmp_id -> cli_process_id
93 : * mappings.
94 : *
95 : * There should normally be very few (0..1..2) of these
96 : * mappings, so the linear search is a good strategy.
97 : *
98 : * Make them thread-safe via a simple spinlock.
99 : *
100 : */
101 :
102 : static_always_inline int
103 1 : ip46_get_icmp_id_and_seq (vlib_main_t * vm, vlib_buffer_t * b0,
104 : u16 * out_icmp_id, u16 * out_icmp_seq, int is_ip6)
105 : {
106 : int l4_offset;
107 1 : if (is_ip6)
108 : {
109 1 : ip6_header_t *ip6 = vlib_buffer_get_current (b0);
110 1 : if (ip6->protocol != IP_PROTOCOL_ICMP6)
111 : {
112 0 : return 0;
113 : }
114 1 : l4_offset = sizeof (*ip6); // IPv6 EH
115 : }
116 : else
117 : {
118 0 : ip4_header_t *ip4 = vlib_buffer_get_current (b0);
119 0 : l4_offset = ip4_header_bytes (ip4);
120 :
121 : }
122 1 : icmp46_header_t *icmp46 = vlib_buffer_get_current (b0) + l4_offset;
123 1 : icmp46_echo_request_t *icmp46_echo = (icmp46_echo_request_t *) (icmp46 + 1);
124 :
125 1 : *out_icmp_id = clib_net_to_host_u16 (icmp46_echo->id);
126 1 : *out_icmp_seq = clib_net_to_host_u16 (icmp46_echo->seq);
127 1 : return 1;
128 : }
129 :
130 : /*
131 : * post the buffer to a given cli process node - the caller should forget bi0 after return.
132 : */
133 :
134 : static_always_inline void
135 0 : ip46_post_icmp_reply_event (vlib_main_t * vm, uword cli_process_id, u32 bi0,
136 : int is_ip6)
137 : {
138 0 : vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0);
139 0 : u64 nowts = clib_cpu_time_now ();
140 :
141 : /* Pass the timestamp to the cli_process thanks to the vnet_buffer unused metadata field */
142 :
143 : /* Camping on unused data... just ensure statically that there is enough space */
144 : STATIC_ASSERT (ARRAY_LEN (vnet_buffer (b0)->unused) *
145 : sizeof (vnet_buffer (b0)->unused[0]) > sizeof (nowts),
146 : "ping reply timestamp fits within remaining space of vnet_buffer unused data");
147 0 : u64 *pnowts = (void *) &vnet_buffer (b0)->unused[0];
148 0 : *pnowts = nowts;
149 :
150 0 : u32 event_id = is_ip6 ? PING_RESPONSE_IP6 : PING_RESPONSE_IP4;
151 0 : vlib_process_signal_event_mt (vm, cli_process_id, event_id, bi0);
152 0 : }
153 :
154 :
155 : static_always_inline void
156 1 : ip46_echo_reply_maybe_trace_buffer (vlib_main_t * vm,
157 : vlib_node_runtime_t * node,
158 : uword cli_process_id, u16 id, u16 seq,
159 : vlib_buffer_t * b0, int is_ip6)
160 : {
161 1 : if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
162 : {
163 1 : icmp_echo_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
164 1 : tr->id = id;
165 1 : tr->seq = seq;
166 1 : tr->cli_process_node = cli_process_id;
167 1 : tr->is_ip6 = is_ip6;
168 : }
169 1 : }
170 :
171 :
172 : static_always_inline uword
173 1 : ip46_icmp_echo_reply_inner_node_fn (vlib_main_t * vm,
174 : vlib_node_runtime_t * node,
175 : vlib_frame_t * frame, int do_trace,
176 : int is_ip6)
177 : {
178 : u32 n_left_from, *from, *to_next;
179 : icmp46_echo_reply_next_t next_index;
180 :
181 1 : from = vlib_frame_vector_args (frame);
182 1 : n_left_from = frame->n_vectors;
183 :
184 1 : next_index = node->cached_next_index;
185 :
186 2 : while (n_left_from > 0)
187 : {
188 : u32 n_left_to_next;
189 1 : vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
190 :
191 2 : while (n_left_from > 0 && n_left_to_next > 0)
192 : {
193 : u32 bi0;
194 : vlib_buffer_t *b0;
195 : /*
196 : * The buffers (replies) are either posted to the CLI thread
197 : * awaiting for them for subsequent analysis and disposal,
198 : * or are sent to the punt node.
199 : *
200 : * So the only "next" node is a punt, normally.
201 : */
202 1 : u32 next0 = ICMP46_ECHO_REPLY_NEXT_PUNT;
203 :
204 1 : bi0 = from[0];
205 1 : b0 = vlib_get_buffer (vm, bi0);
206 1 : from += 1;
207 1 : n_left_from -= 1;
208 :
209 1 : u16 icmp_id = ~0;
210 1 : u16 icmp_seq = ~0;
211 1 : uword cli_process_id = PING_CLI_UNKNOWN_NODE;
212 :
213 1 : if (ip46_get_icmp_id_and_seq (vm, b0, &icmp_id, &icmp_seq, is_ip6))
214 : {
215 1 : cli_process_id = get_cli_process_id_by_icmp_id_mt (vm, icmp_id);
216 : }
217 :
218 1 : if (do_trace)
219 1 : ip46_echo_reply_maybe_trace_buffer (vm, node, cli_process_id,
220 : icmp_id, icmp_seq, b0,
221 : is_ip6);
222 :
223 1 : if (~0 == cli_process_id)
224 : {
225 : /* no outstanding requests for this reply, punt */
226 : /* speculatively enqueue b0 to the current next frame */
227 1 : to_next[0] = bi0;
228 1 : to_next += 1;
229 1 : n_left_to_next -= 1;
230 : /* verify speculative enqueue, maybe switch current next frame */
231 1 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
232 : to_next, n_left_to_next,
233 : bi0, next0);
234 : }
235 : else
236 : {
237 : /* Post the buffer to CLI thread. It will take care of freeing it. */
238 0 : ip46_post_icmp_reply_event (vm, cli_process_id, bi0, is_ip6);
239 : }
240 : }
241 1 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
242 : }
243 1 : return frame->n_vectors;
244 : }
245 :
246 : /*
247 : * select "with-trace" or "without-trace" codepaths upfront.
248 : */
249 : static_always_inline uword
250 1 : ip46_icmp_echo_reply_outer_node_fn (vlib_main_t * vm,
251 : vlib_node_runtime_t * node,
252 : vlib_frame_t * frame, int is_ip6)
253 : {
254 1 : if (node->flags & VLIB_NODE_FLAG_TRACE)
255 1 : return ip46_icmp_echo_reply_inner_node_fn (vm, node, frame,
256 : 1 /* do_trace */ , is_ip6);
257 : else
258 0 : return ip46_icmp_echo_reply_inner_node_fn (vm, node, frame,
259 : 0 /* do_trace */ , is_ip6);
260 : }
261 :
262 : static uword
263 0 : ip4_icmp_echo_reply_node_fn (vlib_main_t * vm,
264 : vlib_node_runtime_t * node, vlib_frame_t * frame)
265 : {
266 0 : return ip46_icmp_echo_reply_outer_node_fn (vm, node, frame,
267 : 0 /* is_ip6 */ );
268 : }
269 :
270 : static uword
271 1 : ip6_icmp_echo_reply_node_fn (vlib_main_t * vm,
272 : vlib_node_runtime_t * node, vlib_frame_t * frame)
273 : {
274 1 : return ip46_icmp_echo_reply_outer_node_fn (vm, node, frame,
275 : 1 /* is_ip6 */ );
276 : }
277 :
278 : /* *INDENT-OFF* */
279 47832 : VLIB_REGISTER_NODE (ip6_icmp_echo_reply_node, static) =
280 : {
281 : .function = ip6_icmp_echo_reply_node_fn,
282 : .name = "ip6-icmp-echo-reply",
283 : .vector_size = sizeof (u32),
284 : .format_trace = format_icmp_echo_trace,
285 : .n_next_nodes = ICMP46_ECHO_REPLY_N_NEXT,
286 : .next_nodes = {
287 : [ICMP46_ECHO_REPLY_NEXT_DROP] = "ip6-drop",
288 : [ICMP46_ECHO_REPLY_NEXT_PUNT] = "ip6-punt",
289 : },
290 : };
291 :
292 47832 : VLIB_REGISTER_NODE (ip4_icmp_echo_reply_node, static) =
293 : {
294 : .function = ip4_icmp_echo_reply_node_fn,
295 : .name = "ip4-icmp-echo-reply",
296 : .vector_size = sizeof (u32),
297 : .format_trace = format_icmp_echo_trace,
298 : .n_next_nodes = ICMP46_ECHO_REPLY_N_NEXT,
299 : .next_nodes = {
300 : [ICMP46_ECHO_REPLY_NEXT_DROP] = "ip4-drop",
301 : [ICMP46_ECHO_REPLY_NEXT_PUNT] = "ip4-punt",
302 : },
303 : };
304 : /* *INDENT-ON* */
305 :
306 : static uword
307 4132 : ip4_icmp_echo_request (vlib_main_t * vm,
308 : vlib_node_runtime_t * node, vlib_frame_t * frame)
309 : {
310 4132 : uword n_packets = frame->n_vectors;
311 : u32 *from, *to_next;
312 : u32 n_left_from, n_left_to_next, next;
313 4132 : ip4_main_t *i4m = &ip4_main;
314 : u16 *fragment_ids, *fid;
315 4132 : u8 host_config_ttl = i4m->host_config.ttl;
316 :
317 4132 : from = vlib_frame_vector_args (frame);
318 4132 : n_left_from = n_packets;
319 4132 : next = node->cached_next_index;
320 :
321 4132 : if (node->flags & VLIB_NODE_FLAG_TRACE)
322 3392 : vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
323 : /* stride */ 1,
324 : sizeof (icmp_input_trace_t));
325 :
326 : /* Get random fragment IDs for replies. */
327 4132 : fid = fragment_ids = clib_random_buffer_get_data (&vm->random_buffer,
328 : n_packets *
329 : sizeof (fragment_ids[0]));
330 :
331 8264 : while (n_left_from > 0)
332 : {
333 4132 : vlib_get_next_frame (vm, node, next, to_next, n_left_to_next);
334 :
335 65119 : while (n_left_from > 2 && n_left_to_next > 2)
336 : {
337 : vlib_buffer_t *p0, *p1;
338 : ip4_header_t *ip0, *ip1;
339 : icmp46_header_t *icmp0, *icmp1;
340 : u32 bi0, src0, dst0;
341 : u32 bi1, src1, dst1;
342 : ip_csum_t sum0, sum1;
343 :
344 60987 : bi0 = to_next[0] = from[0];
345 60987 : bi1 = to_next[1] = from[1];
346 :
347 60987 : from += 2;
348 60987 : n_left_from -= 2;
349 60987 : to_next += 2;
350 60987 : n_left_to_next -= 2;
351 :
352 60987 : p0 = vlib_get_buffer (vm, bi0);
353 60987 : p1 = vlib_get_buffer (vm, bi1);
354 60987 : ip0 = vlib_buffer_get_current (p0);
355 60987 : ip1 = vlib_buffer_get_current (p1);
356 60987 : icmp0 = ip4_next_header (ip0);
357 60987 : icmp1 = ip4_next_header (ip1);
358 :
359 60987 : vnet_buffer (p0)->sw_if_index[VLIB_RX] =
360 60987 : vnet_main.local_interface_sw_if_index;
361 60987 : vnet_buffer (p1)->sw_if_index[VLIB_RX] =
362 60987 : vnet_main.local_interface_sw_if_index;
363 :
364 : /* Update ICMP checksum. */
365 60987 : sum0 = icmp0->checksum;
366 60987 : sum1 = icmp1->checksum;
367 :
368 60987 : ASSERT (icmp0->type == ICMP4_echo_request);
369 60987 : ASSERT (icmp1->type == ICMP4_echo_request);
370 60987 : sum0 = ip_csum_update (sum0, ICMP4_echo_request, ICMP4_echo_reply,
371 : icmp46_header_t, type);
372 60987 : sum1 = ip_csum_update (sum1, ICMP4_echo_request, ICMP4_echo_reply,
373 : icmp46_header_t, type);
374 60987 : icmp0->type = ICMP4_echo_reply;
375 60987 : icmp1->type = ICMP4_echo_reply;
376 :
377 60987 : icmp0->checksum = ip_csum_fold (sum0);
378 60987 : icmp1->checksum = ip_csum_fold (sum1);
379 :
380 60987 : src0 = ip0->src_address.data_u32;
381 60987 : src1 = ip1->src_address.data_u32;
382 60987 : dst0 = ip0->dst_address.data_u32;
383 60987 : dst1 = ip1->dst_address.data_u32;
384 :
385 : /* Swap source and destination address.
386 : Does not change checksum. */
387 60987 : ip0->src_address.data_u32 = dst0;
388 60987 : ip1->src_address.data_u32 = dst1;
389 60987 : ip0->dst_address.data_u32 = src0;
390 60987 : ip1->dst_address.data_u32 = src1;
391 :
392 : /* Update IP checksum. */
393 60987 : sum0 = ip0->checksum;
394 60987 : sum1 = ip1->checksum;
395 :
396 60987 : sum0 = ip_csum_update (sum0, ip0->ttl, host_config_ttl,
397 : ip4_header_t, ttl);
398 60987 : sum1 = ip_csum_update (sum1, ip1->ttl, host_config_ttl,
399 : ip4_header_t, ttl);
400 60987 : ip0->ttl = host_config_ttl;
401 60987 : ip1->ttl = host_config_ttl;
402 :
403 : /* New fragment id. */
404 60987 : sum0 = ip_csum_update (sum0, ip0->fragment_id, fid[0],
405 : ip4_header_t, fragment_id);
406 60987 : sum1 = ip_csum_update (sum1, ip1->fragment_id, fid[1],
407 : ip4_header_t, fragment_id);
408 60987 : ip0->fragment_id = fid[0];
409 60987 : ip1->fragment_id = fid[1];
410 60987 : fid += 2;
411 :
412 60987 : ip0->checksum = ip_csum_fold (sum0);
413 60987 : ip1->checksum = ip_csum_fold (sum1);
414 :
415 60987 : ASSERT (ip4_header_checksum_is_valid (ip0));
416 60987 : ASSERT (ip4_header_checksum_is_valid (ip1));
417 :
418 60987 : p0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
419 60987 : p1->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
420 : }
421 :
422 9618 : while (n_left_from > 0 && n_left_to_next > 0)
423 : {
424 : vlib_buffer_t *p0;
425 : ip4_header_t *ip0;
426 : icmp46_header_t *icmp0;
427 : u32 bi0, src0, dst0;
428 : ip_csum_t sum0;
429 :
430 5486 : bi0 = to_next[0] = from[0];
431 :
432 5486 : from += 1;
433 5486 : n_left_from -= 1;
434 5486 : to_next += 1;
435 5486 : n_left_to_next -= 1;
436 :
437 5486 : p0 = vlib_get_buffer (vm, bi0);
438 5486 : ip0 = vlib_buffer_get_current (p0);
439 5486 : icmp0 = ip4_next_header (ip0);
440 :
441 5486 : vnet_buffer (p0)->sw_if_index[VLIB_RX] =
442 5486 : vnet_main.local_interface_sw_if_index;
443 :
444 : /* Update ICMP checksum. */
445 5486 : sum0 = icmp0->checksum;
446 :
447 5486 : ASSERT (icmp0->type == ICMP4_echo_request);
448 5486 : sum0 = ip_csum_update (sum0, ICMP4_echo_request, ICMP4_echo_reply,
449 : icmp46_header_t, type);
450 5486 : icmp0->type = ICMP4_echo_reply;
451 5486 : icmp0->checksum = ip_csum_fold (sum0);
452 :
453 5486 : src0 = ip0->src_address.data_u32;
454 5486 : dst0 = ip0->dst_address.data_u32;
455 5486 : ip0->src_address.data_u32 = dst0;
456 5486 : ip0->dst_address.data_u32 = src0;
457 :
458 : /* Update IP checksum. */
459 5486 : sum0 = ip0->checksum;
460 :
461 5486 : sum0 = ip_csum_update (sum0, ip0->ttl, host_config_ttl,
462 : ip4_header_t, ttl);
463 5486 : ip0->ttl = host_config_ttl;
464 :
465 5486 : sum0 = ip_csum_update (sum0, ip0->fragment_id, fid[0],
466 : ip4_header_t, fragment_id);
467 5486 : ip0->fragment_id = fid[0];
468 5486 : fid += 1;
469 :
470 5486 : ip0->checksum = ip_csum_fold (sum0);
471 :
472 5486 : ASSERT (ip4_header_checksum_is_valid (ip0));
473 :
474 5486 : p0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
475 : }
476 :
477 4132 : vlib_put_next_frame (vm, node, next, n_left_to_next);
478 : }
479 :
480 4132 : vlib_error_count (vm, ip4_icmp_input_node.index,
481 4132 : ICMP4_ERROR_ECHO_REPLIES_SENT, frame->n_vectors);
482 :
483 4132 : return frame->n_vectors;
484 : }
485 :
486 : static u8 *
487 85312 : format_icmp_input_trace (u8 * s, va_list * va)
488 : {
489 85312 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
490 85312 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
491 85312 : icmp_input_trace_t *t = va_arg (*va, icmp_input_trace_t *);
492 :
493 85312 : s = format (s, "%U",
494 85312 : format_ip4_header, t->packet_data, sizeof (t->packet_data));
495 :
496 85312 : return s;
497 : }
498 :
499 : /* *INDENT-OFF* */
500 47832 : VLIB_REGISTER_NODE (ip4_icmp_echo_request_node,static) = {
501 : .function = ip4_icmp_echo_request,
502 : .name = "ip4-icmp-echo-request",
503 :
504 : .vector_size = sizeof (u32),
505 :
506 : .format_trace = format_icmp_input_trace,
507 :
508 : .n_next_nodes = 1,
509 : .next_nodes = {
510 : [0] = "ip4-load-balance",
511 : },
512 : };
513 : /* *INDENT-ON* */
514 :
515 : typedef enum
516 : {
517 : ICMP6_ECHO_REQUEST_NEXT_LOOKUP,
518 : ICMP6_ECHO_REQUEST_NEXT_OUTPUT,
519 : ICMP6_ECHO_REQUEST_N_NEXT,
520 : } icmp6_echo_request_next_t;
521 :
522 : static uword
523 620 : ip6_icmp_echo_request (vlib_main_t *vm, vlib_node_runtime_t *node,
524 : vlib_frame_t *frame)
525 : {
526 : u32 *from, *to_next;
527 : u32 n_left_from, n_left_to_next, next_index;
528 620 : ip6_main_t *im = &ip6_main;
529 :
530 620 : from = vlib_frame_vector_args (frame);
531 620 : n_left_from = frame->n_vectors;
532 620 : next_index = node->cached_next_index;
533 :
534 620 : if (node->flags & VLIB_NODE_FLAG_TRACE)
535 420 : vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
536 : /* stride */ 1,
537 : sizeof (icmp6_input_trace_t));
538 :
539 1240 : while (n_left_from > 0)
540 : {
541 620 : vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
542 :
543 16570 : while (n_left_from > 2 && n_left_to_next > 2)
544 : {
545 : vlib_buffer_t *p0, *p1;
546 : ip6_header_t *ip0, *ip1;
547 : icmp46_header_t *icmp0, *icmp1;
548 : ip6_address_t tmp0, tmp1;
549 : ip_csum_t sum0, sum1;
550 : u32 bi0, bi1;
551 : u32 fib_index0, fib_index1;
552 15950 : u32 next0 = ICMP6_ECHO_REQUEST_NEXT_LOOKUP;
553 15950 : u32 next1 = ICMP6_ECHO_REQUEST_NEXT_LOOKUP;
554 :
555 15950 : bi0 = to_next[0] = from[0];
556 15950 : bi1 = to_next[1] = from[1];
557 :
558 15950 : from += 2;
559 15950 : n_left_from -= 2;
560 15950 : to_next += 2;
561 15950 : n_left_to_next -= 2;
562 :
563 15950 : p0 = vlib_get_buffer (vm, bi0);
564 15950 : p1 = vlib_get_buffer (vm, bi1);
565 15950 : ip0 = vlib_buffer_get_current (p0);
566 15950 : ip1 = vlib_buffer_get_current (p1);
567 15950 : icmp0 = ip6_next_header (ip0);
568 15950 : icmp1 = ip6_next_header (ip1);
569 :
570 : /* Check icmp type to echo reply and update icmp checksum. */
571 15950 : sum0 = icmp0->checksum;
572 15950 : sum1 = icmp1->checksum;
573 :
574 15950 : ASSERT (icmp0->type == ICMP6_echo_request);
575 15950 : ASSERT (icmp1->type == ICMP6_echo_request);
576 15950 : sum0 = ip_csum_update (sum0, ICMP6_echo_request, ICMP6_echo_reply,
577 : icmp46_header_t, type);
578 15950 : sum1 = ip_csum_update (sum1, ICMP6_echo_request, ICMP6_echo_reply,
579 : icmp46_header_t, type);
580 :
581 15950 : icmp0->checksum = ip_csum_fold (sum0);
582 15950 : icmp1->checksum = ip_csum_fold (sum1);
583 :
584 15950 : icmp0->type = ICMP6_echo_reply;
585 15950 : icmp1->type = ICMP6_echo_reply;
586 :
587 : /* Swap source and destination address. */
588 15950 : tmp0 = ip0->src_address;
589 15950 : tmp1 = ip1->src_address;
590 :
591 15950 : ip0->src_address = ip0->dst_address;
592 15950 : ip1->src_address = ip1->dst_address;
593 :
594 15950 : ip0->dst_address = tmp0;
595 15950 : ip1->dst_address = tmp1;
596 :
597 : /* New hop count. */
598 15950 : ip0->hop_limit = im->host_config.ttl;
599 15950 : ip1->hop_limit = im->host_config.ttl;
600 :
601 15950 : if (ip6_address_is_link_local_unicast (&ip0->src_address) &&
602 0 : !ip6_address_is_link_local_unicast (&ip0->dst_address))
603 : {
604 0 : fib_index0 = vec_elt (im->fib_index_by_sw_if_index,
605 : vnet_buffer (p0)->sw_if_index[VLIB_RX]);
606 0 : vnet_buffer (p0)->sw_if_index[VLIB_TX] = fib_index0;
607 : }
608 15950 : if (ip6_address_is_link_local_unicast (&ip1->src_address) &&
609 0 : !ip6_address_is_link_local_unicast (&ip1->dst_address))
610 : {
611 0 : fib_index1 = vec_elt (im->fib_index_by_sw_if_index,
612 : vnet_buffer (p1)->sw_if_index[VLIB_RX]);
613 0 : vnet_buffer (p1)->sw_if_index[VLIB_TX] = fib_index1;
614 : }
615 15950 : p0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
616 15950 : p1->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
617 :
618 : /* verify speculative enqueues, maybe switch current next frame */
619 : /* if next0==next1==next_index then nothing special needs to be done
620 : */
621 15950 : vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
622 : n_left_to_next, bi0, bi1, next0,
623 : next1);
624 : }
625 :
626 1343 : while (n_left_from > 0 && n_left_to_next > 0)
627 : {
628 : vlib_buffer_t *p0;
629 : ip6_header_t *ip0;
630 : icmp46_header_t *icmp0;
631 : u32 bi0;
632 : ip6_address_t tmp0;
633 : ip_csum_t sum0;
634 : u32 fib_index0;
635 723 : u32 next0 = ICMP6_ECHO_REQUEST_NEXT_LOOKUP;
636 :
637 723 : bi0 = to_next[0] = from[0];
638 :
639 723 : from += 1;
640 723 : n_left_from -= 1;
641 723 : to_next += 1;
642 723 : n_left_to_next -= 1;
643 :
644 723 : p0 = vlib_get_buffer (vm, bi0);
645 723 : ip0 = vlib_buffer_get_current (p0);
646 723 : icmp0 = ip6_next_header (ip0);
647 :
648 : /* Check icmp type to echo reply and update icmp checksum. */
649 723 : sum0 = icmp0->checksum;
650 :
651 723 : ASSERT (icmp0->type == ICMP6_echo_request);
652 723 : sum0 = ip_csum_update (sum0, ICMP6_echo_request, ICMP6_echo_reply,
653 : icmp46_header_t, type);
654 :
655 723 : icmp0->checksum = ip_csum_fold (sum0);
656 :
657 723 : icmp0->type = ICMP6_echo_reply;
658 :
659 : /* Swap source and destination address. */
660 723 : tmp0 = ip0->src_address;
661 723 : ip0->src_address = ip0->dst_address;
662 723 : ip0->dst_address = tmp0;
663 :
664 723 : ip0->hop_limit = im->host_config.ttl;
665 :
666 731 : if (ip6_address_is_link_local_unicast (&ip0->src_address) &&
667 8 : !ip6_address_is_link_local_unicast (&ip0->dst_address))
668 : {
669 : /* if original packet was to the link local, then the
670 : * fib index is that of the LL table, we can't use that
671 : * to foward the response if the new destination
672 : * is global, so reset to the fib index of the link.
673 : * In other case, the fib index we need has been written
674 : * to the buffer already. */
675 1 : fib_index0 = vec_elt (im->fib_index_by_sw_if_index,
676 : vnet_buffer (p0)->sw_if_index[VLIB_RX]);
677 1 : vnet_buffer (p0)->sw_if_index[VLIB_TX] = fib_index0;
678 : }
679 723 : p0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
680 : /* Verify speculative enqueue, maybe switch current next frame */
681 723 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
682 : n_left_to_next, bi0, next0);
683 : }
684 :
685 620 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
686 : }
687 :
688 620 : vlib_error_count (vm, ip6_icmp_input_node.index,
689 620 : ICMP6_ERROR_ECHO_REPLIES_SENT, frame->n_vectors);
690 :
691 620 : return frame->n_vectors;
692 : }
693 :
694 47832 : VLIB_REGISTER_NODE (ip6_icmp_echo_request_node,static) = {
695 : .function = ip6_icmp_echo_request,
696 : .name = "ip6-icmp-echo-request",
697 :
698 : .vector_size = sizeof (u32),
699 :
700 : .format_trace = format_icmp6_input_trace,
701 :
702 : .n_next_nodes = ICMP6_ECHO_REQUEST_N_NEXT,
703 : .next_nodes = {
704 : [ICMP6_ECHO_REQUEST_NEXT_LOOKUP] = "ip6-lookup",
705 : [ICMP6_ECHO_REQUEST_NEXT_OUTPUT] = "interface-output",
706 : },
707 : };
708 :
709 : /*
710 : * A swarm of address-family agnostic helper functions
711 : * for building and sending the ICMP echo request.
712 : *
713 : * Deliberately mostly "static" rather than "static inline"
714 : * so one can trace them sanely if needed in debugger, if needed.
715 : *
716 : */
717 :
718 : static_always_inline u8
719 1680 : get_icmp_echo_payload_byte (int offset)
720 : {
721 1680 : return (offset % 256);
722 : }
723 :
724 : /* Fill in the ICMP ECHO structure, return the safety-checked and possibly shrunk data_len */
725 : static u16
726 28 : init_icmp46_echo_request (vlib_main_t * vm, vlib_buffer_t * b0,
727 : int l4_header_offset,
728 : icmp46_echo_request_t * icmp46_echo, u16 seq_host,
729 : u16 id_host, u64 now, u16 data_len)
730 : {
731 : int i;
732 :
733 :
734 28 : int l34_len =
735 28 : l4_header_offset + sizeof (icmp46_header_t) +
736 : offsetof (icmp46_echo_request_t, data);
737 28 : int max_data_len = vlib_buffer_get_default_data_size (vm) - l34_len;
738 :
739 28 : int first_buf_data_len = data_len < max_data_len ? data_len : max_data_len;
740 :
741 28 : int payload_offset = 0;
742 1708 : for (i = 0; i < first_buf_data_len; i++)
743 1680 : icmp46_echo->data[i] = get_icmp_echo_payload_byte (payload_offset++);
744 :
745 : /* inspired by vlib_buffer_add_data */
746 28 : vlib_buffer_t *hb = b0;
747 28 : int remaining_data_len = data_len - first_buf_data_len;
748 28 : while (remaining_data_len)
749 : {
750 0 : int this_buf_data_len =
751 0 : remaining_data_len <
752 0 : vlib_buffer_get_default_data_size (vm) ? remaining_data_len :
753 0 : vlib_buffer_get_default_data_size (vm);
754 0 : int n_alloc = vlib_buffer_alloc (vm, &b0->next_buffer, 1);
755 0 : if (n_alloc < 1)
756 : {
757 : /* That is how much we have so far - return it... */
758 0 : return (data_len - remaining_data_len);
759 : }
760 0 : b0->flags |= VLIB_BUFFER_NEXT_PRESENT;
761 : /* move on to the newly acquired buffer */
762 0 : b0 = vlib_get_buffer (vm, b0->next_buffer);
763 : /* initialize the data */
764 0 : for (i = 0; i < this_buf_data_len; i++)
765 : {
766 0 : b0->data[i] = get_icmp_echo_payload_byte (payload_offset++);
767 : }
768 0 : b0->current_length = this_buf_data_len;
769 0 : b0->current_data = 0;
770 0 : remaining_data_len -= this_buf_data_len;
771 : }
772 28 : hb->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
773 28 : hb->current_length = l34_len + first_buf_data_len;
774 28 : hb->total_length_not_including_first_buffer = data_len - first_buf_data_len;
775 :
776 28 : icmp46_echo->time_sent = now;
777 28 : icmp46_echo->seq = clib_host_to_net_u16 (seq_host);
778 28 : icmp46_echo->id = clib_host_to_net_u16 (id_host);
779 28 : return data_len;
780 : }
781 :
782 :
783 : static u32
784 28 : ip46_fib_index_from_table_id (u32 table_id, int is_ip6)
785 : {
786 28 : u32 fib_index = is_ip6 ?
787 28 : ip6_fib_index_from_table_id (table_id) :
788 28 : ip4_fib_index_from_table_id (table_id);
789 28 : return fib_index;
790 : }
791 :
792 : static fib_node_index_t
793 28 : ip46_fib_table_lookup_host (u32 fib_index, ip46_address_t * pa46, int is_ip6)
794 : {
795 28 : fib_node_index_t fib_entry_index = is_ip6 ?
796 28 : ip6_fib_table_lookup (fib_index, &pa46->ip6, 128) :
797 28 : ip4_fib_table_lookup (ip4_fib_get (fib_index), &pa46->ip4, 32);
798 28 : return fib_entry_index;
799 : }
800 :
801 : static u32
802 28 : ip46_get_resolving_interface (u32 fib_index, ip46_address_t * pa46,
803 : int is_ip6)
804 : {
805 28 : u32 sw_if_index = ~0;
806 28 : if (~0 != fib_index)
807 : {
808 : fib_node_index_t fib_entry_index;
809 28 : fib_entry_index = ip46_fib_table_lookup_host (fib_index, pa46, is_ip6);
810 28 : sw_if_index = fib_entry_get_resolving_interface (fib_entry_index);
811 : }
812 28 : return sw_if_index;
813 : }
814 :
815 : static u32
816 0 : ip46_fib_table_get_index_for_sw_if_index (u32 sw_if_index, int is_ip6,
817 : ip46_address_t *pa46)
818 : {
819 0 : if (is_ip6)
820 : {
821 0 : if (ip6_address_is_link_local_unicast (&pa46->ip6))
822 0 : return ip6_ll_fib_get (sw_if_index);
823 0 : return ip6_fib_table_get_index_for_sw_if_index (sw_if_index);
824 : }
825 0 : return ip4_fib_table_get_index_for_sw_if_index (sw_if_index);
826 : }
827 :
828 :
829 : static int
830 28 : ip46_fill_l3_header (ip46_address_t * pa46, vlib_buffer_t * b0, int is_ip6)
831 : {
832 28 : if (is_ip6)
833 : {
834 0 : ip6_header_t *ip6 = vlib_buffer_get_current (b0);
835 : /* Fill in ip6 header fields */
836 0 : ip6->ip_version_traffic_class_and_flow_label =
837 0 : clib_host_to_net_u32 (0x6 << 28);
838 0 : ip6->payload_length = 0; /* will be set later */
839 0 : ip6->protocol = IP_PROTOCOL_ICMP6;
840 0 : ip6->hop_limit = 255;
841 0 : ip6->dst_address = pa46->ip6;
842 0 : ip6->src_address = pa46->ip6;
843 0 : return (sizeof (ip6_header_t));
844 : }
845 : else
846 : {
847 28 : ip4_header_t *ip4 = vlib_buffer_get_current (b0);
848 : /* Fill in ip4 header fields */
849 28 : ip4->checksum = 0;
850 28 : ip4->ip_version_and_header_length = 0x45;
851 28 : ip4->tos = 0;
852 28 : ip4->length = 0; /* will be set later */
853 28 : ip4->fragment_id = 0;
854 28 : ip4->flags_and_fragment_offset = 0;
855 28 : ip4->ttl = 0xff;
856 28 : ip4->protocol = IP_PROTOCOL_ICMP;
857 28 : ip4->src_address = pa46->ip4;
858 28 : ip4->dst_address = pa46->ip4;
859 28 : return (sizeof (ip4_header_t));
860 : }
861 : }
862 :
863 : static bool
864 28 : ip46_set_src_address (u32 sw_if_index, vlib_buffer_t * b0, int is_ip6)
865 : {
866 28 : bool res = false;
867 :
868 28 : if (is_ip6)
869 : {
870 0 : ip6_header_t *ip6 = vlib_buffer_get_current (b0);
871 :
872 0 : res = ip6_sas_by_sw_if_index (sw_if_index, &ip6->dst_address,
873 : &ip6->src_address);
874 : }
875 : else
876 : {
877 28 : ip4_header_t *ip4 = vlib_buffer_get_current (b0);
878 :
879 28 : res = ip4_sas_by_sw_if_index (sw_if_index, &ip4->dst_address,
880 : &ip4->src_address);
881 : }
882 28 : return res;
883 : }
884 :
885 : static void
886 0 : ip46_print_buffer_src_address (vlib_main_t * vm, vlib_buffer_t * b0,
887 : int is_ip6)
888 : {
889 : void *format_addr_func;
890 : void *paddr;
891 0 : if (is_ip6)
892 : {
893 0 : ip6_header_t *ip6 = vlib_buffer_get_current (b0);
894 0 : format_addr_func = format_ip6_address;
895 0 : paddr = &ip6->src_address;
896 : }
897 : else
898 : {
899 0 : ip4_header_t *ip4 = vlib_buffer_get_current (b0);
900 0 : format_addr_func = format_ip4_address;
901 0 : paddr = &ip4->src_address;
902 : }
903 0 : vlib_cli_output (vm, "Source address: %U ", format_addr_func, paddr);
904 0 : }
905 :
906 : static u16
907 28 : ip46_fill_icmp_request_at (vlib_main_t * vm, int l4_offset, u16 seq_host,
908 : u16 id_host, u16 data_len, vlib_buffer_t * b0,
909 : int is_ip6)
910 : {
911 28 : icmp46_header_t *icmp46 = vlib_buffer_get_current (b0) + l4_offset;
912 :
913 28 : icmp46->type = is_ip6 ? ICMP6_echo_request : ICMP4_echo_request;
914 28 : icmp46->code = 0;
915 28 : icmp46->checksum = 0;
916 :
917 28 : icmp46_echo_request_t *icmp46_echo = (icmp46_echo_request_t *) (icmp46 + 1);
918 :
919 : data_len =
920 28 : init_icmp46_echo_request (vm, b0, l4_offset, icmp46_echo, seq_host,
921 : id_host, clib_cpu_time_now (), data_len);
922 28 : return data_len;
923 : }
924 :
925 :
926 : /* Compute ICMP4 checksum with multibuffer support. */
927 : u16
928 28 : ip4_icmp_compute_checksum (vlib_main_t * vm, vlib_buffer_t * p0,
929 : ip4_header_t * ip0)
930 : {
931 : ip_csum_t sum0;
932 : u32 ip_header_length, payload_length_host_byte_order;
933 : u32 n_this_buffer, n_bytes_left, n_ip_bytes_this_buffer;
934 : u16 sum16;
935 : void *data_this_buffer;
936 :
937 28 : ip_header_length = ip4_header_bytes (ip0);
938 28 : payload_length_host_byte_order =
939 28 : clib_net_to_host_u16 (ip0->length) - ip_header_length;
940 :
941 : /* ICMP4 checksum does not include the IP header */
942 28 : sum0 = 0;
943 :
944 28 : n_bytes_left = n_this_buffer = payload_length_host_byte_order;
945 28 : data_this_buffer = (void *) ip0 + ip_header_length;
946 28 : n_ip_bytes_this_buffer =
947 28 : p0->current_length - (((u8 *) ip0 - p0->data) - p0->current_data);
948 28 : if (n_this_buffer + ip_header_length > n_ip_bytes_this_buffer)
949 : {
950 0 : n_this_buffer = n_ip_bytes_this_buffer > ip_header_length ?
951 0 : n_ip_bytes_this_buffer - ip_header_length : 0;
952 : }
953 : while (1)
954 : {
955 28 : sum0 = ip_incremental_checksum (sum0, data_this_buffer, n_this_buffer);
956 28 : n_bytes_left -= n_this_buffer;
957 28 : if (n_bytes_left == 0)
958 28 : break;
959 :
960 0 : ASSERT (p0->flags & VLIB_BUFFER_NEXT_PRESENT);
961 0 : p0 = vlib_get_buffer (vm, p0->next_buffer);
962 0 : data_this_buffer = vlib_buffer_get_current (p0);
963 0 : n_this_buffer = p0->current_length;
964 : }
965 :
966 28 : sum16 = ~ip_csum_fold (sum0);
967 :
968 28 : return sum16;
969 : }
970 :
971 :
972 : static void
973 28 : ip46_fix_len_and_csum (vlib_main_t * vm, int l4_offset, u16 data_len,
974 : vlib_buffer_t * b0, int is_ip6)
975 : {
976 28 : u16 payload_length =
977 : data_len + sizeof (icmp46_header_t) + offsetof (icmp46_echo_request_t,
978 : data);
979 28 : u16 total_length = payload_length + l4_offset;
980 28 : icmp46_header_t *icmp46 = vlib_buffer_get_current (b0) + l4_offset;
981 28 : icmp46->checksum = 0;
982 :
983 28 : if (is_ip6)
984 : {
985 0 : ip6_header_t *ip6 = vlib_buffer_get_current (b0);
986 0 : ip6->payload_length = clib_host_to_net_u16 (payload_length);
987 :
988 0 : int bogus_length = 0;
989 0 : icmp46->checksum =
990 0 : ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip6, &bogus_length);
991 : }
992 : else
993 : {
994 28 : ip4_header_t *ip4 = vlib_buffer_get_current (b0);
995 28 : ip4->length = clib_host_to_net_u16 (total_length);
996 :
997 28 : ip4->checksum = ip4_header_checksum (ip4);
998 28 : icmp46->checksum = ip4_icmp_compute_checksum (vm, b0, ip4);
999 : }
1000 28 : }
1001 :
1002 : static u16
1003 56 : at_most_a_frame (u32 count)
1004 : {
1005 56 : return count > VLIB_FRAME_SIZE ? VLIB_FRAME_SIZE : count;
1006 : }
1007 :
1008 : static int
1009 28 : ip46_enqueue_packet (vlib_main_t *vm, vlib_buffer_t *b0, u32 burst,
1010 : u32 lookup_node_index)
1011 : {
1012 28 : vlib_frame_t *f = 0;
1013 28 : int n_sent = 0;
1014 :
1015 : u16 n_to_send;
1016 :
1017 : /*
1018 : * Enqueue the packet, possibly as one or more frames of copies to make
1019 : * bursts. We enqueue b0 as the very last buffer, when there is no possibility
1020 : * for error in vlib_buffer_copy, so as to allow the caller to free it
1021 : * in case we encounter the error in the middle of the loop.
1022 : */
1023 56 : for (n_to_send = at_most_a_frame (burst), burst -= n_to_send; n_to_send > 0;
1024 28 : n_to_send = at_most_a_frame (burst), burst -= n_to_send)
1025 : {
1026 28 : f = vlib_get_frame_to_node (vm, lookup_node_index);
1027 : /* f can not be NULL here - frame allocation failure causes panic */
1028 :
1029 28 : u32 *to_next = vlib_frame_vector_args (f);
1030 28 : f->n_vectors = n_to_send;
1031 :
1032 38 : while (n_to_send > 1)
1033 : {
1034 10 : vlib_buffer_t *b0copy = vlib_buffer_copy (vm, b0);
1035 10 : if (PREDICT_FALSE (b0copy == NULL))
1036 0 : goto ship_and_ret;
1037 10 : *to_next++ = vlib_get_buffer_index (vm, b0copy);
1038 10 : n_to_send--;
1039 10 : n_sent++;
1040 : }
1041 :
1042 : /* n_to_send is guaranteed to equal 1 here */
1043 28 : if (burst > 0)
1044 : {
1045 : /* not the last burst, so still make a copy for the last buffer */
1046 0 : vlib_buffer_t *b0copy = vlib_buffer_copy (vm, b0);
1047 0 : if (PREDICT_FALSE (b0copy == NULL))
1048 0 : goto ship_and_ret;
1049 0 : n_to_send--;
1050 0 : *to_next++ = vlib_get_buffer_index (vm, b0copy);
1051 : }
1052 : else
1053 : {
1054 : /* put the original buffer as the last one of an error-free run */
1055 28 : *to_next++ = vlib_get_buffer_index (vm, b0);
1056 : }
1057 28 : vlib_put_frame_to_node (vm, lookup_node_index, f);
1058 28 : n_sent += f->n_vectors;
1059 : }
1060 28 : return n_sent;
1061 : /*
1062 : * We reach here in case we already enqueued one or more buffers
1063 : * and maybe one or more frames but could not make more copies.
1064 : * There is an outstanding frame - so ship it and return.
1065 : * Caller will have to free the b0 in this case, since
1066 : * we did not enqueue it here yet.
1067 : */
1068 0 : ship_and_ret:
1069 0 : ASSERT (n_to_send <= f->n_vectors);
1070 0 : f->n_vectors -= n_to_send;
1071 0 : n_sent += f->n_vectors;
1072 0 : vlib_put_frame_to_node (vm, lookup_node_index, f);
1073 0 : return n_sent;
1074 : }
1075 :
1076 :
1077 : /*
1078 : * An address-family agnostic ping send function.
1079 : */
1080 :
1081 : #define ERROR_OUT(e) do { err = e; goto done; } while (0)
1082 :
1083 : static send_ip46_ping_result_t
1084 28 : send_ip46_ping (vlib_main_t * vm,
1085 : u32 table_id,
1086 : ip46_address_t * pa46,
1087 : u32 sw_if_index,
1088 : u16 seq_host, u16 id_host, u16 data_len, u32 burst,
1089 : u8 verbose, int is_ip6)
1090 : {
1091 28 : int err = SEND_PING_OK;
1092 28 : u32 bi0 = 0;
1093 28 : int n_buf0 = 0;
1094 : vlib_buffer_t *b0;
1095 :
1096 28 : n_buf0 = vlib_buffer_alloc (vm, &bi0, 1);
1097 28 : if (n_buf0 < 1)
1098 0 : ERROR_OUT (SEND_PING_ALLOC_FAIL);
1099 :
1100 28 : b0 = vlib_get_buffer (vm, bi0);
1101 :
1102 : /*
1103 : * if the user did not provide a source interface,
1104 : * perform a resolution and use an interface
1105 : * via which it succeeds.
1106 : */
1107 : u32 fib_index;
1108 28 : if (~0 == sw_if_index)
1109 : {
1110 28 : fib_index = ip46_fib_index_from_table_id (table_id, is_ip6);
1111 28 : sw_if_index = ip46_get_resolving_interface (fib_index, pa46, is_ip6);
1112 : }
1113 : else
1114 : fib_index =
1115 0 : ip46_fib_table_get_index_for_sw_if_index (sw_if_index, is_ip6, pa46);
1116 :
1117 28 : if (~0 == fib_index)
1118 0 : ERROR_OUT (SEND_PING_NO_TABLE);
1119 28 : if (~0 == sw_if_index)
1120 0 : ERROR_OUT (SEND_PING_NO_INTERFACE);
1121 :
1122 28 : vnet_buffer (b0)->sw_if_index[VLIB_RX] = sw_if_index;
1123 28 : vnet_buffer (b0)->sw_if_index[VLIB_TX] = fib_index;
1124 :
1125 28 : int l4_header_offset = ip46_fill_l3_header (pa46, b0, is_ip6);
1126 :
1127 : /* set the src address in the buffer */
1128 28 : if (!ip46_set_src_address (sw_if_index, b0, is_ip6))
1129 0 : ERROR_OUT (SEND_PING_NO_SRC_ADDRESS);
1130 28 : if (verbose)
1131 0 : ip46_print_buffer_src_address (vm, b0, is_ip6);
1132 :
1133 : data_len =
1134 28 : ip46_fill_icmp_request_at (vm, l4_header_offset, seq_host, id_host,
1135 : data_len, b0, is_ip6);
1136 :
1137 28 : ip46_fix_len_and_csum (vm, l4_header_offset, data_len, b0, is_ip6);
1138 :
1139 28 : u32 node_index = ip6_lookup_node.index;
1140 28 : if (is_ip6)
1141 : {
1142 0 : if (pa46->ip6.as_u32[0] == clib_host_to_net_u32 (0xff020000))
1143 : {
1144 0 : node_index = ip6_rewrite_mcast_node.index;
1145 0 : vnet_buffer (b0)->sw_if_index[VLIB_RX] = sw_if_index;
1146 0 : vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index;
1147 0 : vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
1148 0 : ip6_link_get_mcast_adj (sw_if_index);
1149 : }
1150 : }
1151 : else
1152 : {
1153 28 : node_index = ip4_lookup_node.index;
1154 : }
1155 28 : int n_sent = ip46_enqueue_packet (vm, b0, burst, node_index);
1156 28 : if (n_sent < burst)
1157 0 : err = SEND_PING_NO_BUFFERS;
1158 :
1159 28 : done:
1160 28 : if (err != SEND_PING_OK)
1161 : {
1162 0 : if (n_buf0 > 0)
1163 0 : vlib_buffer_free (vm, &bi0, 1);
1164 : }
1165 28 : return err;
1166 : }
1167 :
1168 : send_ip46_ping_result_t
1169 0 : send_ip6_ping (vlib_main_t *vm, u32 table_id, ip6_address_t *pa6,
1170 : u32 sw_if_index, u16 seq_host, u16 id_host, u16 data_len,
1171 : u32 burst, u8 verbose)
1172 : {
1173 : ip46_address_t target;
1174 0 : target.ip6 = *pa6;
1175 0 : return send_ip46_ping (vm, table_id, &target, sw_if_index, seq_host,
1176 : id_host, data_len, burst, verbose, 1 /* is_ip6 */ );
1177 : }
1178 :
1179 : send_ip46_ping_result_t
1180 28 : send_ip4_ping (vlib_main_t *vm, u32 table_id, ip4_address_t *pa4,
1181 : u32 sw_if_index, u16 seq_host, u16 id_host, u16 data_len,
1182 : u32 burst, u8 verbose)
1183 : {
1184 : ip46_address_t target;
1185 28 : ip46_address_set_ip4 (&target, pa4);
1186 28 : return send_ip46_ping (vm, table_id, &target, sw_if_index, seq_host,
1187 : id_host, data_len, burst, verbose, 0 /* is_ip6 */ );
1188 : }
1189 :
1190 : static void
1191 0 : print_ip46_icmp_reply (vlib_main_t * vm, u32 bi0, int is_ip6)
1192 : {
1193 0 : vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0);
1194 : int l4_offset;
1195 : void *paddr;
1196 : void *format_addr_func;
1197 : u16 payload_length;
1198 : u8 ttl;
1199 0 : if (is_ip6)
1200 : {
1201 0 : ip6_header_t *ip6 = vlib_buffer_get_current (b0);
1202 0 : paddr = (void *) &ip6->src_address;
1203 0 : format_addr_func = (void *) format_ip6_address;
1204 0 : ttl = ip6->hop_limit;
1205 0 : l4_offset = sizeof (ip6_header_t); // FIXME - EH processing ?
1206 0 : payload_length = clib_net_to_host_u16 (ip6->payload_length);
1207 : }
1208 : else
1209 : {
1210 0 : ip4_header_t *ip4 = vlib_buffer_get_current (b0);
1211 0 : paddr = (void *) &ip4->src_address;
1212 0 : format_addr_func = (void *) format_ip4_address;
1213 0 : ttl = ip4->ttl;
1214 0 : l4_offset = ip4_header_bytes (ip4);
1215 0 : payload_length =
1216 0 : clib_net_to_host_u16 (ip4->length) + ip4_header_bytes (ip4);
1217 : }
1218 0 : icmp46_header_t *icmp = vlib_buffer_get_current (b0) + l4_offset;
1219 0 : icmp46_echo_request_t *icmp_echo = (icmp46_echo_request_t *) (icmp + 1);
1220 0 : u64 *dataplane_ts = (u64 *) & vnet_buffer (b0)->unused[0];
1221 :
1222 0 : f64 clocks_per_second = ((f64) vm->clib_time.clocks_per_second);
1223 0 : f64 rtt =
1224 0 : ((f64) (*dataplane_ts - icmp_echo->time_sent)) / clocks_per_second;
1225 :
1226 0 : vlib_cli_output (vm,
1227 : "%d bytes from %U: icmp_seq=%d ttl=%d time=%.4f ms",
1228 : payload_length,
1229 : format_addr_func,
1230 : paddr,
1231 0 : clib_host_to_net_u16 (icmp_echo->seq), ttl, rtt * 1000.0);
1232 0 : }
1233 :
1234 : /*
1235 : * Perform the ping run with the given parameters in the current CLI process.
1236 : * Depending on whether pa4 or pa6 is set, runs IPv4 or IPv6 ping.
1237 : * The amusing side effect is of course if both are set, then both pings are sent.
1238 : * This behavior can be used to ping a dualstack host over IPv4 and IPv6 at once.
1239 : */
1240 :
1241 : static void
1242 5 : run_ping_ip46_address (vlib_main_t * vm, u32 table_id, ip4_address_t * pa4,
1243 : ip6_address_t * pa6, u32 sw_if_index,
1244 : f64 ping_interval, u32 ping_repeat, u32 data_len,
1245 : u32 ping_burst, u32 verbose)
1246 : {
1247 : int i;
1248 5 : uword curr_proc = vlib_current_process (vm);
1249 5 : u32 n_replies = 0;
1250 5 : u32 n_requests = 0;
1251 : u16 icmp_id;
1252 :
1253 : static u32 rand_seed = 0;
1254 :
1255 5 : if (PREDICT_FALSE (!rand_seed))
1256 1 : rand_seed = random_default_seed ();
1257 :
1258 5 : icmp_id = random_u32 (&rand_seed) & 0xffff;
1259 :
1260 5 : while (~0 != get_cli_process_id_by_icmp_id_mt (vm, icmp_id))
1261 : {
1262 0 : vlib_cli_output (vm, "ICMP ID collision at %d, incrementing", icmp_id);
1263 0 : icmp_id++;
1264 : }
1265 :
1266 5 : set_cli_process_id_by_icmp_id_mt (vm, icmp_id, curr_proc);
1267 :
1268 29 : for (i = 1; i <= ping_repeat; i++)
1269 : {
1270 24 : send_ip46_ping_result_t res = SEND_PING_OK;
1271 : f64 sleep_interval;
1272 24 : f64 time_ping_sent = vlib_time_now (vm);
1273 24 : if (pa6)
1274 : {
1275 0 : res = send_ip6_ping (vm, table_id,
1276 : pa6, sw_if_index, i, icmp_id,
1277 : data_len, ping_burst, verbose);
1278 0 : if (SEND_PING_OK == res)
1279 0 : n_requests += ping_burst;
1280 : else
1281 0 : vlib_cli_output (vm, "Failed: %U", format_ip46_ping_result, res);
1282 : }
1283 24 : if (pa4)
1284 : {
1285 24 : res = send_ip4_ping (vm, table_id, pa4,
1286 : sw_if_index, i, icmp_id, data_len,
1287 : ping_burst, verbose);
1288 24 : if (SEND_PING_OK == res)
1289 24 : n_requests += ping_burst;
1290 : else
1291 0 : vlib_cli_output (vm, "Failed: %U", format_ip46_ping_result, res);
1292 : }
1293 :
1294 : /* Collect and print the responses until it is time to send a next ping */
1295 :
1296 177 : while ((i <= ping_repeat)
1297 177 : &&
1298 177 : ((sleep_interval =
1299 177 : time_ping_sent + ping_interval - vlib_time_now (vm)) > 0.0))
1300 : {
1301 153 : uword event_type, *event_data = 0;
1302 153 : vlib_process_wait_for_event_or_clock (vm, sleep_interval);
1303 153 : event_type = vlib_process_get_events (vm, &event_data);
1304 153 : switch (event_type)
1305 : {
1306 153 : case ~0: /* no events => timeout */
1307 153 : break;
1308 0 : case PING_RESPONSE_IP6:
1309 : /* fall-through */
1310 : case PING_RESPONSE_IP4:
1311 : {
1312 : int ii;
1313 0 : int is_ip6 = (event_type == PING_RESPONSE_IP6);
1314 0 : for (ii = 0; ii < vec_len (event_data); ii++)
1315 : {
1316 0 : u32 bi0 = event_data[ii];
1317 0 : print_ip46_icmp_reply (vm, bi0, is_ip6);
1318 0 : n_replies++;
1319 0 : if (0 != bi0)
1320 0 : vlib_buffer_free (vm, &bi0, 1);
1321 : }
1322 : }
1323 0 : break;
1324 0 : case UNIX_CLI_PROCESS_EVENT_READ_READY:
1325 : case UNIX_CLI_PROCESS_EVENT_QUIT:
1326 : /* someone pressed a key, abort */
1327 0 : vlib_cli_output (vm, "Aborted due to a keypress.");
1328 0 : goto double_break;
1329 : }
1330 153 : vec_free (event_data);
1331 : }
1332 : }
1333 5 : double_break:
1334 5 : vlib_cli_output (vm, "\n");
1335 : {
1336 5 : float loss =
1337 : (0 ==
1338 5 : n_requests) ? 0 : 100.0 * ((float) n_requests -
1339 5 : (float) n_replies) / (float) n_requests;
1340 5 : vlib_cli_output (vm,
1341 : "Statistics: %u sent, %u received, %f%% packet loss\n",
1342 : n_requests, n_replies, loss);
1343 5 : clear_cli_process_id_by_icmp_id_mt (vm, icmp_id);
1344 : }
1345 5 : }
1346 :
1347 :
1348 :
1349 : static clib_error_t *
1350 5 : ping_ip_address (vlib_main_t * vm,
1351 : unformat_input_t * input, vlib_cli_command_t * cmd)
1352 : {
1353 : ip4_address_t a4;
1354 : ip6_address_t a6;
1355 5 : clib_error_t *error = 0;
1356 5 : u32 ping_repeat = 5;
1357 5 : u32 ping_burst = 1;
1358 : u8 ping_ip4, ping_ip6;
1359 5 : vnet_main_t *vnm = vnet_get_main ();
1360 5 : u32 data_len = PING_DEFAULT_DATA_LEN;
1361 5 : u32 verbose = 0;
1362 5 : f64 ping_interval = PING_DEFAULT_INTERVAL;
1363 : u32 sw_if_index, table_id;
1364 :
1365 5 : table_id = 0;
1366 5 : ping_ip4 = ping_ip6 = 0;
1367 5 : sw_if_index = ~0;
1368 :
1369 5 : if (unformat (input, "%U", unformat_ip4_address, &a4))
1370 : {
1371 5 : ping_ip4 = 1;
1372 : }
1373 0 : else if (unformat (input, "%U", unformat_ip6_address, &a6))
1374 : {
1375 0 : ping_ip6 = 1;
1376 : }
1377 0 : else if (unformat (input, "ipv4"))
1378 : {
1379 0 : if (unformat (input, "%U", unformat_ip4_address, &a4))
1380 : {
1381 0 : ping_ip4 = 1;
1382 : }
1383 : else
1384 : {
1385 : error =
1386 0 : clib_error_return (0,
1387 : "expecting IPv4 address but got `%U'",
1388 : format_unformat_error, input);
1389 : }
1390 : }
1391 0 : else if (unformat (input, "ipv6"))
1392 : {
1393 0 : if (unformat (input, "%U", unformat_ip6_address, &a6))
1394 : {
1395 0 : ping_ip6 = 1;
1396 : }
1397 : else
1398 : {
1399 : error =
1400 0 : clib_error_return (0,
1401 : "expecting IPv6 address but got `%U'",
1402 : format_unformat_error, input);
1403 : }
1404 : }
1405 : else
1406 : {
1407 : error =
1408 0 : clib_error_return (0,
1409 : "expecting IP4/IP6 address `%U'. Usage: ping <addr> [source <intf>] [size <datasz>] [repeat <count>] [verbose]",
1410 : format_unformat_error, input);
1411 0 : goto done;
1412 : }
1413 :
1414 : /* allow for the second AF in the same ping */
1415 5 : if (!ping_ip4 && (unformat (input, "ipv4")))
1416 : {
1417 0 : if (unformat (input, "%U", unformat_ip4_address, &a4))
1418 : {
1419 0 : ping_ip4 = 1;
1420 : }
1421 : }
1422 5 : else if (!ping_ip6 && (unformat (input, "ipv6")))
1423 : {
1424 0 : if (unformat (input, "%U", unformat_ip6_address, &a6))
1425 : {
1426 0 : ping_ip6 = 1;
1427 : }
1428 : }
1429 :
1430 : /* parse the rest of the parameters in a cycle */
1431 15 : while (!unformat_eof (input, NULL))
1432 : {
1433 10 : if (unformat (input, "source"))
1434 : {
1435 0 : if (!unformat_user
1436 : (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
1437 : {
1438 : error =
1439 0 : clib_error_return (0,
1440 : "unknown interface `%U'",
1441 : format_unformat_error, input);
1442 0 : goto done;
1443 : }
1444 : }
1445 10 : else if (unformat (input, "size"))
1446 : {
1447 0 : if (!unformat (input, "%u", &data_len))
1448 : {
1449 : error =
1450 0 : clib_error_return (0,
1451 : "expecting size but got `%U'",
1452 : format_unformat_error, input);
1453 0 : goto done;
1454 : }
1455 0 : if (data_len > PING_MAXIMUM_DATA_SIZE)
1456 : {
1457 : error =
1458 0 : clib_error_return (0,
1459 : "%d is bigger than maximum allowed payload size %d",
1460 : data_len, PING_MAXIMUM_DATA_SIZE);
1461 0 : goto done;
1462 : }
1463 : }
1464 10 : else if (unformat (input, "table-id"))
1465 : {
1466 0 : if (!unformat (input, "%u", &table_id))
1467 : {
1468 : error =
1469 0 : clib_error_return (0,
1470 : "expecting table-id but got `%U'",
1471 : format_unformat_error, input);
1472 0 : goto done;
1473 : }
1474 : }
1475 10 : else if (unformat (input, "interval"))
1476 : {
1477 5 : if (!unformat (input, "%f", &ping_interval))
1478 : {
1479 : error =
1480 0 : clib_error_return (0,
1481 : "expecting interval (floating point number) got `%U'",
1482 : format_unformat_error, input);
1483 0 : goto done;
1484 : }
1485 : }
1486 5 : else if (unformat (input, "repeat"))
1487 : {
1488 4 : if (!unformat (input, "%u", &ping_repeat))
1489 : {
1490 : error =
1491 0 : clib_error_return (0,
1492 : "expecting repeat count but got `%U'",
1493 : format_unformat_error, input);
1494 0 : goto done;
1495 : }
1496 : }
1497 1 : else if (unformat (input, "burst"))
1498 : {
1499 1 : if (!unformat (input, "%u", &ping_burst))
1500 : {
1501 : error =
1502 0 : clib_error_return (0,
1503 : "expecting burst count but got `%U'",
1504 : format_unformat_error, input);
1505 0 : goto done;
1506 : }
1507 : }
1508 0 : else if (unformat (input, "verbose"))
1509 : {
1510 0 : verbose = 1;
1511 : }
1512 : else
1513 : {
1514 0 : error = clib_error_return (0, "unknown input `%U'",
1515 : format_unformat_error, input);
1516 0 : goto done;
1517 : }
1518 : }
1519 :
1520 : /*
1521 : * Operationally, one won't (and shouldn't) need to send more than a frame worth of pings.
1522 : * But it may be handy during the debugging.
1523 : */
1524 :
1525 : #ifdef CLIB_DEBUG
1526 : #define MAX_PING_BURST (10*VLIB_FRAME_SIZE)
1527 : #else
1528 : #define MAX_PING_BURST (VLIB_FRAME_SIZE)
1529 : #endif
1530 :
1531 5 : if (ping_burst < 1 || ping_burst > MAX_PING_BURST)
1532 0 : return clib_error_return (0, "burst size must be between 1 and %u",
1533 : MAX_PING_BURST);
1534 :
1535 5 : run_ping_ip46_address (vm, table_id, ping_ip4 ? &a4 : NULL,
1536 : ping_ip6 ? &a6 : NULL, sw_if_index, ping_interval,
1537 : ping_repeat, data_len, ping_burst, verbose);
1538 5 : done:
1539 5 : return error;
1540 : }
1541 :
1542 : /*?
1543 : * This command sends an ICMP ECHO_REQUEST to network hosts. The address
1544 : * can be an IPv4 or IPv6 address (or both at the same time).
1545 : *
1546 : * @cliexpar
1547 : * @parblock
1548 : * Example of how ping an IPv4 address:
1549 : * @cliexstart{ping 172.16.1.2 source GigabitEthernet2/0/0 repeat 2}
1550 : * 64 bytes from 172.16.1.2: icmp_seq=1 ttl=64 time=.1090 ms
1551 : * 64 bytes from 172.16.1.2: icmp_seq=2 ttl=64 time=.0914 ms
1552 : *
1553 : * Statistics: 2 sent, 2 received, 0% packet loss
1554 : * @cliexend
1555 : *
1556 : * Example of how ping both an IPv4 address and IPv6 address at the same time:
1557 : * @cliexstart{ping 172.16.1.2 ipv6 fe80::24a5:f6ff:fe9c:3a36 source GigabitEthernet2/0/0 repeat 2 verbose}
1558 : * Adjacency index: 10, sw_if_index: 1
1559 : * Adj: ip6-discover-neighbor
1560 : * Adj Interface: 0
1561 : * Forced set interface: 1
1562 : * Adjacency index: 0, sw_if_index: 4294967295
1563 : * Adj: ip4-miss
1564 : * Adj Interface: 0
1565 : * Forced set interface: 1
1566 : * Source address: 172.16.1.1
1567 : * 64 bytes from 172.16.1.2: icmp_seq=1 ttl=64 time=.1899 ms
1568 : * Adjacency index: 10, sw_if_index: 1
1569 : * Adj: ip6-discover-neighbor
1570 : * Adj Interface: 0
1571 : * Forced set interface: 1
1572 : * Adjacency index: 0, sw_if_index: 4294967295
1573 : * Adj: ip4-miss
1574 : * Adj Interface: 0
1575 : * Forced set interface: 1
1576 : * Source address: 172.16.1.1
1577 : * 64 bytes from 172.16.1.2: icmp_seq=2 ttl=64 time=.0910 ms
1578 : *
1579 : * Statistics: 4 sent, 2 received, 50% packet loss
1580 : * @cliexend
1581 : * @endparblock
1582 : ?*/
1583 : /* *INDENT-OFF* */
1584 51989 : VLIB_CLI_COMMAND (ping_command, static) =
1585 : {
1586 : .path = "ping",
1587 : .function = ping_ip_address,
1588 : .short_help = "ping {<ip-addr> | ipv4 <ip4-addr> | ipv6 <ip6-addr>}"
1589 : " [ipv4 <ip4-addr> | ipv6 <ip6-addr>] [source <interface>]"
1590 : " [size <pktsize:60>] [interval <sec:1>] [repeat <cnt:5>] [table-id <id:0>]"
1591 : " [burst <count:1>] [verbose]",
1592 : .is_mp_safe = 1,
1593 : };
1594 : /* *INDENT-ON* */
1595 :
1596 : static clib_error_t *
1597 575 : ping_cli_init (vlib_main_t * vm)
1598 : {
1599 575 : vlib_thread_main_t *tm = vlib_get_thread_main ();
1600 575 : ping_main_t *pm = &ping_main;
1601 :
1602 575 : pm->ip6_main = &ip6_main;
1603 575 : pm->ip4_main = &ip4_main;
1604 575 : icmp6_register_type (vm, ICMP6_echo_reply, ip6_icmp_echo_reply_node.index);
1605 575 : ip4_icmp_register_type (vm, ICMP4_echo_reply,
1606 : ip4_icmp_echo_reply_node.index);
1607 575 : if (tm->n_vlib_mains > 1)
1608 37 : clib_spinlock_init (&pm->ping_run_check_lock);
1609 :
1610 575 : ip4_icmp_register_type (vm, ICMP4_echo_request,
1611 : ip4_icmp_echo_request_node.index);
1612 575 : icmp6_register_type (vm, ICMP6_echo_request,
1613 : ip6_icmp_echo_request_node.index);
1614 :
1615 575 : ping_plugin_api_hookup (vm);
1616 :
1617 575 : return 0;
1618 : }
1619 :
1620 1151 : VLIB_INIT_FUNCTION (ping_cli_init);
1621 :
1622 : /* *INDENT-OFF* */
1623 : VLIB_PLUGIN_REGISTER () = {
1624 : .version = VPP_BUILD_VER,
1625 : .description = "Ping (ping)",
1626 : };
1627 : /* *INDENT-ON* */
1628 :
1629 : /*
1630 : * fd.io coding-style-patch-verification: ON
1631 : *
1632 : * Local Variables:
1633 : * eval: (c-set-style "gnu")
1634 : * End:
1635 : */
|