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