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