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.23 2003/02/08 19:56:25 ecki 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
)
107 memset((char *) &req
, 0, sizeof(req
));
109 /* Resolve the host name. */
111 fprintf(stderr
, _("arp: need host name\n"));
114 safe_strncpy(host
, *args
, (sizeof host
));
115 if (ap
->input(0, host
, &sa
) < 0) {
119 /* If a host has more than one address, use the correct one! */
120 memcpy((char *) &req
.arp_pa
, (char *) &sa
, sizeof(struct sockaddr
));
123 req
.arp_ha
.sa_family
= hw
->type
;
125 req
.arp_flags
= ATF_PERM
;
127 while (*args
!= NULL
) {
129 fprintf(stderr
, "args=%s\n", *args
);
130 if (!strcmp(*args
, "pub")) {
135 if (!strcmp(*args
, "priv")) {
140 if (!strcmp(*args
, "temp")) {
141 req
.arp_flags
&= ~ATF_PERM
;
145 if (!strcmp(*args
, "trail")) {
146 req
.arp_flags
|= ATF_USETRAILERS
;
150 if (!strcmp(*args
, "dontpub")) {
151 #ifdef HAVE_ATF_DONTPUB
152 req
.arp_flags
|= ATF_DONTPUB
;
154 ENOSUPP("arp", "ATF_DONTPUB");
159 if (!strcmp(*args
, "auto")) {
160 #ifdef HAVE_ATF_MAGIC
161 req
.arp_flags
|= ATF_MAGIC
;
163 ENOSUPP("arp", "ATF_MAGIC");
168 if (!strcmp(*args
, "dev")) {
171 safe_strncpy(device
, *args
, sizeof(device
));
175 if (!strcmp(*args
, "netmask")) {
178 if (strcmp(*args
, "255.255.255.255") != 0) {
180 if (ap
->input(0, host
, &sa
) < 0) {
184 memcpy((char *) &req
.arp_netmask
, (char *) &sa
,
185 sizeof(struct sockaddr
));
186 req
.arp_flags
|= ATF_NETMASK
;
194 // if neighter priv nor pub is given, work on both
198 strcpy(req
.arp_dev
, device
);
200 /* unfortuatelly the kernel interface does not allow us to
201 delete private entries anlone, so we need this hack
202 to avoid "not found" errors if we try both. */
205 /* Call the kernel. */
208 fprintf(stderr
, "arp: SIOCDARP(nopub)\n");
209 if (ioctl(sockfd
, SIOCDARP
, &req
) < 0) {
210 if ((errno
== ENXIO
) || (errno
== ENOENT
)) {
213 printf(_("No ARP entry for %s\n"), host
);
216 perror("SIOCDARP(nopub)");
221 if (!deleted
&& (flags
& 1)) {
223 req
.arp_flags
|= ATF_PUBL
;
225 fprintf(stderr
, "arp: SIOCDARP(pub)\n");
226 if (ioctl(sockfd
, SIOCDARP
, &req
) < 0) {
227 if ((errno
== ENXIO
) || (errno
== ENOENT
)) {
228 printf(_("No ARP entry for %s\n"), host
);
231 perror("SIOCDARP(pub)");
238 /* Get the hardware address to a specified interface name */
239 static int arp_getdevhw(char *ifname
, struct sockaddr
*sa
, struct hwtype
*hw
)
244 strcpy(ifr
.ifr_name
, ifname
);
245 if (ioctl(sockfd
, SIOCGIFHWADDR
, &ifr
) < 0) {
246 fprintf(stderr
, _("arp: cant get HW-Address for `%s': %s.\n"), ifname
, strerror(errno
));
249 if (hw
&& (ifr
.ifr_hwaddr
.sa_family
!= hw
->type
)) {
250 fprintf(stderr
, _("arp: protocol type mismatch.\n"));
253 memcpy((char *) sa
, (char *) &(ifr
.ifr_hwaddr
), sizeof(struct sockaddr
));
256 if (!(xhw
= get_hwntype(ifr
.ifr_hwaddr
.sa_family
)) || (xhw
->print
== 0)) {
257 xhw
= get_hwntype(-1);
259 fprintf(stderr
, _("arp: device `%s' has HW address %s `%s'.\n"), ifname
, xhw
->name
, xhw
->print((char *)&ifr
.ifr_hwaddr
.sa_data
));
264 /* Set an entry in the ARP cache. */
265 static int arp_set(char **args
)
272 memset((char *) &req
, 0, sizeof(req
));
274 /* Resolve the host name. */
276 fprintf(stderr
, _("arp: need host name\n"));
279 safe_strncpy(host
, *args
++, (sizeof host
));
280 if (ap
->input(0, host
, &sa
) < 0) {
284 /* If a host has more than one address, use the correct one! */
285 memcpy((char *) &req
.arp_pa
, (char *) &sa
, sizeof(struct sockaddr
));
287 /* Fetch the hardware address. */
289 fprintf(stderr
, _("arp: need hardware address\n"));
293 if (arp_getdevhw(*args
++, &req
.arp_ha
, hw_set
? hw
: NULL
) < 0)
296 if (hw
->input(*args
++, &req
.arp_ha
) < 0) {
297 fprintf(stderr
, _("arp: invalid hardware address\n"));
302 /* Check out any modifiers. */
303 flags
= ATF_PERM
| ATF_COM
;
304 while (*args
!= NULL
) {
305 if (!strcmp(*args
, "temp")) {
310 if (!strcmp(*args
, "pub")) {
315 if (!strcmp(*args
, "priv")) {
320 if (!strcmp(*args
, "trail")) {
321 flags
|= ATF_USETRAILERS
;
325 if (!strcmp(*args
, "dontpub")) {
326 #ifdef HAVE_ATF_DONTPUB
327 flags
|= ATF_DONTPUB
;
329 ENOSUPP("arp", "ATF_DONTPUB");
334 if (!strcmp(*args
, "auto")) {
335 #ifdef HAVE_ATF_MAGIC
338 ENOSUPP("arp", "ATF_MAGIC");
343 if (!strcmp(*args
, "dev")) {
346 safe_strncpy(device
, *args
, sizeof(device
));
350 if (!strcmp(*args
, "netmask")) {
353 if (strcmp(*args
, "255.255.255.255") != 0) {
355 if (ap
->input(0, host
, &sa
) < 0) {
359 memcpy((char *) &req
.arp_netmask
, (char *) &sa
,
360 sizeof(struct sockaddr
));
361 flags
|= ATF_NETMASK
;
369 /* Fill in the remainder of the request. */
370 req
.arp_flags
= flags
;
372 strcpy(req
.arp_dev
, device
);
374 /* Call the kernel. */
376 fprintf(stderr
, "arp: SIOCSARP()\n");
377 if (ioctl(sockfd
, SIOCSARP
, &req
) < 0) {
385 /* Process an EtherFile */
386 static int arp_file(char *name
)
393 if ((fp
= fopen(name
, "r")) == NULL
) {
394 fprintf(stderr
, _("arp: cannot open etherfile %s !\n"), name
);
397 /* Read the lines in the file. */
399 while (fgets(buff
, sizeof(buff
), fp
) != (char *) NULL
) {
402 fprintf(stderr
, ">> %s", buff
);
403 if ((sp
= strchr(buff
, '\n')) != (char *) NULL
)
405 if (buff
[0] == '#' || buff
[0] == '\0')
408 argc
= getargs(buff
, args
);
410 fprintf(stderr
, _("arp: format error on line %u of etherfile %s !\n"),
414 if (strchr (args
[0], ':') != NULL
) {
415 /* We have a correct ethers file, switch hw adress and hostname
422 if (arp_set(args
) != 0)
423 fprintf(stderr
, _("arp: cannot set entry on line %u of etherfile %s !\n"),
432 /* Print the contents of an ARP request block. */
433 static void arp_disp_2(char *name
, int type
, int arp_flags
, char *hwa
, char *mask
, char *dev
)
435 static int title
= 0;
439 xhw
= get_hwntype(type
);
441 xhw
= get_hwtype(DFLT_HW
);
444 printf(_("Address HWtype HWaddress Flags Mask Iface\n"));
446 /* Setup the flags. */
448 if (arp_flags
& ATF_COM
)
450 if (arp_flags
& ATF_PERM
)
452 if (arp_flags
& ATF_PUBL
)
454 #ifdef HAVE_ATF_MAGIC
455 if (arp_flags
& ATF_MAGIC
)
458 #ifdef HAVE_ATF_DONTPUB
459 if (arp_flags
& ATF_DONTPUB
)
462 if (arp_flags
& ATF_USETRAILERS
)
465 if (!(arp_flags
& ATF_NETMASK
))
468 printf("%-23.23s ", name
);
470 if (!(arp_flags
& ATF_COM
)) {
471 if (arp_flags
& ATF_PUBL
)
472 printf("%-8.8s%-20.20s", "*", _("<from_interface>"));
474 printf("%-8.8s%-20.20s", "", _("(incomplete)"));
476 printf("%-8.8s%-20.20s", xhw
->name
, hwa
);
479 printf("%-6.6s%-15.15s %s\n", flags
, mask
, dev
);
482 /* Print the contents of an ARP request block. */
483 static void arp_disp(char *name
, char *ip
, int type
, int arp_flags
, char *hwa
, char *mask
, char *dev
)
487 xhw
= get_hwntype(type
);
489 xhw
= get_hwtype(DFLT_HW
);
491 printf(_("%s (%s) at "), name
, ip
);
493 if (!(arp_flags
& ATF_COM
)) {
494 if (arp_flags
& ATF_PUBL
)
495 printf("<from_interface> ");
497 printf(_("<incomplete> "));
499 printf("%s [%s] ", hwa
, xhw
->name
);
502 if (arp_flags
& ATF_NETMASK
)
503 printf(_("netmask %s "), mask
);
505 if (arp_flags
& ATF_PERM
)
507 if (arp_flags
& ATF_PUBL
)
509 #ifdef HAVE_ATF_MAGIC
510 if (arp_flags
& ATF_MAGIC
)
513 #ifdef HAVE_ATF_DONTPUB
514 if (arp_flags
& ATF_DONTPUB
)
517 if (arp_flags
& ATF_USETRAILERS
)
520 printf(_("on %s\n"), dev
);
524 /* Display the contents of the ARP cache in the kernel. */
525 static int arp_show(char *name
)
537 int num
, entries
= 0, showed
= 0;
542 /* Resolve the host name. */
543 safe_strncpy(host
, name
, (sizeof host
));
544 if (ap
->input(0, host
, &sa
) < 0) {
548 safe_strncpy(host
, ap
->sprint(&sa
, 1), sizeof(host
));
550 /* Open the PROCps kernel table. */
551 if ((fp
= fopen(_PATH_PROCNET_ARP
, "r")) == NULL
) {
552 perror(_PATH_PROCNET_ARP
);
555 /* Bypass header -- read until newline */
556 if (fgets(line
, sizeof(line
), fp
) != (char *) NULL
) {
559 /* Read the ARP cache entries. */
560 for (; fgets(line
, sizeof(line
), fp
);) {
561 num
= sscanf(line
, "%s 0x%x 0x%x %100s %100s %100s\n",
562 ip
, &type
, &flags
, hwa
, mask
, dev
);
567 /* if the user specified hw-type differs, skip it */
568 if (hw_set
&& (type
!= hw
->type
))
571 /* if the user specified address differs, skip it */
572 if (host
[0] && strcmp(ip
, host
))
575 /* if the user specified device differs, skip it */
576 if (device
[0] && strcmp(dev
, device
))
580 /* This IS ugly but it works -be */
584 if (ap
->input(0, ip
, &sa
) < 0)
587 hostname
= ap
->sprint(&sa
, opt_n
| 0x8000);
588 if (strcmp(hostname
, ip
) == 0)
593 arp_disp_2(hostname
[0] == '?' ? ip
: hostname
, type
, flags
, hwa
, mask
, dev
);
595 arp_disp(hostname
, ip
, type
, flags
, hwa
, mask
, dev
);
599 printf(_("Entries: %d\tSkipped: %d\tFound: %d\n"), entries
, entries
- showed
, showed
);
602 if (host
[0] && !opt_a
)
603 printf(_("%s (%s) -- no entry\n"), name
, host
);
604 else if (hw_set
|| host
[0] || device
[0]) {
605 printf(_("arp: in %d entries no match found.\n"), entries
);
612 static void version(void)
614 fprintf(stderr
, "%s\n%s\n%s\n", Release
, Version
, Features
);
618 static void usage(void)
620 fprintf(stderr
, _("Usage:\n arp [-vn] [<HW>] [-i <if>] [-a] [<hostname>] <-Display ARP cache\n"));
621 fprintf(stderr
, _(" arp [-v] [-i <if>] -d <hostname> [pub][nopub] <-Delete ARP entry\n"));
622 fprintf(stderr
, _(" arp [-vnD] [<HW>] [-i <if>] -f [<filename>] <-Add entry from file\n"));
623 fprintf(stderr
, _(" arp [-v] [<HW>] [-i <if>] -s <hostname> <hwaddr> [temp][nopub] <-Add entry\n"));
624 fprintf(stderr
, _(" arp [-v] [<HW>] [-i <if>] -Ds <hostname> <if> [netmask <nm>] pub <-''-\n\n"));
626 fprintf(stderr
, _(" -a display (all) hosts in alternative (BSD) style\n"));
627 fprintf(stderr
, _(" -s, --set set a new ARP entry\n"));
628 fprintf(stderr
, _(" -d, --delete delete a specified entry\n"));
629 fprintf(stderr
, _(" -v, --verbose be verbose\n"));
630 fprintf(stderr
, _(" -n, --numeric don't resolve names\n"));
631 fprintf(stderr
, _(" -i, --device specify network interface (e.g. eth0)\n"));
632 fprintf(stderr
, _(" -D, --use-device read <hwaddr> from given device\n"));
633 fprintf(stderr
, _(" -A, -p, --protocol specify protocol family\n"));
634 fprintf(stderr
, _(" -f, --file read new entries from file or from /etc/ethers\n\n"));
636 fprintf(stderr
, _(" <HW>=Use '-H <hw>' to specify hardware address type. Default: %s\n"), DFLT_HW
);
637 fprintf(stderr
, _(" List of possible hardware types (which support ARP):\n"));
638 print_hwlist(1); /* 1 = ARPable */
642 int main(int argc
, char **argv
)
645 struct option longopts
[] =
647 {"verbose", 0, 0, 'v'},
648 {"version", 0, 0, 'V'},
650 {"delete", 0, 0, 'd'},
652 {"numeric", 0, 0, 'n'},
654 {"protocol", 1, 0, 'A'},
655 {"hw-type", 1, 0, 'H'},
656 {"device", 1, 0, 'i'},
658 {"use-device", 0, 0, 'D'},
659 {"symbolic", 0, 0, 'N'},
664 setlocale (LC_ALL
, "");
665 bindtextdomain("net-tools", "/usr/share/locale");
666 textdomain("net-tools");
669 /* Initialize variables... */
670 if ((hw
= get_hwtype(DFLT_HW
)) == NULL
) {
671 fprintf(stderr
, _("%s: hardware type not supported!\n"), DFLT_HW
);
674 if ((ap
= get_aftype(DFLT_AF
)) == NULL
) {
675 fprintf(stderr
, _("%s: address family not supported!\n"), DFLT_AF
);
680 /* Fetch the command-line arguments. */
682 while ((i
= getopt_long(argc
, argv
, "A:H:adfp:nsei:t:vh?DNV", longopts
, &lop
)) != EOF
)
710 fprintf(stderr
, _("arp: -N not yet supported.\n"));
718 ap
= get_aftype(optarg
);
720 fprintf(stderr
, _("arp: %s: unknown address family.\n"),
727 hw
= get_hwtype(optarg
);
729 fprintf(stderr
, _("arp: %s: unknown hardware type.\n"),
736 safe_strncpy(device
, optarg
, sizeof(device
));
747 if (ap
->af
!= AF_INET
) {
748 fprintf(stderr
, _("arp: %s: kernel only supports 'inet'.\n"),
753 /* If not hw type specified get default */
755 if ((hw
= get_hwtype(DFLT_HW
)) == NULL
) {
756 fprintf(stderr
, _("%s: hardware type not supported!\n"), DFLT_HW
);
761 fprintf(stderr
, _("arp: %s: hardware type without ARP support.\n"),
765 if ((sockfd
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0) {
769 /* Now see what we have to do here... */
773 what
= arp_show(argv
[optind
]);
776 case 1: /* show an ARP entry in the cache */
777 what
= arp_show(argv
[optind
]);
780 case 2: /* process an EtherFile */
781 what
= arp_file(argv
[optind
] ? argv
[optind
] : "/etc/ethers");
784 case 3: /* delete an ARP entry from the cache */
785 what
= arp_del(&argv
[optind
]);
788 case 4: /* set an ARP entry in the cache */
789 what
= arp_set(&argv
[optind
]);