10f66f451Sopenharmony_ci/* arping - send ARP REQUEST to a neighbour host.
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2013 Sandeep Sharma <sandeep.jack2756@gmail.com>
40f66f451Sopenharmony_ci * Copyright 2013 Kyungwan Han <asura321@gmail.com>
50f66f451Sopenharmony_ci *
60f66f451Sopenharmony_ci * No Standard.
70f66f451Sopenharmony_ci
80f66f451Sopenharmony_ciUSE_ARPING(NEWTOY(arping, "<1>1s:I:w#<0c#<0AUDbqf[+AU][+Df]", TOYFLAG_USR|TOYFLAG_SBIN))
90f66f451Sopenharmony_ci
100f66f451Sopenharmony_ciconfig ARPING
110f66f451Sopenharmony_ci  bool "arping"
120f66f451Sopenharmony_ci  default n
130f66f451Sopenharmony_ci  help
140f66f451Sopenharmony_ci    usage: arping [-fqbDUA] [-c CNT] [-w TIMEOUT] [-I IFACE] [-s SRC_IP] DST_IP
150f66f451Sopenharmony_ci
160f66f451Sopenharmony_ci    Send ARP requests/replies
170f66f451Sopenharmony_ci
180f66f451Sopenharmony_ci    -f         Quit on first ARP reply
190f66f451Sopenharmony_ci    -q         Quiet
200f66f451Sopenharmony_ci    -b         Keep broadcasting, don't go unicast
210f66f451Sopenharmony_ci    -D         Duplicated address detection mode
220f66f451Sopenharmony_ci    -U         Unsolicited ARP mode, update your neighbors
230f66f451Sopenharmony_ci    -A         ARP answer mode, update your neighbors
240f66f451Sopenharmony_ci    -c N       Stop after sending N ARP requests
250f66f451Sopenharmony_ci    -w TIMEOUT Time to wait for ARP reply, seconds
260f66f451Sopenharmony_ci    -I IFACE   Interface to use (default eth0)
270f66f451Sopenharmony_ci    -s SRC_IP  Sender IP address
280f66f451Sopenharmony_ci    DST_IP     Target IP address
290f66f451Sopenharmony_ci*/
300f66f451Sopenharmony_ci#define FOR_arping
310f66f451Sopenharmony_ci#include "toys.h"
320f66f451Sopenharmony_ci#include <netinet/ether.h>
330f66f451Sopenharmony_ci#include <netpacket/packet.h>
340f66f451Sopenharmony_ci
350f66f451Sopenharmony_ciGLOBALS(
360f66f451Sopenharmony_ci    long count;
370f66f451Sopenharmony_ci    unsigned long time_out;
380f66f451Sopenharmony_ci    char *iface;
390f66f451Sopenharmony_ci    char *src_ip;
400f66f451Sopenharmony_ci
410f66f451Sopenharmony_ci    int sockfd;
420f66f451Sopenharmony_ci    unsigned long start, end;
430f66f451Sopenharmony_ci    unsigned sent_at, sent_nr, rcvd_nr, brd_sent, rcvd_req, brd_rcv,
440f66f451Sopenharmony_ci             unicast_flag;
450f66f451Sopenharmony_ci)
460f66f451Sopenharmony_ci
470f66f451Sopenharmony_cistruct sockaddr_ll src_pk, dst_pk;
480f66f451Sopenharmony_cistruct in_addr src_addr, dest_addr;
490f66f451Sopenharmony_ciextern void *mempcpy(void *dest, const void *src, size_t n);
500f66f451Sopenharmony_ci
510f66f451Sopenharmony_ci// Gets information of INTERFACE and updates IFINDEX, MAC and IP.
520f66f451Sopenharmony_cistatic void get_interface(char *interface, int *ifindex, uint32_t *oip,
530f66f451Sopenharmony_ci    uint8_t *mac)
540f66f451Sopenharmony_ci{
550f66f451Sopenharmony_ci  struct ifreq req;
560f66f451Sopenharmony_ci  struct sockaddr_in *ip;
570f66f451Sopenharmony_ci  int fd = xsocket(AF_INET, SOCK_RAW, IPPROTO_RAW);
580f66f451Sopenharmony_ci
590f66f451Sopenharmony_ci  req.ifr_addr.sa_family = AF_INET;
600f66f451Sopenharmony_ci  xstrncpy(req.ifr_name, interface, IFNAMSIZ);
610f66f451Sopenharmony_ci  req.ifr_name[IFNAMSIZ-1] = '\0';
620f66f451Sopenharmony_ci
630f66f451Sopenharmony_ci  xioctl(fd, SIOCGIFFLAGS, &req);
640f66f451Sopenharmony_ci  if (!(req.ifr_flags & IFF_UP)) return;
650f66f451Sopenharmony_ci
660f66f451Sopenharmony_ci  if (oip) {
670f66f451Sopenharmony_ci    xioctl(fd, SIOCGIFADDR, &req);
680f66f451Sopenharmony_ci    ip = (struct sockaddr_in*) &req.ifr_addr;
690f66f451Sopenharmony_ci    *oip = ntohl(ip->sin_addr.s_addr);
700f66f451Sopenharmony_ci  }
710f66f451Sopenharmony_ci  if (ifindex) {
720f66f451Sopenharmony_ci    xioctl(fd, SIOCGIFINDEX, &req);
730f66f451Sopenharmony_ci    *ifindex = req.ifr_ifindex;
740f66f451Sopenharmony_ci  }
750f66f451Sopenharmony_ci  if (mac) {
760f66f451Sopenharmony_ci    xioctl(fd, SIOCGIFHWADDR, &req);
770f66f451Sopenharmony_ci    memcpy(mac, req.ifr_hwaddr.sa_data, 6);
780f66f451Sopenharmony_ci  }
790f66f451Sopenharmony_ci  xclose(fd);
800f66f451Sopenharmony_ci}
810f66f451Sopenharmony_ci
820f66f451Sopenharmony_ci// SIGINT handler, Print Number of Packets send or receive details.
830f66f451Sopenharmony_cistatic void done(int sig)
840f66f451Sopenharmony_ci{
850f66f451Sopenharmony_ci  if (!(toys.optflags & FLAG_q)) {
860f66f451Sopenharmony_ci    xprintf("Sent %u probe(s) (%u broadcast(s))\n", TT.sent_nr, TT.brd_sent);
870f66f451Sopenharmony_ci    xprintf("Received %u repl%s (%u request(s), %u broadcast(s))\n",
880f66f451Sopenharmony_ci        TT.rcvd_nr, TT.rcvd_nr == 1 ? "y":"ies", TT.rcvd_req, TT.brd_rcv);
890f66f451Sopenharmony_ci  }
900f66f451Sopenharmony_ci  if (toys.optflags & FLAG_D) exit(!!TT.rcvd_nr);
910f66f451Sopenharmony_ci  //In -U mode, No reply is expected.
920f66f451Sopenharmony_ci  if (toys.optflags & FLAG_U) exit(EXIT_SUCCESS);
930f66f451Sopenharmony_ci  exit(!TT.rcvd_nr);
940f66f451Sopenharmony_ci}
950f66f451Sopenharmony_ci
960f66f451Sopenharmony_ci// Create and Send Packet
970f66f451Sopenharmony_cistatic void send_packet()
980f66f451Sopenharmony_ci{
990f66f451Sopenharmony_ci  int ret;
1000f66f451Sopenharmony_ci  unsigned char sbuf[256] = {0,};
1010f66f451Sopenharmony_ci  struct arphdr *arp_h = (struct arphdr *) sbuf;
1020f66f451Sopenharmony_ci  unsigned char *ptr = (unsigned char *)(arp_h + 1);
1030f66f451Sopenharmony_ci
1040f66f451Sopenharmony_ci  arp_h->ar_hrd = htons(ARPHRD_ETHER);
1050f66f451Sopenharmony_ci  arp_h->ar_pro = htons(ETH_P_IP);
1060f66f451Sopenharmony_ci  arp_h->ar_hln = src_pk.sll_halen;
1070f66f451Sopenharmony_ci  arp_h->ar_pln = 4;
1080f66f451Sopenharmony_ci  arp_h->ar_op = (toys.optflags & FLAG_A) ? htons(ARPOP_REPLY)
1090f66f451Sopenharmony_ci    : htons(ARPOP_REQUEST);
1100f66f451Sopenharmony_ci
1110f66f451Sopenharmony_ci  ptr = mempcpy(ptr, &src_pk.sll_addr, src_pk.sll_halen);
1120f66f451Sopenharmony_ci  ptr = mempcpy(ptr, &src_addr, 4);
1130f66f451Sopenharmony_ci  ptr = mempcpy(ptr,
1140f66f451Sopenharmony_ci                (toys.optflags & FLAG_A) ? &src_pk.sll_addr : &dst_pk.sll_addr,
1150f66f451Sopenharmony_ci                src_pk.sll_halen);
1160f66f451Sopenharmony_ci  ptr = mempcpy(ptr, &dest_addr, 4);
1170f66f451Sopenharmony_ci
1180f66f451Sopenharmony_ci  ret = sendto(TT.sockfd, sbuf, ptr - sbuf, 0,
1190f66f451Sopenharmony_ci      (struct sockaddr *)&dst_pk, sizeof(dst_pk));
1200f66f451Sopenharmony_ci  if (ret == ptr - sbuf) {
1210f66f451Sopenharmony_ci    struct timeval tval;
1220f66f451Sopenharmony_ci
1230f66f451Sopenharmony_ci    gettimeofday(&tval, NULL);
1240f66f451Sopenharmony_ci    TT.sent_at = tval.tv_sec * 1000000ULL + tval.tv_usec;
1250f66f451Sopenharmony_ci    TT.sent_nr++;
1260f66f451Sopenharmony_ci    if (!TT.unicast_flag) TT.brd_sent++;
1270f66f451Sopenharmony_ci  }
1280f66f451Sopenharmony_ci}
1290f66f451Sopenharmony_ci
1300f66f451Sopenharmony_ci// Receive Packet and filter with valid checks.
1310f66f451Sopenharmony_cistatic void recv_from(struct sockaddr_ll *from, int *recv_len)
1320f66f451Sopenharmony_ci{
1330f66f451Sopenharmony_ci  struct in_addr s_ip, d_ip;
1340f66f451Sopenharmony_ci  struct arphdr *arp_hdr = (struct arphdr *)toybuf;
1350f66f451Sopenharmony_ci  unsigned char *p = (unsigned char *)(arp_hdr + 1);
1360f66f451Sopenharmony_ci
1370f66f451Sopenharmony_ci  if (arp_hdr->ar_op != htons(ARPOP_REQUEST) &&
1380f66f451Sopenharmony_ci      arp_hdr->ar_op != htons(ARPOP_REPLY)) return;
1390f66f451Sopenharmony_ci
1400f66f451Sopenharmony_ci  if (from->sll_pkttype != PACKET_HOST && from->sll_pkttype != PACKET_BROADCAST
1410f66f451Sopenharmony_ci      && from->sll_pkttype != PACKET_MULTICAST) return;
1420f66f451Sopenharmony_ci
1430f66f451Sopenharmony_ci  if (arp_hdr->ar_pro != htons(ETH_P_IP) || (arp_hdr->ar_pln != 4)
1440f66f451Sopenharmony_ci      || (arp_hdr->ar_hln != src_pk.sll_halen)
1450f66f451Sopenharmony_ci      || (*recv_len < (int)(sizeof(*arp_hdr) + 2 * (4 + arp_hdr->ar_hln))))
1460f66f451Sopenharmony_ci    return;
1470f66f451Sopenharmony_ci
1480f66f451Sopenharmony_ci  memcpy(&s_ip.s_addr, p + arp_hdr->ar_hln, 4);
1490f66f451Sopenharmony_ci  memcpy(&d_ip.s_addr, p + arp_hdr->ar_hln + 4 + arp_hdr->ar_hln, 4);
1500f66f451Sopenharmony_ci
1510f66f451Sopenharmony_ci  if (dest_addr.s_addr != s_ip.s_addr) return;
1520f66f451Sopenharmony_ci  if (toys.optflags & FLAG_D) {
1530f66f451Sopenharmony_ci    if (src_addr.s_addr && src_addr.s_addr != d_ip.s_addr) return;
1540f66f451Sopenharmony_ci    if (!memcmp(p, &src_pk.sll_addr, src_pk.sll_halen)) return;
1550f66f451Sopenharmony_ci  } else if (src_addr.s_addr != d_ip.s_addr ) return;
1560f66f451Sopenharmony_ci
1570f66f451Sopenharmony_ci  if (!(toys.optflags & FLAG_q)) {
1580f66f451Sopenharmony_ci    printf("%scast re%s from %s [%s]",
1590f66f451Sopenharmony_ci        from->sll_pkttype == PACKET_HOST ? "Uni" : "Broad",
1600f66f451Sopenharmony_ci        arp_hdr->ar_op == htons(ARPOP_REPLY) ? "ply" : "quest",
1610f66f451Sopenharmony_ci        inet_ntoa(s_ip), ether_ntoa((struct ether_addr *) p));
1620f66f451Sopenharmony_ci    if (TT.sent_at) {
1630f66f451Sopenharmony_ci      unsigned delta;
1640f66f451Sopenharmony_ci      struct timeval tval;
1650f66f451Sopenharmony_ci
1660f66f451Sopenharmony_ci      gettimeofday(&tval, NULL);
1670f66f451Sopenharmony_ci      delta = (tval.tv_sec * 1000000ULL + (tval.tv_usec)) - TT.sent_at;
1680f66f451Sopenharmony_ci      xprintf(" %u.%03ums\n", delta / 1000, delta % 1000);
1690f66f451Sopenharmony_ci    }
1700f66f451Sopenharmony_ci  }
1710f66f451Sopenharmony_ci  TT.rcvd_nr++;
1720f66f451Sopenharmony_ci  if (from->sll_pkttype != PACKET_HOST) TT.brd_rcv++;
1730f66f451Sopenharmony_ci  if (arp_hdr->ar_op == htons(ARPOP_REQUEST)) TT.rcvd_req++;
1740f66f451Sopenharmony_ci  if (toys.optflags & FLAG_f) done(0);
1750f66f451Sopenharmony_ci  if (!(toys.optflags & FLAG_b)) {
1760f66f451Sopenharmony_ci    memcpy(dst_pk.sll_addr, p, src_pk.sll_halen);
1770f66f451Sopenharmony_ci    TT.unicast_flag = 1;
1780f66f451Sopenharmony_ci  }
1790f66f451Sopenharmony_ci}
1800f66f451Sopenharmony_ci
1810f66f451Sopenharmony_ci// Alarm signal Handle, send packets in one second interval.
1820f66f451Sopenharmony_cistatic void send_signal(int sig)
1830f66f451Sopenharmony_ci{
1840f66f451Sopenharmony_ci  struct timeval start;
1850f66f451Sopenharmony_ci
1860f66f451Sopenharmony_ci  gettimeofday(&start, NULL);
1870f66f451Sopenharmony_ci  if (!TT.start)
1880f66f451Sopenharmony_ci    TT.end = TT.start = start.tv_sec * 1000 + start.tv_usec / 1000;
1890f66f451Sopenharmony_ci  else TT.end = start.tv_sec*1000 + start.tv_usec / 1000;
1900f66f451Sopenharmony_ci  if (toys.optflags & FLAG_c) {
1910f66f451Sopenharmony_ci    if (!TT.count) done(0);
1920f66f451Sopenharmony_ci    TT.count--;
1930f66f451Sopenharmony_ci  }
1940f66f451Sopenharmony_ci  if ((toys.optflags & FLAG_w) && ((TT.end - TT.start) >
1950f66f451Sopenharmony_ci        ((TT.time_out)*1000))) done(0);
1960f66f451Sopenharmony_ci  send_packet();
1970f66f451Sopenharmony_ci  alarm(1);
1980f66f451Sopenharmony_ci}
1990f66f451Sopenharmony_ci
2000f66f451Sopenharmony_civoid arping_main(void)
2010f66f451Sopenharmony_ci{
2020f66f451Sopenharmony_ci  struct ifreq ifr;
2030f66f451Sopenharmony_ci  struct sockaddr_ll from;
2040f66f451Sopenharmony_ci  socklen_t len;
2050f66f451Sopenharmony_ci  int if_index, recv_len;
2060f66f451Sopenharmony_ci
2070f66f451Sopenharmony_ci  if (!(toys.optflags & FLAG_I)) TT.iface = "eth0";
2080f66f451Sopenharmony_ci  TT.sockfd = xsocket(AF_PACKET, SOCK_DGRAM, 0);
2090f66f451Sopenharmony_ci
2100f66f451Sopenharmony_ci  memset(&ifr, 0, sizeof(ifr));
2110f66f451Sopenharmony_ci  xstrncpy(ifr.ifr_name, TT.iface, IFNAMSIZ);
2120f66f451Sopenharmony_ci  get_interface(TT.iface, &if_index, NULL, NULL);
2130f66f451Sopenharmony_ci  src_pk.sll_ifindex = if_index;
2140f66f451Sopenharmony_ci
2150f66f451Sopenharmony_ci  xioctl(TT.sockfd, SIOCGIFFLAGS, (char*)&ifr);
2160f66f451Sopenharmony_ci  if (!(ifr.ifr_flags & IFF_UP) && !(toys.optflags & FLAG_q))
2170f66f451Sopenharmony_ci    error_exit("Interface \"%s\" is down", TT.iface);
2180f66f451Sopenharmony_ci  if ((ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK))
2190f66f451Sopenharmony_ci      && !(toys.optflags & FLAG_q)) {
2200f66f451Sopenharmony_ci    xprintf("Interface \"%s\" is not ARPable\n", TT.iface);
2210f66f451Sopenharmony_ci    toys.exitval = (toys.optflags & FLAG_D) ? 0 : 2;
2220f66f451Sopenharmony_ci    return;
2230f66f451Sopenharmony_ci  }
2240f66f451Sopenharmony_ci  if (!inet_aton(*toys.optargs, &dest_addr)) {
2250f66f451Sopenharmony_ci    struct hostent *hp = gethostbyname2(*toys.optargs, AF_INET);
2260f66f451Sopenharmony_ci
2270f66f451Sopenharmony_ci    if (!hp) perror_exit("bad address '%s'", *toys.optargs);
2280f66f451Sopenharmony_ci    memcpy(&dest_addr, hp->h_addr, 4);
2290f66f451Sopenharmony_ci  }
2300f66f451Sopenharmony_ci  if ((toys.optflags & FLAG_s) && !(inet_aton(TT.src_ip, &src_addr)))
2310f66f451Sopenharmony_ci    perror_exit("invalid source address '%s'",TT.src_ip);
2320f66f451Sopenharmony_ci  if (!(toys.optflags & FLAG_D) && (toys.optflags & FLAG_U)
2330f66f451Sopenharmony_ci      && !src_addr.s_addr) src_addr = dest_addr;
2340f66f451Sopenharmony_ci  if (!(toys.optflags & FLAG_D) || src_addr.s_addr) {
2350f66f451Sopenharmony_ci    struct sockaddr_in saddr;
2360f66f451Sopenharmony_ci    int p_fd = xsocket(AF_INET, SOCK_DGRAM, 0);
2370f66f451Sopenharmony_ci
2380f66f451Sopenharmony_ci    if (setsockopt(p_fd, SOL_SOCKET, SO_BINDTODEVICE, TT.iface,
2390f66f451Sopenharmony_ci          strlen(TT.iface))) perror_exit("setsockopt");
2400f66f451Sopenharmony_ci
2410f66f451Sopenharmony_ci    memset(&saddr, 0, sizeof(saddr));
2420f66f451Sopenharmony_ci    saddr.sin_family = AF_INET;
2430f66f451Sopenharmony_ci    if (src_addr.s_addr) {
2440f66f451Sopenharmony_ci      saddr.sin_addr = src_addr;
2450f66f451Sopenharmony_ci      xbind(p_fd, (struct sockaddr*)&saddr, sizeof(saddr));
2460f66f451Sopenharmony_ci    } else {
2470f66f451Sopenharmony_ci      uint32_t oip;
2480f66f451Sopenharmony_ci
2490f66f451Sopenharmony_ci      saddr.sin_port = htons(1025);
2500f66f451Sopenharmony_ci      saddr.sin_addr = dest_addr;
2510f66f451Sopenharmony_ci      xconnect(p_fd, (struct sockaddr *) &saddr, sizeof(saddr));
2520f66f451Sopenharmony_ci      get_interface(TT.iface, NULL, &oip, NULL);
2530f66f451Sopenharmony_ci      src_addr.s_addr = htonl(oip);
2540f66f451Sopenharmony_ci    }
2550f66f451Sopenharmony_ci    xclose(p_fd);
2560f66f451Sopenharmony_ci  }
2570f66f451Sopenharmony_ci
2580f66f451Sopenharmony_ci  src_pk.sll_family = AF_PACKET;
2590f66f451Sopenharmony_ci  src_pk.sll_protocol = htons(ETH_P_ARP);
2600f66f451Sopenharmony_ci  xbind(TT.sockfd, (struct sockaddr *)&src_pk, sizeof(src_pk));
2610f66f451Sopenharmony_ci
2620f66f451Sopenharmony_ci  socklen_t alen = sizeof(src_pk);
2630f66f451Sopenharmony_ci  getsockname(TT.sockfd, (struct sockaddr *)&src_pk, &alen);
2640f66f451Sopenharmony_ci  if (!src_pk.sll_halen) {
2650f66f451Sopenharmony_ci    perror_msg("src is not arpable");
2660f66f451Sopenharmony_ci    toys.exitval = (toys.optflags & FLAG_D) ? 0 : 2;
2670f66f451Sopenharmony_ci    return;
2680f66f451Sopenharmony_ci  }
2690f66f451Sopenharmony_ci  if (!(toys.optflags & FLAG_q)) {
2700f66f451Sopenharmony_ci    xprintf("ARPING to %s", inet_ntoa(dest_addr));
2710f66f451Sopenharmony_ci    xprintf(" from %s via %s\n", inet_ntoa(src_addr), TT.iface);
2720f66f451Sopenharmony_ci  }
2730f66f451Sopenharmony_ci
2740f66f451Sopenharmony_ci  dst_pk = src_pk;
2750f66f451Sopenharmony_ci  //First packet always broadcasts.
2760f66f451Sopenharmony_ci  memset(dst_pk.sll_addr, -1, dst_pk.sll_halen);
2770f66f451Sopenharmony_ci  signal(SIGINT, done);
2780f66f451Sopenharmony_ci  signal(SIGALRM, send_signal);
2790f66f451Sopenharmony_ci
2800f66f451Sopenharmony_ci  send_signal(0); // Send first Broadcast message.
2810f66f451Sopenharmony_ci  while (1) {
2820f66f451Sopenharmony_ci    len = sizeof(from);
2830f66f451Sopenharmony_ci    recv_len = recvfrom(TT.sockfd, toybuf, 4096, 0,
2840f66f451Sopenharmony_ci        (struct sockaddr *)&from, &len);
2850f66f451Sopenharmony_ci    if (recv_len < 0) continue;
2860f66f451Sopenharmony_ci    recv_from(&from, &recv_len);
2870f66f451Sopenharmony_ci  }
2880f66f451Sopenharmony_ci}
289