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 }; 174 bool first = true; 175 176 for (uint32 i = 0; i < sizeof(kFlags) / sizeof(kFlags[0]); i++) { 177 if ((control.flags & kFlags[i].value) != 0) { 178 if (first) { 179 printf(" "); 180 first = false; 181 } else 182 putchar(' '); 183 printf(kFlags[i].name); 184 } 185 } 186 } 187 188 putchar('\n'); 189 } 190 191 192 void 193 list_entries(sockaddr_in *address) 194 { 195 arp_control control; 196 status_t status; 197 198 if (address != NULL) { 199 control.address = address->sin_addr.s_addr; 200 status = _kern_generic_syscall(ARP_SYSCALLS, ARP_GET_ENTRY, 201 &control, sizeof(arp_control)); 202 if (status != B_OK) { 203 fprintf(stderr, "%s: ARP entry does not exist.\n", kProgramName); 204 exit(1); 205 } 206 207 list_entry(control); 208 return; 209 } 210 211 control.cookie = 0; 212 while (_kern_generic_syscall(ARP_SYSCALLS, ARP_GET_ENTRIES, 213 &control, sizeof(arp_control)) == B_OK) { 214 list_entry(control); 215 } 216 } 217 218 219 void 220 delete_entry(sockaddr_in *address) 221 { 222 arp_control control; 223 control.address = address->sin_addr.s_addr; 224 225 status_t status = _kern_generic_syscall(ARP_SYSCALLS, ARP_DELETE_ENTRY, 226 &control, sizeof(arp_control)); 227 if (status != B_OK) { 228 fprintf(stderr, "%s: Could not delete ARP entry: %s\n", 229 kProgramName, strerror(status)); 230 exit(1); 231 } 232 } 233 234 235 status_t 236 set_entry(sockaddr_in *address, uint8 *ethernetAddress, uint32 flags) 237 { 238 arp_control control; 239 control.address = address->sin_addr.s_addr; 240 memcpy(control.ethernet_address, ethernetAddress, ETHER_ADDRESS_LENGTH); 241 control.flags = flags; 242 243 return _kern_generic_syscall(ARP_SYSCALLS, ARP_SET_ENTRY, 244 &control, sizeof(arp_control)); 245 } 246 247 248 int 249 set_entries_from_file(const char *filename) 250 { 251 FILE *file = fopen(filename, "r"); 252 if (file == NULL) { 253 fprintf(stderr, "%s: Could not open file: %s\n", kProgramName, strerror(errno)); 254 return 1; 255 } 256 257 int32 counter = 0; 258 char line[4096]; 259 260 while (fgets(line, sizeof(line), file) != NULL) { 261 counter++; 262 263 // comments and empty lines are allowed 264 if (line[0] == '#' || !strcmp(line, "\n")) 265 continue; 266 267 // parse hostname 268 269 char *rest = line; 270 const char *argument = next_argument(&rest); 271 if (argument == NULL) { 272 fprintf(stderr, "%s: Line %ld is invalid (missing hostname).\n", 273 kProgramName, counter); 274 continue; 275 } 276 277 sockaddr_in address; 278 if (!parse_inet_address(argument, address)) { 279 // get host by name 280 struct hostent *host = gethostbyname(argument); 281 if (host == NULL) { 282 fprintf(stderr, "%s: Line %ld, host \"%s\" is not known in the IPv4 domain: %s\n", 283 kProgramName, counter, argument, strerror(errno)); 284 continue; 285 } 286 if (host->h_addrtype != AF_INET) { 287 fprintf(stderr, "%s: Line %ld, host \"%s\" is not known in the IPv4 domain.\n", 288 kProgramName, counter, argument); 289 continue; 290 } 291 292 address.sin_family = AF_INET; 293 address.sin_len = sizeof(sockaddr_in); 294 address.sin_addr.s_addr = *(in_addr_t *)host->h_addr; 295 } 296 297 // parse ethernet MAC address 298 299 argument = next_argument(&rest); 300 if (argument == NULL) { 301 fprintf(stderr, "%s: Line %ld is invalid (missing ethernet address).\n", 302 kProgramName, counter); 303 continue; 304 } 305 306 uint8 ethernetAddress[6]; 307 if (!parse_ethernet_address(argument, ethernetAddress)) { 308 fprintf(stderr, "%s: Line %ld, \"%s\" is not a valid ethernet address.\n", 309 kProgramName, counter, argument); 310 continue; 311 } 312 313 // parse other options 314 315 uint32 flags = ARP_FLAG_PERMANENT; 316 while ((argument = next_argument(&rest)) != NULL) { 317 if (!set_flags(flags, argument)) { 318 fprintf(stderr, "%s: Line %ld, ignoring invalid flag \"%s\".\n", 319 kProgramName, counter, argument); 320 } 321 } 322 323 status_t status = set_entry(&address, ethernetAddress, flags); 324 if (status != B_OK) { 325 fprintf(stderr, "%s: Line %ld, ARP entry could not been set: %s\n", 326 kProgramName, counter, strerror(status)); 327 } 328 } 329 330 fclose(file); 331 return 0; 332 } 333 334 335 // #pragma mark - 336 337 338 int 339 main(int argc, char** argv) 340 { 341 if (argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) 342 usage(0); 343 344 int32 mode = ARP_LIST; 345 int32 i = 1; 346 347 if (argc > 1 && argv[1][0] == '-') { 348 if (!strcmp(argv[1], "-d")) { 349 // delete entry 350 if (argc != 3) 351 usage(1); 352 353 mode = ARP_DELETE; 354 } else if (!strcmp(argv[1], "-s")) { 355 // set entry 356 if (argc < 4) 357 usage(1); 358 359 mode = ARP_SET; 360 } else if (!strcmp(argv[1], "-a")) { 361 // list all 362 if (argc != 2) 363 usage(1); 364 365 mode = ARP_LIST; 366 } else if (!strcmp(argv[1], "-F")) { 367 // flush all entries 368 if (argc != 2) 369 usage(1); 370 371 mode = ARP_FLUSH; 372 } else if (!strcmp(argv[1], "-f")) { 373 // set from file 374 if (argc != 3) 375 usage(1); 376 377 check_for_arp_syscall(); 378 return set_entries_from_file(argv[2]); 379 } else { 380 fprintf(stderr, "%s: Unknown option %s\n", kProgramName, argv[1]); 381 usage(1); 382 } 383 384 i++; 385 } 386 387 // parse hostname 388 389 const char *hostname = argv[i]; 390 sockaddr_in address; 391 392 if (hostname != NULL && !parse_inet_address(hostname, address)) { 393 // get host by name 394 struct hostent *host = gethostbyname(hostname); 395 if (host == NULL) { 396 fprintf(stderr, "%s: Host \"%s\" is not known in the IPv4 domain: %s\n", 397 kProgramName, hostname, strerror(errno)); 398 return 1; 399 } 400 if (host->h_addrtype != AF_INET) { 401 fprintf(stderr, "%s: Host \"%s\" is not known in the IPv4 domain.\n", 402 kProgramName, hostname); 403 return 1; 404 } 405 406 address.sin_family = AF_INET; 407 address.sin_len = sizeof(sockaddr_in); 408 address.sin_addr.s_addr = *(in_addr_t *)host->h_addr; 409 } 410 411 // parse other options in case of ARP_SET 412 413 uint8 ethernetAddress[6]; 414 uint32 flags = ARP_FLAG_PERMANENT; 415 416 if (mode == ARP_SET) { 417 if (!parse_ethernet_address(argv[3], ethernetAddress)) { 418 fprintf(stderr, "%s: \"%s\" is not a valid ethernet address.\n", 419 kProgramName, argv[3]); 420 return 1; 421 } 422 423 for (int32 i = 4; i < argc; i++) { 424 if (!set_flags(flags, argv[i])) { 425 fprintf(stderr, "%s: Flag \"%s\" is invalid.\n", 426 kProgramName, argv[i]); 427 return 1; 428 } 429 } 430 } 431 432 check_for_arp_syscall(); 433 434 switch (mode) { 435 case ARP_SET: 436 { 437 status_t status = set_entry(&address, ethernetAddress, flags); 438 if (status != B_OK) { 439 fprintf(stderr, "%s: ARP entry could not been set: %s\n", 440 kProgramName, strerror(status)); 441 return 1; 442 } 443 break; 444 } 445 case ARP_DELETE: 446 delete_entry(&address); 447 break; 448 case ARP_LIST: 449 list_entries(hostname ? &address : NULL); 450 break; 451 } 452 453 return 0; 454 } 455 456