This commit is contained in:
2026-06-05 14:03:02 +02:00
commit e7bf726adb
17 changed files with 2714 additions and 0 deletions

179
src/dynarr.c Normal file
View File

@@ -0,0 +1,179 @@
#include <dynarr.h>
DynArr* DynArr_create(size_t elemSize, size_t capacity) {
DynArr* p = (DynArr*)malloc(sizeof(DynArr));
if (!p) return NULL;
p->elemSize = elemSize;
p->capacity = capacity;
p->data = malloc(elemSize * capacity);
if (!p->data) {
free(p);
return NULL;
}
p->size = 0;
return p;
}
// Reserve n blocks in arary; New size will be n, NOT size + n; Reserving less memory that current will fail, use prune instead.
void DynArr_reserve(DynArr* p, size_t n) {
if (n <= p->capacity) {
printf("reserve ignored; attempted to reserve less or equal to current capacity\n");
return;
}
if (n > DYNARR_MAX_CAPACITY) {
printf("reserve ignored; attempted to reserve over 32 bits\n");
return;
}
void* new_data = realloc(p->data, n * p->elemSize);
if (!new_data) {
printf("reserve failed\n");
exit(1);
}
p->data = new_data;
p->capacity = n;
}
// Push data into a new block at the end of the array; If value is NULL, the new block will be zeroed.
void* DynArr_push_back(DynArr* p, void* value) {
//if (value == NULL) {
// printf("push_back ignored; value is null");
// return NULL;
//}
if (p->size >= p->capacity) {
size_t new_cap = (p->capacity == 0) ? 1 : p->capacity * 2;
if (new_cap < p->capacity || new_cap > DYNARR_MAX_CAPACITY) {
printf("push_back ignored; capacity overflow\n");
return NULL;
}
void* new_data = realloc(p->data, new_cap * p->elemSize);
if (!new_data) {
printf("push failed\n");
exit(1);
}
p->capacity = new_cap;
p->data = new_data;
}
void* dst = (void*)((char*)p->data + (p->size * p->elemSize));
if (value == NULL) {
memset(dst, 0, p->elemSize); // Handle NULL value.
} else {
memcpy((char*)dst, value, p->elemSize);
}
p->size++;
return dst;
}
// Remove the last block in the array.
void DynArr_pop_back(DynArr* p) {
if (p->size == 0) {
printf("pop_back ignored; size is 0\n");
return;
}
p->size--; // Will automatically overwrite that memory naturally
}
// Remove first block from array.
void DynArr_pop_front(DynArr* p) {
if (p->size == 0) {
printf("pop_front ignored; size is 0\n");
return;
}
memmove(
(char*)p->data,
(char*)p->data + p->elemSize,
(p->size - 1) * p->elemSize
);
p->size--;
}
// Remove index from array. This moves all blocks after the index block.
void DynArr_remove(DynArr* p, size_t index) {
if (index >= p->size) return;
memmove(
(char*)p->data + (index * p->elemSize),
(char*)p->data + (index + 1) * p->elemSize,
(p->size - index - 1) * p->elemSize
);
p->size--;
}
// Erase the array. This will not free unused blocks.
void DynArr_erase(DynArr* p) {
p->size = 0;
}
// Prune and free unused blocks. If pruning to zero, ensure to reserve after.
void DynArr_prune(DynArr* p) {
void* new_data = realloc(p->data, (p->size == 0 ? 1 : p->size) * p->elemSize);
if (!new_data) {
printf("pruning failed\n");
exit(1);
}
p->data = new_data;
p->capacity = p->size;
}
// Get a pointer to a block by index
void* DynArr_at(DynArr* p, size_t index) {
if (index >= p->size) return NULL;
return (char*)p->data + (index * p->elemSize);
}
// Get the index by block pointer
size_t DynArr_at_ptr(DynArr* p, void* ptr) {
if (!p || !ptr) {
printf("invalid pointer\n");
exit(1);
}
for (size_t i = 0; i < p->size; i++) {
if ((void*)(((char*)p->data) + (i * p->elemSize)) == ptr) {
return i;
}
}
// If for some reason the array has 2^64 elements in it, then fuck it, I guess we'll just crash, I don't care.
return -1;
}
// Get size
size_t DynArr_size(DynArr* p) {
return p->size;
}
// Get element size
size_t DynArr_elemSize(DynArr* p) {
return p->elemSize;
}
// Get capacity
size_t DynArr_capacity(DynArr* p) {
return p->capacity;
}
void DynArr_destroy(DynArr* p) {
if (!p) return;
free(p->data);
free(p);
}
void* DynArr_c_arr(DynArr* p) {
return p->data;
}

58
src/dynset.c Normal file
View File

@@ -0,0 +1,58 @@
#include <dynset.h>
DynSet* DynSet_Create(size_t elemSize) {
DynSet* set = (DynSet*)malloc(sizeof(DynSet));
if (!set) {
return NULL;
}
set->arr = DynArr_create(elemSize, 1);
if (!set->arr) {
free(set);
return NULL;
}
return set;
}
void DynSet_Destroy(DynSet* set) {
if (set) {
DynArr_destroy(set->arr);
free(set);
}
}
int DynSet_Insert(DynSet* set, const void* element) {
if (DynSet_Contains(set, element)) {
return 0; // Element already exists
}
return DynArr_push_back(set->arr, element) != NULL;
}
int DynSet_Contains(DynSet* set, const void* element) {
size_t size = DynArr_size(set->arr);
for (size_t i = 0; i < size; i++) {
void* current = DynArr_at(set->arr, i);
if (memcmp(current, element, set->arr->elemSize) == 0) {
return 1; // Found
}
}
return 0; // Not found
}
size_t DynSet_Size(DynSet* set) {
return DynArr_size(set->arr);
}
void* DynSet_Get(DynSet* set, size_t index) {
return DynArr_at(set->arr, index);
}
void DynSet_Remove(DynSet* set, const void* element) {
size_t size = DynArr_size(set->arr);
for (size_t i = 0; i < size; i++) {
void* current = DynArr_at(set->arr, i);
if (memcmp(current, element, set->arr->elemSize) == 0) {
DynArr_remove(set->arr, i);
return;
}
}
}

413
src/forward/if/interface.c Normal file
View File

