initial support for IPv6 (more testing needed)
socket works (without v6 filter)
traffic selector handle IPv4/v4 cleanly
improvements in traffic selector code
kernel interface accepts v6 traffic selectors and hosts
host_t class has full IPv6 support
This commit is contained in:
@@ -53,17 +53,27 @@ struct private_traffic_selector_t {
|
||||
u_int8_t protocol;
|
||||
|
||||
/**
|
||||
* begin of address range, host order
|
||||
* begin of address range, network order
|
||||
*/
|
||||
union {
|
||||
u_int32_t from_addr_ipv4;
|
||||
/** dummy char for common address manipulation */
|
||||
char from[0];
|
||||
/** IPv4 address */
|
||||
u_int32_t from4[1];
|
||||
/** IPv6 address */
|
||||
u_int32_t from6[4];
|
||||
};
|
||||
|
||||
/**
|
||||
* end of address range, host order
|
||||
* end of address range, network order
|
||||
*/
|
||||
union {
|
||||
u_int32_t to_addr_ipv4;
|
||||
/** dummy char for common address manipulation */
|
||||
char to[0];
|
||||
/** IPv4 address */
|
||||
u_int32_t to4[1];
|
||||
/** IPv6 address */
|
||||
u_int32_t to6[4];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -82,6 +92,60 @@ struct private_traffic_selector_t {
|
||||
char *string;
|
||||
};
|
||||
|
||||
/**
|
||||
* calculate to "to"-address for the "from" address and a subnet size
|
||||
*/
|
||||
static void calc_range(private_traffic_selector_t *this, u_int8_t netbits)
|
||||
{
|
||||
int byte;
|
||||
size_t size = (this->type == TS_IPV4_ADDR_RANGE) ? 4 : 16;
|
||||
|
||||
/* go through the from address, starting at the tail. While we
|
||||
* have not processed the bits belonging to the host, set them to 1 on
|
||||
* the to address. If we reach the bits for the net, copy them from "from". */
|
||||
for (byte = size - 1; byte >=0; byte--)
|
||||
{
|
||||
u_char mask = 0x00;
|
||||
int shift;
|
||||
|
||||
shift = (byte+1) * 8 - netbits;
|
||||
if (shift > 0)
|
||||
{
|
||||
mask = 1 << shift;
|
||||
if (mask != 0xFF)
|
||||
{
|
||||
mask--;
|
||||
}
|
||||
}
|
||||
this->to[byte] = this->from[byte] | mask;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* calculate to subnet size from "to"- and "from"-address
|
||||
*/
|
||||
static u_int8_t calc_netbits(private_traffic_selector_t *this)
|
||||
{
|
||||
int byte, bit;
|
||||
size_t size = (this->type == TS_IPV4_ADDR_RANGE) ? 4 : 16;
|
||||
|
||||
/* go trough all bits of the addresses, begging in the front.
|
||||
* As longer as they equal, the subnet gets larger */
|
||||
for (byte = 0; byte < size; byte++)
|
||||
{
|
||||
for (bit = 7; bit >= 0; bit--)
|
||||
{
|
||||
if ((1<<bit & this->from[byte]) != (1<<bit & this->to[byte]))
|
||||
{
|
||||
return ((7 - bit) + (byte * 8));
|
||||
}
|
||||
}
|
||||
}
|
||||
/* single host, netmask is 32/128 */
|
||||
return (size * 8);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* internal generic constructor
|
||||
*/
|
||||
@@ -105,23 +169,13 @@ static void update_string(private_traffic_selector_t *this)
|
||||
|
||||
if (this->type == TS_IPV4_ADDR_RANGE)
|
||||
{
|
||||
u_int32_t from_no, to_no, bit;
|
||||
u_int8_t mask = 32;
|
||||
u_int8_t mask;
|
||||
|
||||
/* build address string */
|
||||
from_no = htonl(this->from_addr_ipv4);
|
||||
to_no = htonl(this->to_addr_ipv4);
|
||||
inet_ntop(AF_INET, &from_no, addr_str, sizeof(addr_str));
|
||||
inet_ntop(AF_INET, &this->from4, addr_str, sizeof(addr_str));
|
||||
|
||||
/* build network mask string */
|
||||
for (bit = 0; bit < 32; bit++)
|
||||
{
|
||||
if ((1<<bit & from_no) != (1<<bit & to_no))
|
||||
{
|
||||
mask = bit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mask = calc_netbits(this);
|
||||
if (mask != 32)
|
||||
{
|
||||
snprintf(mask_str, sizeof(mask_str), "/%d", mask);
|
||||
@@ -129,8 +183,17 @@ static void update_string(private_traffic_selector_t *this)
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TODO: be a little bit more verbose ;-) */
|
||||
snprintf(addr_str, sizeof(addr_str), "(IPv6 address range)");
|
||||
u_int8_t mask;
|
||||
|
||||
/* build address string */
|
||||
inet_ntop(AF_INET6, &this->from6, addr_str, sizeof(addr_str));
|
||||
|
||||
/* build network mask string */
|
||||
mask = calc_netbits(this);
|
||||
if (mask != 128)
|
||||
{
|
||||
snprintf(mask_str, sizeof(mask_str), "/%d", mask);
|
||||
}
|
||||
}
|
||||
|
||||
/* build protocol string */
|
||||
@@ -208,22 +271,15 @@ static char *get_string(private_traffic_selector_t *this)
|
||||
*/
|
||||
static traffic_selector_t *get_subset(private_traffic_selector_t *this, private_traffic_selector_t *other)
|
||||
{
|
||||
if ((this->type == TS_IPV4_ADDR_RANGE) && (other->type == TS_IPV4_ADDR_RANGE) &&
|
||||
(this->protocol == other->protocol || this->protocol == 0 || other->protocol == 0))
|
||||
if (this->type == other->type && (this->protocol == other->protocol ||
|
||||
this->protocol == 0 || other->protocol == 0))
|
||||
{
|
||||
u_int32_t from_addr, to_addr;
|
||||
u_int16_t from_port, to_port;
|
||||
u_char *from, *to;
|
||||
u_int8_t protocol;
|
||||
size_t size;
|
||||
private_traffic_selector_t *new_ts;
|
||||
|
||||
/* calculate the maximum address range allowed for both */
|
||||
from_addr = max(this->from_addr_ipv4, other->from_addr_ipv4);
|
||||
to_addr = min(this->to_addr_ipv4, other->to_addr_ipv4);
|
||||
if (from_addr > to_addr)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* calculate the maximum port range allowed for both */
|
||||
from_port = max(this->from_port, other->from_port);
|
||||
to_port = min(this->to_port, other->to_port);
|
||||
@@ -231,18 +287,53 @@ static traffic_selector_t *get_subset(private_traffic_selector_t *this, private_
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* select protocol, which is not zero */
|
||||
protocol = max(this->protocol, other->protocol);
|
||||
|
||||
/* got a match, return it */
|
||||
new_ts = traffic_selector_create(protocol, this->type, from_port, to_port);
|
||||
new_ts->from_addr_ipv4 = from_addr;
|
||||
new_ts->to_addr_ipv4 = to_addr;
|
||||
new_ts->type = TS_IPV4_ADDR_RANGE;
|
||||
switch (this->type)
|
||||
{
|
||||
case TS_IPV4_ADDR_RANGE:
|
||||
size = sizeof(this->from4);
|
||||
break;
|
||||
case TS_IPV6_ADDR_RANGE:
|
||||
size = sizeof(this->from6);
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* get higher from-address */
|
||||
if (memcmp(this->from, other->from, size) > 0)
|
||||
{
|
||||
from = this->from;
|
||||
}
|
||||
else
|
||||
{
|
||||
from = other->from;
|
||||
}
|
||||
/* get lower to-address */
|
||||
if (memcmp(this->to, other->to, size) > 0)
|
||||
{
|
||||
to = other->to;
|
||||
}
|
||||
else
|
||||
{
|
||||
to = this->to;
|
||||
}
|
||||
/* if "from" > "to", we don't have a match */
|
||||
if (memcmp(from, to, size) > 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* we have a match in protocol, port, and address: return it... */
|
||||
new_ts = traffic_selector_create(protocol, this->type, from_port, to_port);
|
||||
new_ts->type = this->type;
|
||||
memcpy(new_ts->from, from, size);
|
||||
memcpy(new_ts->to, to, size);
|
||||
update_string(new_ts);
|
||||
|
||||
return &(new_ts->public);
|
||||
return &new_ts->public;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -256,16 +347,28 @@ static bool equals(private_traffic_selector_t *this, private_traffic_selector_t
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (this->type == TS_IPV4_ADDR_RANGE)
|
||||
if (!(this->from_port == other->from_port &&
|
||||
this->to_port == other->to_port &&
|
||||
this->protocol == other->protocol))
|
||||
{
|
||||
if (this->from_addr_ipv4 == other->from_addr_ipv4 &&
|
||||
this->to_addr_ipv4 == other->to_addr_ipv4 &&
|
||||
this->from_port == other->from_port &&
|
||||
this->to_port == other->to_port &&
|
||||
this->protocol == other->protocol)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
switch (this->type)
|
||||
{
|
||||
case TS_IPV4_ADDR_RANGE:
|
||||
if (memeq(this->from4, other->from4, sizeof(this->from4)))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
case TS_IPV6_ADDR_RANGE:
|
||||
if (memeq(this->from6, other->from6, sizeof(this->from6)))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
@@ -275,26 +378,26 @@ static bool equals(private_traffic_selector_t *this, private_traffic_selector_t
|
||||
*/
|
||||
static chunk_t get_from_address(private_traffic_selector_t *this)
|
||||
{
|
||||
chunk_t from_addr = CHUNK_INITIALIZER;
|
||||
chunk_t from = CHUNK_INITIALIZER;
|
||||
|
||||
switch (this->type)
|
||||
{
|
||||
case TS_IPV4_ADDR_RANGE:
|
||||
{
|
||||
u_int32_t network;
|
||||
from_addr.len = sizeof(network);
|
||||
from_addr.ptr = malloc(from_addr.len);
|
||||
/* chunk must contain network order, convert! */
|
||||
network = htonl(this->from_addr_ipv4);
|
||||
memcpy(from_addr.ptr, &network, from_addr.len);
|
||||
break;
|
||||
from.len = sizeof(this->from4);
|
||||
from.ptr = malloc(from.len);
|
||||
memcpy(from.ptr, this->from4, from.len);
|
||||
break;
|
||||
}
|
||||
case TS_IPV6_ADDR_RANGE:
|
||||
{
|
||||
from.len = sizeof(this->from6);
|
||||
from.ptr = malloc(from.len);
|
||||
memcpy(from.ptr, this->from6, from.len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return from_addr;
|
||||
return from;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,26 +405,26 @@ static chunk_t get_from_address(private_traffic_selector_t *this)
|
||||
*/
|
||||
static chunk_t get_to_address(private_traffic_selector_t *this)
|
||||
{
|
||||
chunk_t to_addr = CHUNK_INITIALIZER;
|
||||
chunk_t to = CHUNK_INITIALIZER;
|
||||
|
||||
switch (this->type)
|
||||
{
|
||||
case TS_IPV4_ADDR_RANGE:
|
||||
{
|
||||
u_int32_t network;
|
||||
to_addr.len = sizeof(network);
|
||||
to_addr.ptr = malloc(to_addr.len);
|
||||
/* chunk must contain network order, convert! */
|
||||
network = htonl(this->to_addr_ipv4);
|
||||
memcpy(to_addr.ptr, &network, to_addr.len);
|
||||
break;
|
||||
to.len = sizeof(this->to4);
|
||||
to.ptr = malloc(to.len);
|
||||
memcpy(to.ptr, this->to4, to.len);
|
||||
break;
|
||||
}
|
||||
case TS_IPV6_ADDR_RANGE:
|
||||
{
|
||||
to.len = sizeof(this->to6);
|
||||
to.ptr = malloc(to.len);
|
||||
memcpy(to.ptr, this->to6, to.len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return to_addr;
|
||||
return to;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -361,14 +464,23 @@ static u_int8_t get_protocol(private_traffic_selector_t *this)
|
||||
*/
|
||||
static void update_address_range(private_traffic_selector_t *this, host_t *host)
|
||||
{
|
||||
if (host->get_family(host) == AF_INET &&
|
||||
this->type == TS_IPV4_ADDR_RANGE)
|
||||
if (host->get_family(host) == AF_INET && this->type == TS_IPV4_ADDR_RANGE)
|
||||
{
|
||||
if (this->from_addr_ipv4 == 0)
|
||||
if (this->from4[0] == 0)
|
||||
{
|
||||
chunk_t from = host->get_address(host);
|
||||
this->from_addr_ipv4 = ntohl(*((u_int32_t*)from.ptr));
|
||||
this->to_addr_ipv4 = this->from_addr_ipv4;
|
||||
memcpy(this->from4, from.ptr, from.len);
|
||||
memcpy(this->to4, from.ptr, from.len);
|
||||
}
|
||||
}
|
||||
if (host->get_family(host) == AF_INET6 && this->type == TS_IPV6_ADDR_RANGE)
|
||||
{
|
||||
if (this->from6[0] == 0 && this->from6[1] == 0 &&
|
||||
this->from6[2] == 0 && this->from6[3] == 0)
|
||||
{
|
||||
chunk_t from = host->get_address(host);
|
||||
memcpy(this->from6, from.ptr, from.len);
|
||||
memcpy(this->to6, from.ptr, from.len);
|
||||
}
|
||||
}
|
||||
update_string(this);
|
||||
@@ -387,16 +499,22 @@ static traffic_selector_t *clone_(private_traffic_selector_t *this)
|
||||
{
|
||||
case TS_IPV4_ADDR_RANGE:
|
||||
{
|
||||
clone->from_addr_ipv4 = this->from_addr_ipv4;
|
||||
clone->to_addr_ipv4 = this->to_addr_ipv4;
|
||||
memcpy(clone->from4, this->from4, sizeof(this->from4));
|
||||
memcpy(clone->to4, this->to4, sizeof(this->to4));
|
||||
update_string(clone);
|
||||
return &clone->public;
|
||||
}
|
||||
case TS_IPV6_ADDR_RANGE:
|
||||
{
|
||||
memcpy(clone->from6, this->from6, sizeof(this->from6));
|
||||
memcpy(clone->to6, this->to6, sizeof(this->to6));
|
||||
update_string(clone);
|
||||
return &clone->public;
|
||||
}
|
||||
default:
|
||||
{
|
||||
free(this);
|
||||
return NULL;
|
||||
/* unreachable */
|
||||
return &clone->public;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -413,7 +531,7 @@ static void destroy(private_traffic_selector_t *this)
|
||||
/*
|
||||
* see header
|
||||
*/
|
||||
traffic_selector_t *traffic_selector_create_from_bytes(u_int8_t protocol, ts_type_t type, chunk_t from_addr, u_int16_t from_port, chunk_t to_addr, u_int16_t to_port)
|
||||
traffic_selector_t *traffic_selector_create_from_bytes(u_int8_t protocol, ts_type_t type, chunk_t from, u_int16_t from_port, chunk_t to, u_int16_t to_port)
|
||||
{
|
||||
private_traffic_selector_t *this = traffic_selector_create(protocol, type, from_port, to_port);
|
||||
|
||||
@@ -421,17 +539,26 @@ traffic_selector_t *traffic_selector_create_from_bytes(u_int8_t protocol, ts_typ
|
||||
{
|
||||
case TS_IPV4_ADDR_RANGE:
|
||||
{
|
||||
if (from_addr.len != 4 || to_addr.len != 4)
|
||||
if (from.len != 4 || to.len != 4)
|
||||
{
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
/* chunk contains network order, convert! */
|
||||
this->from_addr_ipv4 = ntohl(*((u_int32_t*)from_addr.ptr));
|
||||
this->to_addr_ipv4 = ntohl(*((u_int32_t*)to_addr.ptr));
|
||||
memcpy(this->from4, from.ptr, from.len);
|
||||
memcpy(this->to4, to.ptr, to.len);
|
||||
break;
|
||||
}
|
||||
case TS_IPV6_ADDR_RANGE:
|
||||
{
|
||||
if (from.len != 16 || to.len != 16)
|
||||
{
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(this->from6, from.ptr, from.len);
|
||||
memcpy(this->to6, to.ptr, to.len);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
free(this);
|
||||
@@ -459,23 +586,44 @@ traffic_selector_t *traffic_selector_create_from_subnet(host_t *net, u_int8_t ne
|
||||
|
||||
this->type = TS_IPV4_ADDR_RANGE;
|
||||
from = net->get_address(net);
|
||||
this->from_addr_ipv4 = ntohl(*((u_int32_t*)from.ptr));
|
||||
if (this->from_addr_ipv4 == 0)
|
||||
memcpy(this->from4, from.ptr, from.len);
|
||||
if (this->from4[0] == 0)
|
||||
{
|
||||
/* use /0 for 0.0.0.0 */
|
||||
this->to_addr_ipv4 = ~0;
|
||||
this->to4[0] = ~0;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->to_addr_ipv4 = this->from_addr_ipv4 | ((1 << (32 - netbits)) - 1);
|
||||
calc_range(this, netbits);
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case AF_INET6:
|
||||
{
|
||||
chunk_t from;
|
||||
|
||||
this->type = TS_IPV6_ADDR_RANGE;
|
||||
from = net->get_address(net);
|
||||
memcpy(this->from6, from.ptr, from.len);
|
||||
if (this->from6[0] == 0 && this->from6[1] == 0 &&
|
||||
this->from6[2] == 0 && this->from6[3] == 0)
|
||||
{
|
||||
/* use /0 for ::0 */
|
||||
this->to6[0] = ~0;
|
||||
this->to6[1] = ~0;
|
||||
this->to6[2] = ~0;
|
||||
this->to6[3] = ~0;
|
||||
}
|
||||
else
|
||||
{
|
||||
calc_range(this, netbits);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
free(this);
|
||||
return NULL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (port)
|
||||
@@ -505,25 +653,31 @@ traffic_selector_t *traffic_selector_create_from_string(u_int8_t protocol, ts_ty
|
||||
{
|
||||
case TS_IPV4_ADDR_RANGE:
|
||||
{
|
||||
if (inet_aton(from_addr, (struct in_addr*)&(this->from_addr_ipv4)) == 0)
|
||||
if (inet_pton(AF_INET, from_addr, (struct in_addr*)this->from4) < 0)
|
||||
{
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
if (inet_aton(to_addr, (struct in_addr*)&(this->to_addr_ipv4)) == 0)
|
||||
if (inet_pton(AF_INET, to_addr, (struct in_addr*)this->to4) < 0)
|
||||
{
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
/* convert to host order, inet_aton has network order */
|
||||
this->from_addr_ipv4 = ntohl(this->from_addr_ipv4);
|
||||
this->to_addr_ipv4 = ntohl(this->to_addr_ipv4);
|
||||
break;
|
||||
}
|
||||
case TS_IPV6_ADDR_RANGE:
|
||||
{
|
||||
free(this);
|
||||
return NULL;
|
||||
if (inet_pton(AF_INET6, from_addr, (struct in6_addr*)this->from6) < 0)
|
||||
{
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
if (inet_pton(AF_INET6, to_addr, (struct in6_addr*)this->to6) < 0)
|
||||
{
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user