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