@@ -0,0 +1,413 @@
#include <forward/if/interface.h>
#include <errno.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#ifndef IPV6_HDRINCL
#define IPV6_HDRINCL 36
#endif
struct interface_v4 {
int listenFd;
int sendFd;
int listening;
struct sockaddr_in localAddress;
pthread_t thread;
interface_packet_callback_t callback;
void* userData;
};
struct interface_v6 {
int listenFd;
int sendFd;
int listening;
struct sockaddr_in6 localAddress;
pthread_t thread;
interface_packet_callback_t callback;
void* userData;
};
static int interface_parse_ipv4(const char* localAddress, struct in_addr* outAddress) {
if (!outAddress) {
return -1;
}
if (localAddress == NULL || localAddress[0] == '\0') {
outAddress->s_addr = htonl(INADDR_ANY);
return 0;
}
return inet_pton(AF_INET, localAddress, outAddress) == 1 ? 0 : -1;
}
static int interface_parse_ipv6(const char* localAddress, struct in6_addr* outAddress) {
if (!outAddress) {
return -1;
}
if (localAddress == NULL || localAddress[0] == '\0') {
*outAddress = in6addr_any;
return 0;
}
return inet_pton(AF_INET6, localAddress, outAddress) == 1 ? 0 : -1;
}
static int interface_open_v4(interface_v4_t* iface) {
iface->listenFd = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
if (iface->listenFd < 0) {
return -1;
}
iface->sendFd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if (iface->sendFd < 0) {
close(iface->listenFd);
iface->listenFd = -1;
return -1;
}
{
int one = 1;
if (setsockopt(iface->sendFd, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0) {
close(iface->listenFd);
close(iface->sendFd);
iface->listenFd = -1;
iface->sendFd = -1;
return -1;
}
}
if (bind(iface->listenFd, (struct sockaddr*)&iface->localAddress, sizeof(iface->localAddress)) < 0) {
close(iface->listenFd);
close(iface->sendFd);
iface->listenFd = -1;
iface->sendFd = -1;
return -1;
}
return 0;
}
static int interface_open_v6(interface_v6_t* iface) {
iface->listenFd = socket(AF_INET6, SOCK_RAW, IPPROTO_IPV6);
if (iface->listenFd < 0) {
return -1;
}
iface->sendFd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
if (iface->sendFd < 0) {
close(iface->listenFd);
iface->listenFd = -1;
return -1;
}
{
int one = 1;
#ifdef IPV6_HDRINCL
if (setsockopt(iface->sendFd, IPPROTO_IPV6, IPV6_HDRINCL, &one, sizeof(one)) < 0) {
close(iface->listenFd);
close(iface->sendFd);
iface->listenFd = -1;
iface->sendFd = -1;
return -1;
}
#endif
}
if (bind(iface->listenFd, (struct sockaddr*)&iface->localAddress, sizeof(iface->localAddress)) < 0) {
close(iface->listenFd);
close(iface->sendFd);
iface->listenFd = -1;
iface->sendFd = -1;
return -1;
}
return 0;
}
static void interface_fill_packet_common(interface_packet_t* packet, sa_family_t family, const void* data, size_t length, const struct sockaddr* source, const struct sockaddr* destination) {
size_t sourceSize = 0;
size_t destinationSize = 0;
packet->family = family;
packet->data = (const uint8_t*)data;
packet->length = length;
memset(&packet->source, 0, sizeof(packet->source));
memset(&packet->destination, 0, sizeof(packet->destination));
if (source) {
sourceSize = (source->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
memcpy(&packet->source, source, sourceSize);
}
if (destination) {
destinationSize = (destination->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
memcpy(&packet->destination, destination, destinationSize);
}
}
static void* interface_listen_v4_thread(void* arg) {
interface_v4_t* iface = (interface_v4_t*)arg;
uint8_t buffer[65535];
while (iface->listening) {
struct sockaddr_storage source;
socklen_t sourceLength = sizeof(source);
ssize_t received = recvfrom(iface->listenFd, buffer, sizeof(buffer), 0, (struct sockaddr*)&source, &sourceLength);
if (received < 0) {
if (!iface->listening || errno == EBADF || errno == EINVAL) {
break;
}
continue;
}
if (iface->callback) {
uint8_t* packetCopy = (uint8_t*)malloc((size_t)received);
if (!packetCopy) {
continue;
}
interface_packet_t packet;
memcpy(packetCopy, buffer, (size_t)received);
interface_fill_packet_common(&packet, AF_INET, packetCopy, (size_t)received, (struct sockaddr*)&source, (struct sockaddr*)&iface->localAddress);
iface->callback(&packet, iface->userData);
free(packetCopy);
}
}
return NULL;
}
static void* interface_listen_v6_thread(void* arg) {
interface_v6_t* iface = (interface_v6_t*)arg;
uint8_t buffer[65535];
while (iface->listening) {
struct sockaddr_storage source;
socklen_t sourceLength = sizeof(source);
ssize_t received = recvfrom(iface->listenFd, buffer, sizeof(buffer), 0, (struct sockaddr*)&source, &sourceLength);
if (received < 0) {
if (!iface->listening || errno == EBADF || errno == EINVAL) {
break;
}
continue;
}
if (iface->callback) {
uint8_t* packetCopy = (uint8_t*)malloc((size_t)received);
if (!packetCopy) {
continue;
}
interface_packet_t packet;
memcpy(packetCopy, buffer, (size_t)received);
interface_fill_packet_common(&packet, AF_INET6, packetCopy, (size_t)received, (struct sockaddr*)&source, (struct sockaddr*)&iface->localAddress);
iface->callback(&packet, iface->userData);
free(packetCopy);
}
}
return NULL;
}
int Interface_Init(void) {
return 0;
}
void Interface_Cleanup(void) {
}
interface_v4_t* Interface_Create_v4(const char* localAddress, interface_packet_callback_t callback, void* userData) {
interface_v4_t* iface = (interface_v4_t*)calloc(1, sizeof(*iface));
if (!iface) {
return NULL;
}
iface->listenFd = -1;
iface->sendFd = -1;
iface->callback = callback;
iface->userData = userData;
iface->localAddress.sin_family = AF_INET;
if (interface_parse_ipv4(localAddress, &iface->localAddress.sin_addr) < 0) {
free(iface);
return NULL;
}
return iface;
}
interface_v6_t* Interface_Create_v6(const char* localAddress, interface_packet_callback_t callback, void* userData) {
interface_v6_t* iface = (interface_v6_t*)calloc(1, sizeof(*iface));
if (!iface) {
return NULL;
}
iface->listenFd = -1;
iface->sendFd = -1;
iface->callback = callback;
iface->userData = userData;
iface->localAddress.sin6_family = AF_INET6;
if (interface_parse_ipv6(localAddress, &iface->localAddress.sin6_addr) < 0) {
free(iface);
return NULL;
}
return iface;
}
int Interface_Listen_v4(interface_v4_t* iface) {
if (!iface || iface->listening) {
return -1;
}
if (interface_open_v4(iface) < 0) {
return -1;
}
iface->listening = 1;
if (pthread_create(&iface->thread, NULL, interface_listen_v4_thread, iface) != 0) {
iface->listening = 0;
close(iface->listenFd);
close(iface->sendFd);
iface->listenFd = -1;
iface->sendFd = -1;
return -1;
}
return 0;
}
int Interface_Listen_v6(interface_v6_t* iface) {
if (!iface || iface->listening) {
return -1;
}
if (interface_open_v6(iface) < 0) {
return -1;
}
iface->listening = 1;
if (pthread_create(&iface->thread, NULL, interface_listen_v6_thread, iface) != 0) {
iface->listening = 0;
close(iface->listenFd);
close(iface->sendFd);
iface->listenFd = -1;
iface->sendFd = -1;
return -1;
}
return 0;
}
void Interface_Stop_v4(interface_v4_t* iface) {
if (!iface) {
return;
}
if (iface->listening) {
iface->listening = 0;
if (iface->listenFd >= 0) {
close(iface->listenFd);
iface->listenFd = -1;
}
pthread_join(iface->thread, NULL);
}
if (iface->sendFd >= 0) {
close(iface->sendFd);
iface->sendFd = -1;
}
}
void Interface_Stop_v6(interface_v6_t* iface) {
if (!iface) {
return;
}
if (iface->listening) {
iface->listening = 0;
if (iface->listenFd >= 0) {
close(iface->listenFd);
iface->listenFd = -1;
}
pthread_join(iface->thread, NULL);
}
if (iface->sendFd >= 0) {
close(iface->sendFd);
iface->sendFd = -1;
}
}
void Interface_Destroy_v4(interface_v4_t* iface) {
if (!iface) {
return;
}
Interface_Stop_v4(iface);
free(iface);
}
void Interface_Destroy_v6(interface_v6_t* iface) {
if (!iface) {
return;
}
Interface_Stop_v6(iface);
free(iface);
}
int Interface_SendRaw_v4(interface_v4_t* iface, const struct in_addr* destination, const void* packet, size_t packetLength) {
struct sockaddr_in destinationAddress;
if (!iface || iface->sendFd < 0 || !destination || !packet || packetLength == 0) {
return -1;
}
memset(&destinationAddress, 0, sizeof(destinationAddress));
destinationAddress.sin_family = AF_INET;
destinationAddress.sin_addr = *destination;
return (sendto(iface->sendFd, packet, packetLength, 0, (struct sockaddr*)&destinationAddress, sizeof(destinationAddress)) < 0) ? -1 : 0;
}
int Interface_SendRaw_v6(interface_v6_t* iface, const struct in6_addr* destination, const void* packet, size_t packetLength) {
struct sockaddr_in6 destinationAddress;
if (!iface || iface->sendFd < 0 || !destination || !packet || packetLength == 0) {
return -1;
}
memset(&destinationAddress, 0, sizeof(destinationAddress));
destinationAddress.sin6_family = AF_INET6;
destinationAddress.sin6_addr = *destination;
return (sendto(iface->sendFd, packet, packetLength, 0, (struct sockaddr*)&destinationAddress, sizeof(destinationAddress)) < 0) ? -1 : 0;
}
int Interface_SendFrame_v4(interface_v4_t* iface, const void* frame, size_t frameLength) {
(void)iface;
(void)frame;
(void)frameLength;
errno = ENOTSUP;
return -1;
}
int Interface_SendFrame_v6(interface_v6_t* iface, const void* frame, size_t frameLength) {
(void)iface;
(void)frame;
(void)frameLength;
errno = ENOTSUP;
return -1;
}

125
src/forward/routetable.c Normal file
View File

@@ -0,0 +1,125 @@
#include <forward/routetable.h>
#include <iputils.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
static art_tree route_table_v4;
static art_tree route_table_v6;
static void build_key_v4(unsigned char out[5], uint32_t network, uint8_t plen) {
uint32_t be = htonl(network);
memcpy(out, &be, 4);
out[4] = plen;
}
static void build_key_v6(unsigned char out[17], __uint128_t network, uint8_t plen) {
uint32_t parts[4] = {
htonl((uint32_t)(network >> 96)),
htonl((uint32_t)(network >> 64)),
htonl((uint32_t)(network >> 32)),
htonl((uint32_t)(network))
};
memcpy(out, parts, 16);
out[16] = plen;
}
static int free_value_cb(void *data, const unsigned char *key, uint32_t key_len, void *value) {
(void)data; (void)key; (void)key_len;
free(value);
return 0;
}
int RouteTable_Init(void) {
if (art_tree_init(&route_table_v4) != 0) return -1;
if (art_tree_init(&route_table_v6) != 0) {
art_tree_destroy(&route_table_v4);
return -1;
}
return 0;
}
void RouteTable_Cleanup(void) {
art_iter(&route_table_v4, free_value_cb, NULL);
art_tree_destroy(&route_table_v4);
art_iter(&route_table_v6, free_value_cb, NULL);
art_tree_destroy(&route_table_v6);
}
int RouteTable_AddRoute_v4(uint32_t destination, uint32_t mask, uint32_t nextHop) {
if (destination == 0 || nextHop == 0 || mask == 0) return -1;
if (destination == 0x7F000001) return -1;
uint8_t prefix_len = IPUtils_MaskToCIDRv4(mask);
uint32_t network = destination & mask;
uint32_t *value = malloc(sizeof(uint32_t));
if (!value) return -1;
*value = nextHop;
unsigned char key[5];
build_key_v4(key, network, prefix_len);
void *old = art_insert(&route_table_v4, key, 5, value);
if (old) free(old);
return 0;
}
int RouteTable_AddRoute_v6(__uint128_t destination, __uint128_t mask, __uint128_t nextHop) {
if (destination == 0 || nextHop == 0 || mask == 0) return -1;
if (destination == (__uint128_t)1) return -1;
uint8_t prefix_len = IPUtils_MaskToCIDRv6(mask);
__uint128_t network = destination & mask;
__uint128_t *value = malloc(sizeof(__uint128_t));
if (!value) return -1;
*value = nextHop;
unsigned char key[17];
build_key_v6(key, network, prefix_len);
void *old = art_insert(&route_table_v6, key, 17, value);
if (old) free(old);
return 0;
}
int RouteTable_GetNextHop_v4(uint32_t destination, uint32_t *nextHop) {
if (!nextHop) return -1;
unsigned char key[5];
for (int plen = 32; plen >= 0; plen--) {
uint32_t mask = IPUtils_CIDRToMaskv4((uint8_t)plen);
uint32_t network = destination & mask;
build_key_v4(key, network, (uint8_t)plen);
uint32_t *val = art_search(&route_table_v4, key, 5);
if (val) {
*nextHop = *val;
return 0;
}
}
return -1;
}
int RouteTable_GetNextHop_v6(__uint128_t destination, __uint128_t *nextHop) {
if (!nextHop) return -1;
unsigned char key[17];
for (int plen = 128; plen >= 0; plen--) {
__uint128_t mask = IPUtils_CIDRToMaskv6((uint8_t)plen);
__uint128_t network = destination & mask;
build_key_v6(key, network, (uint8_t)plen);
__uint128_t *val = art_search(&route_table_v6, key, 17);
if (val) {
*nextHop = *val;
return 0;
}
}
return -1;
}

89
src/iputils.c Normal file
View File

@@ -0,0 +1,89 @@
#include <iputils.h>
uint32_t IPUtils_CIDRToMaskv4(uint8_t cidr) {
if (cidr > 32) { return 0x00; }
return (0xFFFFFFFFUL << (32 - cidr)) & 0xFFFFFFFFUL;
}
uint8_t IPUtils_MaskToCIDRv4(uint32_t mask) {
uint8_t cidr = 0;
for (int i = 31; i >= 0; i--) {
if ((mask >> i) & 1) {
cidr++;
} else {
break;
}
}
return cidr;
}
bool IPUtils_MatchPrefixv4(uint32_t ip, uint32_t prefix, uint8_t cidr) {
uint32_t mask = IPUtils_CIDRToMaskv4(cidr);
return (ip & mask) == (prefix & mask);
}
void IPUtils_PrintIPv4(uint32_t ip, char* buf) {
if (buf) {
snprintf(buf, 16, "%u.%u.%u.%u",
(ip >> 24) & 0xFF,
(ip >> 16) & 0xFF,
(ip >> 8) & 0xFF,
ip & 0xFF);
} else {
printf("%u.%u.%u.%u",
(ip >> 24) & 0xFF,
(ip >> 16) & 0xFF,
(ip >> 8) & 0xFF,
ip & 0xFF);
}
}
__uint128_t IPUtils_CIDRToMaskv6(uint8_t cidr) {
if (cidr > 128) { return 0x00; }
__uint128_t mask = 0;
for (int i = 0; i < cidr; i++) {
mask |= ((__uint128_t)1 << (127 - i));
}
return mask;
}
uint8_t IPUtils_MaskToCIDRv6(__uint128_t mask) {
uint8_t cidr = 0;
for (int i = 127; i >= 0; i--) {
if ((mask >> i) & 1) {
cidr++;
} else {
break;
}
}
return cidr;
}
bool IPUtils_MatchPrefixv6(__uint128_t ip, __uint128_t prefix, uint8_t cidr) {
__uint128_t mask = IPUtils_CIDRToMaskv6(cidr);
return (ip & mask) == (prefix & mask);
}
void IPUtils_PrintIPv6(__uint128_t ip, char* buf) {
if (buf) {
snprintf(buf, 40, "%x:%x:%x:%x:%x:%x:%x:%x",
(unsigned int)((ip >> 112) & 0xFFFF),
(unsigned int)((ip >> 96) & 0xFFFF),
(unsigned int)((ip >> 80) & 0xFFFF),
(unsigned int)((ip >> 64) & 0xFFFF),
(unsigned int)((ip >> 48) & 0xFFFF),
(unsigned int)((ip >> 32) & 0xFFFF),
(unsigned int)((ip >> 16) & 0xFFFF),
(unsigned int)(ip & 0xFFFF));
} else {
printf("%x:%x:%x:%x:%x:%x:%x:%x",
(unsigned int)((ip >> 112) & 0xFFFF),
(unsigned int)((ip >> 96) & 0xFFFF),
(unsigned int)((ip >> 80) & 0xFFFF),
(unsigned int)((ip >> 64) & 0xFFFF),
(unsigned int)((ip >> 48) & 0xFFFF),
(unsigned int)((ip >> 32) & 0xFFFF),
(unsigned int)((ip >> 16) & 0xFFFF),
(unsigned int)(ip & 0xFFFF));
}
}

975
src/libart/art.c Normal file
View File

@@ -0,0 +1,975 @@
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <assert.h>
#include <libart/art.h>
#ifdef __i386__
#include <emmintrin.h>
#else
#ifdef __amd64__
#include <emmintrin.h>
#endif
#endif
/**
* Macros to manipulate pointer tags
*/
#define IS_LEAF(x) (((uintptr_t)x & 1))
#define SET_LEAF(x) ((void*)((uintptr_t)x | 1))
#define LEAF_RAW(x) ((art_leaf*)((void*)((uintptr_t)x & ~1)))
/**
* Allocates a node of the given type,
* initializes to zero and sets the type.
*/
static art_node* alloc_node(uint8_t type) {
art_node* n;
switch (type) {
case NODE4:
n = (art_node*)calloc(1, sizeof(art_node4));
break;
case NODE16:
n = (art_node*)calloc(1, sizeof(art_node16));
break;
case NODE48:
n = (art_node*)calloc(1, sizeof(art_node48));
break;
case NODE256:
n = (art_node*)calloc(1, sizeof(art_node256));
break;
default:
abort();
}
n->type = type;
return n;
}
/**
* Initializes an ART tree
* @return 0 on success.
*/
int art_tree_init(art_tree *t) {
t->root = NULL;
t->size = 0;
return 0;
}
// Recursively destroys the tree
static void destroy_node(art_node *n) {
// Break if null
if (!n) return;
// Special case leafs
if (IS_LEAF(n)) {
free(LEAF_RAW(n));
return;
}
// Handle each node type
int i, idx;
union {
art_node4 *p1;
art_node16 *p2;
art_node48 *p3;
art_node256 *p4;
} p;
switch (n->type) {
case NODE4:
p.p1 = (art_node4*)n;
for (i=0;i<n->num_children;i++) {
destroy_node(p.p1->children[i]);
}
break;
case NODE16:
p.p2 = (art_node16*)n;
for (i=0;i<n->num_children;i++) {
destroy_node(p.p2->children[i]);
}
break;
case NODE48:
p.p3 = (art_node48*)n;
for (i=0;i<256;i++) {
idx = ((art_node48*)n)->keys[i];
if (!idx) continue;
destroy_node(p.p3->children[idx-1]);
}
break;
case NODE256:
p.p4 = (art_node256*)n;
for (i=0;i<256;i++) {
if (p.p4->children[i])
destroy_node(p.p4->children[i]);
}
break;
default:
abort();
}
// Free ourself on the way up
free(n);
}
/**
* Destroys an ART tree
* @return 0 on success.
*/
int art_tree_destroy(art_tree *t) {
destroy_node(t->root);
return 0;
}
/**
* Returns the size of the ART tree.
*/
#ifndef BROKEN_GCC_C99_INLINE
extern inline uint64_t art_size(art_tree *t);
#endif
static art_node** find_child(art_node *n, unsigned char c) {
int i, mask, bitfield;
union {
art_node4 *p1;
art_node16 *p2;
art_node48 *p3;
art_node256 *p4;
} p;
switch (n->type) {
case NODE4:
p.p1 = (art_node4*)n;
for (i=0 ; i < n->num_children; i++) {
/* this cast works around a bug in gcc 5.1 when unrolling loops
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124
*/
if (((unsigned char*)p.p1->keys)[i] == c)
return &p.p1->children[i];
}
break;
{
case NODE16:
p.p2 = (art_node16*)n;
// support non-86 architectures
#ifdef __i386__
// Compare the key to all 16 stored keys
__m128i cmp;
cmp = _mm_cmpeq_epi8(_mm_set1_epi8(c),
_mm_loadu_si128((__m128i*)p.p2->keys));
// Use a mask to ignore children that don't exist
mask = (1 << n->num_children) - 1;
bitfield = _mm_movemask_epi8(cmp) & mask;
#else
#ifdef __amd64__
// Compare the key to all 16 stored keys
__m128i cmp;
cmp = _mm_cmpeq_epi8(_mm_set1_epi8(c),
_mm_loadu_si128((__m128i*)p.p2->keys));
// Use a mask to ignore children that don't exist
mask = (1 << n->num_children) - 1;
bitfield = _mm_movemask_epi8(cmp) & mask;
#else
// Compare the key to all 16 stored keys
bitfield = 0;
for (i = 0; i < 16; ++i) {
if (p.p2->keys[i] == c)
bitfield |= (1 << i);
}
// Use a mask to ignore children that don't exist
mask = (1 << n->num_children) - 1;
bitfield &= mask;
#endif
#endif
/*
* If we have a match (any bit set) then we can
* return the pointer match using ctz to get
* the index.
*/
if (bitfield)
return &p.p2->children[__builtin_ctz(bitfield)];
break;
}
case NODE48:
p.p3 = (art_node48*)n;
i = p.p3->keys[c];
if (i)
return &p.p3->children[i-1];
break;
case NODE256:
p.p4 = (art_node256*)n;
if (p.p4->children[c])
return &p.p4->children[c];
break;
default:
abort();
}
return NULL;
}
// Simple inlined if
static inline int min(int a, int b) {
return (a < b) ? a : b;
}
/**
* Returns the number of prefix characters shared between
* the key and node.
*/
static int check_prefix(const art_node *n, const unsigned char *key, int key_len, int depth) {
int max_cmp = min(min(n->partial_len, MAX_PREFIX_LEN), key_len - depth);
int idx;
for (idx=0; idx < max_cmp; idx++) {
if (n->partial[idx] != key[depth+idx])
return idx;
}
return idx;
}
/**
* Checks if a leaf matches
* @return 0 on success.
*/
static int leaf_matches(const art_leaf *n, const unsigned char *key, int key_len, int depth) {
(void)depth;
// Fail if the key lengths are different
if (n->key_len != (uint32_t)key_len) return 1;
// Compare the keys starting at the depth
return memcmp(n->key, key, key_len);
}
/**
* Searches for a value in the ART tree
* @arg t The tree
* @arg key The key
* @arg key_len The length of the key
* @return NULL if the item was not found, otherwise
* the value pointer is returned.
*/
void* art_search(const art_tree *t, const unsigned char *key, int key_len) {
art_node **child;
art_node *n = t->root;
int prefix_len, depth = 0;
while (n) {
// Might be a leaf
if (IS_LEAF(n)) {
n = (art_node*)LEAF_RAW(n);
// Check if the expanded path matches
if (!leaf_matches((art_leaf*)n, key, key_len, depth)) {
return ((art_leaf*)n)->value;
}
return NULL;
}
// Bail if the prefix does not match
if (n->partial_len) {
prefix_len = check_prefix(n, key, key_len, depth);
if (prefix_len != min(MAX_PREFIX_LEN, n->partial_len))
return NULL;
depth = depth + n->partial_len;
}
// Recursively search
child = find_child(n, key[depth]);
n = (child) ? *child : NULL;
depth++;
}
return NULL;
}
// Find the minimum leaf under a node
static art_leaf* minimum(const art_node *n) {
// Handle base cases
if (!n) return NULL;
if (IS_LEAF(n)) return LEAF_RAW(n);
int idx;
switch (n->type) {
case NODE4:
return minimum(((const art_node4*)n)->children[0]);
case NODE16:
return minimum(((const art_node16*)n)->children[0]);
case NODE48:
idx=0;
while (!((const art_node48*)n)->keys[idx]) idx++;
idx = ((const art_node48*)n)->keys[idx] - 1;
return minimum(((const art_node48*)n)->children[idx]);
case NODE256:
idx=0;
while (!((const art_node256*)n)->children[idx]) idx++;
return minimum(((const art_node256*)n)->children[idx]);
default:
abort();
}
}
// Find the maximum leaf under a node
static art_leaf* maximum(const art_node *n) {
// Handle base cases
if (!n) return NULL;
if (IS_LEAF(n)) return LEAF_RAW(n);
int idx;
switch (n->type) {
case NODE4:
return maximum(((const art_node4*)n)->children[n->num_children-1]);
case NODE16:
return maximum(((const art_node16*)n)->children[n->num_children-1]);
case NODE48:
idx=255;
while (!((const art_node48*)n)->keys[idx]) idx--;
idx = ((const art_node48*)n)->keys[idx] - 1;
return maximum(((const art_node48*)n)->children[idx]);
case NODE256:
idx=255;
while (!((const art_node256*)n)->children[idx]) idx--;
return maximum(((const art_node256*)n)->children[idx]);
default:
abort();
}
}
/**
* Returns the minimum valued leaf
*/
art_leaf* art_minimum(art_tree *t) {
return minimum((art_node*)t->root);
}
/**
* Returns the maximum valued leaf
*/
art_leaf* art_maximum(art_tree *t) {
return maximum((art_node*)t->root);
}
static art_leaf* make_leaf(const unsigned char *key, int key_len, void *value) {
art_leaf *l = (art_leaf*)calloc(1, sizeof(art_leaf)+key_len);
l->value = value;
l->key_len = key_len;
memcpy(l->key, key, key_len);
return l;
}
static int longest_common_prefix(art_leaf *l1, art_leaf *l2, int depth) {
int max_cmp = min(l1->key_len, l2->key_len) - depth;
int idx;
for (idx=0; idx < max_cmp; idx++) {
if (l1->key[depth+idx] != l2->key[depth+idx])
return idx;
}
return idx;
}
static void copy_header(art_node *dest, art_node *src) {
dest->num_children = src->num_children;
dest->partial_len = src->partial_len;
memcpy(dest->partial, src->partial, min(MAX_PREFIX_LEN, src->partial_len));
}
static void add_child256(art_node256 *n, art_node **ref, unsigned char c, void *child) {
(void)ref;
n->n.num_children++;
n->children[c] = (art_node*)child;
}
static void add_child48(art_node48 *n, art_node **ref, unsigned char c, void *child) {
if (n->n.num_children < 48) {
int pos = 0;
while (n->children[pos]) pos++;
n->children[pos] = (art_node*)child;
n->keys[c] = pos + 1;
n->n.num_children++;
} else {
art_node256 *new_node = (art_node256*)alloc_node(NODE256);
for (int i=0;i<256;i++) {
if (n->keys[i]) {
new_node->children[i] = n->children[n->keys[i] - 1];
}
}
copy_header((art_node*)new_node, (art_node*)n);
*ref = (art_node*)new_node;
free(n);
add_child256(new_node, ref, c, child);
}
}
static void add_child16(art_node16 *n, art_node **ref, unsigned char c, void *child) {
if (n->n.num_children < 16) {
unsigned mask = (1 << n->n.num_children) - 1;
// support non-x86 architectures
#ifdef __i386__
__m128i cmp;
// Compare the key to all 16 stored keys
cmp = _mm_cmplt_epi8(_mm_set1_epi8(c),
_mm_loadu_si128((__m128i*)n->keys));
// Use a mask to ignore children that don't exist
unsigned bitfield = _mm_movemask_epi8(cmp) & mask;
#else
#ifdef __amd64__
__m128i cmp;
// Compare the key to all 16 stored keys
cmp = _mm_cmplt_epi8(_mm_set1_epi8(c),
_mm_loadu_si128((__m128i*)n->keys));
// Use a mask to ignore children that don't exist
unsigned bitfield = _mm_movemask_epi8(cmp) & mask;
#else
// Compare the key to all 16 stored keys
unsigned bitfield = 0;
for (short i = 0; i < 16; ++i) {
if (c < n->keys[i])
bitfield |= (1 << i);
}
// Use a mask to ignore children that don't exist
bitfield &= mask;
#endif
#endif
// Check if less than any
unsigned idx;
if (bitfield) {
idx = __builtin_ctz(bitfield);
memmove(n->keys+idx+1,n->keys+idx,n->n.num_children-idx);
memmove(n->children+idx+1,n->children+idx,
(n->n.num_children-idx)*sizeof(void*));
} else
idx = n->n.num_children;
// Set the child
n->keys[idx] = c;
n->children[idx] = (art_node*)child;
n->n.num_children++;
} else {
art_node48 *new_node = (art_node48*)alloc_node(NODE48);
// Copy the child pointers and populate the key map
memcpy(new_node->children, n->children,
sizeof(void*)*n->n.num_children);
for (int i=0;i<n->n.num_children;i++) {
new_node->keys[n->keys[i]] = i + 1;
}
copy_header((art_node*)new_node, (art_node*)n);
*ref = (art_node*)new_node;
free(n);
add_child48(new_node, ref, c, child);
}
}
static void add_child4(art_node4 *n, art_node **ref, unsigned char c, void *child) {
if (n->n.num_children < 4) {
int idx;
for (idx=0; idx < n->n.num_children; idx++) {
if (c < n->keys[idx]) break;
}
// Shift to make room
memmove(n->keys+idx+1, n->keys+idx, n->n.num_children - idx);
memmove(n->children+idx+1, n->children+idx,
(n->n.num_children - idx)*sizeof(void*));
// Insert element
n->keys[idx] = c;
n->children[idx] = (art_node*)child;
n->n.num_children++;
} else {
art_node16 *new_node = (art_node16*)alloc_node(NODE16);
// Copy the child pointers and the key map
memcpy(new_node->children, n->children,
sizeof(void*)*n->n.num_children);
memcpy(new_node->keys, n->keys,
sizeof(unsigned char)*n->n.num_children);
copy_header((art_node*)new_node, (art_node*)n);
*ref = (art_node*)new_node;
free(n);
add_child16(new_node, ref, c, child);
}
}
static void add_child(art_node *n, art_node **ref, unsigned char c, void *child) {
switch (n->type) {
case NODE4:
return add_child4((art_node4*)n, ref, c, child);
case NODE16:
return add_child16((art_node16*)n, ref, c, child);
case NODE48:
return add_child48((art_node48*)n, ref, c, child);
case NODE256:
return add_child256((art_node256*)n, ref, c, child);
default:
abort();
}
}
/**
* Calculates the index at which the prefixes mismatch
*/
static int prefix_mismatch(const art_node *n, const unsigned char *key, int key_len, int depth) {
int max_cmp = min(min(MAX_PREFIX_LEN, n->partial_len), key_len - depth);
int idx;
for (idx=0; idx < max_cmp; idx++) {
if (n->partial[idx] != key[depth+idx])
return idx;
}
// If the prefix is short we can avoid finding a leaf
if (n->partial_len > MAX_PREFIX_LEN) {
// Prefix is longer than what we've checked, find a leaf
art_leaf *l = minimum(n);
max_cmp = min(l->key_len, key_len)- depth;
for (; idx < max_cmp; idx++) {
if (l->key[idx+depth] != key[depth+idx])
return idx;
}
}
return idx;
}
static void* recursive_insert(art_node *n, art_node **ref, const unsigned char *key, int key_len, void *value, int depth, int *old, int replace) {
// If we are at a NULL node, inject a leaf
if (!n) {
*ref = (art_node*)SET_LEAF(make_leaf(key, key_len, value));
return NULL;
}
// If we are at a leaf, we need to replace it with a node
if (IS_LEAF(n)) {
art_leaf *l = LEAF_RAW(n);
// Check if we are updating an existing value
if (!leaf_matches(l, key, key_len, depth)) {
*old = 1;
void *old_val = l->value;
if(replace) l->value = value;
return old_val;
}
// New value, we must split the leaf into a node4
art_node4 *new_node = (art_node4*)alloc_node(NODE4);
// Create a new leaf
art_leaf *l2 = make_leaf(key, key_len, value);
// Determine longest prefix
int longest_prefix = longest_common_prefix(l, l2, depth);
new_node->n.partial_len = longest_prefix;
memcpy(new_node->n.partial, key+depth, min(MAX_PREFIX_LEN, longest_prefix));
// Add the leafs to the new node4
*ref = (art_node*)new_node;
add_child4(new_node, ref, l->key[depth+longest_prefix], SET_LEAF(l));
add_child4(new_node, ref, l2->key[depth+longest_prefix], SET_LEAF(l2));
return NULL;
}
// Check if given node has a prefix
if (n->partial_len) {
// Determine if the prefixes differ, since we need to split
int prefix_diff = prefix_mismatch(n, key, key_len, depth);
if ((uint32_t)prefix_diff >= n->partial_len) {
depth += n->partial_len;
goto RECURSE_SEARCH;
}
// Create a new node
art_node4 *new_node = (art_node4*)alloc_node(NODE4);
*ref = (art_node*)new_node;
new_node->n.partial_len = prefix_diff;
memcpy(new_node->n.partial, n->partial, min(MAX_PREFIX_LEN, prefix_diff));
// Adjust the prefix of the old node
if (n->partial_len <= MAX_PREFIX_LEN) {
add_child4(new_node, ref, n->partial[prefix_diff], n);
n->partial_len -= (prefix_diff+1);
memmove(n->partial, n->partial+prefix_diff+1,
min(MAX_PREFIX_LEN, n->partial_len));
} else {
n->partial_len -= (prefix_diff+1);
art_leaf *l = minimum(n);
add_child4(new_node, ref, l->key[depth+prefix_diff], n);
memcpy(n->partial, l->key+depth+prefix_diff+1,
min(MAX_PREFIX_LEN, n->partial_len));
}
// Insert the new leaf
art_leaf *l = make_leaf(key, key_len, value);
add_child4(new_node, ref, key[depth+prefix_diff], SET_LEAF(l));
return NULL;
}
RECURSE_SEARCH:;
// Find a child to recurse to
art_node **child = find_child(n, key[depth]);
if (child) {
return recursive_insert(*child, child, key, key_len, value, depth+1, old, replace);
}
// No child, node goes within us
art_leaf *l = make_leaf(key, key_len, value);
add_child(n, ref, key[depth], SET_LEAF(l));
return NULL;
}
/**
* inserts a new value into the art tree
* @arg t the tree
* @arg key the key
* @arg key_len the length of the key
* @arg value opaque value.
* @return null if the item was newly inserted, otherwise
* the old value pointer is returned.
*/
void* art_insert(art_tree *t, const unsigned char *key, int key_len, void *value) {
int old_val = 0;
void *old = recursive_insert(t->root, &t->root, key, key_len, value, 0, &old_val, 1);
if (!old_val) t->size++;
return old;
}
/**
* inserts a new value into the art tree (no replace)
* @arg t the tree
* @arg key the key
* @arg key_len the length of the key
* @arg value opaque value.
* @return null if the item was newly inserted, otherwise
* the old value pointer is returned.
*/
void* art_insert_no_replace(art_tree *t, const unsigned char *key, int key_len, void *value) {
int old_val = 0;
void *old = recursive_insert(t->root, &t->root, key, key_len, value, 0, &old_val, 0);
if (!old_val) t->size++;
return old;
}
static void remove_child256(art_node256 *n, art_node **ref, unsigned char c) {
n->children[c] = NULL;
n->n.num_children--;
// Resize to a node48 on underflow, not immediately to prevent
// trashing if we sit on the 48/49 boundary
if (n->n.num_children == 37) {
art_node48 *new_node = (art_node48*)alloc_node(NODE48);
*ref = (art_node*)new_node;
copy_header((art_node*)new_node, (art_node*)n);
int pos = 0;
for (int i=0;i<256;i++) {
if (n->children[i]) {
new_node->children[pos] = n->children[i];
new_node->keys[i] = pos + 1;
pos++;
}
}
free(n);
}
}
static void remove_child48(art_node48 *n, art_node **ref, unsigned char c) {
int pos = n->keys[c];
n->keys[c] = 0;
n->children[pos-1] = NULL;
n->n.num_children--;
if (n->n.num_children == 12) {
art_node16 *new_node = (art_node16*)alloc_node(NODE16);
*ref = (art_node*)new_node;
copy_header((art_node*)new_node, (art_node*)n);
int child = 0;
for (int i=0;i<256;i++) {
pos = n->keys[i];
if (pos) {
new_node->keys[child] = i;
new_node->children[child] = n->children[pos - 1];
child++;
}
}
free(n);
}
}
static void remove_child16(art_node16 *n, art_node **ref, art_node **l) {
int pos = l - n->children;
memmove(n->keys+pos, n->keys+pos+1, n->n.num_children - 1 - pos);
memmove(n->children+pos, n->children+pos+1, (n->n.num_children - 1 - pos)*sizeof(void*));
n->n.num_children--;
if (n->n.num_children == 3) {
art_node4 *new_node = (art_node4*)alloc_node(NODE4);
*ref = (art_node*)new_node;
copy_header((art_node*)new_node, (art_node*)n);
memcpy(new_node->keys, n->keys, 4);
memcpy(new_node->children, n->children, 4*sizeof(void*));
free(n);
}
}
static void remove_child4(art_node4 *n, art_node **ref, art_node **l) {
int pos = l - n->children;
memmove(n->keys+pos, n->keys+pos+1, n->n.num_children - 1 - pos);
memmove(n->children+pos, n->children+pos+1, (n->n.num_children - 1 - pos)*sizeof(void*));
n->n.num_children--;
// Remove nodes with only a single child
if (n->n.num_children == 1) {
art_node *child = n->children[0];
if (!IS_LEAF(child)) {
// Concatenate the prefixes
int prefix = n->n.partial_len;
if (prefix < MAX_PREFIX_LEN) {
n->n.partial[prefix] = n->keys[0];
prefix++;
}
if (prefix < MAX_PREFIX_LEN) {
int sub_prefix = min(child->partial_len, MAX_PREFIX_LEN - prefix);
memcpy(n->n.partial+prefix, child->partial, sub_prefix);
prefix += sub_prefix;
}
// Store the prefix in the child
memcpy(child->partial, n->n.partial, min(prefix, MAX_PREFIX_LEN));
child->partial_len += n->n.partial_len + 1;
}
*ref = child;
free(n);
}
}
static void remove_child(art_node *n, art_node **ref, unsigned char c, art_node **l) {
switch (n->type) {
case NODE4:
return remove_child4((art_node4*)n, ref, l);
case NODE16:
return remove_child16((art_node16*)n, ref, l);
case NODE48:
return remove_child48((art_node48*)n, ref, c);
case NODE256:
return remove_child256((art_node256*)n, ref, c);
default:
abort();
}
}
static art_leaf* recursive_delete(art_node *n, art_node **ref, const unsigned char *key, int key_len, int depth) {
// Search terminated
if (!n) return NULL;
// Handle hitting a leaf node
if (IS_LEAF(n)) {
art_leaf *l = LEAF_RAW(n);
if (!leaf_matches(l, key, key_len, depth)) {
*ref = NULL;
return l;
}
return NULL;
}
// Bail if the prefix does not match
if (n->partial_len) {
int prefix_len = check_prefix(n, key, key_len, depth);
if (prefix_len != min(MAX_PREFIX_LEN, n->partial_len)) {
return NULL;
}
depth = depth + n->partial_len;
}
// Find child node
art_node **child = find_child(n, key[depth]);
if (!child) return NULL;
// If the child is leaf, delete from this node
if (IS_LEAF(*child)) {
art_leaf *l = LEAF_RAW(*child);
if (!leaf_matches(l, key, key_len, depth)) {
remove_child(n, ref, key[depth], child);
return l;
}
return NULL;
// Recurse
} else {
return recursive_delete(*child, child, key, key_len, depth+1);
}
}
/**
* Deletes a value from the ART tree
* @arg t The tree
* @arg key The key
* @arg key_len The length of the key
* @return NULL if the item was not found, otherwise
* the value pointer is returned.
*/
void* art_delete(art_tree *t, const unsigned char *key, int key_len) {
art_leaf *l = recursive_delete(t->root, &t->root, key, key_len, 0);
if (l) {
t->size--;
void *old = l->value;
free(l);
return old;
}
return NULL;
}
// Recursively iterates over the tree
static int recursive_iter(art_node *n, art_callback cb, void *data) {
// Handle base cases
if (!n) return 0;
if (IS_LEAF(n)) {
art_leaf *l = LEAF_RAW(n);
return cb(data, (const unsigned char*)l->key, l->key_len, l->value);
}
int idx, res;
switch (n->type) {
case NODE4:
for (int i=0; i < n->num_children; i++) {
res = recursive_iter(((art_node4*)n)->children[i], cb, data);
if (res) return res;
}
break;
case NODE16:
for (int i=0; i < n->num_children; i++) {
res = recursive_iter(((art_node16*)n)->children[i], cb, data);
if (res) return res;
}
break;
case NODE48:
for (int i=0; i < 256; i++) {
idx = ((art_node48*)n)->keys[i];
if (!idx) continue;
res = recursive_iter(((art_node48*)n)->children[idx-1], cb, data);
if (res) return res;
}
break;
case NODE256:
for (int i=0; i < 256; i++) {
if (!((art_node256*)n)->children[i]) continue;
res = recursive_iter(((art_node256*)n)->children[i], cb, data);
if (res) return res;
}
break;
default:
abort();
}
return 0;
}
/**
* Iterates through the entries pairs in the map,
* invoking a callback for each. The call back gets a
* key, value for each and returns an integer stop value.
* If the callback returns non-zero, then the iteration stops.
* @arg t The tree to iterate over
* @arg cb The callback function to invoke
* @arg data Opaque handle passed to the callback
* @return 0 on success, or the return of the callback.
*/
int art_iter(art_tree *t, art_callback cb, void *data) {
return recursive_iter(t->root, cb, data);
}
/**
* Checks if a leaf prefix matches
* @return 0 on success.
*/
static int leaf_prefix_matches(const art_leaf *n, const unsigned char *prefix, int prefix_len) {
// Fail if the key length is too short
if (n->key_len < (uint32_t)prefix_len) return 1;
// Compare the keys
return memcmp(n->key, prefix, prefix_len);
}
/**
* Iterates through the entries pairs in the map,
* invoking a callback for each that matches a given prefix.
* The call back gets a key, value for each and returns an integer stop value.
* If the callback returns non-zero, then the iteration stops.
* @arg t The tree to iterate over
* @arg prefix The prefix of keys to read
* @arg prefix_len The length of the prefix
* @arg cb The callback function to invoke
* @arg data Opaque handle passed to the callback
* @return 0 on success, or the return of the callback.
*/
int art_iter_prefix(art_tree *t, const unsigned char *key, int key_len, art_callback cb, void *data) {
art_node **child;
art_node *n = t->root;
int prefix_len, depth = 0;
while (n) {
// Might be a leaf
if (IS_LEAF(n)) {
n = (art_node*)LEAF_RAW(n);
// Check if the expanded path matches
if (!leaf_prefix_matches((art_leaf*)n, key, key_len)) {
art_leaf *l = (art_leaf*)n;
return cb(data, (const unsigned char*)l->key, l->key_len, l->value);
}
return 0;
}
// If the depth matches the prefix, we need to handle this node
if (depth == key_len) {
art_leaf *l = minimum(n);
if (!leaf_prefix_matches(l, key, key_len))
return recursive_iter(n, cb, data);
return 0;
}
// Bail if the prefix does not match
if (n->partial_len) {
prefix_len = prefix_mismatch(n, key, key_len, depth);
// Guard if the mis-match is longer than the MAX_PREFIX_LEN
if ((uint32_t)prefix_len > n->partial_len) {
prefix_len = n->partial_len;
}
// If there is no match, search is terminated
if (!prefix_len) {
return 0;
// If we've matched the prefix, iterate on this node
} else if (depth + prefix_len == key_len) {
return recursive_iter(n, cb, data);
}
// if there is a full match, go deeper
depth = depth + n->partial_len;
}
// Recursively search
child = find_child(n, key[depth]);
n = (child) ? *child : NULL;
depth++;
}
return 0;
}

89
src/main.c Normal file
View File

@@ -0,0 +1,89 @@
#include <stdio.h>
#include <stdlib.h>
#include <forward/routetable.h>
#include <forward/if/interface.h>
#include <iputils.h>
#include <unistd.h>
int main(void) {
if (RouteTable_Init() < 0) {
fprintf(stderr, "Failed to initialize route tables\n");
return EXIT_FAILURE;
}
interface_v4_t* iface = Interface_Create_v4("0.0.0.0", NULL, NULL);
interface_v6_t* iface6 = Interface_Create_v6("::", NULL, NULL);
if (!iface || !iface6) {
fprintf(stderr, "Failed to create interfaces\n");
RouteTable_Cleanup();
return EXIT_FAILURE;
}
route_table_v4_entry_t entry = {.destination = 0x01000000, .mask = 0xFF000000, .nextHop = 0xC0A80101}; // Example route: 1.0.0.0/8 via 192.168.1.1
if (RouteTable_AddRoute_v4(entry.destination, entry.mask, entry.nextHop) < 0) {
fprintf(stderr, "Failed to add route to IPv4 route table\n");
}
route_table_v4_entry_t entry2 = {.destination = 0x01020000, .mask = 0xFFFF0000, .nextHop = 0xC0A80102}; // Example route: 1.2.0.0/16 via 192.168.1.1 - more specific than the previous one
if (RouteTable_AddRoute_v4(entry2.destination, entry2.mask, entry2.nextHop) < 0) {
fprintf(stderr, "Failed to add route to IPv4 route table\n");
}
// Get said route
uint32_t nextHop;
if (RouteTable_GetNextHop_v4(0x01020005, &nextHop) == 0) { // 1.2.0.5 should match the /16 route, not the /8 route
char destStr[16], nextHopStr[16];
IPUtils_PrintIPv4(0x01020005, destStr);
IPUtils_PrintIPv4(nextHop, nextHopStr);
printf("Next hop for destination %s is %s\n", destStr, nextHopStr);
} else {
char destStr[16];
IPUtils_PrintIPv4(0x01020005, destStr);
fprintf(stderr, "Failed to get next hop for destination %s\n", destStr);
}
if (RouteTable_GetNextHop_v4(0x01010001, &nextHop) == 0) { // 1.1.0.1 should match the /8 route, not the /16 route
char destStr[16], nextHopStr[16];
IPUtils_PrintIPv4(0x01010001, destStr);
IPUtils_PrintIPv4(nextHop, nextHopStr);
printf("Next hop for destination %s is %s\n", destStr, nextHopStr);
} else {
char destStr[16];
IPUtils_PrintIPv4(0x01010001, destStr);
fprintf(stderr, "Failed to get next hop for destination %s\n", destStr);
}
// Some IPv6 routes
__uint128_t dest6 = (((__uint128_t)0x20010DB8 << 96) | ((__uint128_t)0x00000000 << 64) | ((__uint128_t)0x00000000 << 32) | (__uint128_t)0x00000001); // 2001:0db8::1
__uint128_t mask6 = IPUtils_CIDRToMaskv6(64);
__uint128_t nextHop6 = (((__uint128_t)0x20010DB8 << 96) | ((__uint128_t)0x00000000 << 64) | ((__uint128_t)0x00000000 << 32) | (__uint128_t)0x00000002); // 2001:0db8::2
if (RouteTable_AddRoute_v6(dest6, mask6, nextHop6) < 0) {
fprintf(stderr, "Failed to add route to IPv6 route table\n");
}
// Get said IPv6 route
__uint128_t nextHop6Out;
if (RouteTable_GetNextHop_v6(dest6, &nextHop6Out) == 0) {
char destStr[40], nextHopStr[40];
IPUtils_PrintIPv6(dest6, destStr);
IPUtils_PrintIPv6(nextHop6Out, nextHopStr);
printf("Next hop for destination %s is %s\n", destStr, nextHopStr);
} else {
char destStr[40];
IPUtils_PrintIPv6(dest6, destStr);
fprintf(stderr, "Failed to get next hop for destination %s\n", destStr);
}
while (1) {
// Main loop can be used for future tasks such as monitoring, CLI, etc.
// For now, it just keeps the program running until interrupted.
pause();
}
RouteTable_Cleanup();
Interface_Cleanup();
return 0;
}