view toys/pending/arp.c @ 1572:da1bf31ed322 draft

Tweak the "ignoring return value" fortify workaround for readlinkat. We zero the buffer and if the link read fails that's left alone, so it's ok for the symlink not to be there. Unfortunately, typecasting the return value to (void) doesn't shut up gcc, and having an if(); with the semicolon on the same line doesn't shut up llvm. (The semicolon on a new line would, but C does not have significant whitespace and I'm not going to humor llvm if it plans to start.) So far, empty curly brackets consistently get the warning to shut up.
author Rob Landley <rob@landley.net>
date Mon, 24 Nov 2014 17:23:23 -0600
parents 00c20f410c46
children 41efba077b75
line wrap: on
line source

/* 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;
  strncpy(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;
  strncpy(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 = xopen("/proc/net/arp", O_RDONLY);
  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);
  }
}