eive-obsw/thirdparty/libcsp/src/rtable/csp_rtable_cidr.c
2021-03-04 18:29:28 +01:00

234 lines
5.9 KiB
C

/*
Cubesat Space Protocol - A small network-layer protocol designed for Cubesats
Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com)
Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <string.h>
#include <csp/csp.h>
#include <alloca.h>
#include <csp/arch/csp_malloc.h>
#include <csp/interfaces/csp_if_lo.h>
/* Local typedef for routing table */
typedef struct __attribute__((__packed__)) csp_rtable_s {
uint8_t address;
uint8_t netmask;
uint8_t mac;
csp_iface_t * interface;
struct csp_rtable_s * next;
} csp_rtable_t;
/* Routing entries are stored in a linked list*/
static csp_rtable_t * rtable = NULL;
static csp_rtable_t * csp_rtable_find(uint8_t addr, uint8_t netmask, uint8_t exact) {
/* Remember best result */
csp_rtable_t * best_result = NULL;
uint8_t best_result_mask = 0;
/* Start search */
csp_rtable_t * i = rtable;
while(i) {
/* Look for exact match */
if (i->address == addr && i->netmask == netmask) {
best_result = i;
break;
}
/* Try a CIDR netmask match */
if (!exact) {
uint8_t hostbits = (1 << (CSP_ID_HOST_SIZE - i->netmask)) - 1;
uint8_t netbits = ~hostbits;
//printf("Netbits %x Hostbits %x\r\n", netbits, hostbits);
/* Match network addresses */
uint8_t net_a = i->address & netbits;
uint8_t net_b = addr & netbits;
//printf("A: %hhx, B: %hhx\r\n", net_a, net_b);
/* We have a match */
if (net_a == net_b) {
if (i->netmask >= best_result_mask) {
//printf("Match best result %u %u\r\n", best_result_mask, i->netmask);
best_result = i;
best_result_mask = i->netmask;
}
}
}
i = i->next;
}
#if 0
if (best_result)
csp_debug(CSP_PACKET, "Using routing entry: %u/%u dev %s m:%u\r\n", best_result->address, best_result->netmask, best_result->interface->name, best_result->mac);
#endif
return best_result;
}
void csp_rtable_clear(void) {
for (csp_rtable_t * i = rtable; (i);) {
void * freeme = i;
i = i->next;
csp_free(freeme);
}
rtable = NULL;
/* Set loopback up again */
csp_rtable_set(csp_get_address(), CSP_ID_HOST_SIZE, &csp_if_lo, CSP_NODE_MAC);
}
static int csp_rtable_parse(const char * buffer, int dry_run) {
int valid_entries = 0;
/* Copy string before running strtok */
char * str = alloca(strlen(buffer) + 1);
memcpy(str, buffer, strlen(buffer) + 1);
/* Get first token */
str = strtok(str, ",");
while ((str) && (strlen(str) > 1)) {
int address = 0, netmask = 0, mac = 255;
char name[10] = {};
if (sscanf(str, "%u/%u %s %u", &address, &netmask, name, &mac) != 4) {
if (sscanf(str, "%u/%u %s", &address, &netmask, name) != 3) {
csp_log_error("Parse error %s", str);
return -1;
}
}
//printf("Parsed %u/%u %u %s\r\n", address, netmask, mac, name);
csp_iface_t * ifc = csp_iflist_get_by_name(name);
if (ifc) {
if (dry_run == 0)
csp_rtable_set(address, netmask, ifc, mac);
} else {
csp_log_error("Unknown interface %s", name);
return -1;
}
valid_entries++;
str = strtok(NULL, ",");
}
return valid_entries;
}
void csp_rtable_load(const char * buffer) {
csp_rtable_parse(buffer, 0);
}
int csp_rtable_check(const char * buffer) {
return csp_rtable_parse(buffer, 1);
}
int csp_rtable_save(char * buffer, int maxlen) {
int len = 0;
for (csp_rtable_t * i = rtable; (i); i = i->next) {
if (i->mac != CSP_NODE_MAC) {
len += snprintf(buffer + len, maxlen - len, "%u/%u %s %u, ", i->address, i->netmask, i->interface->name, i->mac);
} else {
len += snprintf(buffer + len, maxlen - len, "%u/%u %s, ", i->address, i->netmask, i->interface->name);
}
}
return len;
}
csp_iface_t * csp_rtable_find_iface(uint8_t id) {
csp_rtable_t * entry = csp_rtable_find(id, CSP_ID_HOST_SIZE, 0);
if (entry == NULL)
return NULL;
return entry->interface;
}
uint8_t csp_rtable_find_mac(uint8_t id) {
csp_rtable_t * entry = csp_rtable_find(id, CSP_ID_HOST_SIZE, 0);
if (entry == NULL)
return 255;
return entry->mac;
}
int csp_rtable_set(uint8_t _address, uint8_t _netmask, csp_iface_t *ifc, uint8_t mac) {
if (ifc == NULL)
return CSP_ERR_INVAL;
/* Set default route in the old way */
int address, netmask;
if (_address == CSP_DEFAULT_ROUTE) {
netmask = 0;
address = 0;
} else {
netmask = _netmask;
address = _address;
}
/* Fist see if the entry exists */
csp_rtable_t * entry = csp_rtable_find(address, netmask, 1);
/* If not, create a new one */
if (!entry) {
entry = csp_malloc(sizeof(csp_rtable_t));
if (entry == NULL)
return CSP_ERR_NOMEM;
entry->next = NULL;
/* Add entry to linked-list */
if (rtable == NULL) {
/* This is the first interface to be added */
rtable = entry;
} else {
/* One or more interfaces were already added */
csp_rtable_t * i = rtable;
while (i->next)
i = i->next;
i->next = entry;
}
}
/* Fill in the data */
entry->address = address;
entry->netmask = netmask;
entry->interface = ifc;
entry->mac = mac;
return CSP_ERR_NONE;
}
#ifdef CSP_DEBUG
void csp_rtable_print(void) {
for (csp_rtable_t * i = rtable; (i); i = i->next) {
if (i->mac == 255) {
printf("%u/%u %s\r\n", i->address, i->netmask, i->interface->name);
} else {
printf("%u/%u %s %u\r\n", i->address, i->netmask, i->interface->name, i->mac);
}
}
}
#endif