network_information_priv.c [plain text]
#include <arpa/inet.h>
#include <assert.h>
#include <notify.h>
#include <string.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <stdbool.h>
#include "network_information_priv.h"
#include <limits.h>
#include <stdio.h>
__private_extern__
const 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;
dest->svr = FALSE;
}
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;
}
bzero(state, new_size);
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;
state->svr = FALSE;
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;
assert(state != NULL);
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_ifstate_set_signature(nwi_ifstate_t ifstate, uint8_t * signature)
{
bcopy(signature, ifstate->signature, sizeof(ifstate->signature));
ifstate->flags |= NWI_IFSTATE_FLAGS_HAS_SIGNATURE;
return;
}
__private_extern__
nwi_ifstate_t
nwi_insert_ifstate(nwi_state_t state,
const char * ifname, int af,
uint64_t flags, Rank rank,
void * ifa,
struct sockaddr * vpn_server_addr,
uint32_t reach_flags)
{
nwi_ifstate_t ifstate;
ifstate = nwi_state_get_ifstate_with_name(state, af, ifname);
if (ifstate != NULL) {
if (ifstate->rank < rank) {
return NULL;
}
}
if (ifstate == NULL) {
uint32_t *last;
ifstate = nwi_ifstate_get_last(state, af, &last);
bzero(ifstate, sizeof(*ifstate));
strlcpy(ifstate->ifname, ifname, sizeof(ifstate->ifname));
ifstate->af = af;
(*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;
}
}
if (vpn_server_addr != NULL && vpn_server_addr->sa_family != 0) {
_nwi_ifstate_set_vpn_server(ifstate, vpn_server_addr);
} else {
_nwi_ifstate_set_vpn_server(ifstate, NULL);
}
ifstate->reach_flags = reach_flags;
ifstate->rank = rank;
ifstate->flags = flags;
return ifstate;
}
__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;
}
#define unchanged ""
#define added "+"
#define deleted "-"
#define changed "!"
#define rank_change "R"
__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)
{
if (strcmp(ifstate->diff_str, rank_change) == 0) {
return changed;
}
return ifstate->diff_str;
}
static
inline
boolean_t
nwi_ifstate_has_changed(nwi_ifstate_t ifstate1, nwi_ifstate_t ifstate2)
{
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 *diff_str)
{
ifstate->diff_str = diff_str;
}
static
void
nwi_ifstate_set_added_or_changed_str(nwi_state_t state, nwi_state_t old_state, nwi_ifstate_t ifstate)
{
nwi_ifstate_t existing_ifstate, new_ifstate;
existing_ifstate = nwi_state_get_ifstate_with_name(old_state,
ifstate->af,
nwi_ifstate_get_ifname(ifstate));
new_ifstate = nwi_ifstate_append(state, ifstate);
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 if (existing_ifstate->rank != new_ifstate->rank) {
nwi_ifstate_set_diff_str(new_ifstate, rank_change);
} else {
nwi_ifstate_set_diff_str(new_ifstate, unchanged);
}
}
return;
}
static
void
nwi_ifstate_set_removed_str(nwi_state_t state, nwi_ifstate_t ifstate)
{
nwi_ifstate_t existing_ifstate;
existing_ifstate = nwi_state_get_ifstate_with_name(state,
ifstate->af,
nwi_ifstate_get_ifname(ifstate));
if (existing_ifstate == NULL) {
nwi_ifstate_t new_ifstate = nwi_ifstate_append(state, ifstate);
nwi_ifstate_set_diff_str(new_ifstate, deleted);
}
return;
}
static
void
nwi_state_merge_added(nwi_state_t state, nwi_state_t old_state,
nwi_state_t new_state)
{
int i;
nwi_ifstate_t scan;
if (new_state == NULL) {
return;
}
if (new_state->ipv4_count) {
for (i = 0, scan = new_state->nwi_ifstates;
i < new_state->ipv4_count; i++, scan++) {
nwi_ifstate_set_added_or_changed_str(state, old_state, scan);
}
nwi_state_set_last(state, AF_INET);
}
if (new_state->ipv6_count) {
for (i = 0, scan = new_state->nwi_ifstates + new_state->ipv6_start;
i < new_state->ipv6_count; i++, scan++) {
nwi_ifstate_set_added_or_changed_str(state, old_state, scan);
}
nwi_state_set_last(state, AF_INET6);
}
return;
}
void
nwi_state_merge_removed(nwi_state_t state, nwi_state_t old_state)
{
int i;
nwi_ifstate_t scan;
if (old_state == NULL) {
return;
}
if (old_state->ipv4_count) {
for (i = 0, scan = old_state->nwi_ifstates;
i < old_state->ipv4_count; i++, scan++) {
nwi_ifstate_set_removed_str(state, scan);
}
nwi_state_set_last(state, AF_INET);
}
if (old_state->ipv6_count) {
for (i = 0, scan = old_state->nwi_ifstates + old_state->ipv6_start;
i < old_state->ipv6_count; i++, scan++) {
nwi_ifstate_set_removed_str(state, scan);
}
nwi_state_set_last(state, AF_INET6);
}
return;
}
__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;
}
static __inline__
void
_nwi_ifstate_set_generation(nwi_ifstate_t ifstate, uint64_t generation_count)
{
ifstate->if_generation_count = generation_count;
return;
}
static
boolean_t
_nwi_ifstate_has_changed(nwi_state_t state, const char * ifname)
{
nwi_ifstate_t ifstate;
ifstate = nwi_state_get_ifstate_with_name(state,
AF_INET,
ifname);
if (ifstate != NULL
&& ifstate->diff_str != NULL
&& strcmp(ifstate->diff_str, unchanged) != 0
&& strcmp(ifstate->diff_str, rank_change) != 0) {
return (TRUE);
}
ifstate = nwi_state_get_ifstate_with_name(state,
AF_INET6,
ifname);
if (ifstate != NULL
&& ifstate->diff_str != NULL
&& strcmp(ifstate->diff_str, unchanged) != 0
&& strcmp(ifstate->diff_str, rank_change) != 0) {
return (TRUE);
}
return (FALSE);
}
__private_extern__
void
_nwi_state_update_interface_generations(nwi_state_t old_state, nwi_state_t state, nwi_state_t changes)
{
int i;
uint64_t generation_count;
nwi_ifstate_t scan;
if (state == NULL || changes == NULL) {
return;
}
generation_count = state->generation_count;
if (state->ipv4_count) {
for (i = 0, scan = state->nwi_ifstates;
i < state->ipv4_count; i++, scan++) {
if (_nwi_ifstate_has_changed(changes, scan->ifname) == TRUE) {
_nwi_ifstate_set_generation(scan, generation_count);
} else {
nwi_ifstate_t old_ifstate;
old_ifstate = nwi_state_get_ifstate_with_name(old_state,
AF_INET,
scan->ifname);
_nwi_ifstate_set_generation(scan,
old_ifstate->if_generation_count);
}
}
}
if (state->ipv6_count) {
for (i = 0, scan = state->nwi_ifstates + state->ipv6_start;
i < state->ipv6_count; i++, scan++) {
if (nwi_ifstate_get_generation(scan) ==
generation_count) {
continue;
}
if (_nwi_ifstate_has_changed(changes, scan->ifname) == TRUE) {
_nwi_ifstate_set_generation(scan, generation_count);
} else {
nwi_ifstate_t old_ifstate;
old_ifstate = nwi_state_get_ifstate_with_name(old_state,
AF_INET6,
scan->ifname);
assert(old_ifstate != NULL);
_nwi_ifstate_set_generation(scan,
old_ifstate->if_generation_count);
}
}
}
return;
}