arp: small fixes for user-supplied device name case
[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                 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(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         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])
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 {
446         char *hw_type;
447         char *protocol;
448
449         /* Initialize variables... */
450         ap = get_aftype(DFLT_AF);
451         if (!ap)
452                 bb_error_msg_and_die("%s: %s not supported", DFLT_AF, "address family");
453
454         getopt32(argc, argv, "A:p:H:t:i:adnDsv", &protocol, &protocol,
455                                  &hw_type, &hw_type, &device);
456         argv += optind;
457         if (option_mask32 & ARP_OPT_A || option_mask32 & ARP_OPT_p) {
458                 ap = get_aftype(protocol);
459                 if (ap == NULL)
460                         bb_error_msg_and_die("%s: unknown %s", protocol, "address family");
461         }
462         if (option_mask32 & ARP_OPT_A || option_mask32 & ARP_OPT_p) {
463                 hw = get_hwtype(hw_type);
464                 if (hw == NULL)
465                         bb_error_msg_and_die("%s: unknown %s", hw_type, "hardware type");
466                 hw_set = 1;
467         }
468         //if (option_mask32 & ARP_OPT_i)... -i
469
470         if (ap->af != AF_INET) {
471                 bb_error_msg_and_die("%s: kernel only supports 'inet'", ap->name);
472         }
473
474         /* If no hw type specified get default */
475         if (!hw) {
476                 hw = get_hwtype(DFLT_HW);
477                 if (!hw)
478                         bb_error_msg_and_die("%s: %s not supported", DFLT_HW, "hardware type");
479         }
480
481         if (hw->alen <= 0) {
482                 bb_error_msg_and_die("%s: %s without ARP support",
483                                                          hw->name, "hardware type");
484         }
485         sockfd = xsocket(AF_INET, SOCK_DGRAM, 0);
486
487         /* Now see what we have to do here... */
488         if (option_mask32 & (ARP_OPT_d|ARP_OPT_s)) {
489                 if (argv[0] == NULL)
490                         bb_error_msg_and_die("need host name");
491                 if (option_mask32 & ARP_OPT_s)
492                         return arp_set(argv);
493                 return arp_del(argv);
494         }
495         //if (option_mask32 & ARP_OPT_a) - default
496         return arp_show(argv[0]);
497 }