--- a/toys/pending/ip.c	2015-07-13 10:51:26.000000000 +0530
+++ b/toys/pending/ip.c	2015-07-14 16:04:15.000000000 +0530
@@ -13,16 +13,17 @@
 USE_IP(OLDTOY(iproute, ip, TOYFLAG_SBIN))
 USE_IP(OLDTOY(iprule, ip, TOYFLAG_SBIN))
 USE_IP(OLDTOY(iptunnel, ip, TOYFLAG_SBIN))
+USE_IP(OLDTOY(ipneigh, ip, TOYFLAG_SBIN))
 
 config IP
   bool "ip"
-  default n
+  default y
   help
     usage: ip [ OPTIONS ] OBJECT { COMMAND }
 
     Show / manipulate routing, devices, policy routing and tunnels.
 
-    where OBJECT := {address | link | route | rule | tunnel}
+    where OBJECT := {address | link | route | rule | tunnel | neigh}
     OPTIONS := { -f[amily] { inet | inet6 | link } | -o[neline] }
 */
 #define FOR_ip
@@ -50,7 +51,7 @@
 
 static struct 
 {
-  int ifindex, scope, scopemask, up, to;
+  int ifindex, scope, scopemask, up, to, zero_prefix, zero_iplast, link;
   char *label, *addr;
 } addrinfo;
 
@@ -454,7 +455,7 @@
   ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
 #endif
 
-static uint32_t get_ifaceindex(char *name, int ext)
+static int get_ifaceindex(char *name, int ext)
 {
   struct if_nameindex *if_ni, *i;
   int index = -1;
@@ -480,7 +481,7 @@
     val = length = 0;
     if (!arg) error_exit("bad hw-addr '%s'", "");
     if (*arg == ':') arg++, count++;
-    sscanf(arg, "%2x%n", &val, &length);
+    sscanf(arg, "%x%n", &val, &length);
     if (!length || length > 2)
       error_exit("bad hw-addr '%s'", arg);
     arg += length;
@@ -506,7 +507,7 @@
   return out;
 }
 
-static void vlan_parse_opt(char **argv, struct nlmsghdr *n, unsigned int size)
+static void virtual_lan_options(char **argv, struct nlmsghdr *n, unsigned int size)
 {
   struct arglist vlan_optlist[] = {{"id", 0}, {"protocol", 1}, 
     {"reorder_hdr", 2}, {"gvrp", 3}, {NULL,-1}};
@@ -601,7 +602,7 @@
       struct rtattr *data = NLMSG_TAIL(&request.mhdr);
       add_string_to_rtattr(&request.mhdr, sizeof(request), 
           IFLA_INFO_DATA, NULL, 0);
-      vlan_parse_opt(++argv, &request.mhdr, sizeof(request));
+      virtual_lan_options(++argv, &request.mhdr, sizeof(request));
       data->rta_len = (void *)NLMSG_TAIL(&request.mhdr) - (void *)data;
     }
     attr->rta_len = (void *)NLMSG_TAIL(&request.mhdr) - (void *)attr;
