Initial revision
[platform/upstream/net-tools.git] / arp.c
1 /*
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.
6  *
7  * NET-TOOLS    A collection of programs that form the base set of the
8  *              NET-3 Networking Distribution for the LINUX operating
9  *              system.
10  *
11  * Version:     arp 1.69 (1996-05-17)
12  *
13  * Maintainer:  Bernd 'eckes' Eckenfels, <net-tools@lina.inka.de>
14  *
15  * Author:      Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
16  *
17  * Changes:
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 
23  *                                      with 1.2.x, too
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
35  *
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.
41  */
42 #include <sys/types.h>
43 #include <sys/socket.h>
44 #include <sys/ioctl.h>
45 #include <net/if.h>
46 #include <net/if_arp.h>
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <errno.h>
50 #include <ctype.h>
51 #include <fcntl.h>
52 #include <string.h>
53 #include <getopt.h>
54 #include <unistd.h>
55 #include "net-support.h"
56 #include "pathnames.h"
57 #include "version.h"
58 #include "config.h"
59 #include "net-locale.h"
60
61 #define DFLT_AF "inet"
62 #define DFLT_HW "ether"
63
64 #define FEATURE_ARP
65 #include "lib/net-features.h"
66
67
68 char *Release = RELEASE,
69      *Version = "arp 1.69 (1996-05-17)";
70
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);
81
82 /* Delete an entry from the ARP cache. */
83 static int
84 arp_del(char **args)
85 {
86   char host[128];
87   struct arpreq req;
88   struct sockaddr sa;
89 #if HAVE_NEW_SIOCSARP
90   struct arpreq_old old_req;
91 #endif
92
93   memset((char *) &req, 0, sizeof(req));
94
95   /* Resolve the host name. */
96   if (*args == NULL) {
97         fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_hostname, "arp: need host name\n"));
98         return(-1);
99   }
100   strcpy(host, *args);
101   if (ap->input(0, host, &sa) < 0) {
102         ap->herror(host);
103         return(-1);
104   }
105
106   /* If a host has more than one address, use the correct one! */
107   memcpy((char *) &req.arp_pa, (char *) &sa, sizeof(struct sockaddr));
108
109   req.arp_flags=0;
110   
111   if (args[1]) {
112         if (strcmp(args[1],"pub")==0)
113                 req.arp_flags|=ATF_PUBL;
114         else
115                 usage();
116   }
117
118 #if HAVE_NEW_SIOCSARP
119   strcpy(req.arp_dev,device);
120   memcpy((char *)&old_req,(char *)&req,sizeof(old_req));
121
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");
130                                 return(-1);
131                         }
132                 } else {
133                         return(0);
134                 }
135         }
136         if (errno == ENXIO) {
137                 printf(NLS_CATGETS(catfd, arpSet, arp_no_arp, 
138                         "No ARP entry for %s\n"), host);
139                 return(-1);
140         }
141         perror("SIOCDARP");
142         return(-1);
143   }
144 #else
145   /* Call the kernel. */
146   if (opt_v)  fprintf(stderr,"arp: old_SIOCDARP()\n");
147   if (ioctl(sockfd, SIOCDARP, &req) < 0) {
148         perror("SIOCDARP");
149         return(-1);
150   }
151 #endif
152
153   return(0);
154 }
155
156 /* Get the hardware address to a specified interface name */
157 static int
158 arp_getdevhw(char *ifname, struct sockaddr *sa, struct hwtype *hw)
159 {
160   struct ifreq ifr;
161   struct hwtype *xhw;
162
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));
166       return(-1);
167   }
168   if (hw && (ifr.ifr_hwaddr.sa_family!=hw->type)) {
169       fprintf(stderr,"arp: protocol type missmatch.\n");
170       return(-1);
171   }
172   memcpy((char *)sa, (char *)&(ifr.ifr_hwaddr), sizeof(struct sockaddr));
173
174   if (opt_v) {
175       if (!(xhw = get_hwntype(ifr.ifr_hwaddr.sa_family)) || (xhw->sprint==0)) {
176         xhw = get_hwntype(-1);
177       }
178       fprintf(stderr, "arp: device `%s' has HW address %s `%s'.\n",ifname, xhw->name, xhw->sprint(&ifr.ifr_hwaddr));
179   }
180   return(0);
181 }
182
183 /* Set an entry in the ARP cache. */
184 static int
185 arp_set(char **args)
186 {
187   char host[128];
188   struct arpreq req;
189 #if HAVE_NEW_SIOCSARP
190   struct arpreq_old old_req;
191 #endif
192   struct sockaddr sa;
193   int flags;
194
195   memset((char *) &req, 0, sizeof(req));
196
197   /* Resolve the host name. */
198   if (*args == NULL) {
199         fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_hostname, "arp: need host name\n"));
200         return(-1);
201   }
202   strcpy(host, *args++);
203   if (ap->input(0, host, &sa) < 0) {
204         ap->herror(host);
205         return(-1);
206   }
207
208   /* If a host has more than one address, use the correct one! */
209   memcpy((char *) &req.arp_pa, (char *) &sa, sizeof(struct sockaddr));
210
211   /* Fetch the hardware address. */
212   if (*args == NULL) {
213         fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_need_hw, "arp: need hardware address\n"));
214         return(-1);
215   }
216
217   if (opt_D) {
218     if (arp_getdevhw(*args++, &req.arp_ha, hw_set?hw:NULL) < 0)
219       return(-1);
220   } else {
221     if (hw->input(*args++, &req.arp_ha) < 0) {
222         fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_invalidhw, "arp: invalid hardware address\n"));
223         return(-1);
224     }
225   }
226   
227   /* Check out any modifiers. */
228   flags = ATF_PERM;
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")) 
235           {
236             if (*++args == NULL) usage();
237             if (strcmp(*args,"255.255.255.255") != 0)
238               {
239                 strcpy(host, *args);
240                 if (ap->input(0, host, &sa) < 0) {
241                         ap->herror(host);
242                         return(-1);
243                 }
244                 memcpy((char *) &req.arp_netmask, (char *) &sa,
245                        sizeof(struct sockaddr));
246                 flags |= ATF_NETMASK;
247               }
248           }
249         args++;
250   }
251
252   if ((flags & ATF_NETMASK) && !(flags & ATF_PUBL))
253     usage();
254
255   /* Fill in the remainder of the request. */
256   req.arp_flags = flags;
257
258 #if HAVE_NEW_SIOCSARP
259   strcpy(req.arp_dev,device);
260   memcpy((char *)&old_req,(char *)&req,sizeof(old_req));
261
262   /* Call the kernel. */
263   if (opt_v)  fprintf(stderr,"arp: SIOCSARP()\n");
264   if (ioctl(sockfd, SIOCSARP, &req) < 0) {
265         if (errno != EINVAL) {
266                 perror("SIOCSARP");
267                 return(-1);
268         }
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");
273                         return(-1);
274                 }
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"));
279                 else
280                         fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_einv_nopub, 
281                                         "Probably destination is on different Interface. See arp(8)\n"));
282                 return(-1);
283         }
284   }
285 #else
286   /* Call the kernel. */
287   if (opt_v)  fprintf(stderr,"arp: old_SIOCSARP()\n");
288   if (ioctl(sockfd, SIOCSARP, &req) < 0) {
289         perror("SIOCSARP");
290         return(-1);
291   }
292 #endif
293
294   return(0);
295 }
296
297
298 /* Process an EtherFile */
299 static int
300 arp_file(char *name)
301 {
302   char buff[1024];
303   char *sp, *args[32];
304   int linenr, argc;
305   FILE *fp;
306
307   if ((fp = fopen(name, "r")) == NULL) {
308         fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_cant_open, "arp: cannot open etherfile %s !\n"), name);
309         return(-1);
310   }
311
312   /* Read the lines in the file. */
313   linenr = 0;
314   while (fgets(buff, sizeof(buff), fp) != (char *)NULL) {
315         linenr++;
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;
319
320         argc = getargs(buff, args);
321         if (argc < 2) {
322                 fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_formaterr, 
323                                             "arp: format error on line %u of etherfile %s !\n"),
324                         linenr, name);
325                 continue;
326         }
327
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"),
331                         linenr, name);
332         }
333   }
334
335   (void) fclose(fp);
336   return(0);
337 }
338
339
340 /* Print the contents of an ARP request block. */
341 static void
342 arp_disp_2(char *ip,int type,int arp_flags,char *hwa,char *mask,char *dev)
343 {
344   static int title = 0;
345   struct hwtype *xhw;
346   struct aftype *xap;
347   char *sp;
348   struct sockaddr sap; 
349   char flags[6];
350   
351   xhw = get_hwntype(type);
352   if (xhw == NULL) 
353     xhw = get_hwtype("ether");
354 /*
355  * xap = get_afntype(req->arp_pa.sa_family);
356  * if (xap == NULL) 
357  */
358   xap = get_aftype("inet");
359     
360   if (title++ == 0) {
361     printf(NLS_CATGETS(catfd, arpSet, arp_address,
362                        "Address\t\t\tHWtype\tHWaddress\t    Flags Mask\t\t  Iface\n"));
363   }
364   /* Setup the flags. */
365   flags[0] = '\0';
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");
371
372   /* This IS ugly but it works -be */
373   if (xap->input(0, ip,&sap) < 0)
374         sp=ip;
375   else
376         sp = xap->sprint(&sap, opt_n);
377   
378   printf("%-23.23s\t%-8.8s", sp, xhw->name);
379   printf("%-20.20s%-6.6s%-15.15s %s\n", hwa, flags,mask,dev);
380 }
381
382
383 /* Display the contents of the ARP cache in the kernel. */
384 static int
385 arp_show(char *name)
386 {
387   char host[100];
388   struct sockaddr sa;
389   char ip[100];
390   char hwa[100];
391   char mask[100];
392   char line[200];
393   char dev[100];
394   int type,flags;
395   FILE *fp;
396   int num,entries=0,showed=0;
397
398   host[0]='\0';
399   
400   if (name != NULL) {
401         /* Resolve the host name. */
402         strcpy(host, name);
403         if (ap->input(0, host, &sa) < 0) {
404                 ap->herror(host);
405                 return(-1);
406         }
407         strcpy(host,ap->sprint(&sa, 1));
408   }
409   
410   /* Open the PROCps kernel table. */
411   if ((fp = fopen(_PATH_PROCNET_ARP, "r")) == NULL) {
412         perror(_PATH_PROCNET_ARP);
413         return(-1);
414   }
415
416   /* Bypass header -- read until newline */
417   if (fgets(line, sizeof(line), fp) != (char *)NULL) {
418         strcpy(mask,"-");
419         strcpy(dev,"-");
420         /* Read the ARP cache entries. */
421         for(;fgets(line,sizeof(line),fp);)
422         {
423                 num=sscanf(line,"%s 0x%x 0x%x %s %s %s\n",
424                                                ip,&type,&flags,hwa,mask,dev);
425                 if(num<4)
426                         break;
427                 
428                 entries++;      
429                 /* if the user specified hw-type differs, skip it */
430                 if (hw_set && (type != hw->type))
431                         continue;
432                         
433                 /* if the user specified address differs, skip it */
434                 if (host[0] && strcmp(ip,host))
435                         continue;
436                 
437                 /* if the user specified device differs, skip it */
438                 if (device[0] && strcmp(dev,device))
439                         continue;
440                 showed++;       
441                 arp_disp_2(ip,type,flags,hwa,mask,dev);
442         }
443   }
444   if (opt_v)
445         printf(NLS_CATGETS(catfd, arpSet, arp_sum,
446                 "Entries: %d\tSkiped: %d\tFound: %d\n"),entries,entries-showed,showed);
447   
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);
451   (void) fclose(fp);
452   return(0);
453 }
454
455 static void
456 version(void)
457 {
458   fprintf(stderr, "%s\n%s\n%s\n",Release,Version,Features);
459   NLS_CATCLOSE(catfd)
460   exit(-1);
461 }
462
463 static void
464 usage(void)
465 {
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"));
478   NLS_CATCLOSE(catfd)
479   exit(-1);
480 }
481
482 int
483 main(int argc, char **argv)
484 {
485   int i, lop, what;
486   struct option longopts[]=
487   {
488         {"verbose",     0,      0,      'v'},
489         {"version",     0,      0,      'V'},
490         {"display",     0,      0,      'a'},
491         {"delete",      0,      0,      'd'},
492         {"file",        0,      0,      'f'},
493         {"numeric",     0,      0,      'n'},
494         {"set",         0,      0,      's'},
495         {"protocol",    1,      0,      'A'},
496         {"hw-type",     1,      0,      'H'},
497         {"device",      0,      0,      'i'},
498         {"help",        0,      0,      'h'},
499         {"use-device",  0,      0,      'D'},
500         {"symbolic",    0,      0,      'N'},
501         {NULL,          0,      0,      0}
502   };    
503              
504 #if NLS
505   setlocale (LC_MESSAGES, "");
506   catfd = catopen ("nettools", MCLoadBySet);
507 #endif
508
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);
513         NLS_CATCLOSE(catfd)
514         return(-1);
515   }
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);
519         NLS_CATCLOSE(catfd)
520         return(-1);
521   }
522   what = -1;
523
524   /* Fetch the command-line arguments. */
525   /* opterr = 0; */
526   while ((i = getopt_long(argc, argv, "A:H:adfp:nsi:t:vh?DNV",longopts, &lop)) != EOF) switch(i) {
527         case 'a':
528                 what = 1;
529                 break;
530
531         case 'd':
532                 what = 3;
533                 break;
534
535         case 'f':
536                 what = 2;
537                 break;
538
539         case 'n':
540                 opt_n = FLAG_NUM;
541                 break;
542         case 'D':
543                 opt_D = 1;
544                 break;
545         case 'N':
546                 opt_N = FLAG_SYM;
547                 fprintf(stderr,"arp: -N not yet supported.\n");
548                 break;
549         case 'A':
550         case 'p':
551                 ap = get_aftype(optarg);
552                 if (ap == NULL) {
553                         fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_unkn_addr,
554                                                     "arp: %s: unknown address family.\n"),
555                                 optarg);
556                         NLS_CATCLOSE(catfd)
557                         exit(-1);
558                 }
559                 break;
560
561         case 's':
562                 what = 4;
563                 break;
564
565         case 'H':
566         case 't':
567                 hw = get_hwtype(optarg);
568                 if (hw == NULL) {
569                         fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_unkn_hw,
570                                                     "arp: %s: unknown hardware type.\n"),
571                                 optarg);
572                         NLS_CATCLOSE(catfd)
573                         exit(-1);
574                 }
575                 hw_set = 1;
576                 break;
577         case 'i':
578                 strncpy(device,optarg,sizeof(device)-1);
579                 device[sizeof(device)-1]='\0';
580                 break;
581
582         case 'v':
583                 opt_v = 1;
584                 break;
585
586         case 'V':
587                 version();
588
589         case '?':
590         case 'h':
591         default:
592                 usage();
593   }
594
595   if (ap->af != AF_INET) {
596         fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_wrong_af,
597                                     "arp: %s: kernel only supports 'inet'.\n"),
598                         ap->name);
599         NLS_CATCLOSE(catfd)
600         exit(-1);
601   }
602   if (hw->alen <= 0) {
603         fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_wrong_hw,
604                                     "arp: %s: hardware type without ARP support.\n"),
605                         hw->name);
606         NLS_CATCLOSE(catfd)
607         exit(-1);
608   }
609   if ((sockfd = socket(AF_INET,SOCK_DGRAM,0)) <0)
610   {
611         perror("socket");
612         NLS_CATCLOSE(catfd)
613         exit(-1);
614   }
615
616   /* Now see what we have to do here... */
617   switch(what) {
618         case 1:         /* show an ARP entry in the cache */
619                 what = arp_show(argv[optind]);
620                 break;
621
622         case 2:         /* process an EtherFile */
623                 what = arp_file(argv[optind]);
624                 break;
625
626         case 3:         /* delete an ARP entry from the cache */
627                 what = arp_del(&argv[optind]);
628                 break;
629
630         case 4:         /* set an ARP entry in the cache */
631                 what = arp_set(&argv[optind]);
632                 break;
633
634         default:
635                 usage();
636   }
637
638   NLS_CATCLOSE(catfd)
639   exit(what);
640 }