summaryrefslogtreecommitdiffstats
path: root/hicn-light/src/socket/api.c
diff options
context:
space:
mode:
Diffstat (limited to 'hicn-light/src/socket/api.c')
-rwxr-xr-xhicn-light/src/socket/api.c604
1 files changed, 604 insertions, 0 deletions
diff --git a/hicn-light/src/socket/api.c b/hicn-light/src/socket/api.c
new file mode 100755
index 000000000..aede01efe
--- /dev/null
+++ b/hicn-light/src/socket/api.c
@@ -0,0 +1,604 @@
+#include <arpa/inet.h> // inet_ntop
+#include <netdb.h> // ''
+#include <search.h> // tfind(), tdestroy(), twalk(), preorder...
+#include <stdbool.h>
+#include <stdio.h> // perror
+#include <stdlib.h> // calloc
+#include <string.h> // memcpy
+#include <sys/socket.h> // ''
+#include <sys/types.h> // getaddrinfo
+#include <unistd.h> // close
+
+#include "api.h"
+#include "error.h"
+#include "ops.h"
+
+#define INET_MAX_ADDRSTRLEN INET6_ADDRSTRLEN
+
+#define IF_NAMESIZE 16
+#define MAX_TABLES 256
+
+#define DEFAULT_INTERVAL 1000
+#define DEFAULT_IDENTIFIER "hicn"
+#define DEFAULT_SOCKET_IDENTIFIER "main"
+#define LOCAL_IPV6_PREFIX "fe80"
+
+#define LOCAL_PRIORITY 32000
+
+extern hicn_socket_ops_t ops;
+
+/* Configuration stored as a global variable to allow access from signal
+ * handlers for instance */
+
+static hicn_conf_t hicn_default_conf = {
+ .identifier = DEFAULT_IDENTIFIER,
+ //.format = HF_INET6_TCP
+};
+
+/* Global state */
+// FIXME move into helper state ?
+
+struct ip_rule_state_ {
+ char tun_name[IF_NAMESIZE];
+ ip_address_t ip_address;
+ uint32_t table_id;
+ uint8_t priority;
+ uint8_t address_family;
+};
+
+struct ip_route_state_ {
+ char remote_ip_address[128]; // this is to big, but it is fine for now
+ uint8_t address_family;
+ uint32_t table_id;
+};
+
+typedef struct ip_rule_state_ ip_rule_state;
+typedef struct ip_route_state_ ip_route_state;
+
+int punting_table_id;
+uint16_t rules_counter;
+uint16_t routes_counter;
+static ip_rule_state rules_to_remove[MAX_TABLES];
+static ip_route_state routes_to_remove[MAX_TABLES];
+
+// END FIXME
+
+hicn_socket_helper_t *hicn_create() {
+ int rc;
+
+ punting_table_id = -1;
+ rules_counter = 0;
+
+ hicn_socket_helper_t *hicn = malloc(sizeof(hicn_socket_helper_t));
+ if (!hicn) {
+ goto ERR_MALLOC;
+ }
+
+ hicn->conf = malloc(sizeof(hicn_conf_t));
+ if (hicn->conf < 0) goto ERR_CONF;
+ memcpy(hicn->conf, &hicn_default_conf, sizeof(hicn_conf_t));
+
+ /* Initialize socket tree to empty */
+ hicn->socket_root = NULL;
+
+ // enable forwarding globally. Per-interface forwarding will be enabled when
+ // interfaces are created (TODO)
+ rc = ops.enable_v6_forwarding(NULL);
+ if (rc < 0) {
+ goto ERR_FW;
+ }
+
+ rc = ops.enable_v4_forwarding();
+ if (rc < 0) {
+ goto ERR_FW;
+ }
+
+ // modify priority of table local
+ /* ip -6 rule del from all prio 0 table local */
+ /* ip -6 rule add from all prio 32000 table local */
+
+ rc = ops.del_lo_prio_rule(NULL, AF_INET6, 0);
+ if (rc < 0) {
+ goto ERR_FW;
+ }
+
+ rc = ops.del_lo_prio_rule(NULL, AF_INET, 0);
+ if (rc < 0) {
+ goto ERR_FW;
+ }
+
+ rc = ops.add_lo_prio_rule(NULL, AF_INET6, LOCAL_PRIORITY);
+ if (rc < 0) {
+ goto ERR_FW;
+ }
+
+ rc = ops.add_lo_prio_rule(NULL, AF_INET, LOCAL_PRIORITY);
+ if (rc < 0) {
+ goto ERR_FW;
+ }
+
+ return hicn;
+
+ERR_FW:
+ free(hicn->conf);
+ERR_CONF:
+ free(hicn);
+ERR_MALLOC:
+ return NULL;
+}
+
+void hicn_destroy() {
+ int rc;
+ uint16_t i;
+
+ /* Restore default rules */
+ printf("Restoring default configuration.\n");
+ rc = ops.del_lo_prio_rule(NULL, AF_INET6, LOCAL_PRIORITY);
+ if (rc < 0) {
+ goto ERR;
+ }
+
+ rc = ops.del_lo_prio_rule(NULL, AF_INET, LOCAL_PRIORITY);
+ if (rc < 0) {
+ goto ERR;
+ }
+
+ rc = ops.add_lo_prio_rule(NULL, AF_INET6, 0);
+ if (rc < 0) {
+ goto ERR;
+ }
+
+ rc = ops.add_lo_prio_rule(NULL, AF_INET, 0);
+ if (rc < 0) {
+ goto ERR;
+ }
+
+ for (i = 0; i < rules_counter; i++) {
+ if (strcmp(rules_to_remove[i].tun_name, "NONE") != 0) {
+ rc = ops.del_rule(rules_to_remove[i].tun_name,
+ rules_to_remove[i].address_family,
+ rules_to_remove[i].table_id);
+ if (rc < 0) {
+ goto ERR;
+ }
+ } else {
+ rc = ops.del_prio_rule(
+ &rules_to_remove[i].ip_address, rules_to_remove[i].address_family,
+ rules_to_remove[i].priority, rules_to_remove[i].table_id);
+ if (rc < 0) {
+ goto ERR;
+ }
+ }
+ }
+
+ for (i = 0; i < routes_counter; i++) {
+ rc = ops.del_out_route(routes_to_remove[i].remote_ip_address,
+ routes_to_remove[i].address_family,
+ routes_to_remove[i].table_id);
+ if (rc < 0) {
+ goto ERR;
+ }
+ }
+
+ERR:
+ if (rc < 0) printf("Unexpected exit. Some state may not be deleted.\n");
+ return;
+}
+
+void hicn_free(hicn_socket_helper_t *hicn) {
+ // close tun ?
+ free(hicn);
+}
+
+hicn_socket_t *hicn_socket_create() {
+ hicn_socket_t *socket = calloc(1, sizeof(hicn_socket_t));
+ if (!socket) {
+ goto ERR_SOCKET;
+ }
+ socket->type = HS_UNSPEC;
+
+ return socket;
+
+ERR_SOCKET:
+ return NULL;
+}
+
+int hicn_socket_cmp(hicn_socket_t *a, hicn_socket_t *b) {
+ return b->fd - a->fd;
+}
+
+ip_address_t *hicn_socket_get_src_ip(hicn_socket_t *socket) {
+ if (socket->type != HS_CONNECTION) {
+ return NULL;
+ }
+ return &socket->connection.tun_ip_address;
+}
+
+typedef int (*cmp_t)(const void *, const void *);
+
+int hicn_socket_add(hicn_socket_helper_t *hicn, hicn_socket_t *socket) {
+ if (!(tsearch(socket, &hicn->socket_root, (cmp_t)hicn_socket_cmp))) {
+ // ERROR("Could not insert field id into index");
+ return -1;
+ }
+ return 0;
+}
+
+hicn_socket_t *hicn_socket_find(hicn_socket_helper_t *hicn, int fd) {
+ hicn_socket_t search = {
+ .fd = fd,
+ };
+ hicn_socket_t **socket =
+ tfind(&search, &hicn->socket_root, (cmp_t)hicn_socket_cmp);
+ return socket ? *socket : NULL;
+}
+
+/*******************************************************************************
+ * New API
+ *******************************************************************************/
+
+int hicn_set_local_endpoint(hicn_socket_t *socket, const char *local_ip_address,
+ bool allow_null) {
+ int rc = HICN_SOCKET_ERROR_NONE;
+
+ if (!local_ip_address) {
+ if (!allow_null) {
+ rc = HICN_SOCKET_ERROR_SOCKET_LOCAL_NULL_ADDRESS;
+ }
+ goto end;
+ }
+
+ /* local_ip_address should be a prefix with global scope in which to pick
+ * the locator address to use as the source.
+ * If we expect to pick another IP for the tun, then it needs to be of size
+ * less than 128.
+ */
+
+ /* Copy the local IP address inside the connection */
+ rc = hicn_ip_pton(local_ip_address, &socket->connection.tun_ip_address);
+ if (rc < 0) {
+ rc = HICN_SOCKET_ERROR_SOCKET_LOCAL_REPR;
+ goto end;
+ }
+
+end:
+ return rc;
+}
+
+// XXX This could be used by hicn_set_remote_endpoint
+// XXX This has been introduced for mapme
+int hicn_get_local_address(const ip_address_t *remote_address,
+ ip_address_t *local_address) {
+ int rc = 0;
+ uint32_t interface_id;
+ char remote_address_str[INET_MAX_ADDRSTRLEN];
+
+ rc = hicn_ip_ntop(remote_address, remote_address_str,
+ sizeof(remote_address_str));
+ if (rc < 0) {
+ rc = HICN_SOCKET_ERROR_BIND_REMOTE_REPR;
+ goto ERR;
+ }
+
+ rc = ops.get_output_ifid(remote_address_str, remote_address->family,
+ &interface_id);
+ if (rc < 0 || interface_id == 0) {
+ rc = HICN_SOCKET_ERROR_BIND_REMOTE_INTERFACE;
+ goto ERR;
+ }
+
+ /* Local ip */
+ rc = ops.get_ip_addr(interface_id, remote_address->family, local_address);
+ if (rc < 0) {
+ rc = HICN_SOCKET_ERROR_BIND_REMOTE_NETMASK;
+ goto ERR;
+ }
+
+ERR:
+ return rc;
+}
+
+/**
+ *
+ * sets socket->interface_id
+ */
+int hicn_set_remote_endpoint(hicn_socket_t *socket,
+ const char *remote_ip_address) {
+ int af, rc = HICN_SOCKET_ERROR_NONE;
+ ip_address_t addr;
+
+ af = get_addr_family(remote_ip_address);
+ if ((af != AF_INET6) && (af != AF_INET)) {
+ return HICN_SOCKET_ERROR_INVALID_IP_ADDRESS;
+ }
+
+ /* Bind local endpoint if not done yet */
+ if (ip_address_empty(&socket->connection.tun_ip_address)) {
+ char local_ip_address[INET_MAX_ADDRSTRLEN];
+
+ /* Local interface id */
+ // INFO("Getting interface_id from gateway IP address %s",
+ // remote_ip_address);
+ /////
+ int addr_family = get_addr_family(remote_ip_address);
+ if (addr_family < 0) {
+ rc = addr_family;
+ goto ERR;
+ }
+
+ rc = ops.get_output_ifid(remote_ip_address, (uint8_t)addr_family,
+ &socket->connection.interface_id);
+ if (rc < 0 || socket->connection.interface_id == 0) {
+ rc = HICN_SOCKET_ERROR_BIND_REMOTE_INTERFACE;
+ goto ERR;
+ }
+
+ /* Local ip */
+ rc = ops.get_ip_addr(socket->connection.interface_id, (uint8_t)addr_family,
+ &addr);
+ if (rc < 0) {
+ rc = HICN_SOCKET_ERROR_BIND_REMOTE_NETMASK;
+ goto ERR;
+ }
+ /////
+
+ /* Convert to representation format */
+ rc = hicn_ip_ntop(&addr, local_ip_address, sizeof(local_ip_address));
+ if (rc < 0) {
+ rc = HICN_SOCKET_ERROR_BIND_REMOTE_REPR;
+ goto ERR;
+ }
+
+ rc = hicn_set_local_endpoint(socket, local_ip_address, true);
+ if (rc < 0) {
+ switch (rc) {
+ case HICN_SOCKET_ERROR_SOCKET_LOCAL_NULL_ADDRESS:
+ rc = HICN_SOCKET_ERROR_BIND_REMOTE_LOCAL_NULL_ADDR;
+ break;
+ case HICN_SOCKET_ERROR_SOCKET_LOCAL_REPR:
+ rc = HICN_SOCKET_ERROR_BIND_REMOTE_LOCAL_REPR;
+ break;
+ case HICN_SOCKET_ERROR_SOCKET_LOCAL_HEURISTIC:
+ rc = HICN_SOCKET_ERROR_BIND_REMOTE_LOCAL_HEURISTIC;
+ break;
+ case HICN_SOCKET_ERROR_SOCKET_LOCAL_SET_TUN_IP:
+ rc = HICN_SOCKET_ERROR_BIND_REMOTE_LOCAL_SET_TUN_IP;
+ break;
+ }
+ goto ERR;
+ }
+ }
+ return HICN_SOCKET_ERROR_NONE;
+
+ERR:
+ return rc;
+}
+
+/**
+ *
+ * We need at least an identifier.
+ */
+int hicn_socket(hicn_socket_helper_t *hicn, const char *identifier,
+ const char *local_ip_address) {
+ int rc;
+
+ hicn_socket_t *socket = hicn_socket_create();
+ if (!socket) {
+ rc = -5;
+ goto ERR_SOCKET;
+ }
+
+ ops.get_tun_name(hicn->conf->identifier, identifier, socket->tun_name);
+
+ // register the hicn face on which to bind prefixes, create the in/out TUN
+ // device
+ socket->fd = ops.tun_create(socket->tun_name);
+ if (socket->fd <= 0) {
+ rc = -2;
+ goto ERR_TUN;
+ }
+
+ // INFO("Successfully created listener on TUN device %s", socket->tun_name);
+
+ /* Retrieve interface id */
+ socket->tun_id = ops.get_ifid(socket->tun_name);
+ if (socket->tun_id < 0) {
+ rc = -3;
+ goto ERR_TUNIFID;
+ }
+ // INFO("Interface id=%d", socket->tun_id);
+
+ // WARN("Need to set offload");
+
+ // INFO("Setting interface up");
+ rc = ops.up_if(socket->tun_id);
+ if (rc < 0) {
+ rc = -4;
+ goto ERR_UP;
+ }
+
+ /* Update state */
+ rc = hicn_socket_add(hicn, socket);
+ if (rc < 0) {
+ rc = -5;
+ goto ERR_ADD;
+ }
+
+ rc = hicn_set_local_endpoint(socket, local_ip_address, true);
+ if (rc < 0) {
+ rc = -6;
+ goto ERR_ADJACENCY;
+ }
+
+ return socket->fd;
+
+ERR_ADJACENCY:
+ERR_ADD:
+ERR_UP:
+ERR_TUNIFID:
+ERR_TUN:
+ free(socket);
+ERR_SOCKET:
+ // ERR_PARAMS:
+ return rc;
+}
+
+int hicn_listen(hicn_socket_helper_t *hicn, int fd, const char *prefix) {
+ int rc;
+ hicn_socket_t *socket = hicn_socket_find(hicn, fd);
+ if (!socket) {
+ return -1;
+ }
+
+ /* Check socket is not a connection */
+ if (socket->type == HS_CONNECTION) {
+ return -1;
+ }
+
+ rc = ops.add_in_route_s(prefix, socket->tun_id);
+ if (rc < 0) {
+ return rc;
+ }
+
+ ip_address_t ip_address;
+ rc = hicn_ip_pton(prefix, &ip_address);
+ if (rc < 0) {
+ return rc;
+ }
+
+ // ip -6 rule add from b001::/16 prio 0 table 100
+ socket->connection.table_id =
+ socket->tun_id % MAX_TABLES; // this table should be unused
+
+ if (punting_table_id == -1) punting_table_id = socket->connection.table_id;
+
+ rc = ops.add_prio_rule(&ip_address, ip_address.family, 0,
+ socket->connection.table_id);
+ if (rc < 0) {
+ return rc;
+ }
+
+ strcpy(rules_to_remove[rules_counter].tun_name, "NONE");
+
+ rules_to_remove[rules_counter].ip_address = ip_address;
+ rules_to_remove[rules_counter].address_family = ip_address.family;
+ rules_to_remove[rules_counter].table_id = socket->connection.table_id;
+ rules_to_remove[rules_counter].priority = 0;
+ ++rules_counter;
+
+ /* Update socket upon success */
+ socket->type = HS_LISTENER;
+
+ return 0;
+}
+
+/**
+ *
+ * We can pass all adjacency parameters but identifier
+ */
+int hicn_bind(hicn_socket_helper_t *hicn, int fd,
+ const char *remote_ip_address) {
+ // uint32_t interface_id;
+ int rc = HICN_SOCKET_ERROR_NONE;
+
+ hicn_socket_t *socket = hicn_socket_find(hicn, fd);
+ if (!socket) {
+ rc = HICN_SOCKET_ERROR_BIND_SOCKET_NOT_FOUND;
+ goto ERR;
+ }
+
+ /* We allow reuse */
+ if (socket->type == HS_CONNECTION) return rc;
+
+ /* Check socket is not a connection */
+ if (socket->type != HS_UNSPEC) {
+ rc = HICN_SOCKET_ERROR_BIND_SOCKET_ALREADY_BOUND;
+ goto ERR;
+ }
+ socket->type = HS_CONNECTION;
+
+ // each connection is associated a table id, let's take it equal to the
+ // tun ID by default (% MAX_TABLES, assuming TUN IDs do not overlap modulo
+ // 256...).
+ // XXX we need to make sure the corresponding table is flushed.
+ socket->connection.table_id =
+ socket->tun_id % MAX_TABLES; // interface_id; // ops.get_free_table_id();
+
+ // XXX use IP address
+ rc = hicn_set_remote_endpoint(socket, remote_ip_address);
+ if (rc < 0) {
+ goto ERR;
+ }
+
+ // rule
+ // ip -6 rule from all iif eth0 lookup 200
+ // INFO("Adding output rule for %s in table %d", socket->tun_name,
+ // socket->connection.table_id);
+ int addr_family = get_addr_family(remote_ip_address);
+ if (addr_family < 0) {
+ rc = addr_family;
+ goto ERR;
+ }
+
+ rc = ops.add_rule(socket->tun_name, (uint8_t)addr_family,
+ socket->connection.table_id);
+ if (rc < 0) {
+ rc = HICN_SOCKET_ERROR_BIND_RULE;
+ goto ERR;
+ }
+
+ strcpy(rules_to_remove[rules_counter].tun_name, socket->tun_name);
+ rules_to_remove[rules_counter].address_family = addr_family;
+ rules_to_remove[rules_counter].table_id = socket->connection.table_id;
+ ++rules_counter;
+
+ // route
+ // ip -6 route add default via 2002::2 table 28
+ // INFO("Adding output route in table %d via gateway %s",
+ // socket->connection.table_id,
+ // remote_ip_address);
+
+ // if the address is an IPv6 and start with fe80 we need to specify the device
+ // in the route
+ u32 default_interface = ~0;
+ if (addr_family == AF_INET6 && strncmp(LOCAL_IPV6_PREFIX, remote_ip_address,
+ strlen(LOCAL_IPV6_PREFIX)) == 0) {
+ rc = ops.get_output_ifid(remote_ip_address, (uint8_t)addr_family,
+ &default_interface);
+ if (rc < 0) {
+ goto ERR;
+ }
+ }
+
+ rc = ops.add_out_route(remote_ip_address, (uint8_t)addr_family,
+ socket->connection.table_id, default_interface);
+ if (rc < 0) {
+ rc = HICN_SOCKET_ERROR_BIND_ROUTE;
+ goto ERR;
+ }
+
+ strcpy(routes_to_remove[routes_counter].remote_ip_address, remote_ip_address);
+ routes_to_remove[routes_counter].table_id = socket->connection.table_id;
+ routes_to_remove[routes_counter].address_family = (uint8_t)addr_family;
+ ++routes_counter;
+
+ // add route for data
+ // ip -6 route add 0:1::/64 dev hicn-if0 table 100
+ // this routes are deleted by removing the tun interfaces
+
+ if (punting_table_id == -1) {
+ // the punting_table_id was not initialized beacause no main-tun was created
+ // we use as an id (socket->tun_id - 1) % MAX_TABLES, so that we will hava a
+ // collision only after 255 new interfaces
+ punting_table_id = (socket->tun_id - 1) % MAX_TABLES;
+ }
+ rc = ops.add_in_route_table(&socket->connection.tun_ip_address,
+ socket->tun_id, punting_table_id);
+ if (rc < 0) {
+ rc = HICN_SOCKET_ERROR_BIND_ROUTE;
+ goto ERR;
+ }
+
+ERR:
+ return rc;
+}