1/* 2 * Copyright (c) 2000-2010 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25/* 26 * NICache.c 27 * - netinfo host cache routines 28 */ 29 30#include <unistd.h> 31#include <stdlib.h> 32#include <stdio.h> 33#include <sys/stat.h> 34#include <sys/socket.h> 35#include <sys/ioctl.h> 36#include <sys/file.h> 37#include <sys/time.h> 38#include <sys/types.h> 39#include <net/if.h> 40#include <netinet/in.h> 41#include <netinet/in_systm.h> 42#include <netinet/ip.h> 43#include <netinet/udp.h> 44#include <netinet/bootp.h> 45#include <net/ethernet.h> 46#include <netinet/if_ether.h> 47#include <net/if_arp.h> 48#include <mach/boolean.h> 49#include <errno.h> 50#include <ctype.h> 51#include <arpa/inet.h> 52#include <string.h> 53#include <syslog.h> 54#include "dprintf.h" 55#include "NICache.h" 56#include "NICachePrivate.h" 57#include "util.h" 58#include "netinfo.h" 59 60#ifdef NICACHE_TEST 61#define TIMESTAMPS 62#endif /* NICACHE_TEST */ 63#ifdef READ_TEST 64#define TIMESTAMPS 65#endif /* READ_TEST */ 66 67#ifdef TIMESTAMPS 68static void 69timestamp_printf(char * msg) 70{ 71 static struct timeval tvp = {0,0}; 72 struct timeval tv; 73 74 gettimeofday(&tv, 0); 75 if (tvp.tv_sec) { 76 struct timeval result; 77 78 timeval_subtract(tv, tvp, &result); 79 printf("%d.%06d (%d.%06d): %s\n", 80 (int)tv.tv_sec, (int)tv.tv_usec, 81 (int)result.tv_sec, (int)result.tv_usec, msg); 82 } 83 else 84 printf("%d.%06d (%d.%06d): %s\n", 85 (int)tv.tv_sec, (int)tv.tv_usec, 0, 0, msg); 86 tvp = tv; 87} 88static __inline__ void 89S_timestamp(char * msg) 90{ 91 timestamp_printf(msg); 92} 93#else /* NICACHE_TEST */ 94static __inline__ void 95S_timestamp(char * msg) 96{ 97} 98#endif /* TIMESTAMPS */ 99 100/** 101 ** Module: PLCacheEntry 102 **/ 103 104PLCacheEntry_t * 105PLCacheEntry_create(ni_proplist pl) 106{ 107 PLCacheEntry_t * entry = malloc(sizeof(*entry)); 108 109 if (entry == NULL) 110 return (NULL); 111 entry->pl = ni_proplist_dup(pl); 112 entry->next = entry->prev = NULL; 113 return (entry); 114} 115 116void 117PLCacheEntry_free(PLCacheEntry_t * ent) 118{ 119 ni_proplist_free(&ent->pl); 120 bzero(ent, sizeof(*ent)); 121 free(ent); 122 return; 123} 124 125/** 126 ** Module: PLCache 127 **/ 128 129void 130PLCache_print(PLCache_t * cache) 131{ 132 int i; 133 PLCacheEntry_t * scan; 134 135 printf("PLCache contains %d elements\n", cache->count); 136 for (i = 0, scan = cache->head; scan; scan = scan->next, i++) { 137 printf("\nEntry %d\n", i); 138 ni_proplist_dump(&scan->pl); 139 } 140} 141 142void 143PLCache_init(PLCache_t * cache) 144{ 145 bzero(cache, sizeof(*cache)); 146 cache->max_entries = CACHE_MAX; 147 return; 148} 149 150int 151PLCache_count(PLCache_t * c) 152{ 153 return (c->count); 154} 155 156void 157PLCache_set_max(PLCache_t * c, int m) 158{ 159 if (m < CACHE_MIN) 160 m = CACHE_MIN; 161 c->max_entries = m; 162 if (c->count > c->max_entries) { 163 int i; 164 int num = c->count - c->max_entries; 165 PLCacheEntry_t * prev; 166 PLCacheEntry_t * scan; 167 168 dprintf(("Count %d max %d, removing %d\n", c->count, c->max_entries, 169 num)); 170 171 /* drop num items from the cache */ 172 prev = NULL; 173 scan = c->tail; 174 for (i = 0; i < num; i++) { 175 dprintf(("Deleting %d\n", i)); 176 prev = scan->prev; 177 PLCacheEntry_free(scan); 178 scan = prev; 179 } 180 c->tail = prev; 181 if (c->tail) { 182 c->tail->next = NULL; 183 } 184 else { 185 c->head = NULL; 186 } 187 c->count = c->max_entries; 188 } 189 return; 190} 191 192void 193PLCache_free(PLCache_t * cache) 194{ 195 PLCacheEntry_t * scan; 196 197 for (scan = cache->head; scan; ) { 198 PLCacheEntry_t * next; 199 200 next = scan->next; 201 PLCacheEntry_free(scan); 202 scan = next; 203 dprintf(("deleting %d\n", ++i)); 204 } 205 bzero(cache, sizeof(*cache)); 206 return; 207} 208 209void 210PLCache_add(PLCache_t * cache, PLCacheEntry_t * entry) 211{ 212 if (entry == NULL) 213 return; 214 215 entry->next = cache->head; 216 entry->prev = NULL; 217 if (cache->head == NULL) { 218 cache->head = cache->tail = entry; 219 } 220 else { 221 cache->head->prev = entry; 222 cache->head = entry; 223 } 224 cache->count++; 225 return; 226} 227 228void 229PLCache_append(PLCache_t * cache, PLCacheEntry_t * entry) 230{ 231 if (entry == NULL) 232 return; 233 234 entry->next = NULL; 235 entry->prev = cache->tail; 236 if (cache->head == NULL) { 237 cache->head = cache->tail = entry; 238 } 239 else { 240 cache->tail->next = entry; 241 cache->tail = entry; 242 } 243 cache->count++; 244 return; 245} 246 247/* 248 * Function: my_fgets 249 * Purpose: 250 * like fgets() but consumes/discards characters until the next newline 251 * once the line buffer is full. 252 */ 253static char * 254my_fgets(char * buf, int buf_size, FILE * f) 255{ 256 boolean_t done = FALSE; 257 int left = buf_size - 1; 258 char * scan; 259 260 scan = buf; 261 while (!done) { 262 int this_char; 263 264 this_char = fgetc(f); 265 switch (this_char) { 266 case 0: 267 case EOF: 268 done = TRUE; 269 break; 270 default: 271 if (left > 0) { 272 *scan++ = (char)this_char; 273 left--; 274 } 275 if (this_char == '\n') { 276 done = TRUE; 277 } 278 break; 279 } 280 } 281 if (scan == buf) { 282 /* we didn't read anything */ 283 return (NULL); 284 } 285 *scan = '\0'; 286 return (buf); 287} 288 289boolean_t 290PLCache_read(PLCache_t * cache, const char * filename) 291{ 292 FILE * file = NULL; 293 int line_number = 0; 294 char line[1024]; 295 ni_proplist pl; 296 enum { 297 nowhere_e, 298 start_e, 299 body_e, 300 end_e 301 } where = nowhere_e; 302 303 NI_INIT(&pl); 304 file = fopen(filename, "r"); 305 if (file == NULL) { 306 perror(filename); 307 goto failed; 308 } 309 310 while (1) { 311 if (my_fgets(line, sizeof(line), file) != line) { 312 if (where == start_e || where == body_e) { 313 fprintf(stderr, "file ends prematurely\n"); 314 } 315 break; 316 } 317 line_number++; 318 if (strcmp(line, "{\n") == 0) { 319 if (where != end_e && where != nowhere_e) { 320 fprintf(stderr, "unexpected '{' at line %d\n", 321 line_number); 322 goto failed; 323 } 324 where = start_e; 325 } 326 else if (strcmp(line, "}\n") == 0) { 327 if (where != start_e && where != body_e) { 328 fprintf(stderr, "unexpected '}' at line %d\n", 329 line_number); 330 goto failed; 331 } 332 if (pl.nipl_len > 0) { 333 PLCache_append(cache, PLCacheEntry_create(pl)); 334 ni_proplist_free(&pl); 335 } 336 where = end_e; 337 } 338 else { 339 char propname[128]; 340 char propval[768] = ""; 341 int len = strlen(line); 342 char * sep = strchr(line, '='); 343 int whitespace_len = strspn(line, " \t\n"); 344 345 if (whitespace_len == len) { 346 continue; 347 } 348 if (sep) { 349 int nlen = (sep - line) - whitespace_len; 350 int vlen = len - whitespace_len - nlen - 2; 351 352 if (nlen >= sizeof(propname)) { 353 fprintf(stderr, 354 "property name truncated to %d bytes at line %d\n", 355 (int)sizeof(propname) - 1, 356 line_number); 357 nlen = sizeof(propname) - 1; 358 } 359 if (vlen >= sizeof(propval)) { 360 fprintf(stderr, 361 "value truncated to %d bytes at line %d\n", 362 (int)sizeof(propval) - 1, 363 line_number); 364 vlen = sizeof(propval) - 1; 365 } 366 strncpy(propname, line + whitespace_len, nlen); 367 propname[nlen] = '\0'; 368 strncpy(propval, sep + 1, vlen); 369 propval[vlen] = '\0'; 370 ni_proplist_insertprop(&pl, propname, propval, NI_INDEX_NULL); 371 } 372 else { 373 int nlen = len - whitespace_len - 1; 374 375 if (nlen >= sizeof(propname)) { 376 fprintf(stderr, 377 "property name truncated to %d bytes at line %d\n", 378 (int)sizeof(propname) - 1, 379 line_number); 380 nlen = sizeof(propname) - 1; 381 } 382 strncpy(propname, line + whitespace_len, nlen); 383 propname[nlen] = '\0'; 384 ni_proplist_insertprop(&pl, propname, NULL, NI_INDEX_NULL); 385 } 386 where = body_e; 387 } 388 } 389 390 failed: 391 if (file) 392 fclose(file); 393 ni_proplist_free(&pl); 394 return (TRUE); 395} 396 397boolean_t 398PLCache_write(PLCache_t * cache, const char * filename) 399{ 400 FILE * file = NULL; 401 PLCacheEntry_t * scan; 402 char tmp_filename[256]; 403 404 snprintf(tmp_filename, sizeof(tmp_filename), "%s-", filename); 405 file = fopen(tmp_filename, "w"); 406 if (file == NULL) { 407 perror(tmp_filename); 408 return (FALSE); 409 } 410 411 for (scan = cache->head; scan; scan = scan->next) { 412 int i; 413 414 fprintf(file, "{\n"); 415 for (i = 0; i < scan->pl.nipl_len; i++) { 416 ni_property * prop = &(scan->pl.nipl_val[i]); 417 ni_namelist * nl_p = &prop->nip_val; 418 if (nl_p->ninl_len == 0) { 419 fprintf(file, "\t%s\n", prop->nip_name); 420 } 421 else { 422 fprintf(file, "\t%s=%s\n", prop->nip_name, 423 nl_p->ninl_val[0]); 424 } 425 } 426 fprintf(file, "}\n"); 427 } 428 fclose(file); 429 rename(tmp_filename, filename); 430 return (TRUE); 431} 432 433void 434PLCache_remove(PLCache_t * cache, PLCacheEntry_t * entry) 435{ 436 if (entry == NULL) 437 return; 438 439 if (entry->prev) 440 entry->prev->next = entry->next; 441 if (entry->next) 442 entry->next->prev = entry->prev; 443 if (entry == cache->head) { 444 cache->head = cache->head->next; 445 } 446 if (entry == cache->tail) { 447 cache->tail = cache->tail->prev; 448 } 449 entry->next = entry->prev = NULL; 450 cache->count--; 451 return; 452} 453 454void 455PLCache_make_head(PLCache_t * cache, PLCacheEntry_t * entry) 456{ 457 if (entry == cache->head) 458 return; /* already the head */ 459 460 PLCache_remove(cache, entry); 461 PLCache_add(cache, entry); 462} 463 464PLCacheEntry_t * 465PLCache_lookup_prop(PLCache_t * PLCache, char * prop, char * value, boolean_t make_head) 466{ 467 PLCacheEntry_t * scan; 468 469 for (scan = PLCache->head; scan; scan = scan->next) { 470 int name_index; 471 472 name_index = ni_proplist_match(scan->pl, prop, value); 473 if (name_index != NI_INDEX_NULL) { 474 if (make_head) { 475 PLCache_make_head(PLCache, scan); 476 } 477 return (scan); 478 } 479 } 480 return (NULL); 481} 482 483PLCacheEntry_t * 484PLCache_lookup_hw(PLCache_t * PLCache, 485 uint8_t hwtype, void * hwaddr, int hwlen, 486 NICacheFunc_t * func, void * arg, 487 struct in_addr * client_ip, 488 boolean_t * has_binding) 489{ 490 struct ether_addr * en_search = (struct ether_addr *)hwaddr; 491 PLCacheEntry_t * scan; 492 493 if (has_binding) 494 *has_binding = FALSE; 495 for (scan = PLCache->head; scan; scan = scan->next) { 496 ni_namelist * en_nl_p; 497 int n; 498 int en_index; 499 int ip_index; 500 ni_namelist * ip_nl_p; 501 502 en_index = ni_proplist_match(scan->pl, NIPROP_ENADDR, NULL); 503 if (en_index == NI_INDEX_NULL) 504 continue; 505 ip_index = ni_proplist_match(scan->pl, NIPROP_IPADDR, NULL); 506 if (ip_index == NI_INDEX_NULL) 507 continue; 508 509 en_nl_p = &scan->pl.nipl_val[en_index].nip_val; 510 ip_nl_p = &scan->pl.nipl_val[ip_index].nip_val; 511 if (en_nl_p->ninl_len == 0 || ip_nl_p->ninl_len == 0) 512 continue; 513 for (n = 0; n < en_nl_p->ninl_len; n++) { 514 struct ether_addr * en_p = ether_aton(en_nl_p->ninl_val[n]); 515 if (en_p == NULL) 516 continue; 517 if (bcmp(en_p, en_search, sizeof(*en_search)) == 0) { 518 int which_ip; 519 520 which_ip = n % ip_nl_p->ninl_len; 521 if (inet_aton(ip_nl_p->ninl_val[which_ip], client_ip) == 0) 522 continue; 523 if (has_binding) 524 *has_binding = TRUE; 525 if (func == NULL || (*func)(arg, *client_ip)) { 526 PLCache_make_head(PLCache, scan); 527 return (scan); 528 } 529 } 530 } 531 } 532 return (NULL); 533} 534 535 536PLCacheEntry_t * 537PLCache_lookup_identifier(PLCache_t * PLCache, 538 char * idstr, NICacheFunc_t * func, void * arg, 539 struct in_addr * client_ip, 540 boolean_t * has_binding) 541{ 542 PLCacheEntry_t * scan; 543 544 if (has_binding) 545 *has_binding = FALSE; 546 for (scan = PLCache->head; scan; scan = scan->next) { 547 ni_namelist * ident_nl_p; 548 int n; 549 int ident_index; 550 int ip_index; 551 ni_namelist * ip_nl_p; 552 553 ident_index = ni_proplist_match(scan->pl, NIPROP_IDENTIFIER, 554 NULL); 555 if (ident_index == NI_INDEX_NULL) 556 continue; 557 ident_nl_p = &scan->pl.nipl_val[ident_index].nip_val; 558 if (ident_nl_p->ninl_len == 0) 559 continue; 560 561 if (client_ip == NULL) { /* don't care about IP binding */ 562 if (strcmp(ident_nl_p->ninl_val[0], idstr) == 0) { 563 if (has_binding) 564 *has_binding = TRUE; 565 PLCache_make_head(PLCache, scan); 566 return (scan); 567 } 568 } 569 570 ip_index = ni_proplist_match(scan->pl, NIPROP_IPADDR, NULL); 571 if (ip_index == NI_INDEX_NULL) 572 continue; 573 ip_nl_p = &scan->pl.nipl_val[ip_index].nip_val; 574 if (ip_nl_p->ninl_len == 0) 575 continue; 576 577 for (n = 0; n < ident_nl_p->ninl_len; n++) { 578 if (strcmp(ident_nl_p->ninl_val[n], idstr) == 0) { 579 int which_ip; 580 581 which_ip = n % ip_nl_p->ninl_len; 582 if (inet_aton(ip_nl_p->ninl_val[which_ip], client_ip) == 0) 583 continue; 584 if (has_binding) 585 *has_binding = TRUE; 586 if (func == NULL || (client_ip != NULL && (*func)(arg, *client_ip))) { 587 PLCache_make_head(PLCache, scan); 588 return (scan); 589 } 590 } 591 } 592 } 593 return (NULL); 594} 595 596 597PLCacheEntry_t * 598PLCache_lookup_ip(PLCache_t * PLCache, struct in_addr iaddr) 599{ 600 PLCacheEntry_t * scan; 601 602 for (scan = PLCache->head; scan; scan = scan->next) { 603 int n; 604 int ip_index; 605 ni_namelist * ip_nl_p; 606 607 ip_index = ni_proplist_match(scan->pl, NIPROP_IPADDR, NULL); 608 if (ip_index == NI_INDEX_NULL) 609 continue; 610 ip_nl_p = &scan->pl.nipl_val[ip_index].nip_val; 611 for (n = 0; n < ip_nl_p->ninl_len; n++) { 612 struct in_addr entry_ip; 613 if (inet_aton(ip_nl_p->ninl_val[n], &entry_ip) == 0) 614 continue; 615 if (iaddr.s_addr == entry_ip.s_addr) { 616 PLCache_make_head(PLCache, scan); 617 return (scan); 618 } 619 } 620 } 621 return (NULL); 622} 623 624#ifdef READ_TEST 625int 626main(int argc, char * argv[]) 627{ 628 PLCache_t PLCache; 629 630 if (argc < 2) 631 exit(1); 632 633 PLCache_init(&PLCache); 634 PLCache_set_max(&PLCache, 100 * 1024 * 1024); 635 S_timestamp("before read"); 636 if (PLCache_read(&PLCache, argv[1]) == TRUE) { 637 S_timestamp("after read"); 638 PLCache_print(&PLCache); 639 640 printf("writing /tmp/readtest.out\n"); 641 PLCache_write(&PLCache, "/tmp/readtest.out"); 642 } 643 else { 644 S_timestamp("after read"); 645 printf("PLCache_read failed\n"); 646 } 647 exit(0); 648} 649 650#endif /* READ_TEST */ 651