busybox/networking/arping.c

438 lines
12 KiB
C
Raw Normal View History

/* vi: set sw=4 ts=4: */
2003-01-09 15:36:01 +05:30
/*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
2003-01-09 15:36:01 +05:30
*
* Author: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
2003-01-09 15:36:01 +05:30
* Busybox port: Nick Fedchik <nick@fedchik.org.ua>
*/
//config:config ARPING
//config: bool "arping (9 kb)"
//config: default y
//config: help
//config: Ping hosts by ARP packets.
//applet:IF_ARPING(APPLET(arping, BB_DIR_USR_SBIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_ARPING) += arping.o
2003-01-09 15:36:01 +05:30
//usage:#define arping_trivial_usage
//usage: "[-fqbDUA] [-c CNT] [-w TIMEOUT] [-I IFACE] [-s SRC_IP] DST_IP"
//usage:#define arping_full_usage "\n\n"
//usage: "Send ARP requests/replies\n"
//usage: "\n -f Quit on first ARP reply"
//usage: "\n -q Quiet"
//usage: "\n -b Keep broadcasting, don't go unicast"
//usage: "\n -D Exit with 1 if DST_IP replies"
//usage: "\n -U Unsolicited ARP mode, update your neighbors"
//usage: "\n -A ARP answer mode, update your neighbors"
//usage: "\n -c N Stop after sending N ARP requests"
//usage: "\n -w TIMEOUT Seconds to wait for ARP reply"
//NB: in iputils-s20160308, iface is mandatory, no default
//usage: "\n -I IFACE Interface to use (default eth0)"
//usage: "\n -s SRC_IP Sender IP address"
//usage: "\n DST_IP Target IP address"
2003-01-09 15:36:01 +05:30
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <netpacket/packet.h>
#include "libbb.h"
#include "common_bufsiz.h"
2003-01-09 15:36:01 +05:30
/* We don't expect to see 1000+ seconds delay, unsigned is enough */
#define MONOTONIC_US() ((unsigned)monotonic_us())
enum {
UNSOLICITED = 1 << 0,
DAD = 1 << 1,
ADVERT = 1 << 2,
QUIET = 1 << 3,
QUIT_ON_REPLY = 1 << 4,
BCAST_ONLY = 1 << 5,
UNICASTING = 1 << 6,
TIMEOUT = 1 << 7,
2006-03-31 03:20:57 +05:30
};
#define GETOPT32(str_timeout, device, source) \
getopt32(argv, "^" \
"UDAqfbc:+w:I:s:" \
/* DAD also sets quit_on_reply, */ \
/* advert also sets unsolicited: */ \
"\0" "=1:Df:AU", \
&count, &str_timeout, &device, &source \
);
2006-03-31 03:20:57 +05:30
struct globals {
struct in_addr src;
struct in_addr dst;
struct sockaddr_ll me;
struct sockaddr_ll he;
int count; // = -1;
unsigned last;
unsigned timeout_us;
unsigned start;
unsigned sent;
unsigned brd_sent;
unsigned received;
unsigned brd_recv;
unsigned req_recv;
/* should be in main(), but are here to reduce stack use: */
struct ifreq ifr;
struct sockaddr_in probe_saddr;
sigset_t sset;
unsigned char packet[4096];
} FIX_ALIASING;
#define src (G.src )
#define dst (G.dst )
#define me (G.me )
#define he (G.he )
#define count (G.count )
#define last (G.last )
#define timeout_us (G.timeout_us)
#define start (G.start )
#define sent (G.sent )
#define brd_sent (G.brd_sent )
#define received (G.received )
#define brd_recv (G.brd_recv )
#define req_recv (G.req_recv )
//#define G (*(struct globals*)bb_common_bufsiz1)
#define G (*ptr_to_globals)
2008-06-25 15:23:17 +05:30
#define INIT_G() do { \
/*setup_common_bufsiz();*/ \
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
2008-06-25 15:23:17 +05:30
count = -1; \
} while (0)
2003-01-09 15:36:01 +05:30
#define sock_fd 3
static int send_pack(struct in_addr *src_addr,
struct in_addr *dst_addr,
struct sockaddr_ll *ME,
struct sockaddr_ll *HE)
2003-01-09 15:36:01 +05:30
{
int err;
unsigned char buf[256];
2003-01-09 15:36:01 +05:30
struct arphdr *ah = (struct arphdr *) buf;
unsigned char *p;
2003-01-09 15:36:01 +05:30
ah->ar_hrd = htons(ARPHRD_ETHER);
ah->ar_pro = htons(ETH_P_IP);
ah->ar_hln = ME->sll_halen;
ah->ar_pln = 4;
ah->ar_op = option_mask32 & ADVERT ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST);
2003-01-09 15:36:01 +05:30
p = (unsigned char *) (ah + 1);
p = mempcpy(p, &ME->sll_addr, ah->ar_hln);
p = mempcpy(p, src_addr, 4);
2003-01-09 15:36:01 +05:30
if (option_mask32 & ADVERT)
p = mempcpy(p, &ME->sll_addr, ah->ar_hln);
2003-01-09 15:36:01 +05:30
else
p = mempcpy(p, &HE->sll_addr, ah->ar_hln);
2003-01-09 15:36:01 +05:30
p = mempcpy(p, dst_addr, 4);
2003-01-09 15:36:01 +05:30
err = sendto(sock_fd, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE));
2003-01-09 15:36:01 +05:30
if (err == p - buf) {
last = MONOTONIC_US();
2003-01-09 15:36:01 +05:30
sent++;
if (!(option_mask32 & UNICASTING))
2003-01-09 15:36:01 +05:30
brd_sent++;
}
return err;
}
2008-07-05 14:48:54 +05:30
static void finish(void) NORETURN;
static void finish(void)
2003-01-09 15:36:01 +05:30
{
if (!(option_mask32 & QUIET)) {
printf("Sent %u probe(s) (%u broadcast(s))\n"
"Received %u response(s)"
" (%u request(s), %u broadcast(s))\n",
sent, brd_sent,
received,
req_recv, brd_recv);
2003-01-09 15:36:01 +05:30
}
if (option_mask32 & DAD)
2003-01-09 15:36:01 +05:30
exit(!!received);
if (option_mask32 & UNSOLICITED)
exit_SUCCESS();
2003-01-09 15:36:01 +05:30
exit(!received);
}
static void catcher(void)
2003-01-09 15:36:01 +05:30
{
unsigned now;
2003-01-09 15:36:01 +05:30
now = MONOTONIC_US();
if (start == 0)
start = now;
2003-01-09 15:36:01 +05:30
if (count == 0 || (timeout_us && (now - start) > timeout_us))
2003-01-09 15:36:01 +05:30
finish();
/* count < 0 means "infinite count" */
if (count > 0)
count--;
if (last == 0 || (now - last) > 500000) {
send_pack(&src, &dst, &me, &he);
if (count == 0 && (option_mask32 & UNSOLICITED))
2003-01-09 15:36:01 +05:30
finish();
}
alarm(1);
}
static void recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
2003-01-09 15:36:01 +05:30
{
struct arphdr *ah = (struct arphdr *) buf;
unsigned char *p = (unsigned char *) (ah + 1);
struct in_addr src_ip, dst_ip;
/* moves below assume in_addr is 4 bytes big, ensure that */
BUILD_BUG_ON(sizeof(struct in_addr) != 4);
BUILD_BUG_ON(sizeof(src_ip.s_addr) != 4);
2003-01-09 15:36:01 +05:30
/* Filter out wild packets */
if (FROM->sll_pkttype != PACKET_HOST
&& FROM->sll_pkttype != PACKET_BROADCAST
&& FROM->sll_pkttype != PACKET_MULTICAST)
return;
2003-01-09 15:36:01 +05:30
/* Only these types are recognized */
2003-01-09 15:36:01 +05:30
if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY))
return;
2003-01-09 15:36:01 +05:30
/* ARPHRD check and this darned FDDI hack here :-( */
if (ah->ar_hrd != htons(FROM->sll_hatype)
&& (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER)))
return;
2003-01-09 15:36:01 +05:30
/* Protocol must be IP. */
2008-02-11 18:56:54 +05:30
if (ah->ar_pro != htons(ETH_P_IP)
|| (ah->ar_pln != 4)
|| (ah->ar_hln != me.sll_halen)
|| (len < (int)(sizeof(*ah) + 2 * (4 + ah->ar_hln)))
) {
return;
}
2008-02-11 18:56:54 +05:30
move_from_unaligned32(src_ip.s_addr, p + ah->ar_hln);
move_from_unaligned32(dst_ip.s_addr, p + ah->ar_hln + 4 + ah->ar_hln);
2008-02-11 18:56:54 +05:30
if (dst.s_addr != src_ip.s_addr)
return;
if (!(option_mask32 & DAD)) {
2008-02-11 18:56:54 +05:30
if ((src.s_addr != dst_ip.s_addr)
|| (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln)))
return;
2003-01-09 15:36:01 +05:30
} else {
/* DAD packet was:
src_ip = 0 (or some src)
src_hw = ME
dst_ip = tested address
dst_hw = <unspec>
We fail, if receive request/reply with:
src_ip = tested_address
src_hw != ME
if src_ip in request was not zero, check
also that it matches to dst_ip, otherwise
dst_ip/dst_hw do not matter.
*/
2008-02-11 18:56:54 +05:30
if ((memcmp(p, &me.sll_addr, me.sll_halen) == 0)
|| (src.s_addr && src.s_addr != dst_ip.s_addr))
return;
2003-01-09 15:36:01 +05:30
}
if (!(option_mask32 & QUIET)) {
2003-01-09 15:36:01 +05:30
int s_printed = 0;
//TODO: arping from iputils-s20160308 print upprcase hex in MAC, follow them?
printf("%scast re%s from %s [%02x:%02x:%02x:%02x:%02x:%02x]",
FROM->sll_pkttype == PACKET_HOST ? "Uni" : "Broad",
ah->ar_op == htons(ARPOP_REPLY) ? "ply" : "quest",
inet_ntoa(src_ip),
p[0], p[1], p[2], p[3], p[4], p[5]
);
2003-01-09 15:36:01 +05:30
if (dst_ip.s_addr != src.s_addr) {
printf("for %s", inet_ntoa(dst_ip));
2003-01-09 15:36:01 +05:30
s_printed = 1;
}
if (memcmp(p + ah->ar_hln + 4, me.sll_addr, ah->ar_hln)) {
unsigned char *pp = p + ah->ar_hln + 4;
2003-01-09 15:36:01 +05:30
if (!s_printed)
printf(" for");
printf(" [%02x:%02x:%02x:%02x:%02x:%02x]",
pp[0], pp[1], pp[2], pp[3], pp[4], pp[5]
);
2003-01-09 15:36:01 +05:30
}
2006-03-31 03:20:57 +05:30
if (last) {
unsigned diff = MONOTONIC_US() - last;
printf(" %u.%03ums\n", diff / 1000, diff % 1000);
2003-01-09 15:36:01 +05:30
} else {
puts(" UNSOLICITED?");
2003-01-09 15:36:01 +05:30
}
fflush_all();
2003-01-09 15:36:01 +05:30
}
received++;
if (FROM->sll_pkttype != PACKET_HOST)
brd_recv++;
if (ah->ar_op == htons(ARPOP_REQUEST))
req_recv++;
if (option_mask32 & QUIT_ON_REPLY)
2003-01-09 15:36:01 +05:30
finish();
if (!(option_mask32 & BCAST_ONLY)) {
2003-01-09 15:36:01 +05:30
memcpy(he.sll_addr, p, me.sll_halen);
option_mask32 |= UNICASTING;
2003-01-09 15:36:01 +05:30
}
}
int arping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
2008-07-05 14:48:54 +05:30
int arping_main(int argc UNUSED_PARAM, char **argv)
2003-01-09 15:36:01 +05:30
{
const char *device = "eth0";
2003-01-09 15:36:01 +05:30
char *source = NULL;
char *target;
2008-02-11 18:56:54 +05:30
char *err_str;
2003-01-09 15:36:01 +05:30
INIT_G();
xmove_fd(xsocket(AF_PACKET, SOCK_DGRAM, 0), sock_fd);
2003-01-09 15:36:01 +05:30
// If you ever change BB_SUID_DROP to BB_SUID_REQUIRE,
// drop suid root privileges here:
//xsetuid(getuid());
2003-01-09 15:36:01 +05:30
{
unsigned opt;
char *str_timeout;
opt = GETOPT32(str_timeout, device, source);
if (opt & TIMEOUT)
timeout_us = xatou_range(str_timeout, 0, INT_MAX/2000000) * 1000000 + 500000;
2003-01-09 15:36:01 +05:30
}
target = argv[optind];
err_str = xasprintf("interface %s %%s", device);
xfunc_error_retval = 2;
2003-01-09 15:36:01 +05:30
/*memset(&G.ifr, 0, sizeof(G.ifr)); - zeroed by INIT_G */
strncpy_IFNAMSIZ(G.ifr.ifr_name, device);
ioctl_or_perror_and_die(sock_fd, SIOCGIFINDEX, &G.ifr, err_str, "not found");
me.sll_ifindex = G.ifr.ifr_ifindex;
2003-01-09 15:36:01 +05:30
xioctl(sock_fd, SIOCGIFFLAGS, (char *) &G.ifr);
if (!(G.ifr.ifr_flags & IFF_UP)) {
bb_error_msg_and_die(err_str, "is down");
}
if (G.ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) {
bb_error_msg(err_str, "is not ARPable");
BUILD_BUG_ON(DAD != 2);
/* exit 0 if DAD, else exit 2 */
return (~option_mask32 & DAD);
2003-01-09 15:36:01 +05:30
}
/* if (!inet_aton(target, &dst)) - not needed */ {
len_and_sockaddr *lsa;
2007-02-04 08:09:08 +05:30
lsa = xhost_and_af2sockaddr(target, 0, AF_INET);
dst = lsa->u.sin.sin_addr;
if (ENABLE_FEATURE_CLEAN_UP)
free(lsa);
2003-01-09 15:36:01 +05:30
}
if (source && !inet_aton(source, &src)) {
2006-03-31 03:20:57 +05:30
bb_error_msg_and_die("invalid source address %s", source);
2003-01-09 15:36:01 +05:30
}
if ((option_mask32 & (DAD|UNSOLICITED)) == UNSOLICITED && src.s_addr == 0)
2003-01-09 15:36:01 +05:30
src = dst;
if (!(option_mask32 & DAD) || src.s_addr) {
/*struct sockaddr_in probe_saddr;*/
int probe_fd = xsocket(AF_INET, SOCK_DGRAM, 0);
2003-01-09 15:36:01 +05:30
setsockopt_bindtodevice(probe_fd, device);
/*memset(&G.probe_saddr, 0, sizeof(G.probe_saddr)); - zeroed by INIT_G */
G.probe_saddr.sin_family = AF_INET;
2003-01-09 15:36:01 +05:30
if (src.s_addr) {
/* Check that this is indeed our IP */
G.probe_saddr.sin_addr = src;
xbind(probe_fd, (struct sockaddr *) &G.probe_saddr, sizeof(G.probe_saddr));
} else { /* !(option_mask32 & DAD) case */
/* Find IP address on this iface */
G.probe_saddr.sin_port = htons(1025);
G.probe_saddr.sin_addr = dst;
2003-01-09 15:36:01 +05:30
if (setsockopt_SOL_SOCKET_1(probe_fd, SO_DONTROUTE) != 0)
bb_perror_msg("setsockopt(%s)", "SO_DONTROUTE");
xconnect(probe_fd, (struct sockaddr *) &G.probe_saddr, sizeof(G.probe_saddr));
bb_getsockname(probe_fd, (struct sockaddr *) &G.probe_saddr, sizeof(G.probe_saddr));
if (G.probe_saddr.sin_family != AF_INET)
libbb: reduce the overhead of single parameter bb_error_msg() calls Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower overhead call to bb_perror_msg() when only a string was being printed with no parameters. This saves space for some CPU architectures because it avoids the overhead of a call to a variadic function. However there has never been a simple version of bb_error_msg(), and since 2007 many new calls to bb_perror_msg() have been added that only take a single parameter and so could have been using bb_simple_perror_message(). This changeset introduces 'simple' versions of bb_info_msg(), bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and bb_herror_msg_and_die(), and replaces all calls that only take a single parameter, or use something like ("%s", arg), with calls to the corresponding 'simple' version. Since it is likely that single parameter calls to the variadic functions may be accidentally reintroduced in the future a new debugging config option WARN_SIMPLE_MSG has been introduced. This uses some macro magic which will cause any such calls to generate a warning, but this is turned off by default to avoid use of the unpleasant macros in normal circumstances. This is a large changeset due to the number of calls that have been replaced. The only files that contain changes other than simple substitution of function calls are libbb.h, libbb/herror_msg.c, libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c, networking/udhcp/common.h and util-linux/mdev.c additonal macros have been added for logging so that single parameter and multiple parameter logging variants exist. The amount of space saved varies considerably by architecture, and was found to be as follows (for 'defconfig' using GCC 7.4): Arm: -92 bytes MIPS: -52 bytes PPC: -1836 bytes x86_64: -938 bytes Note that for the MIPS architecture only an exception had to be made disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h) because it made these files larger on MIPS. Signed-off-by: James Byrne <james.byrne@origamienergy.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
bb_simple_error_msg_and_die("no IP address configured");
src = G.probe_saddr.sin_addr;
2003-01-09 15:36:01 +05:30
}
close(probe_fd);
}
2003-01-09 15:36:01 +05:30
me.sll_family = AF_PACKET;
//me.sll_ifindex = ifindex; - done before
2003-01-09 15:36:01 +05:30
me.sll_protocol = htons(ETH_P_ARP);
xbind(sock_fd, (struct sockaddr *) &me, sizeof(me));
2003-01-09 15:36:01 +05:30
bb_getsockname(sock_fd, (struct sockaddr *) &me, sizeof(me));
//never happens:
//if (getsockname(sock_fd, (struct sockaddr *) &me, &alen) == -1)
// bb_perror_msg_and_die("getsockname");
2003-01-09 15:36:01 +05:30
if (me.sll_halen == 0) {
2008-02-11 18:56:54 +05:30
bb_error_msg(err_str, "is not ARPable (no ll address)");
BUILD_BUG_ON(DAD != 2);
/* exit 0 if DAD, else exit 2 */
return (~option_mask32 & DAD);
2003-01-09 15:36:01 +05:30
}
he = me;
memset(he.sll_addr, -1, he.sll_halen);
if (!(option_mask32 & QUIET)) {
/* inet_ntoa uses static storage, can't use in same printf */
printf("ARPING %s", inet_ntoa(dst));
printf(" from %s %s\n", inet_ntoa(src), device);
}
2003-01-09 15:36:01 +05:30
/*sigemptyset(&G.sset); - zeroed by INIT_G */
sigaddset(&G.sset, SIGALRM);
sigaddset(&G.sset, SIGINT);
signal_SA_RESTART_empty_mask(SIGINT, (void (*)(int))finish);
signal_SA_RESTART_empty_mask(SIGALRM, (void (*)(int))catcher);
2003-01-09 15:36:01 +05:30
/* Send the first packet, arm ALRM */
2003-01-09 15:36:01 +05:30
catcher();
while (1) {
struct sockaddr_ll from;
socklen_t alen = sizeof(from);
2003-01-09 15:36:01 +05:30
int cc;
/* Unblock SIGALRM so that the previously called alarm()
* can prevent recvfrom from blocking forever in case the
* inherited procmask is blocking SIGALRM.
*/
sigprocmask(SIG_UNBLOCK, &G.sset, NULL);
cc = recvfrom(sock_fd, G.packet, sizeof(G.packet), 0, (struct sockaddr *) &from, &alen);
/* Don't allow SIGALRMs while we process the reply */
sigprocmask(SIG_BLOCK, &G.sset, NULL);
if (cc < 0) {
libbb: reduce the overhead of single parameter bb_error_msg() calls Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower overhead call to bb_perror_msg() when only a string was being printed with no parameters. This saves space for some CPU architectures because it avoids the overhead of a call to a variadic function. However there has never been a simple version of bb_error_msg(), and since 2007 many new calls to bb_perror_msg() have been added that only take a single parameter and so could have been using bb_simple_perror_message(). This changeset introduces 'simple' versions of bb_info_msg(), bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and bb_herror_msg_and_die(), and replaces all calls that only take a single parameter, or use something like ("%s", arg), with calls to the corresponding 'simple' version. Since it is likely that single parameter calls to the variadic functions may be accidentally reintroduced in the future a new debugging config option WARN_SIMPLE_MSG has been introduced. This uses some macro magic which will cause any such calls to generate a warning, but this is turned off by default to avoid use of the unpleasant macros in normal circumstances. This is a large changeset due to the number of calls that have been replaced. The only files that contain changes other than simple substitution of function calls are libbb.h, libbb/herror_msg.c, libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c, networking/udhcp/common.h and util-linux/mdev.c additonal macros have been added for logging so that single parameter and multiple parameter logging variants exist. The amount of space saved varies considerably by architecture, and was found to be as follows (for 'defconfig' using GCC 7.4): Arm: -92 bytes MIPS: -52 bytes PPC: -1836 bytes x86_64: -938 bytes Note that for the MIPS architecture only an exception had to be made disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h) because it made these files larger on MIPS. Signed-off-by: James Byrne <james.byrne@origamienergy.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
bb_simple_perror_msg("recvfrom");
2003-01-09 15:36:01 +05:30
continue;
}
recv_pack(G.packet, cc, &from);
2003-01-09 15:36:01 +05:30
}
}