#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/types.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/bootp.h>
#include <net/ethernet.h>
#include <netinet/if_ether.h>
#include <net/if_arp.h>
#include <mach/boolean.h>
#include <errno.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <string.h>
#include <syslog.h>
#include "dprintf.h"
#include "NICache.h"
#include "NICachePrivate.h"
#include "util.h"
#include "netinfo.h"
#ifdef NICACHE_TEST
#define TIMESTAMPS
#endif NICACHE_TEST
#ifdef READ_TEST
#define TIMESTAMPS
#endif READ_TEST
#ifdef TIMESTAMPS
static void
timestamp_printf(char * msg)
{
static struct timeval tvp = {0,0};
struct timeval tv;
gettimeofday(&tv, 0);
if (tvp.tv_sec) {
struct timeval result;
timeval_subtract(tv, tvp, &result);
printf("%d.%06d (%d.%06d): %s\n",
(int)tv.tv_sec, (int)tv.tv_usec,
(int)result.tv_sec, (int)result.tv_usec, msg);
}
else
printf("%d.%06d (%d.%06d): %s\n",
(int)tv.tv_sec, (int)tv.tv_usec, 0, 0, msg);
tvp = tv;
}
static __inline__ void
S_timestamp(char * msg)
{
timestamp_printf(msg);
}
#else NICACHE_TEST
static __inline__ void
S_timestamp(char * msg)
{
}
#endif TIMESTAMPS
PLCacheEntry_t *
PLCacheEntry_create(ni_proplist pl)
{
PLCacheEntry_t * entry = malloc(sizeof(*entry));
if (entry == NULL)
return (NULL);
entry->pl = ni_proplist_dup(pl);
entry->next = entry->prev = NULL;
return (entry);
}
void
PLCacheEntry_free(PLCacheEntry_t * ent)
{
ni_proplist_free(&ent->pl);
bzero(ent, sizeof(*ent));
free(ent);
return;
}
void
PLCache_print(PLCache_t * cache)
{
int i;
PLCacheEntry_t * scan;
printf("PLCache contains %d elements\n", cache->count);
for (i = 0, scan = cache->head; scan; scan = scan->next, i++) {
printf("\nEntry %d\n", i);
ni_proplist_dump(&scan->pl);
}
}
void
PLCache_init(PLCache_t * cache)
{
bzero(cache, sizeof(*cache));
cache->max_entries = CACHE_MAX;
return;
}
int
PLCache_count(PLCache_t * c)
{
return (c->count);
}
void
PLCache_set_max(PLCache_t * c, int m)
{
if (m < CACHE_MIN)
m = CACHE_MIN;
c->max_entries = m;
if (c->count > c->max_entries) {
int i;
int num = c->count - c->max_entries;
PLCacheEntry_t * prev;
PLCacheEntry_t * scan;
dprintf(("Count %d max %d, removing %d\n", c->count, c->max_entries,
num));
prev = NULL;
scan = c->tail;
for (i = 0; i < num; i++) {
dprintf(("Deleting %d\n", i));
prev = scan->prev;
PLCacheEntry_free(scan);
scan = prev;
}
c->tail = prev;
if (c->tail) {
c->tail->next = NULL;
}
else {
c->head = NULL;
}
c->count = c->max_entries;
}
return;
}
void
PLCache_free(PLCache_t * cache)
{
PLCacheEntry_t * scan = cache->head;
for (scan = cache->head; scan; ) {
PLCacheEntry_t * next;
next = scan->next;
PLCacheEntry_free(scan);
scan = next;
dprintf(("deleting %d\n", ++i));
}
bzero(cache, sizeof(*cache));
return;
}
void
PLCache_add(PLCache_t * cache, PLCacheEntry_t * entry)
{
if (entry == NULL)
return;
entry->next = cache->head;
entry->prev = NULL;
if (cache->head == NULL) {
cache->head = cache->tail = entry;
}
else {
cache->head->prev = entry;
cache->head = entry;
}
cache->count++;
return;
}
void
PLCache_append(PLCache_t * cache, PLCacheEntry_t * entry)
{
if (entry == NULL)
return;
entry->next = NULL;
entry->prev = cache->tail;
if (cache->head == NULL) {
cache->head = cache->tail = entry;
}
else {
cache->tail->next = entry;
cache->tail = entry;
}
cache->count++;
return;
}
boolean_t
PLCache_read(PLCache_t * cache, const char * filename)
{
FILE * file = NULL;
int line_number = 0;
char line[1024];
ni_proplist pl;
enum {
nowhere_e,
start_e,
body_e,
end_e
} where = nowhere_e;
NI_INIT(&pl);
file = fopen(filename, "r");
if (file == NULL) {
perror(filename);
goto failed;
}
while (1) {
if (fgets(line, sizeof(line), file) != line) {
if (where == start_e || where == body_e) {
fprintf(stderr, "file ends prematurely\n");
}
break;
}
line_number++;
if (strcmp(line, "{\n") == 0) {
if (where != end_e && where != nowhere_e) {
fprintf(stderr, "unexpected '{' at line %d\n",
line_number);
goto failed;
}
where = start_e;
}
else if (strcmp(line, "}\n") == 0) {
if (where != start_e && where != body_e) {
fprintf(stderr, "unexpected '}' at line %d\n",
line_number);
goto failed;
}
if (pl.nipl_len > 0) {
PLCache_append(cache, PLCacheEntry_create(pl));
ni_proplist_free(&pl);
}
where = end_e;
}
else {
char propname[128];
char propval[768] = "";
int len = strlen(line);
char * sep = strchr(line, '=');
int whitespace_len = strspn(line, " \t\n");
if (whitespace_len == len) {
continue;
}
if (sep) {
int nlen = (sep - line) - whitespace_len;
int vlen = len - whitespace_len - nlen - 2;
strncpy(propname, line + whitespace_len, nlen);
propname[nlen] = '\0';
strncpy(propval, sep + 1, vlen);
propval[vlen] = '\0';
ni_proplist_insertprop(&pl, propname, propval, NI_INDEX_NULL);
}
else {
int nlen = len - whitespace_len - 1;
strncpy(propname, line + whitespace_len, nlen);
propname[nlen] = '\0';
ni_proplist_insertprop(&pl, propname, NULL, NI_INDEX_NULL);
}
where = body_e;
}
}
failed:
if (file)
fclose(file);
ni_proplist_free(&pl);
return (TRUE);
}
boolean_t
PLCache_write(PLCache_t * cache, const char * filename)
{
FILE * file = NULL;
PLCacheEntry_t * scan;
char tmp_filename[256];
sprintf(tmp_filename, "%s-", filename);
file = fopen(tmp_filename, "w");
if (file == NULL) {
perror(tmp_filename);
return (FALSE);
}
for (scan = cache->head; scan; scan = scan->next) {
int i;
fprintf(file, "{\n");
for (i = 0; i < scan->pl.nipl_len; i++) {
ni_property * prop = &(scan->pl.nipl_val[i]);
ni_namelist * nl_p = &prop->nip_val;
if (nl_p->ninl_len == 0) {
fprintf(file, "\t%s\n", prop->nip_name);
}
else {
fprintf(file, "\t%s=%s\n", prop->nip_name,
nl_p->ninl_val[0]);
}
}
fprintf(file, "}\n");
}
fclose(file);
rename(tmp_filename, filename);
return (TRUE);
}
void
PLCache_remove(PLCache_t * cache, PLCacheEntry_t * entry)
{
if (entry == NULL)
return;
if (entry->prev)
entry->prev->next = entry->next;
if (entry->next)
entry->next->prev = entry->prev;
if (entry == cache->head) {
cache->head = cache->head->next;
}
if (entry == cache->tail) {
cache->tail = cache->tail->prev;
}
entry->next = entry->prev = NULL;
cache->count--;
return;
}
void
PLCache_make_head(PLCache_t * cache, PLCacheEntry_t * entry)
{
if (entry == cache->head)
return;
PLCache_remove(cache, entry);
PLCache_add(cache, entry);
}
PLCacheEntry_t *
PLCache_lookup_prop(PLCache_t * PLCache, char * prop, char * value, boolean_t make_head)
{
PLCacheEntry_t * scan;
for (scan = PLCache->head; scan; scan = scan->next) {
int name_index;
name_index = ni_proplist_match(scan->pl, prop, value);
if (name_index != NI_INDEX_NULL) {
if (make_head) {
PLCache_make_head(PLCache, scan);
}
return (scan);
}
}
return (NULL);
}
PLCacheEntry_t *
PLCache_lookup_hw(PLCache_t * PLCache,
uint8_t hwtype, void * hwaddr, int hwlen,
NICacheFunc_t * func, void * arg,
struct in_addr * client_ip,
boolean_t * has_binding)
{
struct ether_addr * en_search = (struct ether_addr *)hwaddr;
PLCacheEntry_t * scan;
if (has_binding)
*has_binding = FALSE;
for (scan = PLCache->head; scan; scan = scan->next) {
ni_namelist * en_nl_p;
int n;
int en_index;
int ip_index;
ni_namelist * ip_nl_p;
en_index = ni_proplist_match(scan->pl, NIPROP_ENADDR, NULL);
if (en_index == NI_INDEX_NULL)
continue;
ip_index = ni_proplist_match(scan->pl, NIPROP_IPADDR, NULL);
if (ip_index == NI_INDEX_NULL)
continue;
en_nl_p = &scan->pl.nipl_val[en_index].nip_val;
ip_nl_p = &scan->pl.nipl_val[ip_index].nip_val;
if (en_nl_p->ninl_len == 0 || ip_nl_p->ninl_len == 0)
continue;
for (n = 0; n < en_nl_p->ninl_len; n++) {
struct ether_addr * en_p = ether_aton(en_nl_p->ninl_val[n]);
if (en_p == NULL)
continue;
if (bcmp(en_p, en_search, sizeof(*en_search)) == 0) {
int which_ip;
which_ip = n % ip_nl_p->ninl_len;
if (inet_aton(ip_nl_p->ninl_val[which_ip], client_ip) == 0)
continue;
if (has_binding)
*has_binding = TRUE;
if (func == NULL || (*func)(arg, *client_ip)) {
PLCache_make_head(PLCache, scan);
return (scan);
}
}
}
}
return (NULL);
}
PLCacheEntry_t *
PLCache_lookup_identifier(PLCache_t * PLCache,
char * idstr, NICacheFunc_t * func, void * arg,
struct in_addr * client_ip,
boolean_t * has_binding)
{
PLCacheEntry_t * scan;
if (has_binding)
*has_binding = FALSE;
for (scan = PLCache->head; scan; scan = scan->next) {
ni_namelist * ident_nl_p;
int n;
int ident_index;
int ip_index;
ni_namelist * ip_nl_p;
ident_index = ni_proplist_match(scan->pl, NIPROP_IDENTIFIER,
NULL);
if (ident_index == NI_INDEX_NULL)
continue;
ident_nl_p = &scan->pl.nipl_val[ident_index].nip_val;
if (ident_nl_p->ninl_len == 0)
continue;
if (client_ip == NULL) {
if (strcmp(ident_nl_p->ninl_val[0], idstr) == 0) {
if (has_binding)
*has_binding = TRUE;
PLCache_make_head(PLCache, scan);
return (scan);
}
}
ip_index = ni_proplist_match(scan->pl, NIPROP_IPADDR, NULL);
if (ip_index == NI_INDEX_NULL)
continue;
ip_nl_p = &scan->pl.nipl_val[ip_index].nip_val;
if (ip_nl_p->ninl_len == 0)
continue;
for (n = 0; n < ident_nl_p->ninl_len; n++) {
if (strcmp(ident_nl_p->ninl_val[n], idstr) == 0) {
int which_ip;
which_ip = n % ip_nl_p->ninl_len;
if (inet_aton(ip_nl_p->ninl_val[which_ip], client_ip) == 0)
continue;
if (has_binding)
*has_binding = TRUE;
if (func == NULL || (*func)(arg, *client_ip)) {
PLCache_make_head(PLCache, scan);
return (scan);
}
}
}
}
return (NULL);
}
PLCacheEntry_t *
PLCache_lookup_ip(PLCache_t * PLCache, struct in_addr iaddr)
{
PLCacheEntry_t * scan;
for (scan = PLCache->head; scan; scan = scan->next) {
int n;
int ip_index;
ni_namelist * ip_nl_p;
ip_index = ni_proplist_match(scan->pl, NIPROP_IPADDR, NULL);
if (ip_index == NI_INDEX_NULL)
continue;
ip_nl_p = &scan->pl.nipl_val[ip_index].nip_val;
for (n = 0; n < ip_nl_p->ninl_len; n++) {
struct in_addr entry_ip;
if (inet_aton(ip_nl_p->ninl_val[n], &entry_ip) == 0)
continue;
if (iaddr.s_addr == entry_ip.s_addr) {
PLCache_make_head(PLCache, scan);
return (scan);
}
}
}
return (NULL);
}
#ifdef READ_TEST
int
main(int argc, char * argv[])
{
PLCache_t PLCache;
if (argc < 2)
exit(1);
PLCache_init(&PLCache);
PLCache_set_max(&PLCache, 100 * 1024 * 1024);
S_timestamp("before read");
if (PLCache_read(&PLCache, argv[1]) == TRUE) {
S_timestamp("after read");
PLCache_print(&PLCache);
printf("writing /tmp/readtest.out\n");
PLCache_write(&PLCache, "/tmp/readtest.out");
}
else {
S_timestamp("after read");
printf("PLCache_read failed\n");
}
exit(0);
}
#endif READ_TEST