326 lines
7.4 KiB
C
326 lines
7.4 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <arpa/inet.h>
|
|
#include <net/if.h>
|
|
|
|
#include <linux/socket.h>
|
|
#include <linux/types.h>
|
|
#include <linux/netlink.h>
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include "list.h"
|
|
#include "common.h"
|
|
#include "nwstatus.h"
|
|
|
|
typedef struct {
|
|
List *interfaces;
|
|
List *ipv4;
|
|
List *ipv6;
|
|
} netconfig_t;
|
|
|
|
static netconfig_t *nstatus = NULL;
|
|
|
|
static void sort_addresses(List *list);
|
|
|
|
void
|
|
netstatus_init(void) {
|
|
if (nstatus == NULL) {
|
|
nstatus = malloc(sizeof(netconfig_t));
|
|
nstatus->interfaces = new_list();
|
|
nstatus->ipv4 = new_list();
|
|
nstatus->ipv6 = new_list();
|
|
}
|
|
}
|
|
|
|
static int
|
|
find_interface(void *data, void *user_data) {
|
|
return ((interface_t *)data)->index == *((int *)user_data);
|
|
}
|
|
|
|
static interface_t *
|
|
new_interface(int index) {
|
|
interface_t *result;
|
|
result = malloc(sizeof(interface_t));
|
|
result->name = NULL;
|
|
result->index = index;
|
|
result->has_link = -1;
|
|
list_append(nstatus->interfaces, result);
|
|
return result;
|
|
}
|
|
|
|
static interface_t *
|
|
get_interface(index) {
|
|
interface_t *result;
|
|
result = list_search(nstatus->interfaces, find_interface, &index);
|
|
if (result == NULL) {
|
|
result = new_interface(index);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void
|
|
interface_set_name(int index, char *name) {
|
|
interface_t *interface;
|
|
interface = get_interface(index);
|
|
free(interface->name);
|
|
interface->name = strdup(name);
|
|
}
|
|
|
|
void
|
|
interface_set_link(int index, int has_link) {
|
|
interface_t *interface;
|
|
interface = get_interface(index);
|
|
interface->has_link = has_link;
|
|
}
|
|
|
|
void
|
|
interface_set_flags(int index, unsigned int flags) {
|
|
interface_t *interface;
|
|
interface = get_interface(index);
|
|
interface->flags = flags;
|
|
}
|
|
|
|
int
|
|
interface_get_link(int index) {
|
|
return get_interface(index)->has_link;
|
|
}
|
|
|
|
char *interface_get_name(int index) {
|
|
return get_interface(index)->name;
|
|
}
|
|
|
|
static int
|
|
do_del_address(void *data, void *user_data) {
|
|
address_t *a;
|
|
a = (address_t *)data;
|
|
if (a->interface == user_data) {
|
|
switch (a->family) {
|
|
case AF_INET:
|
|
list_del(nstatus->ipv4, a);
|
|
break;
|
|
case AF_INET6:
|
|
list_del(nstatus->ipv6, a);
|
|
break;
|
|
}
|
|
free(a);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void del_interface(int index) {
|
|
interface_t *interface;
|
|
interface = get_interface(index);
|
|
list_foreach(nstatus->ipv4, do_del_address, interface);
|
|
list_foreach(nstatus->ipv6, do_del_address, interface);
|
|
list_del(nstatus->interfaces, interface);
|
|
sort_addresses(nstatus->ipv4);
|
|
sort_addresses(nstatus->ipv6);
|
|
}
|
|
|
|
static int
|
|
find_v4(void *data, void *user_data) {
|
|
address_t *a;
|
|
a = (address_t *)data;
|
|
return a->a.v4.s_addr == ((struct in_addr *) user_data)->s_addr;
|
|
}
|
|
|
|
static int
|
|
find_v6(void *data, void *user_data) {
|
|
address_t *a;
|
|
struct in6_addr *addr;
|
|
int i;
|
|
|
|
a = (address_t *)data;
|
|
addr = (struct in6_addr *)user_data;
|
|
for (i = 0 ; i < 4 ; i++) {
|
|
if (a->a.v6.s6_addr32[i] != addr->s6_addr32[i]) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
add_address(int index, uint8_t scope, uint8_t family, void *addr) {
|
|
address_t *a = NULL;
|
|
switch (family) {
|
|
case AF_INET:
|
|
a = list_search(nstatus->ipv4, find_v4, addr);
|
|
break;
|
|
case AF_INET6:
|
|
a = list_search(nstatus->ipv6, find_v6, addr);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
if (a == NULL) {
|
|
a = malloc(sizeof(address_t));
|
|
a->family = family;
|
|
a->interface = get_interface(index);
|
|
a->scope = scope;
|
|
a->current = 0;
|
|
switch (family) {
|
|
case AF_INET:
|
|
a->a.v4 = *(struct in_addr*)addr;
|
|
list_append(nstatus->ipv4, a);
|
|
sort_addresses(nstatus->ipv4);
|
|
break;
|
|
case AF_INET6:
|
|
a->a.v6 = *(struct in6_addr *)addr;
|
|
list_append(nstatus->ipv6, a);
|
|
sort_addresses(nstatus->ipv6);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
del_address(int index, uint8_t family, void *addr) {
|
|
address_t *a = NULL;
|
|
switch (family) {
|
|
case AF_INET:
|
|
a = list_search(nstatus->ipv4, find_v4, addr);
|
|
list_del(nstatus->ipv4, a);
|
|
sort_addresses(nstatus->ipv4);
|
|
break;
|
|
case AF_INET6:
|
|
a = list_search(nstatus->ipv6, find_v6, addr);
|
|
list_del(nstatus->ipv6, a);
|
|
sort_addresses(nstatus->ipv6);
|
|
break;
|
|
}
|
|
free(a);
|
|
}
|
|
|
|
static
|
|
int v4address_type(address_t *addr) {
|
|
#define IN_SUBNET(i0,i1,i2,i3, mask) \
|
|
!(((ntohl(addr->a.v4.s_addr)) - \
|
|
((i0 << 24) + (i1 << 16) + (i2 << 8) + (i3))) \
|
|
& (0xffffffff << (32 - mask)))
|
|
#define NORMAL 0
|
|
#define PRIVATE 1
|
|
#define ZEROCONF 2
|
|
|
|
if (IN_SUBNET(168,254,0,0, 16)) {
|
|
return ZEROCONF;
|
|
}
|
|
if (IN_SUBNET(10,0,0,0, 8)
|
|
|| IN_SUBNET(172,16,0,0, 12)
|
|
|| IN_SUBNET(192,168,0,0, 16)) {
|
|
return PRIVATE;
|
|
}
|
|
|
|
return NORMAL;
|
|
}
|
|
|
|
|
|
static int print_address(void *data, void *user_data);
|
|
int
|
|
address_cmp(void *data0, void *data1, void *user_data) {
|
|
address_t *a0 = (address_t *)data0;
|
|
address_t *a1 = (address_t *)data1;
|
|
/* We want scope global/universe in front */
|
|
if (a0->scope != RT_SCOPE_UNIVERSE || a1->scope != RT_SCOPE_UNIVERSE) {
|
|
return (a1->scope == RT_SCOPE_UNIVERSE ? 1 : 0) -
|
|
(a0->scope == RT_SCOPE_UNIVERSE ? 1 : 0);
|
|
}
|
|
/* Address with link up before addresses without a link */
|
|
if (!a0->interface->has_link || !a1->interface->has_link) {
|
|
return (a1->interface->has_link ? 1 : 0) -
|
|
(a0->interface->has_link ? 1 : 0);
|
|
}
|
|
/* Prefer to first have normal, then private then zeroconf addresses */
|
|
if (a0->family == AF_INET) {
|
|
int a0_type = v4address_type(a0);
|
|
int a1_type = v4address_type(a1);
|
|
if (a0_type != a1_type) {
|
|
return a0_type - a1_type;
|
|
}
|
|
}
|
|
return a1->current - a0->current;
|
|
}
|
|
|
|
static void
|
|
sort_addresses(List *list) {
|
|
list_sort(list, address_cmp, NULL);
|
|
}
|
|
|
|
static
|
|
address_t *get_best_address(List *list) {
|
|
List_ptr *p;
|
|
address_t *result = NULL;
|
|
p = new_list_ptr(list);
|
|
if (list_ptr_first(p)) {
|
|
result = (address_t *)list_ptr_get_data(p);
|
|
if (result->scope != RT_SCOPE_UNIVERSE
|
|
|| !result->interface->has_link) {
|
|
result = NULL;
|
|
}
|
|
}
|
|
del_list_ptr(p);
|
|
return result;
|
|
}
|
|
|
|
address_t *get_best_v4_address(void) {
|
|
return get_best_address(nstatus->ipv4);
|
|
}
|
|
|
|
address_t *get_best_v6_address(void) {
|
|
return get_best_address(nstatus->ipv6);
|
|
}
|
|
|
|
static int
|
|
unset_current_address(void *data, void *user_data) {
|
|
((address_t *)data)->current = FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
void address_toggle_current(address_t *address) {
|
|
switch (address->family) {
|
|
case AF_INET:
|
|
list_foreach(nstatus->ipv4, unset_current_address, NULL);
|
|
break;
|
|
case AF_INET6:
|
|
list_foreach(nstatus->ipv6, unset_current_address, NULL);
|
|
break;
|
|
}
|
|
address->current = TRUE;
|
|
}
|
|
|
|
static int
|
|
print_interfaces(void *data, void *user_data) {
|
|
interface_t *i;
|
|
i = (interface_t *)data;
|
|
printf("%s (%s, %x)\n", i->name, i->has_link ? "UP" : "DOWN", i->flags);
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
print_address(void *data, void *user_data) {
|
|
char buffer[INET6_ADDRSTRLEN];
|
|
address_t *a;
|
|
a = (address_t *)data;
|
|
if (a == NULL) {
|
|
return TRUE;
|
|
}
|
|
|
|
inet_ntop(a->family, &(a->a.v4), buffer, INET6_ADDRSTRLEN);
|
|
printf("\t%s (%d): %s\n", a->interface->name, a->interface->index, buffer);
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
print_status(void) {
|
|
list_foreach(nstatus->interfaces, print_interfaces, NULL);
|
|
list_foreach(nstatus->ipv4, print_address, NULL);
|
|
list_foreach(nstatus->ipv6, print_address, NULL);
|
|
printf("Choosen addresses:\n");
|
|
print_address(get_best_v4_address(), NULL);
|
|
print_address(get_best_v6_address(), NULL);
|
|
}
|