Blame SOURCES/bz1250728-send_arp-fix-buffer-overflow-on-infiniband.patch

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