I *always* forgotting svn add
[oweals/busybox.git] / networking / arp.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * arp.c - Manipulate the system ARP cache
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version
8  * 2 of the License, or (at your option) any later version.
9  *
10  * Author: Fred N. van Kempen, <waltje at uwalt.nl.mugnet.org>
11  * Busybox port: Paul van Gool <pvangool at mimotech.com>
12  *
13  * modified for getopt32 by Arne Bernin <arne [at] alamut.de>
14  */
15
16 #include "busybox.h"
17 #include "inet_common.h"
18
19 #include <arpa/inet.h>
20 #include <net/if.h>
21 #include <net/if_arp.h>
22 #include <netinet/ether.h>
23 #include <netpacket/packet.h>
24
25 #define DEBUG 0
26
27 #define DFLT_AF "inet"
28 #define DFLT_HW "ether"
29
30 #define _PATH_PROCNET_ARP "/proc/net/arp"
31
32 #define ARP_OPT_A (0x1)
33 #define ARP_OPT_p (0x2)
34 #define ARP_OPT_H (0x4)
35 #define ARP_OPT_t (0x8)
36 #define ARP_OPT_i (0x10)
37 #define ARP_OPT_a (0x20)
38 #define ARP_OPT_d (0x40)
39 #define ARP_OPT_n (0x80)        /* do not resolve addresses     */
40 #define ARP_OPT_D (0x100)       /* HW-address is devicename     */
41 #define ARP_OPT_s (0x200)
42 #define ARP_OPT_v (0x400 * DEBUG)       /* debugging output flag        */
43
44
45 static const struct aftype *ap; /* current address family       */
46 static const struct hwtype *hw; /* current hardware type        */
47 static int sockfd;              /* active socket descriptor     */
48 static smallint hw_set;         /* flag if hw-type was set (-H) */
49 static char *device = "";       /* current device               */
50
51 static const char *const options[] = {
52         "pub",
53         "priv",
54         "temp",
55         "trail",
56         "dontpub",
57         "auto",
58         "dev",
59         "netmask",
60         NULL
61 };
62
63 /* Delete an entry from the ARP cache. */
64 /* Called only from main, once */
65 static int arp_del(char **args)
66 {
67         char host[128];
68         struct arpreq req;
69         struct sockaddr sa;
70         int flags = 0;
71         int err;
72
73         memset(&req, 0, sizeof(req));
74
75         /* Resolve the host name. */
76         safe_strncpy(host, *args, 128);
77         if (ap->input(0, host, &sa) < 0) {
78                 bb_herror_msg_and_die("%s", host);
79         }
80
81         /* If a host has more than one address, use the correct one! */
82         memcpy(&req.arp_pa, &sa, sizeof(struct sockaddr));
83
84         if (hw_set)
85                 req.arp_ha.sa_family = hw->type;
86
87         req.arp_flags = ATF_PERM;
88         args++;
89         while (*args != NULL) {
90                 if (option_mask32 & ARP_OPT_v)
91                         bb_error_msg("args=%s", *args);
92
93                 switch (index_in_str_array(options, *args)) {
94                 case 0: // "pub"
95                         flags |= 1;
96                         args++;
97                         break;
98                 case 1: // "priv"
99                         flags |= 2;
100                         args++;
101                         break;
102                 case 2: // "temp"
103                         req.arp_flags &= ~ATF_PERM;
104                         args++;
105                         break;
106                 case 3: // "trail"
107                         req.arp_flags |= ATF_USETRAILERS;
108                         args++;
109                         break;
110                 case 4: // "dontpub"
111 #ifdef HAVE_ATF_DONTPUB
112                         req.arp_flags |= ATF_DONTPUB;
113 #else
114                         bb_error_msg("feature ATF_DONTPUB is not supported");
115 #endif
116                         args++;
117                         break;
118                 case 5: // "auto"
119 #ifdef HAVE_ATF_MAGIC
120                         req.arp_flags |= ATF_MAGIC;
121 #else
122                         bb_error_msg("feature ATF_MAGIC is not supported");
123 #endif
124                         args++;
125                         break;
126                 case 6: // "dev"
127                         if (*++args == NULL)
128                                 bb_show_usage();
129                         device = *args;
130                         args++;
131                         break;
132                 case 7: // "netmask"
133                         if (*++args == NULL)
134                                 bb_show_usage();
135                         if (strcmp(*args, "255.255.255.255") != 0) {
136                                 safe_strncpy(host, *args, 128);
137                                 if (ap->input(0, host, &sa) < 0) {
138                                         bb_herror_msg("%s", host);
139                                         return -1;
140                                 }
141                                 memcpy(&req.arp_netmask, &sa, sizeof(struct sockaddr));
142                                 req.arp_flags |= ATF_NETMASK;
143                         }
144                         args++;
145                         break;
146                 default:
147                         bb_show_usage();
148                         break;
149                 }
150         }
151         if (flags == 0)
152                 flags = 3;
153
154         strncpy(req.arp_dev, device, sizeof(req.arp_dev));
155
156         err = -1;
157
158         /* Call the kernel. */
159         if (flags & 2) {
160                 if (option_mask32 & ARP_OPT_v)
161                         bb_error_msg("SIOCDARP(nopub)");
162                 err = ioctl(sockfd, SIOCDARP, &req);
163                 if (err < 0) {
164                         if (errno == ENXIO) {
165                                 if (flags & 1)
166                                         goto nopub;
167                                 printf("No ARP entry for %s\n", host);
168                                 return -1;
169                         }
170                         bb_perror_msg_and_die("SIOCDARP(priv)");
171                 }
172         }
173         if ((flags & 1) && err) {
174  nopub:
175                 req.arp_flags |= ATF_PUBL;
176                 if (option_mask32 & ARP_OPT_v)
177                         bb_error_msg("SIOCDARP(pub)");
178                 if (ioctl(sockfd, SIOCDARP, &req) < 0) {
179                         if (errno == ENXIO) {
180                                 printf("No ARP entry for %s\n", host);
181                                 return -1;
182                         }
183                         bb_perror_msg_and_die("SIOCDARP(pub)");
184                 }
185         }
186         return 0;
187 }
188
189 /* Get the hardware address to a specified interface name */
190 static void
191 arp_getdevhw(char *ifname, struct sockaddr *sa, const struct hwtype *hwt)
192 {
193         struct ifreq ifr;
194         const struct hwtype *xhw;
195
196         strcpy(ifr.ifr_name, ifname);
197         if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) < 0) {
198                 bb_perror_msg_and_die("cant get HW-Address for '%s'", ifname);
199         }
200         if (hwt && (ifr.ifr_hwaddr.sa_family != hw->type)) {
201                 bb_error_msg_and_die("protocol type mismatch");
202         }
203         memcpy(sa, &(ifr.ifr_hwaddr), sizeof(struct sockaddr));
204
205         if (option_mask32 & ARP_OPT_v) {
206                 xhw = get_hwntype(ifr.ifr_hwaddr.sa_family);
207                 if (!xhw || !xhw->print) {
208                         xhw = get_hwntype(-1);
209                 }
210                 bb_error_msg("device '%s' has HW address %s '%s'",
211                                          ifname, xhw->name,
212                                          xhw->print((char *) &ifr.ifr_hwaddr.sa_data));
213         }
214 }
215
216 /* Set an entry in the ARP cache. */
217 /* Called only from main, once */
218 static int arp_set(char **args)
219 {
220         char host[128];
221         struct arpreq req;
222         struct sockaddr sa;
223         int flags;
224         
225         memset(&req, 0, sizeof(req));
226
227         safe_strncpy(host, *args++, 128);
228         if (ap->input(0, host, &sa) < 0) {
229                 bb_herror_msg_and_die("%s", host);
230         }
231         /* If a host has more than one address, use the correct one! */
232         memcpy(&req.arp_pa, &sa, sizeof(struct sockaddr));
233
234         /* Fetch the hardware address. */
235         if (*args == NULL) {
236                 bb_error_msg_and_die("need hardware address");
237         }
238         if (option_mask32 & ARP_OPT_D) {
239                 arp_getdevhw(*args++, &req.arp_ha, hw_set ? hw : NULL);
240         } else {
241                 if (hw->input(*args++, &req.arp_ha) < 0) {
242                         bb_error_msg_and_die("invalid hardware address");
243                 }
244         }
245
246         /* Check out any modifiers. */
247         flags = ATF_PERM | ATF_COM;
248         while (*args != NULL) {
249                 switch (index_in_str_array(options, *args)) {
250                 case 0: // "pub"
251                         flags |= ATF_PUBL;
252                         args++;
253                         break;
254                 case 1: // "priv"
255                         flags &= ~ATF_PUBL;
256                         args++;
257                         break;
258                 case 2: // "temp"
259                         flags &= ~ATF_PERM;
260                         args++;
261                         break;
262                 case 3: // "trail"
263                         flags |= ATF_USETRAILERS;
264                         args++;
265                         break;
266                 case 4: // "dontpub"
267 #ifdef HAVE_ATF_DONTPUB
268                         flags |= ATF_DONTPUB;
269 #else
270                         bb_error_msg("feature ATF_DONTPUB is not supported");
271 #endif
272                         args++;
273                         break;
274                 case 5: // "auto"
275 #ifdef HAVE_ATF_MAGIC
276                         flags |= ATF_MAGIC;
277 #else
278                         bb_error_msg("feature ATF_MAGIC is not supported");
279 #endif
280                         args++;
281                         break;
282                 case 6: // "dev"
283                         if (*++args == NULL)
284                                 bb_show_usage();
285                         device = *args;
286                         args++;
287                         break;
288                 case 7: // "netmask"
289                         if (*++args == NULL)
290                                 bb_show_usage();
291                         if (strcmp(*args, "255.255.255.255") != 0) {
292                                 safe_strncpy(host, *args++, 128);
293                                 if (ap->input(0, host, &sa) < 0) {
294                                         bb_herror_msg_and_die("%s", host);
295                                 }
296                                 memcpy(&req.arp_netmask, &sa, sizeof(struct sockaddr));
297                                 flags |= ATF_NETMASK;
298                         }
299                         args++;
300                         break;
301                 default:
302                         bb_show_usage();
303                         break;
304                 }
305         }
306
307         /* Fill in the remainder of the request. */
308         req.arp_flags = flags;
309
310         strncpy(req.arp_dev, device, sizeof(req.arp_dev));
311
312         /* Call the kernel. */
313         if (option_mask32 & ARP_OPT_v)
314                 bb_error_msg("SIOCSARP()");
315         if (ioctl(sockfd, SIOCSARP, &req) < 0) {
316                 bb_perror_msg_and_die("SIOCSARP");
317         }
318         return 0;
319 }
320
321
322 /* Print the contents of an ARP request block. */
323 static void
324 arp_disp(char *name, char *ip, int type, int arp_flags,
325                  char *hwa, char *mask, char *dev)
326 {
327         const struct hwtype *xhw;
328
329         xhw = get_hwntype(type);
330         if (xhw == NULL)
331                 xhw = get_hwtype(DFLT_HW);
332
333         printf("%s (%s) at ", name, ip);
334
335         if (!(arp_flags & ATF_COM)) {
336                 if (arp_flags & ATF_PUBL)
337                         printf("* ");
338                 else
339                         printf("<incomplete> ");
340         } else {
341                 printf("%s [%s] ", hwa, xhw->name);
342         }
343
344         if (arp_flags & ATF_NETMASK)
345                 printf("netmask %s ", mask);
346
347         if (arp_flags & ATF_PERM)
348                 printf("PERM ");
349         if (arp_flags & ATF_PUBL)
350                 printf("PUP ");
351 #ifdef HAVE_ATF_MAGIC
352         if (arp_flags & ATF_MAGIC)
353                 printf("AUTO ");
354 #endif
355 #ifdef HAVE_ATF_DONTPUB
356         if (arp_flags & ATF_DONTPUB)
357                 printf("DONTPUB ");
358 #endif
359         if (arp_flags & ATF_USETRAILERS)
360                 printf("TRAIL ");
361
362         printf("on %s\n", dev);
363 }
364
365 /* Display the contents of the ARP cache in the kernel. */
366 /* Called only from main, once */
367 static int arp_show(char *name)
368 {
369         char host[100];
370         struct sockaddr sa;
371         char ip[100];
372         char hwa[100];
373         char mask[100];
374         char line[200];
375         char dev[100];
376         int type, flags;
377         FILE *fp;
378         char *hostname;
379         int num, entries = 0, shown = 0;
380
381         host[0] = '\0';
382
383         if (name != NULL) {
384                 /* Resolve the host name. */
385                 safe_strncpy(host, name, (sizeof host));
386                 if (ap->input(0, host, &sa) < 0) {
387                         bb_herror_msg_and_die("%s", host);
388                 }
389                 safe_strncpy(host, ap->sprint(&sa, 1), sizeof(host));
390         }
391         /* Open the PROCps kernel table. */
392         fp = xfopen(_PATH_PROCNET_ARP, "r");
393         /* Bypass header -- read until newline */
394         if (fgets(line, sizeof(line), fp) != (char *) NULL) {
395                 mask[0] = '-'; mask[1] = '\0';
396                 dev[0] = '-'; dev[1] = '\0';
397                 /* Read the ARP cache entries. */
398                 for (; fgets(line, sizeof(line), fp);) {
399                         num = sscanf(line, "%s 0x%x 0x%x %100s %100s %100s\n",
400                                                  ip, &type, &flags, hwa, mask, dev);
401                         if (num < 4)
402                                 break;
403
404                         entries++;
405                         /* if the user specified hw-type differs, skip it */
406                         if (hw_set && (type != hw->type))
407                                 continue;
408
409                         /* if the user specified address differs, skip it */
410                         if (host[0] && strcmp(ip, host) != 0)
411                                 continue;
412
413                         /* if the user specified device differs, skip it */
414                         if (device && strcmp(dev, device) != 0)
415                                 continue;
416
417                         shown++;
418                         /* This IS ugly but it works -be */
419                         if (option_mask32 & ARP_OPT_n)
420                                 hostname = "?";
421                         else {
422                                 if (ap->input(0, ip, &sa) < 0)
423                                         hostname = ip;
424                                 else
425                                         hostname = ap->sprint(&sa, (option_mask32 & ARP_OPT_n) | 0x8000);
426                                 if (strcmp(hostname, ip) == 0)
427                                         hostname = "?";
428                         }
429
430                         arp_disp(hostname, ip, type, flags, hwa, mask, dev);
431                 }
432         }
433         if (option_mask32 & ARP_OPT_v)
434                 printf("Entries: %d\tSkipped: %d\tFound: %d\n",
435                            entries, entries - shown, shown);
436
437         if (!shown) {
438                 if (hw_set || host[0] || device)
439                         printf("No match found in %d entries\n", entries);
440         }
441
442         fclose(fp);
443
444         return 0;
445 }
446
447 int arp_main(int argc, char **argv)
448 {
449         char *hw_type;
450         char *protocol;
451
452         /* Initialize variables... */
453         ap = get_aftype(DFLT_AF);
454         if (!ap)
455                 bb_error_msg_and_die("%s: %s not supported", DFLT_AF, "address family");
456
457         getopt32(argc, argv, "A:p:H:t:i:adnDsv", &protocol, &protocol,
458                                  &hw_type, &hw_type, &device);
459         argv += optind;
460         if (option_mask32 & ARP_OPT_A || option_mask32 & ARP_OPT_p) {
461                 ap = get_aftype(protocol);
462                 if (ap == NULL)
463                         bb_error_msg_and_die("%s: unknown %s", protocol, "address family");
464         }
465         if (option_mask32 & ARP_OPT_A || option_mask32 & ARP_OPT_p) {
466                 hw = get_hwtype(hw_type);
467                 if (hw == NULL)
468                         bb_error_msg_and_die("%s: unknown %s", hw_type, "hardware type");
469                 hw_set = 1;
470         }
471         //if (option_mask32 & ARP_OPT_i)... -i
472
473         if (ap->af != AF_INET) {
474                 bb_error_msg_and_die("%s: kernel only supports 'inet'", ap->name);
475         }
476
477         /* If no hw type specified get default */
478         if (!hw) {
479                 hw = get_hwtype(DFLT_HW);
480                 if (!hw)
481                         bb_error_msg_and_die("%s: %s not supported", DFLT_HW, "hardware type");
482         }
483
484         if (hw->alen <= 0) {
485                 bb_error_msg_and_die("%s: %s without ARP support",
486                                                          hw->name, "hardware type");
487         }
488         sockfd = xsocket(AF_INET, SOCK_DGRAM, 0);
489
490         /* Now see what we have to do here... */
491         if (option_mask32 & (ARP_OPT_d|ARP_OPT_s)) {
492                 if (argv[0] == NULL)
493                         bb_error_msg_and_die("need host name");
494                 if (option_mask32 & ARP_OPT_s)
495                         return arp_set(argv);
496                 return arp_del(argv);
497         }
498         //if (option_mask32 & ARP_OPT_a) - default
499         return arp_show(argv[0]);
500 }