blob: 5f7c4a1fde8904d68861481ab25f2373525d4093 [file] [log] [blame]
/* 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 IFACE 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;
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;
}
static void resolve_host(char *host, struct sockaddr *sa)
{
struct addrinfo *ai = xgetaddrinfo(host, NULL, AF_INET, SOCK_STREAM, 0, 0);
memcpy(sa, ai->ai_addr, ai->ai_addrlen);
freeaddrinfo(ai);
}
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 (!FLAG(D)) {
char *ptr = toys.optargs[1];
char *p = ptr, *hw_addr = req.arp_ha.sa_data;
while (*hw_addr && (p-ptr) < 6) {
int val, len;
if (*hw_addr == ':') hw_addr++;
if (!sscanf(hw_addr, "%2x%n", &val, &len)) break;
hw_addr += len;
*p++ = val;
}
if ((p-ptr) != 6 || *hw_addr)
error_exit("bad hw addr '%s'", req.arp_ha.sa_data);
} else {
struct ifreq ifre;
xstrncpy(ifre.ifr_name, toys.optargs[1], IFNAMSIZ);
xioctl(TT.sockfd, SIOCGIFHWADDR, &ifre);
if (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 (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 = 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 (FLAG(v)) xprintf("Delete entry for %s\n", toys.optargs[0]);
return 0;
}
void arp_main(void)
{
struct sockaddr sa;
char ip[16], hw_addr[30], mask[16], dev[16], *host_ip = NULL;
FILE *fp;
int h_type, type, flag, i, entries = 0, disp = 0;
TT.device = "";
memset(&sa, 0, sizeof(sa));
TT.sockfd = xsocket(AF_INET, SOCK_STREAM, 0);
if (FLAG(A) || 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 (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 (FLAG(s) || FLAG(d)) {
if (!toys.optargs[0]) error_exit("-%c needs a host name", FLAG(d)?'d':'s');
resolve_host(toys.optargs[0], &sa);
memcpy(&req.arp_pa, &sa, sizeof(struct sockaddr));
if (FLAG(s) && !set_entry()) return;
if (FLAG(d) && !delete_entry()) return;
}
// Show arp cache.
if (toys.optargs[0]) {
resolve_host(toys.optargs[0], &sa);
ip_to_host(&sa, NI_NUMERICHOST);
host_ip = xstrdup(toybuf);
}
fp = xfopen("/proc/net/arp", "r");
fgets(toybuf, sizeof(toybuf), fp); // Skip header.
while (fscanf(fp, "%15s 0x%x 0x%x %29s %15s %15s",
ip, &h_type, &flag, hw_addr, mask, dev) == 6) {
char *host_name = "?";
entries++;
if ((FLAG(H) && get_index(hwtype, TT.hw_type) != h_type) ||
(FLAG(i) && strcmp(TT.interface, dev)) ||
(toys.optargs[0] && strcmp(host_ip, ip))) {
continue;
}
resolve_host(ip, &sa);
if (FLAG(n)) ip_to_host(&sa, NI_NUMERICHOST);
else if (!ip_to_host(&sa, NI_NAMEREQD)) host_name = toybuf;
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);
}
if (FLAG(v))
xprintf("Entries: %d\tSkipped: %d\tFound: %d\n",
entries, entries - disp, disp);
if (toys.optargs[0] && !disp)
xprintf("%s (%s) -- no entry\n", toys.optargs[0], host_ip);
if (CFG_TOYBOX_FREE) {
free(host_ip);
fclose(fp);
}
}