Commit d7309836 authored by Marek Vavrusa's avatar Marek Vavrusa

Fixed mask calculation on big-endian platforms, cleaned up comparison.

refs #100
parent 378abbeb
......@@ -19,88 +19,99 @@
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <limits.h>
#include "common/acl.h"
#include "libknot/util/endian.h"
static inline uint32_t acl_sa_ipv4(sockaddr_t *a) {
static inline uint32_t ipv4_chunk(sockaddr_t *a)
{
/* Stored as big end first. */
return a->addr4.sin_addr.s_addr;
}
static inline uint32_t acl_fill_mask32(short c) {
/*! \todo Consider optimizing using LUT. */
assert(c >= 0 && c <= 32);
unsigned r = 0;
/*! This actually builds big-endian mask
* as we will match against addresses in
* network byte-order (big-endian).
* Otherwise it should be built from
* HO bit -> LO bit.
*/
for (char i = 0; i < c; ++i) {
r |= (1 << i);
}
return r;
static inline uint32_t ipv6_chunk(sockaddr_t *a, uint8_t idx)
{
/* Is byte array, 4x 32bit value. */
return ((uint32_t *)&a->addr6.sin6_addr)[idx];
}
static int acl_compare(void *k1, void *k2)
static inline uint32_t ip_chunk(sockaddr_t *a, uint8_t idx)
{
sockaddr_t* a1 = (sockaddr_t *)k1;
sockaddr_t* a2 = (sockaddr_t *)k2;
if (sockaddr_family(a) == AF_INET)
return ipv4_chunk(a);
/* Check different length, IPv4 goes first. */
int ldiff = a1->len - a2->len;
if (ldiff != 0) {
return ldiff < 0 ? -1 : 1;
}
return ipv6_chunk(a, idx);
}
/* Compare integers if IPv4. */
if (sockaddr_family(a1) == AF_INET) {
/*! \brief Compare chunks using given mask. */
static int cmp_chunk(sockaddr_t *a, sockaddr_t *b, uint8_t idx, uint32_t mask)
{
const uint32_t c1 = ip_chunk(a, idx) & mask;
const uint32_t c2 = ip_chunk(b, idx) & mask;
/* Compute mask .*/
uint32_t mask = acl_fill_mask32(a1->prefix);
if (c1 > c2)
return 1;
if (c1 < c2)
return -1;
return 0;
}
/* Compare address. */
int cmp1 = (acl_sa_ipv4(a1) & mask);
int cmp2 = (acl_sa_ipv4(a2) & mask);
if (cmp1 > cmp2) return 1;
if (cmp1 < cmp2) return -1;
return 0;
/*!
* \brief Calculate bitmask for byte array from the MSB.
*
* \note i.e. 8 means top 8 bits set, 11111111000000000000000000000000
*
* \param nbits number of bits set to 1
* \return mask
*/
static uint32_t acl_fill_mask32(short nbits)
{
assert(nbits >= 0 && nbits <= 32);
uint32_t r = 0;
for (char i = 0; i < nbits; ++i) {
r |= 1 << (31 - i);
}
/* IPv6 matching. */
#ifndef DISABLE_IPV6
if (sockaddr_family(a1) == AF_INET6) {
/* Get mask .*/
short chunk = a1->prefix;
/* Compare address by 32bit chunks. */
uint32_t* a1p = (uint32_t *)(&a1->addr6.sin6_addr);
uint32_t* a2p = (uint32_t *)(&a2->addr6.sin6_addr);
/* Mask 0 = 0 bits to compare from LO->HO (in big-endian).
* Mask 128 = 128 bits to compare.
*/
while (chunk > 0) {
uint32_t mask = 0xffffffff;
if ((size_t)chunk > sizeof(mask) << 3) {
chunk -= sizeof(mask) << 3;
} else {
mask = acl_fill_mask32(chunk);
chunk = 0;
}
int cmp1 = (*(a1p++) & mask);
int cmp2 = (*(a2p++) & mask);
if (cmp1 > cmp2) return 1;
if (cmp1 < cmp2) return -1;
/* Make sure the mask has MSB on the leftmost bit. */
return htobe32(r);
}
static int acl_compare(void *k1, void *k2)
{
int ret = 0;
sockaddr_t* a1 = (sockaddr_t *)k1;
sockaddr_t* a2 = (sockaddr_t *)k2;
uint32_t mask = 0xffffffff;
short mask_bits = a1->prefix;
const short chunk_bits = sizeof(mask) * CHAR_BIT;
/* Check different length, IPv4 goes first. */
if (a1->len != a2->len) {
if (a1->len < a2->len)
return -1;
else
return 1;
}
/* At most 4xchunk_bits for IPv6 */
unsigned i = 0;
while (ret == 0 && mask_bits > 0) {
/* Compute mask for current chunk. */
if (mask_bits <= chunk_bits) {
mask = acl_fill_mask32(mask_bits);
mask_bits = 0; /* Last chunk */
} else {
mask_bits -= chunk_bits;
}
return 0;
/* Empty mask - shortcut, we're done. */
if (mask > 0)
ret = cmp_chunk(a1, a2, i, mask);
++i;
}
#endif
return 0;
return ret;
}
acl_t *acl_new(acl_rule_t default_rule, const char *name)
......
......@@ -145,7 +145,7 @@ static int acl_tests_run(int argc, char *argv[])
acl_create(acl, &match_pf4, ACL_ACCEPT, 0, 0); /* Make decoy. */
acl_key_t *rval = NULL;
ret = acl_match(acl, &test_pf4, &rval);
ok(rval->val == sval, "acl: search for preferred node");
ok(rval && rval->val == sval, "acl: search for preferred node");
// 19. Scenario after truncating
ok(acl_truncate(acl) == ACL_ACCEPT, "acl: truncate");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment