commit ec8f8d2b86d1dc8300a7a72922302724c19416c0
Author: Lars Ellenberg <lars.ellenberg@linbit.com>
Date:   Wed Aug 5 15:33:10 2015 +0200

    send_arp: fix for infiniband, re-merge from upstream iputils arping
    
    send_arp.linux.c was created to avoid a libnet dependency.
    It was Based on arping from iputils as it was back then,
    and adapted to the command line conventions established by
    the libnet based send_arp by Matt Soffen,
    as it was used by IPaddr and IPaddr2.
    
    On infiniband, the link layer addresses won't fit into a sockaddr_ll,
    causing a buffer overflow.
    The broadcast address may be different from "all 0xff".
    
    This (and some more) has been fixed meanwhile in iputils arping.
    
    This is just a re-merge from iputils, keeping the few adaptations we
    need for IPaddr2 command line conventions, and some static/inline/const
    fixes to quiescen the compiler warnings.

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>
 
+#ifdef USE_SYSFS
+#include <sysfs/libsysfs.h>
+struct sysfs_devattr_values;
+#endif
+
+#ifndef WITHOUT_IFADDRS
+#include <ifaddrs.h>
+#endif
+
+#ifdef USE_IDN
+#include <idna.h>
+#include <locale.h>
+#endif
+
+static char SNAPSHOT[] = "s20121221";
+
 static void usage(void) __attribute__((noreturn));
 
-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 DEFAULT_DEVICE
+#define DEFAULT_DEVICE "eth0"
+#endif
+#ifdef DEFAULT_DEVICE
+# define DEFAULT_DEVICE_STR	DEFAULT_DEVICE
+#else
+# define DEFAULT_DEVICE		NULL
+#endif
 
-static struct sockaddr_ll me;
-static struct sockaddr_ll he;
+struct device {
+	const char *name;
+	int ifindex;
+#ifndef WITHOUT_IFADDRS
+	struct ifaddrs *ifa;
+#endif
+#ifdef USE_SYSFS
+	struct sysfs_devattr_values *sysfs;
+#endif
+};
 
-static struct timeval start, last;
+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;
 
-static int sent, brd_sent;
-static int received, brd_recv, req_recv;
+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);
 	}
 }
