1 /*
2 * Copyright 2006-2008, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 */
8
9
10 #include <arp_control.h>
11
12 #include <generic_syscall_defs.h>
13 #include <syscalls.h>
14
15 #include <arpa/inet.h>
16 #include <netdb.h>
17 #include <netinet/in.h>
18
19 #include <ctype.h>
20 #include <errno.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25
26
27 extern const char* __progname;
28 const char* kProgramName = __progname;
29
30
31 enum modes {
32 ARP_LIST = 0,
33 ARP_DELETE,
34 ARP_SET,
35 ARP_SET_FROM_FILE
36 };
37
38
39 static const char *
ethernet_address_to_string(uint8 * address)40 ethernet_address_to_string(uint8 *address)
41 {
42 static char string[64];
43 snprintf(string, sizeof(string), "%02x:%02x:%02x:%02x:%02x:%02x",
44 address[0], address[1], address[2], address[3], address[4], address[5]);
45 return string;
46 }
47
48
49 static bool
parse_ethernet_address(const char * string,uint8 * address)50 parse_ethernet_address(const char *string, uint8 *address)
51 {
52 return sscanf(string, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &address[0], &address[1],
53 &address[2], &address[3], &address[4], &address[5]) == 6;
54 }
55
56
57 static bool
parse_inet_address(const char * string,sockaddr_in & address)58 parse_inet_address(const char* string, sockaddr_in& address)
59 {
60 in_addr inetAddress;
61 if (inet_aton(string, &inetAddress) != 1)
62 return false;
63
64 address.sin_family = AF_INET;
65 address.sin_len = sizeof(struct sockaddr_in);
66 address.sin_port = 0;
67 address.sin_addr = inetAddress;
68 memset(&address.sin_zero[0], 0, sizeof(address.sin_zero));
69
70 return true;
71 }
72
73
74 static void
check_for_arp_syscall(void)75 check_for_arp_syscall(void)
76 {
77 uint32 version = 0;
78 status_t status = _kern_generic_syscall(ARP_SYSCALLS, B_SYSCALL_INFO,
79 &version, sizeof(version));
80 if (status != B_OK) {
81 fprintf(stderr, "\"ARP\" module not available.\n");
82 exit(1);
83 }
84 }
85
86
87 static void
usage(int status)88 usage(int status)
89 {
90 printf("usage: %s [<command>] [<hostname>] [<ethernet-address>] [temp] [pub]\n"
91 " %s -f <filename>\n"
92 "Where <command> can be the one of:\n"
93 " -s - sets an entry in the ARP cache\n"
94 " -d - deletes the specified ARP cache entry\n"
95 " -a - list all entries [default]\n"
96 " -f - read ARP entries from file\n"
97 " -F - flush all temporary ARP entries\n"
98 "The ethernet address is specified by six hex bytes separated by colons.\n"
99 "When setting a new ARP cache entry, \"temp\" creates a temporary entry\n"
100 "that will be removed after a timeout, and \"pub\" will cause ARP to\n"
101 "answer to ARP requests for this entry.\n\n"
102 "Example:\n"
103 "\t%s -s 192.168.0.2 00:09:ab:cd:ef:12 pub\n",
104 kProgramName, kProgramName, kProgramName);
105
106 exit(status);
107 }
108
109
110 static bool
set_flags(uint32 & flags,const char * argument)111 set_flags(uint32 &flags, const char *argument)
112 {
113 if (!strcmp(argument, "temp") || !strcmp(argument, "temporary"))
114 flags &= ~ARP_FLAG_PERMANENT;
115 else if (!strcmp(argument, "pub") || !strcmp(argument, "publish"))
116 flags |= ARP_FLAG_PUBLISH;
117 else if (!strcmp(argument, "reject"))
118 flags |= ARP_FLAG_REJECT;
119 else
120 return false;
121
122 return true;
123 }
124
125
126 static char *
next_argument(char ** _line)127 next_argument(char **_line)
128 {
129 char *start = *_line;
130 if (!start[0])
131 return NULL;
132
133 char *end = start;
134 while (end[0] && !isspace(end[0])) {
135 end++;
136 }
137
138 if (end[0]) {
139 // skip all whitespace until next argument (or end of line)
140 end[0] = '\0';
141 end++;
142
143 while (end[0] && isspace(end[0])) {
144 end++;
145 }
146 }
147
148 *_line = end;
149 return start;
150 }
151
152
153 // #pragma mark -
154
155
156 static void
list_entry(arp_control & control)157 list_entry(arp_control &control)
158 {
159 in_addr address;
160 address.s_addr = control.address;
161 printf("%15s %s", inet_ntoa(address),
162 ethernet_address_to_string(control.ethernet_address));
163
164 if (control.flags != 0) {
165 const struct {
166 int value;
167 const char *name;
168 } kFlags[] = {
169 {ARP_FLAG_PERMANENT, "permanent"},
170 {ARP_FLAG_LOCAL, "local"},
171 {ARP_FLAG_PUBLISH, "publish"},
172 {ARP_FLAG_REJECT, "reject"},
173 {ARP_FLAG_VALID, "valid"},
174 };
175 bool first = true;
176
177 for (uint32 i = 0; i < sizeof(kFlags) / sizeof(kFlags[0]); i++) {
178 if ((control.flags & kFlags[i].value) != 0) {
179 if (first) {
180 printf(" ");
181 first = false;
182 } else
183 putchar(' ');
184 fputs(kFlags[i].name, stdout);
185 }
186 }
187 }
188
189 putchar('\n');
190 }
191
192
193 static void
list_entries(sockaddr_in * address)194 list_entries(sockaddr_in *address)
195 {
196 arp_control control;
197 status_t status;
198
199 if (address != NULL) {
200 control.address = address->sin_addr.s_addr;
201 status = _kern_generic_syscall(ARP_SYSCALLS, ARP_GET_ENTRY,
202 &control, sizeof(arp_control));
203 if (status != B_OK) {
204 fprintf(stderr, "%s: ARP entry does not exist.\n", kProgramName);
205 exit(1);
206 }
207
208 list_entry(control);
209 return;
210 }
211
212 control.cookie = 0;
213 while (_kern_generic_syscall(ARP_SYSCALLS, ARP_GET_ENTRIES,
214 &control, sizeof(arp_control)) == B_OK) {
215 list_entry(control);
216 }
217 }
218
219
220 static void
delete_entry(sockaddr_in * address)221 delete_entry(sockaddr_in *address)
222 {
223 arp_control control;
224 control.address = address->sin_addr.s_addr;
225
226 status_t status = _kern_generic_syscall(ARP_SYSCALLS, ARP_DELETE_ENTRY,
227 &control, sizeof(arp_control));
228 if (status != B_OK) {
229 fprintf(stderr, "%s: Could not delete ARP entry: %s\n",
230 kProgramName, strerror(status));
231 exit(1);
232 }
233 }
234
235
236 static status_t
set_entry(sockaddr_in * address,uint8 * ethernetAddress,uint32 flags)237 set_entry(sockaddr_in *address, uint8 *ethernetAddress, uint32 flags)
238 {
239 arp_control control;
240 control.address = address->sin_addr.s_addr;
241 memcpy(control.ethernet_address, ethernetAddress, ETHER_ADDRESS_LENGTH);
242 control.flags = flags;
243
244 return _kern_generic_syscall(ARP_SYSCALLS, ARP_SET_ENTRY,
245 &control, sizeof(arp_control));
246 }
247
248
249 static int
set_entries_from_file(const char * filename)250 set_entries_from_file(const char *filename)
251 {
252 FILE *file = fopen(filename, "r");
253 if (file == NULL) {
254 fprintf(stderr, "%s: Could not open file: %s\n", kProgramName, strerror(errno));
255 return 1;
256 }
257
258 int32 counter = 0;
259 char line[4096];
260
261 while (fgets(line, sizeof(line), file) != NULL) {
262 counter++;
263
264 // comments and empty lines are allowed
265 if (line[0] == '#' || !strcmp(line, "\n"))
266 continue;
267
268 // parse hostname
269
270 char *rest = line;
271 const char *argument = next_argument(&rest);
272 if (argument == NULL) {
273 fprintf(stderr, "%s: Line %" B_PRId32 " is invalid (missing hostname).\n",
274 kProgramName, counter);
275 continue;
276 }
277
278 sockaddr_in address;
279 if (!parse_inet_address(argument, address)) {
280 // get host by name
281 struct hostent *host = gethostbyname(argument);
282 if (host == NULL) {
283 fprintf(stderr, "%s: Line %" B_PRId32 ", host \"%s\" is not known in the IPv4 domain: %s\n",
284 kProgramName, counter, argument, strerror(errno));
285 continue;
286 }
287 if (host->h_addrtype != AF_INET) {
288 fprintf(stderr, "%s: Line %" B_PRId32 ", host \"%s\" is not known in the IPv4 domain.\n",
289 kProgramName, counter, argument);
290 continue;
291 }
292
293 address.sin_family = AF_INET;
294 address.sin_len = sizeof(sockaddr_in);
295 address.sin_addr.s_addr = *(in_addr_t *)host->h_addr;
296 }
297
298 // parse ethernet MAC address
299
300 argument = next_argument(&rest);
301 if (argument == NULL) {
302 fprintf(stderr, "%s: Line %" B_PRId32 " is invalid (missing ethernet address).\n",
303 kProgramName, counter);
304 continue;
305 }
306
307 uint8 ethernetAddress[6];
308 if (!parse_ethernet_address(argument, ethernetAddress)) {
309 fprintf(stderr, "%s: Line %" B_PRId32 ", \"%s\" is not a valid ethernet address.\n",
310 kProgramName, counter, argument);
311 continue;
312 }
313
314 // parse other options
315
316 uint32 flags = ARP_FLAG_PERMANENT;
317 while ((argument = next_argument(&rest)) != NULL) {
318 if (!set_flags(flags, argument)) {
319 fprintf(stderr, "%s: Line %" B_PRId32 ", ignoring invalid flag \"%s\".\n",
320 kProgramName, counter, argument);
321 }
322 }
323
324 status_t status = set_entry(&address, ethernetAddress, flags);
325 if (status != B_OK) {
326 fprintf(stderr, "%s: Line %" B_PRId32 ", ARP entry could not been set: %s\n",
327 kProgramName, counter, strerror(status));
328 }
329 }
330
331 fclose(file);
332 return 0;
333 }
334
335
336 static void
flush_entries()337 flush_entries()
338 {
339 arp_control control;
340
341 _kern_generic_syscall(ARP_SYSCALLS, ARP_FLUSH_ENTRIES,
342 &control, sizeof(arp_control));
343 }
344
345
346 // #pragma mark -
347
348
349 int
main(int argc,char ** argv)350 main(int argc, char** argv)
351 {
352 if (argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")))
353 usage(0);
354
355 int32 mode = ARP_LIST;
356 int32 i = 1;
357
358 if (argc > 1 && argv[1][0] == '-') {
359 if (!strcmp(argv[1], "-d")) {
360 // delete entry
361 if (argc != 3)
362 usage(1);
363
364 mode = ARP_DELETE;
365 } else if (!strcmp(argv[1], "-s")) {
366 // set entry
367 if (argc < 4)
368 usage(1);
369
370 mode = ARP_SET;
371 } else if (!strcmp(argv[1], "-a")) {
372 // list all
373 if (argc != 2)
374 usage(1);
375
376 mode = ARP_LIST;
377 } else if (!strcmp(argv[1], "-F")) {
378 // flush all entries
379 if (argc != 2)
380 usage(1);
381
382 check_for_arp_syscall();
383 flush_entries();
384 return 0;
385 } else if (!strcmp(argv[1], "-f")) {
386 // set from file
387 if (argc != 3)
388 usage(1);
389
390 check_for_arp_syscall();
391 return set_entries_from_file(argv[2]);
392 } else {
393 fprintf(stderr, "%s: Unknown option %s\n", kProgramName, argv[1]);
394 usage(1);
395 }
396
397 i++;
398 }
399
400 // parse hostname
401
402 const char *hostname = argv[i];
403 sockaddr_in address;
404
405 if (hostname != NULL && !parse_inet_address(hostname, address)) {
406 // get host by name
407 struct hostent *host = gethostbyname(hostname);
408 if (host == NULL) {
409 fprintf(stderr, "%s: Host \"%s\" is not known in the IPv4 domain: %s\n",
410 kProgramName, hostname, strerror(errno));
411 return 1;
412 }
413 if (host->h_addrtype != AF_INET) {
414 fprintf(stderr, "%s: Host \"%s\" is not known in the IPv4 domain.\n",
415 kProgramName, hostname);
416 return 1;
417 }
418
419 address.sin_family = AF_INET;
420 address.sin_len = sizeof(sockaddr_in);
421 address.sin_addr.s_addr = *(in_addr_t *)host->h_addr;
422 }
423
424 // parse other options in case of ARP_SET
425
426 uint8 ethernetAddress[6];
427 uint32 flags = ARP_FLAG_PERMANENT;
428
429 if (mode == ARP_SET) {
430 if (!parse_ethernet_address(argv[3], ethernetAddress)) {
431 fprintf(stderr, "%s: \"%s\" is not a valid ethernet address.\n",
432 kProgramName, argv[3]);
433 return 1;
434 }
435
436 for (int32 i = 4; i < argc; i++) {
437 if (!set_flags(flags, argv[i])) {
438 fprintf(stderr, "%s: Flag \"%s\" is invalid.\n",
439 kProgramName, argv[i]);
440 return 1;
441 }
442 }
443 }
444
445 check_for_arp_syscall();
446
447 switch (mode) {
448 case ARP_SET:
449 {
450 status_t status = set_entry(&address, ethernetAddress, flags);
451 if (status != B_OK) {
452 fprintf(stderr, "%s: ARP entry could not been set: %s\n",
453 kProgramName, strerror(status));
454 return 1;
455 }
456 break;
457 }
458 case ARP_DELETE:
459 delete_entry(&address);
460 break;
461 case ARP_LIST:
462 list_entries(hostname ? &address : NULL);
463 break;
464 }
465
466 return 0;
467 }
468
469