2 * arp This file contains an implementation of the command
3 * that maintains the kernel's ARP cache. It is derived
4 * from Berkeley UNIX arp(8), but cleaner and with sup-
5 * port for devices other than Ethernet.
7 * NET-TOOLS A collection of programs that form the base set of the
8 * NET-3 Networking Distribution for the LINUX operating
11 * Version: $Id: arp.c,v 1.27 2009/09/06 22:50:11 vapier Exp $
13 * Maintainer: Bernd 'eckes' Eckenfels, <net-tools@lina.inka.de>
15 * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
18 * (based on work from Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>)
19 * Alan Cox : modified for NET3
20 * Andrew Tridgell : proxy arp netmasks
21 * Bernd Eckenfels : -n option
22 * Bernd Eckenfels : Use only /proc for display
23 * {1.60} Bernd Eckenfels : new arpcode (-i) for 1.3.42 but works
25 * {1.61} Bernd Eckenfels : more verbose messages
26 * {1.62} Bernd Eckenfels : check -t for hw adresses and try to
27 * explain EINVAL (jeff)
28 *970125 {1.63} Bernd Eckenfels : -a print hardwarename instead of tiltle
29 *970201 {1.64} Bernd Eckenfels : net-features.h support
30 *970203 {1.65} Bernd Eckenfels : "#define" in "#if",
31 * -H|-A additional to -t|-p
32 *970214 {1.66} Bernd Eckenfels : Fix optarg required for -H and -A
33 *970412 {1.67} Bernd Eckenfels : device=""; is default
34 *970514 {1.68} Bernd Eckenfels : -N and -D
35 *970517 {1.69} Bernd Eckenfels : usage() fixed
36 *970622 {1.70} Bernd Eckenfels : arp -d priv
37 *970106 {1.80} Bernd Eckenfels : new syntax without -D and with "dev <If>",
38 * ATF_MAGIC, ATF_DONTPUB support.
39 * Typo fix (Debian Bug#5728 Giuliano Procida)
40 *970803 {1.81} Bernd Eckenfels : removed junk comment line 1
41 *970925 {1.82} Bernd Eckenfels : include fix for libc6
42 *980213 (1.83) Phil Blundell: set ATF_COM on new entries
43 *980629 (1.84) Arnaldo Carvalho de Melo: gettext instead of catgets
44 *990101 {1.85} Bernd Eckenfels fixed usage and return codes
45 *990105 (1.86) Phil Blundell: don't ignore EINVAL in arp_set
46 *991121 (1.87) Bernd Eckenfels: yes --device has a mandatory arg
47 *010404 (1.88) Arnaldo Carvalho de Melo: use setlocale
49 * This program is free software; you can redistribute it
50 * and/or modify it under the terms of the GNU General
51 * Public License as published by the Free Software
52 * Foundation; either version 2 of the License, or (at
53 * your option) any later version.
55 #include <sys/types.h>
56 #include <sys/socket.h>
57 #include <sys/ioctl.h>
59 /* #include <linux/netdevice.h> */
60 /* #include <linux/if_arp.h> */
61 #include <net/if_arp.h>
70 #include "net-support.h"
71 #include "pathnames.h"
77 #define DFLT_AF "inet"
78 #define DFLT_HW "ether"
81 #include "lib/net-features.h"
83 char *Release = RELEASE, *Version = "arp 1.88 (2001-04-04)";
85 int opt_n = 0; /* do not resolve addresses */
86 int opt_N = 0; /* use symbolic names */
87 int opt_v = 0; /* debugging output flag */
88 int opt_D = 0; /* HW-address is devicename */
89 int opt_e = 0; /* 0=BSD output, 1=new linux */
90 int opt_a = 0; /* all entries, substring match */
91 struct aftype *ap; /* current address family */
92 struct hwtype *hw; /* current hardware type */
93 int sockfd = 0; /* active socket descriptor */
94 int hw_set = 0; /* flag if hw-type was set (-H) */
95 char device[16] = ""; /* current device */
96 static void usage(void);
98 /* Delete an entry from the ARP cache. */
99 static int arp_del(char **args)
103 struct sockaddr_storage ss;
108 memset((char *) &req, 0, sizeof(req));
110 /* Resolve the host name. */
112 fprintf(stderr, _("arp: need host name\n"));
115 safe_strncpy(host, *args, (sizeof host));
116 sa = (struct sockaddr *)&ss;
117 if (ap->input(0, host, sa) < 0) {
121 /* If a host has more than one address, use the correct one! */
122 memcpy((char *) &req.arp_pa, (char *) sa, sizeof(struct sockaddr));
125 req.arp_ha.sa_family = hw->type;
127 req.arp_flags = ATF_PERM;
129 while (*args != NULL) {
131 fprintf(stderr, "args=%s\n", *args);
132 if (!strcmp(*args, "pub")) {
137 if (!strcmp(*args, "priv")) {
142 if (!strcmp(*args, "temp")) {
143 req.arp_flags &= ~ATF_PERM;
147 if (!strcmp(*args, "trail")) {
148 req.arp_flags |= ATF_USETRAILERS;
152 if (!strcmp(*args, "dontpub")) {
154 req.arp_flags |= ATF_DONTPUB;
156 ENOSUPP("arp", "ATF_DONTPUB");
161 if (!strcmp(*args, "auto")) {
163 req.arp_flags |= ATF_MAGIC;
165 ENOSUPP("arp", "ATF_MAGIC");
170 if (!strcmp(*args, "dev")) {
173 safe_strncpy(device, *args, sizeof(device));
177 if (!strcmp(*args, "netmask")) {
180 if (strcmp(*args, "255.255.255.255") != 0) {
182 if (ap->input(0, host, sa) < 0) {
186 memcpy((char *) &req.arp_netmask, (char *) sa,
187 sizeof(struct sockaddr));
188 req.arp_flags |= ATF_NETMASK;
196 // if neighter priv nor pub is given, work on both
200 strcpy(req.arp_dev, device);
202 /* unfortuatelly the kernel interface does not allow us to
203 delete private entries anlone, so we need this hack
204 to avoid "not found" errors if we try both. */
207 /* Call the kernel. */
210 fprintf(stderr, "arp: SIOCDARP(dontpub)\n");
211 if (ioctl(sockfd, SIOCDARP, &req) < 0) {
212 if ((errno == ENXIO) || (errno == ENOENT)) {
215 printf(_("No ARP entry for %s\n"), host);
218 perror("SIOCDARP(dontpub)");
223 if (!deleted && (flags & 1)) {
225 req.arp_flags |= ATF_PUBL;
227 fprintf(stderr, "arp: SIOCDARP(pub)\n");
228 if (ioctl(sockfd, SIOCDARP, &req) < 0) {
229 if ((errno == ENXIO) || (errno == ENOENT)) {
230 printf(_("No ARP entry for %s\n"), host);
233 perror("SIOCDARP(pub)");
240 /* Get the hardware address to a specified interface name */
241 static int arp_getdevhw(char *ifname, struct sockaddr *sa, struct hwtype *hw)
246 strcpy(ifr.ifr_name, ifname);
247 if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) < 0) {
248 fprintf(stderr, _("arp: cant get HW-Address for `%s': %s.\n"), ifname, strerror(errno));
251 if (hw && (ifr.ifr_hwaddr.sa_family != hw->type)) {
252 fprintf(stderr, _("arp: protocol type mismatch.\n"));
255 memcpy((char *) sa, (char *) &(ifr.ifr_hwaddr), sizeof(struct sockaddr));
258 if (!(xhw = get_hwntype(ifr.ifr_hwaddr.sa_family)) || (xhw->print == 0)) {
259 xhw = get_hwntype(-1);
261 fprintf(stderr, _("arp: device `%s' has HW address %s `%s'.\n"), ifname, xhw->name, xhw->print((char *)&ifr.ifr_hwaddr.sa_data));
266 /* Set an entry in the ARP cache. */
267 static int arp_set(char **args)
271 struct sockaddr_storage ss;
275 memset((char *) &req, 0, sizeof(req));
277 /* Resolve the host name. */
279 fprintf(stderr, _("arp: need host name\n"));
282 safe_strncpy(host, *args++, (sizeof host));
283 sa = (struct sockaddr *)&ss;
284 if (ap->input(0, host, sa) < 0) {
288 /* If a host has more than one address, use the correct one! */
289 memcpy((char *) &req.arp_pa, (char *) sa, sizeof(struct sockaddr));
291 /* Fetch the hardware address. */
293 fprintf(stderr, _("arp: need hardware address\n"));
297 if (arp_getdevhw(*args++, &req.arp_ha, hw_set ? hw : NULL) < 0)
300 if (hw->input(*args++, &req.arp_ha) < 0) {
301 fprintf(stderr, _("arp: invalid hardware address\n"));
306 /* Check out any modifiers. */
307 flags = ATF_PERM | ATF_COM;
308 while (*args != NULL) {
309 if (!strcmp(*args, "temp")) {
314 if (!strcmp(*args, "pub")) {
319 if (!strcmp(*args, "priv")) {
324 if (!strcmp(*args, "trail")) {
325 flags |= ATF_USETRAILERS;
329 if (!strcmp(*args, "dontpub")) {
331 flags |= ATF_DONTPUB;
333 ENOSUPP("arp", "ATF_DONTPUB");
338 if (!strcmp(*args, "auto")) {
342 ENOSUPP("arp", "ATF_MAGIC");
347 if (!strcmp(*args, "dev")) {
350 safe_strncpy(device, *args, sizeof(device));
354 if (!strcmp(*args, "netmask")) {
357 if (strcmp(*args, "255.255.255.255") != 0) {
359 if (ap->input(0, host, sa) < 0) {
363 memcpy((char *) &req.arp_netmask, (char *) sa,
364 sizeof(struct sockaddr));
365 flags |= ATF_NETMASK;
373 /* Fill in the remainder of the request. */
374 req.arp_flags = flags;
376 strcpy(req.arp_dev, device);
378 /* Call the kernel. */
380 fprintf(stderr, "arp: SIOCSARP()\n");
381 if (ioctl(sockfd, SIOCSARP, &req) < 0) {
389 /* Process an EtherFile */
390 static int arp_file(char *name)
397 if ((fp = fopen(name, "r")) == NULL) {
398 fprintf(stderr, _("arp: cannot open etherfile %s !\n"), name);
401 /* Read the lines in the file. */
403 while (fgets(buff, sizeof(buff), fp) != (char *) NULL) {
406 fprintf(stderr, ">> %s", buff);
407 if ((sp = strchr(buff, '\n')) != (char *) NULL)
409 if (buff[0] == '#' || buff[0] == '\0')
412 argc = getargs(buff, args);
414 fprintf(stderr, _("arp: format error on line %u of etherfile %s !\n"),
418 if (strchr (args[0], ':') != NULL) {
419 /* We have a correct ethers file, switch hw adress and hostname
426 if (arp_set(args) != 0)
427 fprintf(stderr, _("arp: cannot set entry on line %u of etherfile %s !\n"),
436 /* Print the contents of an ARP request block. */
437 static void arp_disp_2(char *name, int type, int arp_flags, char *hwa, char *mask, char *dev)
439 static int title = 0;
443 xhw = get_hwntype(type);
445 xhw = get_hwtype(DFLT_HW);
448 printf(_("Address HWtype HWaddress Flags Mask Iface\n"));
450 /* Setup the flags. */
452 if (arp_flags & ATF_COM)
454 if (arp_flags & ATF_PERM)
456 if (arp_flags & ATF_PUBL)
459 if (arp_flags & ATF_MAGIC)
463 if (arp_flags & ATF_DONTPUB)
466 if (arp_flags & ATF_USETRAILERS)
469 if (!(arp_flags & ATF_NETMASK))
472 printf("%-23.23s ", name);
474 if (!(arp_flags & ATF_COM)) {
475 if (arp_flags & ATF_PUBL)
476 printf("%-8.8s%-20.20s", "*", _("<from_interface>"));
478 printf("%-8.8s%-20.20s", "", _("(incomplete)"));
480 printf("%-8.8s%-20.20s", xhw->name, hwa);
483 printf("%-6.6s%-15.15s %s\n", flags, mask, dev);
486 /* Print the contents of an ARP request block. */
487 static void arp_disp(char *name, char *ip, int type, int arp_flags, char *hwa, char *mask, char *dev)
491 xhw = get_hwntype(type);
493 xhw = get_hwtype(DFLT_HW);
495 printf(_("%s (%s) at "), name, ip);
497 if (!(arp_flags & ATF_COM)) {
498 if (arp_flags & ATF_PUBL)
499 printf("<from_interface> ");
501 printf(_("<incomplete> "));
503 printf("%s [%s] ", hwa, xhw->name);
506 if (arp_flags & ATF_NETMASK)
507 printf(_("netmask %s "), mask);
509 if (arp_flags & ATF_PERM)
511 if (arp_flags & ATF_PUBL)
514 if (arp_flags & ATF_MAGIC)
518 if (arp_flags & ATF_DONTPUB)
521 if (arp_flags & ATF_USETRAILERS)
524 printf(_("on %s\n"), dev);
528 /* Display the contents of the ARP cache in the kernel. */
529 static int arp_show(char *name)
532 struct sockaddr_storage ss;
542 int num, entries = 0, showed = 0;
546 sa = (struct sockaddr *)&ss;
548 /* Resolve the host name. */
549 safe_strncpy(host, name, (sizeof host));
550 if (ap->input(0, host, sa) < 0) {
554 safe_strncpy(host, ap->sprint(sa, 1), sizeof(host));
556 /* Open the PROCps kernel table. */
557 if ((fp = fopen(_PATH_PROCNET_ARP, "r")) == NULL) {
558 perror(_PATH_PROCNET_ARP);
561 /* Bypass header -- read until newline */
562 if (fgets(line, sizeof(line), fp) != (char *) NULL) {
565 /* Read the ARP cache entries. */
566 for (; fgets(line, sizeof(line), fp);) {
567 num = sscanf(line, "%s 0x%x 0x%x %99s %99s %99s\n",
568 ip, &type, &flags, hwa, mask, dev);
573 /* if the user specified hw-type differs, skip it */
574 if (hw_set && (type != hw->type))
577 /* if the user specified address differs, skip it */
578 if (host[0] && strcmp(ip, host))
581 /* if the user specified device differs, skip it */
582 if (device[0] && strcmp(dev, device))
586 /* This IS ugly but it works -be */
590 if (ap->input(0, ip, sa) < 0)
593 hostname = ap->sprint(sa, opt_n | 0x8000);
594 if (strcmp(hostname, ip) == 0)
599 arp_disp_2(hostname[0] == '?' ? ip : hostname, type, flags, hwa, mask, dev);
601 arp_disp(hostname, ip, type, flags, hwa, mask, dev);
605 printf(_("Entries: %d\tSkipped: %d\tFound: %d\n"), entries, entries - showed, showed);
608 if (host[0] && !opt_a)
609 printf(_("%s (%s) -- no entry\n"), name, host);
610 else if (hw_set || host[0] || device[0]) {
611 printf(_("arp: in %d entries no match found.\n"), entries);
618 static void version(void)
620 fprintf(stderr, "%s\n%s\n%s\n", Release, Version, Features);
624 static void usage(void)
626 fprintf(stderr, _("Usage:\n arp [-vn] [<HW>] [-i <if>] [-a] [<hostname>] <-Display ARP cache\n"));
627 fprintf(stderr, _(" arp [-v] [-i <if>] -d <host> [pub] <-Delete ARP entry\n"));
628 fprintf(stderr, _(" arp [-vnD] [<HW>] [-i <if>] -f [<filename>] <-Add entry from file\n"));
629 fprintf(stderr, _(" arp [-v] [<HW>] [-i <if>] -s <host> <hwaddr> [temp] <-Add entry\n"));
630 fprintf(stderr, _(" arp [-v] [<HW>] [-i <if>] -Ds <host> <if> [netmask <nm>] pub <-''-\n\n"));
632 fprintf(stderr, _(" -a display (all) hosts in alternative (BSD) style\n"));
633 fprintf(stderr, _(" -e display (all) hosts in default (Linux) style\n"));
634 fprintf(stderr, _(" -s, --set set a new ARP entry\n"));
635 fprintf(stderr, _(" -d, --delete delete a specified entry\n"));
636 fprintf(stderr, _(" -v, --verbose be verbose\n"));
637 fprintf(stderr, _(" -n, --numeric don't resolve names\n"));
638 fprintf(stderr, _(" -i, --device specify network interface (e.g. eth0)\n"));
639 fprintf(stderr, _(" -D, --use-device read <hwaddr> from given device\n"));
640 fprintf(stderr, _(" -A, -p, --protocol specify protocol family\n"));
641 fprintf(stderr, _(" -f, --file read new entries from file or from /etc/ethers\n\n"));
643 fprintf(stderr, _(" <HW>=Use '-H <hw>' to specify hardware address type. Default: %s\n"), DFLT_HW);
644 fprintf(stderr, _(" List of possible hardware types (which support ARP):\n"));
645 print_hwlist(1); /* 1 = ARPable */
649 int main(int argc, char **argv)
652 struct option longopts[] =
654 {"verbose", 0, 0, 'v'},
655 {"version", 0, 0, 'V'},
657 {"delete", 0, 0, 'd'},
659 {"numeric", 0, 0, 'n'},
661 {"protocol", 1, 0, 'A'},
662 {"hw-type", 1, 0, 'H'},
663 {"device", 1, 0, 'i'},
665 {"use-device", 0, 0, 'D'},
666 {"symbolic", 0, 0, 'N'},
671 setlocale (LC_ALL, "");
672 bindtextdomain("net-tools", "/usr/share/locale");
673 textdomain("net-tools");
676 /* Initialize variables... */
677 if ((hw = get_hwtype(DFLT_HW)) == NULL) {
678 fprintf(stderr, _("%s: hardware type not supported!\n"), DFLT_HW);
681 if ((ap = get_aftype(DFLT_AF)) == NULL) {
682 fprintf(stderr, _("%s: address family not supported!\n"), DFLT_AF);
687 /* Fetch the command-line arguments. */
689 while ((i = getopt_long(argc, argv, "A:H:adfp:nsei:t:vh?DNV", longopts, &lop)) != EOF)
717 fprintf(stderr, _("arp: -N not yet supported.\n"));
725 ap = get_aftype(optarg);
727 fprintf(stderr, _("arp: %s: unknown address family.\n"),
734 hw = get_hwtype(optarg);
736 fprintf(stderr, _("arp: %s: unknown hardware type.\n"),
743 safe_strncpy(device, optarg, sizeof(device));
754 if (ap->af != AF_INET) {
755 fprintf(stderr, _("arp: %s: kernel only supports 'inet'.\n"),
760 /* If not hw type specified get default */
762 if ((hw = get_hwtype(DFLT_HW)) == NULL) {
763 fprintf(stderr, _("%s: hardware type not supported!\n"), DFLT_HW);
768 fprintf(stderr, _("arp: %s: hardware type without ARP support.\n"),
772 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
776 /* Now see what we have to do here... */
780 what = arp_show(argv[optind]);
783 case 1: /* show an ARP entry in the cache */
784 what = arp_show(argv[optind]);
787 case 2: /* process an EtherFile */
788 what = arp_file(argv[optind] ? argv[optind] : "/etc/ethers");
791 case 3: /* delete an ARP entry from the cache */
792 what = arp_del(&argv[optind]);
795 case 4: /* set an ARP entry in the cache */
796 what = arp_set(&argv[optind]);