ping: add -I for bind device

Add -I option to specify the network device to use for sending ICMP
echo requests. This allows users to explicitly bind ping to a
specific network interface, which is particularly useful in
multi-homed systems with multiple network interfaces.

Signed-off-by: meijian <meijian@xiaomi.com>
This commit is contained in:
meijian 2025-03-14 17:23:30 +08:00 committed by Xiang Xiao
parent 532a055abb
commit 5d3b2d07d6
6 changed files with 68 additions and 2 deletions

View file

@ -50,6 +50,7 @@
#define ICMP_E_POLL -11 /* extra: error code */
#define ICMP_E_RECVFROM -13 /* extra: error code */
#define ICMP_E_RECVSMALL -15 /* extra: recv bytes */
#define ICMP_E_BINDDEV -17 /* extra: error bind */
/* Negative even number represent warning(recoverable) */
@ -70,6 +71,9 @@ struct ping_result_s;
struct ping_info_s
{
FAR const char *hostname; /* Host name to ping */
#ifdef CONFIG_NET_BINDTODEVICE
FAR const char *devname; /* Device name to bind */
#endif
uint16_t count; /* Number of pings requested */
uint16_t datalen; /* Number of bytes to be sent */
uint16_t delay; /* Deciseconds to delay between pings */

View file

@ -50,6 +50,7 @@
#define ICMPv6_E_POLL -11 /* extra: error code */
#define ICMPv6_E_RECVFROM -13 /* extra: error code */
#define ICMPv6_E_RECVSMALL -15 /* extra: recv bytes */
#define ICMPV6_E_BINDDEV -17 /* extra: error bind */
/* Negative even number represent warning(recoverable) */
@ -70,6 +71,9 @@ struct ping6_result_s;
struct ping6_info_s
{
FAR const char *hostname; /* Host name to ping */
#ifdef CONFIG_NET_BINDTODEVICE
FAR const char *devname; /* Device name to bind */
#endif
uint16_t count; /* Number of pings requested */
uint16_t datalen; /* Number of bytes to be sent */
uint16_t delay; /* Deciseconds to delay between pings */

View file

@ -232,6 +232,20 @@ void icmp_ping(FAR const struct ping_info_s *info)
return;
}
#ifdef CONFIG_NET_BINDTODEVICE
if (info->devname)
{
ret = setsockopt(priv->sockfd, SOL_SOCKET, SO_BINDTODEVICE,
info->devname, strlen(info->devname));
if (ret < 0)
{
icmp_callback(&result, ICMP_E_BINDDEV, errno);
free(priv);
return;
}
}
#endif
priv->kickoff = clock();
memset(&priv->destaddr, 0, sizeof(struct sockaddr_in));

View file

@ -218,6 +218,20 @@ void icmp6_ping(FAR const struct ping6_info_s *info)
return;
}
#ifdef CONFIG_NET_BINDTODEVICE
if (info->devname)
{
ret = setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
info->devname, strlen(info->devname));
if (ret < 0)
{
icmp6_callback(&result, ICMPV6_E_BINDDEV, errno);
free(iobuffer);
return;
}
}
#endif
kickoff = clock();
memset(&destaddr, 0, sizeof(struct sockaddr_in6));

View file

@ -100,6 +100,7 @@ static void show_usage(FAR const char *progname, int exitcode)
printf(" -s <size> specifies the number of data bytes to be sent. "
"Default %u.\n",
ICMP_PING_DATALEN);
printf(" -I <interface> is the bind device for traffic\n");
printf(" -h shows this text and exits.\n");
exit(exitcode);
}
@ -172,6 +173,12 @@ static void ping_result(FAR const struct ping_result_s *result)
fprintf(stderr, "ERROR: short ICMP packet: %ld\n", result->extra);
break;
#ifdef CONFIG_NET_BINDTODEVICE
case ICMP_E_BINDDEV:
fprintf(stderr, "ERROR: setsockopt error: %ld\n", result->extra);
break;
#endif
case ICMP_W_IDDIFF:
fprintf(stderr,
"WARNING: Ignoring ICMP reply with ID %ld. "
@ -300,7 +307,7 @@ int main(int argc, FAR char *argv[])
exitcode = EXIT_FAILURE;
while ((option = getopt(argc, argv, ":c:i:W:s:h")) != ERROR)
while ((option = getopt(argc, argv, ":c:i:W:s:I:h")) != ERROR)
{
switch (option)
{
@ -360,6 +367,14 @@ int main(int argc, FAR char *argv[])
}
break;
case 'I':
#ifdef CONFIG_NET_BINDTODEVICE
info.devname = optarg;
#else
fprintf(stderr, "ERROR: Bind to device not supported\n");
#endif
break;
case 'h':
exitcode = EXIT_SUCCESS;
goto errout_with_usage;

View file

@ -100,6 +100,7 @@ static void show_usage(FAR const char *progname, int exitcode)
printf(" -s <size> specifies the number of data bytes to be sent. "
" Default %u.\n",
ICMPv6_PING6_DATALEN);
printf(" -I <interface> is the bind device for traffic\n");
printf(" -h shows this text and exits.\n");
exit(exitcode);
}
@ -176,6 +177,12 @@ static void ping6_result(FAR const struct ping6_result_s *result)
result->extra, result->id);
break;
#ifdef CONFIG_NET_BINDTODEVICE
case ICMPV6_E_BINDDEV:
fprintf(stderr, "ERROR: setsockopt error: %ld\n", result->extra);
break;
#endif
case ICMPv6_W_SEQNOBIG:
fprintf(stderr,
"WARNING: Ignoring ICMP reply to sequence %ld. "
@ -295,7 +302,7 @@ int main(int argc, FAR char *argv[])
exitcode = EXIT_FAILURE;
while ((option = getopt(argc, argv, ":c:i:W:s:h")) != ERROR)
while ((option = getopt(argc, argv, ":c:i:W:s:I:h")) != ERROR)
{
switch (option)
{
@ -355,6 +362,14 @@ int main(int argc, FAR char *argv[])
}
break;
case 'I':
#ifdef CONFIG_NET_BINDTODEVICE
info.devname = optarg;
#else
fprintf(stderr, "ERROR: Bind to device not supported\n");
#endif
break;
case 'h':
exitcode = EXIT_SUCCESS;
goto errout_with_usage;