network_information_priv.c [plain text]
#include <arpa/inet.h>
#include <notify.h>
#include <string.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <syslog.h>
#include <stdbool.h>
#include "network_information_priv.h"
#include <limits.h>
sa_family_t nwi_af_list[] = {AF_INET, AF_INET6};
static __inline__ unsigned int
nwi_state_compute_size(unsigned int n)
{
return (offsetof(nwi_state, nwi_ifstates[n]));
}
__private_extern__
nwi_state_t
nwi_state_copy_priv(nwi_state_t src)
{
nwi_state_t dest = NULL;
if (src == NULL) {
return dest;
}
dest = malloc(src->size);
if (dest != NULL) {
bcopy(src, dest, src->size);
dest->ref = 1;
}
return dest;
}
__private_extern__
nwi_state_t
nwi_state_new(nwi_state_t old_state, int elems)
{
nwi_state_t state = NULL;
int new_size;
if (old_state == NULL && elems == 0) {
return NULL;
}
new_size = (elems != 0)?
(sizeof(nwi_state) + nwi_state_compute_size((elems+1) * 2)):0;
if (old_state != NULL) {
if (old_state->size >= new_size) {
return (old_state);
}
}
state = malloc(new_size);
if (state == NULL) {
return NULL;
}
state->size = new_size;
state->ipv6_start = elems + 1;
if (old_state != NULL) {
state->ipv6_count = old_state->ipv6_count;
if (state->ipv6_count > 0) {
bcopy((void*) &old_state->nwi_ifstates[old_state->ipv6_start],
(void*) &state->nwi_ifstates[state->ipv6_start],
old_state->ipv6_count * sizeof(nwi_ifstate));
}
state->ipv4_count = old_state->ipv4_count;
if (state->ipv4_count > 0) {
bcopy((void*) old_state->nwi_ifstates,
(void*) state->nwi_ifstates,
old_state->ipv4_count * sizeof(nwi_ifstate));
}
free(old_state);
} else {
state->ipv4_count = 0;
state->ipv6_count = 0;
}
nwi_state_set_last(state, AF_INET);
nwi_state_set_last(state, AF_INET6);
state->ref = 1;
return state;
}
static inline
nwi_ifstate_t nwi_ifstate_get_last(nwi_state_t state, int af, uint32_t** last)
{
uint32_t* count;
int idx;
count = (af == AF_INET)
?&state->ipv4_count:&state->ipv6_count;
idx = (af == AF_INET)
?state->ipv4_count:(state->ipv6_start + state->ipv6_count);
*last = count;
return &state->nwi_ifstates[idx];
}
__private_extern__
void
nwi_insert_ifstate(nwi_state_t state,
const char* ifname, int af,
uint64_t flags, Rank rank,
void* ifa)
{
nwi_ifstate_t ifstate;
ifstate = nwi_state_get_ifstate_with_name(state, af, ifname);
if (ifstate != NULL) {
if (ifstate->rank < rank) {
return;
}
}
if (ifstate == NULL) {
uint32_t *last;
ifstate = nwi_ifstate_get_last(state, af, &last);
strcpy(ifstate->ifname, ifname);
ifstate->af_alias = NULL;
ifstate->af = af;
ifstate->diff_ch = NULL;
(*last)++;
}
if (ifa != NULL) {
switch (af) {
case AF_INET:
ifstate->iaddr = *((struct in_addr *) ifa);
break;
case AF_INET6:
ifstate->iaddr6 = *((struct in6_addr *) ifa);
break;
default:
break;
}
}
ifstate->rank = rank;
ifstate->flags = flags;
return;
}
__private_extern__
void
nwi_state_clear(nwi_state_t state, int af)
{
uint32_t* count;
count = (af == AF_INET)
?&state->ipv4_count:&state->ipv6_count;
*count = 0;
nwi_state_set_last(state, af);
return;
}
__private_extern__
void
nwi_state_set_last(nwi_state_t state, int af)
{
int last_elem_idx;
if (state == NULL) {
return;
}
last_elem_idx = (af == AF_INET)
?state->ipv4_count
:(state->ipv6_start + state->ipv6_count);
state->nwi_ifstates[last_elem_idx].ifname[0] = '\0';
state->nwi_ifstates[last_elem_idx].flags
|= NWI_IFSTATE_FLAGS_NOT_IN_LIST;
}
__private_extern__
void
_nwi_state_dump(int level, nwi_state_t state)
{
const char * addr_str;
void * address;
int i;
char ntopbuf[INET6_ADDRSTRLEN];
nwi_ifstate_t scan;
if (state == NULL) {
syslog(level, "<empty nwi_state>");
return;
}
syslog(level, "nwi_state = { gen = %llu size = %u #ipv4 = %u #ipv6 = %u }",
state->generation_count,
state->size,
state->ipv4_count,
state->ipv6_count);
if (state->ipv4_count) {
syslog(level, "IPv4:");
for (i = 0, scan = state->nwi_ifstates;
i < state->ipv4_count; i++, scan++) {
bool has_dns = (scan->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0;
bool never = (scan->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) != 0;
address = nwi_ifstate_get_address(scan);
addr_str = inet_ntop(scan->af, address, ntopbuf, sizeof(ntopbuf));
syslog(level, " [%d]: %s%s%s%s rank %u iaddr: %s " ,
i, scan->ifname, scan->diff_ch != NULL?scan->diff_ch:"",
has_dns ? " dns" : "",
never ? " never" : "",
scan->rank,
addr_str);
}
}
if (state->ipv6_count) {
syslog(level, "IPv6:");
for (i = 0, scan = state->nwi_ifstates + state->ipv6_start;
i < state->ipv6_count; i++, scan++) {
bool has_dns = (scan->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0;
bool never = (scan->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) != 0;
address = nwi_ifstate_get_address(scan);
addr_str = inet_ntop(scan->af, address, ntopbuf, sizeof(ntopbuf));
syslog(level, " [%d]: %s%s%s%s rank %u iaddr6: %s ",
i, scan->ifname, scan->diff_ch != NULL?scan->diff_ch:"",
has_dns ? " dns" : "",
never ? " never" : "",
scan->rank,
addr_str);
}
}
return;
}
#define unchanged ""
#define added "+"
#define deleted "-"
#define changed "!"
__private_extern__
void *
nwi_ifstate_get_address(nwi_ifstate_t ifstate)
{
return (void *)&ifstate->iaddr;
}
__private_extern__
const char *
nwi_ifstate_get_diff_str(nwi_ifstate_t ifstate)
{
return ifstate->diff_ch;
}
static
inline
boolean_t
nwi_ifstate_has_changed(nwi_ifstate_t ifstate1, nwi_ifstate_t ifstate2)
{
if (ifstate1->rank != ifstate2->rank) {
return TRUE;
}
if (ifstate1->flags != ifstate2->flags) {
return TRUE;
}
if (ifstate1->af == AF_INET) {
if (memcmp(&ifstate1->iaddr, &ifstate2->iaddr, sizeof(struct in_addr)) != 0) {
return TRUE;
}
} else {
if (memcmp(&ifstate1->iaddr6, &ifstate2->iaddr6, sizeof(struct in6_addr)) != 0) {
return TRUE;
}
}
return FALSE;
}
static
inline
nwi_ifstate_t
nwi_ifstate_append(nwi_state_t state, nwi_ifstate_t scan)
{
nwi_ifstate_t new_ifstate = NULL;
uint32_t *last;
new_ifstate = nwi_ifstate_get_last(state, scan->af, &last);
memcpy(new_ifstate, scan, sizeof(*scan));
(*last)++;
return new_ifstate;
}
static
inline
void
nwi_ifstate_set_diff_str(nwi_ifstate_t ifstate, const char * ch)
{
ifstate->diff_ch = ch;
}
static
void
nwi_state_merge_added(nwi_state_t state, nwi_state_t old_state,
nwi_state_t new_state)
{
int idx;
nwi_ifstate_t scan;
for (idx = 0; idx < sizeof(nwi_af_list)/sizeof(nwi_af_list[0]); idx++) {
scan = nwi_state_get_first_ifstate(new_state, nwi_af_list[idx]);
while (scan != NULL) {
nwi_ifstate_t existing_ifstate, new_ifstate;
const char* ifname;
ifname = nwi_ifstate_get_ifname(scan);
existing_ifstate = nwi_state_get_ifstate_with_name(old_state, scan->af, ifname);
new_ifstate = nwi_ifstate_append(state, scan);
nwi_ifstate_set_diff_str(new_ifstate, added);
if (existing_ifstate != NULL) {
if (nwi_ifstate_has_changed(existing_ifstate, new_ifstate) == TRUE) {
nwi_ifstate_set_diff_str(new_ifstate, changed);
} else {
nwi_ifstate_set_diff_str(new_ifstate, unchanged);
}
}
scan = nwi_ifstate_get_next(scan, scan->af);
}
nwi_state_set_last(state, nwi_af_list[idx]);
}
return;
}
static
void
nwi_state_merge_removed(nwi_state_t state, nwi_state_t old_state)
{
int idx;
nwi_ifstate_t scan;
for (idx = 0; idx < sizeof(nwi_af_list)/sizeof(nwi_af_list[0]); idx++) {
scan = nwi_state_get_first_ifstate(old_state, nwi_af_list[idx]);
while (scan != NULL) {
nwi_ifstate_t existing_ifstate;
const char* ifname;
ifname = nwi_ifstate_get_ifname(scan);
existing_ifstate = nwi_state_get_ifstate_with_name(state, scan->af, ifname);
if (existing_ifstate == NULL) {
nwi_ifstate_t new_ifstate = nwi_ifstate_append(state, scan);
nwi_ifstate_set_diff_str(new_ifstate, deleted);
}
scan = nwi_ifstate_get_next(scan, scan->af);
}
nwi_state_set_last(state, nwi_af_list[idx]);
}
}
__private_extern__
nwi_state_t
nwi_state_diff(nwi_state_t old_state, nwi_state_t new_state)
{
nwi_state_t diff;
int total_count = 0;
if (old_state != NULL) {
total_count = old_state->ipv4_count + old_state->ipv6_count;
}
if (new_state != NULL) {
total_count += new_state->ipv4_count + new_state->ipv6_count;
}
if (total_count == 0) {
return NULL;
}
diff = nwi_state_new(NULL, total_count);
nwi_state_merge_added(diff, old_state, new_state);
nwi_state_merge_removed(diff, old_state);
return diff;
}