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