Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions docs/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,8 @@ custom_target(
# The list is hardcoded since we can't run grcli during meson configuration.
grcli_commands = [
'address', 'affinity', 'conntrack', 'dnat44', 'events', 'fdb', 'flood',
'graph', 'interface', 'log', 'nexthop', 'ping', 'ping6', 'route',
'router-advert', 'snat44', 'stats', 'trace', 'traceroute', 'traceroute6',
'tunsrc',
'graph', 'interface', 'log', 'nexthop', 'ping', 'route', 'router-advert',
'snat44', 'stats', 'trace', 'traceroute', 'tunsrc',
]

foreach cmd : grcli_commands
Expand Down
8 changes: 8 additions & 0 deletions modules/infra/cli/gr_cli_l3.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,13 @@ struct cli_addr_ops {
STAILQ_ENTRY(cli_addr_ops) next;
};

struct cli_icmp_ops {
addr_family_t af;
cmd_cb_t ping;
cmd_cb_t traceroute;
STAILQ_ENTRY(cli_icmp_ops) next;
};

void cli_route_ops_register(struct cli_route_ops *);
void cli_addr_ops_register(struct cli_addr_ops *);
void cli_icmp_ops_register(struct cli_icmp_ops *);
107 changes: 107 additions & 0 deletions modules/infra/cli/icmp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2026 Robin Jarry

#include <gr_api.h>
#include <gr_cli.h>
#include <gr_cli_iface.h>
#include <gr_cli_l3.h>
#include <gr_net_types.h>

#include <ecoli.h>

#include <errno.h>
#include <sys/queue.h>
#include <unistd.h>

static STAILQ_HEAD(, cli_icmp_ops) icmp_ops = STAILQ_HEAD_INITIALIZER(icmp_ops);

void cli_icmp_ops_register(struct cli_icmp_ops *ops) {
assert(ops != NULL);
assert(ops->ping != NULL);
assert(ops->traceroute != NULL);
struct cli_icmp_ops *o;
STAILQ_FOREACH (o, &icmp_ops, next)
assert(ops->af != o->af);
STAILQ_INSERT_TAIL(&icmp_ops, ops, next);
}

static cmd_status_t ping(struct gr_api_client *c, const struct ec_pnode *p) {
struct cli_icmp_ops *ops;
uint8_t ip[16];

STAILQ_FOREACH (ops, &icmp_ops, next) {
if (arg_ip(p, "DEST", ip, ops->af) == 0)
return ops->ping(c, p);
}

errno = ENOPROTOOPT;
return CMD_ERROR;
}

static cmd_status_t traceroute(struct gr_api_client *c, const struct ec_pnode *p) {
struct cli_icmp_ops *ops;
uint8_t ip[16];

STAILQ_FOREACH (ops, &icmp_ops, next) {
if (arg_ip(p, "DEST", ip, ops->af) == 0)
return ops->traceroute(c, p);
}

errno = ENOPROTOOPT;
return CMD_ERROR;
}

static int ctx_init(struct ec_node *root) {
int ret;

ret = CLI_COMMAND(
CLI_CONTEXT(
root, CTX_ARG("ping", "Send ICMP{v6} echo requests and wait for replies.")
),
"DEST [(vrf VRF),(count COUNT),(delay DELAY),(iface IFACE),(ident IDENT)]",
ping,
"Send ICMP{v6} echo requests and wait for replies.",
with_help("IP{v6} destination address.", ec_node_re("DEST", IP_ANY_RE)),
with_help(
"Output interface name.",
ec_node_dyn("IFACE", complete_iface_names, INT2PTR(GR_IFACE_TYPE_UNDEF))
),
with_help("L3 routing domain name.", ec_node_dyn("VRF", complete_vrf_names, NULL)),
with_help("Number of packets to send.", ec_node_uint("COUNT", 1, UINT16_MAX, 10)),
with_help("Delay in ms between icmp6 echo.", ec_node_uint("DELAY", 0, 10000, 10)),
with_help(
"Icmp ident field (default: random).",
ec_node_uint("IDENT", 1, UINT16_MAX, 10)
)
);
if (ret < 0)
return ret;

ret = CLI_COMMAND(
CLI_CONTEXT(root, CTX_ARG("traceroute", "Discover IP{v6} intermediate gateways.")),
"DEST [(vrf VRF),(iface IFACE),(ident IDENT)]",
traceroute,
"Discover IP{v6} intermediate gateways.",
with_help("IP{v6} destination address.", ec_node_re("DEST", IP_ANY_RE)),
with_help("L3 routing domain name.", ec_node_dyn("VRF", complete_vrf_names, NULL)),
with_help(
"Output interface name.",
ec_node_dyn("IFACE", complete_iface_names, INT2PTR(GR_IFACE_TYPE_UNDEF))
),
with_help(
"Icmp ident field (default: random).",
ec_node_uint("IDENT", 1, UINT16_MAX, 10)
)
);

return ret;
}

static struct cli_context ctx = {
.name = "icmp",
.init = ctx_init,
};

static void __attribute__((constructor, used)) init(void) {
cli_context_register(&ctx);
}
1 change: 1 addition & 0 deletions modules/infra/cli/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ cli_src += files(
'bond.c',
'events.c',
'graph.c',
'icmp.c',
'iface.c',
'vrf.c',
'nexthop.c',
Expand Down
52 changes: 8 additions & 44 deletions modules/ip/cli/icmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <gr_api.h>
#include <gr_cli.h>
#include <gr_cli_iface.h>
#include <gr_cli_l3.h>
#include <gr_ip4.h>
#include <gr_net_types.h>

Expand Down Expand Up @@ -135,7 +136,7 @@ static cmd_status_t ping(struct gr_api_client *c, const struct ec_pnode *p) {
uint16_t ident = random();
uint16_t msdelay = 1000;

if (arg_ip4(p, "IP", &req.addr) < 0)
if (arg_ip4(p, "DEST", &req.addr) < 0)
return CMD_ERROR;
if (arg_vrf(c, p, "VRF", &req.vrf) < 0)
return CMD_ERROR;
Expand All @@ -162,7 +163,7 @@ static cmd_status_t traceroute(struct gr_api_client *c, const struct ec_pnode *p
cmd_status_t ret = CMD_SUCCESS;
uint16_t ident = random();

if (arg_ip4(p, "IP", &req.addr) < 0)
if (arg_ip4(p, "DEST", &req.addr) < 0)
return CMD_ERROR;
if ((ret = arg_u16(p, "IDENT", &ident)) < 0 && ret != ENOENT)
return CMD_ERROR;
Expand All @@ -180,49 +181,12 @@ static cmd_status_t traceroute(struct gr_api_client *c, const struct ec_pnode *p
return ret;
}

static int ctx_init(struct ec_node *root) {
int ret;

ret = CLI_COMMAND(
CLI_CONTEXT(
root, CTX_ARG("ping", "Send IPv4 ICMP echo requests and wait for replies.")
),
"IP [(vrf VRF),(count COUNT),(delay DELAY),(ident IDENT)]",
ping,
"Send IPv4 ICMP echo requests and wait for replies.",
with_help("IPv4 destination address.", ec_node_re("IP", IPV4_RE)),
with_help("L3 routing domain name.", ec_node_dyn("VRF", complete_vrf_names, NULL)),
with_help("Number of packets to send.", ec_node_uint("COUNT", 1, UINT16_MAX, 10)),
with_help("Delay in ms between icmp echo.", ec_node_uint("DELAY", 0, 10000, 10)),
with_help(
"Icmp ident field (default: random).",
ec_node_uint("IDENT", 1, UINT16_MAX, 10)
)
);
if (ret < 0)
return ret;

ret = CLI_COMMAND(
CLI_CONTEXT(root, CTX_ARG("traceroute", "Discover IPv4 intermediate gateways.")),
"IP [(ident IDENT),(vrf VRF)]",
traceroute,
"Discover IPv4 intermediate gateways.",
with_help("IPv4 destination address.", ec_node_re("IP", IPV4_RE)),
with_help(
"Icmp ident field (default: random).",
ec_node_uint("IDENT", 1, UINT16_MAX, 10)
),
with_help("L3 routing domain name.", ec_node_dyn("VRF", complete_vrf_names, NULL))
);

return ret;
}

static struct cli_context ctx = {
.name = "ping",
.init = ctx_init,
static struct cli_icmp_ops ops = {
.af = GR_AF_IP4,
.ping = ping,
.traceroute = traceroute,
};

static void __attribute__((constructor, used)) init(void) {
cli_context_register(&ctx);
cli_icmp_ops_register(&ops);
}
55 changes: 6 additions & 49 deletions modules/ip6/cli/icmp6.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <gr_api.h>
#include <gr_cli.h>
#include <gr_cli_iface.h>
#include <gr_cli_l3.h>
#include <gr_ip6.h>
#include <gr_net_types.h>

Expand Down Expand Up @@ -193,57 +194,13 @@ static cmd_status_t traceroute(struct gr_api_client *c, const struct ec_pnode *p
return ret;
}

static int ctx_init(struct ec_node *root) {
int ret;

ret = CLI_COMMAND(
CLI_CONTEXT(
root, CTX_ARG("ping6", "Send ICMPv6 echo requests and wait for replies.")
),
"DEST [(vrf VRF),(count COUNT),(delay DELAY),(iface IFACE),(ident IDENT)]",
ping,
"Send ICMPv6 echo requests and wait for replies.",
with_help("IPv6 destination address.", ec_node_re("DEST", IPV6_RE)),
with_help(
"Output interface name.",
ec_node_dyn("IFACE", complete_iface_names, INT2PTR(GR_IFACE_TYPE_UNDEF))
),
with_help("L3 routing domain name.", ec_node_dyn("VRF", complete_vrf_names, NULL)),
with_help("Number of packets to send.", ec_node_uint("COUNT", 1, UINT16_MAX, 10)),
with_help("Delay in ms between icmp6 echo.", ec_node_uint("DELAY", 0, 10000, 10)),
with_help(
"Icmp ident field (default: random).",
ec_node_uint("IDENT", 1, UINT16_MAX, 10)
)
);
if (ret < 0)
return ret;

ret = CLI_COMMAND(
CLI_CONTEXT(root, CTX_ARG("traceroute6", "Discover IPv6 intermediate gateways.")),
"DEST [(vrf VRF),(iface IFACE),(ident IDENT)]",
traceroute,
"Discover IPv6 intermediate gateways.",
with_help("IPv6 destination address.", ec_node_re("DEST", IPV6_RE)),
with_help("L3 routing domain name.", ec_node_dyn("VRF", complete_vrf_names, NULL)),
with_help(
"Output interface name.",
ec_node_dyn("IFACE", complete_iface_names, INT2PTR(GR_IFACE_TYPE_UNDEF))
),
with_help(
"Icmp ident field (default: random).",
ec_node_uint("IDENT", 1, UINT16_MAX, 10)
)
);
static struct cli_icmp_ops ops = {
.af = GR_AF_IP6,
.ping = ping,
.traceroute = traceroute,

return ret;
}

static struct cli_context ctx = {
.name = "ping6",
.init = ctx_init,
};

static void __attribute__((constructor, used)) init(void) {
cli_context_register(&ctx);
cli_icmp_ops_register(&ops);
}
10 changes: 5 additions & 5 deletions smoke/ip6_builtin_icmp_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ done

sleep 3 # wait for DAD

grcli ping6 fd00:ba4:0::2 count 10 delay 100
grcli ping6 fd00:ba4:1::2 count 3 delay 10
grcli ping fd00:ba4:0::2 count 10 delay 100
grcli ping fd00:ba4:1::2 count 3 delay 10

# Expect this test to fail
grcli ping6 fd00:baa::1 count 1 && fail "ping to unknown route succeeded"
grcli ping6 fd00:ba4:1::3 count 1 && fail "ping to non-existent host succeeded"
grcli ping fd00:baa::1 count 1 && fail "ping to unknown route succeeded"
grcli ping fd00:ba4:1::3 count 1 && fail "ping to non-existent host succeeded"

grcli traceroute6 fd00:ba4:1::2
grcli traceroute fd00:ba4:1::2
2 changes: 1 addition & 1 deletion smoke/ospf6_frr_manualtest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,4 @@ while ! vtysh -c 'show ipv6 route ospf6 json' | jq '."2001:db8:1000::/64"[0].pro
attempts=$((attempts - 1))
done

grcli ping6 2001:db8:1000::1 count 3 delay 10
grcli ping 2001:db8:1000::1 count 3 delay 10
Loading