/* * arping.c * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Authors: Alexey Kuznetsov, * YOSHIFUJI Hideaki */ /* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CAPABILITIES #include #include #endif #include #include #include #include #include #include #include #include #ifdef USE_SYSFS #include struct sysfs_devattr_values; #endif #ifndef WITHOUT_IFADDRS #include #endif #ifdef USE_IDN #include #include #endif static char SNAPSHOT[] = "s20121221"; 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 ) #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) #if 1 /* hb_mode: always print hb_mode usage in this binary */ static char print_usage[]={ "send_arp: sends out custom ARP packet.\n" " usage: send_arp [-i repeatinterval-ms] [-r repeatcount] [-p pidfile] \\\n" " device src_ip_addr src_hw_addr broadcast_ip_addr netmask\n" "\n" " where:\n" " repeatinterval-ms: ignored\n" "\n" " repeatcount: how many ARP packets to send.\n" "\n" " pidfile: pid file to use\n" "\n" " device: network interface to use\n" "\n" " src_ip_addr: source ip address\n" "\n" " src_hw_addr: only \"auto\" is supported.\n" " If other specified, it will exit without sending any ARP packets.\n" "\n" " broadcast_ip_addr: ignored\n" "\n" " netmask: ignored\n" "\n" " Notes: Other options of iputils-arping may be accepted but it's not\n" " intended to be supported in this binary.\n" "\n" }; void usage(void) { fprintf(stderr, "%s\n", print_usage); exit(2); } #else /* hb_mode */ void usage(void) { fprintf(stderr, "Usage: arping [-fqbDUAV] [-c count] [-w timeout] [-I device] [-s source] destination\n" " -f : quit on first reply\n" " -q : be quiet\n" " -b : keep broadcasting, don't go unicast\n" " -D : duplicate address detection mode\n" " -U : Unsolicited ARP mode, update your neighbours\n" " -A : ARP answer mode, update your neighbours\n" " -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" #ifdef DEFAULT_DEVICE_STR " (" DEFAULT_DEVICE_STR ")" #endif "\n" " -s source : source ip address\n" " destination : ask for what ip address\n" ); exit(2); } #endif /* hb_mode */ static void set_signal(int signo, void (*handler)(void)) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = (void (*)(int))handler; sa.sa_flags = SA_RESTART; sigaction(signo, &sa, NULL); } #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; struct timeval now; unsigned char buf[256]; struct arphdr *ah = (struct arphdr*)buf; unsigned char *p = (unsigned char *)(ah+1); ah->ar_hrd = htons(ME->sll_hatype); if (ah->ar_hrd == htons(ARPHRD_FDDI)) ah->ar_hrd = htons(ARPHRD_ETHER); ah->ar_pro = htons(ETH_P_IP); ah->ar_hln = ME->sll_halen; ah->ar_pln = 4; ah->ar_op = advert ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST); memcpy(p, &ME->sll_addr, ah->ar_hln); p+=ME->sll_halen; memcpy(p, &src, 4); p+=4; if (advert) memcpy(p, &ME->sll_addr, ah->ar_hln); else memcpy(p, &HE->sll_addr, ah->ar_hln); p+=ah->ar_hln; memcpy(p, &dst, 4); p+=4; gettimeofday(&now, NULL); err = sendto(s, buf, p-buf, 0, (struct sockaddr*)HE, SLL_LEN(ah->ar_hln)); if (err == p-buf) { last = now; sent++; if (!unicasting) brd_sent++; } return err; } static void finish(void) { if (!quiet) { printf("Sent %d probes (%d broadcast(s))\n", sent, brd_sent); printf("Received %d response(s)", received); if (brd_recv || req_recv) { printf(" ("); if (req_recv) printf("%d request(s)", req_recv); if (brd_recv) printf("%s%d broadcast(s)", req_recv ? ", " : "", brd_recv); printf(")"); } printf("\n"); fflush(stdout); } fflush(stdout); if (dad) exit(!!received); if (unsolicited) exit(0); exit(!received); } static void catcher(void) { struct timeval tv, tv_s, tv_o; gettimeofday(&tv, NULL); if (start.tv_sec==0) start = tv; 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(); 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); } static void print_hex(unsigned char *p, int len) { int i; for (i=0; isll_pkttype != PACKET_HOST && FROM->sll_pkttype != PACKET_BROADCAST && FROM->sll_pkttype != PACKET_MULTICAST) return 0; /* Only these types are recognised */ if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY)) return 0; /* ARPHRD check and this darned FDDI hack here :-( */ if (ah->ar_hrd != htons(FROM->sll_hatype) && (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER))) return 0; /* Protocol must be IP. */ if (ah->ar_pro != htons(ETH_P_IP)) return 0; if (ah->ar_pln != 4) return 0; if (ah->ar_hln != ((struct sockaddr_ll *)&me)->sll_halen) return 0; if (len < sizeof(*ah) + 2*(4 + ah->ar_hln)) return 0; memcpy(&src_ip, p+ah->ar_hln, 4); memcpy(&dst_ip, p+ah->ar_hln+4+ah->ar_hln, 4); if (!dad) { if (src_ip.s_addr != dst.s_addr) return 0; if (src.s_addr != dst_ip.s_addr) return 0; if (memcmp(p+ah->ar_hln+4, ((struct sockaddr_ll *)&me)->sll_addr, ah->ar_hln)) return 0; } else { /* DAD packet was: src_ip = 0 (or some src) src_hw = ME dst_ip = tested address dst_hw = We fail, if receive request/reply with: src_ip = tested_address src_hw != ME if src_ip in request was not zero, check also that it matches to dst_ip, otherwise dst_ip/dst_hw do not matter. */ if (src_ip.s_addr != dst.s_addr) return 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; } if (!quiet) { int s_printed = 0; printf("%s ", FROM->sll_pkttype==PACKET_HOST ? "Unicast" : "Broadcast"); printf("%s from ", ah->ar_op == htons(ARPOP_REPLY) ? "reply" : "request"); printf("%s [", inet_ntoa(src_ip)); print_hex(p, ah->ar_hln); printf("] "); if (dst_ip.s_addr != src.s_addr) { printf("for %s ", inet_ntoa(dst_ip)); s_printed = 1; } if (memcmp(p+ah->ar_hln+4, ((struct sockaddr_ll *)&me)->sll_addr, ah->ar_hln)) { if (!s_printed) printf("for "); printf("["); print_hex(p+ah->ar_hln+4, ah->ar_hln); printf("]"); } if (last.tv_sec) { long usecs = (tv.tv_sec-last.tv_sec) * 1000000 + tv.tv_usec-last.tv_usec; long msecs = (usecs+500)/1000; usecs -= msecs*1000 - 500; printf(" %ld.%03ldms\n", msecs, usecs); } else { printf(" UNSOLICITED?\n"); } fflush(stdout); } received++; if (FROM->sll_pkttype != PACKET_HOST) brd_recv++; if (ah->ar_op == htons(ARPOP_REQUEST)) req_recv++; if (quit_on_reply || (count == 0 && received == sent)) finish(); if(!broadcast_only) { memcpy(((struct sockaddr_ll *)&he)->sll_addr, p, ((struct sockaddr_ll *)&me)->sll_halen); unicasting=1; } return 1; } #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) { /* Avoid an "error exit" log message if we're killed */ nsig = 0; 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; int hb_mode = 0; signal(SIGTERM, byebye); signal(SIGPIPE, byebye); limit_capabilities(); #ifdef USE_IDN setlocale(LC_ALL, ""); #endif enable_capability_raw(); s = socket(PF_PACKET, SOCK_DGRAM, 0); socket_errno = errno; disable_capability_raw(); while ((ch = getopt(argc, argv, "h?bfDUAqc:w:s:I:Vr:i:p:")) != EOF) { switch(ch) { case 'b': broadcast_only=1; break; case 'D': dad++; quit_on_reply=1; break; case 'U': unsolicited++; break; case 'A': advert++; unsolicited++; break; case 'q': quiet++; break; case 'r': /* send_arp.libnet compatibility option */ hb_mode = 1; /* fall-through */ case 'c': count = atoi(optarg); break; case 'w': timeout = atoi(optarg); break; case 'I': device.name = optarg; break; case 'f': quit_on_reply=1; break; case 's': source = optarg; break; case 'V': printf("send_arp utility, based on arping from iputils-%s\n", SNAPSHOT); exit(0); case 'p': case 'i': hb_mode = 1; /* send_arp.libnet compatibility options, ignore */ break; case 'h': case '?': default: usage(); } } if(hb_mode) { /* send_arp.libnet compatibility mode */ if (argc - optind != 5) { usage(); return 1; } /* * argv[optind+1] DEVICE dc0,eth0:0,hme0:0, * argv[optind+2] IP 192.168.195.186 * argv[optind+3] MAC ADDR 00a0cc34a878 * argv[optind+4] BROADCAST 192.168.195.186 * argv[optind+5] NETMASK ffffffffffff */ unsolicited = 1; device.name = argv[optind]; target = argv[optind+1]; if (strcmp(argv[optind+2], "auto")) { fprintf(stderr, "send_arp.linux: Gratuitous ARPs are not sent in the Cluster IP configuration\n"); /* return success to suppress an error log by the RA */ exit(0); } } else { argc -= optind; argv += optind; if (argc != 1) usage(); target = *argv; } if (device.name && !*device.name) device.name = NULL; if (s < 0) { errno = socket_errno; perror("arping: socket"); exit(2); } if (find_device() < 0) exit(2); if (!device.ifindex) { if (device.name) { fprintf(stderr, "arping: Device %s not available.\n", device.name); exit(2); } fprintf(stderr, "arping: device (option -I) is required.\n"); usage(); } if (inet_aton(target, &dst) != 1) { struct hostent *hp; 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); } if (source && inet_aton(source, &src) != 1) { fprintf(stderr, "arping: invalid source %s\n", source); exit(2); } if (!dad && unsolicited && src.s_addr == 0) src = dst; if (!dad || src.s_addr) { struct sockaddr_in saddr; int probe_fd = socket(AF_INET, SOCK_DGRAM, 0); if (probe_fd < 0) { perror("socket"); exit(2); } 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; if (src.s_addr) { saddr.sin_addr = src; if (bind(probe_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) { perror("bind"); exit(2); } } else if (!dad) { int on = 1; socklen_t alen = sizeof(saddr); saddr.sin_port = htons(1025); saddr.sin_addr = dst; if (setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, (char*)&on, sizeof(on)) == -1) perror("WARNING: setsockopt(SO_DONTROUTE)"); if (connect(probe_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) { perror("connect"); exit(2); } if (getsockname(probe_fd, (struct sockaddr*)&saddr, &alen) == -1) { perror("getsockname"); exit(2); } src = saddr.sin_addr; } close(probe_fd); }; ((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); } if (1) { socklen_t alen = sizeof(me); if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) { perror("getsockname"); exit(2); } } if (((struct sockaddr_ll *)&me)->sll_halen == 0) { if (!quiet) printf("Interface \"%s\" is not ARPable (no ll address)\n", device.name); exit(dad?0:2); } he = me; 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.name ? : ""); } if (!src.s_addr && !dad) { fprintf(stderr, "arping: no source address in not-DAD mode\n"); exit(2); } drop_capabilities(); set_signal(SIGINT, finish); set_signal(SIGALRM, catcher); catcher(); while(1) { sigset_t sset, osset; unsigned char packet[4096]; struct sockaddr_storage from; socklen_t alen = sizeof(from); int cc; if ((cc = recvfrom(s, packet, sizeof(packet), 0, (struct sockaddr *)&from, &alen)) < 0) { perror("arping: recvfrom"); continue; } sigemptyset(&sset); sigaddset(&sset, SIGALRM); sigaddset(&sset, SIGINT); sigprocmask(SIG_BLOCK, &sset, &osset); recv_pack(packet, cc, (struct sockaddr_ll *)&from); sigprocmask(SIG_SETMASK, &osset, NULL); } }