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: arp 1.69 (1996-05-17)
13 * Maintainer: Bernd 'eckes' Eckenfels, <net-tools@lina.inka.de>
15 * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
18 * Alan Cox : modified for NET3
19 * Andrew Tridgell : proxy arp netmasks
20 * Bernd Eckenfels : -n option
21 * Bernd Eckenfels : Use only /proc for display
22 * {1.60} Bernd Eckenfels : new arpcode (-i) for 1.3.42 but works
24 * {1.61} Bernd Eckenfels : more verbose messages
25 * {1.62} Bernd Eckenfels : check -t for hw adresses and try to
26 * explain EINVAL (jeff)
27 *960125 {1.63} Bernd Eckenfels : -a print hardwarename instead of tiltle
28 *960201 {1.64} Bernd Eckenfels : net-features.h support
29 *960203 {1.65} Bernd Eckenfels : "#define" in "#if",
30 * -H|-A additional to -t|-p
31 *960214 {1.66} Bernd Eckenfels : Fix optarg required for -H and -A
32 *960412 {1.67} Bernd Eckenfels : device=""; is default
33 *960514 {1.68} Bernd Eckenfels : -N and -D
34 *960517 {1.69} Bernd Eckenfels : usage() fixed
36 * This program is free software; you can redistribute it
37 * and/or modify it under the terms of the GNU General
38 * Public License as published by the Free Software
39 * Foundation; either version 2 of the License, or (at
40 * your option) any later version.
42 #include <sys/types.h>
43 #include <sys/socket.h>
44 #include <sys/ioctl.h>
46 #include <net/if_arp.h>
55 #include "net-support.h"
56 #include "pathnames.h"
59 #include "net-locale.h"
61 #define DFLT_AF "inet"
62 #define DFLT_HW "ether"
65 #include "lib/net-features.h"
68 char *Release = RELEASE,
69 *Version = "arp 1.69 (1996-05-17)";
71 int opt_n = 0; /* do not resolve addresses */
72 int opt_N = 0; /* use symbolic names */
73 int opt_v = 0; /* debugging output flag */
74 int opt_D = 0; /* HW-address is devicename */
75 struct aftype *ap; /* current address family */
76 struct hwtype *hw; /* current hardware type */
77 int sockfd=0; /* active socket descriptor */
78 int hw_set = 0; /* flag if hw-type was set (-H) */
79 char device[16]=""; /* current device */
80 static void usage(void);
82 /* Delete an entry from the ARP cache. */
90 struct arpreq_old old_req;
93 memset((char *) &req, 0, sizeof(req));
95 /* Resolve the host name. */
97 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_hostname, "arp: need host name\n"));
101 if (ap->input(0, host, &sa) < 0) {
106 /* If a host has more than one address, use the correct one! */
107 memcpy((char *) &req.arp_pa, (char *) &sa, sizeof(struct sockaddr));
112 if (strcmp(args[1],"pub")==0)
113 req.arp_flags|=ATF_PUBL;
118 #if HAVE_NEW_SIOCSARP
119 strcpy(req.arp_dev,device);
120 memcpy((char *)&old_req,(char *)&req,sizeof(old_req));
122 /* Call the kernel. */
123 if (opt_v) fprintf(stderr,"arp: SIOCDARP()\n");
124 if (ioctl(sockfd, SIOCDARP, &req) < 0) {
125 if (errno == EINVAL) {
126 if (opt_v) fprintf(stderr,"arp: OLD_SIOCDARP()\n");
127 if (ioctl(sockfd, OLD_SIOCDARP, &old_req) < 0) {
128 if (errno != ENXIO) {
129 perror("OLD_SIOCSARP");
136 if (errno == ENXIO) {
137 printf(NLS_CATGETS(catfd, arpSet, arp_no_arp,
138 "No ARP entry for %s\n"), host);
145 /* Call the kernel. */
146 if (opt_v) fprintf(stderr,"arp: old_SIOCDARP()\n");
147 if (ioctl(sockfd, SIOCDARP, &req) < 0) {
156 /* Get the hardware address to a specified interface name */
158 arp_getdevhw(char *ifname, struct sockaddr *sa, struct hwtype *hw)
163 strcpy(ifr.ifr_name, ifname);
164 if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) < 0) {
165 fprintf(stderr,"arp: cant get HW-Address for `%s': %s.\n", ifname, strerror(errno));
168 if (hw && (ifr.ifr_hwaddr.sa_family!=hw->type)) {
169 fprintf(stderr,"arp: protocol type missmatch.\n");
172 memcpy((char *)sa, (char *)&(ifr.ifr_hwaddr), sizeof(struct sockaddr));
175 if (!(xhw = get_hwntype(ifr.ifr_hwaddr.sa_family)) || (xhw->sprint==0)) {
176 xhw = get_hwntype(-1);
178 fprintf(stderr, "arp: device `%s' has HW address %s `%s'.\n",ifname, xhw->name, xhw->sprint(&ifr.ifr_hwaddr));
183 /* Set an entry in the ARP cache. */
189 #if HAVE_NEW_SIOCSARP
190 struct arpreq_old old_req;
195 memset((char *) &req, 0, sizeof(req));
197 /* Resolve the host name. */
199 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_hostname, "arp: need host name\n"));
202 strcpy(host, *args++);
203 if (ap->input(0, host, &sa) < 0) {
208 /* If a host has more than one address, use the correct one! */
209 memcpy((char *) &req.arp_pa, (char *) &sa, sizeof(struct sockaddr));
211 /* Fetch the hardware address. */
213 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_need_hw, "arp: need hardware address\n"));
218 if (arp_getdevhw(*args++, &req.arp_ha, hw_set?hw:NULL) < 0)
221 if (hw->input(*args++, &req.arp_ha) < 0) {
222 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_invalidhw, "arp: invalid hardware address\n"));
227 /* Check out any modifiers. */
229 while (*args != NULL) {
230 if (! strcmp(*args, "temp")) flags &= ~ATF_PERM;
231 if (! strcmp(*args, "pub")) flags |= ATF_PUBL;
232 /* if (! strcmp(*args, "rarp")) flags |= ATF_RARP;*/
233 if (! strcmp(*args, "trail")) flags |= ATF_USETRAILERS;
234 if (! strcmp(*args, "netmask"))
236 if (*++args == NULL) usage();
237 if (strcmp(*args,"255.255.255.255") != 0)
240 if (ap->input(0, host, &sa) < 0) {
244 memcpy((char *) &req.arp_netmask, (char *) &sa,
245 sizeof(struct sockaddr));
246 flags |= ATF_NETMASK;
252 if ((flags & ATF_NETMASK) && !(flags & ATF_PUBL))
255 /* Fill in the remainder of the request. */
256 req.arp_flags = flags;
258 #if HAVE_NEW_SIOCSARP
259 strcpy(req.arp_dev,device);
260 memcpy((char *)&old_req,(char *)&req,sizeof(old_req));
262 /* Call the kernel. */
263 if (opt_v) fprintf(stderr,"arp: SIOCSARP()\n");
264 if (ioctl(sockfd, SIOCSARP, &req) < 0) {
265 if (errno != EINVAL) {
269 if (opt_v) fprintf(stderr,"arp: OLD_SIOCSARP()\n");
270 if (ioctl(sockfd, OLD_SIOCSARP, &old_req) < 0) {
271 if (errno != EINVAL) {
272 perror("OLD_SIOCSARP");
275 perror("SIOCSARP and OLD_SIOCSARP");
276 if (flags & ATF_PUBL)
277 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_einv_pub,
278 "Probably destination is reached via ARP Interface. See arp(8)\n"));
280 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_einv_nopub,
281 "Probably destination is on different Interface. See arp(8)\n"));
286 /* Call the kernel. */
287 if (opt_v) fprintf(stderr,"arp: old_SIOCSARP()\n");
288 if (ioctl(sockfd, SIOCSARP, &req) < 0) {
298 /* Process an EtherFile */
307 if ((fp = fopen(name, "r")) == NULL) {
308 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_cant_open, "arp: cannot open etherfile %s !\n"), name);
312 /* Read the lines in the file. */
314 while (fgets(buff, sizeof(buff), fp) != (char *)NULL) {
316 if (opt_v == 1) fprintf(stderr, ">> %s", buff);
317 if ((sp = strchr(buff, '\n')) != (char *)NULL) *sp = '\0';
318 if (buff[0] == '#' || buff[0] == '\0') continue;
320 argc = getargs(buff, args);
322 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_formaterr,
323 "arp: format error on line %u of etherfile %s !\n"),
328 if (arp_set(args) != 0) {
329 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_cant_set,
330 "arp: cannot set entry on line %u of etherfile %s !\n"),
340 /* Print the contents of an ARP request block. */
342 arp_disp_2(char *ip,int type,int arp_flags,char *hwa,char *mask,char *dev)
344 static int title = 0;
351 xhw = get_hwntype(type);
353 xhw = get_hwtype("ether");
355 * xap = get_afntype(req->arp_pa.sa_family);
358 xap = get_aftype("inet");
361 printf(NLS_CATGETS(catfd, arpSet, arp_address,
362 "Address\t\t\tHWtype\tHWaddress\t Flags Mask\t\t Iface\n"));
364 /* Setup the flags. */
366 if (arp_flags & ATF_COM) strcat(flags, "C");
367 if (arp_flags & ATF_PERM) strcat(flags, "M");
368 if (arp_flags & ATF_PUBL) strcat(flags, "P");
369 /* if (arp_flags & ATF_RARP) strcat(flags, "R");*/
370 if (arp_flags & ATF_USETRAILERS) strcat(flags, "T");
372 /* This IS ugly but it works -be */
373 if (xap->input(0, ip,&sap) < 0)
376 sp = xap->sprint(&sap, opt_n);
378 printf("%-23.23s\t%-8.8s", sp, xhw->name);
379 printf("%-20.20s%-6.6s%-15.15s %s\n", hwa, flags,mask,dev);
383 /* Display the contents of the ARP cache in the kernel. */
396 int num,entries=0,showed=0;
401 /* Resolve the host name. */
403 if (ap->input(0, host, &sa) < 0) {
407 strcpy(host,ap->sprint(&sa, 1));
410 /* Open the PROCps kernel table. */
411 if ((fp = fopen(_PATH_PROCNET_ARP, "r")) == NULL) {
412 perror(_PATH_PROCNET_ARP);
416 /* Bypass header -- read until newline */
417 if (fgets(line, sizeof(line), fp) != (char *)NULL) {
420 /* Read the ARP cache entries. */
421 for(;fgets(line,sizeof(line),fp);)
423 num=sscanf(line,"%s 0x%x 0x%x %s %s %s\n",
424 ip,&type,&flags,hwa,mask,dev);
429 /* if the user specified hw-type differs, skip it */
430 if (hw_set && (type != hw->type))
433 /* if the user specified address differs, skip it */
434 if (host[0] && strcmp(ip,host))
437 /* if the user specified device differs, skip it */
438 if (device[0] && strcmp(dev,device))
441 arp_disp_2(ip,type,flags,hwa,mask,dev);
445 printf(NLS_CATGETS(catfd, arpSet, arp_sum,
446 "Entries: %d\tSkiped: %d\tFound: %d\n"),entries,entries-showed,showed);
448 if (!showed && (hw_set || host[0] || device[0]))
449 printf(NLS_CATGETS(catfd, arpSet, arp_none,
450 "arp: in %d entries no match found.\n"),entries);
458 fprintf(stderr, "%s\n%s\n%s\n",Release,Version,Features);
466 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_usage1,
467 "Usage: arp [-vn] [-H type] [-i if] -a [hostname]\n"));
468 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_usage2,
469 " arp [-v] [-i if] -d hostname [pub]\n"));
470 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_usage3,
471 " arp [-v] [-H type] [-i if] -s hostname hw_addr [temp]\n"));
472 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_usage4,
473 " arp [-v] [-H type] [-i if] -s hostname hw_addr [netmask nm] pub\n"));
474 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_usage5,
475 " arp [-v] [-H type] [-i if] -Ds hostname if [netmask nm] pub\n"));
476 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_usage6,
477 " arp [-vnD] [-H type] [-i if] -f filename\n"));
483 main(int argc, char **argv)
486 struct option longopts[]=
488 {"verbose", 0, 0, 'v'},
489 {"version", 0, 0, 'V'},
490 {"display", 0, 0, 'a'},
491 {"delete", 0, 0, 'd'},
493 {"numeric", 0, 0, 'n'},
495 {"protocol", 1, 0, 'A'},
496 {"hw-type", 1, 0, 'H'},
497 {"device", 0, 0, 'i'},
499 {"use-device", 0, 0, 'D'},
500 {"symbolic", 0, 0, 'N'},
505 setlocale (LC_MESSAGES, "");
506 catfd = catopen ("nettools", MCLoadBySet);
509 /* Initialize variables... */
510 if ((hw = get_hwtype(DFLT_HW)) == NULL) {
511 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_hw_not_supp,
512 "%s: hardware type not supported!\n"), DFLT_HW);
516 if ((ap = get_aftype(DFLT_AF)) == NULL) {
517 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_fam_not_supp,
518 "%s: address family not supported!\n"), DFLT_AF);
524 /* Fetch the command-line arguments. */
526 while ((i = getopt_long(argc, argv, "A:H:adfp:nsi:t:vh?DNV",longopts, &lop)) != EOF) switch(i) {
547 fprintf(stderr,"arp: -N not yet supported.\n");
551 ap = get_aftype(optarg);
553 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_unkn_addr,
554 "arp: %s: unknown address family.\n"),
567 hw = get_hwtype(optarg);
569 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_unkn_hw,
570 "arp: %s: unknown hardware type.\n"),
578 strncpy(device,optarg,sizeof(device)-1);
579 device[sizeof(device)-1]='\0';
595 if (ap->af != AF_INET) {
596 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_wrong_af,
597 "arp: %s: kernel only supports 'inet'.\n"),
603 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_wrong_hw,
604 "arp: %s: hardware type without ARP support.\n"),
609 if ((sockfd = socket(AF_INET,SOCK_DGRAM,0)) <0)
616 /* Now see what we have to do here... */
618 case 1: /* show an ARP entry in the cache */
619 what = arp_show(argv[optind]);
622 case 2: /* process an EtherFile */
623 what = arp_file(argv[optind]);
626 case 3: /* delete an ARP entry from the cache */
627 what = arp_del(&argv[optind]);
630 case 4: /* set an ARP entry in the cache */
631 what = arp_set(&argv[optind]);