@@ -622,7 +623,7 @@
   if (!name) {
     snprintf(toybuf, IFNAMSIZ, "%s%d", type, 0);
     for (len = 1; ; len++) {
-      if (!get_ifaceindex(toybuf, 0)) break;
+      if (get_ifaceindex(toybuf, 0) == -1) break;
       snprintf(toybuf, IFNAMSIZ, "%s%d", type, len);
     }
     name = toybuf;
@@ -649,8 +650,9 @@
   xstrncpy(req.ifr_name, *argv, IF_NAMESIZE);
   fd = xsocket(AF_INET, SOCK_DGRAM, 0);
   xioctl(fd, SIOCGIFINDEX, &req);
-  for (++argv; *argv;) {
-    if ((idx = substring_to_idx(*argv++, cmd_objectlist)) == -1) iphelp();
+  while (*argv++){
+    idx = substring_to_idx(*argv++, cmd_objectlist);
+    if ((idx == -1) || ((idx > 1) && !*argv)) iphelp();
     switch(idx) {
       case 0:
         flags |= IFF_UP; break;
@@ -659,8 +661,7 @@
       case 2:
       case 3:
       case 4:
-        if (!*argv) iphelp();
-        else if (!strcmp(*argv, "on")) {
+        if (!strcmp(*argv, "on")) {
           if (idx == 2) {
             masks &= ~case_flags[idx-2];
             flags &= ~case_flags[idx-2];
@@ -751,7 +752,7 @@
     {"MULTICAST", IFF_MULTICAST}, {"PORTSEL", IFF_PORTSEL},
     {"AUTOMEDIA", IFF_AUTOMEDIA}, {"DYNAMIC", IFF_DYNAMIC}, {NULL,-1}};
 
-  if (link->parent != -1) {
+  if (link->parent != -1 && link->parent != 0) {
     int fd = 0;
     struct ifreq req;
 
@@ -770,7 +771,8 @@
   if (!(flags = get_flag_string(iface_flags, link->flags, 1)))
     error_exit("Invalid data.");    
   if (!TT.singleline) line_feed="\n    ";
-  if (link->parent != -1) {
+  if (!link->parent) sprintf(toybuf,"%s@%s", link->iface, "NONE");
+  else if (link->parent != -1) {
     char iface[IF_NAMESIZE];
 
     if (!if_indextoname(link->parent, iface)) perror_exit(NULL);
@@ -833,6 +835,12 @@
 #ifdef ARPHRD_VOID
     {"void",ARPHRD_VOID},
 #endif
+    {"[800]",ARPHRD_IEEE802_TR},
+    {"[801]",ARPHRD_IEEE80211},
+    {"[802]",ARPHRD_IEEE80211_PRISM},
+    {"[803]",ARPHRD_IEEE80211_RADIOTAP},
+    {"[804]",ARPHRD_IEEE802154},
+    {"[805]",ARPHRD_IEEE802154_PHY},
     {NULL,-1}};
   char *lname = get_flag_string(hwtypes, iface->ifi_type, 0);
 
@@ -920,7 +928,12 @@
   } request;
   uint32_t index = 0;
 
-  if (*argv && strcasecmp("up",*argv)) index = get_ifaceindex(*argv, 1);
+  if (*argv && strcasecmp("up",*argv)) {
+        index = get_ifaceindex(*argv, 1);
+        if (*++argv && strcasecmp("up",*argv)) 
+        error_exit("Error: either \"dev\" is duplicate, or %s is a garbage."
+                                                                        ,*argv);
+  }
   memset(&request, 0, sizeof(request));
   request.mhdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
   request.mhdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
@@ -1017,8 +1030,12 @@
     }
   }
 
-  if (addrinfo.to && strcmp(addrinfo.addr, lbuf))
-    return 0;
+  if (addrinfo.to) {
+       char *buf = strstr(lbuf, addrinfo.addr);
+       if ((!addrinfo.zero_iplast && !addrinfo.zero_prefix && !buf) 
+       || (addrinfo.zero_iplast && !buf))
+       return 0;
+  }
 
   if (rta_tb[IFA_BROADCAST]) {
     if (!inet_ntop(ifa->ifa_family, RTA_DATA(rta_tb[IFA_BROADCAST]),
@@ -1162,14 +1179,16 @@
           parse_prefix(addr, &netmask, &len, *argv,
               req.ifadd.ifa_family);
           if (len)
-            req.ifadd.ifa_family = ((len == 4) ? AF_INET : AF_INET6);
-          length_local = len;
+              req.ifadd.ifa_family = ((len == 4) ? AF_INET : AF_INET6);
+          if (length_local)
+              error_exit("either local is duplicate, or \"%s\" is garbage", *argv );
           local.bitlen = netmask;
           local.bytelen = len;
           memcpy(local.data, addr, sizeof(local.data));
           local.family = req.ifadd.ifa_family;
           add_string_to_rtattr(&req.nlm, sizeof(req),
               IFA_LOCAL, &local.data, local.bytelen);
+          length_local = local.bytelen;
         }
         break;
     }
@@ -1207,7 +1226,18 @@
   if (!scoped && (cmd != RTM_DELADDR) && (local.family == AF_INET)
       && (local.bytelen >= 1 && *(uint8_t*)&local.data == 127))
     req.ifadd.ifa_scope = RT_SCOPE_HOST;
-  req.ifadd.ifa_index = get_ifaceindex(dev, 1);
+  
+  {
+      struct ifreq ifr;
+      int fd;
+
+      xstrncpy(ifr.ifr_name, dev, strlen(dev)+1);
+      ifr.ifr_ifindex = -1;
+      xioctl(fd = socket(AF_INET, SOCK_DGRAM, 0), SIOCGIFINDEX, &ifr);
+      close(fd);
+      req.ifadd.ifa_index = ifr.ifr_ifindex;
+}  
+  
 
   send_nlmesg(RTM_NEWADDR, 0, AF_UNSPEC, (void *)&req, req.nlm.nlmsg_len);
   length = recv(TT.sockfd, reply, sizeof(reply), 0);
@@ -1227,9 +1257,11 @@
 
 static int ipaddr_listflush(char **argv)
 {
-  int idx; uint32_t netmask = 0, found = 0;
+  int idx;
+  uint32_t netmask = 0, found = 0;
   char *tmp = NULL, *name = NULL;
   struct double_list *dlist;
+  char *saveptr, *toremove = ".0";
   struct arglist cmd_objectlist[] = {{"to", 0}, {"scope", 1}, {"up", 2},
     {"label", 3}, {"dev", 4}, {NULL, -1}};
 
@@ -1245,18 +1277,36 @@
   addrinfo.scope = -1;
   while (*argv) {
     switch (idx = substring_to_idx(*argv, cmd_objectlist)) {
-      case 0: 
+      case 0:
         {// ADDR_TO
-          if (!*++argv) error_exit("Incomplete Command line");
-          else if (!strcmp(*argv, "0")) return 0;
+          int tlen, deflt = 0, iplast=0;
           uint32_t addr[4] = {0,};
           uint8_t len = 0;
 
+          if (!*++argv) error_exit("Incomplete Command line");
+          else if (!strcmp(*argv, "0") || (!strchr(*argv, '.') &&
+                     !strchr(*argv, ':')&& !strchr(*argv, '/'))) return 0;
           addrinfo.to = 1;
           parse_prefix(addr, &netmask, &len, *argv, TT.addressfamily);
-          if (len)
-            TT.addressfamily = ((len == 4) ? AF_INET : AF_INET6);
-          addrinfo.addr  = strtok(*argv, "/");
+          if (len) 
+             TT.addressfamily = ((len == 4) ? AF_INET : AF_INET6);
+          char *ip_dup = addrinfo.addr  = strtok_r(*argv, "/", &saveptr);
+          if (!strcmp(saveptr, "0")) {
+                  addrinfo.zero_prefix = 1;
+                  break;
+          }
+          // Special check for "10.20.30.0" type of IPs
+          tlen = strlen(addrinfo.addr);
+          if (!strcmp(&addrinfo.addr[tlen-1], "0")) iplast = 1;
+
+          if (strstr(addrinfo.addr, "127")) deflt = 1;
+          if (!deflt && iplast) addrinfo.zero_iplast = 1;
+          if (addrinfo.zero_iplast) {
+              addrinfo.addr = strstr(ip_dup,toremove);
+              memmove(addrinfo.addr,addrinfo.addr+strlen(toremove),
+                     1+strlen(ip_dup+strlen(toremove)));
+              addrinfo.addr = ip_dup;
+          }
         }
         break;
       case 1: // ADDR_SCOPE
@@ -1292,7 +1342,6 @@
         break;
       case 4: // ADDR_DEV
         if (!*++argv) error_exit("Incomplete Command line");
-
       default:                               
         if (TT.filter_dev)
           error_exit("Either \"dev\" is duplicate or %s is garbage",
@@ -1336,7 +1385,7 @@
 {
   struct nlmsghdr *addr_ptr;
   int ip_match = 0;
-
+ 
   addrinfo.ifindex = link->iface_idx;
   send_nlmesg(RTM_GETADDR, NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST,
       AF_UNSPEC, NULL, 0);
@@ -1354,6 +1403,7 @@
     struct ifaddrmsg *addressInfo = NLMSG_DATA(addr_ptr);
     char lbuf[INET6_ADDRSTRLEN];
     struct rtattr *rta, *rta_tb[IFA_MAX+1] = {0,};
+    addrinfo.link = 0;
 
     int len1 = addr_ptr->nlmsg_len - NLMSG_LENGTH(sizeof(*addressInfo));
     if (len1 > 0) {
@@ -1374,8 +1424,11 @@
           if (rta_tb[IFA_LOCAL]) {
             if (!inet_ntop(TT.addressfamily, RTA_DATA(rta_tb[IFA_LOCAL]),
                   lbuf, sizeof(lbuf))) perror_exit("inet");
-            if (strcmp(addrinfo.addr, lbuf))
-              continue;
+            if ( !addrinfo.zero_iplast && !addrinfo.zero_prefix && 
+                !(strstr(lbuf, addrinfo.addr)))
+                    continue;
+            if ( addrinfo.zero_iplast && !(strstr(lbuf, addrinfo.addr)))
+                    continue;
             ip_match=1;
           }
           if (!ip_match)
@@ -1388,27 +1441,32 @@
               (addrinfo.ifindex == addressInfo->ifa_index)) {
             if ((addrinfo.scope ^ addressInfo->ifa_scope) & addrinfo.scopemask)
               continue;
-            else if (addrinfo.up && (link->flags & IFF_UP))
-              print_link_output(link);
-            else if (!addrinfo.up) print_link_output(link);
+            else if (!addrinfo.link) {
+                if (addrinfo.up && !(link->flags & IFF_UP));
+                else {
+                    print_link_output(link);
+                    addrinfo.link = 1;
+                }
+            }
           }
           if (TT.addressfamily &&
               (addrinfo.ifindex == addressInfo->ifa_index) &&
               (addrinfo.scope == -1)){
-            if (addrinfo.up && (link->flags & IFF_UP))
-              print_link_output(link);
-            else if (!addrinfo.up) print_link_output(link);
+              if (!addrinfo.link) {
+                  if (addrinfo.up && !(link->flags & IFF_UP));
+                  else {
+                      print_link_output(link);
+                      addrinfo.link = 1;
+                  }
+              }
           }
         }
-
-        for (; NLMSG_OK(addr_ptr, len); addr_ptr = NLMSG_NEXT(addr_ptr, len)) {
           if ((addr_ptr->nlmsg_type == RTM_NEWADDR))
             print_addrinfo(addr_ptr, flag_l);
           if ((addr_ptr->nlmsg_type == NLMSG_DONE) ||
               (addr_ptr->nlmsg_type == NLMSG_ERROR) ||
               (TT.flush && addrinfo.to))
             goto ret_stop;          
-        }
         if ((addr_ptr->nlmsg_type == NLMSG_DONE) ||
             (addr_ptr->nlmsg_type == NLMSG_ERROR))
           break;
@@ -1667,6 +1725,7 @@
   return 0;
 }
 
+
 static int route_get(char **argv)
 {
   int idx, flag;
@@ -2060,7 +2119,373 @@
   }
   return 0; // non reachable code.
 }
+//=========================================================================
+#ifndef NDA_RTA
+#define NDA_RTA(r) \
+  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
+#endif
+#ifndef NDA_PAYLOAD
+#define NDA_PAYLOAD(n)	NLMSG_PAYLOAD(n,sizeof(struct ndmsg))
+#endif
+
+#ifndef NDTA_RTA
+#define NDTA_RTA(r) \
+  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg))))
+#endif
+#ifndef NDTA_PAYLOAD
+#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg))
+#endif
+
+struct {
+  int state_set;
+  int fb, dev ,unused , state, all;
+  struct I_data to;
+} filter_neigh;
+
+static void printstate(int state, int val, char *string)
+{
+  if (val & state) {
+    printf("%s%s",string,(val & ~state) ? "," : "");
+  }
+}
+
+static int display_neigh_info(struct nlmsghdr *mhdr, char **argv)
+{
+  char iface[IF_NAMESIZE];
+  struct ndmsg *msg = NLMSG_DATA(mhdr);
+  int32_t tvar, msglen = mhdr->nlmsg_len - NLMSG_LENGTH(sizeof(struct ndmsg));
+  struct rtattr *rta, *attr[NDA_MAX+1] = {0,};
+
+  tvar = msglen;
+  for (rta = NDA_RTA(msg); RTA_OK(rta, tvar); rta=RTA_NEXT(rta, tvar))
+    if (rta->rta_type <= NDA_MAX) attr[rta->rta_type] = rta;
+
+  if (!filter_neigh.state_set) {
+    if ((!msg->ndm_state )||(msg->ndm_state == NUD_NOARP ) ) return 0;
+  }
+  else
+    if (filter_neigh.state != msg->ndm_state ) return 0;
+
+  if (filter_neigh.dev && (filter_neigh.dev != msg->ndm_ifindex)) return 0;
+  if (filter_neigh.unused && attr[NDA_CACHEINFO]) {
+    struct nda_cacheinfo *ci = RTA_DATA(attr[NDA_CACHEINFO]);
+    if (ci->ndm_refcnt) return 0;
+  }
+  if (filter_neigh.to.family) {
+    if(filter_neigh.to.netmask) {
+      char dst[INET6_ADDRSTRLEN];
+      int len = 0;
+      inet_ntop(msg->ndm_family, RTA_DATA(attr[NDA_DST]), toybuf, sizeof(toybuf));
+      inet_ntop(filter_neigh.to.family, &filter_neigh.to.addr, dst, sizeof(dst));
+      len = strlen(dst);
+      while (toybuf[len--] != '.') ;
+      if(strncmp(dst, toybuf, len+1)) return 0;
+    }
+    else if (memcmp(RTA_DATA(attr[NDA_DST]), &filter_neigh.to.addr,
+        filter_neigh.to.len)) return 0;
+  }
+  if (filter_neigh.fb) {
+    int sockfd;
+    struct {
+      struct nlmsghdr mhdr;
+      struct ndmsg msg;
+      unsigned char data[256];
+    } request;
+    
+    if (!filter_neigh.state_set) {
+      if ( msg->ndm_state == NUD_PERMANENT ) return 0;
+    }
+
+    memset(&request, 0, sizeof(request));
+    request.mhdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+    request.msg.ndm_family = msg->ndm_family;
+    request.msg.ndm_flags = 0;
+    request.msg.ndm_type = 0;
+    request.msg.ndm_state = NUD_PERMANENT;
+    request.mhdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
+    request.mhdr.nlmsg_type = RTM_DELNEIGH;
+    request.msg.ndm_ifindex = msg->ndm_ifindex;
+
+    if (attr[NDA_DST]) {
+      uint32_t addr[8] = {0,}, netmask = 0;
+      uint8_t len = 0;
+
+      *toybuf = 0;
+      inet_ntop(msg->ndm_family, RTA_DATA(attr[NDA_DST]), toybuf, sizeof(toybuf));
+      parse_prefix(addr, &netmask, &len, toybuf , request.msg.ndm_family);
+      if (len) 
+        request.msg.ndm_family = ((len == 4) ? AF_INET : AF_INET6);
+      add_string_to_rtattr(&request.mhdr, sizeof(request), NDA_DST,
+          addr, len);
+    }
+    sockfd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+    if (send(sockfd , (void*)&request, sizeof(request), 0) < 0)
+      perror_exit("Unable to send data on socket.");
+
+    while (1) {
+      struct nlmsghdr *mhdr;
+      int msglen = recv(sockfd, toybuf, sizeof(toybuf), 0);
+
+      if ((msglen < 0) && (errno == EINTR || errno == EAGAIN)) continue;
+      else if (msglen < 0) {
+        error_msg("netlink receive error %s", strerror(errno));
+        xclose(sockfd);
+        return 1;
+      } else if (!msglen) {
+        error_msg("EOF on netlink");
+        xclose(sockfd);
+        return 1;
+      }
 
+      for (mhdr = (struct nlmsghdr*)toybuf; NLMSG_OK(mhdr, msglen);
+          mhdr = NLMSG_NEXT(mhdr, msglen)) {
+        switch (mhdr->nlmsg_type) {
+          case NLMSG_DONE:
+            xclose(sockfd);
+            return 0;
+          case NLMSG_ERROR:
+            {
+              struct nlmsgerr *merr = (struct nlmsgerr*)NLMSG_DATA(mhdr);
+
+              if (!merr->error)  { xclose(sockfd); return 0; }
+              if (mhdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
+                error_msg("ERROR truncated");
+              else {
+                errno = -merr->error;
+                perror_msg("RTNETLINK answers");
+              }
+              xclose(sockfd);
+              return 1;
+            }
+          default:
+            break;
+        }
+      }
+    }
+    xclose(sockfd);
+  }
+  if (attr[NDA_DST]) {
+    *toybuf = 0;
+    inet_ntop(msg->ndm_family, RTA_DATA(attr[NDA_DST]), toybuf, sizeof(toybuf));
+    printf("%s ", toybuf);
+  }
+  if_indextoname(msg->ndm_ifindex,iface);
+  printf("dev %s ", iface);
+  if (attr[NDA_LLADDR]) {
+    char a[64]= {0};
+    fill_address(RTA_DATA(attr[NDA_LLADDR]),a);
+    printf("lladdr %s ",a);
+  }
+  if (msg->ndm_flags & NTF_ROUTER) printf(" router");
+  if (msg->ndm_flags & NTF_PROXY) printf(" proxy");
+  if (!msg->ndm_state) printf("%s","NONE");
+  else {
+    printstate(NUD_INCOMPLETE,msg->ndm_state,"INCOMPLETE");
+    printstate(NUD_REACHABLE,msg->ndm_state,"REACHABLE");
+    printstate(NUD_STALE,msg->ndm_state,"STALE");
+    printstate(NUD_DELAY,msg->ndm_state,"DELAY");
+    printstate(NUD_PROBE,msg->ndm_state,"PROBE");
+    printstate(NUD_FAILED,msg->ndm_state,"FAILED");
+    printstate(NUD_NOARP,msg->ndm_state,"NOARP");
+    printstate(NUD_PERMANENT,msg->ndm_state,"PERMANENT");
+  }
+  printf("\n");
+  return 0;
+}
+
+static void show_ipneigh_help()
+{
+  printf("Usage: ip neigh { add | del | change | replace } "
+      "{ ADDR [ lladdr LLADDR ]\n"
+      "          [ nud { permanent | noarp | stale | reachable } ]\n"
+      "          | proxy ADDR } [ dev DEV ]\n");
+  printf("       ip neigh {show|flush} [ to PREFIX ] "
+      " [ dev DEV ] [ nud STATE ]\n");
+  exit(1);
+}
+
+static int getstate(char *state)
+{
+  int ret;
+  struct arglist cmd_objectlist[] = {{"permanent", NUD_PERMANENT},
+    {"reachable", NUD_REACHABLE},{"stale", NUD_STALE},{"noarp", NUD_NOARP},
+    {"none", NUD_NONE},{"incomplete", NUD_INCOMPLETE},{"delay", NUD_DELAY},
+    {"probe", NUD_PROBE},{"failed", NUD_FAILED},{NULL, -1}};
+
+  ret = substring_to_idx(state, cmd_objectlist);
+  if (ret == -1) show_ipneigh_help();
+  return ret;
+}
+
+
+static int neigh_get(char **argv, int flush)
+{
+  char *slash = NULL;
+  int idx = -1 ;
+  struct {
+    struct nlmsghdr mhdr;
+    struct ndmsg msg;
+  } request;
+  struct arglist cmd_objectlist[] = {{"flush", 0},{"to", 4}, {"dev", 2},
+    {"unused",3},{"nud", 1},{"all", 5},{NULL, -1}};
+  int dup_dst = 0, dup_dev = 0;
+  memset(&filter_neigh,0,sizeof(filter_neigh));
+
+  if (flush) filter_neigh.fb = 1;
+  for (; *argv; argv++) {
+    switch(idx = substring_to_idx(*argv++, cmd_objectlist)) {
+      case 1:
+        filter_neigh.state = getstate(*argv);
+        filter_neigh.state_set = 1;
+        break;
+      case 2:
+        if (!*argv) show_ipneigh_help();
+        if (dup_dev++) error_exit("Duplicate argument 'dev'");
+        filter_neigh.dev = get_ifaceindex(*argv, 1);
+        break;
+      case 3:
+        argv--;
+        filter_neigh.unused = 1;
+        break;
+      case 5:
+        argv--;
+        filter_neigh.all = 1;
+        break;
+      case 4:
+        if (!*argv) show_ipneigh_help();
+        argv++;
+      default:
+        argv--;
+        if (dup_dst++) error_exit("Duplicate argument 'address'");
+        parse_prefix(filter_neigh.to.addr, &filter_neigh.to.netmask,
+            &filter_neigh.to.len, *argv, filter_neigh.to.family);
+        if (filter_neigh.to.len)
+          filter_neigh.to.family = ((filter_neigh.to.len == 4) ?
+              AF_INET : AF_INET6);
+        slash = strchr(*argv,'/');
+        if(!slash) filter_neigh.to.netmask = 0;
+        break;
+    }
+  }
+
+  memset(&request, 0, sizeof(request));
+  request.mhdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+  request.mhdr.nlmsg_flags = NLM_F_ROOT |NLM_F_MATCH| NLM_F_REQUEST;
+  request.mhdr.nlmsg_type = RTM_GETNEIGH;
+  request.msg.ndm_family = TT.addressfamily;
+  request.msg.ndm_state = 0xFFFF & NUD_NOARP;
+  request.msg.ndm_flags = 0;
+  request.msg.ndm_type = 0;
+  send_nlmesg(0, 0, 0, &request, sizeof(request));
+  filter_nlmesg(display_neigh_info, NULL);
+  return 0;
+}
+
+static int neigh_update(char **argv){
+  struct {
+    struct nlmsghdr mhdr;
+    struct ndmsg msg;
+    unsigned char data[256];
+  } request;
+  int idx= 0;
+  char abuf[IF_NAMESIZE] = {0,};
+  struct arglist cmd_objectlist[] = {{"delete", 0}, {"add", 1},{"change", 2},
+    {"chg", 2},{"replace", 3},{"show", 4},{"list", 4},{"lst", 4},{"flush", 5},
+    {"help", 6},{"lladdr", 7},{"nud", 8},{"proxy", 9},{"dev", 11},{"to", 12},
+    {NULL, -1}};
+  int dup_lla = 0,dup_dst=0, dev=0;
+
+  memset(&request, 0, sizeof(request));
+  request.mhdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+  request.msg.ndm_family = TT.addressfamily;
+  request.msg.ndm_flags = 0;
+  request.msg.ndm_type = 0;
+  request.msg.ndm_state = NUD_PERMANENT;
+  //for operation add delete and replace 
+  switch(idx = substring_to_idx(*argv++, cmd_objectlist)) {
+    case 0:
+      request.mhdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
+      request.mhdr.nlmsg_type = RTM_DELNEIGH;          
+      break;
+    case 1:
+      request.mhdr.nlmsg_type = RTM_NEWNEIGH;    
+      request.mhdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK|NLM_F_CREATE|
+                                                                    NLM_F_EXCL;
+      break;
+    case 2:
+      request.mhdr.nlmsg_type = RTM_NEWNEIGH;   
+      request.mhdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK|NLM_F_REPLACE;
+      break;
+    case 3:
+      request.mhdr.nlmsg_type = RTM_NEWNEIGH;    
+      request.mhdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK|NLM_F_CREATE|
+                                                                 NLM_F_REPLACE;
+      break;
+    case 4:
+      return neigh_get(argv,0);
+    case 5:
+      return neigh_get(argv,1);
+    default:
+      show_ipneigh_help();
+  }
+
+  //operation parameters
+  for (; *argv; argv++) {
+    uint32_t addr[8] = {0,}, netmask = 0;
+    uint8_t len = 0;
+    switch(idx = substring_to_idx(*argv++, cmd_objectlist)) {
+      case 6:
+        show_ipneigh_help();
+      case 7:
+        if (dup_lla++) error_exit("Duplicate argument 'lladdr'");
+        if (!*argv) show_ipneigh_help();
+        fill_hwaddr(*argv, IF_NAMESIZE, (unsigned char *)abuf);
+        add_string_to_rtattr(&request.mhdr, sizeof(request),NDA_LLADDR, 
+            abuf, 6);
+        break;
+      case 8:
+        if (!*argv) show_ipneigh_help();
+        request.msg.ndm_state = getstate(*argv);
+        break;
+      case 9:
+        if (dup_dst++) error_exit("Duplicate argument 'address'");
+        if (!*argv) show_ipneigh_help();
+        parse_prefix(addr, &netmask, &len,*argv,request.msg.ndm_family);
+        if (len) request.msg.ndm_family = ((len == 4) ? AF_INET : 
+            AF_INET6);
+        add_string_to_rtattr(&request.mhdr, sizeof(request), NDA_DST,
+            addr, len);
+        request.msg.ndm_flags |= NTF_PROXY;
+        break;
+      case 11:
+        if (!*argv) show_ipneigh_help();
+        request.msg.ndm_ifindex = get_ifaceindex(*argv, 1);
+        dev = 1;
+        break;
+      case 12:
+        if (!*argv) show_ipneigh_help();
+        ++argv;
+      default:
+        if (dup_dst++) error_exit("Duplicate argument 'to'");
+        parse_prefix(addr, &netmask, &len, *--argv, request.msg.ndm_family);
+        if (len) request.msg.ndm_family = ((len == 4) ? AF_INET : 
+            AF_INET6);
+        add_string_to_rtattr(&request.mhdr, sizeof(request), NDA_DST,
+            addr, len);
+        break;
+    }
+  }
+  if (!dev || !dup_dst)
+    error_exit("Device and destination are required arguments.");
+  send_nlmesg(0, 0, 0, &request, sizeof(request));
+  if (filter_nlmesg(NULL, NULL)) return 2;
+  return 0;
+}
+
+static int ipneigh(char **argv) 
+{
+  return (!*argv) ? neigh_get(argv,0) : neigh_update(argv);
+}
 
 // ===========================================================================
 // code for ip rule.
@@ -2347,15 +2772,22 @@
 
 static int display_tunnel(struct ip_tunnel_parm *ptnl)
 {
-  char rmt_addr[64], lcl_addr[64], ikey_str[64], okey_str[64];
+  if (ptnl->iph.protocol == IPPROTO_IPIP) strcpy(toybuf, "ip");
+  else if (ptnl->iph.protocol == IPPROTO_GRE) strcpy(toybuf, "gre");
+  else if (ptnl->iph.protocol == IPPROTO_IPV6) strcpy(toybuf, "ipv6");
+  else strcpy(toybuf, "unknown");
+
+  printf("%s: %s/ip", ptnl->name, toybuf);
+  *toybuf = *(toybuf+64) = 0;
+  if (ptnl->iph.daddr)
+    inet_ntop(AF_INET, &ptnl->iph.daddr, toybuf, 64);
+  if (!*toybuf) strcpy(toybuf, "any");
+  if (ptnl->iph.saddr)
+    inet_ntop(AF_INET, &ptnl->iph.daddr, toybuf+64, 64);
+  if (!*(toybuf+64))
+    strcpy(toybuf+64, "any");
 
-  printf("%s: %s/ip", ptnl->name, ptnl->iph.protocol == IPPROTO_IPIP ? "ip" :
-      (ptnl->iph.protocol == IPPROTO_GRE ? "gre" :
-       (ptnl->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")));
-  printf("  remote %s  local %s ", ptnl->iph.daddr ?
-      inet_ntop(AF_INET, &ptnl->iph.daddr, rmt_addr, sizeof(rmt_addr)) : "any",
-      ptnl->iph.saddr ? inet_ntop(AF_INET, &ptnl->iph.saddr, lcl_addr,
-        sizeof(lcl_addr)) : "any");
+  printf("  remote %s  local %s ", toybuf, toybuf+64);
   if (ptnl->link) {
     struct ifreq req;
     int fd;
@@ -2376,13 +2808,15 @@
         namefromRPDB((ptnl->iph.tos & ~1), RPDB_rtdsfield));
   }
   if (!(ptnl->iph.frag_off & htons(IP_DF))) printf(" nopmtudisc");
-  inet_ntop(AF_INET, &ptnl->i_key, ikey_str, sizeof(ikey_str));
+  *toybuf = 0;
+  inet_ntop(AF_INET, &ptnl->i_key, toybuf, 64);
   if ((ptnl->i_flags & GRE_KEY) && (ptnl->o_flags & GRE_KEY)
-      && ptnl->o_key == ptnl->i_key) printf(" key %s", ikey_str);
+      && ptnl->o_key == ptnl->i_key) printf(" key %s", toybuf);
   else if ((ptnl->i_flags | ptnl->o_flags) & GRE_KEY) {
-    inet_ntop(AF_INET, &ptnl->o_key, okey_str, sizeof(okey_str));
-    if (ptnl->i_flags & GRE_KEY) printf(" ikey %s ", ikey_str);
-    if (ptnl->o_flags & GRE_KEY) printf(" okey %s ", okey_str);
+    *(toybuf+64) = 0;
+    inet_ntop(AF_INET, &ptnl->o_key, toybuf+64, 64);
+    if (ptnl->i_flags & GRE_KEY) printf(" ikey %s ", toybuf);
+    if (ptnl->o_flags & GRE_KEY) printf(" okey %s ", toybuf+64);
   }
   if (ptnl->i_flags & GRE_SEQ) printf("\n  Drop packets out of sequence.\n");
   if (ptnl->i_flags & GRE_CSUM)
@@ -2730,8 +3164,8 @@
 {
   char **optargv = toys.argv;
   int idx, isip = !(toys.which->name[2]); //1 -> if only ip
-  cmdobj ipcmd, cmdobjlist[] = {ipaddr, iplink, iproute, iprule, iptunnel};
-
+  cmdobj ipcmd, cmdobjlist[] = {ipaddr,iplink,iproute,iprule,iptunnel,ipneigh};
+  
   for (++optargv; *optargv; ++optargv) {
     char *ptr = *optargv;
     struct arglist ip_options[] = {{"oneline", 0}, {"family",  1},
@@ -2770,13 +3204,12 @@
                break; // unreachable code.
     }
   }
-
-  TT.sockfd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
-
+  
+  TT.sockfd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);  
   if (isip) {// only for ip
     if (*optargv) {
       struct arglist ip_objectlist[] = { {"address", 0}, {"link", 1},
-        {"route", 2}, {"rule", 3}, {"tunnel", 4}, {"tunl", 4}, {NULL, -1}};
+      {"route", 2}, {"rule", 3}, {"tunnel", 4}, {"tunl", 4}, {"neighbour",5},{NULL, -1}};
 
       if ((idx = substring_to_idx(*optargv, ip_objectlist)) == -1) iphelp();
       ipcmd = cmdobjlist[idx];
@@ -2784,7 +3217,7 @@
     } else iphelp();
   } else {
     struct arglist ip_objectlist[] = { {"ipaddr", 0}, {"iplink", 1},
-      {"iproute", 2}, {"iprule", 3}, {"iptunnel", 4}, {NULL, -1}};
+      {"iproute", 2}, {"iprule", 3}, {"iptunnel", 4}, {"ipneighbour",5},{NULL, -1}};
     if ((idx = string_to_idx(toys.which->name, ip_objectlist)) == -1)
       iphelp();
     ipcmd = cmdobjlist[idx];
