You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1189 lines
27 KiB

diff --git a/tools/send_arp.linux.c b/tools/send_arp.linux.c
index e1c1960..477100a 100644
--- a/tools/send_arp.linux.c
+++ b/tools/send_arp.linux.c
@@ -7,6 +7,23 @@
* 2 of the License, or (at your option) any later version.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
+ */
+
+/* Andrew Beekhof, Lars Ellenberg:
+ * Based on arping from iputils,
+ * adapted to the command line conventions established by the libnet based
+ * send_arp tool as used by the IPaddr and IPaddr2 resource agents.
+ * The libnet based send_arp, and its command line argument convention,
+ * was first added to the heartbeat project by Matt Soffen.
+ *
+ * Latest "resync" with iputils as of:
+ * git://git.linux-ipv6.org/gitroot/iputils.git
+ * 511f8356e22615479c3cc16bca64d72d204f6df3
+ * Fri Jul 24 10:48:47 2015
+ * To get various bugfixes and support for infiniband and other link layer
+ * addresses which do not fit into plain "sockaddr_ll", and broadcast addresses
+ * that may be different from memset(,0xff,).
*/
#include <stdlib.h>
@@ -16,12 +33,17 @@
#include <sys/file.h>
#include <sys/time.h>
#include <sys/signal.h>
+#include <signal.h>
#include <sys/ioctl.h>
-#include <linux/if.h>
+#include <net/if.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <net/if_arp.h>
#include <sys/uio.h>
+#ifdef CAPABILITIES
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#endif
#include <netdb.h>
#include <unistd.h>
@@ -32,40 +54,85 @@
#include <netinet/in.h>
#include <arpa/inet.h>
-static void usage(void) __attribute__((noreturn));
+#ifdef USE_SYSFS
+#include <sysfs/libsysfs.h>
+struct sysfs_devattr_values;
+#endif
-static int quit_on_reply;
-static char *device;
-static int ifindex;
-static char *source;
-static struct in_addr src, dst;
-static char *target;
-static int dad = 0, unsolicited = 0, advert = 0;
-static int quiet = 0;
-static int count = -1;
-static int timeout = 0;
-static int unicasting = 0;
-static int s = 0;
-static int broadcast_only = 0;
+#ifndef WITHOUT_IFADDRS
+#include <ifaddrs.h>
+#endif
-static struct sockaddr_ll me;
-static struct sockaddr_ll he;
+#ifdef USE_IDN
+#include <idna.h>
+#include <locale.h>
+#endif
-static struct timeval start, last;
+static char SNAPSHOT[] = "s20121221";
-static int sent, brd_sent;
-static int received, brd_recv, req_recv;
+static void usage(void) __attribute__((noreturn));
+
+#ifndef DEFAULT_DEVICE
+#define DEFAULT_DEVICE "eth0"
+#endif
+#ifdef DEFAULT_DEVICE
+# define DEFAULT_DEVICE_STR DEFAULT_DEVICE
+#else
+# define DEFAULT_DEVICE NULL
+#endif
+
+struct device {
+ const char *name;
+ int ifindex;
+#ifndef WITHOUT_IFADDRS
+ struct ifaddrs *ifa;
+#endif
+#ifdef USE_SYSFS
+ struct sysfs_devattr_values *sysfs;
+#endif
+};
+
+int quit_on_reply=0;
+struct device device = {
+ .name = DEFAULT_DEVICE,
+};
+char *source;
+struct in_addr src, dst;
+char *target;
+int dad, unsolicited, advert;
+int quiet;
+int count=-1;
+int timeout;
+int unicasting;
+int s;
+int broadcast_only;
+
+struct sockaddr_storage me;
+struct sockaddr_storage he;
+
+struct timeval start, last;
+
+int sent, brd_sent;
+int received, brd_recv, req_recv;
+
+#ifndef CAPABILITIES
+static uid_t euid;
+#endif
#define MS_TDIFF(tv1,tv2) ( ((tv1).tv_sec-(tv2).tv_sec)*1000 + \
((tv1).tv_usec-(tv2).tv_usec)/1000 )
-static void print_hex(unsigned char *p, int len);
-static int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM);
-static void set_signal(int signo, void (*handler)(void));
-static int send_pack(int s, struct in_addr src, struct in_addr dst,
- struct sockaddr_ll *ME, struct sockaddr_ll *HE);
-static void finish(void);
-static void catcher(void);
+#define OFFSET_OF(name,ele) ((size_t)&((name *)0)->ele)
+
+static socklen_t sll_len(size_t halen)
+{
+ socklen_t len = OFFSET_OF(struct sockaddr_ll, sll_addr) + halen;
+ if (len < sizeof(struct sockaddr_ll))
+ len = sizeof(struct sockaddr_ll);
+ return len;
+}
+
+#define SLL_LEN(hln) sll_len(hln)
void usage(void)
{
@@ -80,14 +147,18 @@ void usage(void)
" -V : print version and exit\n"
" -c count : how many packets to send\n"
" -w timeout : how long to wait for a reply\n"
- " -I device : which ethernet device to use (eth0)\n"
+ " -I device : which ethernet device to use"
+#ifdef DEFAULT_DEVICE_STR
+ " (" DEFAULT_DEVICE_STR ")"
+#endif
+ "\n"
" -s source : source ip address\n"
" destination : ask for what ip address\n"
);
exit(2);
}
-void set_signal(int signo, void (*handler)(void))
+static void set_signal(int signo, void (*handler)(void))
{
struct sigaction sa;
@@ -97,7 +168,126 @@ void set_signal(int signo, void (*handler)(void))
sigaction(signo, &sa, NULL);
}
-int send_pack(int s, struct in_addr src, struct in_addr dst,
+#ifdef CAPABILITIES
+static const cap_value_t caps[] = { CAP_NET_RAW, };
+static cap_flag_value_t cap_raw = CAP_CLEAR;
+#endif
+
+static void limit_capabilities(void)
+{
+#ifdef CAPABILITIES
+ cap_t cap_p;
+
+ cap_p = cap_get_proc();
+ if (!cap_p) {
+ perror("arping: cap_get_proc");
+ exit(-1);
+ }
+
+ cap_get_flag(cap_p, CAP_NET_RAW, CAP_PERMITTED, &cap_raw);
+
+ if (cap_raw != CAP_CLEAR) {
+ if (cap_clear(cap_p) < 0) {
+ perror("arping: cap_clear");
+ exit(-1);
+ }
+
+ cap_set_flag(cap_p, CAP_PERMITTED, 1, caps, CAP_SET);
+
+ if (cap_set_proc(cap_p) < 0) {
+ perror("arping: cap_set_proc");
+ if (errno != EPERM)
+ exit(-1);
+ }
+ }
+
+ if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+ perror("arping: prctl");
+ exit(-1);
+ }
+
+ if (setuid(getuid()) < 0) {
+ perror("arping: setuid");
+ exit(-1);
+ }
+
+ if (prctl(PR_SET_KEEPCAPS, 0) < 0) {
+ perror("arping: prctl");
+ exit(-1);
+ }
+
+ cap_free(cap_p);
+#else
+ euid = geteuid();
+#endif
+}
+
+static int modify_capability_raw(int on)
+{
+#ifdef CAPABILITIES
+ cap_t cap_p;
+
+ if (cap_raw != CAP_SET)
+ return on ? -1 : 0;
+
+ cap_p = cap_get_proc();
+ if (!cap_p) {
+ perror("arping: cap_get_proc");
+ return -1;
+ }
+
+ cap_set_flag(cap_p, CAP_EFFECTIVE, 1, caps, on ? CAP_SET : CAP_CLEAR);
+
+ if (cap_set_proc(cap_p) < 0) {
+ perror("arping: cap_set_proc");
+ return -1;
+ }
+
+ cap_free(cap_p);
+#else
+ if (setuid(on ? euid : getuid())) {
+ perror("arping: setuid");
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+static int enable_capability_raw(void)
+{
+ return modify_capability_raw(1);
+}
+
+static int disable_capability_raw(void)
+{
+ return modify_capability_raw(0);
+}
+
+static void drop_capabilities(void)
+{
+#ifdef CAPABILITIES
+ cap_t cap_p = cap_init();
+
+ if (!cap_p) {
+ perror("arping: cap_init");
+ exit(-1);
+ }
+
+ if (cap_set_proc(cap_p) < 0) {
+ perror("arping: cap_set_proc");
+ exit(-1);
+ }
+
+ cap_free(cap_p);
+#else
+ if (setuid(getuid()) < 0) {
+ perror("arping: setuid");
+ exit(-1);
+ }
+#endif
+}
+
+static int send_pack(int s, struct in_addr src, struct in_addr dst,
struct sockaddr_ll *ME, struct sockaddr_ll *HE)
{
int err;
@@ -130,7 +320,7 @@ int send_pack(int s, struct in_addr src, struct in_addr dst,
p+=4;
gettimeofday(&now, NULL);
- err = sendto(s, buf, p-buf, 0, (struct sockaddr*)HE, sizeof(*HE));
+ err = sendto(s, buf, p-buf, 0, (struct sockaddr*)HE, SLL_LEN(ah->ar_hln));
if (err == p-buf) {
last = now;
sent++;
@@ -140,7 +330,7 @@ int send_pack(int s, struct in_addr src, struct in_addr dst,
return err;
}
-void finish(void)
+static void finish(void)
{
if (!quiet) {
printf("Sent %d probes (%d broadcast(s))\n", sent, brd_sent);
@@ -158,40 +348,43 @@ void finish(void)
printf("\n");
fflush(stdout);
}
-
- if (dad) {
- fflush(stdout);
- exit(!!received);
- }
-
+ fflush(stdout);
+ if (dad)
+ exit(!!received);
if (unsolicited)
exit(0);
-
- fflush(stdout);
exit(!received);
}
-void catcher(void)
+static void catcher(void)
{
- struct timeval tv;
+ struct timeval tv, tv_s, tv_o;
gettimeofday(&tv, NULL);
if (start.tv_sec==0)
start = tv;
- if (count-- == 0 || (timeout && MS_TDIFF(tv,start) > timeout*1000 + 500))
+ timersub(&tv, &start, &tv_s);
+ tv_o.tv_sec = timeout;
+ tv_o.tv_usec = 500 * 1000;
+
+ if (count-- == 0 || (timeout && timercmp(&tv_s, &tv_o, >)))
finish();
- if (last.tv_sec==0 || MS_TDIFF(tv,last) > 500) {
- send_pack(s, src, dst, &me, &he);
+ timersub(&tv, &last, &tv_s);
+ tv_o.tv_sec = 0;
+
+ if (last.tv_sec==0 || timercmp(&tv_s, &tv_o, >)) {
+ send_pack(s, src, dst,
+ (struct sockaddr_ll *)&me, (struct sockaddr_ll *)&he);
if (count == 0 && unsolicited)
finish();
}
alarm(1);
}
-void print_hex(unsigned char *p, int len)
+static void print_hex(unsigned char *p, int len)
{
int i;
for (i=0; i<len; i++) {
@@ -201,7 +394,7 @@ void print_hex(unsigned char *p, int len)
}
}
-int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
+static int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
{
struct timeval tv;
struct arphdr *ah = (struct arphdr*)buf;
@@ -231,7 +424,7 @@ int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
return 0;
if (ah->ar_pln != 4)
return 0;
- if (ah->ar_hln != me.sll_halen)
+ if (ah->ar_hln != ((struct sockaddr_ll *)&me)->sll_halen)
return 0;
if (len < sizeof(*ah) + 2*(4 + ah->ar_hln))
return 0;
@@ -242,7 +435,7 @@ int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
return 0;
if (src.s_addr != dst_ip.s_addr)
return 0;
- if (memcmp(p+ah->ar_hln+4, &me.sll_addr, ah->ar_hln))
+ if (memcmp(p+ah->ar_hln+4, ((struct sockaddr_ll *)&me)->sll_addr, ah->ar_hln))
return 0;
} else {
/* DAD packet was:
@@ -260,7 +453,7 @@ int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
*/
if (src_ip.s_addr != dst.s_addr)
return 0;
- if (memcmp(p, &me.sll_addr, me.sll_halen) == 0)
+ if (memcmp(p, ((struct sockaddr_ll *)&me)->sll_addr, ((struct sockaddr_ll *)&me)->sll_halen) == 0)
return 0;
if (src.s_addr && src.s_addr != dst_ip.s_addr)
return 0;
@@ -276,7 +469,7 @@ int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
printf("for %s ", inet_ntoa(dst_ip));
s_printed = 1;
}
- if (memcmp(p+ah->ar_hln+4, me.sll_addr, ah->ar_hln)) {
+ if (memcmp(p+ah->ar_hln+4, ((struct sockaddr_ll *)&me)->sll_addr, ah->ar_hln)) {
if (!s_printed)
printf("for ");
printf("[");
@@ -299,16 +492,78 @@ int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
brd_recv++;
if (ah->ar_op == htons(ARPOP_REQUEST))
req_recv++;
- if (quit_on_reply)
+ if (quit_on_reply || (count == 0 && received == sent))
finish();
if(!broadcast_only) {
- memcpy(he.sll_addr, p, me.sll_halen);
+ memcpy(((struct sockaddr_ll *)&he)->sll_addr, p, ((struct sockaddr_ll *)&me)->sll_halen);
unicasting=1;
}
return 1;
}
-#include <signal.h>
+#ifdef USE_SYSFS
+union sysfs_devattr_value {
+ unsigned long ulong;
+ void *ptr;
+};
+
+enum {
+ SYSFS_DEVATTR_IFINDEX,
+ SYSFS_DEVATTR_FLAGS,
+ SYSFS_DEVATTR_ADDR_LEN,
+#if 0
+ SYSFS_DEVATTR_TYPE,
+ SYSFS_DEVATTR_ADDRESS,
+#endif
+ SYSFS_DEVATTR_BROADCAST,
+ SYSFS_DEVATTR_NUM
+};
+
+struct sysfs_devattr_values
+{
+ char *ifname;
+ union sysfs_devattr_value value[SYSFS_DEVATTR_NUM];
+};
+
+static int sysfs_devattr_ulong_dec(char *ptr, struct sysfs_devattr_values *v, unsigned idx);
+static int sysfs_devattr_ulong_hex(char *ptr, struct sysfs_devattr_values *v, unsigned idx);
+static int sysfs_devattr_macaddr(char *ptr, struct sysfs_devattr_values *v, unsigned idx);
+
+struct sysfs_devattrs {
+ const char *name;
+ int (*handler)(char *ptr, struct sysfs_devattr_values *v, unsigned int idx);
+ int free;
+} sysfs_devattrs[SYSFS_DEVATTR_NUM] = {
+ [SYSFS_DEVATTR_IFINDEX] = {
+ .name = "ifindex",
+ .handler = sysfs_devattr_ulong_dec,
+ },
+ [SYSFS_DEVATTR_ADDR_LEN] = {
+ .name = "addr_len",
+ .handler = sysfs_devattr_ulong_dec,
+ },
+ [SYSFS_DEVATTR_FLAGS] = {
+ .name = "flags",
+ .handler = sysfs_devattr_ulong_hex,
+ },
+#if 0
+ [SYSFS_DEVATTR_TYPE] = {
+ .name = "type",
+ .handler = sysfs_devattr_ulong_dec,
+ },
+ [SYSFS_DEVATTR_ADDRESS] = {
+ .name = "address",
+ .handler = sysfs_devattr_macaddr,
+ .free = 1,
+ },
+#endif
+ [SYSFS_DEVATTR_BROADCAST] = {
+ .name = "broadcast",
+ .handler = sysfs_devattr_macaddr,
+ .free = 1,
+ },
+};
+#endif
static void byebye(int nsig)
{
@@ -317,26 +572,477 @@ static void byebye(int nsig)
exit(nsig);
}
+/*
+ * find_device()
+ *
+ * This function checks 1) if the device (if given) is okay for ARP,
+ * or 2) find fist appropriate device on the system.
+ *
+ * Return value:
+ * >0 : Succeeded, and appropriate device not found.
+ * device.ifindex remains 0.
+ * 0 : Succeeded, and approptiate device found.
+ * device.ifindex is set.
+ * <0 : Failed. Support not found, or other
+ * : system error. Try other method.
+ *
+ * If an appropriate device found, it is recorded inside the
+ * "device" variable for later reference.
+ *
+ * We have several implementations for this.
+ * by_ifaddrs(): requires getifaddr() in glibc, and rtnetlink in
+ * kernel. default and recommended for recent systems.
+ * by_sysfs(): requires libsysfs , and sysfs in kernel.
+ * by_ioctl(): unable to list devices without ipv4 address; this
+ * means, you need to supply the device name for
+ * DAD purpose.
+ */
+/* Common check for ifa->ifa_flags */
+static int check_ifflags(unsigned int ifflags, int fatal)
+{
+ if (!(ifflags & IFF_UP)) {
+ if (fatal) {
+ if (!quiet)
+ printf("Interface \"%s\" is down\n", device.name);
+ exit(2);
+ }
+ return -1;
+ }
+ if (ifflags & (IFF_NOARP | IFF_LOOPBACK)) {
+ if (fatal) {
+ if (!quiet)
+ printf("Interface \"%s\" is not ARPable\n", device.name);
+ exit(dad ? 0 : 2);
+ }
+ return -1;
+ }
+ return 0;
+}
+
+static int find_device_by_ifaddrs(void)
+{
+#ifndef WITHOUT_IFADDRS
+ int rc;
+ struct ifaddrs *ifa0, *ifa;
+ int count = 0;
+
+ rc = getifaddrs(&ifa0);
+ if (rc) {
+ perror("getifaddrs");
+ return -1;
+ }
+
+ for (ifa = ifa0; ifa; ifa = ifa->ifa_next) {
+ if (!ifa->ifa_addr)
+ continue;
+ if (ifa->ifa_addr->sa_family != AF_PACKET)
+ continue;
+ if (device.name && ifa->ifa_name && strcmp(ifa->ifa_name, device.name))
+ continue;
+
+ if (check_ifflags(ifa->ifa_flags, device.name != NULL) < 0)
+ continue;
+
+ if (!((struct sockaddr_ll *)ifa->ifa_addr)->sll_halen)
+ continue;
+ if (!ifa->ifa_broadaddr)
+ continue;
+
+ device.ifa = ifa;
+
+ if (count++)
+ break;
+ }
+
+ if (count == 1 && device.ifa) {
+ device.ifindex = if_nametoindex(device.ifa->ifa_name);
+ if (!device.ifindex) {
+ perror("arping: if_nametoindex");
+ freeifaddrs(ifa0);
+ return -1;
+ }
+ device.name = device.ifa->ifa_name;
+ return 0;
+ }
+ return 1;
+#else
+ return -1;
+#endif
+}
+
+#ifdef USE_SYSFS
+static void sysfs_devattr_values_init(struct sysfs_devattr_values *v, int do_free)
+{
+ int i;
+ if (do_free) {
+ free(v->ifname);
+ for (i = 0; i < SYSFS_DEVATTR_NUM; i++) {
+ if (sysfs_devattrs[i].free)
+ free(v->value[i].ptr);
+ }
+ }
+ memset(v, 0, sizeof(*v));
+}
+
+static int sysfs_devattr_ulong(char *ptr, struct sysfs_devattr_values *v, unsigned int idx,
+ unsigned int base)
+{
+ unsigned long *p;
+ char *ep;
+
+ if (!ptr || !v)
+ return -1;
+
+ p = &v->value[idx].ulong;
+ errno = 0;
+ *p = strtoul(ptr, &ep, base);
+ if ((*ptr && isspace(*ptr & 0xff)) || errno || (*ep != '\0' && *ep != '\n'))
+ goto out;
+
+ return 0;
+out:
+ return -1;
+}
+
+static int sysfs_devattr_ulong_dec(char *ptr, struct sysfs_devattr_values *v, unsigned int idx)
+{
+ int rc = sysfs_devattr_ulong(ptr, v, idx, 10);
+ return rc;
+}
+
+static int sysfs_devattr_ulong_hex(char *ptr, struct sysfs_devattr_values *v, unsigned int idx)
+{
+ int rc = sysfs_devattr_ulong(ptr, v, idx, 16);
+ return rc;
+}
+
+static int sysfs_devattr_macaddr(char *ptr, struct sysfs_devattr_values *v, unsigned int idx)
+{
+ unsigned char *m;
+ int i;
+ unsigned int addrlen;
+
+ if (!ptr || !v)
+ return -1;
+
+ addrlen = v->value[SYSFS_DEVATTR_ADDR_LEN].ulong;
+ m = malloc(addrlen);
+
+ for (i = 0; i < addrlen; i++) {
+ if (i && *(ptr + i * 3 - 1) != ':')
+ goto out;
+ if (sscanf(ptr + i * 3, "%02hhx", &m[i]) != 1)
+ goto out;
+ }
+
+ v->value[idx].ptr = m;
+ return 0;
+out:
+ free(m);
+ return -1;
+}
+#endif
+
+static int find_device_by_sysfs(void)
+{
+ int rc = -1;
+#ifdef USE_SYSFS
+ struct sysfs_class *cls_net;
+ struct dlist *dev_list;
+ struct sysfs_class_device *dev;
+ struct sysfs_attribute *dev_attr;
+ struct sysfs_devattr_values sysfs_devattr_values;
+ int count = 0;
+
+ if (!device.sysfs) {
+ device.sysfs = malloc(sizeof(*device.sysfs));
+ sysfs_devattr_values_init(device.sysfs, 0);
+ }
+
+ cls_net = sysfs_open_class("net");
+ if (!cls_net) {
+ perror("sysfs_open_class");
+ return -1;
+ }
+
+ dev_list = sysfs_get_class_devices(cls_net);
+ if (!dev_list) {
+ perror("sysfs_get_class_devices");
+ goto out;
+ }
+
+ sysfs_devattr_values_init(&sysfs_devattr_values, 0);
+
+ dlist_for_each_data(dev_list, dev, struct sysfs_class_device) {
+ int i;
+ int rc = -1;
+
+ if (device.name && strcmp(dev->name, device.name))
+ goto do_next;
+
+ sysfs_devattr_values_init(&sysfs_devattr_values, 1);
+
+ for (i = 0; i < SYSFS_DEVATTR_NUM; i++) {
+
+ dev_attr = sysfs_get_classdev_attr(dev, sysfs_devattrs[i].name);
+ if (!dev_attr) {
+ perror("sysfs_get_classdev_attr");
+ rc = -1;
+ break;
+ }
+ if (sysfs_read_attribute(dev_attr)) {
+ perror("sysfs_read_attribute");
+ rc = -1;
+ break;
+ }
+ rc = sysfs_devattrs[i].handler(dev_attr->value, &sysfs_devattr_values, i);
+
+ if (rc < 0)
+ break;
+ }
+
+ if (rc < 0)
+ goto do_next;
+
+ if (check_ifflags(sysfs_devattr_values.value[SYSFS_DEVATTR_FLAGS].ulong,
+ device.name != NULL) < 0)
+ goto do_next;
+
+ if (!sysfs_devattr_values.value[SYSFS_DEVATTR_ADDR_LEN].ulong)
+ goto do_next;
+
+ if (device.sysfs->value[SYSFS_DEVATTR_IFINDEX].ulong) {
+ if (device.sysfs->value[SYSFS_DEVATTR_FLAGS].ulong & IFF_RUNNING)
+ goto do_next;
+ }
+
+ sysfs_devattr_values.ifname = strdup(dev->name);
+ if (!sysfs_devattr_values.ifname) {
+ perror("malloc");
+ goto out;
+ }
+
+ sysfs_devattr_values_init(device.sysfs, 1);
+ memcpy(device.sysfs, &sysfs_devattr_values, sizeof(*device.sysfs));
+ sysfs_devattr_values_init(&sysfs_devattr_values, 0);
+
+ if (count++)
+ break;
+
+ continue;
+do_next:
+ sysfs_devattr_values_init(&sysfs_devattr_values, 1);
+ }
+
+ if (count == 1) {
+ device.ifindex = device.sysfs->value[SYSFS_DEVATTR_IFINDEX].ulong;
+ device.name = device.sysfs->ifname;
+ }
+ rc = !device.ifindex;
+out:
+ sysfs_close_class(cls_net);
+#endif
+ return rc;
+}
+
+static int check_device_by_ioctl(int s, struct ifreq *ifr)
+{
+ if (ioctl(s, SIOCGIFFLAGS, ifr) < 0) {
+ perror("ioctl(SIOCGIFINDEX");
+ return -1;
+ }
+
+ if (check_ifflags(ifr->ifr_flags, device.name != NULL) < 0)
+ return 1;
+
+ if (ioctl(s, SIOCGIFINDEX, ifr) < 0) {
+ perror("ioctl(SIOCGIFINDEX");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int find_device_by_ioctl(void)
+{
+ int s;
+ struct ifreq *ifr0, *ifr, *ifr_end;
+ size_t ifrsize = sizeof(*ifr);
+ struct ifconf ifc;
+ static struct ifreq ifrbuf;
+ int count = 0;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ memset(&ifrbuf, 0, sizeof(ifrbuf));
+
+ if (device.name) {
+ strncpy(ifrbuf.ifr_name, device.name, sizeof(ifrbuf.ifr_name) - 1);
+ if (check_device_by_ioctl(s, &ifrbuf))
+ goto out;
+ count++;
+ } else {
+ do {
+ int rc;
+ ifr0 = malloc(ifrsize);
+ if (!ifr0) {
+ perror("malloc");
+ goto out;
+ }
+
+ ifc.ifc_buf = (char *)ifr0;
+ ifc.ifc_len = ifrsize;
+
+ rc = ioctl(s, SIOCGIFCONF, &ifc);
+ if (rc < 0) {
+ perror("ioctl(SIOCFIFCONF");
+ goto out;
+ }
+
+ if (ifc.ifc_len + sizeof(*ifr0) + sizeof(struct sockaddr_storage) - sizeof(struct sockaddr) <= ifrsize)
+ break;
+ ifrsize *= 2;
+ free(ifr0);
+ ifr0 = NULL;
+ } while(ifrsize < INT_MAX / 2);
+
+ if (!ifr0) {
+ fprintf(stderr, "arping: too many interfaces!?\n");
+ goto out;
+ }
+
+ ifr_end = (struct ifreq *)(((char *)ifr0) + ifc.ifc_len - sizeof(*ifr0));
+ for (ifr = ifr0; ifr <= ifr_end; ifr++) {
+ if (check_device_by_ioctl(s, &ifrbuf))
+ continue;
+ memcpy(&ifrbuf.ifr_name, ifr->ifr_name, sizeof(ifrbuf.ifr_name));
+ if (count++)
+ break;
+ }
+ }
+
+ close(s);
+
+ if (count == 1) {
+ device.ifindex = ifrbuf.ifr_ifindex;
+ device.name = ifrbuf.ifr_name;
+ }
+ return !device.ifindex;
+out:
+ close(s);
+ return -1;
+}
+
+static int find_device(void)
+{
+ int rc;
+ rc = find_device_by_ifaddrs();
+ if (rc >= 0)
+ goto out;
+ rc = find_device_by_sysfs();
+ if (rc >= 0)
+ goto out;
+ rc = find_device_by_ioctl();
+out:
+ return rc;
+}
+
+/*
+ * set_device_broadcast()
+ *
+ * This fills the device "broadcast address"
+ * based on information found by find_device() funcion.
+ */
+static int set_device_broadcast_ifaddrs_one(struct device *device, unsigned char *ba, size_t balen, int fatal)
+{
+#ifndef WITHOUT_IFADDRS
+ struct ifaddrs *ifa;
+ struct sockaddr_ll *sll;
+
+ if (!device)
+ return -1;
+
+ ifa = device->ifa;
+ if (!ifa)
+ return -1;
+
+ sll = (struct sockaddr_ll *)ifa->ifa_broadaddr;
+
+ if (sll->sll_halen != balen) {
+ if (fatal) {
+ if (!quiet)
+ printf("Address length does not match...\n");
+ exit(2);
+ }
+ return -1;
+ }
+ memcpy(ba, sll->sll_addr, sll->sll_halen);
+ return 0;
+#else
+ return -1;
+#endif
+}
+static int set_device_broadcast_sysfs(struct device *device, unsigned char *ba, size_t balen)
+{
+#ifdef USE_SYSFS
+ struct sysfs_devattr_values *v;
+ if (!device)
+ return -1;
+ v = device->sysfs;
+ if (!v)
+ return -1;
+ if (v->value[SYSFS_DEVATTR_ADDR_LEN].ulong != balen)
+ return -1;
+ memcpy(ba, v->value[SYSFS_DEVATTR_BROADCAST].ptr, balen);
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+static int set_device_broadcast_fallback(struct device *device, unsigned char *ba, size_t balen)
+{
+ if (!quiet)
+ fprintf(stderr, "WARNING: using default broadcast address.\n");
+ memset(ba, -1, balen);
+ return 0;
+}
+
+static void set_device_broadcast(struct device *dev, unsigned char *ba, size_t balen)
+{
+ if (!set_device_broadcast_ifaddrs_one(dev, ba, balen, 0))
+ return;
+ if (!set_device_broadcast_sysfs(dev, ba, balen))
+ return;
+ set_device_broadcast_fallback(dev, ba, balen);
+}
+
int
main(int argc, char **argv)
{
int socket_errno;
int ch;
- uid_t uid = getuid();
int hb_mode = 0;
signal(SIGTERM, byebye);
signal(SIGPIPE, byebye);
-
- device = strdup("eth0");
-
+
+ limit_capabilities();
+
+#ifdef USE_IDN
+ setlocale(LC_ALL, "");
+#endif
+
+ enable_capability_raw();
+
s = socket(PF_PACKET, SOCK_DGRAM, 0);
socket_errno = errno;
- if (setuid(uid)) {
- perror("arping: setuid");
- exit(-1);
- }
+ disable_capability_raw();
while ((ch = getopt(argc, argv, "h?bfDUAqc:w:s:I:Vr:i:p:")) != EOF) {
switch(ch) {
@@ -367,7 +1073,7 @@ main(int argc, char **argv)
timeout = atoi(optarg);
break;
case 'I':
- device = optarg;
+ device.name = optarg;
break;
case 'f':
quit_on_reply=1;
@@ -376,7 +1082,7 @@ main(int argc, char **argv)
source = optarg;
break;
case 'V':
- printf("send_arp utility\n");
+ printf("send_arp utility, based on arping from iputils-%s\n", SNAPSHOT);
exit(0);
case 'p':
case 'i':
@@ -405,7 +1111,7 @@ main(int argc, char **argv)
*/
unsolicited = 1;
- device = argv[optind];
+ device.name = argv[optind];
target = argv[optind+1];
} else {
@@ -417,10 +1123,8 @@ main(int argc, char **argv)
target = *argv;
}
- if (device == NULL) {
- fprintf(stderr, "arping: device (option -I) is required\n");
- usage();
- }
+ if (device.name && !*device.name)
+ device.name = NULL;
if (s < 0) {
errno = socket_errno;
@@ -428,39 +1132,42 @@ main(int argc, char **argv)
exit(2);
}
- if (1) {
- struct ifreq ifr;
- memset(&ifr, 0, sizeof(ifr));
- strncpy(ifr.ifr_name, device, IFNAMSIZ-1);
- if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
- fprintf(stderr, "arping: unknown iface %s\n", device);
- exit(2);
- }
- ifindex = ifr.ifr_ifindex;
+ if (find_device() < 0)
+ exit(2);
- if (ioctl(s, SIOCGIFFLAGS, (char*)&ifr)) {
- perror("ioctl(SIOCGIFFLAGS)");
+ if (!device.ifindex) {
+ if (device.name) {
+ fprintf(stderr, "arping: Device %s not available.\n", device.name);
exit(2);
}
- if (!(ifr.ifr_flags&IFF_UP)) {
- if (!quiet)
- printf("Interface \"%s\" is down\n", device);
- exit(2);
- }
- if (ifr.ifr_flags&(IFF_NOARP|IFF_LOOPBACK)) {
- if (!quiet)
- printf("Interface \"%s\" is not ARPable\n", device);
- exit(dad?0:2);
- }
+ fprintf(stderr, "arping: device (option -I) is required.\n");
+ usage();
}
if (inet_aton(target, &dst) != 1) {
struct hostent *hp;
- hp = gethostbyname2(target, AF_INET);
+ char *idn = target;
+#ifdef USE_IDN
+ int rc;
+
+ rc = idna_to_ascii_lz(target, &idn, 0);
+
+ if (rc != IDNA_SUCCESS) {
+ fprintf(stderr, "arping: IDN encoding failed: %s\n", idna_strerror(rc));
+ exit(2);
+ }
+#endif
+
+ hp = gethostbyname2(idn, AF_INET);
if (!hp) {
fprintf(stderr, "arping: unknown host %s\n", target);
exit(2);
}
+
+#ifdef USE_IDN
+ free(idn);
+#endif
+
memcpy(&dst, hp->h_addr, 4);
}
@@ -480,9 +1187,13 @@ main(int argc, char **argv)
perror("socket");
exit(2);
}
- if (device) {
- if (setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1)
+ if (device.name) {
+ enable_capability_raw();
+
+ if (setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device.name, strlen(device.name)+1) == -1)
perror("WARNING: interface is ignored");
+
+ disable_capability_raw();
}
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
@@ -514,9 +1225,9 @@ main(int argc, char **argv)
close(probe_fd);
};
- me.sll_family = AF_PACKET;
- me.sll_ifindex = ifindex;
- me.sll_protocol = htons(ETH_P_ARP);
+ ((struct sockaddr_ll *)&me)->sll_family = AF_PACKET;
+ ((struct sockaddr_ll *)&me)->sll_ifindex = device.ifindex;
+ ((struct sockaddr_ll *)&me)->sll_protocol = htons(ETH_P_ARP);
if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) {
perror("bind");
exit(2);
@@ -529,18 +1240,20 @@ main(int argc, char **argv)
exit(2);
}
}
- if (me.sll_halen == 0) {
+ if (((struct sockaddr_ll *)&me)->sll_halen == 0) {
if (!quiet)
- printf("Interface \"%s\" is not ARPable (no ll address)\n", device);
+ printf("Interface \"%s\" is not ARPable (no ll address)\n", device.name);
exit(dad?0:2);
}
he = me;
- memset(he.sll_addr, -1, he.sll_halen);
+
+ set_device_broadcast(&device, ((struct sockaddr_ll *)&he)->sll_addr,
+ ((struct sockaddr_ll *)&he)->sll_halen);
if (!quiet) {
printf("ARPING %s ", inet_ntoa(dst));
- printf("from %s %s\n", inet_ntoa(src), device ? : "");
+ printf("from %s %s\n", inet_ntoa(src), device.name ? : "");
}
if (!src.s_addr && !dad) {
@@ -548,6 +1261,8 @@ main(int argc, char **argv)
exit(2);
}
+ drop_capabilities();
+
set_signal(SIGINT, finish);
set_signal(SIGALRM, catcher);
@@ -556,7 +1271,7 @@ main(int argc, char **argv)
while(1) {
sigset_t sset, osset;
unsigned char packet[4096];
- struct sockaddr_ll from;
+ struct sockaddr_storage from;
socklen_t alen = sizeof(from);
int cc;
@@ -565,11 +1280,12 @@ main(int argc, char **argv)
perror("arping: recvfrom");
continue;
}
+
sigemptyset(&sset);
sigaddset(&sset, SIGALRM);
sigaddset(&sset, SIGINT);
sigprocmask(SIG_BLOCK, &sset, &osset);
- recv_pack(packet, cc, &from);
+ recv_pack(packet, cc, (struct sockaddr_ll *)&from);
sigprocmask(SIG_SETMASK, &osset, NULL);
}
}