Type: feature stats of the like from: https://datatracker.ietf.org/doc/html/draft-ietf-rtgwg-arp-yang-model-03#section-4 Signed-off-by: Neale Ranns <neale@graphiant.com> Change-Id: Icb1bf4f6f7e6ccc2f44b0008d4774b61cae96184
803 lines
22 KiB
C
803 lines
22 KiB
C
/*
|
|
* Copyright (c) 2021 Cisco and/or its affiliates.
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <vlib/vlib.h>
|
|
#include <vlib/unix/unix.h>
|
|
#include <vnet/plugin/plugin.h>
|
|
#include <vpp/app/version.h>
|
|
#include <vnet/ip-neighbor/ip4_neighbor.h>
|
|
#include <vnet/ip-neighbor/ip6_neighbor.h>
|
|
#include <arping/arping.h>
|
|
|
|
arping_main_t arping_main;
|
|
|
|
#define foreach_arping_error _ (NONE, "no error")
|
|
|
|
typedef enum
|
|
{
|
|
#define _(f, s) ARPING_ERROR_##f,
|
|
foreach_arping_error
|
|
#undef _
|
|
ARPING_N_ERROR,
|
|
} arping__error_t;
|
|
|
|
static char *arping_error_strings[] = {
|
|
#define _(n, s) s,
|
|
foreach_arping_error
|
|
#undef _
|
|
};
|
|
|
|
#define foreach_arping \
|
|
_ (DROP, "error-drop") \
|
|
_ (IO, "interface-output")
|
|
|
|
typedef enum
|
|
{
|
|
#define _(sym, str) ARPING_NEXT_##sym,
|
|
foreach_arping
|
|
#undef _
|
|
ARPING_N_NEXT,
|
|
} arping_next_t;
|
|
|
|
typedef struct arping_trace_t_
|
|
{
|
|
u32 sw_if_index;
|
|
u16 arp_opcode;
|
|
ethernet_arp_ip4_over_ethernet_address_t reply;
|
|
} arping_trace_t;
|
|
|
|
typedef enum
|
|
{
|
|
#define _(sym, str) ARPING6_NEXT_##sym,
|
|
foreach_arping
|
|
#undef _
|
|
ARPING6_N_NEXT,
|
|
} arping6_next_t;
|
|
|
|
typedef CLIB_PACKED (struct {
|
|
mac_address_t mac;
|
|
ip6_address_t ip6;
|
|
}) ethernet_arp_ip6_over_ethernet_address_t;
|
|
|
|
typedef struct arping6_trace_t_
|
|
{
|
|
u32 sw_if_index;
|
|
u8 type;
|
|
ethernet_arp_ip6_over_ethernet_address_t reply;
|
|
} arping6_trace_t;
|
|
|
|
/* packet trace format function */
|
|
static u8 *
|
|
format_arping_trace (u8 *s, va_list *args)
|
|
{
|
|
CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
|
|
CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
|
|
arping_trace_t *t = va_arg (*args, arping_trace_t *);
|
|
|
|
s = format (s, "sw-if-index: %u, opcode: %U, from %U (%U)", t->sw_if_index,
|
|
format_ethernet_arp_opcode, t->arp_opcode, format_mac_address,
|
|
&t->reply.mac, format_ip4_address, &t->reply.ip4);
|
|
|
|
return s;
|
|
}
|
|
|
|
static u8 *
|
|
format_arping6_trace (u8 *s, va_list *args)
|
|
{
|
|
CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
|
|
CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
|
|
arping6_trace_t *t = va_arg (*args, arping6_trace_t *);
|
|
|
|
s = format (s, "sw-if-index: %u, type: %u, from %U (%U)", t->sw_if_index,
|
|
t->type, format_mac_address, &t->reply.mac, format_ip6_address,
|
|
&t->reply.ip6);
|
|
|
|
return s;
|
|
}
|
|
|
|
VLIB_NODE_FN (arping_input_node)
|
|
(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
|
|
{
|
|
u32 n_left_from, *from, *to_next, n_left_to_next;
|
|
arping_next_t next_index;
|
|
arping_main_t *am = &arping_main;
|
|
|
|
next_index = node->cached_next_index;
|
|
n_left_from = frame->n_vectors;
|
|
from = vlib_frame_vector_args (frame);
|
|
|
|
while (n_left_from > 0)
|
|
{
|
|
vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
|
|
|
|
while (n_left_from >= 2 && n_left_to_next >= 2)
|
|
{
|
|
u32 next0, next1, bi0, bi1;
|
|
vlib_buffer_t *b0, *b1;
|
|
ethernet_arp_header_t *arp0, *arp1;
|
|
u32 sw_if_index0, sw_if_index1;
|
|
arping_intf_t *aif0, *aif1;
|
|
|
|
bi0 = to_next[0] = from[0];
|
|
bi1 = to_next[1] = from[1];
|
|
|
|
from += 2;
|
|
n_left_from -= 2;
|
|
to_next += 2;
|
|
n_left_to_next -= 2;
|
|
|
|
next0 = next1 = ARPING_NEXT_DROP;
|
|
|
|
b0 = vlib_get_buffer (vm, bi0);
|
|
b1 = vlib_get_buffer (vm, bi1);
|
|
|
|
arp0 = vlib_buffer_get_current (b0);
|
|
arp1 = vlib_buffer_get_current (b1);
|
|
|
|
vnet_feature_next (&next0, b0);
|
|
vnet_feature_next (&next1, b1);
|
|
|
|
sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
|
|
sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
|
|
|
|
if (PREDICT_TRUE (arp0->opcode ==
|
|
clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply)))
|
|
{
|
|
aif0 = am->interfaces[sw_if_index0];
|
|
if (PREDICT_TRUE (aif0->address.ip.ip4.as_u32 ==
|
|
arp0->ip4_over_ethernet[0].ip4.as_u32))
|
|
{
|
|
aif0->recv.from4.ip4.as_u32 =
|
|
arp0->ip4_over_ethernet[0].ip4.as_u32;
|
|
clib_memcpy_fast (&aif0->recv.from4.mac,
|
|
&arp0->ip4_over_ethernet[0].mac, 6);
|
|
aif0->reply_count++;
|
|
}
|
|
}
|
|
if (PREDICT_TRUE (arp1->opcode ==
|
|
clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply)))
|
|
{
|
|
aif1 = am->interfaces[sw_if_index1];
|
|
if (PREDICT_TRUE (aif1->address.ip.ip4.as_u32 ==
|
|
arp1->ip4_over_ethernet[0].ip4.as_u32))
|
|
{
|
|
aif1->recv.from4.ip4.as_u32 =
|
|
arp1->ip4_over_ethernet[0].ip4.as_u32;
|
|
clib_memcpy_fast (&aif1->recv.from4.mac,
|
|
&arp1->ip4_over_ethernet[0].mac, 6);
|
|
aif1->reply_count++;
|
|
}
|
|
}
|
|
|
|
if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
|
|
{
|
|
arping_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
|
|
t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
|
|
t->arp_opcode = clib_host_to_net_u16 (arp0->opcode);
|
|
t->reply.ip4.as_u32 = arp0->ip4_over_ethernet[0].ip4.as_u32;
|
|
clib_memcpy_fast (&t->reply.mac, &arp0->ip4_over_ethernet[0].mac,
|
|
6);
|
|
}
|
|
if (PREDICT_FALSE ((b1->flags & VLIB_BUFFER_IS_TRACED)))
|
|
{
|
|
arping_trace_t *t = vlib_add_trace (vm, node, b1, sizeof (*t));
|
|
t->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX];
|
|
t->arp_opcode = clib_host_to_net_u16 (arp1->opcode);
|
|
t->reply.ip4.as_u32 = arp1->ip4_over_ethernet[0].ip4.as_u32;
|
|
clib_memcpy_fast (&t->reply.mac, &arp1->ip4_over_ethernet[0].mac,
|
|
6);
|
|
}
|
|
|
|
vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
|
|
n_left_to_next, bi0, bi1, next0,
|
|
next1);
|
|
}
|
|
|
|
while (n_left_from > 0 && n_left_to_next > 0)
|
|
{
|
|
u32 next0, bi0;
|
|
vlib_buffer_t *b0;
|
|
ethernet_arp_header_t *arp0;
|
|
arping_intf_t *aif0;
|
|
u32 sw_if_index0;
|
|
|
|
bi0 = to_next[0] = from[0];
|
|
|
|
from += 1;
|
|
n_left_from -= 1;
|
|
to_next += 1;
|
|
n_left_to_next -= 1;
|
|
next0 = ARPING_NEXT_DROP;
|
|
|
|
b0 = vlib_get_buffer (vm, bi0);
|
|
arp0 = vlib_buffer_get_current (b0);
|
|
|
|
vnet_feature_next (&next0, b0);
|
|
|
|
sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
|
|
|
|
if (PREDICT_TRUE (arp0->opcode ==
|
|
clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply)))
|
|
{
|
|
aif0 = am->interfaces[sw_if_index0];
|
|
if (PREDICT_TRUE (aif0->address.ip.ip4.as_u32 ==
|
|
arp0->ip4_over_ethernet[0].ip4.as_u32))
|
|
{
|
|
aif0->recv.from4.ip4.as_u32 =
|
|
arp0->ip4_over_ethernet[0].ip4.as_u32;
|
|
clib_memcpy_fast (&aif0->recv.from4.mac,
|
|
&arp0->ip4_over_ethernet[0].mac, 6);
|
|
aif0->reply_count++;
|
|
}
|
|
}
|
|
|
|
if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
|
|
{
|
|
arping_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
|
|
t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
|
|
t->arp_opcode = clib_host_to_net_u16 (arp0->opcode);
|
|
t->reply.ip4.as_u32 = arp0->ip4_over_ethernet[0].ip4.as_u32;
|
|
clib_memcpy_fast (&t->reply.mac, &arp0->ip4_over_ethernet[0].mac,
|
|
6);
|
|
}
|
|
|
|
vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
|
|
n_left_to_next, bi0, next0);
|
|
}
|
|
|
|
vlib_put_next_frame (vm, node, next_index, n_left_to_next);
|
|
}
|
|
|
|
return frame->n_vectors;
|
|
}
|
|
|
|
VLIB_REGISTER_NODE (arping_input_node) =
|
|
{
|
|
.name = "arping-input",.vector_size = sizeof (u32),.format_trace =
|
|
format_arping_trace,.type = VLIB_NODE_TYPE_INTERNAL,.n_errors =
|
|
ARPING_N_ERROR,.error_strings = arping_error_strings,.n_next_nodes =
|
|
ARPING_N_NEXT,.next_nodes =
|
|
{
|
|
[ARPING_NEXT_DROP] = "error-drop",[ARPING_NEXT_IO] = "interface-output",}
|
|
,};
|
|
|
|
VNET_FEATURE_INIT (arping_feat_node, static) = {
|
|
.arc_name = "arp",
|
|
.node_name = "arping-input",
|
|
.runs_before = VNET_FEATURES ("arp-reply"),
|
|
};
|
|
|
|
VLIB_NODE_FN (arping6_input_node)
|
|
(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
|
|
{
|
|
u32 n_left_from, *from, *to_next, n_left_to_next;
|
|
arping_next_t next_index;
|
|
arping_main_t *am = &arping_main;
|
|
|
|
next_index = node->cached_next_index;
|
|
n_left_from = frame->n_vectors;
|
|
from = vlib_frame_vector_args (frame);
|
|
|
|
while (n_left_from > 0)
|
|
{
|
|
vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
|
|
|
|
while (n_left_from >= 2 && n_left_to_next >= 2)
|
|
{
|
|
u32 next0, next1, bi0, bi1;
|
|
vlib_buffer_t *b0, *b1;
|
|
ip6_header_t *ip60, *ip61;
|
|
u32 sw_if_index0, sw_if_index1;
|
|
arping_intf_t *aif0, *aif1;
|
|
icmp6_neighbor_solicitation_or_advertisement_header_t *sol_adv0,
|
|
*sol_adv1;
|
|
icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
|
|
*lladdr0,
|
|
*lladdr1;
|
|
|
|
bi0 = to_next[0] = from[0];
|
|
bi1 = to_next[1] = from[1];
|
|
|
|
from += 2;
|
|
n_left_from -= 2;
|
|
to_next += 2;
|
|
n_left_to_next -= 2;
|
|
|
|
next0 = next1 = ARPING6_NEXT_DROP;
|
|
|
|
b0 = vlib_get_buffer (vm, bi0);
|
|
b1 = vlib_get_buffer (vm, bi1);
|
|
|
|
ip60 = vlib_buffer_get_current (b0);
|
|
ip61 = vlib_buffer_get_current (b1);
|
|
|
|
vnet_feature_next (&next0, b0);
|
|
vnet_feature_next (&next1, b1);
|
|
|
|
sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
|
|
sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
|
|
|
|
sol_adv0 = ip6_next_header (ip60);
|
|
lladdr0 =
|
|
(icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
|
|
*) (sol_adv0 + 1);
|
|
|
|
if (PREDICT_TRUE (sol_adv0->icmp.type ==
|
|
ICMP6_neighbor_advertisement))
|
|
{
|
|
aif0 = am->interfaces[sw_if_index0];
|
|
if (PREDICT_TRUE (clib_memcmp (&aif0->address.ip.ip6,
|
|
&sol_adv0->target_address,
|
|
sizeof (aif0->address.ip.ip6)) ==
|
|
0))
|
|
{
|
|
clib_memcpy_fast (&aif0->recv.from6.ip6,
|
|
&sol_adv0->target_address,
|
|
sizeof (aif0->recv.from6.ip6));
|
|
clib_memcpy_fast (&aif0->recv.from6.mac,
|
|
lladdr0->ethernet_address, 6);
|
|
aif0->reply_count++;
|
|
}
|
|
}
|
|
|
|
sol_adv1 = ip6_next_header (ip61);
|
|
lladdr1 =
|
|
(icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
|
|
*) (sol_adv1 + 1);
|
|
|
|
if (PREDICT_TRUE (sol_adv1->icmp.type ==
|
|
ICMP6_neighbor_advertisement))
|
|
{
|
|
aif1 = am->interfaces[sw_if_index1];
|
|
if (PREDICT_TRUE (clib_memcmp (&aif1->address.ip.ip6,
|
|
&sol_adv1->target_address,
|
|
sizeof (aif1->address.ip.ip6)) ==
|
|
0))
|
|
{
|
|
clib_memcpy_fast (&aif1->recv.from6.ip6,
|
|
&sol_adv1->target_address,
|
|
sizeof (aif1->recv.from6.ip6));
|
|
clib_memcpy_fast (&aif1->recv.from6.mac,
|
|
lladdr1->ethernet_address, 6);
|
|
aif1->reply_count++;
|
|
}
|
|
}
|
|
|
|
if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
|
|
{
|
|
arping6_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
|
|
t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
|
|
t->type = sol_adv0->icmp.type;
|
|
clib_memcpy_fast (&t->reply.ip6, &sol_adv0->target_address,
|
|
sizeof (t->reply.ip6));
|
|
clib_memcpy_fast (&t->reply.mac, lladdr0->ethernet_address, 6);
|
|
}
|
|
if (PREDICT_FALSE ((b1->flags & VLIB_BUFFER_IS_TRACED)))
|
|
{
|
|
arping6_trace_t *t = vlib_add_trace (vm, node, b1, sizeof (*t));
|
|
t->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX];
|
|
t->type = sol_adv1->icmp.type;
|
|
clib_memcpy_fast (&t->reply.ip6, &sol_adv1->target_address,
|
|
sizeof (t->reply.ip6));
|
|
clib_memcpy_fast (&t->reply.mac, lladdr1->ethernet_address, 6);
|
|
}
|
|
|
|
vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
|
|
n_left_to_next, bi0, bi1, next0,
|
|
next1);
|
|
}
|
|
|
|
while (n_left_from > 0 && n_left_to_next > 0)
|
|
{
|
|
u32 next0, bi0;
|
|
vlib_buffer_t *b0;
|
|
arping_intf_t *aif0;
|
|
u32 sw_if_index0;
|
|
ip6_header_t *ip60;
|
|
icmp6_neighbor_solicitation_or_advertisement_header_t *sol_adv0;
|
|
icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
|
|
*lladdr0;
|
|
|
|
bi0 = to_next[0] = from[0];
|
|
|
|
from += 1;
|
|
n_left_from -= 1;
|
|
to_next += 1;
|
|
n_left_to_next -= 1;
|
|
next0 = ARPING_NEXT_DROP;
|
|
|
|
b0 = vlib_get_buffer (vm, bi0);
|
|
ip60 = vlib_buffer_get_current (b0);
|
|
|
|
vnet_feature_next (&next0, b0);
|
|
|
|
sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
|
|
|
|
sol_adv0 = ip6_next_header (ip60);
|
|
lladdr0 =
|
|
(icmp6_neighbor_discovery_ethernet_link_layer_address_option_t
|
|
*) (sol_adv0 + 1);
|
|
if (PREDICT_TRUE (sol_adv0->icmp.type ==
|
|
ICMP6_neighbor_advertisement))
|
|
{
|
|
aif0 = am->interfaces[sw_if_index0];
|
|
if (PREDICT_TRUE (clib_memcmp (&aif0->address.ip.ip6,
|
|
&sol_adv0->target_address,
|
|
sizeof (aif0->address.ip.ip6)) ==
|
|
0))
|
|
{
|
|
clib_memcpy_fast (&aif0->recv.from6.ip6,
|
|
&sol_adv0->target_address,
|
|
sizeof (aif0->recv.from6.ip6));
|
|
clib_memcpy_fast (&aif0->recv.from6.mac,
|
|
lladdr0->ethernet_address, 6);
|
|
aif0->reply_count++;
|
|
}
|
|
}
|
|
|
|
if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
|
|
{
|
|
arping6_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
|
|
t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
|
|
t->type = sol_adv0->icmp.type;
|
|
clib_memcpy_fast (&t->reply.ip6, &sol_adv0->target_address,
|
|
sizeof (t->reply.ip6));
|
|
clib_memcpy_fast (&t->reply.mac, lladdr0->ethernet_address, 6);
|
|
}
|
|
|
|
vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
|
|
n_left_to_next, bi0, next0);
|
|
}
|
|
|
|
vlib_put_next_frame (vm, node, next_index, n_left_to_next);
|
|
}
|
|
|
|
return frame->n_vectors;
|
|
}
|
|
|
|
VLIB_REGISTER_NODE (arping6_input_node) =
|
|
{
|
|
.name = "arping6-input",.vector_size = sizeof (u32),.format_trace =
|
|
format_arping6_trace,.type = VLIB_NODE_TYPE_INTERNAL,.n_errors =
|
|
ARPING_N_ERROR,.error_strings = arping_error_strings,.n_next_nodes =
|
|
ARPING_N_NEXT,.next_nodes =
|
|
{
|
|
[ARPING6_NEXT_DROP] = "error-drop",[ARPING6_NEXT_IO] = "interface-output",}
|
|
,};
|
|
|
|
VNET_FEATURE_INIT (arping6_feat_node, static) = {
|
|
.arc_name = "ip6-local",
|
|
.node_name = "arping6-input",
|
|
.runs_before = VNET_FEATURES ("ip6-local-end-of-arc"),
|
|
};
|
|
|
|
static clib_error_t *
|
|
arping_neighbor_advertisement (vlib_main_t *vm, arping_args_t *args)
|
|
{
|
|
vnet_main_t *vnm = vnet_get_main ();
|
|
u32 send_count = 0;
|
|
|
|
while (args->repeat > 0)
|
|
{
|
|
send_count++;
|
|
if (args->address.version == AF_IP4)
|
|
{
|
|
if (args->silence == 0)
|
|
vlib_cli_output (vm, "Sending %u GARP to %U", send_count,
|
|
format_ip4_address, &args->address.ip.ip4);
|
|
ip4_neighbor_advertise (vm, vnm, args->sw_if_index,
|
|
vlib_get_thread_index (),
|
|
&args->address.ip.ip4);
|
|
}
|
|
else
|
|
{
|
|
if (args->silence == 0)
|
|
vlib_cli_output (vm, "Sending %u Neighbor Advertisement to %U",
|
|
send_count, format_ip6_address,
|
|
&args->address.ip.ip6);
|
|
ip6_neighbor_advertise (vm, vnm, args->sw_if_index,
|
|
vlib_get_thread_index (),
|
|
&args->address.ip.ip6);
|
|
}
|
|
args->repeat--;
|
|
if ((args->interval > 0.0) && (args->repeat > 0))
|
|
vlib_process_suspend (vm, args->interval);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
arping_vnet_feature_enable_disable (vlib_main_t *vm, const char *arc_name,
|
|
const char *node_name, u32 sw_if_index,
|
|
int enable_disable, void *feature_config,
|
|
u32 n_feature_config_bytes)
|
|
{
|
|
vlib_worker_thread_barrier_sync (vm);
|
|
vnet_feature_enable_disable (arc_name, node_name, sw_if_index,
|
|
enable_disable, feature_config,
|
|
n_feature_config_bytes);
|
|
vlib_worker_thread_barrier_release (vm);
|
|
}
|
|
|
|
static void
|
|
arping_vec_validate (vlib_main_t *vm, u32 sw_if_index)
|
|
{
|
|
arping_main_t *am = &arping_main;
|
|
|
|
if (sw_if_index >= vec_len (am->interfaces))
|
|
{
|
|
vlib_worker_thread_barrier_sync (vm);
|
|
vec_validate (am->interfaces, sw_if_index);
|
|
vlib_worker_thread_barrier_release (vm);
|
|
}
|
|
}
|
|
|
|
static clib_error_t *
|
|
arping_neighbor_probe_dst (vlib_main_t *vm, arping_args_t *args)
|
|
{
|
|
arping_main_t *am = &arping_main;
|
|
u32 send_count = 0;
|
|
clib_error_t *error;
|
|
arping_intf_t aif;
|
|
|
|
/* Disallow multiple sends on the same interface for now. Who needs it? */
|
|
if ((vec_len (am->interfaces) > args->sw_if_index) &&
|
|
(am->interfaces[args->sw_if_index] != 0))
|
|
{
|
|
error = clib_error_return (
|
|
0, "arping command is in progress for the same interface. "
|
|
"Please try again later.");
|
|
args->rv = VNET_API_ERROR_INVALID_VALUE;
|
|
return error;
|
|
}
|
|
|
|
arping_vec_validate (vm, args->sw_if_index);
|
|
clib_memset (&aif, 0, sizeof (aif));
|
|
aif.interval = args->interval;
|
|
aif.repeat = args->repeat;
|
|
aif.reply_count = 0;
|
|
am->interfaces[args->sw_if_index] = &aif;
|
|
|
|
clib_memcpy (&aif.address, &args->address, sizeof (aif.address));
|
|
if (args->address.version == AF_IP4)
|
|
arping_vnet_feature_enable_disable (vm, "arp", "arping-input",
|
|
args->sw_if_index, 1, 0, 0);
|
|
else
|
|
arping_vnet_feature_enable_disable (vm, "ip6-local", "arping6-input",
|
|
args->sw_if_index, 1, 0, 0);
|
|
|
|
while (args->repeat > 0)
|
|
{
|
|
send_count++;
|
|
if (args->address.version == AF_IP4)
|
|
{
|
|
if (args->silence == 0)
|
|
vlib_cli_output (vm, "Sending %u ARP Request to %U", send_count,
|
|
format_ip4_address, &args->address.ip.ip4);
|
|
ip4_neighbor_probe_dst (args->sw_if_index, vlib_get_thread_index (),
|
|
&args->address.ip.ip4);
|
|
}
|
|
else
|
|
{
|
|
if (args->silence == 0)
|
|
vlib_cli_output (vm, "Sending %u Neighbor Solicitation to %U",
|
|
send_count, format_ip6_address,
|
|
&args->address.ip.ip6);
|
|
ip6_neighbor_probe_dst (args->sw_if_index, vlib_get_thread_index (),
|
|
&args->address.ip.ip6);
|
|
}
|
|
args->repeat--;
|
|
if ((args->interval > 0.0) && (args->repeat > 0))
|
|
vlib_process_suspend (vm, args->interval);
|
|
}
|
|
|
|
/* wait for a second on the reply */
|
|
u32 wait_count = 0;
|
|
while ((aif.reply_count < send_count) && (wait_count < 10))
|
|
{
|
|
vlib_process_suspend (vm, 0.1);
|
|
wait_count++;
|
|
}
|
|
|
|
if (args->address.version == AF_IP4)
|
|
{
|
|
clib_memcpy (&args->recv.from4, &aif.recv.from4,
|
|
sizeof (args->recv.from4));
|
|
arping_vnet_feature_enable_disable (vm, "arp", "arping-input",
|
|
args->sw_if_index, 0, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
clib_memcpy (&args->recv.from6, &aif.recv.from6,
|
|
sizeof (args->recv.from6));
|
|
arping_vnet_feature_enable_disable (vm, "ip6-local", "arping6-input",
|
|
args->sw_if_index, 0, 0, 0);
|
|
}
|
|
args->reply_count = aif.reply_count;
|
|
|
|
am->interfaces[args->sw_if_index] = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
arping_run_command (vlib_main_t *vm, arping_args_t *args)
|
|
{
|
|
if (args->is_garp)
|
|
args->error = arping_neighbor_advertisement (vm, args);
|
|
else
|
|
args->error = arping_neighbor_probe_dst (vm, args);
|
|
}
|
|
|
|
static clib_error_t *
|
|
arping_ip_address (vlib_main_t *vm, unformat_input_t *input,
|
|
vlib_cli_command_t *cmd)
|
|
{
|
|
clib_error_t *error = 0;
|
|
vnet_main_t *vnm = vnet_get_main ();
|
|
arping_args_t args = { 0 };
|
|
f64 interval = ARPING_DEFAULT_INTERVAL;
|
|
|
|
args.repeat = ARPING_DEFAULT_REPEAT;
|
|
args.interval = ARPING_DEFAULT_INTERVAL;
|
|
args.sw_if_index = ~0;
|
|
args.silence = 0;
|
|
|
|
if (unformat (input, "gratuitous"))
|
|
args.is_garp = 1;
|
|
|
|
if (unformat (input, "%U", unformat_ip4_address, &args.address.ip.ip4))
|
|
args.address.version = AF_IP4;
|
|
else if (unformat (input, "%U", unformat_ip6_address, &args.address.ip.ip6))
|
|
args.address.version = AF_IP6;
|
|
else
|
|
{
|
|
error = clib_error_return (
|
|
0,
|
|
"expecting IP4/IP6 address `%U'. Usage: arping [gratuitous] <addr> "
|
|
"<intf> [repeat <count>] [interval <secs>]",
|
|
format_unformat_error, input);
|
|
goto done;
|
|
}
|
|
|
|
if (!unformat_user (input, unformat_vnet_sw_interface, vnm,
|
|
&args.sw_if_index))
|
|
{
|
|
error = clib_error_return (0, "unknown interface `%U'",
|
|
format_unformat_error, input);
|
|
goto done;
|
|
}
|
|
|
|
/* parse the rest of the parameters in a cycle */
|
|
while (!unformat_eof (input, NULL))
|
|
{
|
|
if (unformat (input, "interval"))
|
|
{
|
|
if (!unformat (input, "%f", &interval))
|
|
{
|
|
error = clib_error_return (
|
|
0, "expecting interval (floating point number) got `%U'",
|
|
format_unformat_error, input);
|
|
goto done;
|
|
}
|
|
args.interval = interval;
|
|
}
|
|
else if (unformat (input, "repeat"))
|
|
{
|
|
if (!unformat (input, "%u", &args.repeat))
|
|
{
|
|
error =
|
|
clib_error_return (0, "expecting repeat count but got `%U'",
|
|
format_unformat_error, input);
|
|
goto done;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = clib_error_return (0, "unknown input `%U'",
|
|
format_unformat_error, input);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
arping_run_command (vm, &args);
|
|
|
|
if (args.reply_count)
|
|
{
|
|
if (args.address.version == AF_IP4)
|
|
vlib_cli_output (vm, "Received %u ARP Replies from %U (%U)",
|
|
args.reply_count, format_mac_address,
|
|
&args.recv.from4.mac, format_ip4_address,
|
|
&args.recv.from4.ip4);
|
|
else
|
|
vlib_cli_output (
|
|
vm, "Received %u ICMP6 neighbor advertisements from %U (%U)",
|
|
args.reply_count, format_mac_address, &args.recv.from6.mac,
|
|
format_ip6_address, &args.recv.from6.ip6);
|
|
}
|
|
else if (args.is_garp == 0)
|
|
vlib_cli_output (vm, "Received 0 Reply");
|
|
|
|
error = args.error;
|
|
done:
|
|
return error;
|
|
}
|
|
// clang-format off
|
|
/*?
|
|
* This command sends an ARP REQUEST or gratuitous ARP to network hosts. The
|
|
* address can be an IPv4 or IPv6 address.
|
|
*
|
|
* @cliexpar
|
|
* @parblock
|
|
* Example of how to send an IPv4 ARP REQUEST
|
|
* @cliexstart{arping 100.1.1.10 VirtualEthernet0/0/0 repeat 3 interval 1}
|
|
* Sending 1 ARP Request to 100.1.1.10
|
|
* Sending 2 ARP Request to 100.1.1.10
|
|
* Sending 3 ARP Request to 100.1.1.10
|
|
* Received 3 ARP Replies from 52:53:00:00:04:01 (100.1.1.10)
|
|
* @cliexend
|
|
*
|
|
* Example of how to send an IPv6 Neighbor Solicitation
|
|
* @cliexstart{arping 2001:192::2 VirtualEthernet0/0/0 repeat 3 interval 1}
|
|
* Sending 1 Neighbor Solicitation to 2001:192::2
|
|
* Sending 2 Neighbor Solicitation to 2001:192::2
|
|
* Sending 3 Neighbor Solicitation to 2001:192::2
|
|
* Received 3 ICMP6 neighbor advertisements from 52:53:00:00:04:01 (2001:192::2)
|
|
* @cliexend
|
|
*
|
|
* Example of how to send an IPv4 gratuitous ARP
|
|
* @cliexstart{arping gratuitous 100.1.1.100 VirtualEthernet0/0/0 repeat 2}
|
|
* Sending 1 GARP to 100.1.1.100
|
|
* Sending 2 GARP to 100.1.1.100
|
|
* @cliexend
|
|
* @endparblock
|
|
*
|
|
?*/
|
|
// clang-format on
|
|
VLIB_CLI_COMMAND (arping_command, static) = {
|
|
.path = "arping",
|
|
.function = arping_ip_address,
|
|
.short_help = "arping [gratuitous] {addr} {interface}"
|
|
" [interval {sec}] [repeat {cnt}]",
|
|
.is_mp_safe = 1,
|
|
};
|
|
|
|
static clib_error_t *
|
|
arping_cli_init (vlib_main_t *vm)
|
|
{
|
|
/* initialize binary API */
|
|
arping_plugin_api_hookup (vm);
|
|
|
|
return 0;
|
|
}
|
|
|
|
VLIB_INIT_FUNCTION (arping_cli_init);
|
|
|
|
VLIB_PLUGIN_REGISTER () = {
|
|
.version = VPP_BUILD_VER,
|
|
.description = "Arping (arping)",
|
|
};
|
|
|
|
/*
|
|
* fd.io coding-style-patch-verification: ON
|
|
*
|
|
* Local Variables:
|
|
* eval: (c-set-style "gnu")
|
|
* End:
|
|
*/
|