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 * 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 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 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 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 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 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 * 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 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 printf(kFlags[i].name); 185 } 186 } 187 } 188 189 putchar('\n'); 190 } 191 192 193 static void 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 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 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 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 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 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