diff options
Diffstat (limited to 'toys/pending/arping.c')
-rw-r--r-- | toys/pending/arping.c | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/toys/pending/arping.c b/toys/pending/arping.c new file mode 100644 index 00000000..75a691f7 --- /dev/null +++ b/toys/pending/arping.c @@ -0,0 +1,298 @@ +/* arping - send ARP REQUEST to a neighbour host. + * + * Copyright 2013 Sandeep Sharma <sandeep.jack2756@gmail.com> + * Copyright 2013 Kyungwan Han <asura321@gmail.com> + * + * No Standard. + +USE_ARPING(NEWTOY(arping, "<1>1s:I:w#<0c#<0AUDbqf[+AU][+Df]", TOYFLAG_USR|TOYFLAG_SBIN)) + +config ARPING + bool "arping" + default n + help + Usage: arping [-fqbDUA] [-c CNT] [-w TIMEOUT] [-I IFACE] [-s SRC_IP] DST_IP + + Send ARP requests/replies + + -f Quit on first ARP reply + -q Quiet + -b Keep broadcasting, don't go unicast + -D Duplicated address detection mode + -U Unsolicited ARP mode, update your neighbors + -A ARP answer mode, update your neighbors + -c N Stop after sending N ARP requests + -w TIMEOUT Time to wait for ARP reply, seconds + -I IFACE Interface to use (default eth0) + -s SRC_IP Sender IP address + DST_IP Target IP address +*/ +#define FOR_arping +#include "toys.h" +#include <netinet/ether.h> +#include <netpacket/packet.h> + +GLOBALS( + long count; + unsigned long time_out; + char *iface; + char *src_ip; + + int sockfd; + unsigned start; + unsigned end; + unsigned sent_at; + unsigned sent_nr; + unsigned rcvd_nr; + unsigned brd_sent; + unsigned rcvd_req; + unsigned brd_rcv; + unsigned unicast_flag; +) + +struct sockaddr_ll src_pk, dst_pk; +struct in_addr src_addr, dest_addr; +extern void *mempcpy(void *dest, const void *src, size_t n); + +// Gets information of INTERFACE and updates IFINDEX, MAC and IP. +static void get_interface(char *interface, int *ifindex, uint32_t *oip, + uint8_t *mac) +{ + struct ifreq req; + struct sockaddr_in *ip; + int fd = xsocket(AF_INET, SOCK_RAW, IPPROTO_RAW); + + req.ifr_addr.sa_family = AF_INET; + strncpy(req.ifr_name, interface, IFNAMSIZ); + req.ifr_name[IFNAMSIZ-1] = '\0'; + + xioctl(fd, SIOCGIFFLAGS, &req); + if (!(req.ifr_flags & IFF_UP)) return; + + if (oip) { + xioctl(fd, SIOCGIFADDR, &req); + ip = (struct sockaddr_in*) &req.ifr_addr; + *oip = ntohl(ip->sin_addr.s_addr); + } + if (ifindex) { + xioctl(fd, SIOCGIFINDEX, &req); + *ifindex = req.ifr_ifindex; + } + if (mac) { + xioctl(fd, SIOCGIFHWADDR, &req); + memcpy(mac, req.ifr_hwaddr.sa_data, 6); + } + xclose(fd); +} + +// SIGINT handler, Print Number of Packets send or receive details. +static void done(int sig) +{ + if (!(toys.optflags & FLAG_q)) { + xprintf("Sent %u probe(s) (%u broadcast(s))\n", TT.sent_nr, TT.brd_sent); + xprintf("Received %u repl%s (%u request(s), %u broadcast(s))\n", + TT.rcvd_nr, TT.rcvd_nr == 1 ? "y":"ies", TT.rcvd_req, TT.brd_rcv); + } + if (toys.optflags & FLAG_D) exit(!!TT.rcvd_nr); + //In -U mode, No reply is expected. + if (toys.optflags & FLAG_U) exit(EXIT_SUCCESS); + exit(!TT.rcvd_nr); +} + +// Create and Send Packet +static void send_packet() +{ + int ret; + unsigned char sbuf[256] = {0,}; + struct arphdr *arp_h = (struct arphdr *) sbuf; + unsigned char *ptr = (unsigned char *)(arp_h + 1); + + arp_h->ar_hrd = htons(ARPHRD_ETHER); + arp_h->ar_pro = htons(ETH_P_IP); + arp_h->ar_hln = src_pk.sll_halen; + arp_h->ar_pln = 4; + arp_h->ar_op = (toys.optflags & FLAG_A) ? htons(ARPOP_REPLY) + : htons(ARPOP_REQUEST); + + ptr = mempcpy(ptr, &src_pk.sll_addr, src_pk.sll_halen); + ptr = mempcpy(ptr, &src_addr, 4); + if (toys.optflags & FLAG_A) + ptr = mempcpy(ptr, &src_pk.sll_addr, src_pk.sll_halen); + else ptr = mempcpy(ptr, &dst_pk.sll_addr, src_pk.sll_halen); + + ptr = mempcpy(ptr, &dest_addr, 4); + ret = sendto(TT.sockfd, sbuf, ptr - sbuf, 0, + (struct sockaddr *)&dst_pk, sizeof(dst_pk)); + if (ret == ptr - sbuf) { + struct timeval tval; + + gettimeofday(&tval, NULL); + TT.sent_at = (tval.tv_sec * 1000000ULL + (tval.tv_usec)); + TT.sent_nr++; + if (!TT.unicast_flag) TT.brd_sent++; + } +} + +// Receive Packet and filter with valid checks. +static void recv_from(struct sockaddr_ll *from, int *recv_len) +{ + struct in_addr s_ip, d_ip; + struct arphdr *arp_hdr = (struct arphdr *)toybuf; + unsigned char *p = (unsigned char *)(arp_hdr + 1); + + if (arp_hdr->ar_op != htons(ARPOP_REQUEST) && + arp_hdr->ar_op != htons(ARPOP_REPLY)) return; + + if (from->sll_pkttype != PACKET_HOST && from->sll_pkttype != PACKET_BROADCAST + && from->sll_pkttype != PACKET_MULTICAST) return; + + if (arp_hdr->ar_pro != htons(ETH_P_IP) || (arp_hdr->ar_pln != 4) + || (arp_hdr->ar_hln != src_pk.sll_halen) + || (*recv_len < (int)(sizeof(*arp_hdr) + 2 * (4 + arp_hdr->ar_hln)))) + return; + + memcpy(&s_ip.s_addr, p + arp_hdr->ar_hln, 4); + memcpy(&d_ip.s_addr, p + arp_hdr->ar_hln + 4 + arp_hdr->ar_hln, 4); + + if (dest_addr.s_addr != s_ip.s_addr) return; + if (toys.optflags & FLAG_D) { + if (src_addr.s_addr && src_addr.s_addr != d_ip.s_addr) return; + if (!memcmp(p, &src_pk.sll_addr, src_pk.sll_halen)) return; + } else if (src_addr.s_addr != d_ip.s_addr ) return; + + if (!(toys.optflags & FLAG_q)) { + printf("%scast re%s from %s [%s]", + from->sll_pkttype == PACKET_HOST ? "Uni" : "Broad", + arp_hdr->ar_op == htons(ARPOP_REPLY) ? "ply" : "quest", + inet_ntoa(s_ip), ether_ntoa((struct ether_addr *) p)); + if (TT.sent_at) { + unsigned delta; + struct timeval tval; + + gettimeofday(&tval, NULL); + delta = (tval.tv_sec * 1000000ULL + (tval.tv_usec)) - TT.sent_at; + xprintf(" %u.%03ums\n", delta / 1000, delta % 1000); + xflush(); + } + } + TT.rcvd_nr++; + if (from->sll_pkttype != PACKET_HOST) TT.brd_rcv++; + if (arp_hdr->ar_op == htons(ARPOP_REQUEST)) TT.rcvd_req++; + if (toys.optflags & FLAG_f) done(0); + if (!(toys.optflags & FLAG_b)) { + memcpy(dst_pk.sll_addr, p, src_pk.sll_halen); + TT.unicast_flag = 1; + } +} + +// Alarm signal Handle, send packets in one second interval. +static void send_signal(int sig) +{ + struct timeval start; + + gettimeofday(&start, NULL); + if (!TT.start) + TT.end = TT.start = start.tv_sec * 1000 + start.tv_usec / 1000; + else TT.end = start.tv_sec*1000 + start.tv_usec / 1000; + if (toys.optflags & FLAG_c) { + if (!TT.count) done(0); + TT.count--; + } + if ((toys.optflags & FLAG_w) && ((TT.end - TT.start) > + ((TT.time_out)*1000))) done(0); + send_packet(); + alarm(1); +} + +void arping_main(void) +{ + struct ifreq ifr; + struct sockaddr_ll from; + socklen_t len; + int if_index, recv_len; + + if (!(toys.optflags & FLAG_I)) TT.iface = "eth0"; + TT.sockfd = xsocket(AF_PACKET, SOCK_DGRAM, 0); + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, TT.iface, IFNAMSIZ); + get_interface(TT.iface, &if_index, NULL, NULL); + src_pk.sll_ifindex = if_index; + + xioctl(TT.sockfd, SIOCGIFFLAGS, (char*)&ifr); + if (!(ifr.ifr_flags & IFF_UP) && !(toys.optflags & FLAG_q)) + error_exit("Interface \"%s\" is down", TT.iface); + if ((ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) + && !(toys.optflags & FLAG_q)) { + xprintf("Interface \"%s\" is not ARPable\n", TT.iface); + toys.exitval = (toys.optflags & FLAG_D) ? 0 : 2; + return; + } + if (!inet_aton(*toys.optargs, &dest_addr)) { + struct hostent *hp = gethostbyname2(*toys.optargs, AF_INET); + + if (!hp) perror_exit("bad address '%s'", *toys.optargs); + memcpy(&dest_addr, hp->h_addr, 4); + } + if ((toys.optflags & FLAG_s) && !(inet_aton(TT.src_ip, &src_addr))) + perror_exit("invalid source address '%s'",TT.src_ip); + if (!(toys.optflags & FLAG_D) && (toys.optflags & FLAG_U) + && !src_addr.s_addr) src_addr = dest_addr; + if (!(toys.optflags & FLAG_D) || src_addr.s_addr) { + struct sockaddr_in saddr; + int p_fd = xsocket(AF_INET, SOCK_DGRAM, 0); + + if (setsockopt(p_fd, SOL_SOCKET, SO_BINDTODEVICE, TT.iface, + strlen(TT.iface))) perror_exit("setsockopt"); + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + if (src_addr.s_addr) { + saddr.sin_addr = src_addr; + if (bind(p_fd, (struct sockaddr*)&saddr, sizeof(saddr))) + perror_exit("bind"); + } else { + uint32_t oip; + + saddr.sin_port = htons(1025); + saddr.sin_addr = dest_addr; + if (connect(p_fd, (struct sockaddr *) &saddr, sizeof(saddr))) + perror_exit("cannot connect to remote host"); + get_interface(TT.iface, NULL, &oip, NULL); + src_addr.s_addr = htonl(oip); + } + xclose(p_fd); + } + + src_pk.sll_family = AF_PACKET; + src_pk.sll_protocol = htons(ETH_P_ARP); + if (bind(TT.sockfd, (struct sockaddr *)&src_pk, sizeof(src_pk))) + perror_exit("bind"); + + socklen_t alen = sizeof(src_pk); + getsockname(TT.sockfd, (struct sockaddr *)&src_pk, &alen); + if (!src_pk.sll_halen) { + perror_msg("src is not arpable"); + toys.exitval = (toys.optflags & FLAG_D) ? 0 : 2; + return; + } + if (!(toys.optflags & FLAG_q)) { + xprintf("ARPING to %s", inet_ntoa(dest_addr)); + xprintf(" from %s via %s\n", inet_ntoa(src_addr), TT.iface); + } + + dst_pk = src_pk; + //First packet always broadcasts. + memset(dst_pk.sll_addr, -1, dst_pk.sll_halen); + signal(SIGINT, done); + signal(SIGALRM, send_signal); + + send_signal(0); // Send first Broadcast message. + while (1) { + len = sizeof(from); + recv_len = recvfrom(TT.sockfd, toybuf, 4096, 0, + (struct sockaddr *)&from, &len); + if (recv_len < 0) continue; + recv_from(&from, &recv_len); + } +} |