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