IPv4ClasslessRoute.c [plain text]
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/param.h>
#include <mach/boolean.h>
#include <stddef.h>
#include <arpa/inet.h>
#include <SystemConfiguration/SCValidation.h>
#include "IPv4ClasslessRoute.h"
#include "symbol_scope.h"
#include "cfutil.h"
#include "util.h"
#ifdef TEST_IPV4ROUTE
#define DEBUG 1
#endif
#include "IPConfigurationLog.h"
#define N_BITS_PER_BYTE 8
#define ROUTELIST_BUF_INITIAL_SIZE 64
typedef struct {
uint8_t rb_buf_s[ROUTELIST_BUF_INITIAL_SIZE];
bool rb_buf_user_supplied;
uint8_t * rb_buf;
int rb_buf_used;
int rb_buf_size;
} IPv4ClasslessRouteListBuf, * IPv4ClasslessRouteListBufRef;
STATIC void
IPv4ClasslessRouteListBufInit(IPv4ClasslessRouteListBufRef rb,
uint8_t * buf, int buf_size)
{
bzero(rb, sizeof(*rb));
if (buf != NULL) {
rb->rb_buf_user_supplied = TRUE;
rb->rb_buf = buf;
rb->rb_buf_size = buf_size;
}
else {
rb->rb_buf = rb->rb_buf_s;
rb->rb_buf_size = sizeof(rb->rb_buf_s);
}
return;
}
STATIC void
IPv4ClasslessRouteListBufFree(IPv4ClasslessRouteListBufRef rb)
{
if (rb->rb_buf_user_supplied == FALSE) {
if (rb->rb_buf != NULL && rb->rb_buf != rb->rb_buf_s) {
free(rb->rb_buf);
}
}
bzero(rb, sizeof(*rb));
return;
}
STATIC __inline__ int
IPv4ClasslessRouteListBufUsed(IPv4ClasslessRouteListBufRef rb)
{
return (rb->rb_buf_used);
}
STATIC __inline__ uint8_t *
IPv4ClasslessRouteListBufBuffer(IPv4ClasslessRouteListBufRef rb)
{
return (rb->rb_buf);
}
STATIC bool
IPv4ClasslessRouteListBufAddData(IPv4ClasslessRouteListBufRef rb,
const void * data, int data_len)
{
#ifdef DEBUG
printf("Adding: ");
print_data((void *)data, data_len);
#endif
if (data_len > (rb->rb_buf_size - rb->rb_buf_used)) {
if (rb->rb_buf_user_supplied) {
IPConfigLogFL(LOG_NOTICE,
"user-supplied buffer failed to add data with"
" length %d (> %d)",
data_len, (rb->rb_buf_size - rb->rb_buf_used));
return (FALSE);
}
#ifdef DEBUG
printf("Buffer growing from %d to %d\n", rb->rb_buf_size,
rb->rb_buf_size + ((data_len > ROUTELIST_BUF_INITIAL_SIZE)
? data_len : ROUTELIST_BUF_INITIAL_SIZE));
#endif
rb->rb_buf_size += (data_len > ROUTELIST_BUF_INITIAL_SIZE)
? data_len : ROUTELIST_BUF_INITIAL_SIZE;
if (rb->rb_buf == rb->rb_buf_s) {
rb->rb_buf = malloc(rb->rb_buf_size);
memcpy(rb->rb_buf, rb->rb_buf_s, rb->rb_buf_used);
}
else {
rb->rb_buf = reallocf(rb->rb_buf, rb->rb_buf_size);
if (rb->rb_buf == NULL) {
return (FALSE);
}
}
}
memcpy(rb->rb_buf + rb->rb_buf_used, data, data_len);
rb->rb_buf_used += data_len;
return (TRUE);
}
STATIC bool
IPv4ClasslessRouteListBufAddRoute(IPv4ClasslessRouteListBufRef rb,
IPv4ClasslessRouteRef element)
{
uint8_t prefix_length;
bool added = FALSE;
prefix_length = element->prefix_length;
if (prefix_length > 32) {
return (FALSE);
}
added = IPv4ClasslessRouteListBufAddData(rb, &prefix_length,
sizeof(prefix_length));
if (added) {
if (prefix_length > 0) {
int length;
length = roundup(prefix_length, N_BITS_PER_BYTE)
/ N_BITS_PER_BYTE;
added = IPv4ClasslessRouteListBufAddData(rb, &element->dest,
length);
}
if (added) {
added = IPv4ClasslessRouteListBufAddData(rb, &element->gate,
sizeof(element->gate));
}
}
return (added);
}
PRIVATE_EXTERN uint8_t *
IPv4ClasslessRouteListBufferCreate(IPv4ClasslessRouteRef list, int list_count,
uint8_t * buffer, int * buffer_size)
{
int bufsize = *buffer_size;
int i;
IPv4ClasslessRouteListBuf rb;
int used = 0;
if (list == NULL || list_count == 0) {
goto failed;
}
if (buffer == NULL) {
bufsize = 0;
}
else if (bufsize == 0) {
goto failed;
}
IPv4ClasslessRouteListBufInit(&rb, buffer, bufsize);
for (i = 0; i < list_count; i++) {
if (IPv4ClasslessRouteListBufAddRoute(&rb, list + i) == FALSE) {
IPConfigLogFL(LOG_NOTICE, "failed to add " IP_FORMAT,
IP_LIST(&list[i].dest));
IPv4ClasslessRouteListBufFree(&rb);
if (buffer != NULL) {
buffer = NULL;
}
goto failed;
}
}
used = IPv4ClasslessRouteListBufUsed(&rb);
if (buffer == NULL) {
buffer = malloc(used);
memcpy(buffer, IPv4ClasslessRouteListBufBuffer(&rb), used);
}
IPv4ClasslessRouteListBufFree(&rb);
failed:
*buffer_size = used;
return (buffer);
}
#ifdef TEST_IPV4ROUTE
STATIC void
IPv4ClasslessRouteListPrint(IPv4ClasslessRouteRef list, int list_count)
{
int i;
printf("%d routes\n", list_count);
for (i = 0; i < list_count; i++) {
char buf[32];
snprintf(buf, sizeof(buf), "%s/%d",
inet_ntoa(list[i].dest), list[i].prefix_length);
printf("%d. %-20s%-20s\n", i + 1, buf, inet_ntoa(list[i].gate));
}
return;
}
#endif
STATIC int
IPv4ClasslessRouteListParse(const uint8_t * buffer, int buffer_size,
IPv4ClasslessRouteRef list, int list_count)
{
int count = 0;
int left;
const uint8_t * scan;
for (left = buffer_size, scan = buffer;
left > 0; ) {
int length;
int needed;
uint8_t prefix_length = scan[0];
if (prefix_length > 32) {
IPConfigLogFL(LOG_NOTICE,
"prefix length is %d (> 32)",
prefix_length);
count = 0;
break;
}
length = roundup(prefix_length, N_BITS_PER_BYTE) / N_BITS_PER_BYTE;
needed = 1 + length + sizeof(struct in_addr);
if (left < needed) {
IPConfigLogFL(LOG_NOTICE,
"truncated descriptor %d < %d",
left, needed);
count = 0;
break;
}
if (list != NULL) {
if (count >= list_count) {
IPConfigLogFL(LOG_NOTICE,
"supplied route list too small (%d elements)",
list_count);
count = 0;
break;
}
list[count].prefix_length = prefix_length;
list[count].dest.s_addr = 0;
memcpy(&list[count].dest, scan + 1, length);
memcpy(&list[count].gate, scan + 1 + length,
sizeof(list[count].gate));
}
count++;
left -= needed;
scan += needed;
}
return (count);
}
PRIVATE_EXTERN IPv4ClasslessRouteRef
IPv4ClasslessRouteListCreate(const uint8_t * buffer, int buffer_size,
int * list_count)
{
int count = 0;
IPv4ClasslessRouteRef list = NULL;
if (buffer == NULL || buffer_size == 0) {
goto failed;
}
count = IPv4ClasslessRouteListParse(buffer, buffer_size, NULL, 0);
if (count != 0) {
list = (IPv4ClasslessRouteRef)malloc(sizeof(*list) * count);
if (IPv4ClasslessRouteListParse(buffer, buffer_size, list, count)
!= count) {
free(list);
list = NULL;
count = 0;
}
}
failed:
*list_count = count;
return (list);
}
STATIC bool
parse_destination_descriptor(CFStringRef str, struct in_addr * ip,
int * ret_prefix_length)
{
char * descr;
bool parsed = FALSE;
int prefix_length = -1;
char * slash;
descr = my_CFStringToCString(str, kCFStringEncodingASCII);
if (descr == NULL) {
goto failed;
}
slash = strchr(descr, '/');
if (slash != NULL) {
*slash = '\0';
prefix_length = strtoul(slash + 1, NULL, 0);
if (prefix_length < 0 || prefix_length > 32) {
IPConfigLogFL(LOG_NOTICE,
"invalid prefix length %d", prefix_length);
goto failed;
}
}
if (inet_aton(descr, ip) == 1) {
uint32_t ipval;
ipval = ntohl(ip->s_addr);
if (prefix_length == -1) {
if (IN_CLASSA(ipval)) {
prefix_length = 8;
}
else if (IN_CLASSB(ipval)) {
prefix_length = 16;
}
else {
prefix_length = 24;
}
}
if ((ipval & prefix_to_mask32(prefix_length)) == 0) {
ip->s_addr = 0;
prefix_length = 0;
}
*ret_prefix_length = prefix_length;
parsed = TRUE;
}
failed:
if (descr != NULL) {
free(descr);
}
return (parsed);
}
PRIVATE_EXTERN IPv4ClasslessRouteRef
IPv4ClasslessRouteListCreateWithArray(CFArrayRef string_list,
int * ret_count)
{
CFIndex count;
bool has_default = FALSE;
CFIndex i;
IPv4ClasslessRouteRef list = NULL;
CFIndex list_count = 0;
IPv4ClasslessRouteRef scan;
count = CFArrayGetCount(string_list);
if (count == 0) {
goto done;
}
if (count & 1) {
IPConfigLogFL(LOG_NOTICE,
"Classless route requires pairs of IP address values");
goto done;
}
count = count / 2;
list = (IPv4ClasslessRouteRef)malloc(sizeof(*list) * (count + 1));
for (scan = list + 1, i = 0; i < count; i++) {
CFStringRef dest = CFArrayGetValueAtIndex(string_list, 2 * i);
CFStringRef gate = CFArrayGetValueAtIndex(string_list, 2 * i + 1);
if (isA_CFString(dest) == NULL
|| isA_CFString(gate) == NULL) {
IPConfigLogFL(LOG_NOTICE, "Classless route array contains non-string");
list_count = 0;
break;
}
if (parse_destination_descriptor(dest, &scan->dest,
&scan->prefix_length)
== FALSE) {
IPConfigLogFL(LOG_NOTICE,
"Invalid route destination descriptor '%@'",
dest);
list_count = 0;
break;
}
if (my_CFStringToIPAddress(gate, &scan->gate) == FALSE) {
IPConfigLogFL(LOG_NOTICE,
"Invalid route gateway address '%@'", gate);
list_count = 0;
break;
}
if (scan->dest.s_addr == 0 && has_default == FALSE) {
has_default = TRUE;
list[0] = *scan;
}
else {
scan++;
}
list_count++;
}
if (list_count == 0) {
free(list);
list = NULL;
}
else if (has_default == FALSE) {
list[0].dest.s_addr = 0;
list[0].gate.s_addr = 0;
list[0].prefix_length = 0;
list_count++;
}
done:
*ret_count = list_count;
return (list);
}
PRIVATE_EXTERN IPv4ClasslessRouteRef
IPv4ClasslessRouteListGetDefault(IPv4ClasslessRouteRef list, int list_count)
{
int i;
IPv4ClasslessRouteRef scan;
if (list == NULL) {
return (NULL);
}
for (i = 0, scan = list; i < list_count; i++, scan++) {
if (scan->dest.s_addr == 0) {
return (scan);
}
}
return (NULL);
}
#ifdef TEST_IPV4ROUTE
typedef struct {
const char * dest;
const char * gate;
uint8_t prefix_length;
} TestRoute, * TestRouteRef;
typedef struct {
const TestRouteRef list;
int count;
} TestEntry;
typedef struct {
const uint8_t * buf;
int buf_size;
const char * description;
bool good;
} TestBufEntry;
STATIC IPv4ClasslessRouteRef
TestRouteCreateIPv4ClasslessRoute(TestRoute * list, int count)
{
int i;
IPv4ClasslessRouteRef ret_list;
ret_list = (IPv4ClasslessRouteRef)
malloc(sizeof(IPv4ClasslessRoute) * count);
for (i = 0; i < count; i++) {
inet_aton(list[i].dest, &ret_list[i].dest);
inet_aton(list[i].gate, &ret_list[i].gate);
ret_list[i].prefix_length = list[i].prefix_length;
}
return (ret_list);
}
STATIC TestRoute test_routes_1[] = {
{ "0.0.0.0", "192.0.1.1", 0 },
{ "10.0.1.1", "1.2.3.4", 24 },
{ "10.0.2.1", "2.3.4.5", 24 },
{ "10.0.3.1", "3.4.5.6", 24 },
{ "10.0.4.1", "0.0.0.0", 24 },
{ "1.1.1.1", "1.2.3.4", 1 },
{ "66.66.66.254", "192.0.1.1", 31 },
{ "192.168.2.0", "192.0.1.1", 30 },
{ "172.16.1.0", "192.0.1.1", 30 },
};
STATIC TestRoute test_routes_2[] = {
{ "0.0.0.0", "9.9.9.9", 0 },
{ "10.0.0.0", "1.1.1.1", 8 },
{ "10.0.0.0", "2.2.2.2", 24 },
{ "10.17.0.0", "3.3.3.3", 16, },
{ "10.27.129.0", "4.4.4.4", 24 },
{ "10.229.0.128", "5.5.5.5", 25 },
{ "10.198.122.47", "6.6.6.6", 32 },
{ "129.210.177.132", "7.7.7.7", 25 }
};
STATIC TestEntry tests[] = {
{ test_routes_1, sizeof(test_routes_1) / sizeof(test_routes_1[0]) },
{ test_routes_2, sizeof(test_routes_2) / sizeof(test_routes_2[0]) },
};
STATIC const uint8_t test_bad_buf_1[] = {
24,
10, 0, 0
};
STATIC const uint8_t test_bad_buf_2[] = {
0,
1, 2, 3, 4,
24
};
STATIC const uint8_t test_bad_buf_3[] = {
0,
1, 2, 3, 4,
24,
192, 168, 1,
5, 6, 7, 8,
255
};
STATIC const uint8_t test_good_buf_1[] = {
1,
10,
1, 2, 3, 4,
24,
192, 168, 1,
5, 6, 7, 8,
};
STATIC TestBufEntry test_bufs[] = {
{ test_bad_buf_1, sizeof(test_bad_buf_1), "too short", FALSE },
{ test_bad_buf_2, sizeof(test_bad_buf_2), "too short 2", FALSE },
{ test_bad_buf_3, sizeof(test_bad_buf_3), "bad prefix length", FALSE },
{ test_good_buf_1, sizeof(test_good_buf_1), "good 1", TRUE },
};
int
main(int argc, char * argv[])
{
int count;
int i;
count = sizeof(tests) / sizeof(tests[0]);
for (i = 0; i < count; i++) {
IPv4ClasslessRouteRef list;
int list_count;
IPv4ClasslessRouteRef list_copy;
int list_copy_count;
uint8_t * routes_buf;
int routes_buf_size;
printf("______________________________\n");
printf("test #%d.\n", i + 1);
list_count = tests[i].count;
list = TestRouteCreateIPv4ClasslessRoute(tests[i].list, list_count);
routes_buf = IPv4ClasslessRouteListBufferCreate(list, list_count,
NULL, &routes_buf_size);
IPv4ClasslessRouteListPrint(list, list_count);
print_data(routes_buf, routes_buf_size);
list_copy = IPv4ClasslessRouteListCreate(routes_buf, routes_buf_size,
&list_copy_count);
if (list_copy == NULL || list_copy_count != list_count) {
fprintf(stderr,
"%d: list_copy = %p, list_copy_count %d, list_count %d\n",
i, list_copy, list_copy_count, list_count);
exit(1);
}
IPv4ClasslessRouteListPrint(list_copy, list_copy_count);
free(routes_buf);
free(list_copy);
free(list);
}
count = sizeof(test_bufs) / sizeof(test_bufs[0]);
for (i = 0; i < count; i++) {
IPv4ClasslessRouteRef list;
int list_count;
list = IPv4ClasslessRouteListCreate(test_bufs[i].buf,
test_bufs[i].buf_size,
&list_count);
if (list == NULL) {
if (test_bufs[i].good == TRUE) {
fprintf(stderr, "Test %d (%s) FAILED\n", i + 1,
test_bufs[i].description);
fprintf(stderr, "good data failed to parse\n");
exit(1);
}
else {
printf("Test %d (%s) SUCCESS\n", i + 1,
test_bufs[i].description);
}
}
else {
if (test_bufs[i].good == FALSE) {
fprintf(stderr, "Test %d (%s) FAILED\n", i + 1,
test_bufs[i].description);
fprintf(stderr, "bad data parsed OK\n");
exit(1);
}
else {
printf("Test %d (%s) SUCCESS\n", i + 1,
test_bufs[i].description);
}
IPv4ClasslessRouteListPrint(list, list_count);
free(list);
}
}
exit(0);
return (0);
}
#endif