6937487be7
Back in 2007, commit 0c97c9d437
("'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>
439 lines
12 KiB
C
439 lines
12 KiB
C
/* vi: set sw=4 ts=4: */
|
|
/*
|
|
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
|
|
*
|
|
* Author: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
|
|
* Busybox port: Nick Fedchik <nick@fedchik.org.ua>
|
|
*/
|
|
//config:config ARPING
|
|
//config: bool "arping (9 kb)"
|
|
//config: default y
|
|
//config: select PLATFORM_LINUX
|
|
//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
|
|
|
|
//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"
|
|
|
|
#include <arpa/inet.h>
|
|
#include <net/if.h>
|
|
#include <netinet/ether.h>
|
|
#include <netpacket/packet.h>
|
|
|
|
#include "libbb.h"
|
|
#include "common_bufsiz.h"
|
|
|
|
/* 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,
|
|
};
|
|
#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 \
|
|
);
|
|
|
|
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)
|
|
#define INIT_G() do { \
|
|
/*setup_common_bufsiz();*/ \
|
|
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
|
|
count = -1; \
|
|
} while (0)
|
|
|
|
#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)
|
|
{
|
|
int err;
|
|
unsigned char buf[256];
|
|
struct arphdr *ah = (struct arphdr *) buf;
|
|
unsigned char *p;
|
|
|
|
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);
|
|
|
|
p = (unsigned char *) (ah + 1);
|
|
p = mempcpy(p, &ME->sll_addr, ah->ar_hln);
|
|
p = mempcpy(p, src_addr, 4);
|
|
|
|
if (option_mask32 & ADVERT)
|
|
p = mempcpy(p, &ME->sll_addr, ah->ar_hln);
|
|
else
|
|
p = mempcpy(p, &HE->sll_addr, ah->ar_hln);
|
|
|
|
p = mempcpy(p, dst_addr, 4);
|
|
|
|
err = sendto(sock_fd, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE));
|
|
if (err == p - buf) {
|
|
last = MONOTONIC_US();
|
|
sent++;
|
|
if (!(option_mask32 & UNICASTING))
|
|
brd_sent++;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static void finish(void) NORETURN;
|
|
static void finish(void)
|
|
{
|
|
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);
|
|
}
|
|
if (option_mask32 & DAD)
|
|
exit(!!received);
|
|
if (option_mask32 & UNSOLICITED)
|
|
exit(EXIT_SUCCESS);
|
|
exit(!received);
|
|
}
|
|
|
|
static void catcher(void)
|
|
{
|
|
unsigned now;
|
|
|
|
now = MONOTONIC_US();
|
|
if (start == 0)
|
|
start = now;
|
|
|
|
if (count == 0 || (timeout_us && (now - start) > timeout_us))
|
|
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))
|
|
finish();
|
|
}
|
|
alarm(1);
|
|
}
|
|
|
|
static void recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
|
|
{
|
|
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);
|
|
|
|
/* Filter out wild packets */
|
|
if (FROM->sll_pkttype != PACKET_HOST
|
|
&& FROM->sll_pkttype != PACKET_BROADCAST
|
|
&& FROM->sll_pkttype != PACKET_MULTICAST)
|
|
return;
|
|
|
|
/* Only these types are recognized */
|
|
if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY))
|
|
return;
|
|
|
|
/* 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;
|
|
|
|
/* Protocol must be IP. */
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
if (dst.s_addr != src_ip.s_addr)
|
|
return;
|
|
if (!(option_mask32 & DAD)) {
|
|
if ((src.s_addr != dst_ip.s_addr)
|
|
|| (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln)))
|
|
return;
|
|
} 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.
|
|
*/
|
|
if ((memcmp(p, &me.sll_addr, me.sll_halen) == 0)
|
|
|| (src.s_addr && src.s_addr != dst_ip.s_addr))
|
|
return;
|
|
}
|
|
if (!(option_mask32 & QUIET)) {
|
|
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]
|
|
);
|
|
if (dst_ip.s_addr != src.s_addr) {
|
|
printf("for %s", inet_ntoa(dst_ip));
|
|
s_printed = 1;
|
|
}
|
|
if (memcmp(p + ah->ar_hln + 4, me.sll_addr, ah->ar_hln)) {
|
|
unsigned char *pp = p + ah->ar_hln + 4;
|
|
if (!s_printed)
|
|
printf(" for");
|
|
printf(" [%02x:%02x:%02x:%02x:%02x:%02x]",
|
|
pp[0], pp[1], pp[2], pp[3], pp[4], pp[5]
|
|
);
|
|
}
|
|
|
|
if (last) {
|
|
unsigned diff = MONOTONIC_US() - last;
|
|
printf(" %u.%03ums\n", diff / 1000, diff % 1000);
|
|
} else {
|
|
puts(" UNSOLICITED?");
|
|
}
|
|
fflush_all();
|
|
}
|
|
received++;
|
|
if (FROM->sll_pkttype != PACKET_HOST)
|
|
brd_recv++;
|
|
if (ah->ar_op == htons(ARPOP_REQUEST))
|
|
req_recv++;
|
|
if (option_mask32 & QUIT_ON_REPLY)
|
|
finish();
|
|
if (!(option_mask32 & BCAST_ONLY)) {
|
|
memcpy(he.sll_addr, p, me.sll_halen);
|
|
option_mask32 |= UNICASTING;
|
|
}
|
|
}
|
|
|
|
int arping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
|
int arping_main(int argc UNUSED_PARAM, char **argv)
|
|
{
|
|
const char *device = "eth0";
|
|
char *source = NULL;
|
|
char *target;
|
|
char *err_str;
|
|
|
|
INIT_G();
|
|
|
|
xmove_fd(xsocket(AF_PACKET, SOCK_DGRAM, 0), sock_fd);
|
|
|
|
// If you ever change BB_SUID_DROP to BB_SUID_REQUIRE,
|
|
// drop suid root privileges here:
|
|
//xsetuid(getuid());
|
|
|
|
{
|
|
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;
|
|
}
|
|
|
|
target = argv[optind];
|
|
err_str = xasprintf("interface %s %%s", device);
|
|
xfunc_error_retval = 2;
|
|
|
|
/*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;
|
|
|
|
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);
|
|
}
|
|
|
|
/* if (!inet_aton(target, &dst)) - not needed */ {
|
|
len_and_sockaddr *lsa;
|
|
lsa = xhost_and_af2sockaddr(target, 0, AF_INET);
|
|
dst = lsa->u.sin.sin_addr;
|
|
if (ENABLE_FEATURE_CLEAN_UP)
|
|
free(lsa);
|
|
}
|
|
|
|
if (source && !inet_aton(source, &src)) {
|
|
bb_error_msg_and_die("invalid source address %s", source);
|
|
}
|
|
|
|
if ((option_mask32 & (DAD|UNSOLICITED)) == UNSOLICITED && src.s_addr == 0)
|
|
src = dst;
|
|
|
|
if (!(option_mask32 & DAD) || src.s_addr) {
|
|
/*struct sockaddr_in probe_saddr;*/
|
|
int probe_fd = xsocket(AF_INET, SOCK_DGRAM, 0);
|
|
|
|
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;
|
|
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;
|
|
|
|
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)
|
|
bb_simple_error_msg_and_die("no IP address configured");
|
|
src = G.probe_saddr.sin_addr;
|
|
}
|
|
close(probe_fd);
|
|
}
|
|
|
|
me.sll_family = AF_PACKET;
|
|
//me.sll_ifindex = ifindex; - done before
|
|
me.sll_protocol = htons(ETH_P_ARP);
|
|
xbind(sock_fd, (struct sockaddr *) &me, sizeof(me));
|
|
|
|
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");
|
|
if (me.sll_halen == 0) {
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
|
|
/*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);
|
|
|
|
/* Send the first packet, arm ALRM */
|
|
catcher();
|
|
|
|
while (1) {
|
|
struct sockaddr_ll from;
|
|
socklen_t alen = sizeof(from);
|
|
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) {
|
|
bb_simple_perror_msg("recvfrom");
|
|
continue;
|
|
}
|
|
recv_pack(G.packet, cc, &from);
|
|
}
|
|
}
|