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.
1188 lines
27 KiB
1188 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); |
|
} |
|
}
|
|
|