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 <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 ARP_FLUSH, 37 }; 38 39 40 static const char * 41 ethernet_address_to_string(uint8 *address) 42 { 43 static char string[64]; 44 snprintf(string, sizeof(string), "%02x:%02x:%02x:%02x:%02x:%02x", 45 address[0], address[1], address[2], address[3], address[4], address[5]); 46 return string; 47 } 48 49 50 static bool 51 parse_ethernet_address(const char *string, uint8 *address) 52 { 53 return sscanf(string, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &address[0], &address[1], 54 &address[2], &address[3], &address[4], &address[5]) == 6; 55 } 56 57 58 static bool 59 parse_inet_address(const char* string, sockaddr_in& address) 60 { 61 in_addr inetAddress; 62 if (inet_aton(string, &inetAddress) != 1) 63 return false; 64 65 address.sin_family = AF_INET; 66 address.sin_len = sizeof(struct sockaddr_in); 67 address.sin_port = 0; 68 address.sin_addr = inetAddress; 69 memset(&address.sin_zero[0], 0, sizeof(address.sin_zero)); 70 71 return true; 72 } 73 74 75 static void 76 check_for_arp_syscall(void) 77 { 78 uint32 version = 0; 79 status_t status = _kern_generic_syscall(ARP_SYSCALLS, B_SYSCALL_INFO, 80 &version, sizeof(version)); 81 if (status != B_OK) { 82 fprintf(stderr, "\"ARP\" module not available.\n"); 83 exit(1); 84 } 85 } 86 87 88 void 89 usage(int status) 90 { 91 printf("usage: %s [<command>] [<hostname>] [<ethernet-address>] [temp] [pub]\n" 92 " %s -f <filename>\n" 93 "Where <command> can be the one of:\n" 94 " -s - sets an entry in the ARP cache\n" 95 " -d - deletes the specified ARP cache entry\n" 96 " -a - list all entries [default]\n" 97 " -f - read ARP entries from file\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 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 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 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 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 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 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 %ld 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 %ld, 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 %ld, 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 %ld 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 %ld, \"%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 %ld, 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 %ld, 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 // #pragma mark - 337 338 339 int 340 main(int argc, char** argv) 341 { 342 if (argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) 343 usage(0); 344 345 int32 mode = ARP_LIST; 346 int32 i = 1; 347 348 if (argc > 1 && argv[1][0] == '-') { 349 if (!strcmp(argv[1], "-d")) { 350 // delete entry 351 if (argc != 3) 352 usage(1); 353 354 mode = ARP_DELETE; 355 } else if (!strcmp(argv[1], "-s")) { 356 // set entry 357 if (argc < 4) 358 usage(1); 359 360 mode = ARP_SET; 361 } else if (!strcmp(argv[1], "-a")) { 362 // list all 363 if (argc != 2) 364 usage(1); 365 366 mode = ARP_LIST; 367 } else if (!strcmp(argv[1], "-F")) { 368 // flush all entries 369 if (argc != 2) 370 usage(1); 371 372 mode = ARP_FLUSH; 373 } else if (!strcmp(argv[1], "-f")) { 374 // set from file 375 if (argc != 3) 376 usage(1); 377 378 check_for_arp_syscall(); 379 return set_entries_from_file(argv[2]); 380 } else { 381 fprintf(stderr, "%s: Unknown option %s\n", kProgramName, argv[1]); 382 usage(1); 383 } 384 385 i++; 386 } 387 388 // parse hostname 389 390 const char *hostname = argv[i]; 391 sockaddr_in address; 392 393 if (hostname != NULL && !parse_inet_address(hostname, address)) { 394 // get host by name 395 struct hostent *host = gethostbyname(hostname); 396 if (host == NULL) { 397 fprintf(stderr, "%s: Host \"%s\" is not known in the IPv4 domain: %s\n", 398 kProgramName, hostname, strerror(errno)); 399 return 1; 400 } 401 if (host->h_addrtype != AF_INET) { 402 fprintf(stderr, "%s: Host \"%s\" is not known in the IPv4 domain.\n", 403 kProgramName, hostname); 404 return 1; 405 } 406 407 address.sin_family = AF_INET; 408 address.sin_len = sizeof(sockaddr_in); 409 address.sin_addr.s_addr = *(in_addr_t *)host->h_addr; 410 } 411 412 // parse other options in case of ARP_SET 413 414 uint8 ethernetAddress[6]; 415 uint32 flags = ARP_FLAG_PERMANENT; 416 417 if (mode == ARP_SET) { 418 if (!parse_ethernet_address(argv[3], ethernetAddress)) { 419 fprintf(stderr, "%s: \"%s\" is not a valid ethernet address.\n", 420 kProgramName, argv[3]); 421 return 1; 422 } 423 424 for (int32 i = 4; i < argc; i++) { 425 if (!set_flags(flags, argv[i])) { 426 fprintf(stderr, "%s: Flag \"%s\" is invalid.\n", 427 kProgramName, argv[i]); 428 return 1; 429 } 430 } 431 } 432 433 check_for_arp_syscall(); 434 435 switch (mode) { 436 case ARP_SET: 437 { 438 status_t status = set_entry(&address, ethernetAddress, flags); 439 if (status != B_OK) { 440 fprintf(stderr, "%s: ARP entry could not been set: %s\n", 441 kProgramName, strerror(status)); 442 return 1; 443 } 444 break; 445 } 446 case ARP_DELETE: 447 delete_entry(&address); 448 break; 449 case ARP_LIST: 450 list_entries(hostname ? &address : NULL); 451 break; 452 } 453 454 return 0; 455 } 456 457