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