| /* arp.c - manipulate the system ARP cache |
| * |
| * Copyright 2014 Sandeep Sharma <sandeep.jack2756@gmail.com> |
| * Copyright 2014 Kyungwan Han <asura321@gamil.com> |
| * No Standard |
| |
| USE_ARP(NEWTOY(arp, "vi:nDsdap:A:H:[+Ap][!sd]", TOYFLAG_USR|TOYFLAG_BIN)) |
| |
| config ARP |
| bool "arp" |
| default n |
| help |
| Usage: arp |
| [-vn] [-H HWTYPE] [-i IF] -a [HOSTNAME] |
| [-v] [-i IF] -d HOSTNAME [pub] |
| [-v] [-H HWTYPE] [-i IF] -s HOSTNAME HWADDR [temp] |
| [-v] [-H HWTYPE] [-i IF] -s HOSTNAME HWADDR [netmask MASK] pub |
| [-v] [-H HWTYPE] [-i IF] -Ds HOSTNAME IFACE [netmask MASK] pub |
| |
| Manipulate ARP cache |
| |
| -a Display (all) hosts |
| -s Set new ARP entry |
| -d Delete a specified entry |
| -v Verbose |
| -n Don't resolve names |
| -i IF Network interface |
| -D Read <hwaddr> from given device |
| -A,-p AF Protocol family |
| -H HWTYPE Hardware address type |
| |
| */ |
| |
| #define FOR_arp |
| #include "toys.h" |
| #include <net/if_arp.h> |
| |
| GLOBALS( |
| char *hw_type; |
| char *af_type_A; |
| char *af_type_p; |
| char *interface; |
| |
| int sockfd; |
| char *device; |
| ) |
| |
| struct arpreq req; //Global request structure |
| |
| struct type { |
| char *name; |
| int val; |
| }; |
| |
| struct type hwtype[] = { |
| {"ether", ARPHRD_ETHER }, |
| {"loop" ,ARPHRD_LOOPBACK}, |
| {"ppp" ,ARPHRD_PPP}, |
| {"infiniband" ,ARPHRD_INFINIBAND}, |
| {NULL, -1}, |
| }; |
| |
| struct type aftype[] = { |
| {"inet", AF_INET }, |
| {"inet6" ,AF_INET6}, |
| {"unspec" ,AF_UNSPEC}, |
| {NULL, -1}, |
| }; |
| |
| struct type flag_type[] = { |
| {"PERM", ATF_PERM }, |
| {"PUB" ,ATF_PUBL}, |
| {"DONTPUB" ,ATF_DONTPUB}, |
| {"TRAIL" ,ATF_USETRAILERS}, |
| {NULL, -1}, |
| }; |
| |
| static int get_index(struct type arr[], char *name) |
| { |
| int i; |
| |
| for (i = 0; arr[i].name; i++) |
| if (!strcmp(arr[i].name, name)) break; |
| return arr[i].val; |
| } |
| |
| |
| void get_hw_add(char *hw_addr, char *ptr) |
| { |
| char *p = ptr, *hw = hw_addr; |
| |
| while (*hw_addr && (p-ptr) < 6) { |
| int val, len = 0; |
| |
| if (*hw_addr == ':') hw_addr++; |
| sscanf(hw_addr, "%2x%n", &val, &len); |
| if (!len || len > 2) break; |
| hw_addr += len; |
| *p++ = val; |
| } |
| |
| if ((p-ptr) != 6 || *hw_addr) |
| error_exit("bad hw addr '%s'", hw); |
| } |
| |
| static void resolve_host(char *host, struct sockaddr *sa) |
| { |
| struct addrinfo hints, *res = NULL; |
| int ret; |
| |
| memset(&hints, 0, sizeof hints); |
| hints.ai_family = AF_INET; |
| hints.ai_socktype = SOCK_STREAM; |
| if ((ret = getaddrinfo(host, NULL, &hints, &res))) |
| perror_exit("%s", gai_strerror(ret)); |
| |
| memcpy(sa, res->ai_addr, res->ai_addrlen); |
| freeaddrinfo(res); |
| } |
| |
| static void check_flags(int *i, char** argv) |
| { |
| struct sockaddr sa; |
| int flag = *i, j; |
| struct flags { |
| char *name; |
| int or, flag; |
| } f[] = { |
| {"pub", 1 ,ATF_PUBL}, |
| {"priv", 0 ,~ATF_PUBL}, |
| {"trail", 1, ATF_USETRAILERS}, |
| {"temp", 0, ~ATF_PERM}, |
| {"dontpub",1, ATF_DONTPUB}, |
| }; |
| |
| for (;*argv; argv++) { |
| for (j = 0; j < ARRAY_LEN(f); j++) { |
| if (!strcmp(*argv, f[j].name)) { |
| (f[j].or) ?(flag |= f[j].flag):(flag &= f[j].flag); |
| break; |
| } |
| } |
| if (j > 4 && !strcmp(*argv, "netmask")) { |
| if (!*++argv) error_exit("NULL netmask"); |
| if (strcmp(*argv, "255.255.255.255")) { |
| resolve_host(toys.optargs[0], &sa); |
| memcpy(&req.arp_netmask, &sa, sizeof(sa)); |
| flag |= ATF_NETMASK; |
| } else argv++; |
| } else if (j > 4 && !strcmp(*argv, "dev")) { |
| if (!*++argv) error_exit("NULL dev"); |
| TT.device = *argv; |
| } else if (j > 4) error_exit("invalid arg"); |
| } |
| *i = flag; |
| } |
| |
| static int set_entry(void) |
| { |
| int flags = 0; |
| |
| if (!toys.optargs[1]) error_exit("bad syntax"); |
| |
| if (!(toys.optflags & FLAG_D)) get_hw_add(toys.optargs[1], (char*)&req.arp_ha.sa_data); |
| else { |
| struct ifreq ifre; |
| |
| xstrncpy(ifre.ifr_name, toys.optargs[1], IFNAMSIZ); |
| xioctl(TT.sockfd, SIOCGIFHWADDR, &ifre); |
| if ((toys.optflags & FLAG_H) && (ifre.ifr_hwaddr.sa_family != ARPHRD_ETHER)) |
| error_exit("protocol type mismatch"); |
| memcpy(&req.arp_ha, &(ifre.ifr_hwaddr), sizeof(req.arp_ha)); |
| } |
| |
| flags = ATF_PERM | ATF_COM; |
| if (toys.optargs[2]) check_flags(&flags, (toys.optargs+2)); |
| req.arp_flags = flags; |
| xstrncpy(req.arp_dev, TT.device, sizeof(req.arp_dev)); |
| xioctl(TT.sockfd, SIOCSARP, &req); |
| |
| if (toys.optflags & FLAG_v) xprintf("Entry set for %s\n", toys.optargs[0]); |
| return 0; |
| } |
| |
| static int ip_to_host(struct sockaddr *sa, int flag) |
| { |
| int status = 0; |
| char hbuf[NI_MAXHOST] = {0,}, sbuf[NI_MAXSERV] = {0,}; |
| socklen_t len = sizeof(struct sockaddr_in6); |
| |
| *toybuf = 0; |
| if (!(status = getnameinfo(sa, len, hbuf, sizeof(hbuf), sbuf, |
| sizeof(sbuf), flag))) { |
| strcpy(toybuf, hbuf); |
| return 0; |
| } |
| return 1; |
| } |
| |
| static int delete_entry(void) |
| { |
| int flags; |
| |
| flags = ATF_PERM; |
| if (toys.optargs[1]) check_flags(&flags, (toys.optargs+1)); |
| req.arp_flags = flags; |
| xstrncpy(req.arp_dev, TT.device, sizeof(req.arp_dev)); |
| xioctl(TT.sockfd, SIOCDARP, &req); |
| |
| if (toys.optflags & FLAG_v) xprintf("Delete entry for %s\n", toys.optargs[0]); |
| return 0; |
| } |
| |
| void arp_main(void) |
| { |
| struct sockaddr sa; |
| char ip[128], hw_addr[128], mask[12], dev[128], *host_ip = NULL, *buf; |
| int h_type, type, flag, i, fd, entries = 0, disp = 0; |
| |
| TT.device = ""; |
| memset(&sa, 0, sizeof(sa)); |
| memset(&req, 0, sizeof(req)); |
| TT.sockfd = xsocket(AF_INET, SOCK_STREAM, 0); |
| |
| if ((toys.optflags & FLAG_A) || (toys.optflags & FLAG_p)) { |
| if ((type = get_index(aftype, |
| (TT.af_type_A)?TT.af_type_A:TT.af_type_p)) != AF_INET) |
| error_exit((type != -1)?"only inet supported by kernel":"unknown family"); |
| } |
| |
| req.arp_ha.sa_family = ARPHRD_ETHER; |
| if (toys.optflags & FLAG_H) { |
| if ((type = get_index(hwtype, TT.hw_type)) != ARPHRD_ETHER) |
| error_exit((type != -1)?"h/w type not supported":"unknown h/w type"); |
| req.arp_ha.sa_family = type; |
| } |
| |
| if (((toys.optflags & FLAG_s) || toys.optflags & FLAG_d)) { |
| if (!toys.optargs[0]) error_exit("host name req"); |
| resolve_host(toys.optargs[0], &sa); |
| memcpy(&req.arp_pa, &sa, sizeof(struct sockaddr)); |
| } |
| |
| if ((toys.optflags & FLAG_s) && !set_entry()) return; |
| if ((toys.optflags & FLAG_d) && !delete_entry()) return; |
| |
| //show arp chache |
| fd = xopenro("/proc/net/arp"); |
| buf = get_line(fd); |
| free(buf); //skip first line |
| |
| if (toys.optargs[0]) { |
| resolve_host(toys.optargs[0], &sa); |
| ip_to_host(&sa, NI_NUMERICHOST); |
| host_ip = xstrdup(toybuf); |
| } |
| |
| while ((buf = get_line(fd))) { |
| char *host_name = "?"; |
| |
| if ((sscanf(buf, "%s 0x%x 0x%x %s %s %s\n", ip, |
| &h_type, &flag, hw_addr, mask, dev )) != 6) break; |
| entries++; |
| if (((toys.optflags & FLAG_H) && (get_index(hwtype, TT.hw_type) != h_type)) |
| || ((toys.optflags & FLAG_i) && strcmp(TT.interface, dev)) |
| || (toys.optargs[0] && strcmp(host_ip, ip))) { |
| free(buf); |
| continue; |
| } |
| |
| resolve_host(buf, &sa); |
| if (!(toys.optflags & FLAG_n)) { |
| if (!ip_to_host(&sa, NI_NAMEREQD)) host_name = toybuf; |
| } else ip_to_host(&sa, NI_NUMERICHOST); |
| |
| disp++; |
| printf("%s (%s) at" , host_name, ip); |
| |
| for (i = 0; hwtype[i].name; i++) |
| if (hwtype[i].val & h_type) break; |
| if (!hwtype[i].name) error_exit("unknown h/w type"); |
| |
| if (!(flag & ATF_COM)) { |
| if ((flag & ATF_PUBL)) printf(" *"); |
| else printf(" <incomplete>"); |
| } else printf(" %s [%s]", hw_addr, hwtype[i].name); |
| |
| if (flag & ATF_NETMASK) printf("netmask %s ", mask); |
| |
| for (i = 0; flag_type[i].name; i++) |
| if (flag_type[i].val & flag) printf(" %s", flag_type[i].name); |
| |
| printf(" on %s\n", dev); |
| free(buf); |
| } |
| |
| if (toys.optflags & FLAG_v) |
| xprintf("Entries: %d\tSkipped: %d\tFound: %d\n", |
| entries, entries - disp, disp); |
| if (!disp) xprintf("No Match found in %d entries\n", entries); |
| |
| if (CFG_TOYBOX_FREE) { |
| free(host_ip); |
| free(buf); |
| xclose(fd); |
| } |
| } |