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